ymandel updated this revision to Diff 450353.
ymandel added a comment.

add comment

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131280/new/

https://reviews.llvm.org/D131280

Files:
  clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ 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 <typename Matcher>
 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<std::string> 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<ASTUnit> 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<llvm::StringMap<ControlFlowContext>> Result =
+      buildFunctionMapFromAST(*UnitPtr);
+  ASSERT_TRUE((bool)Result);
+  llvm::StringMap<ControlFlowContext> AnalyzableFunctions = std::move(*Result);
+
   ASSERT_THAT_ERROR(
       test::checkDataflow<NoopAnalysis>(
-          Code, TargetFun,
+          Code, std::move(MainASTUnit), ast_matchers::hasName(TargetFun),
           [Options](ASTContext &C, Environment &) {
             return NoopAnalysis(C, Options);
           },
@@ -53,18 +84,18 @@
                   std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
                   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 <typename Matcher>
 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<NoopAnalysis>(
-          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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(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<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
-                EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
-              },
-              {/*.ApplyBuiltinTransfer=*/true,
-               /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+        auto &FooVal = *cast<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
-                EXPECT_TRUE(Env.flowConditionImplies(FooVal));
-                EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+        auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+        EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+        EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
 
-                auto &BarVal =
-                    *cast<BoolValue>(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<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(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<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
-                EXPECT_TRUE(Env.flowConditionImplies(FooVal));
-                EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+        auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+        EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+        EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
 
-                auto &BarVal =
-                    *cast<BoolValue>(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<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     Results,
-                 ASTContext &ASTCtx) {
-                ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
-                const Environment &Env = Results[0].second.Env;
+  runDataflow(
+      Code,
+      [](llvm::ArrayRef<
+             std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+             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<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
-                EXPECT_FALSE(Env.flowConditionImplies(BarVal));
-                EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+        auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+        EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+        EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
 
-                auto &BazVal =
-                    *cast<BoolValue>(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<BoolValue>(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<std::string, DataflowAnalysisState<NoopLattice>>>
-                     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<std::string, DataflowAnalysisState<NoopLattice>>>
+             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) {
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,22 +71,19 @@
   std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates;
 };
 
+/// `Code` must be the source code from `ASTUnit` was built.
 template <typename AnalysisT>
 llvm::Error checkDataflow(
-    llvm::StringRef Code,
+    llvm::StringRef Code, std::unique_ptr<ASTUnit> ASTUnit,
     ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
     std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
     std::function<void(ASTContext &, const CFGStmt &,
                        const TypeErasedDataflowAnalysisState &)>
         PostVisitStmt,
-    std::function<void(AnalysisData)> VerifyResults, ArrayRef<std::string> Args,
-    const tooling::FileContentMappings &VirtualMappedFiles = {}) {
-  llvm::Annotations AnnotatedCode(Code);
-  auto Unit = tooling::buildASTFromCodeWithArgs(
-      AnnotatedCode.code(), Args, "input.cc", "clang-dataflow-test",
-      std::make_shared<PCHContainerOperations>(),
-      tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles);
-  auto &Context = Unit->getASTContext();
+    std::function<void(AnalysisData)> VerifyResults,
+    llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
+  const llvm::Annotations AnnotatedCode(Code);
+  auto &Context = ASTUnit->getASTContext();
 
   if (Context.getDiagnostics().getClient()->getNumErrors() != 0) {
     return llvm::make_error<llvm::StringError>(
@@ -104,11 +101,12 @@
     return llvm::make_error<llvm::StringError>(
         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<WatchedLiteralsSolver>());
+  DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>(),
+                                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 <typename AnalysisT>
 llvm::Error checkDataflow(
     llvm::StringRef Code,
     ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
     std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
+    std::function<void(ASTContext &, const CFGStmt &,
+                       const TypeErasedDataflowAnalysisState &)>
+        PostVisitStmt,
+    std::function<void(AnalysisData)> VerifyResults, ArrayRef<std::string> Args,
+    const tooling::FileContentMappings &VirtualMappedFiles = {},
+    llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(
+      Code, Args, "input.cc", "clang-dataflow-test",
+      std::make_shared<PCHContainerOperations>(),
+      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 <typename AnalysisT>
+llvm::Error checkDataflow(
+    llvm::StringRef Code, std::unique_ptr<ASTUnit> ASTUnit,
+    ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
+    std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
     std::function<void(
         llvm::ArrayRef<std::pair<
             std::string, DataflowAnalysisState<typename AnalysisT::Lattice>>>,
         ASTContext &)>
         VerifyResults,
-    ArrayRef<std::string> Args,
-    const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+    llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
   using StateT = DataflowAnalysisState<typename AnalysisT::Lattice>;
 
   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 <typename AnalysisT>
 llvm::Error checkDataflow(
     llvm::StringRef Code, llvm::StringRef TargetFun,
@@ -207,10 +232,17 @@
         ASTContext &)>
         VerifyResults,
     ArrayRef<std::string> 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<ControlFlowContext> AnalyzableFunctions = {}) {
+  auto ASTUnit = tooling::buildASTFromCodeWithArgs(
+      Code, Args, "input.cc", "clang-dataflow-test",
+      std::make_shared<PCHContainerOperations>(),
+      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.
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ 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());
 
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ 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);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ 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
Index: clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
+++ 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 <utility>
@@ -74,5 +75,33 @@
   return build(D, *S, *C);
 }
 
+static void InsertFunction(FunctionDecl *F,
+                           llvm::StringMap<ControlFlowContext> &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<llvm::StringMap<ControlFlowContext>>
+buildFunctionMapFromAST(ASTUnit &Unit) {
+  llvm::StringMap<ControlFlowContext> Functions;
+  for (auto It = Unit.top_level_begin(); It != Unit.top_level_end(); ++It) {
+    if (auto *C = dyn_cast<CXXRecordDecl>(*It)) {
+      for (auto *M : C->methods())
+        InsertFunction(M, Functions);
+    } else if (auto *F = dyn_cast<FunctionDecl>(*It)) {
+      InsertFunction(F, Functions);
+    }
+  }
+  return Functions;
+}
+
 } // namespace dataflow
 } // namespace clang
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ 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`.
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ 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 <cassert>
 #include <memory>
@@ -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<Solver> S)
+  DataflowAnalysisContext(
+      std::unique_ptr<Solver> S,
+      llvm::StringMap<ControlFlowContext> 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<AtomicBoolValue *, BoolValue *> FlowConditionConstraints;
 
-  llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
+  // Analyzable function definitions, keyed by fully qualified names. See
+  // constructor for detailed explanation.
+  llvm::StringMap<ControlFlowContext> 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<const FunctionDecl *, const ControlFlowContext*>
+      FunctionContexts;
 };
 
 } // namespace dataflow
Index: clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
+++ 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 <memory>
@@ -35,7 +36,8 @@
   static llvm::Expected<ControlFlowContext> 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<ControlFlowContext> build(const Decl *D, Stmt *S,
                                                   ASTContext *C);
 
@@ -65,6 +67,22 @@
   llvm::DenseMap<const Stmt *, const CFGBlock *> 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.
+/// FIXME: fully qualified names are not enough to uniquely identify members of
+/// overload sets. change the key to support uniqueness.
+llvm::Expected<llvm::StringMap<ControlFlowContext>>
+buildFunctionMapFromAST(ASTUnit &Unit);
+
 } // namespace dataflow
 } // namespace clang
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to