diff --git a/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h b/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h --- a/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h @@ -18,6 +18,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "clang/Analysis/CFG.h" +#include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Error.h" #include @@ -35,7 +36,8 @@ static llvm::Expected build(const Decl *D, Stmt &S, ASTContext &C); - // DEPRECATED. Use overload above. + // FIXME: fix known callers and uncomment the annotation. + // [[deprecated("Use overload above.")]] static llvm::Expected build(const Decl *D, Stmt *S, ASTContext *C); @@ -65,6 +67,20 @@ llvm::DenseMap StmtToBlock; }; +/// Builds a map of all declared functions in the given AST, mapping from fully +/// qualified function name to the `ControlFlowContext` that corresponds to that +/// function. Fully qualified names are used so that we can related same-named +/// functions from two different ASTs. This is critical for providing +/// implementations of functions that are suitable for inlining during analysis +/// of their callsites. +/// +/// N.B. The return value, if not empty, will point into `Unit`, so `Unit` must +/// outlive the return value. +/// +/// FIXME: add support for constructors and declarations nested in namespaces. +llvm::Expected> +buildFunctionMapFromAST(ASTUnit &Unit); + } // namespace dataflow } // namespace clang diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -24,6 +24,7 @@ #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Compiler.h" #include #include @@ -56,12 +57,22 @@ public: /// Constructs a dataflow analysis context. /// + /// Paramter `AnalyzableFunctions` is a map of analyzable function bodies + /// (represented as `ControlFlowContext`s), keyed by the fully-qualified + /// function name. The analysis will *only* inline calls to the listed + /// functions and will use the provided implementation for that purpose. The + /// actual function definition, even if available in the current TU, is + /// ignored. + /// /// Requirements: /// /// `S` must not be null. - DataflowAnalysisContext(std::unique_ptr S) + DataflowAnalysisContext( + std::unique_ptr S, + llvm::StringMap AnalyzableFunctions = {}) : S(std::move(S)), TrueVal(createAtomicBoolValue()), - FalseVal(createAtomicBoolValue()) { + FalseVal(createAtomicBoolValue()), + AnalyzableFunctions(std::move(AnalyzableFunctions)) { assert(this->S != nullptr); } @@ -346,7 +357,16 @@ FlowConditionDeps; llvm::DenseMap FlowConditionConstraints; - llvm::DenseMap FunctionContexts; + // Analyzable function definitions, keyed by fully qualified names. See + // constructor for detailed explanation. + llvm::StringMap AnalyzableFunctions; + + // Cache mapping function decls directly to `ControlFlowContext`s. If present + // in the map, the function decl has already been checked against + // `AnalyzableFunctions` (null if not a member of `AnalyzableFunctions`). If + // absent, the decl has not yet been checked. + llvm::DenseMap + FunctionContexts; }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -129,20 +129,19 @@ /// with a symbolic representation of the `this` pointee. Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); - /// Creates and returns an environment to use for an inline analysis of the - /// callee. Uses the storage location from each argument in the `Call` as the - /// storage location for the corresponding parameter in the callee. + /// Creates and returns an environment to use for an inline analysis of the + /// callee. `FuncDecl` provides the definition of the function to be analyzed. + /// Uses the storage location from each argument in the `Call` as the storage + /// location for the corresponding parameter in the callee. /// /// Requirements: /// - /// The callee of `Call` must be a `FunctionDecl` with a body. + /// The body of `FuncDecl` must not reference globals. /// - /// The body of the callee must not reference globals. - /// - /// The arguments of `Call` must map 1:1 to the callee's parameters. + /// The arguments of `Call` must map 1:1 to `FuncDecl`'s parameters. /// /// Each argument of `Call` must already have a `StorageLocation`. - Environment pushCall(const CallExpr *Call) const; + Environment pushCall(const CallExpr *Call, const FunctionDecl &FuncDecl) const; /// Moves gathered information back into `this` from a `CalleeEnv` created via /// `pushCall`. diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp --- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "clang/Analysis/CFG.h" +#include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Error.h" #include @@ -74,5 +75,33 @@ return build(D, *S, *C); } +static void InsertFunction(FunctionDecl *F, + llvm::StringMap &Functions) { + auto *Body = F->getBody(); + if (Body == nullptr) + return; + auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext()); + assert(CFCtx && + "All functions in model should translate to CFG successfully"); + std::string QualName; + llvm::raw_string_ostream OS(QualName); + F->printQualifiedName(OS); + Functions.insert({std::move(QualName), std::move(*CFCtx)}); +} + +llvm::Expected> +buildFunctionMapFromAST(ASTUnit &Unit) { + llvm::StringMap Functions; + for (auto It = Unit.top_level_begin(); It != Unit.top_level_end(); ++It) { + if (auto *C = dyn_cast(*It)) { + for (auto *M : C->methods()) + InsertFunction(M, Functions); + } else if (auto *F = dyn_cast(*It)) { + InsertFunction(F, Functions); + } + } + return Functions; +} + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -14,6 +14,7 @@ #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/AST/ExprCXX.h" +#include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DebugSupport.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/Support/Debug.h" @@ -336,23 +337,22 @@ const ControlFlowContext * DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) { - // Canonicalize the key: - F = F->getDefinition(); - if (F == nullptr) - return nullptr; + // Canonicalize the key, if possible. + if (auto *C = F->getCanonicalDecl()) + F = C; auto It = FunctionContexts.find(F); if (It != FunctionContexts.end()) - return &It->second; - - if (Stmt *Body = F->getBody()) { - auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext()); - // FIXME: Handle errors. - assert(CFCtx); - auto Result = FunctionContexts.insert({F, std::move(*CFCtx)}); - return &Result.first->second; - } - - return nullptr; + return It->second; + + const ControlFlowContext *CFCtx = nullptr; + std::string QualName; + llvm::raw_string_ostream OS(QualName); + F->printQualifiedName(OS); + auto It2 = AnalyzableFunctions.find(QualName); + if (It2 != AnalyzableFunctions.end()) + CFCtx = &It2->second; + FunctionContexts.insert({F, CFCtx}); + return CFCtx; } } // namespace dataflow diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -203,13 +203,12 @@ } } -Environment Environment::pushCall(const CallExpr *Call) const { +Environment Environment::pushCall(const CallExpr *Call, + const FunctionDecl &FuncDecl) const { Environment Env(*this); // FIXME: Support references here. Env.ReturnLoc = Env.getStorageLocation(*Call, SkipPast::Reference); - const auto *FuncDecl = Call->getDirectCallee(); - assert(FuncDecl != nullptr); // FIXME: In order to allow the callee to reference globals, we probably need // to call `initGlobalVars` here in some way. @@ -219,14 +218,14 @@ } } - auto ParamIt = FuncDecl->param_begin(); + auto ParamIt = FuncDecl.param_begin(); auto ArgIt = Call->arg_begin(); auto ArgEnd = Call->arg_end(); // FIXME: Parameters don't always map to arguments 1:1; examples include // overloaded operators implemented as member functions, and parameter packs. for (; ArgIt != ArgEnd; ++ParamIt, ++ArgIt) { - assert(ParamIt != FuncDecl->param_end()); + assert(ParamIt != FuncDecl.param_end()); const Expr *Arg = *ArgIt; auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -530,6 +530,12 @@ if (!Options.ContextSensitive) return; + // Note that it is important for the storage location of `S` to be set + // before `pushCall`, because the latter uses it to set the storage + // location for `return`. + auto &ReturnLoc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, ReturnLoc); + const ControlFlowContext *CFCtx = Env.getControlFlowContext(F); if (!CFCtx) return; @@ -540,19 +546,14 @@ auto ExitBlock = CFCtx->getCFG().getExit().getBlockID(); - // Note that it is important for the storage location of `S` to be set - // before `pushCall`, because the latter uses it to set the storage - // location for `return`. - auto &ReturnLoc = Env.createStorageLocation(*S); - Env.setStorageLocation(*S, ReturnLoc); - auto CalleeEnv = Env.pushCall(S); + const FunctionDecl *FuncDecl = CFCtx->getDecl()->getAsFunction(); + assert(FuncDecl != nullptr && "ControlFlowContexts in the environment " + "should always carry a FunctionDecl"); + auto CalleeEnv = Env.pushCall(S, *FuncDecl); // FIXME: Use the same analysis as the caller for the callee. Note, // though, that doing so would require support for changing the analysis's // ASTContext. - assert( - CFCtx->getDecl() != nullptr && - "ControlFlowContexts in the environment should always carry a decl"); auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(), DataflowAnalysisOptions()); diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -71,22 +71,19 @@ std::vector> &BlockStates; }; +/// `Code` must be the source code from `ASTUnit` was built. template llvm::Error checkDataflow( - llvm::StringRef Code, + llvm::StringRef Code, std::unique_ptr ASTUnit, ast_matchers::internal::Matcher TargetFuncMatcher, std::function MakeAnalysis, std::function PostVisitStmt, - std::function VerifyResults, ArrayRef Args, - const tooling::FileContentMappings &VirtualMappedFiles = {}) { - llvm::Annotations AnnotatedCode(Code); - auto Unit = tooling::buildASTFromCodeWithArgs( - AnnotatedCode.code(), Args, "input.cc", "clang-dataflow-test", - std::make_shared(), - tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles); - auto &Context = Unit->getASTContext(); + std::function VerifyResults, + llvm::StringMap AnalyzableFunctions = {}) { + const llvm::Annotations AnnotatedCode(Code); + auto &Context = ASTUnit->getASTContext(); if (Context.getDiagnostics().getClient()->getNumErrors() != 0) { return llvm::make_error( @@ -104,11 +101,12 @@ return llvm::make_error( llvm::errc::invalid_argument, "Could not find target function."); - auto CFCtx = ControlFlowContext::build(F, F->getBody(), &F->getASTContext()); + auto CFCtx = ControlFlowContext::build(F, *F->getBody(), F->getASTContext()); if (!CFCtx) return CFCtx.takeError(); - DataflowAnalysisContext DACtx(std::make_unique()); + DataflowAnalysisContext DACtx(std::make_unique(), + std::move(AnalyzableFunctions)); Environment Env(DACtx, *F); auto Analysis = MakeAnalysis(Context, Env); @@ -141,24 +139,50 @@ return llvm::Error::success(); } -// Runs dataflow on the body of the function that matches `TargetFuncMatcher` in -// code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`. +/// `AnalyzableFunctions` is passed to the `DataflowAnalysisContext` +/// constructor. +// FIXME: This now called in one place. Inline and remove. template llvm::Error checkDataflow( llvm::StringRef Code, ast_matchers::internal::Matcher TargetFuncMatcher, std::function MakeAnalysis, + std::function + PostVisitStmt, + std::function VerifyResults, ArrayRef Args, + const tooling::FileContentMappings &VirtualMappedFiles = {}, + llvm::StringMap AnalyzableFunctions = {}) { + auto Unit = tooling::buildASTFromCodeWithArgs( + Code, Args, "input.cc", "clang-dataflow-test", + std::make_shared(), + tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles); + return checkDataflow(Code, std::move(Unit), std::move(TargetFuncMatcher), + std::move(MakeAnalysis), std::move(PostVisitStmt), + std::move(VerifyResults), + std::move(AnalyzableFunctions)); +} + +/// Runs dataflow on the body of the function that matches `TargetFuncMatcher` +/// in code snippet `Code`. `AnalyzableFunctions` is passed to the +/// `DataflowAnalysisContext` constructor. Requires: `AnalysisT` contains a type +/// `Lattice`. +template +llvm::Error checkDataflow( + llvm::StringRef Code, std::unique_ptr ASTUnit, + ast_matchers::internal::Matcher TargetFuncMatcher, + std::function MakeAnalysis, std::function>>, ASTContext &)> VerifyResults, - ArrayRef Args, - const tooling::FileContentMappings &VirtualMappedFiles = {}) { + llvm::StringMap AnalyzableFunctions = {}) { using StateT = DataflowAnalysisState; return checkDataflow( - Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis), + Code, std::move(ASTUnit), std::move(TargetFuncMatcher), + std::move(MakeAnalysis), /*PostVisitStmt=*/nullptr, [&VerifyResults](AnalysisData AnalysisData) { if (AnalysisData.BlockStates.empty()) { @@ -192,11 +216,12 @@ } VerifyResults(Results, AnalysisData.ASTCtx); }, - Args, VirtualMappedFiles); + std::move(AnalyzableFunctions)); } -// Runs dataflow on the body of the function named `target_fun` in code snippet -// `code`. +/// Runs dataflow on the body of the function named `target_fun` in code snippet +/// `code`. `AnalyzableFunctions` is passed to the `DataflowAnalysisContext` +/// constructor. template llvm::Error checkDataflow( llvm::StringRef Code, llvm::StringRef TargetFun, @@ -207,10 +232,17 @@ ASTContext &)> VerifyResults, ArrayRef Args, - const tooling::FileContentMappings &VirtualMappedFiles = {}) { - return checkDataflow(Code, ast_matchers::hasName(TargetFun), - std::move(MakeAnalysis), std::move(VerifyResults), Args, - VirtualMappedFiles); + const tooling::FileContentMappings &VirtualMappedFiles = {}, + llvm::StringMap AnalyzableFunctions = {}) { + auto ASTUnit = tooling::buildASTFromCodeWithArgs( + Code, Args, "input.cc", "clang-dataflow-test", + std::make_shared(), + tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles); + + return checkDataflow(Code, std::move(ASTUnit), + ast_matchers::hasName(TargetFun), + std::move(MakeAnalysis), std::move(VerifyResults), + std::move(AnalyzableFunctions)); } /// Returns the `ValueDecl` for the given identifier. diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -11,12 +11,15 @@ #include "clang/AST/Decl.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "clang/Basic/LangStandard.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Testing/Support/Error.h" @@ -40,11 +43,39 @@ template void runDataflow(llvm::StringRef Code, Matcher Match, DataflowAnalysisOptions Options, + llvm::StringRef ModeledFunctionsCode = {}, LangStandard::Kind Std = LangStandard::lang_cxx17, llvm::StringRef TargetFun = "target") { + const std::vector Args = { + "-fsyntax-only", "-fno-delayed-template-parsing", + "-std=" + + std::string(LangStandard::getLangStandardForKind(Std).getName())}; + + auto MainASTUnit = tooling::buildASTFromCodeWithArgs(Code, Args); + ASSERT_NE(MainASTUnit, nullptr); + + // The unique_ptr has to live outside the if-body since the lifetime of the + // function map is linked to the lifetime of the ASTUnit. The raw pointer + // allows us to select between the main TU and the (optional) model TU. + std::unique_ptr ModelASTUnit = nullptr; + ASTUnit *UnitPtr = nullptr; + if (ModeledFunctionsCode.empty()) { + UnitPtr = MainASTUnit.get(); + } else { + ModelASTUnit = + tooling::buildASTFromCodeWithArgs(ModeledFunctionsCode, Args); + ASSERT_NE(ModelASTUnit, nullptr); + UnitPtr = ModelASTUnit.get(); + } + + llvm::Expected> Result = + buildFunctionMapFromAST(*UnitPtr); + ASSERT_TRUE((bool)Result); + llvm::StringMap AnalyzableFunctions = std::move(*Result); + ASSERT_THAT_ERROR( test::checkDataflow( - Code, TargetFun, + Code, std::move(MainASTUnit), ast_matchers::hasName(TargetFun), [Options](ASTContext &C, Environment &) { return NoopAnalysis(C, Options); }, @@ -53,18 +84,18 @@ std::pair>> Results, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, - {"-fsyntax-only", "-fno-delayed-template-parsing", - "-std=" + std::string( - LangStandard::getLangStandardForKind(Std).getName())}), + std::move(AnalyzableFunctions)), llvm::Succeeded()); } +// FIXME: inline this overload and remove it. template void runDataflow(llvm::StringRef Code, Matcher Match, LangStandard::Kind Std = LangStandard::lang_cxx17, bool ApplyBuiltinTransfer = true, llvm::StringRef TargetFun = "target") { - runDataflow(Code, Match, {ApplyBuiltinTransfer, {}}, Std, TargetFun); + runDataflow(Code, Match, /*Options=*/{ApplyBuiltinTransfer, {}}, + /*AnalyzableFunctions=*/{}, Std, TargetFun); } TEST(TransferTest, IntVarDeclNotTrackedWhenTransferDisabled) { @@ -1320,9 +1351,19 @@ } }; )"; + + auto ASTUnit = tooling::buildASTFromCodeWithArgs( + Code, + /*Args=*/ + {"-fsyntax-only", "-fno-delayed-template-parsing", + "-std=" + std::string(LangStandard::getLangStandardForKind( + LangStandard::lang_cxx17) + .getName())}, + "input.cc", "clang-dataflow-test"); + ASSERT_THAT_ERROR( test::checkDataflow( - Code, cxxConstructorDecl(ofClass(hasName("B"))), + Code, std::move(ASTUnit), cxxConstructorDecl(ofClass(hasName("B"))), [](ASTContext &C, Environment &) { return NoopAnalysis(C, /*ApplyBuiltinTransfer=*/true); }, @@ -1335,11 +1376,7 @@ // the future, we can expand this test to check more specific // properties. EXPECT_THAT(Results, ElementsAre(Pair("p", _))); - }, - {"-fsyntax-only", "-fno-delayed-template-parsing", - "-std=" + std::string(LangStandard::getLangStandardForKind( - LangStandard::lang_cxx17) - .getName())}), + }), llvm::Succeeded()); } @@ -3880,30 +3917,34 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); - ASSERT_THAT(FooDecl, NotNull()); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); - auto &FooVal = - *cast(Env.getValue(*FooDecl, SkipPast::None)); - EXPECT_FALSE(Env.flowConditionImplies(FooVal)); - EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/false}}); + auto &FooVal = *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_FALSE(Env.flowConditionImplies(FooVal)); + EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/false}}, + /*ModeledFunctionsCode=*/""); } TEST(TransferTest, ContextSensitiveSetTrue) { + std::string Functions = R"( + void SetBool(bool &Var) { Var = true; } + )"; std::string Code = R"( bool GiveBool(); - void SetBool(bool &Var) { Var = true; } + void SetBool(bool &Var); void target() { bool Foo = GiveBool(); @@ -3927,13 +3968,15 @@ EXPECT_TRUE(Env.flowConditionImplies(FooVal)); }, {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } TEST(TransferTest, ContextSensitiveSetFalse) { + std::string Functions = R"(void SetBool(bool &Var) { Var = false; })"; std::string Code = R"( bool GiveBool(); - void SetBool(bool &Var) { Var = false; } + void SetBool(bool &Var); void target() { bool Foo = GiveBool(); @@ -3941,29 +3984,33 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); - ASSERT_THAT(FooDecl, NotNull()); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); - auto &FooVal = - *cast(Env.getValue(*FooDecl, SkipPast::None)); - EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + auto &FooVal = *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } TEST(TransferTest, ContextSensitiveSetBothTrueAndFalse) { + std::string Functions = R"( + void SetBool(bool &Var, bool Val) { Var = Val; } + )"; std::string Code = R"( bool GiveBool(); - void SetBool(bool &Var, bool Val) { Var = Val; } + void SetBool(bool &Var, bool Val); void target() { bool Foo = GiveBool(); @@ -3973,39 +4020,42 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); - ASSERT_THAT(FooDecl, NotNull()); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); - ASSERT_THAT(BarDecl, NotNull()); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); - auto &FooVal = - *cast(Env.getValue(*FooDecl, SkipPast::None)); - EXPECT_TRUE(Env.flowConditionImplies(FooVal)); - EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); + auto &FooVal = *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(FooVal)); + EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); - auto &BarVal = - *cast(Env.getValue(*BarDecl, SkipPast::None)); - EXPECT_FALSE(Env.flowConditionImplies(BarVal)); - EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + auto &BarVal = *cast(Env.getValue(*BarDecl, SkipPast::None)); + EXPECT_FALSE(Env.flowConditionImplies(BarVal)); + EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } TEST(TransferTest, ContextSensitiveSetTwoLayers) { - std::string Code = R"( - bool GiveBool(); + std::string Functions = R"( void SetBool1(bool &Var) { Var = true; } void SetBool2(bool &Var) { SetBool1(Var); } + )"; + std::string Code = R"( + bool GiveBool(); + void SetBool2(bool &Var); void target() { bool Foo = GiveBool(); @@ -4013,32 +4063,36 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); - ASSERT_THAT(FooDecl, NotNull()); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); - auto &FooVal = - *cast(Env.getValue(*FooDecl, SkipPast::None)); - EXPECT_FALSE(Env.flowConditionImplies(FooVal)); - EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + auto &FooVal = *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_FALSE(Env.flowConditionImplies(FooVal)); + EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } TEST(TransferTest, ContextSensitiveSetMultipleLines) { - std::string Code = R"( + std::string Functions = R"( void SetBools(bool &Var1, bool &Var2) { Var1 = true; Var2 = false; } + )"; + std::string Code = R"( + void SetBools(bool &Var1, bool &Var2); void target() { bool Foo = false; @@ -4047,36 +4101,36 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); - ASSERT_THAT(FooDecl, NotNull()); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); - ASSERT_THAT(BarDecl, NotNull()); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); - auto &FooVal = - *cast(Env.getValue(*FooDecl, SkipPast::None)); - EXPECT_TRUE(Env.flowConditionImplies(FooVal)); - EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); + auto &FooVal = *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(FooVal)); + EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal))); - auto &BarVal = - *cast(Env.getValue(*BarDecl, SkipPast::None)); - EXPECT_FALSE(Env.flowConditionImplies(BarVal)); - EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + auto &BarVal = *cast(Env.getValue(*BarDecl, SkipPast::None)); + EXPECT_FALSE(Env.flowConditionImplies(BarVal)); + EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } TEST(TransferTest, ContextSensitiveSetMultipleBlocks) { - std::string Code = R"( + std::string Functions = R"( void IfCond(bool Cond, bool &Then, bool &Else) { if (Cond) { Then = true; @@ -4084,6 +4138,9 @@ Else = true; } } + )"; + std::string Code = R"( + void IfCond(bool Cond, bool &Then, bool &Else); void target() { bool Foo = false; @@ -4093,34 +4150,41 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - const Environment &Env = Results[0].second.Env; + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); - ASSERT_THAT(BarDecl, NotNull()); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); - ASSERT_THAT(BazDecl, NotNull()); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); - auto &BarVal = - *cast(Env.getValue(*BarDecl, SkipPast::None)); - EXPECT_FALSE(Env.flowConditionImplies(BarVal)); - EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); + auto &BarVal = *cast(Env.getValue(*BarDecl, SkipPast::None)); + EXPECT_FALSE(Env.flowConditionImplies(BarVal)); + EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal))); - auto &BazVal = - *cast(Env.getValue(*BazDecl, SkipPast::None)); - EXPECT_TRUE(Env.flowConditionImplies(BazVal)); - EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal))); - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + auto &BazVal = *cast(Env.getValue(*BazDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(BazVal)); + EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal))); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Functions); } +llvm::StringLiteral Model = R"( + void Noop() { return; } + bool GiveTrue() { return true; } + bool GiveFalse() { return false; } + bool GiveBack(bool Arg) { return Arg; } +)"; + TEST(TransferTest, ContextSensitiveReturnVoid) { std::string Code = R"( void Noop() { return; } @@ -4130,24 +4194,26 @@ // [[p]] } )"; - runDataflow(Code, - [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p", _))); - // This just tests that the analysis doesn't crash. - }, - {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + runDataflow( + Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + // This just tests that the analysis doesn't crash. + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, + Model); } TEST(TransferTest, ContextSensitiveReturnTrue) { std::string Code = R"( - bool GiveBool() { return true; } + bool GiveTrue(); void target() { - bool Foo = GiveBool(); + bool Foo = GiveTrue(); // [[p]] } )"; @@ -4167,15 +4233,15 @@ EXPECT_TRUE(Env.flowConditionImplies(FooVal)); }, {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model); } TEST(TransferTest, ContextSensitiveReturnFalse) { std::string Code = R"( - bool GiveBool() { return false; } + bool GiveFalse(); void target() { - bool Foo = GiveBool(); + bool Foo = GiveFalse(); // [[p]] } )"; @@ -4195,13 +4261,13 @@ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal))); }, {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model); } TEST(TransferTest, ContextSensitiveReturnArg) { std::string Code = R"( bool GiveBool(); - bool GiveBack(bool Arg) { return Arg; } + bool GiveBack(bool Arg); void target() { bool Foo = GiveBool(); @@ -4226,7 +4292,7 @@ EXPECT_TRUE(Env.flowConditionImplies(BazVal)); }, {/*.ApplyBuiltinTransfer=*/true, - /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model); } TEST(TransferTest, ContextSensitiveMethodLiteral) {