xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp (revision 37458c66bfefaa4f4cb40043cb88413e5b826e3f)
1 #include "TestingSupport.h"
2 #include "clang/AST/ASTContext.h"
3 #include "clang/ASTMatchers/ASTMatchFinder.h"
4 #include "clang/ASTMatchers/ASTMatchers.h"
5 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
6 #include "clang/Tooling/Tooling.h"
7 #include "llvm/Testing/ADT/StringMapEntry.h"
8 #include "llvm/Testing/Support/Error.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
11 
12 using namespace clang;
13 using namespace dataflow;
14 
15 namespace {
16 
17 using ::clang::ast_matchers::functionDecl;
18 using ::clang::ast_matchers::hasAnyName;
19 using ::clang::ast_matchers::hasName;
20 using ::clang::ast_matchers::isDefinition;
21 using ::clang::dataflow::test::AnalysisInputs;
22 using ::clang::dataflow::test::AnalysisOutputs;
23 using ::clang::dataflow::test::checkDataflow;
24 using ::llvm::IsStringMapEntry;
25 using ::testing::_;
26 using ::testing::IsEmpty;
27 using ::testing::UnorderedElementsAre;
28 
29 template <typename T>
findTargetFunc(ASTContext & Context,T FunctionMatcher)30 const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
31   auto TargetMatcher =
32       functionDecl(FunctionMatcher, isDefinition()).bind("target");
33   for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
34     const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
35     if (Func == nullptr)
36       continue;
37     if (Func->isTemplated())
38       continue;
39     return Func;
40   }
41   return nullptr;
42 }
43 
runTest(llvm::StringRef Code,llvm::StringRef TargetName,std::function<void (const llvm::DenseMap<const Stmt *,std::string> &)> RunChecks)44 void runTest(
45     llvm::StringRef Code, llvm::StringRef TargetName,
46     std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
47         RunChecks) {
48   llvm::Annotations AnnotatedCode(Code);
49   auto Unit = tooling::buildASTFromCodeWithArgs(
50       AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
51   auto &Context = Unit->getASTContext();
52   const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
53   ASSERT_NE(Func, nullptr);
54 
55   llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
56       test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
57   ASSERT_TRUE(static_cast<bool>(Mapping));
58 
59   RunChecks(Mapping.get());
60 }
61 
TEST(BuildStatementToAnnotationMappingTest,ReturnStmt)62 TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
63   runTest(R"(
64     int target() {
65       return 42;
66       /*[[ok]]*/
67     }
68   )",
69           "target",
70           [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
71             ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
72             EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
73             EXPECT_EQ(Annotations.begin()->second, "ok");
74           });
75 }
76 
checkDataflow(llvm::StringRef Code,ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,std::function<void (const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,const AnalysisOutputs &)> Expectations)77 void checkDataflow(
78     llvm::StringRef Code,
79     ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
80     std::function<
81         void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
82              const AnalysisOutputs &)>
83         Expectations) {
84   ASSERT_THAT_ERROR(checkDataflow<NoopAnalysis>(
85                         AnalysisInputs<NoopAnalysis>(
86                             Code, std::move(TargetFuncMatcher),
87                             [](ASTContext &Context, Environment &) {
88                               return NoopAnalysis(
89                                   Context,
90                                   // Don't apply builtin transfer function.
91                                   DataflowAnalysisOptions{std::nullopt});
92                             })
93                             .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
94                         /*VerifyResults=*/std::move(Expectations)),
95                     llvm::Succeeded());
96 }
97 
TEST(ProgramPointAnnotations,NoAnnotations)98 TEST(ProgramPointAnnotations, NoAnnotations) {
99   ::testing::MockFunction<void(
100       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
101       const AnalysisOutputs &)>
102       Expectations;
103 
104   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
105 
106   checkDataflow("void target() {}", hasName("target"),
107                 Expectations.AsStdFunction());
108 }
109 
TEST(ProgramPointAnnotations,NoAnnotationsDifferentTarget)110 TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
111   ::testing::MockFunction<void(
112       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
113       const AnalysisOutputs &)>
114       Expectations;
115 
116   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
117 
118   checkDataflow("void target() {}", hasName("target"),
119                 Expectations.AsStdFunction());
120 }
121 
TEST(ProgramPointAnnotations,WithProgramPoint)122 TEST(ProgramPointAnnotations, WithProgramPoint) {
123   ::testing::MockFunction<void(
124       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
125       const AnalysisOutputs &)>
126       Expectations;
127 
128   EXPECT_CALL(
129       Expectations,
130       Call(UnorderedElementsAre(IsStringMapEntry("program-point", _)), _))
131       .Times(1);
132 
133   checkDataflow(R"cc(void target() {
134                        int n;
135                        // [[program-point]]
136                      })cc",
137                 hasName("target"), Expectations.AsStdFunction());
138 }
139 
TEST(ProgramPointAnnotations,MultipleProgramPoints)140 TEST(ProgramPointAnnotations, MultipleProgramPoints) {
141   ::testing::MockFunction<void(
142       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
143       const AnalysisOutputs &)>
144       Expectations;
145 
146   EXPECT_CALL(Expectations,
147               Call(UnorderedElementsAre(IsStringMapEntry("program-point-1", _),
148                                         IsStringMapEntry("program-point-2", _)),
149                    _))
150       .Times(1);
151 
152   checkDataflow(R"cc(void target(bool b) {
153                        if (b) {
154                          int n;
155                          // [[program-point-1]]
156                        } else {
157                          int m;
158                          // [[program-point-2]]
159                        }
160                      })cc",
161                 hasName("target"), Expectations.AsStdFunction());
162 }
163 
TEST(ProgramPointAnnotations,MultipleFunctionsMultipleProgramPoints)164 TEST(ProgramPointAnnotations, MultipleFunctionsMultipleProgramPoints) {
165   ::testing::MockFunction<void(
166       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
167       const AnalysisOutputs &)>
168       Expectations;
169 
170   EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
171                                      IsStringMapEntry("program-point-1a", _),
172                                      IsStringMapEntry("program-point-1b", _)),
173                                  _))
174       .Times(1);
175 
176   EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
177                                      IsStringMapEntry("program-point-2a", _),
178                                      IsStringMapEntry("program-point-2b", _)),
179                                  _))
180       .Times(1);
181 
182   checkDataflow(
183       R"cc(
184         void target1(bool b) {
185           if (b) {
186             int n;
187             // [[program-point-1a]]
188           } else {
189             int m;
190             // [[program-point-1b]]
191           }
192         }
193 
194         void target2(bool b) {
195           if (b) {
196             int n;
197             // [[program-point-2a]]
198           } else {
199             int m;
200             // [[program-point-2b]]
201           }
202         }
203       )cc",
204       functionDecl(hasAnyName("target1", "target2")),
205       Expectations.AsStdFunction());
206 }
207 
208 } // namespace
209