1 //===- unittests/AST/DataCollectionTest.cpp -------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains tests for the DataCollection module.
10 //
11 // They work by hashing the collected data of two nodes and asserting that the
12 // hash values are equal iff the nodes are considered equal.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "clang/AST/DataCollection.h"
17 #include "clang/AST/DeclTemplate.h"
18 #include "clang/AST/StmtVisitor.h"
19 #include "clang/ASTMatchers/ASTMatchFinder.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gtest/gtest.h"
22 
23 using namespace clang;
24 using namespace tooling;
25 using namespace ast_matchers;
26 
27 namespace {
28 class StmtDataCollector : public ConstStmtVisitor<StmtDataCollector> {
29   ASTContext &Context;
30   llvm::MD5 &DataConsumer;
31 
addData(const T & Data)32   template <class T> void addData(const T &Data) {
33     data_collection::addDataToConsumer(DataConsumer, Data);
34   }
35 
36 public:
StmtDataCollector(const Stmt * S,ASTContext & Context,llvm::MD5 & DataConsumer)37   StmtDataCollector(const Stmt *S, ASTContext &Context, llvm::MD5 &DataConsumer)
38       : Context(Context), DataConsumer(DataConsumer) {
39     this->Visit(S);
40   }
41 
42 #define DEF_ADD_DATA(CLASS, CODE)                                              \
43   template <class Dummy = void> Dummy Visit##CLASS(const CLASS *S) {           \
44     CODE;                                                                      \
45     ConstStmtVisitor<StmtDataCollector>::Visit##CLASS(S);                      \
46   }
47 
48 #include "clang/AST/StmtDataCollectors.inc"
49 };
50 } // end anonymous namespace
51 
52 namespace {
53 struct StmtHashMatch : public MatchFinder::MatchCallback {
54   unsigned NumFound;
55   llvm::MD5::MD5Result &Hash;
StmtHashMatch__anon7cbcbae30211::StmtHashMatch56   StmtHashMatch(llvm::MD5::MD5Result &Hash) : NumFound(0), Hash(Hash) {}
57 
run__anon7cbcbae30211::StmtHashMatch58   void run(const MatchFinder::MatchResult &Result) override {
59     const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id");
60     if (!S)
61       return;
62     ++NumFound;
63     if (NumFound > 1)
64       return;
65     llvm::MD5 MD5;
66     StmtDataCollector(S, *Result.Context, MD5);
67     MD5.final(Hash);
68   }
69 };
70 } // end anonymous namespace
71 
hashStmt(llvm::MD5::MD5Result & Hash,const StatementMatcher & StmtMatch,StringRef Code)72 static testing::AssertionResult hashStmt(llvm::MD5::MD5Result &Hash,
73                                          const StatementMatcher &StmtMatch,
74                                          StringRef Code) {
75   StmtHashMatch Hasher(Hash);
76   MatchFinder Finder;
77   Finder.addMatcher(StmtMatch, &Hasher);
78   std::unique_ptr<FrontendActionFactory> Factory(
79       newFrontendActionFactory(&Finder));
80   if (!runToolOnCode(Factory->create(), Code))
81     return testing::AssertionFailure()
82            << "Parsing error in \"" << Code.str() << "\"";
83   if (Hasher.NumFound == 0)
84     return testing::AssertionFailure() << "Matcher didn't find any statements";
85   if (Hasher.NumFound > 1)
86     return testing::AssertionFailure()
87            << "Matcher should match only one statement "
88               "(found "
89            << Hasher.NumFound << ")";
90   return testing::AssertionSuccess();
91 }
92 
93 static testing::AssertionResult
isStmtHashEqual(const StatementMatcher & StmtMatch,StringRef Code1,StringRef Code2)94 isStmtHashEqual(const StatementMatcher &StmtMatch, StringRef Code1,
95                 StringRef Code2) {
96   llvm::MD5::MD5Result Hash1, Hash2;
97   testing::AssertionResult Result = hashStmt(Hash1, StmtMatch, Code1);
98   if (!Result)
99     return Result;
100   if (!(Result = hashStmt(Hash2, StmtMatch, Code2)))
101     return Result;
102 
103   return testing::AssertionResult(Hash1 == Hash2);
104 }
105 
TEST(StmtDataCollector,TestDeclRefExpr)106 TEST(StmtDataCollector, TestDeclRefExpr) {
107   ASSERT_TRUE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
108                               "int x, r = x;"));
109   ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
110                                "int y, r = y;"));
111   ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
112                                "namespace n { int x, r = x; };"));
113 }
114 
TEST(StmtDataCollector,TestMemberExpr)115 TEST(StmtDataCollector, TestMemberExpr) {
116   ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
117                               "struct { int x; } X; int r = X.x;",
118                               "struct { int x; } X; int r = (&X)->x;"));
119   ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
120                               "struct { int x; } X; int r = X.x;",
121                               "struct { int x; } Y; int r = Y.x;"));
122   ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
123                               "struct { int x; } X; int r = X.x;",
124                               "struct C { int x; } X; int r = X.C::x;"));
125   ASSERT_FALSE(isStmtHashEqual(memberExpr().bind("id"),
126                                "struct { int x; } X; int r = X.x;",
127                                "struct { int y; } X; int r = X.y;"));
128 }
129 
TEST(StmtDataCollector,TestIntegerLiteral)130 TEST(StmtDataCollector, TestIntegerLiteral) {
131   ASSERT_TRUE(
132       isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 0;"));
133   ASSERT_TRUE(
134       isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x =00;"));
135   ASSERT_FALSE(
136       isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 1;"));
137 }
138 
TEST(StmtDataCollector,TestFloatingLiteral)139 TEST(StmtDataCollector, TestFloatingLiteral) {
140   ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;",
141                               "double x = .0;"));
142   ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .10;",
143                               "double x = .1;"));
144   ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .1;",
145                               "double x = 1e-1;"));
146   ASSERT_FALSE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;",
147                                "double x = .1;"));
148 }
149 
TEST(StmtDataCollector,TestStringLiteral)150 TEST(StmtDataCollector, TestStringLiteral) {
151   ASSERT_TRUE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)",
152                               R"(char x[] = "0";)"));
153   ASSERT_FALSE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)",
154                                R"(char x[] = "1";)"));
155 }
156 
TEST(StmtDataCollector,TestCXXBoolLiteral)157 TEST(StmtDataCollector, TestCXXBoolLiteral) {
158   ASSERT_TRUE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;",
159                               "bool x = false;"));
160   ASSERT_FALSE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;",
161                                "bool x = true;"));
162 }
163 
TEST(StmtDataCollector,TestCharacterLiteral)164 TEST(StmtDataCollector, TestCharacterLiteral) {
165   ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';",
166                               "char x = '0';"));
167   ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"),
168                               R"(char x = '\0';)",
169                               R"(char x = '\x00';)"));
170   ASSERT_FALSE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';",
171                                "char x = '1';"));
172 }
173