teemperor created this revision. teemperor added reviewers: v.g.vassilev, zaks.anna. teemperor added a subscriber: cfe-commits.
The ASTStructure class generates data for performant searching Stmts with similar structure. http://reviews.llvm.org/D20795 Files: include/clang/AST/ASTStructure.h include/clang/Basic/SourceManager.h lib/AST/ASTStructure.cpp lib/AST/CMakeLists.txt lib/Sema/SemaChecking.cpp unittests/AST/ASTStructureTest.cpp unittests/AST/CMakeLists.txt
Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_unittest(ASTTests ASTContextParentMapTest.cpp ASTImporterTest.cpp + ASTStructureTest.cpp ASTTypeTraitsTest.cpp ASTVectorTest.cpp CommentLexer.cpp Index: unittests/AST/ASTStructureTest.cpp =================================================================== --- /dev/null +++ unittests/AST/ASTStructureTest.cpp @@ -0,0 +1,679 @@ +//===- unittests/AST/ASTStructureTest.cpp --- AST structure hasing tests-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for ASTStructure. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "clang/AST/ASTStructure.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang; + +class FunctionFinder : public RecursiveASTVisitor<FunctionFinder> { + std::string FunctionName; + FunctionDecl *FoundFunction = nullptr; + +public: + FunctionFinder(const std::string &FunctionName) + : FunctionName(FunctionName) {} + + FunctionDecl *getDecl() { return FoundFunction; } + + bool VisitFunctionDecl(FunctionDecl *D) { + if (D->getQualifiedNameAsString() == FunctionName) { + FoundFunction = D; + return false; + } + return true; + } +}; + +// Returns true if the declaration with the given qualified name +// is hashed when processing the given code. +bool isHashed(const std::string &DeclName, const std::string &code) { + auto ASTUnit = tooling::buildASTFromCode(code); + ASTContext &ASTContext = ASTUnit->getASTContext(); + + auto Hash = ASTStructure(ASTUnit->getASTContext()); + + FunctionFinder Finder(DeclName); + Finder.TraverseTranslationUnitDecl(ASTContext.getTranslationUnitDecl()); + + assert(Finder.getDecl()); + return Hash.findHash(Finder.getDecl()->getBody()).Success; +} + +class FindStmt : public RecursiveASTVisitor<FindStmt> { + Stmt::StmtClass NeededStmtClass; + Stmt *FoundStmt = nullptr; + +public: + FindStmt(Stmt::StmtClass NeededStmtClass) + : NeededStmtClass(NeededStmtClass) {} + + Stmt *getStmt() { return FoundStmt; } + + bool VisitStmt(Stmt *S) { + if (S->getStmtClass() == NeededStmtClass) { + FoundStmt = S; + return false; + } + return true; + } +}; + +// Returns true if any Stmt with the given class +// is hashed when processing the given code. +bool isHashed(Stmt::StmtClass NeededStmtClass, const std::string &code) { + auto ASTUnit = tooling::buildASTFromCode(code); + ASTContext &ASTContext = ASTUnit->getASTContext(); + + auto Hash = ASTStructure(ASTUnit->getASTContext()); + + FindStmt Finder(NeededStmtClass); + Finder.TraverseTranslationUnitDecl(ASTContext.getTranslationUnitDecl()); + + assert(Finder.getStmt()); + return Hash.findHash(Finder.getStmt()).Success; +} + +// Returns true if the Decls with the given qualified names +// have similar structure according to ASTStructure. +bool compareStructure(const std::string &DeclNameA, + const std::string &DeclNameB, const std::string &codeA, + const std::string &codeB) { + auto ASTUnitA = tooling::buildASTFromCodeWithArgs( + codeA, {"-std=c++1z", "-fms-extensions"}); + auto ASTUnitB = tooling::buildASTFromCodeWithArgs( + codeB, {"-std=c++1z", "-fms-extensions"}); + + auto HashA = ASTStructure(ASTUnitA->getASTContext()); + auto HashB = ASTStructure(ASTUnitB->getASTContext()); + + auto &ASTContextA = ASTUnitA->getASTContext(); + auto &ASTContextB = ASTUnitB->getASTContext(); + + FunctionFinder FinderA(DeclNameA); + FinderA.TraverseTranslationUnitDecl(ASTContextA.getTranslationUnitDecl()); + assert(FinderA.getDecl()); + + FunctionFinder FinderB(DeclNameB); + FinderB.TraverseTranslationUnitDecl(ASTContextB.getTranslationUnitDecl()); + assert(FinderB.getDecl()); + + auto HashSearchA = HashA.findHash(FinderA.getDecl()->getBody()); + auto HashSearchB = HashB.findHash(FinderB.getDecl()->getBody()); + assert(HashSearchA.Success); + assert(HashSearchB.Success); + return HashSearchA.Hash == HashSearchB.Hash; +} + +// Returns true if the given Stmts +// have similar structure according to ASTStructure. +bool compareStmt(const std::string &codeA, const std::string &codeB) { + return compareStructure("x", "x", "void x() { " + codeA + "}", + "void x() { " + codeB + "}"); +} + +TEST(ASTStructure, IfStmt) { + ASSERT_TRUE(compareStmt("if (true) {}", "if (false) {}")); + ASSERT_FALSE(compareStmt("if (true) { int x; }", "if (false) {}")); + ASSERT_FALSE(compareStmt("if (int y = 0) {}", "if (false) {}")); +} + +TEST(ASTStructure, StmtExpr) { + ASSERT_TRUE( + compareStmt("int v = ({int x = 4; x;});", "int v = ({int y = 5; y;});")); + ASSERT_FALSE(compareStmt("int v = ({int x = 4 + 4; x;});", + "int v = ({int y = 5; y;});")); + ASSERT_FALSE(compareStmt("int v = ({int x = 5; x;});", + "int v = ({int y = 5; y++; y;});")); +} + +TEST(ASTStructure, MSDependentExistsStmt) { + // Check that __if_exists != __if_not_exists + ASSERT_FALSE(compareStructure("x", "x", + R"test( + template<typename T> + void x(T &t) { + __if_exists (T::foo) { + } + } + )test", + R"test( + template<typename T> + void x(T &t) { + __if_not_exists (T::foo) { + } + } + )test")); +} + +TEST(ASTStructure, DeclStmt) { + // Check that types don't influence the structure. + ASSERT_TRUE(compareStmt("int y = 0;", "long v = 0;")); + // Check that the initialization expression influences the structure. + ASSERT_FALSE(compareStmt("int y = 0;", "int y = (1 + 1);")); + // Check that multiple declarations != single declarations + ASSERT_FALSE(compareStmt("int a, b = 0;", "int b = 0;")); +} + +TEST(ASTStructure, ArraySubscriptExpr) { + // Check that the parameter influences the structure + ASSERT_FALSE(compareStmt("int i[2]; i[1] = 0;", "int i[2]; i[1 + 0] = 0;")); + // Reverse check from above + ASSERT_FALSE( + compareStmt("int i[2]; (1)[i] = 0;", "int i[2]; (1 + 0)[i] = 0;")); + // Previous two checks mixed together. + ASSERT_FALSE( + compareStmt("int i[2]; (i)[1 + 0] = 0;", "int i[2]; (1 + 0)[i] = 0;")); +} + +TEST(ASTStructure, ConditionalOperator) { + ASSERT_TRUE(compareStmt("int y = true ? 1 : 0;", "int x = false ? 0 : 1;")); + // first operand different + ASSERT_FALSE( + compareStmt("int y = true == true ? 1 : 0;", "int y = false ? 1 : 0;")); + // second operand different + ASSERT_FALSE( + compareStmt("int y = true ? 1 : 0;", "int y = true ? 1 + 1 : 0;")); + // third operand different + ASSERT_FALSE( + compareStmt("int y = true ? 1 : 0;", "int y = true ? 1 : 0 + 0;")); + + // Check GNU version is also correctly handled + ASSERT_FALSE(compareStmt("int y = 1 ? : 0;", "int y = 1 ? : 0 + 0;")); + ASSERT_FALSE(compareStmt("int y = 1 ? : 0;", "int y = 1 + 1 ? : 0;")); +} + +TEST(ASTStructure, CXXFold) { + // Different operators influence structure + ASSERT_FALSE(compareStructure( + "x", "x", + "template<class... A> bool x(A... args) { return (... && args); }", + "template<class... A> bool x(A... args) { return (... || args); }")); + // Right-side vs left-side influences structure + ASSERT_FALSE(compareStructure( + "x", "x", + "template<class... A> bool x(A... args) { return (... && args); }", + "template<class... A> bool x(A... args) { return (args && ...); }")); +} + +TEST(ASTStructure, CXXOperatorCallExpr) { + // Different operands influence structure + ASSERT_FALSE(compareStructure("x", "x", + R"test( + class X { + public: + void operator-=(int i); + void operator+=(int i); + }; + void x() { X x; x += 1; } + )test", + + R"test( + class X { + public: + void operator-=(int i); + void operator+=(int i); + }; + void x() { X x; x -= 1; } + )test")); +} + +TEST(ASTStructure, CXXTryStmt) { + ASSERT_TRUE(compareStmt("try { int x; } catch (int x) {}", + "try { int y; } catch (int x) {}")); + // Different body influences structure. + ASSERT_FALSE(compareStmt("try { int x; } catch (int x) {}", + "try { } catch (int x) {}")); +} + +TEST(ASTStructure, DoStmt) { + ASSERT_TRUE(compareStmt("do { int x; } while (true);", + "do { int y; } while (false);")); + // Different body influences structure. + ASSERT_FALSE( + compareStmt("do { int x; } while (true);", "do { } while (true);")); + // Different condition influences structure. + ASSERT_FALSE(compareStmt("int v; do { int x; } while ((v = 1));", + "int v; do { int y; } while (true);")); +} + +TEST(ASTStructure, GCCAsmStmt) { + // Different assembly influences structure + ASSERT_FALSE(compareStmt( + R"test( + int a, b = 1; + asm ("mov %1, %0\n\t" + "add $1, %0" + : "=r" (a) + : "r" (b)); + + )test", + R"test( + int a, b = 1; + // NOTE: different asm here + asm ("mov %1, %1\n\t" + "add $1, %0" + : "=r" (a) + : "r" (b)); + + )test")); + // Different output operands influences structure + ASSERT_FALSE(compareStmt( + R"test( + int a, b = 1, c = 1; + asm ("mov %1, %0\n\t" + "add $1, %0" + : "=r" (a) + : "r" (b)); + )test", + R"test( + int a, b = 1, c = 1; + asm ("mov %1, %0\n\t" + "add $1, %0" + : "=r" (a) + // NOTE: Different output operands + : "r" (b), "r" (c)); + )test")); + // TODO Check different input operands +} + +TEST(ASTStructure, LambdaExpr) { + ASSERT_TRUE(compareStmt("auto a = [](){ return 1; };", + "auto a = [](){ return 2; };")); + // Different lambda body influences structure. + ASSERT_FALSE(compareStmt("int i; auto a = [](){ return 2; };", + "int i; auto a = [](){ return 1 + 1; };")); + // Different capture types influence structure. + ASSERT_FALSE(compareStmt("int i; auto a = [i](){ return 1; };", + "int i; auto a = [&i](){ return 1; };")); + // Different amount of captures influences structure. + ASSERT_FALSE(compareStmt("int i, j; auto a = [i](){ return 1; };", + "int i, j; auto a = [i, j](){ return 1; };")); + // Different function signature influences structure. + ASSERT_FALSE(compareStmt("auto a = [](int i){ return i; };", + "auto a = [](int i, int b){ return i; };")); +} + +TEST(ASTStructure, CompoundStmt) { + ASSERT_TRUE(compareStmt("int x; int y;", "int x; int y;")); + // Different amount of Stmts influence structure. + ASSERT_FALSE(compareStmt("int x;", "int x; int y;")); +} + +TEST(ASTStructure, Labels) { + ASSERT_TRUE(compareStmt("lbl: goto lbl;", "lbl: goto lbl;")); + ASSERT_TRUE(compareStmt("void* lbladdr = &&lbl; goto *lbladdr; lbl:;", + "void* lbladdr = &&lbl; goto *lbladdr; lbl:;")); + // Different label names influence structure. + // FIXME: Put lbl names into feature vectors if possible. + ASSERT_FALSE(compareStmt("lbl: goto lbl;", "lbl2: goto lbl2;")); + // Same as above with indirect goto. + ASSERT_FALSE( + compareStmt("lbl2:; void* lbladdr = &&lbl; goto *lbladdr; lbl:;", + "lbl2:; void* lbladdr = &&lbl2; goto *lbladdr; lbl:;")); +} + +TEST(ASTStructure, WhileStmt) { + ASSERT_TRUE( + compareStmt("while (true) { int x; }", "while (false) { int y; }")); + ASSERT_TRUE(compareStmt("while (int v = 0) { int x; }", + "while (int w = 0) { int y; }")); + // Different bodies influence structure. + ASSERT_FALSE(compareStmt("while (true) { int x; }", "while (false) { }")); + // Different condition influences structure. + ASSERT_FALSE(compareStmt("int v; while ((v = 0)) { int x; }", + "int v; while (false) { int y; }")); +} + +TEST(ASTStructure, NumberLiterals) { + // Test that different number literals always + // result in the same structure. + // Some of the tests have implicit casts in them that also + // need to be ignored by ASTStructure to pass the test. + ASSERT_TRUE(compareStmt("double x = 1;", "double x = 1l;")); + ASSERT_TRUE(compareStmt("double x = 1u;", "double x = 1l;")); + ASSERT_TRUE(compareStmt("double x = 1.0;", "long x = 1;")); + ASSERT_TRUE(compareStmt("double x = 1.0f;", "double x = 1l;")); +} + +TEST(ASTStructure, AtomicExpr) { + ASSERT_TRUE( + compareStmt("int i[2]; __atomic_store_n(i, 1, __ATOMIC_RELAXED);", + "int j[2]; __atomic_store_n(j, 1, __ATOMIC_RELAXED);")); + // Test first argument influences structure. + ASSERT_FALSE( + compareStmt("int i[2]; __atomic_store_n(i, 1, __ATOMIC_RELAXED);", + "int i[2]; __atomic_store_n(i + 1, 1, __ATOMIC_RELAXED);")); + // Test second argument influences structure. + ASSERT_FALSE( + compareStmt("int i[2]; __atomic_store_n(i, 1, __ATOMIC_RELAXED);", + "int i[2]; __atomic_store_n(i, 1 + 1, __ATOMIC_RELAXED);")); + // Test that different builtin types influence structure. + ASSERT_FALSE( + compareStmt("int i[2]; __atomic_exchange_n(i, 1, __ATOMIC_RELAXED);", + "int i[2]; __atomic_store_n (i, 1, __ATOMIC_RELAXED);")); +} + +TEST(ASTStructure, BinaryOperator) { + ASSERT_TRUE(compareStructure("x", "x", "int x() { return 1 + 4 * 8; }", + "int x() { return 2 + 3 * 9; }")); + // Different operations influence structure. + ASSERT_FALSE(compareStructure("x", "x", "int x() { return 1 + 4 - 8; }", + "int x() { return 2 + 3 * 9; }")); +} + +TEST(ASTStructure, UnaryOperator) { + ASSERT_TRUE(compareStructure("x", "x", "int x() { return -8; }", + "int x() { return -9; }")); + // Different operations influence structure. + ASSERT_FALSE(compareStructure("x", "x", "int x() { return -8; }", + "int x() { return +8; }")); +} + +TEST(ASTStructure, InitListExpr) { + ASSERT_TRUE(compareStructure( + "x", "x", "struct A {int a, b, c; }; void x() { A a = {1, 2, 3}; }", + "struct A {int a, b, c; }; void x() { A a = {4, 5, 6}; }")); + // Subexprs in the list influence structure. + ASSERT_FALSE(compareStructure( + "x", "x", "struct A {int a, b, c; }; void x() { A a = {1 + 1, 2, 3}; }", + "struct A {int a, b, c; }; void x() { A a = {2, 2, 3}; }")); +} + +TEST(ASTStructure, Casting) { + ASSERT_TRUE(compareStructure("x", "x", + "int x() { return static_cast<unsigned>(1); }", + "int x() { return static_cast<long>(1); }")); + // Different cast types don't influence structure. + ASSERT_TRUE(compareStructure("x", "x", + "int x() { return static_cast<unsigned>(1); }", + "int x() { return static_cast<long>(1); }")); + // Different kinds of casts influence structure. + ASSERT_FALSE(compareStructure( + "x", "x", "int i[2] = {0, 0}; int *x() { return static_cast<int *>(i); }", + "const int i[2] = {0, 0}; int *x() { return const_cast<int *>(i); }")); + + // Different arguments influence structure. + ASSERT_FALSE( + compareStructure("x", "x", "int x() { return static_cast<unsigned>(1); }", + "int x() { return static_cast<unsigned>(1 + 1); }")); + ASSERT_FALSE(compareStructure("x", "x", "int x() { return (int) (1 + 1); }", + "int x() { return (int) (1); }")); +} + +TEST(ASTStructure, CXXCatchStmt) { + ASSERT_TRUE( + compareStmt("try {} catch (long x) {}", "try {} catch (int x) {}")); + // Body influences structure. + ASSERT_FALSE( + compareStmt("try {} catch (...) { int x; }", "try {} catch (...) {}")); + // Catch-all influences structure. + ASSERT_FALSE(compareStmt("try {} catch (int x) {}", "try {} catch (...) {}")); +} + +TEST(ASTStructure, ForStmt) { + // Two functions with different + // naming conventions but doing the same need + // to have the same structure. + ASSERT_TRUE(compareStructure( + "array_sum", "ArraySum", "int array_sum(int* array, unsigned len) {\n" + " int sum = 0;\n" + " for (unsigned i = 0; i < len; i++)\n" + " sum += array[i];\n" + " return sum;\n" + "}\n", + + "int ArraySum(int* InputArray, unsigned Length) {\n" + " int Sum = 0;\n" + " for (unsigned j = 0; j < Length; j++)\n" + " Sum += InputArray[j];\n" + " return Sum;\n" + "}\n")); + + ASSERT_FALSE(compareStructure( + "array_sum", "array_sum", "int array_sum(int* array, unsigned len) {" + " int sum = 0;" + " for (unsigned i = 0; i < len; i++)" + " sum += array[i];" + " return sum;" + "}", + + "int array_sum(int* array, unsigned len) {" + " int sum = 0;" + " for (unsigned i = 0; i < len; i++)" + " sum += array[i];" + " if (sum < 0) return 0;" // Note that we added an if here and so the + // structure changed + " return sum;" + "}")); +} + +// Macro tests + +TEST(ASTStructure, MacroTest) { + // Code that is an macro argument should be hashed + ASSERT_TRUE(isHashed(Stmt::StmtClass::DoStmtClass, + R"test( + #define GTEST1(Code) void foo() { Code } + #define GTEST2(Code) GTEST1({ int gtest_var; Code }) + GTEST2({ + do { + int i = 0; + int j = 2; + } while(0); + }) + )test")); + // Function is generated by macro and shouldn't be hashed. + ASSERT_FALSE(isHashed("foo", + R"test( + #define GTEST1(Code) void foo() { Code } + #define GTEST2(Code) GTEST1({ while(false){} int gtest_var; Code }) + GTEST2({ + do { + int i = 0; + int j = 2; + } while(0); + }) + )test")); + // Code that is in macro bodies shouldn't be hashed + ASSERT_FALSE(isHashed(Stmt::StmtClass::WhileStmtClass, + R"test( + #define GTEST1(Code) void foo() { Code } + #define GTEST2(Code) GTEST1({ while(false){} Code }) + GTEST2({}) + )test")); +} + +// Imported tests from the related GSoC 2015 project +TEST(ASTStructure, GSoC2015CompoundStmt) { + ASSERT_TRUE(compareStructure("x", "x", + R"test( + void x() { + int a, b, c, d, e; + a++; + b--; + c++; + d--; + e--; + d*=2; + } + )test", + + R"test( + void x() { + int z, x, q, w, r; + z++; + x--; + q++; + w--; + r--; + w*=2; + } + )test")); +} + +TEST(ASTStructure, GSoC2015CompoundStmtLocals) { + ASSERT_TRUE(compareStructure("x", "x", + R"test( + void x() { + int one = 21, + two = -1; + if (one == 0) { + two++; + for (two = 0; two < 10; two++) { + one--; + } + one = 21; + } + } + )test", + + R"test( + void x() { + int a = 21, + b = -1; + if (a == 0) { + b++; + for (b = 0; b < 10; b++) { + a--; + } + a = 21; + } + } + )test")); +} + +TEST(ASTStructure, CompoundStmtLocal) { + ASSERT_TRUE(compareStructure("x", "x", + R"test( + int global_one, + global_two; + + void x() { + global_one = 21; + if (global_one == 0) { + global_two++; + for (global_two = 0; global_two < 10; global_two++) { + global_one--; + } + global_one = 21; + } + } + )test", + + R"test( + int global_one, + global_two; + + void x() { + global_two = 21; + if (global_two == 0) { + global_one++; + for (global_one = 0; global_one < 10; global_one++) { + global_two--; + } + global_two = 21; + } + } + )test")); +} + +// Use case tests + +// This is the example from the project proposal: +// Two test cases testing the setWidth and setHeight method +// of the Image class. The setHeight test however is an +// clone of the setWidth test case and should be detected as such. +TEST(ASTStructure, ImageTest) { + ASSERT_TRUE(compareStructure("testWidthRanges", "testHeightRanges", + R"test(struct Image { + int width() { return 0; } + int height() { return 0; } + void setWidth(int x) {} + void setHeight(int y) {} + }; + void assert(bool); + void testWidthRanges() { + Image img; + img.setWidth(0); + assert(img.width() == 0); + img.setWidth(1); + assert(img.width() == 1); + try { + img.setWidth(-1); + assert(false); + } catch (...) { } + })test", + + R"test(struct Image { + int width() { return 0; } + int height() { return 0; } + void setWidth(int x) {} + void setHeight(int y) {} + }; + void assert(bool); + void testHeightRanges() { + Image img; + img.setHeight(0); + assert(img.height() == 0); + img.setHeight(1); + assert(img.height() == 1); + try { + // NOTE: Failed to adapt clone here + img.setWidth(-1); + assert(false); + } catch (...) { } + })test")); + + ASSERT_FALSE(compareStructure("testWidthRanges", "testHeightRanges", + R"test(struct Image { + int width() { return 0; } + int height() { return 0; } + void setWidth(int x) {} + void setHeight(int y) {} + }; + void assert(bool); + void testWidthRanges() { + Image img; + img.setWidth(0); + assert(img.width() == 0); + img.setWidth(1); + assert(img.width() == 1); + try { + img.setWidth(-1); + assert(false); + } catch (...) { } + })test", + + R"test(struct Image { + int width() { return 0; } + int height() { return 0; } + void setWidth(int x) {} + void setHeight(int y) {} + }; + void assert(bool); + void testHeightRanges() { + Image img; + img.setHeight(0); + assert(img.height() == 0); + // NOTE: Missing img.setHeight(1); + assert(img.height() == 1); + try { + // NOTE: Failed to adapt clone here + img.setWidth(-1); + assert(false); + } catch (...) { } + })test")); +} Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -8144,22 +8144,6 @@ return true; } -// Returns true if the SourceLocation is expanded from any macro body. -// Returns false if the SourceLocation is invalid, is from not in a macro -// expansion, or is from expanded from a top-level macro argument. -static bool IsInAnyMacroBody(const SourceManager &SM, SourceLocation Loc) { - if (Loc.isInvalid()) - return false; - - while (Loc.isMacroID()) { - if (SM.isMacroBodyExpansion(Loc)) - return true; - Loc = SM.getImmediateMacroCallerLoc(Loc); - } - - return false; -} - /// \brief Diagnose pointers that are always non-null. /// \param E the expression containing the pointer /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is @@ -8175,8 +8159,8 @@ // Don't warn inside macros. if (E->getExprLoc().isMacroID()) { const SourceManager &SM = getSourceManager(); - if (IsInAnyMacroBody(SM, E->getExprLoc()) || - IsInAnyMacroBody(SM, Range.getBegin())) + if (SM.IsInAnyMacroBody(E->getExprLoc()) || + SM.IsInAnyMacroBody(Range.getBegin())) return; } E = E->IgnoreImpCasts(); Index: lib/AST/CMakeLists.txt =================================================================== --- lib/AST/CMakeLists.txt +++ lib/AST/CMakeLists.txt @@ -7,6 +7,7 @@ ASTDiagnostic.cpp ASTDumper.cpp ASTImporter.cpp + ASTStructure.cpp ASTTypeTraits.cpp AttrImpl.cpp CXXInheritance.cpp Index: lib/AST/ASTStructure.cpp =================================================================== --- /dev/null +++ lib/AST/ASTStructure.cpp @@ -0,0 +1,464 @@ +//===--- ASTStructure.cpp - Analyses the structure of an AST ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the StructuralHash class. +// +//===----------------------------------------------------------------------===// + +#include <clang/AST/ASTStructure.h> +#include <clang/AST/RecursiveASTVisitor.h> +#include <clang/AST/ASTContext.h> + +#include <iostream> + +using namespace clang; + +namespace { + +class StructuralHashVisitor + : public RecursiveASTVisitor<StructuralHashVisitor> { + +public: + explicit StructuralHashVisitor(ASTStructure &Hash, ASTContext &Context) + : SH(Hash), Context(Context) {} + + ~StructuralHashVisitor() { + // As each hash value is only saved when the calculation of the next + // Stmt starts, we need to explicitly save the hash value of + // the last Stmt here. + SaveCurrentHash(); + } + + bool shouldTraversePostOrder() const { return true; } + + bool PostVisitStmt(Stmt *S) { + // At this point we know that all Visit* calls for the previous Stmt have + // finished, so we know save the calculated hash before starting + // to calculate the next hash. + SaveCurrentHash(); + + // PostVisitStmt is the first method to be called for a new + // Stmt, so we save what Stmt we are currently processing + // for SaveCurrentHash. + CurrentStmt = S; + // Reset the initial hash code for this Stmt. + Hash = 0; + ClassHash = S->getStmtClass(); + // Reset options for the current hash code. + SkipHash = false; + IgnoreClassHash = false; + + if (shouldSkipStmt(S)) + return Skip(); + + // Incorporate the hash values of all child Stmts into the current. + // Hash value. + for (Stmt *Child : S->children()) { + if (Child == nullptr) { + // We use an placeholder value for missing children. + CalcHash(313); + } else { + CalcHash(Child); + } + } + + return true; + } + +// Define all PostVisit methods for all possible Stmts. +#define STMT(CLASS, PARENT) bool PostVisit##CLASS(CLASS *S); +#include "clang/AST/StmtNodes.inc" + +private: + + bool shouldSkipStmt(Stmt *S) { + switch (S->getStmtClass()) { + case Stmt::FloatingLiteralClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::IntegerLiteralClass: + return false; + default: + return Context.getSourceManager().IsInAnyMacroBody(S->getLocStart()) || + Context.getSourceManager().IsInAnyMacroBody(S->getLocEnd()); + } + } + + // Marks the current Stmt as no to be processed. + // Always returns \c true that it can be called + bool Skip() { + SkipHash = true; + return true; + } + + // Merges the given value into the current hash code. + void CalcHash(unsigned Value) { + // We follow the same idea as Java's hashCode(): + // Multiply with an prime, then add the new value to the + // hash code. + Hash *= 53; + Hash += Value; + } + + void CalcHash(const llvm::StringRef &String) { + for (char c : String) { + CalcHash(static_cast<unsigned>(c)); + } + } + + // Merges the hash code of the given Stmt into the + // current hash code. Stmts that weren't hashed before by this visitor + // are ignored. + void CalcHash(Stmt *S) { + auto I = SH.findHash(S); + if (I.Success) { + CalcHash(I.Hash); + } + } + + // Saves the current hash code into the persistent storage of this + // ASTStructure. + void SaveCurrentHash() { + if (SkipHash) { + return; + } + if (!IgnoreClassHash) { + Hash += ClassHash; + } + SH.add(Hash, CurrentStmt); + CurrentStmt = nullptr; + } + + ASTStructure &SH; + ASTContext &Context; + + // The current statement that is being hashed at the moment + // or 0 if there is no statement currently hashed. + Stmt *CurrentStmt = nullptr; + + // All members specify properties of the hash process for the current + // Stmt. They are resetted after the Stmt is successfully hased. + + // If the current hash should not be saved for later use. + bool SkipHash = false; + + // The hash value that is calculated right now. + unsigned Hash; + + // If true, the current Hash only depends on custom data of the + // current Stmt and the child values. + // Use case for this are implicit Stmts that need their child to be + // processed but shouldn't influence the hash value of the parent. + bool IgnoreClassHash = false; + + // A hash value that is unique to the current Stmt class. + // By default, this value is merged with the \c Hash variable + // for the resulting + unsigned ClassHash; +}; + +#define DEF_STMT_VISIT(CLASS, CODE) \ + bool StructuralHashVisitor::PostVisit##CLASS(CLASS *S) { \ + if (SkipHash) \ + return true; \ + { CODE; } \ + return true; \ + } + +//--- Builtin functionality ------------------------------------------------// +DEF_STMT_VISIT(ArrayTypeTraitExpr, { CalcHash(S->getTrait()); }) +DEF_STMT_VISIT(AsTypeExpr, {}) +DEF_STMT_VISIT(AtomicExpr, { + CalcHash(S->isVolatile()); + CalcHash(S->getOp()); +}) +DEF_STMT_VISIT(ChooseExpr, {}) +DEF_STMT_VISIT(ConvertVectorExpr, {}) +DEF_STMT_VISIT(CXXNoexceptExpr, {}) +DEF_STMT_VISIT(ExpressionTraitExpr, { CalcHash(S->getTrait()); }) +DEF_STMT_VISIT(ShuffleVectorExpr, {}) +DEF_STMT_VISIT(PredefinedExpr, { CalcHash(S->getIdentType()); }) +DEF_STMT_VISIT(TypeTraitExpr, { CalcHash(S->getTrait()); }) +DEF_STMT_VISIT(VAArgExpr, {}) + +//--- MS properties --------------------------------------------------------// +DEF_STMT_VISIT(MSPropertyRefExpr, {}) +DEF_STMT_VISIT(MSPropertySubscriptExpr, {}) + +//--- Calls ----------------------------------------------------------------// +DEF_STMT_VISIT(CXXOperatorCallExpr, { CalcHash(S->getOperator()); }) +DEF_STMT_VISIT(CXXMemberCallExpr, {}) +DEF_STMT_VISIT(CallExpr, {}) + +//--- Invalid code ---------------------------------------------------------// +// We don't support hasing invalid code, so we skip all Stmts representing +// invalid code. +DEF_STMT_VISIT(TypoExpr, { return Skip(); }) +DEF_STMT_VISIT(UnresolvedLookupExpr, { return Skip(); }) +DEF_STMT_VISIT(UnresolvedMemberExpr, { return Skip(); }) +DEF_STMT_VISIT(CXXUnresolvedConstructExpr, { return Skip(); }) +DEF_STMT_VISIT(OverloadExpr, { return Skip(); }) + +//--- Exceptions -----------------------------------------------------------// +DEF_STMT_VISIT(CXXThrowExpr, {}) +DEF_STMT_VISIT(CXXCatchStmt, { + if (S->getExceptionDecl()) + CalcHash(829); +}) +DEF_STMT_VISIT(CXXTryStmt, {}) +DEF_STMT_VISIT(SEHExceptStmt, {}) +DEF_STMT_VISIT(SEHFinallyStmt, {}) +DEF_STMT_VISIT(SEHLeaveStmt, {}) +DEF_STMT_VISIT(SEHTryStmt, {}) + +//--- Literals -------------------------------------------------------------// +DEF_STMT_VISIT(CharacterLiteral, { + // We treat all literals as integer literals + // as the hash is type independent + ClassHash = Stmt::StmtClass::IntegerLiteralClass; +}) +DEF_STMT_VISIT(FloatingLiteral, { + // We treat all literals as integer literals + // as the hash is type independent + ClassHash = Stmt::StmtClass::IntegerLiteralClass; +}) +DEF_STMT_VISIT(ImaginaryLiteral, { + // We treat all literals as integer literals + // as the hash is type independent + ClassHash = Stmt::StmtClass::IntegerLiteralClass; +}) +DEF_STMT_VISIT(IntegerLiteral, {}) + +DEF_STMT_VISIT(ObjCBoolLiteralExpr, {}) +DEF_STMT_VISIT(CXXBoolLiteralExpr, {}) + +DEF_STMT_VISIT(StringLiteral, {}) +DEF_STMT_VISIT(ObjCStringLiteral, {}) + +DEF_STMT_VISIT(CompoundLiteralExpr, {}) + +DEF_STMT_VISIT(GNUNullExpr, {}) +DEF_STMT_VISIT(CXXNullPtrLiteralExpr, {}) + +// TODO implement hashing for this +DEF_STMT_VISIT(UserDefinedLiteral, { return Skip(); }) + +//--- C++-OOP Stmts --------------------------------------------------------// +DEF_STMT_VISIT(MemberExpr, {}) +DEF_STMT_VISIT(CXXNewExpr, {}) +DEF_STMT_VISIT(CXXThisExpr, {}) +DEF_STMT_VISIT(CXXConstructExpr, {}) +DEF_STMT_VISIT(CXXDeleteExpr, { + CalcHash(S->isArrayFormAsWritten()); + CalcHash(S->isGlobalDelete()); +}) +DEF_STMT_VISIT(DesignatedInitExpr, {}) +DEF_STMT_VISIT(DesignatedInitUpdateExpr, {}) +DEF_STMT_VISIT(NoInitExpr, {}) +DEF_STMT_VISIT(InitListExpr, {}) +DEF_STMT_VISIT(CXXStdInitializerListExpr, {}) + +DEF_STMT_VISIT(CXXTemporaryObjectExpr, { IgnoreClassHash = true; }) +DEF_STMT_VISIT(CXXDefaultArgExpr, {}) +DEF_STMT_VISIT(CXXDefaultInitExpr, {}) + +//--- Casts ----------------------------------------------------------------// +DEF_STMT_VISIT(CXXFunctionalCastExpr, {}) + +DEF_STMT_VISIT(CXXNamedCastExpr, {}) +DEF_STMT_VISIT(CXXConstCastExpr, {}) +DEF_STMT_VISIT(CXXDynamicCastExpr, {}) +DEF_STMT_VISIT(CXXReinterpretCastExpr, {}) +DEF_STMT_VISIT(CXXStaticCastExpr, {}) + +DEF_STMT_VISIT(ImplicitCastExpr, { IgnoreClassHash = true; }) + +DEF_STMT_VISIT(CastExpr, {}) +DEF_STMT_VISIT(ExplicitCastExpr, {}) +DEF_STMT_VISIT(CStyleCastExpr, {}) +DEF_STMT_VISIT(ObjCBridgedCastExpr, { CalcHash(S->getBridgeKind()); }) + +//--- Miscellaneous Exprs --------------------------------------------------// +DEF_STMT_VISIT(Expr, {}) +DEF_STMT_VISIT(ParenExpr, {}) +DEF_STMT_VISIT(ArraySubscriptExpr, {}) +DEF_STMT_VISIT(BinaryOperator, { CalcHash(S->getOpcode()); }) +DEF_STMT_VISIT(UnaryOperator, { CalcHash(S->getOpcode()); }) + +//--- Control flow ---------------------------------------------------------// +DEF_STMT_VISIT(ForStmt, {}) +DEF_STMT_VISIT(GotoStmt, {}) +DEF_STMT_VISIT(IfStmt, {}) +DEF_STMT_VISIT(WhileStmt, {}) +DEF_STMT_VISIT(IndirectGotoStmt, {}) +DEF_STMT_VISIT(LabelStmt, { CalcHash(S->getDecl()->getName()); }) +DEF_STMT_VISIT(MSDependentExistsStmt, { CalcHash(S->isIfExists()); }) +DEF_STMT_VISIT(AddrLabelExpr, { CalcHash(S->getLabel()->getName()); }) +DEF_STMT_VISIT(BreakStmt, {}) +DEF_STMT_VISIT(CompoundStmt, {}) +DEF_STMT_VISIT(ContinueStmt, {}) +DEF_STMT_VISIT(DoStmt, {}) + +DEF_STMT_VISIT(SwitchStmt, {}) +DEF_STMT_VISIT(SwitchCase, {}) +DEF_STMT_VISIT(CaseStmt, {}) +DEF_STMT_VISIT(DefaultStmt, {}) + +DEF_STMT_VISIT(ReturnStmt, {}) + +DEF_STMT_VISIT(AbstractConditionalOperator, {}) +DEF_STMT_VISIT(BinaryConditionalOperator, {}) +DEF_STMT_VISIT(ConditionalOperator, {}) + +//--- Objective-C ----------------------------------------------------------// +DEF_STMT_VISIT(ObjCArrayLiteral, {}) +DEF_STMT_VISIT(ObjCBoxedExpr, {}) +DEF_STMT_VISIT(ObjCDictionaryLiteral, {}) +DEF_STMT_VISIT(ObjCEncodeExpr, {}) +DEF_STMT_VISIT(ObjCIndirectCopyRestoreExpr, { CalcHash(S->shouldCopy()); }) +DEF_STMT_VISIT(ObjCIsaExpr, {}) +DEF_STMT_VISIT(ObjCIvarRefExpr, {}) +DEF_STMT_VISIT(ObjCMessageExpr, {}) +DEF_STMT_VISIT(ObjCPropertyRefExpr, { + CalcHash(S->isSuperReceiver()); + CalcHash(S->isImplicitProperty()); +}) +DEF_STMT_VISIT(ObjCProtocolExpr, {}) +DEF_STMT_VISIT(ObjCSelectorExpr, {}) +DEF_STMT_VISIT(ObjCSubscriptRefExpr, {}) +DEF_STMT_VISIT(ObjCAtCatchStmt, { CalcHash(S->hasEllipsis()); }) +DEF_STMT_VISIT(ObjCAtFinallyStmt, {}) +DEF_STMT_VISIT(ObjCAtSynchronizedStmt, {}) +DEF_STMT_VISIT(ObjCAtThrowStmt, {}) +DEF_STMT_VISIT(ObjCAtTryStmt, {}) +DEF_STMT_VISIT(ObjCAutoreleasePoolStmt, {}) +DEF_STMT_VISIT(ObjCForCollectionStmt, {}) + +//--- Miscellaneous Stmts --------------------------------------------------// +DEF_STMT_VISIT(CXXFoldExpr, { + CalcHash(S->isRightFold()); + CalcHash(S->getOperator()); +}) +DEF_STMT_VISIT(StmtExpr, {}) +DEF_STMT_VISIT(ExprWithCleanups, {}) +DEF_STMT_VISIT(GenericSelectionExpr, { CalcHash(S->getNumAssocs()); }) +DEF_STMT_VISIT(LambdaExpr, { + for (const LambdaCapture &C : S->captures()) { + CalcHash(C.isPackExpansion()); + CalcHash(C.getCaptureKind()); + } + CalcHash(S->isGenericLambda()); + CalcHash(S->isMutable()); + CalcHash(S->getCallOperator()->param_size()); +}) +DEF_STMT_VISIT(OpaqueValueExpr, { IgnoreClassHash = true; }) +DEF_STMT_VISIT(MaterializeTemporaryExpr, { IgnoreClassHash = true; }) +DEF_STMT_VISIT(SubstNonTypeTemplateParmExpr, {}) +DEF_STMT_VISIT(SubstNonTypeTemplateParmPackExpr, {}) +DEF_STMT_VISIT(DeclStmt, { + auto numDecls = std::distance(S->decl_begin(), S->decl_end()); + CalcHash(537u + static_cast<unsigned>(numDecls)); +}) + +DEF_STMT_VISIT(CompoundAssignOperator, {}) +DEF_STMT_VISIT(CXXBindTemporaryExpr, {}) +DEF_STMT_VISIT(NullStmt, {}) +DEF_STMT_VISIT(CXXScalarValueInitExpr, {}) +DEF_STMT_VISIT(ImplicitValueInitExpr, {}) +DEF_STMT_VISIT(OffsetOfExpr, {}) +DEF_STMT_VISIT(SizeOfPackExpr, {}) +DEF_STMT_VISIT(DeclRefExpr, {}) +DEF_STMT_VISIT(DependentScopeDeclRefExpr, {}) +DEF_STMT_VISIT(CXXPseudoDestructorExpr, {}) +DEF_STMT_VISIT(FunctionParmPackExpr, {}) +DEF_STMT_VISIT(ParenListExpr, {}) +DEF_STMT_VISIT(PackExpansionExpr, {}) +DEF_STMT_VISIT(UnaryExprOrTypeTraitExpr, {}) +DEF_STMT_VISIT(PseudoObjectExpr, {}) +DEF_STMT_VISIT(GCCAsmStmt, { + CalcHash(S->isVolatile()); + CalcHash(S->isSimple()); + CalcHash(S->getAsmString()->getString()); + CalcHash(S->getNumOutputs()); + for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { + CalcHash(S->getOutputName(I)); + VisitStringLiteral(S->getOutputConstraintLiteral(I)); + } + CalcHash(S->getNumInputs()); + for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { + CalcHash(S->getInputName(I)); + CalcHash(S->getInputConstraintLiteral(I)); + } + CalcHash(S->getNumClobbers()); + for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) + CalcHash(S->getClobberStringLiteral(I)); +}) + +// TODO: implement hashing custom data of these Stmts +DEF_STMT_VISIT(AsmStmt, {}) +DEF_STMT_VISIT(MSAsmStmt, {}) +DEF_STMT_VISIT(CXXForRangeStmt, {}) +DEF_STMT_VISIT(CapturedStmt, {}) +DEF_STMT_VISIT(CoreturnStmt, {}) +DEF_STMT_VISIT(CoroutineBodyStmt, {}) +DEF_STMT_VISIT(CoroutineSuspendExpr, {}) +DEF_STMT_VISIT(AttributedStmt, {}) +DEF_STMT_VISIT(BlockExpr, {}) +DEF_STMT_VISIT(CoawaitExpr, {}) +DEF_STMT_VISIT(CoyieldExpr, {}) +DEF_STMT_VISIT(CUDAKernelCallExpr, {}) +DEF_STMT_VISIT(CXXUuidofExpr, {}) +DEF_STMT_VISIT(ExtVectorElementExpr, {}) +DEF_STMT_VISIT(CXXDependentScopeMemberExpr, {}) +DEF_STMT_VISIT(CXXTypeidExpr, {}) + +//--- OpenMP ---------------------------------------------------------------// +// All OMP Stmts don't have any data attached to them, +// so we can just use the default code. +DEF_STMT_VISIT(OMPExecutableDirective, {}) +DEF_STMT_VISIT(OMPAtomicDirective, {}) +DEF_STMT_VISIT(OMPBarrierDirective, {}) +DEF_STMT_VISIT(OMPCancelDirective, {}) +DEF_STMT_VISIT(OMPCancellationPointDirective, {}) +DEF_STMT_VISIT(OMPCriticalDirective, {}) +DEF_STMT_VISIT(OMPFlushDirective, {}) +DEF_STMT_VISIT(OMPLoopDirective, {}) +DEF_STMT_VISIT(OMPDistributeDirective, {}) +DEF_STMT_VISIT(OMPForDirective, {}) +DEF_STMT_VISIT(OMPForSimdDirective, {}) +DEF_STMT_VISIT(OMPParallelForDirective, {}) +DEF_STMT_VISIT(OMPParallelForSimdDirective, {}) +DEF_STMT_VISIT(OMPSimdDirective, {}) +DEF_STMT_VISIT(OMPTaskLoopDirective, {}) +DEF_STMT_VISIT(OMPTaskLoopSimdDirective, {}) +DEF_STMT_VISIT(OMPMasterDirective, {}) +DEF_STMT_VISIT(OMPOrderedDirective, {}) +DEF_STMT_VISIT(OMPParallelDirective, {}) +DEF_STMT_VISIT(OMPParallelSectionsDirective, {}) +DEF_STMT_VISIT(OMPSectionDirective, {}) +DEF_STMT_VISIT(OMPSectionsDirective, {}) +DEF_STMT_VISIT(OMPSingleDirective, {}) +DEF_STMT_VISIT(OMPTargetDataDirective, {}) +DEF_STMT_VISIT(OMPTargetDirective, {}) +DEF_STMT_VISIT(OMPTargetEnterDataDirective, {}) +DEF_STMT_VISIT(OMPTargetExitDataDirective, {}) +DEF_STMT_VISIT(OMPTargetParallelDirective, {}) +DEF_STMT_VISIT(OMPTargetParallelForDirective, {}) +DEF_STMT_VISIT(OMPTaskDirective, {}) +DEF_STMT_VISIT(OMPTaskgroupDirective, {}) +DEF_STMT_VISIT(OMPTaskwaitDirective, {}) +DEF_STMT_VISIT(OMPTaskyieldDirective, {}) +DEF_STMT_VISIT(OMPTeamsDirective, {}) +DEF_STMT_VISIT(OMPArraySectionExpr, {}) +} + +ASTStructure::ASTStructure(ASTContext &Context) { + StructuralHashVisitor visitor(*this, Context); + visitor.TraverseDecl(Context.getTranslationUnitDecl()); +} Index: include/clang/Basic/SourceManager.h =================================================================== --- include/clang/Basic/SourceManager.h +++ include/clang/Basic/SourceManager.h @@ -1169,6 +1169,28 @@ return getDecomposedLoc(SpellingLoc).second; } + + // + + /// \brief Tests whether the given source location is expanded from any + /// macro body. + /// + /// \returns Returns true if the SourceLocation is expanded from any macro + /// body. Returns false if the SourceLocation is invalid, is from not in a + /// macro expansion, or is from expanded from a top-level macro argument. + bool IsInAnyMacroBody(SourceLocation Loc) const { + if (Loc.isInvalid()) + return false; + + while (Loc.isMacroID()) { + if (isMacroBodyExpansion(Loc)) + return true; + Loc = getImmediateMacroCallerLoc(Loc); + } + + return false; + } + /// \brief Tests whether the given source location represents a macro /// argument's expansion into the function-like macro definition. /// Index: include/clang/AST/ASTStructure.h =================================================================== --- /dev/null +++ include/clang/AST/ASTStructure.h @@ -0,0 +1,88 @@ +//===--- AstStructure.h - Analyses the structure of an AST -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ASTStructure class, which can analyse the structure +// of an AST. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_STRUCTURALHASH_H +#define LLVM_CLANG_AST_STRUCTURALHASH_H + +#include <unordered_map> +#include <clang/AST/Stmt.h> + +namespace clang { + +/// ASTStructure - This class analyses the structure of the Stmts +/// in a given AST and is intended to be used to find sub-trees with identical +/// structure. The structure of a tree equals the functionality of the +/// code behind it. +/// +/// Specifically this class provides a locality-sensitive hash function +/// for Stmts that generates colliding hash values +/// for nodes with the same structure. +/// +/// This is done by only hashing +/// information that describes structure (e.g. the type of each +/// node). +/// Other information such as the names of variables, classes +/// and other parts of the program are ignored. +class ASTStructure { + + std::unordered_map<const Stmt *, unsigned> HashedStmts; + +public: + /// + /// \brief ASTStructure generates information about the Stmts in the AST. + /// + /// \param Context The AST that shall be analyzed. + /// + /// Analyses the Stmts in the given AST and stores all information + /// about their structure in the newly created ASTStructure object. + explicit ASTStructure(ASTContext &Context); + + struct HashSearchResult { + unsigned Hash; + bool Success; + }; + + /// + /// \brief Looks up the structure hash code for the given Stmt + /// in the storage of this ASTStructure object. + /// + /// \param S the given Stmt. + /// + /// \returns A \c HashSearchResult containing information of whether + /// the search was successful and if yes the found hash code. + /// + /// The structure hash code is a integer describing the structure + /// of the given Stmt. Stmts with an equal structure hash code probably + /// have the same structure. + HashSearchResult findHash(const Stmt *S) { + auto I = HashedStmts.find(S); + if (I == HashedStmts.end()) { + return {0, false}; + } + return {I->second, true}; + } + /// + /// \brief Adds with the given Stmt with the associated structure hash code + /// to the storage of this ASTStructure object. + /// + /// \param Hash the hash code of the given Stmt. + /// \param S the given Stmt. + void add(unsigned Hash, const Stmt *S) { + HashedStmts.insert(std::make_pair(S, Hash)); + } +}; + +} // end namespace clang + +#endif
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits