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