diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
35 files changed, 1752 insertions, 1127 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5f45d26d1e..7fb1c09ca0 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -22,16 +22,21 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, AnalyzerOptions &Options, CodeInjector *injector) : AnaCtxMgr( - ASTCtx, Options.UnoptimizedCFG, Options.includeImplicitDtorsInCFG(), - /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), - Options.includeLifetimeInCFG(), + ASTCtx, Options.UnoptimizedCFG, + Options.ShouldIncludeImplicitDtorsInCFG, + /*AddInitializers=*/true, + Options.ShouldIncludeTemporaryDtorsInCFG, + Options.ShouldIncludeLifetimeInCFG, // Adding LoopExit elements to the CFG is a requirement for loop // unrolling. - Options.includeLoopExitInCFG() || Options.shouldUnrollLoops(), - Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), - Options.shouldConditionalizeStaticInitializers(), - /*addCXXNewAllocator=*/true, Options.includeRichConstructorsInCFG(), - Options.shouldElideConstructors(), injector), + Options.ShouldIncludeLoopExitInCFG || + Options.ShouldUnrollLoops, + Options.ShouldIncludeScopesInCFG, + Options.ShouldSynthesizeBodies, + Options.ShouldConditionalizeStaticInitializers, + /*addCXXNewAllocator=*/true, + Options.ShouldIncludeRichConstructorsInCFG, + Options.ShouldElideConstructors, injector), Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index c910d31d4b..d9b63c209d 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -34,7 +34,7 @@ std::vector<StringRef> AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { static const StringRef StaticAnalyzerChecks[] = { #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT) \ FULLNAME, #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -49,114 +49,71 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { return Result; } -AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { - if (UserMode == UMK_NotSet) { - StringRef ModeStr = - Config.insert(std::make_pair("mode", "deep")).first->second; - UserMode = llvm::StringSwitch<UserModeKind>(ModeStr) - .Case("shallow", UMK_Shallow) - .Case("deep", UMK_Deep) - .Default(UMK_NotSet); - assert(UserMode != UMK_NotSet && "User mode is invalid."); - } - return UserMode; -} - -AnalyzerOptions::ExplorationStrategyKind -AnalyzerOptions::getExplorationStrategy() { - if (ExplorationStrategy == ExplorationStrategyKind::NotSet) { - StringRef StratStr = - Config - .insert(std::make_pair("exploration_strategy", "unexplored_first_queue")) - .first->second; - ExplorationStrategy = - llvm::StringSwitch<ExplorationStrategyKind>(StratStr) - .Case("dfs", ExplorationStrategyKind::DFS) - .Case("bfs", ExplorationStrategyKind::BFS) - .Case("unexplored_first", - ExplorationStrategyKind::UnexploredFirst) - .Case("unexplored_first_queue", - ExplorationStrategyKind::UnexploredFirstQueue) - .Case("bfs_block_dfs_contents", - ExplorationStrategyKind::BFSBlockDFSContents) - .Default(ExplorationStrategyKind::NotSet); - assert(ExplorationStrategy != ExplorationStrategyKind::NotSet && - "User mode is invalid."); - } - return ExplorationStrategy; -} - -IPAKind AnalyzerOptions::getIPAMode() { - if (IPAMode == IPAK_NotSet) { - // Use the User Mode to set the default IPA value. - // Note, we have to add the string to the Config map for the ConfigDumper - // checker to function properly. - const char *DefaultIPA = nullptr; - UserModeKind HighLevelMode = getUserMode(); - if (HighLevelMode == UMK_Shallow) - DefaultIPA = "inlining"; - else if (HighLevelMode == UMK_Deep) - DefaultIPA = "dynamic-bifurcate"; - assert(DefaultIPA); - - // Lookup the ipa configuration option, use the default from User Mode. - StringRef ModeStr = - Config.insert(std::make_pair("ipa", DefaultIPA)).first->second; - IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr) - .Case("none", IPAK_None) - .Case("basic-inlining", IPAK_BasicInlining) - .Case("inlining", IPAK_Inlining) - .Case("dynamic", IPAK_DynamicDispatch) - .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) - .Default(IPAK_NotSet); - assert(IPAConfig != IPAK_NotSet && "IPA Mode is invalid."); - - // Set the member variable. - IPAMode = IPAConfig; - } - - return IPAMode; +ExplorationStrategyKind +AnalyzerOptions::getExplorationStrategy() const { + auto K = + llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>( + ExplorationStrategy) + .Case("dfs", ExplorationStrategyKind::DFS) + .Case("bfs", ExplorationStrategyKind::BFS) + .Case("unexplored_first", + ExplorationStrategyKind::UnexploredFirst) + .Case("unexplored_first_queue", + ExplorationStrategyKind::UnexploredFirstQueue) + .Case("unexplored_first_location_queue", + ExplorationStrategyKind::UnexploredFirstLocationQueue) + .Case("bfs_block_dfs_contents", + ExplorationStrategyKind::BFSBlockDFSContents) + .Default(None); + assert(K.hasValue() && "User mode is invalid."); + return K.getValue(); +} + +IPAKind AnalyzerOptions::getIPAMode() const { + auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode) + .Case("none", IPAK_None) + .Case("basic-inlining", IPAK_BasicInlining) + .Case("inlining", IPAK_Inlining) + .Case("dynamic", IPAK_DynamicDispatch) + .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) + .Default(None); + assert(K.hasValue() && "IPA Mode is invalid."); + + return K.getValue(); } bool -AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { +AnalyzerOptions::mayInlineCXXMemberFunction( + CXXInlineableMemberKind Param) const { if (getIPAMode() < IPAK_Inlining) return false; - if (!CXXMemberInliningMode) { - static const char *ModeKey = "c++-inlining"; - - StringRef ModeStr = - Config.insert(std::make_pair(ModeKey, "destructors")).first->second; + auto K = + llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>( + CXXMemberInliningMode) + .Case("constructors", CIMK_Constructors) + .Case("destructors", CIMK_Destructors) + .Case("methods", CIMK_MemberFunctions) + .Case("none", CIMK_None) + .Default(None); - CXXInlineableMemberKind &MutableMode = - const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode); - - MutableMode = llvm::StringSwitch<CXXInlineableMemberKind>(ModeStr) - .Case("constructors", CIMK_Constructors) - .Case("destructors", CIMK_Destructors) - .Case("none", CIMK_None) - .Case("methods", CIMK_MemberFunctions) - .Default(CXXInlineableMemberKind()); - - if (!MutableMode) { - // FIXME: We should emit a warning here about an unknown inlining kind, - // but the AnalyzerOptions doesn't have access to a diagnostic engine. - MutableMode = CIMK_None; - } - } + assert(K.hasValue() && "Invalid c++ member function inlining mode."); - return CXXMemberInliningMode >= K; + return *K >= Param; } -static StringRef toString(bool b) { return b ? "true" : "false"; } - -StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, - StringRef OptionName, - StringRef Default, - bool SearchInParents) { +StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, + StringRef DefaultVal, + const CheckerBase *C, + bool SearchInParents) const { + assert(C); // Search for a package option if the option for the checker is not specified // and search in parents is enabled. + StringRef CheckerName = C->getTagDescription(); + + assert(!CheckerName.empty() && + "Empty checker name! Make sure the checker object (including it's " + "bases!) if fully initialized before calling this function!"); ConfigTable::const_iterator E = Config.end(); do { ConfigTable::const_iterator I = @@ -165,338 +122,35 @@ StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, return StringRef(I->getValue()); size_t Pos = CheckerName.rfind('.'); if (Pos == StringRef::npos) - return Default; + return DefaultVal; CheckerName = CheckerName.substr(0, Pos); } while (!CheckerName.empty() && SearchInParents); - return Default; + return DefaultVal; } -bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, - const CheckerBase *C, - bool SearchInParents) { +bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, + const CheckerBase *C, + bool SearchInParents) const { // FIXME: We should emit a warning here if the value is something other than // "true", "false", or the empty string (meaning the default value), // but the AnalyzerOptions doesn't have access to a diagnostic engine. - StringRef Default = toString(DefaultVal); - StringRef V = - C ? getCheckerOption(C->getTagDescription(), Name, Default, - SearchInParents) - : StringRef(Config.insert(std::make_pair(Name, Default)).first->second); - return llvm::StringSwitch<bool>(V) + assert(C); + return llvm::StringSwitch<bool>( + getCheckerStringOption(Name, DefaultVal ? "true" : "false", C, + SearchInParents)) .Case("true", true) .Case("false", false) .Default(DefaultVal); } -bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, - bool DefaultVal, const CheckerBase *C, - bool SearchInParents) { - if (!V.hasValue()) - V = getBooleanOption(Name, DefaultVal, C, SearchInParents); - return V.getValue(); -} - -bool AnalyzerOptions::includeTemporaryDtorsInCFG() { - return getBooleanOption(IncludeTemporaryDtorsInCFG, - "cfg-temporary-dtors", - /* Default = */ true); -} - -bool AnalyzerOptions::includeImplicitDtorsInCFG() { - return getBooleanOption(IncludeImplicitDtorsInCFG, - "cfg-implicit-dtors", - /* Default = */ true); -} - -bool AnalyzerOptions::includeLifetimeInCFG() { - return getBooleanOption(IncludeLifetimeInCFG, "cfg-lifetime", - /* Default = */ false); -} - -bool AnalyzerOptions::includeLoopExitInCFG() { - return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", - /* Default = */ false); -} - -bool AnalyzerOptions::includeRichConstructorsInCFG() { - return getBooleanOption(IncludeRichConstructorsInCFG, - "cfg-rich-constructors", - /* Default = */ true); -} - -bool AnalyzerOptions::includeScopesInCFG() { - return getBooleanOption(IncludeScopesInCFG, - "cfg-scopes", - /* Default = */ false); -} - -bool AnalyzerOptions::mayInlineCXXStandardLibrary() { - return getBooleanOption(InlineCXXStandardLibrary, - "c++-stdlib-inlining", - /*Default=*/true); -} - -bool AnalyzerOptions::mayInlineTemplateFunctions() { - return getBooleanOption(InlineTemplateFunctions, - "c++-template-inlining", - /*Default=*/true); -} - -bool AnalyzerOptions::mayInlineCXXAllocator() { - return getBooleanOption(InlineCXXAllocator, - "c++-allocator-inlining", - /*Default=*/true); -} - -bool AnalyzerOptions::mayInlineCXXContainerMethods() { - return getBooleanOption(InlineCXXContainerMethods, - "c++-container-inlining", - /*Default=*/false); -} - -bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() { - return getBooleanOption(InlineCXXSharedPtrDtor, - "c++-shared_ptr-inlining", - /*Default=*/false); -} - -bool AnalyzerOptions::mayInlineCXXTemporaryDtors() { - return getBooleanOption(InlineCXXTemporaryDtors, - "c++-temp-dtor-inlining", - /*Default=*/true); -} - -bool AnalyzerOptions::mayInlineObjCMethod() { - return getBooleanOption(ObjCInliningMode, - "objc-inlining", - /* Default = */ true); -} - -bool AnalyzerOptions::shouldSuppressNullReturnPaths() { - return getBooleanOption(SuppressNullReturnPaths, - "suppress-null-return-paths", - /* Default = */ true); -} - -bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() { - return getBooleanOption(AvoidSuppressingNullArgumentPaths, - "avoid-suppressing-null-argument-paths", - /* Default = */ false); -} - -bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { - return getBooleanOption(SuppressInlinedDefensiveChecks, - "suppress-inlined-defensive-checks", - /* Default = */ true); -} - -bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { - return getBooleanOption(SuppressFromCXXStandardLibrary, - "suppress-c++-stdlib", - /* Default = */ true); -} - -bool AnalyzerOptions::shouldCrosscheckWithZ3() { - return getBooleanOption(CrosscheckWithZ3, - "crosscheck-with-z3", - /* Default = */ false); -} - -bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() { - return getBooleanOption(ReportIssuesInMainSourceFile, - "report-in-main-source-file", - /* Default = */ false); -} - - -bool AnalyzerOptions::shouldWriteStableReportFilename() { - return getBooleanOption(StableReportFilename, - "stable-report-filename", - /* Default = */ false); -} - -bool AnalyzerOptions::shouldSerializeStats() { - return getBooleanOption(SerializeStats, - "serialize-stats", - /* Default = */ false); -} - -bool AnalyzerOptions::shouldElideConstructors() { - return getBooleanOption(ElideConstructors, - "elide-constructors", - /* Default = */ true); -} - -int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, +int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, const CheckerBase *C, - bool SearchInParents) { - SmallString<10> StrBuf; - llvm::raw_svector_ostream OS(StrBuf); - OS << DefaultVal; - - StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(), - SearchInParents) - : StringRef(Config.insert(std::make_pair(Name, OS.str())) - .first->second); - - int Res = DefaultVal; - bool b = V.getAsInteger(10, Res); - assert(!b && "analyzer-config option should be numeric"); - (void)b; - return Res; -} - -StringRef AnalyzerOptions::getOptionAsString(StringRef Name, - StringRef DefaultVal, - const CheckerBase *C, - bool SearchInParents) { - return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal, - SearchInParents) - : StringRef( - Config.insert(std::make_pair(Name, DefaultVal)).first->second); -} - -unsigned AnalyzerOptions::getAlwaysInlineSize() { - if (!AlwaysInlineSize.hasValue()) - AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3); - return AlwaysInlineSize.getValue(); -} - -unsigned AnalyzerOptions::getMaxInlinableSize() { - if (!MaxInlinableSize.hasValue()) { - int DefaultValue = 0; - UserModeKind HighLevelMode = getUserMode(); - switch (HighLevelMode) { - default: - llvm_unreachable("Invalid mode."); - case UMK_Shallow: - DefaultValue = 4; - break; - case UMK_Deep: - DefaultValue = 100; - break; - } - - MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue); - } - return MaxInlinableSize.getValue(); -} - -unsigned AnalyzerOptions::getGraphTrimInterval() { - if (!GraphTrimInterval.hasValue()) - GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000); - return GraphTrimInterval.getValue(); -} - -unsigned AnalyzerOptions::getMaxSymbolComplexity() { - if (!MaxSymbolComplexity.hasValue()) - MaxSymbolComplexity = getOptionAsInteger("max-symbol-complexity", 35); - return MaxSymbolComplexity.getValue(); -} - -unsigned AnalyzerOptions::getMaxTimesInlineLarge() { - if (!MaxTimesInlineLarge.hasValue()) - MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); - return MaxTimesInlineLarge.getValue(); -} - -unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() { - if (!MinCFGSizeTreatFunctionsAsLarge.hasValue()) - MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger( - "min-cfg-size-treat-functions-as-large", 14); - return MinCFGSizeTreatFunctionsAsLarge.getValue(); -} - -unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { - if (!MaxNodesPerTopLevelFunction.hasValue()) { - int DefaultValue = 0; - UserModeKind HighLevelMode = getUserMode(); - switch (HighLevelMode) { - default: - llvm_unreachable("Invalid mode."); - case UMK_Shallow: - DefaultValue = 75000; - break; - case UMK_Deep: - DefaultValue = 225000; - break; - } - MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue); - } - return MaxNodesPerTopLevelFunction.getValue(); -} - -bool AnalyzerOptions::shouldSynthesizeBodies() { - return getBooleanOption("faux-bodies", true); -} - -bool AnalyzerOptions::shouldPrunePaths() { - return getBooleanOption("prune-paths", true); -} - -bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { - return getBooleanOption("cfg-conditional-static-initializers", true); -} - -bool AnalyzerOptions::shouldInlineLambdas() { - if (!InlineLambdas.hasValue()) - InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true); - return InlineLambdas.getValue(); -} - -bool AnalyzerOptions::shouldWidenLoops() { - if (!WidenLoops.hasValue()) - WidenLoops = getBooleanOption("widen-loops", /*Default=*/false); - return WidenLoops.getValue(); -} - -bool AnalyzerOptions::shouldUnrollLoops() { - if (!UnrollLoops.hasValue()) - UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false); - return UnrollLoops.getValue(); -} - -bool AnalyzerOptions::shouldDisplayNotesAsEvents() { - if (!DisplayNotesAsEvents.hasValue()) - DisplayNotesAsEvents = - getBooleanOption("notes-as-events", /*Default=*/false); - return DisplayNotesAsEvents.getValue(); -} - -bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() { - if (!AggressiveBinaryOperationSimplification.hasValue()) - AggressiveBinaryOperationSimplification = - getBooleanOption("aggressive-binary-operation-simplification", - /*Default=*/false); - return AggressiveBinaryOperationSimplification.getValue(); -} - -bool AnalyzerOptions::shouldEagerlyAssume() { - if (!EagerlyAssumeBinOpBifurcation.hasValue()) - EagerlyAssumeBinOpBifurcation = - getBooleanOption("eagerly-assume", true); - return EagerlyAssumeBinOpBifurcation.getValue(); -} - -StringRef AnalyzerOptions::getCTUDir() { - if (!CTUDir.hasValue()) { - CTUDir = getOptionAsString("ctu-dir", ""); - if (!llvm::sys::fs::is_directory(*CTUDir)) - CTUDir = ""; - } - return CTUDir.getValue(); -} - -bool AnalyzerOptions::naiveCTUEnabled() { - if (!NaiveCTU.hasValue()) { - NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis", - /*Default=*/false); - } - return NaiveCTU.getValue(); -} - -StringRef AnalyzerOptions::getCTUIndexName() { - if (!CTUIndexName.hasValue()) - CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt"); - return CTUIndexName.getValue(); + bool SearchInParents) const { + int Ret = DefaultVal; + bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, + SearchInParents) + .getAsInteger(10, Ret); + assert(!HasFailed && "analyzer-config option should be numeric"); + (void)HasFailed; + return Ret; } diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index db4c1432cc..d8ed6942de 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -207,7 +207,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2) { switch (Op) { default: - assert(false && "Invalid Opcode."); + llvm_unreachable("Invalid Opcode."); case BO_Mul: return &getValue( V1 * V2 ); diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 8899fa67a3..fd7f532104 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -546,7 +546,8 @@ static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, } } -static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM); std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( @@ -819,7 +820,7 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, // and values by tracing interesting calculations backwards through evaluated // expressions along a path. This is probably overly complicated, but the idea // is that if an expression computed an "interesting" value, the child -// expressions are are also likely to be "interesting" as well (which then +// expressions are also likely to be "interesting" as well (which then // propagates to the values they in turn compute). This reverse propagation // is needed to track interesting correlations across function call boundaries, // where formal arguments bind to actual arguments, etc. This is also needed @@ -841,7 +842,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R, default: if (!isa<CastExpr>(Ex)) break; - // Fall through. + LLVM_FALLTHROUGH; case Stmt::BinaryOperatorClass: case Stmt::UnaryOperatorClass: { for (const Stmt *SubStmt : Ex->children()) { @@ -1265,7 +1266,7 @@ static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) { if (!S) break; - if (isa<ExprWithCleanups>(S) || + if (isa<FullExpr>(S) || isa<CXXBindTemporaryExpr>(S) || isa<SubstNonTypeTemplateParmExpr>(S)) continue; @@ -1972,12 +1973,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( PathDiagnosticLocation::createBegin(D, SM)); } - if (!AddPathEdges && GenerateDiagnostics) - CompactPathDiagnostic(PD->getMutablePieces(), SM); // Finally, prune the diagnostic path of uninteresting stuff. if (!PD->path.empty()) { - if (R->shouldPrunePath() && Opts.shouldPrunePaths()) { + if (R->shouldPrunePath() && Opts.ShouldPrunePaths) { bool stillHasNotes = removeUnneededCalls(PD->getMutablePieces(), R, LCM); assert(stillHasNotes); @@ -2007,6 +2006,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( removeRedundantMsgs(PD->getMutablePieces()); removeEdgesToDefaultInitializers(PD->getMutablePieces()); } + + if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions) + CompactMacroExpandedPieces(PD->getMutablePieces(), SM); + return PD; } @@ -2436,9 +2439,10 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { return true; } -/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object -/// and collapses PathDiagosticPieces that are expanded by macros. -static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { +/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic +/// object and collapses PathDiagosticPieces that are expanded by macros. +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM) { using MacroStackTy = std::vector< std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; @@ -2454,7 +2458,7 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { // Recursively compact calls. if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) { - CompactPathDiagnostic(call->path, SM); + CompactMacroExpandedPieces(call->path, SM); } // Get the location of the PathDiagnosticPiece. @@ -2617,7 +2621,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( generateVisitorsDiagnostics(R, ErrorNode, BRC); if (R->isValid()) { - if (Opts.shouldCrosscheckWithZ3()) { + if (Opts.ShouldCrosscheckWithZ3) { // If crosscheck is enabled, remove all visitors, add the refutation // visitor and check again R->clearVisitors(); @@ -2959,7 +2963,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { } PathPieces &Pieces = PD->getMutablePieces(); - if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) { + if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) { // For path diagnostic consumers that don't support extra notes, // we may optionally convert those to path notes. for (auto I = report->getNotes().rbegin(), @@ -3096,7 +3100,7 @@ BugReporter::generateDiagnosticForConsumerMap( // report location to the last piece in the main source file. AnalyzerOptions &Opts = getAnalyzerOptions(); for (auto const &P : *Out) - if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) + if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll) P.second->resetDiagnosticLocationToMainFile(); return Out; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 1e2939b79d..da94b6eb21 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -71,12 +71,6 @@ using namespace ento; // Utility functions. //===----------------------------------------------------------------------===// -bool bugreporter::isDeclRefExprToReference(const Expr *E) { - if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) - return DRE->getDecl()->getType()->isReferenceType(); - return false; -} - static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) { if (B->isAdditiveOp() && B->getType()->isPointerType()) { if (B->getLHS()->getType()->isPointerType()) { @@ -142,8 +136,8 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { E = AE->getBase(); } else if (const auto *PE = dyn_cast<ParenExpr>(E)) { E = PE->getSubExpr(); - } else if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) { - E = EWC->getSubExpr(); + } else if (const auto *FE = dyn_cast<FullExpr>(E)) { + E = FE->getSubExpr(); } else { // Other arbitrary stuff. break; @@ -160,20 +154,6 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { return E; } -const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const auto *BE = dyn_cast<BinaryOperator>(S)) - return BE->getRHS(); - return nullptr; -} - -const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const auto *RS = dyn_cast<ReturnStmt>(S)) - return RS->getRetValue(); - return nullptr; -} - //===----------------------------------------------------------------------===// // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// @@ -696,8 +676,8 @@ public: bool EnableNullFPSuppression, BugReport &BR, const SVal V) { AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; - if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths() - && V.getAs<Loc>()) + if (EnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>()) BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( R->getAs<SubRegion>(), V)); } @@ -828,7 +808,8 @@ public: AnalyzerOptions &Options = State->getAnalysisManager().options; bool EnableNullFPSuppression = false; - if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()) + if (InEnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths) if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); @@ -884,7 +865,7 @@ public: RetE = RetE->IgnoreParenCasts(); // If we're returning 0, we should track where that 0 came from. - bugreporter::trackNullOrUndefValue(N, RetE, BR, EnableNullFPSuppression); + bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression); // Build an appropriate message based on the return value. SmallString<64> Msg; @@ -897,7 +878,7 @@ public: // future nodes. We want to emit a path note as well, in case // the report is resurrected as valid later on. if (EnableNullFPSuppression && - Options.shouldAvoidSuppressingNullArgumentPaths()) + Options.ShouldAvoidSuppressingNullArgumentPaths) Mode = MaybeUnsuppress; if (RetE->getType()->isObjCObjectPointerType()) { @@ -945,7 +926,7 @@ public: visitNodeMaybeUnsuppress(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { #ifndef NDEBUG - assert(Options.shouldAvoidSuppressingNullArgumentPaths()); + assert(Options.ShouldAvoidSuppressingNullArgumentPaths); #endif // Are we at the entry node for this call? @@ -979,8 +960,7 @@ public: if (!State->isNull(*ArgV).isConstrainedTrue()) continue; - if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, - EnableNullFPSuppression)) + if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression)) ShouldInvalidate = false; // If we /can't/ track the null pointer, we should err on the side of @@ -1258,8 +1238,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { if (!IsParam) InitE = InitE->IgnoreParenCasts(); - bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, - EnableNullFPSuppression); + bugreporter::trackExpressionValue(StoreSite, InitE, BR, + EnableNullFPSuppression); } ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), BR, EnableNullFPSuppression); @@ -1399,7 +1379,7 @@ SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) : V(Value) { // Check if the visitor is disabled. AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; - if (!Options.shouldSuppressInlinedDefensiveChecks()) + if (!Options.ShouldSuppressInlinedDefensiveChecks) IsSatisfied = true; assert(N->getState()->isNull(V).isConstrainedTrue() && @@ -1515,8 +1495,8 @@ static const MemRegion *getLocationRegionIfReference(const Expr *E, static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) { Ex = Ex->IgnoreParenCasts(); - if (const auto *EWC = dyn_cast<ExprWithCleanups>(Ex)) - return peelOffOuterExpr(EWC->getSubExpr(), N); + if (const auto *FE = dyn_cast<FullExpr>(Ex)) + return peelOffOuterExpr(FE->getSubExpr(), N); if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex)) return peelOffOuterExpr(OVE->getSourceExpr(), N); if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) { @@ -1588,14 +1568,13 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, return N; } -bool bugreporter::trackNullOrUndefValue(const ExplodedNode *InputNode, - const Stmt *InputS, - BugReport &report, - bool EnableNullFPSuppression) { - if (!InputS || !InputNode || !isa<Expr>(InputS)) +bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, + const Expr *E, BugReport &report, + bool EnableNullFPSuppression) { + if (!E || !InputNode) return false; - const Expr *Inner = peelOffOuterExpr(cast<Expr>(InputS), InputNode); + const Expr *Inner = peelOffOuterExpr(E, InputNode); const ExplodedNode *LVNode = findNodeForExpression(InputNode, Inner); if (!LVNode) return false; @@ -1606,11 +1585,11 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *InputNode, // At this point in the path, the receiver should be live since we are at the // message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) - trackNullOrUndefValue(LVNode, Receiver, report, EnableNullFPSuppression); + trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. - if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { + if (ExplodedGraph::isInterestingLValueExpr(Inner)) { SVal LVal = LVNode->getSVal(Inner); const MemRegion *RR = getLocationRegionIfReference(Inner, LVNode); @@ -1703,7 +1682,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *InputNode, if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( - loc::MemRegionVal(RegionRVal), false)); + loc::MemRegionVal(RegionRVal), /*assumption=*/false)); } } return true; @@ -1751,8 +1730,8 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackNullOrUndefValue(N, Receiver, BR, - /*EnableNullFPSuppression*/ false); + bugreporter::trackExpressionValue(N, Receiver, BR, + /*EnableNullFPSuppression*/ false); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -2241,7 +2220,7 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( // the user's fault, we currently don't report them very well, and // Note that this will not help for any other data structure libraries, like // TR1, Boost, or llvm/ADT. - if (Options.shouldSuppressFromCXXStandardLibrary()) { + if (Options.ShouldSuppressFromCXXStandardLibrary) { BR.markInvalid(getTag(), nullptr); return; } else { @@ -2448,15 +2427,19 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( // Add constraints to the solver for (const auto &I : Constraints) { - SymbolRef Sym = I.first; + const SymbolRef Sym = I.first; + auto RangeIt = I.second.begin(); - SMTExprRef Constraints = RefutationSolver->fromBoolean(false); - for (const auto &Range : I.second) { + SMTExprRef Constraints = SMTConv::getRangeExpr( + RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(), + /*InRange=*/true); + while ((++RangeIt) != I.second.end()) { Constraints = RefutationSolver->mkOr( Constraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym, - Range.From(), Range.To(), + RangeIt->From(), RangeIt->To(), /*InRange=*/true)); } + RefutationSolver->addConstraint(Constraints); } diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index db06e4efd5..17334d841e 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -45,13 +45,15 @@ add_clang_library(clangStaticAnalyzerCore RangedConstraintManager.cpp RegionStore.cpp RetainSummaryManager.cpp - SValBuilder.cpp - SVals.cpp + SarifDiagnostics.cpp SimpleConstraintManager.cpp SimpleSValBuilder.cpp Store.cpp SubEngine.cpp + SValBuilder.cpp + SVals.cpp SymbolManager.cpp + TaintManager.cpp WorkList.cpp Z3ConstraintManager.cpp diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 7b6a8d4d25..767116630f 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -503,10 +503,14 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, const ParmVarDecl *ParamDecl = *I; assert(ParamDecl && "Formal parameter has no decl?"); + // TODO: Support allocator calls. if (Call.getKind() != CE_CXXAllocator) if (Call.isArgumentConstructedDirectly(Idx)) continue; + // TODO: Allocators should receive the correct size and possibly alignment, + // determined in compile-time but not represented as arg-expressions, + // which makes getArgSVal() fail and return UnknownVal. SVal ArgVal = Call.getArgSVal(Idx); if (!ArgVal.isUnknown()) { Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); @@ -550,13 +554,14 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const { AnalyzerOptions &Opts = Engine->getAnalysisManager().options; // Try to get CTU definition only if CTUDir is provided. - if (!Opts.naiveCTUEnabled()) + if (!Opts.IsNaiveCTUEnabled) return {}; cross_tu::CrossTranslationUnitContext &CTUCtx = *Engine->getCrossTranslationUnitContext(); llvm::Expected<const FunctionDecl *> CTUDeclOrError = - CTUCtx.getCrossTUDefinition(FD, Opts.getCTUDir(), Opts.getCTUIndexName()); + CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir, Opts.CTUIndexName, + Opts.DisplayCTUProgress); if (!CTUDeclOrError) { handleAllErrors(CTUDeclOrError.takeError(), diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index b422a88719..72bfd84b40 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -17,6 +17,8 @@ using namespace clang; using namespace ento; +int ImplicitNullDerefEvent::Tag; + StringRef CheckerBase::getTagDescription() const { return getCheckName().getName(); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 3740e4bf4d..688c47e984 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -441,8 +441,8 @@ void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExprEngine &Eng, const ReturnStmt *RS) { - // We define the builder outside of the loop bacause if at least one checkers - // creates a sucsessor for Pred, we do not need to generate an + // We define the builder outside of the loop because if at least one checker + // creates a successor for Pred, we do not need to generate an // autotransition for it. NodeBuilder Bldr(Pred, Dst, BC); for (const auto checkFn : EndFunctionCheckers) { diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 89adea2cff..00475c04fd 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -12,7 +12,6 @@ #include "clang/Basic/LLVM.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" @@ -30,6 +29,41 @@ static const char PackageSeparator = '.'; using CheckerInfoSet = llvm::SetVector<const CheckerRegistry::CheckerInfo *>; +namespace { +/// Represents a request to include or exclude a checker or package from a +/// specific analysis run. +/// +/// \sa CheckerRegistry::initializeManager +class CheckerOptInfo { + StringRef Name; + bool Enable; + bool Claimed; + +public: + CheckerOptInfo(StringRef name, bool enable) + : Name(name), Enable(enable), Claimed(false) { } + + StringRef getName() const { return Name; } + bool isEnabled() const { return Enable; } + bool isDisabled() const { return !isEnabled(); } + + bool isClaimed() const { return Claimed; } + bool isUnclaimed() const { return !isClaimed(); } + void claim() { Claimed = true; } +}; + +} // end of anonymous namespace + +static SmallVector<CheckerOptInfo, 8> +getCheckerOptList(const AnalyzerOptions &opts) { + SmallVector<CheckerOptInfo, 8> checkerOpts; + for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) { + const std::pair<std::string, bool> &opt = opts.CheckersControlList[i]; + checkerOpts.push_back(CheckerOptInfo(opt.first, opt.second)); + } + return checkerOpts; +} + static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, const CheckerRegistry::CheckerInfo &b) { return a.FullName < b.FullName; @@ -52,6 +86,7 @@ static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, return false; } +/// Collects the checkers for the supplied \p opt option into \p collected. static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, const llvm::StringMap<size_t> &packageSizes, CheckerOptInfo &opt, CheckerInfoSet &collected) { @@ -101,13 +136,15 @@ void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, } void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, - SmallVectorImpl<CheckerOptInfo> &opts) const { + const AnalyzerOptions &Opts, + DiagnosticsEngine &diags) const { // Sort checkers for efficient collection. llvm::sort(Checkers, checkerNameLT); + llvm::SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(Opts); // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers; - for (auto &i : opts) + for (auto &i : checkerOpts) collectCheckers(Checkers, Packages, i, enabledCheckers); // Initialize the CheckerManager with all enabled checkers. @@ -115,6 +152,15 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, checkerMgr.setCurrentCheckName(CheckName(i->FullName)); i->Initialize(checkerMgr); } + + for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { + if (checkerOpts[i].isUnclaimed()) { + diags.Report(diag::err_unknown_analyzer_checker) + << checkerOpts[i].getName(); + diags.Report(diag::note_suggest_disabling_all_checkers); + } + + } } void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts, @@ -176,13 +222,14 @@ void CheckerRegistry::printHelp(raw_ostream &out, } } -void CheckerRegistry::printList( - raw_ostream &out, SmallVectorImpl<CheckerOptInfo> &opts) const { +void CheckerRegistry::printList(raw_ostream &out, + const AnalyzerOptions &opts) const { llvm::sort(Checkers, checkerNameLT); + llvm::SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(opts); // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers; - for (auto &i : opts) + for (auto &i : checkerOpts) collectCheckers(Checkers, Packages, i, enabledCheckers); for (const auto *i : enabledCheckers) diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index e5a5296e02..196854cb09 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -53,26 +53,28 @@ STATISTIC(NumPathsExplored, // Core analysis engine. //===----------------------------------------------------------------------===// -static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) { +static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts, + SubEngine &subengine) { switch (Opts.getExplorationStrategy()) { - case AnalyzerOptions::ExplorationStrategyKind::DFS: + case ExplorationStrategyKind::DFS: return WorkList::makeDFS(); - case AnalyzerOptions::ExplorationStrategyKind::BFS: + case ExplorationStrategyKind::BFS: return WorkList::makeBFS(); - case AnalyzerOptions::ExplorationStrategyKind::BFSBlockDFSContents: + case ExplorationStrategyKind::BFSBlockDFSContents: return WorkList::makeBFSBlockDFSContents(); - case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirst: + case ExplorationStrategyKind::UnexploredFirst: return WorkList::makeUnexploredFirst(); - case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirstQueue: + case ExplorationStrategyKind::UnexploredFirstQueue: return WorkList::makeUnexploredFirstPriorityQueue(); - default: - llvm_unreachable("Unexpected case"); + case ExplorationStrategyKind::UnexploredFirstLocationQueue: + return WorkList::makeUnexploredFirstPriorityLocationQueue(); } + llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind"); } CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts) - : SubEng(subengine), WList(generateWorkList(Opts)), + : SubEng(subengine), WList(generateWorkList(Opts, subengine)), BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index 5309339168..da7854df1d 100644 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -77,5 +77,10 @@ void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, } } +void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() { + static int index = 0; + return &index; +} + } // namespace ento } // namespace clang diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 96b5931036..b45f93b6dd 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -44,6 +44,9 @@ static const Expr *ignoreTransparentExprs(const Expr *E) { case Stmt::ExprWithCleanupsClass: E = cast<ExprWithCleanups>(E)->getSubExpr(); break; + case Stmt::ConstantExprClass: + E = cast<ConstantExpr>(E)->getSubExpr(); + break; case Stmt::CXXBindTemporaryExprClass: E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); break; @@ -89,6 +92,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::ExprWithCleanupsClass: case Stmt::GenericSelectionExprClass: case Stmt::OpaqueValueExprClass: + case Stmt::ConstantExprClass: case Stmt::ParenExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: llvm_unreachable("Should have been handled by ignoreTransparentExprs"); @@ -189,11 +193,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, // Mark all symbols in the block expr's value live. RSScaner.scan(X); - continue; - } else { - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); } } diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 7d7b88a811..d6bcbb96b5 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -284,15 +284,13 @@ ExplodedNode * const *ExplodedNode::NodeGroup::end() const { } int64_t ExplodedNode::getID(ExplodedGraph *G) const { - Optional<int64_t> Out = G->getAllocator().identifyObject(this); - assert(Out && "Wrong allocator used"); - assert(*Out % alignof(ExplodedNode) == 0 && "Wrong alignment information"); - return *Out / alignof(ExplodedNode); + return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this); } bool ExplodedNode::isTrivial() const { return pred_size() == 1 && succ_size() == 1 && - (*pred_begin())->getState()->getID() == getState()->getID(); + getFirstPred()->getState()->getID() == getState()->getID() && + getFirstPred()->succ_size() == 1; } ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 151865f874..45d0df7ae4 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -98,11 +98,12 @@ STATISTIC(NumMaxBlockCountReachedInInlined, STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); - //===----------------------------------------------------------------------===// // Internal program state traits. //===----------------------------------------------------------------------===// +namespace { + // When modeling a C++ constructor, for a variety of reasons we need to track // the location of the object for the duration of its ConstructionContext. // ObjectsUnderConstruction maps statements within the construction context @@ -137,9 +138,17 @@ public: const ConstructionContextItem &getItem() const { return Impl.first; } const LocationContext *getLocationContext() const { return Impl.second; } + ASTContext &getASTContext() const { + return getLocationContext()->getDecl()->getASTContext(); + } + void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { - OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ',' - << getItem().getKindAsString(); + OS << "(LC" << getLocationContext()->getID() << ','; + if (const Stmt *S = getItem().getStmtOrNull()) + OS << 'S' << S->getID(getASTContext()); + else + OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext()); + OS << ',' << getItem().getKindAsString(); if (getItem().getKind() == ConstructionContextItem::ArgumentKind) OS << " #" << getItem().getIndex(); OS << ") "; @@ -164,6 +173,7 @@ public: return Impl < RHS.Impl; } }; +} // namespace typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> ObjectsUnderConstructionMap; @@ -191,9 +201,9 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), BR(mgr, *this), VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { - unsigned TrimInterval = mgr.options.getGraphTrimInterval(); + unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { - // Enable eager node reclaimation when constructing the ExplodedGraph. + // Enable eager node reclamation when constructing the ExplodedGraph. G.enableNodeReclamation(TrimInterval); } } @@ -673,44 +683,35 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // Process any special transfer function for dead symbols. // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node"); - if (!SymReaper.hasDeadSymbols()) { - // Generate a CleanedNode that has the environment and store cleaned - // up. Since no symbols are dead, we can optimize and not clean out - // the constraint manager. - StmtNodeBuilder Bldr(Pred, Out, *currBldrCtx); - Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, &cleanupTag, K); - - } else { - // Call checkers with the non-cleaned state so that they could query the - // values of the soon to be dead symbols. - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, - DiagnosticStmt, *this, K); - - // For each node in CheckedSet, generate CleanedNodes that have the - // environment, the store, and the constraints cleaned up but have the - // user-supplied states as the predecessors. - StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); - for (const auto I : CheckedSet) { - ProgramStateRef CheckerState = I->getState(); - - // The constraint manager has not been cleaned up yet, so clean up now. - CheckerState = getConstraintManager().removeDeadBindings(CheckerState, - SymReaper); - - assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && - "Checkers are not allowed to modify the Environment as a part of " - "checkDeadSymbols processing."); - assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && - "Checkers are not allowed to modify the Store as a part of " - "checkDeadSymbols processing."); - - // Create a state based on CleanedState with CheckerState GDM and - // generate a transition to that state. - ProgramStateRef CleanedCheckerSt = + // Call checkers with the non-cleaned state so that they could query the + // values of the soon to be dead symbols. + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); + + // For each node in CheckedSet, generate CleanedNodes that have the + // environment, the store, and the constraints cleaned up but have the + // user-supplied states as the predecessors. + StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); + for (const auto I : CheckedSet) { + ProgramStateRef CheckerState = I->getState(); + + // The constraint manager has not been cleaned up yet, so clean up now. + CheckerState = + getConstraintManager().removeDeadBindings(CheckerState, SymReaper); + + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Environment as a part of " + "checkDeadSymbols processing."); + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Store as a part of " + "checkDeadSymbols processing."); + + // Create a state based on CleanedState with CheckerState GDM and + // generate a transition to that state. + ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); - } + Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); } } @@ -753,7 +754,7 @@ void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { NodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef NewState = Pred->getState(); - if(AMgr.options.shouldUnrollLoops()) + if(AMgr.options.ShouldUnrollLoops) NewState = processLoopEnd(S, NewState); LoopExit PP(S, Pred->getLocationContext()); @@ -885,7 +886,7 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, // TODO: We're not evaluating allocators for all cases just yet as // we're not handling the return value correctly, which causes false // positives when the alpha.cplusplus.NewDeleteLeaks check is on. - if (Opts.mayInlineCXXAllocator()) + if (Opts.MayInlineCXXAllocator) VisitCXXNewAllocatorCall(NE, Pred, Dst); else { NodeBuilder Bldr(Pred, Dst, *currBldrCtx); @@ -1032,7 +1033,7 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, MR = V->getAsRegion(); } - // If copy elision has occured, and the constructor corresponding to the + // If copy elision has occurred, and the constructor corresponding to the // destructor was elided, we need to skip the destructor as well. if (isDestructorElided(State, BTE, LC)) { State = cleanupElidedDestructor(State, BTE, LC); @@ -1100,7 +1101,7 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, // This is a fallback solution in case we didn't have a construction // context when we were constructing the temporary. Otherwise the map should // have been populated there. - if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) { + if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) { // In case we don't have temporary destructors in the CFG, do not mark // the initialization - we would otherwise never clean it up. Dst = PreVisit; @@ -1280,6 +1281,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Expr::ConstantExprClass: case Stmt::ExprWithCleanupsClass: // Handled due to fully linearised CFG. break; @@ -1460,7 +1462,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::LambdaExprClass: - if (AMgr.options.shouldInlineLambdas()) { + if (AMgr.options.ShouldInlineLambdas) { Bldr.takeNodes(Pred); VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst); Bldr.addNodes(Dst); @@ -1489,7 +1491,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.takeNodes(Pred); - if (AMgr.options.shouldEagerlyAssume() && + if (AMgr.options.ShouldEagerlyAssume && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); @@ -1753,7 +1755,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::UnaryOperatorClass: { Bldr.takeNodes(Pred); const auto *U = cast<UnaryOperator>(S); - if (AMgr.options.shouldEagerlyAssume() && (U->getOpcode() == UO_LNot)) { + if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U); @@ -1854,7 +1856,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); // If we reach a loop which has a known bound (and meets // other constraints) then consider completely unrolling it. - if(AMgr.options.shouldUnrollLoops()) { + if(AMgr.options.ShouldUnrollLoops) { unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (Term) { @@ -1876,7 +1878,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // maximum number of times, widen the loop. unsigned int BlockCount = nodeBuilder.getContext().blockCount(); if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && - AMgr.options.shouldWidenLoops()) { + AMgr.options.ShouldWidenLoops) { const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (!(Term && (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) @@ -2377,7 +2379,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); Optional<std::pair<SVal, QualType>> VInfo; - if (AMgr.options.shouldInlineLambdas() && DeclRefEx && + if (AMgr.options.ShouldInlineLambdas && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { // Lookup the field of the lambda. @@ -3106,3 +3108,8 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes, llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; return ""; } + +void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { + static int index = 0; + return &index; +} diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 61b7a290e4..7d47cf4f33 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -412,10 +412,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: case CK_ObjCObjectLValueCast: - case CK_ZeroToOCLEvent: - case CK_ZeroToOCLQueue: + case CK_ZeroToOCLOpaqueType: case CK_IntToOCLSampler: - case CK_LValueBitCast: { + case CK_LValueBitCast: + case CK_FixedPointCast: + case CK_FixedPointToBoolean: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; @@ -809,8 +810,9 @@ void ExprEngine:: VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder B(Pred, Dst, *currBldrCtx); - APSInt IV; - if (OOE->EvaluateAsInt(IV, getContext())) { + Expr::EvalResult Result; + if (OOE->EvaluateAsInt(Result, getContext())) { + APSInt IV = Result.Val.getInt(); assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); assert(OOE->getType()->isBuiltinType()); assert(OOE->getType()->getAs<BuiltinType>()->isInteger()); @@ -956,7 +958,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, } case UO_Plus: assert(!U->isGLValue()); - // FALL-THROUGH. + LLVM_FALLTHROUGH; case UO_Deref: case UO_Extension: { handleUOExtension(I, U, Bldr); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 933d54e11c..1f64976a9f 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -159,7 +159,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( return std::make_pair(State, FieldVal); } case ConstructionContext::NewAllocatedObjectKind: { - if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC); const auto *NE = NECC->getCXXNewExpr(); SVal V = *getObjectUnderConstruction(State, NE, LCtx); @@ -210,7 +210,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( llvm_unreachable("Unhandled return value construction context!"); } case ConstructionContext::ElidedTemporaryObjectKind: { - assert(AMgr.getAnalyzerOptions().shouldElideConstructors()); + assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); @@ -426,7 +426,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, } } } - // FALLTHROUGH + LLVM_FALLTHROUGH; case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have @@ -445,7 +445,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } - // FALLTHROUGH + LLVM_FALLTHROUGH; case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, @@ -706,7 +706,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ProgramStateRef State = Pred->getState(); // Retrieve the stored operator new() return value. - if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { symVal = *getObjectUnderConstruction(State, CNE, LCtx); State = finishObjectConstruction(State, CNE, LCtx); } @@ -726,7 +726,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); - if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 66cbebad1c..758195d8d9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -349,7 +349,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { /*WasInlined=*/true); } else if (CE && !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr. - AMgr.getAnalyzerOptions().mayInlineCXXAllocator())) { + AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, *this, /*WasInlined=*/true); } else { @@ -386,7 +386,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, // Do not count the small functions when determining the stack depth. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI); const CFG *CalleeCFG = CalleeADC->getCFG(); - if (CalleeCFG->getNumBlockIDs() > AMgr.options.getAlwaysInlineSize()) + if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize) ++StackDepth; } LCtx = LCtx->getParent(); @@ -683,7 +683,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, : nullptr; if (CC && isa<NewAllocatedObjectConstructionContext>(CC) && - !Opts.mayInlineCXXAllocator()) + !Opts.MayInlineCXXAllocator) return CIP_DisallowedOnce; // FIXME: We don't handle constructors or destructors for arrays properly. @@ -712,7 +712,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, // If we don't handle temporary destructors, we shouldn't inline // their constructors. if (CallOpts.IsTemporaryCtorOrDtor && - !Opts.includeTemporaryDtorsInCFG()) + !Opts.ShouldIncludeTemporaryDtorsInCFG) return CIP_DisallowedOnce; // If we did not find the correct this-region, it would be pointless @@ -743,7 +743,8 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, return CIP_DisallowedOnce; // Allow disabling temporary destructor inlining with a separate option. - if (CallOpts.IsTemporaryCtorOrDtor && !Opts.mayInlineCXXTemporaryDtors()) + if (CallOpts.IsTemporaryCtorOrDtor && + !Opts.MayInlineCXXTemporaryDtors) return CIP_DisallowedOnce; // If we did not find the correct this-region, it would be pointless @@ -754,13 +755,13 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, break; } case CE_CXXAllocator: - if (Opts.mayInlineCXXAllocator()) + if (Opts.MayInlineCXXAllocator) break; // Do not inline allocators until we model deallocators. // This is unfortunate, but basically necessary for smart pointers and such. return CIP_DisallowedAlways; case CE_ObjCMessage: - if (!Opts.mayInlineObjCMethod()) + if (!Opts.MayInlineObjCMethod) return CIP_DisallowedAlways; if (!(Opts.getIPAMode() == IPAK_DynamicDispatch || Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate)) @@ -844,19 +845,19 @@ static bool mayInlineDecl(AnalysisManager &AMgr, if (Ctx.getLangOpts().CPlusPlus) { if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) { // Conditionally control the inlining of template functions. - if (!Opts.mayInlineTemplateFunctions()) + if (!Opts.MayInlineTemplateFunctions) if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) return false; // Conditionally control the inlining of C++ standard library functions. - if (!Opts.mayInlineCXXStandardLibrary()) + if (!Opts.MayInlineCXXStandardLibrary) if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) if (AnalysisDeclContext::isInStdNamespace(FD)) return false; // Conditionally control the inlining of methods on objects that look // like C++ containers. - if (!Opts.mayInlineCXXContainerMethods()) + if (!Opts.MayInlineCXXContainerMethods) if (!AMgr.isInCodeFile(FD->getLocation())) if (isContainerMethod(Ctx, FD)) return false; @@ -865,7 +866,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr, // We don't currently do a good job modeling shared_ptr because we can't // see the reference count, so treating as opaque is probably the best // idea. - if (!Opts.mayInlineCXXSharedPtrDtor()) + if (!Opts.MayInlineCXXSharedPtrDtor) if (isCXXSharedPtrDtor(FD)) return false; } @@ -878,7 +879,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr, return false; // Do not inline large functions. - if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize()) + if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize) return false; // It is possible that the live variables analysis cannot be @@ -946,21 +947,21 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, unsigned StackDepth = 0; examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); if ((StackDepth >= Opts.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize()) + ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize) || IsRecursive)) return false; // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > - Opts.getMaxTimesInlineLarge()) && + Opts.MaxTimesInlineLarge) && CalleeCFG->getNumBlockIDs() >= - Opts.getMinCFGSizeTreatFunctionsAsLarge()) { + Opts.MinCFGSizeTreatFunctionsAsLarge) { NumReachedInlineCountMax++; return false; } if (HowToInline == Inline_Minimal && - (CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize() + (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize || IsRecursive)) return false; diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index d5764a4099..fc82f11769 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -218,7 +218,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, int FD; SmallString<128> Model, ResultPath; - if (!AnalyzerOpts.shouldWriteStableReportFilename()) { + if (!AnalyzerOpts.ShouldWriteStableReportFilename) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); if (std::error_code EC = llvm::sys::fs::make_absolute(Model)) { diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 221b9176de..da368de322 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1175,6 +1175,15 @@ const MemRegion *MemRegion::getBaseRegion() const { return R; } +// getgetMostDerivedObjectRegion gets the region of the root class of a C++ +// class hierarchy. +const MemRegion *MemRegion::getMostDerivedObjectRegion() const { + const MemRegion *R = this; + while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) + R = BR->getSuperRegion(); + return R; +} + bool MemRegion::isSubRegionOf(const MemRegion *) const { return false; } diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index b45dfcfb36..db4cf76578 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/TokenConcatenation.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" @@ -24,20 +25,26 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" + using namespace clang; using namespace ento; using namespace markup; +//===----------------------------------------------------------------------===// +// Declarations of helper classes and functions for emitting bug reports in +// plist format. +//===----------------------------------------------------------------------===// + namespace { class PlistDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; - const LangOptions &LangOpts; + const Preprocessor &PP; + AnalyzerOptions &AnOpts; const bool SupportsCrossFileDiagnostics; - const bool SerializeStatistics; public: PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string& prefix, - const LangOptions &LangOpts, + const Preprocessor &PP, bool supportsMultipleFiles); ~PlistDiagnostics() override {} @@ -59,37 +66,116 @@ namespace { }; } // end anonymous namespace -PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& output, - const LangOptions &LO, - bool supportsMultipleFiles) - : OutputFile(output), - LangOpts(LO), - SupportsCrossFileDiagnostics(supportsMultipleFiles), - SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {} +namespace { -void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, - PP.getLangOpts(), false)); -} +/// A helper class for emitting a single report. +class PlistPrinter { + const FIDMap& FM; + AnalyzerOptions &AnOpts; + const Preprocessor &PP; + llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; + +public: + PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, + const Preprocessor &PP) + : FM(FM), AnOpts(AnOpts), PP(PP) { + } -void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, - PP.getLangOpts(), true)); -} + void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { + ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true); + + // Don't emit a warning about an unused private field. + (void)AnOpts; + } + + /// Print the expansions of the collected macro pieces. + /// + /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one + /// is found through a call piece, etc), it's subpieces are reported, and the + /// piece itself is collected. Call this function after the entire bugpath + /// was reported. + void ReportMacroExpansions(raw_ostream &o, unsigned indent); + +private: + void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P, + unsigned indent, unsigned depth, bool includeControlFlow, + bool isKeyEvent = false) { + switch (P.getKind()) { + case PathDiagnosticPiece::ControlFlow: + if (includeControlFlow) + ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent); + break; + case PathDiagnosticPiece::Call: + ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent, + depth); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth, + isKeyEvent); + break; + case PathDiagnosticPiece::Macro: + ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent, + depth); + break; + case PathDiagnosticPiece::Note: + ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent); + break; + } + } + + void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges, + unsigned indent); + void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); + + void ReportControlFlow(raw_ostream &o, + const PathDiagnosticControlFlowPiece& P, + unsigned indent); + void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, + unsigned indent, unsigned depth, bool isKeyEvent = false); + void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, + unsigned indent, unsigned depth); + void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth); + void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + unsigned indent); +}; + +} // end of anonymous namespace -static void EmitRanges(raw_ostream &o, - const ArrayRef<SourceRange> Ranges, - const FIDMap& FM, - const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent) { +namespace { + +struct ExpansionInfo { + std::string MacroName; + std::string Expansion; + ExpansionInfo(std::string N, std::string E) + : MacroName(std::move(N)), Expansion(std::move(E)) {} +}; + +} // end of anonymous namespace + +static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, + AnalyzerOptions &AnOpts, + const Preprocessor &PP, + const PathPieces &Path); + +/// Print coverage information to output stream {@code o}. +/// May modify the used list of files {@code Fids} by inserting new ones. +static void printCoverage(const PathDiagnostic *D, + unsigned InputIndentLevel, + SmallVectorImpl<FileID> &Fids, + FIDMap &FM, + llvm::raw_fd_ostream &o); + +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP); + +//===----------------------------------------------------------------------===// +// Methods of PlistPrinter. +//===----------------------------------------------------------------------===// + +void PlistPrinter::EmitRanges(raw_ostream &o, + const ArrayRef<SourceRange> Ranges, + unsigned indent) { if (Ranges.empty()) return; @@ -97,6 +183,10 @@ static void EmitRanges(raw_ostream &o, Indent(o, indent) << "<key>ranges</key>\n"; Indent(o, indent) << "<array>\n"; ++indent; + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + for (auto &R : Ranges) EmitRange(o, SM, Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), @@ -105,7 +195,8 @@ static void EmitRanges(raw_ostream &o, Indent(o, indent) << "</array>\n"; } -static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) { +void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message, + unsigned indent) { // Output the text. assert(!Message.empty()); Indent(o, indent) << "<key>extended_message</key>\n"; @@ -119,12 +210,12 @@ static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) { EmitString(o, Message) << '\n'; } -static void ReportControlFlow(raw_ostream &o, - const PathDiagnosticControlFlowPiece& P, - const FIDMap& FM, - const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent) { +void PlistPrinter::ReportControlFlow(raw_ostream &o, + const PathDiagnosticControlFlowPiece& P, + unsigned indent) { + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); Indent(o, indent) << "<dict>\n"; ++indent; @@ -173,13 +264,11 @@ static void ReportControlFlow(raw_ostream &o, Indent(o, indent) << "</dict>\n"; } -static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, - const FIDMap& FM, - const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth, - bool isKeyEvent = false) { +void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, + unsigned indent, unsigned depth, + bool isKeyEvent) { + + const SourceManager &SM = PP.getSourceManager(); Indent(o, indent) << "<dict>\n"; ++indent; @@ -198,7 +287,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, // Output the ranges (if any). ArrayRef<SourceRange> Ranges = P.getRanges(); - EmitRanges(o, Ranges, FM, SM, LangOpts, indent); + EmitRanges(o, Ranges, indent); // Output the call depth. Indent(o, indent) << "<key>depth</key>"; @@ -212,61 +301,80 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, Indent(o, indent); o << "</dict>\n"; } -static void ReportPiece(raw_ostream &o, - const PathDiagnosticPiece &P, - const FIDMap& FM, const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth, - bool includeControlFlow, - bool isKeyEvent = false); - -static void ReportCall(raw_ostream &o, - const PathDiagnosticCallPiece &P, - const FIDMap& FM, const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth) { +void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, + unsigned indent, + unsigned depth) { if (auto callEnter = P.getCallEnterEvent()) - ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, + ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true, P.isLastInMainSourceFile()); ++depth; if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent()) - ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, - indent, depth, true); + ReportPiece(o, *callEnterWithinCaller, indent, depth, + /*includeControlFlow*/ true); for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) - ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); + ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true); --depth; if (auto callExit = P.getCallExitEvent()) - ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); + ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true); } -static void ReportMacro(raw_ostream &o, - const PathDiagnosticMacroPiece& P, - const FIDMap& FM, const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth) { +void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, + const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth) { + MacroPieces.push_back(&P); - for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); - I!=E; ++I) { - ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); + for (PathPieces::const_iterator I = P.subPieces.begin(), + E = P.subPieces.end(); + I != E; ++I) { + ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false); } } -static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, - const FIDMap& FM, - const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth) { +void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { + + for (const PathDiagnosticMacroPiece *P : MacroPieces) { + const SourceManager &SM = PP.getSourceManager(); + ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + // Output the location. + FullSourceLoc L = P->getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P->getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the macro name. + Indent(o, indent) << "<key>name</key>"; + EmitString(o, EI.MacroName) << '\n'; + + // Output what it expands into. + Indent(o, indent) << "<key>expansion</key>"; + EmitString(o, EI.Expansion) << '\n'; + + // Finish up. + --indent; + Indent(o, indent); + o << "</dict>\n"; + } +} + +void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + unsigned indent) { + + const SourceManager &SM = PP.getSourceManager(); Indent(o, indent) << "<dict>\n"; ++indent; @@ -279,7 +387,7 @@ static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, // Output the ranges (if any). ArrayRef<SourceRange> Ranges = P.getRanges(); - EmitRanges(o, Ranges, FM, SM, LangOpts, indent); + EmitRanges(o, Ranges, indent); // Output the text. EmitMessage(o, P.getString(), indent); @@ -289,44 +397,9 @@ static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, Indent(o, indent); o << "</dict>\n"; } -static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, - const FIDMap& FM, const SourceManager &SM, - const LangOptions &LangOpts) { - ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); -} - -static void ReportPiece(raw_ostream &o, - const PathDiagnosticPiece &P, - const FIDMap& FM, const SourceManager &SM, - const LangOptions &LangOpts, - unsigned indent, - unsigned depth, - bool includeControlFlow, - bool isKeyEvent) { - switch (P.getKind()) { - case PathDiagnosticPiece::ControlFlow: - if (includeControlFlow) - ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, - LangOpts, indent); - break; - case PathDiagnosticPiece::Call: - ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, - indent, depth); - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, - indent, depth, isKeyEvent); - break; - case PathDiagnosticPiece::Macro: - ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, - indent, depth); - break; - case PathDiagnosticPiece::Note: - ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts, - indent, depth); - break; - } -} +//===----------------------------------------------------------------------===// +// Static function definitions. +//===----------------------------------------------------------------------===// /// Print coverage information to output stream {@code o}. /// May modify the used list of files {@code Fids} by inserting new ones. @@ -361,6 +434,78 @@ static void printCoverage(const PathDiagnostic *D, assert(IndentLevel == InputIndentLevel); } +static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, + AnalyzerOptions &AnOpts, + const Preprocessor &PP, + const PathPieces &Path) { + PlistPrinter Printer(FM, AnOpts, PP); + assert(std::is_partitioned( + Path.begin(), Path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }) && + "PathDiagnostic is not partitioned so that notes precede the rest"); + + PathPieces::const_iterator FirstNonNote = std::partition_point( + Path.begin(), Path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }); + + PathPieces::const_iterator I = Path.begin(); + + if (FirstNonNote != Path.begin()) { + o << " <key>notes</key>\n" + " <array>\n"; + + for (; I != FirstNonNote; ++I) + Printer.ReportDiag(o, **I); + + o << " </array>\n"; + } + + o << " <key>path</key>\n"; + + o << " <array>\n"; + + for (PathPieces::const_iterator E = Path.end(); I != E; ++I) + Printer.ReportDiag(o, **I); + + o << " </array>\n"; + + if (!AnOpts.ShouldDisplayMacroExpansions) + return; + + o << " <key>macro_expansions</key>\n" + " <array>\n"; + Printer.ReportMacroExpansions(o, /* indent */ 4); + o << " </array>\n"; +} + +//===----------------------------------------------------------------------===// +// Methods of PlistDiagnostics. +//===----------------------------------------------------------------------===// + +PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& output, + const Preprocessor &PP, + bool supportsMultipleFiles) + : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + +void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string& s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, + /*supportsMultipleFiles*/ false)); +} + +void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, + /*supportsMultipleFiles*/ true)); +} void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { @@ -368,17 +513,15 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; - const SourceManager* SM = nullptr; - - if (!Diags.empty()) - SM = &Diags.front()->path.front()->getLocation().getManager(); + const SourceManager& SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); - auto AddPieceFID = [&FM, &Fids, SM](const PathDiagnosticPiece &Piece) { - AddFID(FM, Fids, *SM, Piece.getLocation().asLocation()); + auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) { + AddFID(FM, Fids, SM, Piece.getLocation().asLocation()); ArrayRef<SourceRange> Ranges = Piece.getRanges(); for (const SourceRange &Range : Ranges) { - AddFID(FM, Fids, *SM, Range.getBegin()); - AddFID(FM, Fids, *SM, Range.getEnd()); + AddFID(FM, Fids, SM, Range.getBegin()); + AddFID(FM, Fids, SM, Range.getEnd()); } }; @@ -437,39 +580,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <dict>\n"; const PathDiagnostic *D = *DI; - const PathPieces &PP = D->path; - - assert(std::is_partitioned( - PP.begin(), PP.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }) && - "PathDiagnostic is not partitioned so that notes precede the rest"); - - PathPieces::const_iterator FirstNonNote = std::partition_point( - PP.begin(), PP.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }); - - PathPieces::const_iterator I = PP.begin(); - - if (FirstNonNote != PP.begin()) { - o << " <key>notes</key>\n" - " <array>\n"; - - for (; I != FirstNonNote; ++I) - ReportDiag(o, **I, FM, *SM, LangOpts); - - o << " </array>\n"; - } - - o << " <key>path</key>\n"; - - o << " <array>\n"; - - for (PathPieces::const_iterator E = PP.end(); I != E; ++I) - ReportDiag(o, **I, FM, *SM, LangOpts); - - o << " </array>\n"; + printBugPath(o, FM, AnOpts, PP, D->path); // Output the bug type and bug category. o << " <key>description</key>"; @@ -484,12 +595,12 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); - FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() + FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D->getLocation().asLocation()), - *SM); + SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); - EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), + EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), DeclWithIssue, LangOpts)) << '\n'; @@ -534,16 +645,16 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // site and the end of scope (leak report location). if (UPDLoc.isValid()) { FullSourceLoc UFunL( - SM->getExpansionLoc( + SM.getExpansionLoc( D->getUniqueingDecl()->getBody()->getBeginLoc()), - *SM); + SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { - FullSourceLoc FunL(SM->getExpansionLoc(Body->getBeginLoc()), *SM); + FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; @@ -555,7 +666,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Output the location of the bug. o << " <key>location</key>\n"; - EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); + EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { @@ -590,10 +701,10 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>files</key>\n" " <array>\n"; for (FileID FID : Fids) - EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; + EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; o << " </array>\n"; - if (llvm::AreStatisticsEnabled() && SerializeStatistics) { + if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) { o << " <key>statistics</key>\n"; std::string stats; llvm::raw_string_ostream os(stats); @@ -605,3 +716,402 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Finish. o << "</dict>\n</plist>"; } + +//===----------------------------------------------------------------------===// +// Declarations of helper functions and data structures for expanding macros. +//===----------------------------------------------------------------------===// + +namespace { + +using ExpArgTokens = llvm::SmallVector<Token, 2>; + +/// Maps unexpanded macro arguments to expanded arguments. A macro argument may +/// need to expanded further when it is nested inside another macro. +class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> { +public: + void expandFromPrevMacro(const MacroArgMap &Super); +}; + +struct MacroNameAndArgs { + std::string Name; + const MacroInfo *MI = nullptr; + MacroArgMap Args; + + MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M) + : Name(std::move(N)), MI(MI), Args(std::move(M)) {} +}; + +class TokenPrinter { + llvm::raw_ostream &OS; + const Preprocessor &PP; + + Token PrevTok, PrevPrevTok; + TokenConcatenation ConcatInfo; + +public: + TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP) + : OS(OS), PP(PP), ConcatInfo(PP) { + PrevTok.setKind(tok::unknown); + PrevPrevTok.setKind(tok::unknown); + } + + void printToken(const Token &Tok); +}; + +} // end of anonymous namespace + +/// The implementation method of getMacroExpansion: It prints the expansion of +/// a macro to \p Printer, and returns with the name of the macro. +/// +/// Since macros can be nested in one another, this function may call itself +/// recursively. +/// +/// Unfortunately, macro arguments have to expanded manually. To understand why, +/// observe the following example: +/// +/// #define PRINT(x) print(x) +/// #define DO_SOMETHING(str) PRINT(str) +/// +/// DO_SOMETHING("Cute panda cubs."); +/// +/// As we expand the last line, we'll immediately replace PRINT(str) with +/// print(x). The information that both 'str' and 'x' refers to the same string +/// is an information we have to forward, hence the argument \p PrevArgs. +static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs); + +/// Retrieves the name of the macro and what it's arguments expand into +/// at \p ExpanLoc. +/// +/// For example, for the following macro expansion: +/// +/// #define SET_TO_NULL(x) x = 0 +/// #define NOT_SUSPICIOUS(a) \ +/// { \ +/// int b = 0; \ +/// } \ +/// SET_TO_NULL(a) +/// +/// int *ptr = new int(4); +/// NOT_SUSPICIOUS(&ptr); +/// *ptr = 5; +/// +/// When \p ExpanLoc references the last line, the macro name "NOT_SUSPICIOUS" +/// and the MacroArgMap map { (a, &ptr) } will be returned. +/// +/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of +/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map +/// { (x, a) } will be returned. +static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, + const Preprocessor &PP); + +/// Retrieves the ')' token that matches '(' \p It points to. +static MacroInfo::tokens_iterator getMatchingRParen( + MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End); + +/// Retrieves the macro info for \p II refers to at \p Loc. This is important +/// because macros can be redefined or undefined. +static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, + const SourceManager &SM, + const IdentifierInfo *II, + SourceLocation Loc); + +//===----------------------------------------------------------------------===// +// Definitions of helper functions and methods for expanding macros. +//===----------------------------------------------------------------------===// + +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP) { + + llvm::SmallString<200> ExpansionBuf; + llvm::raw_svector_ostream OS(ExpansionBuf); + TokenPrinter Printer(OS, PP); + std::string MacroName = + getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}); + return { MacroName, OS.str() }; +} + +static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs) { + + const SourceManager &SM = PP.getSourceManager(); + + MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); + + // Manually expand its arguments from the previous macro. + Info.Args.expandFromPrevMacro(PrevArgs); + + // Iterate over the macro's tokens and stringify them. + for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E; + ++It) { + Token T = *It; + + // If this token is not an identifier, we only need to print it. + if (T.isNot(tok::identifier)) { + Printer.printToken(T); + continue; + } + + const auto *II = T.getIdentifierInfo(); + assert(II && + "This token is an identifier but has no IdentifierInfo!"); + + // If this token is a macro that should be expanded inside the current + // macro. + if (const MacroInfo *MI = + getMacroInfoForLocation(PP, SM, II, T.getLocation())) { + getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args); + + // If this is a function-like macro, skip its arguments, as + // getExpandedMacro() already printed them. If this is the case, let's + // first jump to the '(' token. + if (MI->getNumParams() != 0) + It = getMatchingRParen(++It, E); + continue; + } + + // If this token is the current macro's argument, we should expand it. + auto ArgMapIt = Info.Args.find(II); + if (ArgMapIt != Info.Args.end()) { + for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(), + ArgEnd = ArgMapIt->second.end(); + ArgIt != ArgEnd; ++ArgIt) { + + // These tokens may still be macros, if that is the case, handle it the + // same way we did above. + const auto *ArgII = ArgIt->getIdentifierInfo(); + if (!ArgII) { + Printer.printToken(*ArgIt); + continue; + } + + const auto *MI = PP.getMacroInfo(ArgII); + if (!MI) { + Printer.printToken(*ArgIt); + continue; + } + + getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, + Info.Args); + if (MI->getNumParams() != 0) + ArgIt = getMatchingRParen(++ArgIt, ArgEnd); + } + continue; + } + + // If control reached here, then this token isn't a macro identifier, nor an + // unexpanded macro argument that we need to handle, print it. + Printer.printToken(T); + } + + return Info.Name; +} + +static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, + const Preprocessor &PP) { + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + // First, we create a Lexer to lex *at the expansion location* the tokens + // referring to the macro's name and its arguments. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc); + const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first); + const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second; + + Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, + MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd()); + + // Acquire the macro's name. + Token TheTok; + RawLexer.LexFromRawLexer(TheTok); + + std::string MacroName = PP.getSpelling(TheTok); + + const auto *II = PP.getIdentifierInfo(MacroName); + assert(II && "Failed to acquire the IndetifierInfo for the macro!"); + + const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); + assert(MI && "The macro must've been defined at it's expansion location!"); + + // Acquire the macro's arguments. + // + // The rough idea here is to lex from the first left parentheses to the last + // right parentheses, and map the macro's unexpanded arguments to what they + // will be expanded to. An expanded macro argument may contain several tokens + // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at + // which point we start lexing the next argument or finish. + ArrayRef<const IdentifierInfo *> MacroArgs = MI->params(); + if (MacroArgs.empty()) + return { MacroName, MI, {} }; + + RawLexer.LexFromRawLexer(TheTok); + assert(TheTok.is(tok::l_paren) && + "The token after the macro's identifier token should be '('!"); + + MacroArgMap Args; + + // When the macro's argument is a function call, like + // CALL_FN(someFunctionName(param1, param2)) + // we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide + // actual macro arguments, or do not represent the macro argument's closing + // parentheses, so we'll count how many parentheses aren't closed yet. + // If ParanthesesDepth + // * = 0, then there are no more arguments to lex. + // * = 1, then if we find a tok::comma, we can start lexing the next arg. + // * > 1, then tok::comma is a part of the current arg. + int ParenthesesDepth = 1; + + // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren, + // even if we lex a tok::comma and ParanthesesDepth == 1. + const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__"); + + for (const IdentifierInfo *UnexpArgII : MacroArgs) { + MacroArgMap::mapped_type ExpandedArgTokens; + + // One could also simply not supply a single argument to __VA_ARGS__ -- this + // results in a preprocessor warning, but is not an error: + // #define VARIADIC(ptr, ...) \ + // someVariadicTemplateFunction(__VA_ARGS__) + // + // int *ptr; + // VARIADIC(ptr); // Note that there are no commas, this isn't just an + // // empty parameter -- there are no parameters for '...'. + // In any other case, ParenthesesDepth mustn't be 0 here. + if (ParenthesesDepth != 0) { + + // Lex the first token of the next macro parameter. + RawLexer.LexFromRawLexer(TheTok); + + while (!(ParenthesesDepth == 1 && + (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { + assert(TheTok.isNot(tok::eof) && + "EOF encountered while looking for expanded macro args!"); + + if (TheTok.is(tok::l_paren)) + ++ParenthesesDepth; + + if (TheTok.is(tok::r_paren)) + --ParenthesesDepth; + + if (ParenthesesDepth == 0) + break; + + if (TheTok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(TheTok); + + ExpandedArgTokens.push_back(TheTok); + RawLexer.LexFromRawLexer(TheTok); + } + } else { + assert(UnexpArgII == __VA_ARGS__II); + } + + Args.emplace(UnexpArgII, std::move(ExpandedArgTokens)); + } + + assert(TheTok.is(tok::r_paren) && + "Expanded macro argument acquisition failed! After the end of the loop" + " this token should be ')'!"); + + return { MacroName, MI, Args }; +} + +static MacroInfo::tokens_iterator getMatchingRParen( + MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End) { + + assert(It->is(tok::l_paren) && "This token should be '('!"); + + // Skip until we find the closing ')'. + int ParenthesesDepth = 1; + while (ParenthesesDepth != 0) { + ++It; + + assert(It->isNot(tok::eof) && + "Encountered EOF while attempting to skip macro arguments!"); + assert(It != End && + "End of the macro definition reached before finding ')'!"); + + if (It->is(tok::l_paren)) + ++ParenthesesDepth; + + if (It->is(tok::r_paren)) + --ParenthesesDepth; + } + return It; +} + +static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, + const SourceManager &SM, + const IdentifierInfo *II, + SourceLocation Loc) { + + const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II); + if (!MD) + return nullptr; + + return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo(); +} + +void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) { + + for (value_type &Pair : *this) { + ExpArgTokens &CurrExpArgTokens = Pair.second; + + // For each token in the expanded macro argument. + auto It = CurrExpArgTokens.begin(); + while (It != CurrExpArgTokens.end()) { + if (It->isNot(tok::identifier)) { + ++It; + continue; + } + + const auto *II = It->getIdentifierInfo(); + assert(II); + + // Is this an argument that "Super" expands further? + if (!Super.count(II)) { + ++It; + continue; + } + + const ExpArgTokens &SuperExpArgTokens = Super.at(II); + + It = CurrExpArgTokens.insert( + It, SuperExpArgTokens.begin(), SuperExpArgTokens.end()); + std::advance(It, SuperExpArgTokens.size()); + It = CurrExpArgTokens.erase(It); + } + } +} + +void TokenPrinter::printToken(const Token &Tok) { + // If this is the first token to be printed, don't print space. + if (PrevTok.isNot(tok::unknown)) { + // If the tokens were already space separated, or if they must be to avoid + // them being implicitly pasted, add a space between them. + if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, + Tok)) { + // AvoidConcat doesn't check for ##, don't print a space around it. + if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) { + OS << ' '; + } + } + } + + if (!Tok.isOneOf(tok::hash, tok::hashhash)) { + if (PrevTok.is(tok::hash)) + OS << '\"' << PP.getSpelling(Tok) << '\"'; + else + OS << PP.getSpelling(Tok); + } + + PrevPrevTok = PrevTok; + PrevTok = Tok; +} diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 93d08b1aa5..ceb4fbdf78 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -70,10 +70,7 @@ ProgramState::~ProgramState() { } int64_t ProgramState::getID() const { - Optional<int64_t> Out = getStateManager().Alloc.identifyObject(this); - assert(Out && "Wrong allocator used"); - assert(*Out % alignof(ProgramState) == 0 && "Wrong alignment information"); - return *Out / alignof(ProgramState); + return getStateManager().Alloc.identifyKnownAlignedObject<ProgramState>(this); } ProgramStateManager::ProgramStateManager(ASTContext &Ctx, @@ -662,22 +659,12 @@ bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const return S.scan(val); } -bool ProgramState::scanReachableSymbols(const SVal *I, const SVal *E, - SymbolVisitor &visitor) const { +bool ProgramState::scanReachableSymbols( + llvm::iterator_range<region_iterator> Reachable, + SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) - return false; - } - return true; -} - -bool ProgramState::scanReachableSymbols(const MemRegion * const *I, - const MemRegion * const *E, - SymbolVisitor &visitor) const { - ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) + for (const MemRegion *R : Reachable) { + if (!S.scan(R)) return false; } return true; @@ -845,4 +832,3 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { return false; } - diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index e8c7bdbde3..d9b58d0f51 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -399,7 +399,7 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State, for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { SymbolRef Sym = I.getKey(); - if (SymReaper.maybeDead(Sym)) { + if (SymReaper.isDead(Sym)) { Changed = true; CR = CRFactory.remove(CR, Sym); } diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index f99853f070..146dc20ad0 100644 --- a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -200,6 +200,11 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym, } } +void *ProgramStateTrait<ConstraintRange>::GDMIndex() { + static int Index; + return &Index; +} + } // end of namespace ento } // end of namespace clang diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index aa3b4044dc..f5eb9b5e72 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -350,7 +350,7 @@ public: if (SubEngine *Eng = StateMgr.getOwningEngine()) { AnalyzerOptions &Options = Eng->getAnalysisManager().options; SmallStructLimit = - Options.getOptionAsInteger("region-store-small-struct-limit", 2); + Options.RegionStoreSmallStructLimit; } } @@ -1330,11 +1330,11 @@ RegionStoreManager::invalidateRegions(Store store, case GFK_All: B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - // FALLTHROUGH + LLVM_FALLTHROUGH; case GFK_SystemOnly: B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - // FALLTHROUGH + LLVM_FALLTHROUGH; case GFK_None: break; } @@ -2571,24 +2571,9 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. - if (W.isVisited(Base)) - continue; - - // Remove the dead entry. - B = B.remove(Base); - - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) - SymReaper.maybeDead(SymR->getSymbol()); - - // Mark all non-live symbols that this binding references as dead. - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - SVal X = CI.getData(); - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); - } + // Otherwise, remove the dead entry. + if (!W.isVisited(Base)) + B = B.remove(Base); } return StoreRef(B.asStore(), *this); diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index c16b141e38..3bbb4c7f9a 100644 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// // // This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation and Cocoa -// on (Mac OS X). +// implements a reference count checker for Core Foundation, Cocoa +// and OSObject (on Mac OS X). // //===----------------------------------------------------------------------===// @@ -24,6 +24,31 @@ using namespace clang; using namespace ento; +template <class T> +constexpr static bool isOneOf() { + return false; +} + +/// Helper function to check whether the class is one of the +/// rest of varargs. +template <class T, class P, class... ToCompare> +constexpr static bool isOneOf() { + return std::is_same<T, P>::value || isOneOf<T, ToCompare...>(); +} + +template <class T> bool RetainSummaryManager::isAttrEnabled() { + if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr, + CFReturnsNotRetainedAttr, NSConsumedAttr, NSConsumesSelfAttr, + NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr, + NSReturnsNotRetainedAttr>()) { + return TrackObjCAndCFObjects; + } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr, + OSReturnsNotRetainedAttr, OSReturnsRetainedAttr>()) { + return TrackOSObjects; + } + llvm_unreachable("Unexpected attribute passed"); +} + ArgEffects RetainSummaryManager::getArgEffects() { ArgEffects AE = ScratchArgs; ScratchArgs = AF.getEmptyMap(); @@ -65,6 +90,10 @@ static bool isOSObjectSubclass(const Decl *D) { return isSubclass(D, "OSObject"); } +static bool isOSObjectDynamicCast(StringRef S) { + return S == "safeMetaCast"; +} + static bool isOSIteratorSubclass(const Decl *D) { return isSubclass(D, "OSIterator"); } @@ -94,35 +123,78 @@ static bool isMakeCollectable(StringRef FName) { return FName.contains_lower("MakeCollectable"); } +/// A function is OSObject related if it is declared on a subclass +/// of OSObject, or any of the parameters is a subclass of an OSObject. +static bool isOSObjectRelated(const CXXMethodDecl *MD) { + if (isOSObjectSubclass(MD->getParent())) + return true; + + for (ParmVarDecl *Param : MD->parameters()) { + QualType PT = Param->getType()->getPointeeType(); + if (!PT.isNull()) + if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl()) + if (isOSObjectSubclass(RD)) + return true; + } + + return false; +} + const RetainSummary * -RetainSummaryManager::generateSummary(const FunctionDecl *FD, - bool &AllowAnnotations) { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) { - return getPersistentStopSummary(); +RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, + StringRef FName, QualType RetTy) { + if (RetTy->isPointerType()) { + const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); + if (PD && isOSObjectSubclass(PD)) { + if (const IdentifierInfo *II = FD->getIdentifier()) { + if (isOSObjectDynamicCast(II->getName())) + return getDefaultSummary(); + + // All objects returned with functions *not* starting with + // get, or iterators, are returned at +1. + if ((!II->getName().startswith("get") && + !II->getName().startswith("Get")) || + isOSIteratorSubclass(PD)) { + return getOSSummaryCreateRule(FD); + } else { + return getOSSummaryGetRule(FD); + } + } + } } - // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the - // function's type. - const FunctionType *FT = FD->getType()->getAs<FunctionType>(); - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return getDefaultSummary(); + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + const CXXRecordDecl *Parent = MD->getParent(); + if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { + if (FName == "release") + return getOSSummaryReleaseRule(FD); - StringRef FName = II->getName(); + if (FName == "retain") + return getOSSummaryRetainRule(FD); - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - FName = FName.substr(FName.find_first_not_of('_')); + if (FName == "free") + return getOSSummaryFreeRule(FD); + + if (MD->getOverloadedOperator() == OO_New) + return getOSSummaryCreateRule(MD); + } + } + + return nullptr; +} + +const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject( + const FunctionDecl *FD, + StringRef FName, + QualType RetTy, + const FunctionType *FT, + bool &AllowAnnotations) { - // Inspect the result type. - QualType RetTy = FT->getReturnType(); std::string RetTyName = RetTy.getAsString(); // FIXME: This should all be refactored into a chain of "summary lookup" // filters. assert(ScratchArgs.isEmpty()); - if (FName == "pthread_create" || FName == "pthread_setspecific") { // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. // This will be addressed better with IPA. @@ -213,28 +285,11 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, if (RetTy->isPointerType()) { - const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); - if (TrackOSObjects && PD && isOSObjectSubclass(PD)) { - if (const IdentifierInfo *II = FD->getIdentifier()) { - - // All objects returned with functions starting with "get" are getters. - if (II->getName().startswith("get")) { - - // ...except for iterators. - if (isOSIteratorSubclass(PD)) - return getOSSummaryCreateRule(FD); - return getOSSummaryGetRule(FD); - } else { - return getOSSummaryCreateRule(FD); - } - } - } - // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { if (isRetain(FD, FName)) { - // CFRetain isn't supposed to be annotated. However, this may as well - // be a user-made "safe" CFRetain function that is incorrectly + // CFRetain isn't supposed to be annotated. However, this may as + // well be a user-made "safe" CFRetain function that is incorrectly // annotated as cf_returns_retained due to lack of better options. // We want to ignore such annotation. AllowAnnotations = false; @@ -275,21 +330,9 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } } - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { - if (FName == "release") - return getOSSummaryReleaseRule(FD); - - if (FName == "retain") - return getOSSummaryRetainRule(FD); - } - } - // Check for release functions, the only kind of functions that we care // about that don't return a pointer type. - if (FName.size() >= 2 && FName[0] == 'C' && - (FName[1] == 'F' || FName[1] == 'G')) { + if (FName.startswith("CG") || FName.startswith("CF")) { // Test for 'CGCF'. FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); @@ -324,13 +367,41 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } } - if (isa<CXXMethodDecl>(FD)) { + return nullptr; +} - // Stop tracking arguments passed to C++ methods, as those might be - // wrapping smart pointers. - return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, - DoNothing); - } +const RetainSummary * +RetainSummaryManager::generateSummary(const FunctionDecl *FD, + bool &AllowAnnotations) { + // We generate "stop" summaries for implicitly defined functions. + if (FD->isImplicit()) + return getPersistentStopSummary(); + + const IdentifierInfo *II = FD->getIdentifier(); + + StringRef FName = II ? II->getName() : ""; + + // Strip away preceding '_'. Doing this here will effect all the checks + // down below. + FName = FName.substr(FName.find_first_not_of('_')); + + // Inspect the result type. Strip away any typedefs. + const auto *FT = FD->getType()->getAs<FunctionType>(); + QualType RetTy = FT->getReturnType(); + + if (TrackOSObjects) + if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy)) + return S; + + if (TrackObjCAndCFObjects) + if (const RetainSummary *S = + getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations)) + return S; + + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) + if (!(TrackOSObjects && isOSObjectRelated(MD))) + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, + DoNothing); return getDefaultSummary(); } @@ -461,16 +532,14 @@ RetainSummaryManager::getSummary(const CallEvent &Call, const RetainSummary *Summ; switch (Call.getKind()) { case CE_Function: - Summ = getFunctionSummary(cast<SimpleFunctionCall>(Call).getDecl()); - break; case CE_CXXMember: - Summ = getFunctionSummary(cast<CXXMemberCall>(Call).getDecl()); - break; case CE_CXXMemberOperator: - case CE_Block: case CE_CXXConstructor: - case CE_CXXDestructor: case CE_CXXAllocator: + Summ = getFunctionSummary(cast_or_null<FunctionDecl>(Call.getDecl())); + break; + case CE_Block: + case CE_CXXDestructor: // FIXME: These calls are currently unsupported. return getPersistentStopSummary(); case CE_ObjCMessage: { @@ -503,26 +572,21 @@ bool RetainSummaryManager::isTrustedReferenceCountImplementation( return hasRCAnnotation(FD, "rc_ownership_trusted_implementation"); } -bool RetainSummaryManager::canEval(const CallExpr *CE, - const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation) { - // For now, we're only handling the functions that return aliases of their - // arguments: CFRetain (and its families). - // Eventually we should add other functions we can model entirely, - // such as CFRelease, which don't invalidate their arguments or globals. - if (CE->getNumArgs() != 1) - return false; +Optional<RetainSummaryManager::BehaviorSummary> +RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, + bool &hasTrustedImplementationAnnotation) { IdentifierInfo *II = FD->getIdentifier(); if (!II) - return false; + return None; StringRef FName = II->getName(); FName = FName.substr(FName.find_first_not_of('_')); QualType ResultTy = CE->getCallReturnType(Ctx); if (ResultTy->isObjCIdType()) { - return II->isStr("NSMakeCollectable"); + if (II->isStr("NSMakeCollectable")) + return BehaviorSummary::Identity; } else if (ResultTy->isPointerType()) { // Handle: (CF|CG|CV)Retain // CFAutorelease @@ -530,18 +594,34 @@ bool RetainSummaryManager::canEval(const CallExpr *CE, if (cocoa::isRefType(ResultTy, "CF", FName) || cocoa::isRefType(ResultTy, "CG", FName) || cocoa::isRefType(ResultTy, "CV", FName)) - return isRetain(FD, FName) || isAutorelease(FD, FName) || - isMakeCollectable(FName); + if (isRetain(FD, FName) || isAutorelease(FD, FName) || + isMakeCollectable(FName)) + return BehaviorSummary::Identity; + + // safeMetaCast is called by OSDynamicCast. + // We assume that OSDynamicCast is either an identity (cast is OK, + // the input was non-zero), + // or that it returns zero (when the cast failed, or the input + // was zero). + if (TrackOSObjects && isOSObjectDynamicCast(FName)) { + return BehaviorSummary::IdentityOrZero; + } const FunctionDecl* FDD = FD->getDefinition(); if (FDD && isTrustedReferenceCountImplementation(FDD)) { hasTrustedImplementationAnnotation = true; - return true; + return BehaviorSummary::Identity; } } - return false; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + const CXXRecordDecl *Parent = MD->getParent(); + if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) + if (FName == "release" || FName == "retain") + return BehaviorSummary::NoOp; + } + return None; } const RetainSummary * @@ -585,6 +665,14 @@ RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { } const RetainSummary * +RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNoRet(), + /*ReceiverEff=*/DoNothing, + /*DefaultEff=*/DoNothing, + /*ThisEff=*/Dealloc); +} + +const RetainSummary * RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { return getPersistentSummary(RetEffect::MakeOwned(RetEffect::OS)); } @@ -618,7 +706,7 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { Optional<RetEffect> RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, const Decl *D) { - if (cocoa::isCocoaObjectRef(RetTy)) { + if (TrackObjCAndCFObjects && cocoa::isCocoaObjectRef(RetTy)) { if (D->hasAttr<NSReturnsRetainedAttr>()) return ObjCAllocRetE; @@ -630,17 +718,73 @@ RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, return None; } - if (D->hasAttr<CFReturnsRetainedAttr>()) + if (hasEnabledAttr<CFReturnsRetainedAttr>(D)) { return RetEffect::MakeOwned(RetEffect::CF); - else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) + } else if (hasEnabledAttr<OSReturnsRetainedAttr>(D)) { + return RetEffect::MakeOwned(RetEffect::OS); + } else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) { return RetEffect::MakeOwned(RetEffect::Generalized); + } - if (D->hasAttr<CFReturnsNotRetainedAttr>()) + if (hasEnabledAttr<CFReturnsNotRetainedAttr>(D)) { return RetEffect::MakeNotOwned(RetEffect::CF); + } else if (hasEnabledAttr<OSReturnsNotRetainedAttr>(D)) { + return RetEffect::MakeNotOwned(RetEffect::OS); + } else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) { + return RetEffect::MakeNotOwned(RetEffect::Generalized); + } + + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) + for (const auto *PD : MD->overridden_methods()) + if (auto RE = getRetEffectFromAnnotations(RetTy, PD)) + return RE; return None; } +bool RetainSummaryManager::applyFunctionParamAnnotationEffect(const ParmVarDecl *pd, + unsigned parm_idx, + const FunctionDecl *FD, + ArgEffects::Factory &AF, + RetainSummaryTemplate &Template) { + if (hasEnabledAttr<NSConsumedAttr>(pd)) { + Template->addArg(AF, parm_idx, DecRefMsg); + return true; + } else if (hasEnabledAttr<CFConsumedAttr>(pd) || + hasEnabledAttr<OSConsumedAttr>(pd) || + hasRCAnnotation(pd, "rc_ownership_consumed")) { + Template->addArg(AF, parm_idx, DecRef); + return true; + } else if (hasEnabledAttr<CFReturnsRetainedAttr>(pd) || + hasRCAnnotation(pd, "rc_ownership_returns_retained")) { + QualType PointeeTy = pd->getType()->getPointeeType(); + if (!PointeeTy.isNull()) { + if (coreFoundation::isCFObjectRef(PointeeTy)) { + Template->addArg(AF, parm_idx, RetainedOutParameter); + return true; + } + } + } else if (hasEnabledAttr<CFReturnsNotRetainedAttr>(pd)) { + QualType PointeeTy = pd->getType()->getPointeeType(); + if (!PointeeTy.isNull()) { + if (coreFoundation::isCFObjectRef(PointeeTy)) { + Template->addArg(AF, parm_idx, UnretainedOutParameter); + return true; + } + } + } else { + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + for (const auto *OD : MD->overridden_methods()) { + const ParmVarDecl *OP = OD->parameters()[parm_idx]; + if (applyFunctionParamAnnotationEffect(OP, parm_idx, OD, AF, Template)) + return true; + } + } + } + + return false; +} + void RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD) { @@ -652,31 +796,18 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, // Effects on the parameters. unsigned parm_idx = 0; - for (FunctionDecl::param_const_iterator pi = FD->param_begin(), + for (auto pi = FD->param_begin(), pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->hasAttr<NSConsumedAttr>()) - Template->addArg(AF, parm_idx, DecRefMsg); - else if (pd->hasAttr<CFConsumedAttr>() || - hasRCAnnotation(pd, "rc_ownership_consumed")) - Template->addArg(AF, parm_idx, DecRef); - else if (pd->hasAttr<CFReturnsRetainedAttr>() || - hasRCAnnotation(pd, "rc_ownership_returns_retained")) { - QualType PointeeTy = pd->getType()->getPointeeType(); - if (!PointeeTy.isNull()) - if (coreFoundation::isCFObjectRef(PointeeTy)) - Template->addArg(AF, parm_idx, RetainedOutParameter); - } else if (pd->hasAttr<CFReturnsNotRetainedAttr>()) { - QualType PointeeTy = pd->getType()->getPointeeType(); - if (!PointeeTy.isNull()) - if (coreFoundation::isCFObjectRef(PointeeTy)) - Template->addArg(AF, parm_idx, UnretainedOutParameter); - } + applyFunctionParamAnnotationEffect(pd, parm_idx, FD, AF, Template); } QualType RetTy = FD->getReturnType(); if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD)) Template->setRetEffect(*RetE); + + if (hasEnabledAttr<OSConsumesThisAttr>(FD)) + Template->setThisEffect(DecRef); } void @@ -694,13 +825,12 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, // Effects on the parameters. unsigned parm_idx = 0; - for (ObjCMethodDecl::param_const_iterator - pi=MD->param_begin(), pe=MD->param_end(); + for (auto pi=MD->param_begin(), pe=MD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->hasAttr<NSConsumedAttr>()) + if (pd->hasAttr<NSConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRefMsg); - else if (pd->hasAttr<CFConsumedAttr>()) { + } else if (pd->hasAttr<CFConsumedAttr>() || pd->hasAttr<OSConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRef); } else if (pd->hasAttr<CFReturnsRetainedAttr>()) { QualType PointeeTy = pd->getType()->getPointeeType(); @@ -848,6 +978,10 @@ RetainSummaryManager::getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries) { + // Objective-C method summaries are only applicable to ObjC and CF objects. + if (!TrackObjCAndCFObjects) + return getDefaultSummary(); + // Look up a summary in our summary cache. const RetainSummary *Summ = CachedSummaries.find(ID, S); @@ -958,7 +1092,9 @@ void RetainSummaryManager::InitializeMethodSummaries() { CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { ASTContext &Ctx = MD->getASTContext(); LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false); + RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, + /*TrackNSAndCFObjects=*/true, + /*TrackOSObjects=*/false); const RetainSummary *S = M.getMethodSummary(MD); CallEffects CE(S->getRetEffect()); CE.Receiver = S->getReceiverEffect(); @@ -972,7 +1108,9 @@ CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { CallEffects CallEffects::getEffect(const FunctionDecl *FD) { ASTContext &Ctx = FD->getASTContext(); LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false); + RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, + /*TrackNSAndCFObjects=*/true, + /*TrackOSObjects=*/false); const RetainSummary *S = M.getFunctionSummary(FD); CallEffects CE(S->getRetEffect()); unsigned N = FD->param_size(); diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index ef3d5b7665..617c4ba27d 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -362,9 +362,9 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { return None; ASTContext &Ctx = getContext(); - llvm::APSInt Result; + Expr::EvalResult Result; if (E->EvaluateAsInt(Result, Ctx)) - return makeIntVal(Result); + return makeIntVal(Result.Val.getInt()); if (Loc::isLocType(E->getType())) if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) @@ -385,7 +385,7 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op, // instead of generating an Unknown value and propagate the taint info to it. const unsigned MaxComp = StateMgr.getOwningEngine() ->getAnalysisManager() - .options.getMaxSymbolComplexity(); + .options.MaxSymbolComplexity; if (symLHS && symRHS && (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 559ca2c984..b32be9e82d 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -85,7 +85,7 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) - return X->getLoc().getAsLocSymbol(); + return X->getLoc().getAsLocSymbol(IncludeBaseRegions); if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion *R = X->getRegion(); diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp new file mode 100644 index 0000000000..36046f0cfd --- /dev/null +++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -0,0 +1,332 @@ +//===--- SarifDiagnostics.cpp - Sarif Diagnostics for Paths -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the SarifDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace clang; +using namespace ento; + +namespace { +class SarifDiagnostics : public PathDiagnosticConsumer { + std::string OutputFile; + +public: + SarifDiagnostics(AnalyzerOptions &, const std::string &Output) + : OutputFile(Output) {} + ~SarifDiagnostics() override = default; + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *FM) override; + + StringRef getName() const override { return "SarifDiagnostics"; } + PathGenerationScheme getGenerationScheme() const override { return Minimal; } + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { return true; } +}; +} // end anonymous namespace + +void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &Output, + const Preprocessor &) { + C.push_back(new SarifDiagnostics(AnalyzerOpts, Output)); +} + +static StringRef getFileName(const FileEntry &FE) { + StringRef Filename = FE.tryGetRealPathName(); + if (Filename.empty()) + Filename = FE.getName(); + return Filename; +} + +static std::string percentEncodeURICharacter(char C) { + // RFC 3986 claims alpha, numeric, and this handful of + // characters are not reserved for the path component and + // should be written out directly. Otherwise, percent + // encode the character and write that out instead of the + // reserved character. + if (llvm::isAlnum(C) || + StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C)) + return std::string(&C, 1); + return "%" + llvm::toHex(StringRef(&C, 1)); +} + +static std::string fileNameToURI(StringRef Filename) { + llvm::SmallString<32> Ret = StringRef("file://"); + + // Get the root name to see if it has a URI authority. + StringRef Root = sys::path::root_name(Filename); + if (Root.startswith("//")) { + // There is an authority, so add it to the URI. + Ret += Root.drop_front(2).str(); + } else if (!Root.empty()) { + // There is no authority, so end the component and add the root to the URI. + Ret += Twine("/" + Root).str(); + } + + auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename); + assert(Iter != End && "Expected there to be a non-root path component."); + // Add the rest of the path components, encoding any reserved characters; + // we skip past the first path component, as it was handled it above. + std::for_each(++Iter, End, [&Ret](StringRef Component) { + // For reasons unknown to me, we may get a backslash with Windows native + // paths for the initial backslash following the drive component, which + // we need to ignore as a URI path part. + if (Component == "\\") + return; + + // Add the separator between the previous path part and the one being + // currently processed. + Ret += "/"; + + // URI encode the part. + for (char C : Component) { + Ret += percentEncodeURICharacter(C); + } + }); + + return Ret.str().str(); +} + +static json::Object createFileLocation(const FileEntry &FE) { + return json::Object{{"uri", fileNameToURI(getFileName(FE))}}; +} + +static json::Object createFile(const FileEntry &FE) { + return json::Object{{"fileLocation", createFileLocation(FE)}, + {"roles", json::Array{"resultFile"}}, + {"length", FE.getSize()}, + {"mimeType", "text/plain"}}; +} + +static json::Object createFileLocation(const FileEntry &FE, + json::Array &Files) { + std::string FileURI = fileNameToURI(getFileName(FE)); + + // See if the Files array contains this URI already. If it does not, create + // a new file object to add to the array. + auto I = llvm::find_if(Files, [&](const json::Value &File) { + if (const json::Object *Obj = File.getAsObject()) { + if (const json::Object *FileLoc = Obj->getObject("fileLocation")) { + Optional<StringRef> URI = FileLoc->getString("uri"); + return URI && URI->equals(FileURI); + } + } + return false; + }); + + // Calculate the index within the file location array so it can be stored in + // the JSON object. + auto Index = static_cast<unsigned>(std::distance(Files.begin(), I)); + if (I == Files.end()) + Files.push_back(createFile(FE)); + + return json::Object{{"uri", FileURI}, {"fileIndex", Index}}; +} + +static json::Object createTextRegion(SourceRange R, const SourceManager &SM) { + return json::Object{ + {"startLine", SM.getExpansionLineNumber(R.getBegin())}, + {"endLine", SM.getExpansionLineNumber(R.getEnd())}, + {"startColumn", SM.getExpansionColumnNumber(R.getBegin())}, + {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}}; +} + +static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE, + const SourceManager &SMgr, + json::Array &Files) { + return json::Object{{{"fileLocation", createFileLocation(FE, Files)}, + {"region", createTextRegion(R, SMgr)}}}; +} + +enum class Importance { Important, Essential, Unimportant }; + +static StringRef importanceToStr(Importance I) { + switch (I) { + case Importance::Important: + return "important"; + case Importance::Essential: + return "essential"; + case Importance::Unimportant: + return "unimportant"; + } + llvm_unreachable("Fully covered switch is not so fully covered"); +} + +static json::Object createThreadFlowLocation(json::Object &&Location, + Importance I) { + return json::Object{{"location", std::move(Location)}, + {"importance", importanceToStr(I)}}; +} + +static json::Object createMessage(StringRef Text) { + return json::Object{{"text", Text.str()}}; +} + +static json::Object createLocation(json::Object &&PhysicalLocation, + StringRef Message = "") { + json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}}; + if (!Message.empty()) + Ret.insert({"message", createMessage(Message)}); + return Ret; +} + +static Importance calculateImportance(const PathDiagnosticPiece &Piece) { + switch (Piece.getKind()) { + case PathDiagnosticPiece::Kind::Call: + case PathDiagnosticPiece::Kind::Macro: + case PathDiagnosticPiece::Kind::Note: + // FIXME: What should be reported here? + break; + case PathDiagnosticPiece::Kind::Event: + return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important + : Importance::Essential; + case PathDiagnosticPiece::Kind::ControlFlow: + return Importance::Unimportant; + } + return Importance::Unimportant; +} + +static json::Object createThreadFlow(const PathPieces &Pieces, + json::Array &Files) { + const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); + json::Array Locations; + for (const auto &Piece : Pieces) { + const PathDiagnosticLocation &P = Piece->getLocation(); + Locations.push_back(createThreadFlowLocation( + createLocation(createPhysicalLocation(P.asRange(), + *P.asLocation().getFileEntry(), + SMgr, Files), + Piece->getString()), + calculateImportance(*Piece))); + } + return json::Object{{"locations", std::move(Locations)}}; +} + +static json::Object createCodeFlow(const PathPieces &Pieces, + json::Array &Files) { + return json::Object{ + {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}}; +} + +static json::Object createTool() { + return json::Object{{"name", "clang"}, + {"fullName", "clang static analyzer"}, + {"language", "en-US"}, + {"version", getClangFullVersion()}}; +} + +static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, + const StringMap<unsigned> &RuleMapping) { + const PathPieces &Path = Diag.path.flatten(false); + const SourceManager &SMgr = Path.front()->getLocation().getManager(); + + auto Iter = RuleMapping.find(Diag.getCheckName()); + assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?"); + + return json::Object{ + {"message", createMessage(Diag.getVerboseDescription())}, + {"codeFlows", json::Array{createCodeFlow(Path, Files)}}, + {"locations", + json::Array{createLocation(createPhysicalLocation( + Diag.getLocation().asRange(), + *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}}, + {"ruleIndex", Iter->getValue()}, + {"ruleId", Diag.getCheckName()}}; +} + +static StringRef getRuleDescription(StringRef CheckName) { + return llvm::StringSwitch<StringRef>(CheckName) +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT) \ + .Case(FULLNAME, HELPTEXT) +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + ; +} + +static json::Object createRule(const PathDiagnostic &Diag) { + StringRef CheckName = Diag.getCheckName(); + return json::Object{ + {"fullDescription", createMessage(getRuleDescription(CheckName))}, + {"name", createMessage(CheckName)}, + {"id", CheckName}}; +} + +static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + json::Array Rules; + llvm::StringSet<> Seen; + + llvm::for_each(Diags, [&](const PathDiagnostic *D) { + StringRef RuleID = D->getCheckName(); + std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID); + if (P.second) { + RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index. + Rules.push_back(createRule(*D)); + } + }); + + return Rules; +} + +static json::Object createResources(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + return json::Object{{"rules", createRules(Diags, RuleMapping)}}; +} + +static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) { + json::Array Results, Files; + StringMap<unsigned> RuleMapping; + json::Object Resources = createResources(Diags, RuleMapping); + + llvm::for_each(Diags, [&](const PathDiagnostic *D) { + Results.push_back(createResult(*D, Files, RuleMapping)); + }); + + return json::Object{{"tool", createTool()}, + {"resources", std::move(Resources)}, + {"results", std::move(Results)}, + {"files", std::move(Files)}}; +} + +void SarifDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, FilesMade *) { + // We currently overwrite the file if it already exists. However, it may be + // useful to add a feature someday that allows the user to append a run to an + // existing SARIF file. One danger from that approach is that the size of the + // file can become large very quickly, so decoding into JSON to append a run + // may be an expensive operation. + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; + return; + } + json::Object Sarif{ + {"$schema", + "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, + {"version", "2.0.0-csd.2.beta.2018-11-28"}, + {"runs", json::Array{createRun(Diags)}}}; + OS << llvm::formatv("{0:2}", json::Value(std::move(Sarif))); +} diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 36a0f4e6d8..2f6a0c8ffc 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -459,7 +459,7 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State, // FIXME: After putting complexity threshold to the symbols we can always // rearrange additive operations but rearrange comparisons only if // option is set. - if(!Opts.shouldAggressivelySimplifyBinaryOperation()) + if(!Opts.ShouldAggressivelySimplifyBinaryOperation) return None; SymbolRef LSym = Lhs.getAsSymbol(); @@ -475,9 +475,6 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State, SingleTy = ResultTy; if (LSym->getType() != SingleTy) return None; - // Substracting unsigned integers is a nightmare. - if (!SingleTy->isSignedIntegerOrEnumerationType()) - return None; } else { // Don't rearrange other operations. return None; @@ -485,6 +482,10 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State, assert(!SingleTy.isNull() && "We should have figured out the type by now!"); + // Rearrange signed symbolic expressions only + if (!SingleTy->isSignedIntegerOrEnumerationType()) + return None; + SymbolRef RSym = Rhs.getAsSymbol(); if (!RSym || RSym->getType() != SingleTy) return None; @@ -624,7 +625,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_LE: case BO_GE: op = BinaryOperator::reverseComparisonOp(op); - // FALL-THROUGH + LLVM_FALLTHROUGH; case BO_EQ: case BO_NE: case BO_Add: @@ -638,7 +639,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // (~0)>>a if (LHSValue.isAllOnesValue() && LHSValue.isSigned()) return evalCastFromNonLoc(lhs, resultTy); - // FALL-THROUGH + LLVM_FALLTHROUGH; case BO_Shl: // 0<<a and 0>>a if (LHSValue == 0) diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index cc9939a68d..794fd84364 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -88,7 +88,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) return R; // We don't know what to make of it. Return a NULL region, which - // will be interpretted as UnknownVal. + // will be interpreted as UnknownVal. return nullptr; } diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index ec1224e52b..66273f099a 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -83,10 +83,13 @@ void SymbolCast::dumpToStream(raw_ostream &os) const { } void SymbolConjured::dumpToStream(raw_ostream &os) const { - os << "conj_$" << getSymbolID() << '{' << T.getAsString() - << ", LC" << LCtx->getID() << ", S" << S->getID( - LCtx->getDecl()->getASTContext()) << ", #" << Count - << '}'; + os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC" + << LCtx->getID(); + if (S) + os << ", S" << S->getID(LCtx->getDecl()->getASTContext()); + else + os << ", no stmt"; + os << ", #" << Count << '}'; } void SymbolDerived::dumpToStream(raw_ostream &os) const { @@ -398,7 +401,6 @@ void SymbolReaper::markDependentsLive(SymbolRef sym) { void SymbolReaper::markLive(SymbolRef sym) { TheLiving[sym] = NotProcessed; - TheDead.erase(sym); markDependentsLive(sym); } @@ -423,14 +425,6 @@ void SymbolReaper::markInUse(SymbolRef sym) { MetadataInUse.insert(sym); } -bool SymbolReaper::maybeDead(SymbolRef sym) { - if (isLive(sym)) - return false; - - TheDead.insert(sym); - return true; -} - bool SymbolReaper::isLiveRegion(const MemRegion *MR) { if (RegionRoots.count(MR)) return true; diff --git a/lib/StaticAnalyzer/Core/TaintManager.cpp b/lib/StaticAnalyzer/Core/TaintManager.cpp new file mode 100644 index 0000000000..c34b0ca183 --- /dev/null +++ b/lib/StaticAnalyzer/Core/TaintManager.cpp @@ -0,0 +1,23 @@ +//== TaintManager.cpp ------------------------------------------ -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" + +using namespace clang; +using namespace ento; + +void *ProgramStateTrait<TaintMap>::GDMIndex() { + static int index = 0; + return &index; +} + +void *ProgramStateTrait<DerivedSymTaint>::GDMIndex() { + static int index; + return &index; +} diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp index 4b227375da..e705393cb8 100644 --- a/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -152,7 +152,7 @@ public: auto BE = N->getLocation().getAs<BlockEntrance>(); if (!BE) { - // Assume the choice of the order of the preceeding block entrance was + // Assume the choice of the order of the preceding block entrance was // correct. StackUnexplored.push_back(U); } else { @@ -252,3 +252,63 @@ public: std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() { return llvm::make_unique<UnexploredFirstPriorityQueue>(); } + +namespace { +class UnexploredFirstPriorityLocationQueue : public WorkList { + using LocIdentifier = const CFGBlock *; + + // How many times each location was visited. + // Is signed because we negate it later in order to have a reversed + // comparison. + using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>; + + // Compare by number of times the location was visited first (negated + // to prefer less often visited locations), then by insertion time (prefer + // expanding nodes inserted sooner first). + using QueuePriority = std::pair<int, unsigned long>; + using QueueItem = std::pair<WorkListUnit, QueuePriority>; + + struct ExplorationComparator { + bool operator() (const QueueItem &LHS, const QueueItem &RHS) { + return LHS.second < RHS.second; + } + }; + + // Number of inserted nodes, used to emulate DFS ordering in the priority + // queue when insertions are equal. + unsigned long Counter = 0; + + // Number of times a current location was reached. + VisitedTimesMap NumReached; + + // The top item is the largest one. + llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator> + queue; + +public: + bool hasWork() const override { + return !queue.empty(); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + unsigned NumVisited = 0; + if (auto BE = N->getLocation().getAs<BlockEntrance>()) + NumVisited = NumReached[BE->getBlock()]++; + + queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter))); + } + + WorkListUnit dequeue() override { + QueueItem U = queue.top(); + queue.pop(); + return U.first; + } + +}; + +} + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() { + return llvm::make_unique<UnexploredFirstPriorityLocationQueue>(); +} diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp index 16ab6a33eb..c4729f969f 100644 --- a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp @@ -46,7 +46,7 @@ public: // Function used to report errors void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { llvm::report_fatal_error("Z3 error: " + - llvm::Twine(Z3_get_error_msg_ex(Context, Error))); + llvm::Twine(Z3_get_error_msg(Context, Error))); } /// Wrapper for Z3 context @@ -77,32 +77,27 @@ class Z3Sort : public SMTSort { public: /// Default constructor, mainly used by make_shared - Z3Sort(Z3Context &C, Z3_sort ZS) : SMTSort(), Context(C), Sort(ZS) { + Z3Sort(Z3Context &C, Z3_sort ZS) : Context(C), Sort(ZS) { Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); } /// Override implicit copy constructor for correct reference counting. - Z3Sort(const Z3Sort &Copy) - : SMTSort(), Context(Copy.Context), Sort(Copy.Sort) { + Z3Sort(const Z3Sort &Other) : Context(Other.Context), Sort(Other.Sort) { Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); } - /// Provide move constructor - Z3Sort(Z3Sort &&Move) : SMTSort(), Context(Move.Context), Sort(nullptr) { - *this = std::move(Move); - } - - /// Provide move assignment constructor - Z3Sort &operator=(Z3Sort &&Move) { - if (this != &Move) { - if (Sort) - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - Sort = Move.Sort; - Move.Sort = nullptr; - } + /// Override implicit copy assignment constructor for correct reference + /// counting. + Z3Sort &operator=(const Z3Sort &Other) { + Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Other.Sort)); + Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); + Sort = Other.Sort; return *this; } + Z3Sort(Z3Sort &&Other) = delete; + Z3Sort &operator=(Z3Sort &&Other) = delete; + ~Z3Sort() { if (Sort) Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); @@ -134,13 +129,6 @@ public: static_cast<const Z3Sort &>(Other).Sort); } - Z3Sort &operator=(const Z3Sort &Move) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Move.Sort)); - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - Sort = Move.Sort; - return *this; - } - void print(raw_ostream &OS) const override { OS << Z3_sort_to_string(Context.Context, Sort); } @@ -167,22 +155,18 @@ public: Z3_inc_ref(Context.Context, AST); } - /// Provide move constructor - Z3Expr(Z3Expr &&Move) : SMTExpr(), Context(Move.Context), AST(nullptr) { - *this = std::move(Move); - } - - /// Provide move assignment constructor - Z3Expr &operator=(Z3Expr &&Move) { - if (this != &Move) { - if (AST) - Z3_dec_ref(Context.Context, AST); - AST = Move.AST; - Move.AST = nullptr; - } + /// Override implicit copy assignment constructor for correct reference + /// counting. + Z3Expr &operator=(const Z3Expr &Other) { + Z3_inc_ref(Context.Context, Other.AST); + Z3_dec_ref(Context.Context, AST); + AST = Other.AST; return *this; } + Z3Expr(Z3Expr &&Other) = delete; + Z3Expr &operator=(Z3Expr &&Other) = delete; + ~Z3Expr() { if (AST) Z3_dec_ref(Context.Context, AST); @@ -202,14 +186,6 @@ public: static_cast<const Z3Expr &>(Other).AST); } - /// Override implicit move constructor for correct reference counting. - Z3Expr &operator=(const Z3Expr &Move) { - Z3_inc_ref(Context.Context, Move.AST); - Z3_dec_ref(Context.Context, AST); - AST = Move.AST; - return *this; - } - void print(raw_ostream &OS) const override { OS << Z3_ast_to_string(Context.Context, AST); } @@ -228,30 +204,13 @@ class Z3Model { public: Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) { - assert(C.Context != nullptr); Z3_model_inc_ref(Context.Context, Model); } - /// Override implicit copy constructor for correct reference counting. - Z3Model(const Z3Model &Copy) : Context(Copy.Context), Model(Copy.Model) { - Z3_model_inc_ref(Context.Context, Model); - } - - /// Provide move constructor - Z3Model(Z3Model &&Move) : Context(Move.Context), Model(nullptr) { - *this = std::move(Move); - } - - /// Provide move assignment constructor - Z3Model &operator=(Z3Model &&Move) { - if (this != &Move) { - if (Model) - Z3_model_dec_ref(Context.Context, Model); - Model = Move.Model; - Move.Model = nullptr; - } - return *this; - } + Z3Model(const Z3Model &Other) = delete; + Z3Model(Z3Model &&Other) = delete; + Z3Model &operator=(Z3Model &Other) = delete; + Z3Model &operator=(Z3Model &&Other) = delete; ~Z3Model() { if (Model) @@ -310,32 +269,14 @@ class Z3Solver : public SMTSolver { Z3_solver Solver; public: - Z3Solver() : SMTSolver(), Solver(Z3_mk_simple_solver(Context.Context)) { + Z3Solver() : Solver(Z3_mk_simple_solver(Context.Context)) { Z3_solver_inc_ref(Context.Context, Solver); } - /// Override implicit copy constructor for correct reference counting. - Z3Solver(const Z3Solver &Copy) - : SMTSolver(), Context(Copy.Context), Solver(Copy.Solver) { - Z3_solver_inc_ref(Context.Context, Solver); - } - - /// Provide move constructor - Z3Solver(Z3Solver &&Move) - : SMTSolver(), Context(Move.Context), Solver(nullptr) { - *this = std::move(Move); - } - - /// Provide move assignment constructor - Z3Solver &operator=(Z3Solver &&Move) { - if (this != &Move) { - if (Solver) - Z3_solver_dec_ref(Context.Context, Solver); - Solver = Move.Solver; - Move.Solver = nullptr; - } - return *this; - } + Z3Solver(const Z3Solver &Other) = delete; + Z3Solver(Z3Solver &&Other) = delete; + Z3Solver &operator=(Z3Solver &Other) = delete; + Z3Solver &operator=(Z3Solver &&Other) = delete; ~Z3Solver() { if (Solver) @@ -671,7 +612,7 @@ public: toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); } - SMTExprRef mkFPtoSBV(const SMTExprRef &From, const SMTSortRef &To) override { + SMTExprRef mkSBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef(Z3Expr( Context, @@ -679,7 +620,7 @@ public: toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); } - SMTExprRef mkFPtoUBV(const SMTExprRef &From, const SMTSortRef &To) override { + SMTExprRef mkUBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef(Z3Expr( Context, @@ -687,14 +628,14 @@ public: toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); } - SMTExprRef mkSBVtoFP(const SMTExprRef &From, unsigned ToWidth) override { + SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef(Z3Expr( Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST, toZ3Expr(*From).AST, ToWidth))); } - SMTExprRef mkUBVtoFP(const SMTExprRef &From, unsigned ToWidth) override { + SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef(Z3Expr( Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST, @@ -733,9 +674,11 @@ public: llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, bool isUnsigned) override { - return llvm::APSInt(llvm::APInt( - BitWidth, Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST), - 10)); + return llvm::APSInt( + llvm::APInt(BitWidth, + Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST), + 10), + isUnsigned); } bool getBoolean(const SMTExprRef &Exp) override { @@ -747,36 +690,6 @@ public: return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context))); } - SMTExprRef fromBoolean(const bool Bool) override { - Z3_ast AST = - Bool ? Z3_mk_true(Context.Context) : Z3_mk_false(Context.Context); - return newExprRef(Z3Expr(Context, AST)); - } - - SMTExprRef fromAPFloat(const llvm::APFloat &Float) override { - SMTSortRef Sort = - getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); - - llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false); - SMTExprRef Z3Int = fromAPSInt(Int); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST, - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef fromAPSInt(const llvm::APSInt &Int) override { - SMTSortRef Sort = getBitvectorSort(Int.getBitWidth()); - Z3_ast AST = Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), - toZ3Sort(*Sort).Sort); - return newExprRef(Z3Expr(Context, AST)); - } - - SMTExprRef fromInt(const char *Int, uint64_t BitWidth) override { - SMTSortRef Sort = getBitvectorSort(BitWidth); - Z3_ast AST = Z3_mk_numeral(Context.Context, Int, toZ3Sort(*Sort).Sort); - return newExprRef(Z3Expr(Context, AST)); - } - bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST, llvm::APFloat &Float, bool useSemantics) { assert(Sort->isFloatSort() && "Unsupported sort to floating-point!"); @@ -837,7 +750,7 @@ public: } bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override { - Z3Model Model = getModel(); + Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); Z3_func_decl Func = Z3_get_app_decl( Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) @@ -851,7 +764,7 @@ public: } bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override { - Z3Model Model = getModel(); + Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); Z3_func_decl Func = Z3_get_app_decl( Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) @@ -882,14 +795,10 @@ public: return Z3_solver_pop(Context.Context, Solver, NumStates); } - /// Get a model from the solver. Caller should check the model is - /// satisfiable. - Z3Model getModel() { - return Z3Model(Context, Z3_solver_get_model(Context.Context, Solver)); - } + bool isFPSupported() override { return true; } /// Reset the solver and remove all constraints. - void reset() const override { Z3_solver_reset(Context.Context, Solver); } + void reset() override { Z3_solver_reset(Context.Context, Solver); } void print(raw_ostream &OS) const override { OS << Z3_solver_to_string(Context.Context, Solver); @@ -902,49 +811,6 @@ class Z3ConstraintManager : public SMTConstraintManager<ConstraintZ3, Z3Expr> { public: Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) : SMTConstraintManager(SE, SB, Solver) {} - - bool canReasonAbout(SVal X) const override { - const TargetInfo &TI = getBasicVals().getContext().getTargetInfo(); - - Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); - if (!SymVal) - return true; - - const SymExpr *Sym = SymVal->getSymbol(); - QualType Ty = Sym->getType(); - - // Complex types are not modeled - if (Ty->isComplexType() || Ty->isComplexIntegerType()) - return false; - - // Non-IEEE 754 floating-point types are not modeled - if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) && - (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() || - &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble()))) - return false; - - if (isa<SymbolData>(Sym)) - return true; - - SValBuilder &SVB = getSValBuilder(); - - if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) - return canReasonAbout(SVB.makeSymbolVal(SC->getOperand())); - - if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) - return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS())); - - if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) - return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS())); - - if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE)) - return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) && - canReasonAbout(SVB.makeSymbolVal(SSE->getRHS())); - } - - llvm_unreachable("Unsupported expression to reason about!"); - } }; // end class Z3ConstraintManager } // end anonymous namespace @@ -956,7 +822,7 @@ SMTSolverRef clang::ento::CreateZ3Solver() { return llvm::make_unique<Z3Solver>(); #else llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_BUILD_Z3=ON", + "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", false); return nullptr; #endif @@ -968,7 +834,7 @@ ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder()); #else llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_BUILD_Z3=ON", + "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", false); return nullptr; #endif |