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 32 template <class T> void addData(const T &Data) { 33 data_collection::addDataToConsumer(DataConsumer, Data); 34 } 35 36 public: 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; 56 StmtHashMatch(llvm::MD5::MD5Result &Hash) : NumFound(0), Hash(Hash) {} 57 58 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 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 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 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 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 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 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 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 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 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