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/Support/Error.h"
8 #include "gmock/gmock.h"
9 #include "gtest/gtest.h"
10 
11 using namespace clang;
12 using namespace dataflow;
13 
14 namespace {
15 
16 using ::clang::ast_matchers::functionDecl;
17 using ::clang::ast_matchers::hasName;
18 using ::clang::ast_matchers::isDefinition;
19 using ::testing::_;
20 using ::testing::IsEmpty;
21 using ::testing::Pair;
22 using ::testing::UnorderedElementsAre;
23 
24 template <typename T>
findTargetFunc(ASTContext & Context,T FunctionMatcher)25 const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
26   auto TargetMatcher =
27       functionDecl(FunctionMatcher, isDefinition()).bind("target");
28   for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
29     const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
30     if (Func == nullptr)
31       continue;
32     if (Func->isTemplated())
33       continue;
34     return Func;
35   }
36   return nullptr;
37 }
38 
runTest(llvm::StringRef Code,llvm::StringRef TargetName,std::function<void (const llvm::DenseMap<const Stmt *,std::string> &)> RunChecks)39 void runTest(
40     llvm::StringRef Code, llvm::StringRef TargetName,
41     std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
42         RunChecks) {
43   llvm::Annotations AnnotatedCode(Code);
44   auto Unit = tooling::buildASTFromCodeWithArgs(
45       AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
46   auto &Context = Unit->getASTContext();
47   const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
48   ASSERT_NE(Func, nullptr);
49 
50   llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
51       test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
52   ASSERT_TRUE(static_cast<bool>(Mapping));
53 
54   RunChecks(Mapping.get());
55 }
56 
TEST(BuildStatementToAnnotationMappingTest,ReturnStmt)57 TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
58   runTest(R"(
59     int target() {
60       return 42;
61       /*[[ok]]*/
62     }
63   )",
64           "target",
65           [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
66             ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
67             EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
68             EXPECT_EQ(Annotations.begin()->second, "ok");
69           });
70 }
71 
checkDataflow(llvm::StringRef Code,llvm::StringRef Target,std::function<void (llvm::ArrayRef<std::pair<std::string,DataflowAnalysisState<NoopLattice>>>,ASTContext &)> Expectations)72 void checkDataflow(
73     llvm::StringRef Code, llvm::StringRef Target,
74     std::function<void(llvm::ArrayRef<std::pair<
75                            std::string, DataflowAnalysisState<NoopLattice>>>,
76                        ASTContext &)>
77         Expectations) {
78   ASSERT_THAT_ERROR(
79       test::checkDataflow<NoopAnalysis>(
80           Code, Target,
81           [](ASTContext &Context, Environment &) {
82             return NoopAnalysis(Context, /*ApplyBuiltinTransfer=*/false);
83           },
84           std::move(Expectations), {"-fsyntax-only", "-std=c++17"}),
85       llvm::Succeeded());
86 }
87 
TEST(ProgramPointAnnotations,NoAnnotations)88 TEST(ProgramPointAnnotations, NoAnnotations) {
89   ::testing::MockFunction<void(
90       llvm::ArrayRef<
91           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
92       ASTContext &)>
93       Expectations;
94 
95   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
96 
97   checkDataflow("void target() {}", "target", Expectations.AsStdFunction());
98 }
99 
TEST(ProgramPointAnnotations,NoAnnotationsDifferentTarget)100 TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
101   ::testing::MockFunction<void(
102       llvm::ArrayRef<
103           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
104       ASTContext &)>
105       Expectations;
106 
107   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
108 
109   checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction());
110 }
111 
TEST(ProgramPointAnnotations,WithCodepoint)112 TEST(ProgramPointAnnotations, WithCodepoint) {
113   ::testing::MockFunction<void(
114       llvm::ArrayRef<
115           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
116       ASTContext &)>
117       Expectations;
118 
119   EXPECT_CALL(Expectations,
120               Call(UnorderedElementsAre(Pair("program-point", _)), _))
121       .Times(1);
122 
123   checkDataflow(R"cc(void target() {
124                      int n;
125                      // [[program-point]]
126                    })cc",
127                 "target", Expectations.AsStdFunction());
128 }
129 
TEST(ProgramPointAnnotations,MultipleCodepoints)130 TEST(ProgramPointAnnotations, MultipleCodepoints) {
131   ::testing::MockFunction<void(
132       llvm::ArrayRef<
133           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
134       ASTContext &)>
135       Expectations;
136 
137   EXPECT_CALL(Expectations,
138               Call(UnorderedElementsAre(Pair("program-point-1", _),
139                                         Pair("program-point-2", _)),
140                    _))
141       .Times(1);
142 
143   checkDataflow(R"cc(void target(bool b) {
144                      if (b) {
145                        int n;
146                        // [[program-point-1]]
147                      } else {
148                        int m;
149                        // [[program-point-2]]
150                      }
151                    })cc",
152                 "target", Expectations.AsStdFunction());
153 }
154 
155 } // namespace
156