xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp (revision 37458c66bfefaa4f4cb40043cb88413e5b826e3f)
13dd7877bSStanislav Gatev #include "TestingSupport.h"
23dd7877bSStanislav Gatev #include "clang/AST/ASTContext.h"
33dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchFinder.h"
43dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
532dcb759SSam Estep #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
63dd7877bSStanislav Gatev #include "clang/Tooling/Tooling.h"
79cbdef61SWei Yi Tee #include "llvm/Testing/ADT/StringMapEntry.h"
83dd7877bSStanislav Gatev #include "llvm/Testing/Support/Error.h"
93dd7877bSStanislav Gatev #include "gmock/gmock.h"
103dd7877bSStanislav Gatev #include "gtest/gtest.h"
113dd7877bSStanislav Gatev 
123dd7877bSStanislav Gatev using namespace clang;
133dd7877bSStanislav Gatev using namespace dataflow;
143dd7877bSStanislav Gatev 
153dd7877bSStanislav Gatev namespace {
163dd7877bSStanislav Gatev 
173dd7877bSStanislav Gatev using ::clang::ast_matchers::functionDecl;
18c8b31da1SDmitri Gribenko using ::clang::ast_matchers::hasAnyName;
193dd7877bSStanislav Gatev using ::clang::ast_matchers::hasName;
203dd7877bSStanislav Gatev using ::clang::ast_matchers::isDefinition;
219cbdef61SWei Yi Tee using ::clang::dataflow::test::AnalysisInputs;
229cbdef61SWei Yi Tee using ::clang::dataflow::test::AnalysisOutputs;
239cbdef61SWei Yi Tee using ::clang::dataflow::test::checkDataflow;
249cbdef61SWei Yi Tee using ::llvm::IsStringMapEntry;
253dd7877bSStanislav Gatev using ::testing::_;
263dd7877bSStanislav Gatev using ::testing::IsEmpty;
273dd7877bSStanislav Gatev using ::testing::UnorderedElementsAre;
283dd7877bSStanislav Gatev 
293dd7877bSStanislav Gatev template <typename T>
findTargetFunc(ASTContext & Context,T FunctionMatcher)303dd7877bSStanislav Gatev const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
313dd7877bSStanislav Gatev   auto TargetMatcher =
323dd7877bSStanislav Gatev       functionDecl(FunctionMatcher, isDefinition()).bind("target");
333dd7877bSStanislav Gatev   for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
343dd7877bSStanislav Gatev     const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
353dd7877bSStanislav Gatev     if (Func == nullptr)
363dd7877bSStanislav Gatev       continue;
373dd7877bSStanislav Gatev     if (Func->isTemplated())
383dd7877bSStanislav Gatev       continue;
393dd7877bSStanislav Gatev     return Func;
403dd7877bSStanislav Gatev   }
413dd7877bSStanislav Gatev   return nullptr;
423dd7877bSStanislav Gatev }
433dd7877bSStanislav Gatev 
runTest(llvm::StringRef Code,llvm::StringRef TargetName,std::function<void (const llvm::DenseMap<const Stmt *,std::string> &)> RunChecks)441d83a16bSSam Estep void runTest(
451d83a16bSSam Estep     llvm::StringRef Code, llvm::StringRef TargetName,
463dd7877bSStanislav Gatev     std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
473dd7877bSStanislav Gatev         RunChecks) {
483dd7877bSStanislav Gatev   llvm::Annotations AnnotatedCode(Code);
493dd7877bSStanislav Gatev   auto Unit = tooling::buildASTFromCodeWithArgs(
503dd7877bSStanislav Gatev       AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
513dd7877bSStanislav Gatev   auto &Context = Unit->getASTContext();
523dd7877bSStanislav Gatev   const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
533dd7877bSStanislav Gatev   ASSERT_NE(Func, nullptr);
543dd7877bSStanislav Gatev 
553dd7877bSStanislav Gatev   llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
563dd7877bSStanislav Gatev       test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
573dd7877bSStanislav Gatev   ASSERT_TRUE(static_cast<bool>(Mapping));
583dd7877bSStanislav Gatev 
593dd7877bSStanislav Gatev   RunChecks(Mapping.get());
603dd7877bSStanislav Gatev }
613dd7877bSStanislav Gatev 
TEST(BuildStatementToAnnotationMappingTest,ReturnStmt)621d83a16bSSam Estep TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
633dd7877bSStanislav Gatev   runTest(R"(
643dd7877bSStanislav Gatev     int target() {
653dd7877bSStanislav Gatev       return 42;
663dd7877bSStanislav Gatev       /*[[ok]]*/
673dd7877bSStanislav Gatev     }
683dd7877bSStanislav Gatev   )",
693dd7877bSStanislav Gatev           "target",
703dd7877bSStanislav Gatev           [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
713dd7877bSStanislav Gatev             ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
723dd7877bSStanislav Gatev             EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
733dd7877bSStanislav Gatev             EXPECT_EQ(Annotations.begin()->second, "ok");
743dd7877bSStanislav Gatev           });
753dd7877bSStanislav Gatev }
763dd7877bSStanislav Gatev 
checkDataflow(llvm::StringRef Code,ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,std::function<void (const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,const AnalysisOutputs &)> Expectations)773dd7877bSStanislav Gatev void checkDataflow(
78c8b31da1SDmitri Gribenko     llvm::StringRef Code,
79c8b31da1SDmitri Gribenko     ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
809cbdef61SWei Yi Tee     std::function<
819cbdef61SWei Yi Tee         void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
829cbdef61SWei Yi Tee              const AnalysisOutputs &)>
833dd7877bSStanislav Gatev         Expectations) {
849cbdef61SWei Yi Tee   ASSERT_THAT_ERROR(checkDataflow<NoopAnalysis>(
859cbdef61SWei Yi Tee                         AnalysisInputs<NoopAnalysis>(
86c8b31da1SDmitri Gribenko                             Code, std::move(TargetFuncMatcher),
873dd7877bSStanislav Gatev                             [](ASTContext &Context, Environment &) {
889cbdef61SWei Yi Tee                               return NoopAnalysis(
89*37458c66SMartin Braenne                                   Context,
90*37458c66SMartin Braenne                                   // Don't apply builtin transfer function.
91*37458c66SMartin Braenne                                   DataflowAnalysisOptions{std::nullopt});
929cbdef61SWei Yi Tee                             })
939cbdef61SWei Yi Tee                             .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
94c8b31da1SDmitri Gribenko                         /*VerifyResults=*/std::move(Expectations)),
953dd7877bSStanislav Gatev                     llvm::Succeeded());
963dd7877bSStanislav Gatev }
973dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,NoAnnotations)983dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, NoAnnotations) {
993dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1009cbdef61SWei Yi Tee       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
1019cbdef61SWei Yi Tee       const AnalysisOutputs &)>
1023dd7877bSStanislav Gatev       Expectations;
1033dd7877bSStanislav Gatev 
1043dd7877bSStanislav Gatev   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
1053dd7877bSStanislav Gatev 
106c8b31da1SDmitri Gribenko   checkDataflow("void target() {}", hasName("target"),
107c8b31da1SDmitri Gribenko                 Expectations.AsStdFunction());
1083dd7877bSStanislav Gatev }
1093dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,NoAnnotationsDifferentTarget)1103dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
1113dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1129cbdef61SWei Yi Tee       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
1139cbdef61SWei Yi Tee       const AnalysisOutputs &)>
1143dd7877bSStanislav Gatev       Expectations;
1153dd7877bSStanislav Gatev 
1163dd7877bSStanislav Gatev   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
1173dd7877bSStanislav Gatev 
118c8b31da1SDmitri Gribenko   checkDataflow("void target() {}", hasName("target"),
119c8b31da1SDmitri Gribenko                 Expectations.AsStdFunction());
1203dd7877bSStanislav Gatev }
1213dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,WithProgramPoint)122c8b31da1SDmitri Gribenko TEST(ProgramPointAnnotations, WithProgramPoint) {
1233dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1249cbdef61SWei Yi Tee       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
1259cbdef61SWei Yi Tee       const AnalysisOutputs &)>
1263dd7877bSStanislav Gatev       Expectations;
1273dd7877bSStanislav Gatev 
1289cbdef61SWei Yi Tee   EXPECT_CALL(
1299cbdef61SWei Yi Tee       Expectations,
1309cbdef61SWei Yi Tee       Call(UnorderedElementsAre(IsStringMapEntry("program-point", _)), _))
1313dd7877bSStanislav Gatev       .Times(1);
1323dd7877bSStanislav Gatev 
1333dd7877bSStanislav Gatev   checkDataflow(R"cc(void target() {
1343dd7877bSStanislav Gatev                        int n;
1353dd7877bSStanislav Gatev                        // [[program-point]]
1363dd7877bSStanislav Gatev                      })cc",
137c8b31da1SDmitri Gribenko                 hasName("target"), Expectations.AsStdFunction());
1383dd7877bSStanislav Gatev }
1393dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,MultipleProgramPoints)140c8b31da1SDmitri Gribenko TEST(ProgramPointAnnotations, MultipleProgramPoints) {
1413dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1429cbdef61SWei Yi Tee       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
1439cbdef61SWei Yi Tee       const AnalysisOutputs &)>
1443dd7877bSStanislav Gatev       Expectations;
1453dd7877bSStanislav Gatev 
1463dd7877bSStanislav Gatev   EXPECT_CALL(Expectations,
1479cbdef61SWei Yi Tee               Call(UnorderedElementsAre(IsStringMapEntry("program-point-1", _),
1489cbdef61SWei Yi Tee                                         IsStringMapEntry("program-point-2", _)),
1493dd7877bSStanislav Gatev                    _))
1503dd7877bSStanislav Gatev       .Times(1);
1513dd7877bSStanislav Gatev 
1523dd7877bSStanislav Gatev   checkDataflow(R"cc(void target(bool b) {
1533dd7877bSStanislav Gatev                        if (b) {
1543dd7877bSStanislav Gatev                          int n;
1553dd7877bSStanislav Gatev                          // [[program-point-1]]
1563dd7877bSStanislav Gatev                        } else {
1573dd7877bSStanislav Gatev                          int m;
1583dd7877bSStanislav Gatev                          // [[program-point-2]]
1593dd7877bSStanislav Gatev                        }
1603dd7877bSStanislav Gatev                      })cc",
161c8b31da1SDmitri Gribenko                 hasName("target"), Expectations.AsStdFunction());
162c8b31da1SDmitri Gribenko }
163c8b31da1SDmitri Gribenko 
TEST(ProgramPointAnnotations,MultipleFunctionsMultipleProgramPoints)164c8b31da1SDmitri Gribenko TEST(ProgramPointAnnotations, MultipleFunctionsMultipleProgramPoints) {
165c8b31da1SDmitri Gribenko   ::testing::MockFunction<void(
166c8b31da1SDmitri Gribenko       const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
167c8b31da1SDmitri Gribenko       const AnalysisOutputs &)>
168c8b31da1SDmitri Gribenko       Expectations;
169c8b31da1SDmitri Gribenko 
170c8b31da1SDmitri Gribenko   EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
171c8b31da1SDmitri Gribenko                                      IsStringMapEntry("program-point-1a", _),
172c8b31da1SDmitri Gribenko                                      IsStringMapEntry("program-point-1b", _)),
173c8b31da1SDmitri Gribenko                                  _))
174c8b31da1SDmitri Gribenko       .Times(1);
175c8b31da1SDmitri Gribenko 
176c8b31da1SDmitri Gribenko   EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
177c8b31da1SDmitri Gribenko                                      IsStringMapEntry("program-point-2a", _),
178c8b31da1SDmitri Gribenko                                      IsStringMapEntry("program-point-2b", _)),
179c8b31da1SDmitri Gribenko                                  _))
180c8b31da1SDmitri Gribenko       .Times(1);
181c8b31da1SDmitri Gribenko 
182c8b31da1SDmitri Gribenko   checkDataflow(
183c8b31da1SDmitri Gribenko       R"cc(
184c8b31da1SDmitri Gribenko         void target1(bool b) {
185c8b31da1SDmitri Gribenko           if (b) {
186c8b31da1SDmitri Gribenko             int n;
187c8b31da1SDmitri Gribenko             // [[program-point-1a]]
188c8b31da1SDmitri Gribenko           } else {
189c8b31da1SDmitri Gribenko             int m;
190c8b31da1SDmitri Gribenko             // [[program-point-1b]]
191c8b31da1SDmitri Gribenko           }
192c8b31da1SDmitri Gribenko         }
193c8b31da1SDmitri Gribenko 
194c8b31da1SDmitri Gribenko         void target2(bool b) {
195c8b31da1SDmitri Gribenko           if (b) {
196c8b31da1SDmitri Gribenko             int n;
197c8b31da1SDmitri Gribenko             // [[program-point-2a]]
198c8b31da1SDmitri Gribenko           } else {
199c8b31da1SDmitri Gribenko             int m;
200c8b31da1SDmitri Gribenko             // [[program-point-2b]]
201c8b31da1SDmitri Gribenko           }
202c8b31da1SDmitri Gribenko         }
203c8b31da1SDmitri Gribenko       )cc",
204c8b31da1SDmitri Gribenko       functionDecl(hasAnyName("target1", "target2")),
205c8b31da1SDmitri Gribenko       Expectations.AsStdFunction());
2063dd7877bSStanislav Gatev }
2073dd7877bSStanislav Gatev 
2083dd7877bSStanislav Gatev } // namespace
209