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