stettberger updated this revision to Diff 138019.
stettberger marked an inline comment as done.
stettberger added a comment.

Addressed comments, for more details, please see the mail on cfe-devel


Repository:
  rC Clang

https://reviews.llvm.org/D40731

Files:
  include/clang/AST/AttrDataCollectors.td
  include/clang/AST/CHashVisitor.h
  include/clang/AST/CMakeLists.txt
  include/clang/AST/DataCollection.h
  include/clang/AST/DeclDataCollectors.td
  include/clang/AST/StmtDataCollectors.td
  include/clang/AST/TypeDataCollectors.td
  unittests/AST/CHashTest.cpp
  unittests/AST/CMakeLists.txt

Index: unittests/AST/CMakeLists.txt
===================================================================
--- unittests/AST/CMakeLists.txt
+++ unittests/AST/CMakeLists.txt
@@ -17,6 +17,7 @@
   NamedDeclPrinterTest.cpp
   SourceLocationTest.cpp
   StmtPrinterTest.cpp
+  CHashTest.cpp
   )
 
 target_link_libraries(ASTTests
Index: unittests/AST/CHashTest.cpp
===================================================================
--- /dev/null
+++ unittests/AST/CHashTest.cpp
@@ -0,0 +1,116 @@
+//===- unittests/AST/DataCollectionTest.cpp -------------------------------===//
+//
+//                     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 the DataCollection module.
+//
+// They work by hashing the collected data of two nodes and asserting that the
+// hash values are equal iff the nodes are considered equal.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CHashVisitor.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace clang;
+using namespace tooling;
+
+
+class CHashConsumer : public ASTConsumer {
+    CompilerInstance &CI;
+    llvm::MD5::MD5Result *ASTHash;
+
+public:
+
+    CHashConsumer(CompilerInstance &CI, llvm::MD5::MD5Result *ASTHash)
+        : CI(CI), ASTHash(ASTHash){}
+
+    virtual void HandleTranslationUnit(clang::ASTContext &Context) override {
+        TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
+
+        // Traversing the translation unit decl via a RecursiveASTVisitor
+        // will visit all nodes in the AST.
+        CHashVisitor<> Visitor(Context);
+        Visitor.TraverseDecl(TU);
+        // Copy Away the resulting hash
+        *ASTHash = *Visitor.getHash(TU);
+
+    }
+
+    ~CHashConsumer() override {}
+};
+
+struct CHashAction : public ASTFrontendAction {
+    llvm::MD5::MD5Result *Hash;
+
+    CHashAction(llvm::MD5::MD5Result *Hash) : Hash(Hash) {}
+
+    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                   StringRef) override {
+        return std::unique_ptr<ASTConsumer>(new CHashConsumer(CI, Hash));
+    }
+};
+
+static testing::AssertionResult
+isASTHashEqual(StringRef Code1, StringRef Code2) {
+    llvm::MD5::MD5Result Hash1, Hash2;
+    if (!runToolOnCode(new CHashAction(&Hash1), Code1)) {
+        return testing::AssertionFailure()
+            << "Parsing error in (A)\"" << Code1.str() << "\"";
+    }
+    if (!runToolOnCode(new CHashAction(&Hash2), Code2)) {
+        return testing::AssertionFailure()
+            << "Parsing error in (B) \"" << Code2.str() << "\"";
+    }
+    return testing::AssertionResult(Hash1 == Hash2);
+}
+
+TEST(CHashVisitor, TestRecordTypes) {
+    ASSERT_TRUE(isASTHashEqual( // Unused record definition
+                     "struct foobar { int a0; char a1; unsigned long a2; };",
+                     "struct foobar { int a0; char a1;};"
+                     ));
+
+    // Recursive Record Types
+    ASSERT_FALSE(isASTHashEqual( // Names still matter
+                    "struct ll { struct ll* foo; }; struct ll a;",
+                    "struct ll { struct ll* bar; }; struct ll a;"
+                    ));
+
+    ASSERT_TRUE(isASTHashEqual( // Order does not matter
+                     "struct b; struct a { struct b* o; }; struct b { struct a* o; }; struct a X;",
+                     "struct a; struct b { struct a* o; }; struct a { struct b* o; }; struct a X;"
+                     ));
+
+    // The initialization of a struct with a reference to itself should not crash
+    ASSERT_FALSE(isASTHashEqual(
+                    "struct foo { int N; }; struct foo a = { sizeof(a) };",
+                    "struct foo { int N; }; struct foo a = { sizeof(struct foo) };"
+                    ));
+}
+
+TEST(CHashVisitor, TypedefTest) {
+    ASSERT_FALSE(isASTHashEqual(
+                    "typedef int bar_t;  bar_t x;",
+                    "typedef char bar_t; bar_t x;"
+                    ));
+}
+
+
+TEST(CHashVisitor, TestSourceStructure) {
+    ASSERT_FALSE(isASTHashEqual(
+                     "void foo() { int c; if (0) { c = 1; } }",
+                     "void foo() { int c; if (0) { } c = 1; }"));
+
+    ASSERT_FALSE(isASTHashEqual(
+                     "void f1() {} void f2() {       }",
+                     "void f1() {} void f2() { f1(); }"));
+
+}
Index: include/clang/AST/TypeDataCollectors.td
===================================================================
--- /dev/null
+++ include/clang/AST/TypeDataCollectors.td
@@ -0,0 +1,78 @@
+//--- Types ---------------------------------------------------------------//
+
+class Type {
+  code Code = [{
+     addData(llvm::hash_value(S->getTypeClass()));
+  }];
+}
+
+class BuiltinType {
+   code Code = [{
+      addData(S->getKind());
+   }];
+}
+
+class ArrayType  {
+   code Code = [{
+      addData(S->getSizeModifier());
+      addData(S->getIndexTypeCVRQualifiers());
+   }];
+}
+
+class ConstantArrayType {
+   code Code = [{
+      addData(S->getSize().getZExtValue());
+   }];
+}
+
+class VectorType {
+   code Code = [{
+      addData(S->getNumElements());
+      addData(S->getVectorKind());
+   }];
+}
+
+class FunctionType {
+   code Code = [{
+      addData(S->getRegParmType());
+      addData(S->getCallConv());
+   }];
+}
+
+class FunctionProtoType {
+   code Code = [{
+      addData(S->getExceptionSpecType());
+      addData(S->isVariadic());
+      addData(S->getRefQualifier());
+      addData(S->hasTrailingReturn());
+
+      addData(S->param_types());
+      addData(S->exceptions());
+   }];
+}
+
+class UnaryTransformType {
+   code Code = [{
+        addData(S->getUTTKind());
+   }];
+}
+
+class AttributedType {
+   code Code = [{
+        addData(S->getAttrKind());
+   }];
+}
+
+class ElaboratedType {
+   code Code = [{
+        addData(S->getKeyword());
+   }];
+}
+
+class ObjCObjectType {
+   code Code = [{
+        addData(S->getTypeArgsAsWritten());
+   }];
+}
+
+
Index: include/clang/AST/StmtDataCollectors.td
===================================================================
--- include/clang/AST/StmtDataCollectors.td
+++ include/clang/AST/StmtDataCollectors.td
@@ -5,6 +5,9 @@
     // macro-generated code.
     addData(data_collection::getMacroStack(S->getLocStart(), Context));
     addData(data_collection::getMacroStack(S->getLocEnd(), Context));
+
+    // CrossRef
+    addData(S->children());
   }];
 }
 
@@ -28,16 +31,29 @@
 class PredefinedExpr {
   code Code = [{
     addData(S->getIdentType());
+    addData(S->getFunctionName()->getString());
   }];
 }
 class TypeTraitExpr {
   code Code = [{
     addData(S->getTrait());
+    // CrossRef
+    addData(S->getNumArgs());
     for (unsigned i = 0; i < S->getNumArgs(); ++i)
       addData(S->getArg(i)->getType());
   }];
 }
 
+class UnaryExprOrTypeTraitExpr {
+  code Code = [{
+    addData(S->getKind());
+    if (S->isArgumentType()) {
+       addData(S->getArgumentType());
+    }
+  }];
+}
+
+
 //--- Calls --------------------------------------------------------------//
 class CallExpr {
   code Code = [{
@@ -72,6 +88,7 @@
 }
 class MemberExpr {
   code Code = [{
+    // I suspect this should be included: addData(S->isArrow());
     addData(S->getMemberDecl()->getName());
   }];
 }
@@ -124,6 +141,12 @@
   }];
 }
 
+class CastExpr {
+  code Code = [{
+    addData(S->getCastKind());
+  }];
+}
+
 //--- Miscellaneous Exprs ------------------------------------------------//
 class BinaryOperator {
   code Code = [{
@@ -136,6 +159,12 @@
   }];
 }
 
+class VAArgExpr {
+  code Code = [{
+       addData(S->isMicrosoftABI());
+  }];
+}
+
 //--- Control flow -------------------------------------------------------//
 class GotoStmt {
   code Code = [{
@@ -189,27 +218,48 @@
 }
 class GenericSelectionExpr {
   code Code = [{
+    // CrossRef
+    addData(S->getNumAssocs());
     for (unsigned i = 0; i < S->getNumAssocs(); ++i) {
       addData(S->getAssocType(i));
     }
   }];
 }
+
+class PseudoObjectExpr {
+  code Code = [{
+    // CrossRef
+    addData(S->semantics());
+  }];
+}
+
 class LambdaExpr {
   code Code = [{
+    addData(S->isGenericLambda());
+    addData(S->isMutable());
+    addData(S->hasExplicitParameters());
+    addData(S->hasExplicitResultType());
+
+
+    // CrossRef
+    addData(S->captures());
+    addData(S->explicit_captures());
     for (const LambdaCapture &C : S->captures()) {
       addData(C.isPackExpansion());
       addData(C.getCaptureKind());
       if (C.capturesVariable())
         addData(C.getCapturedVar()->getType());
     }
-    addData(S->isGenericLambda());
-    addData(S->isMutable());
+    
+
   }];
 }
 class DeclStmt {
   code Code = [{
-    auto numDecls = std::distance(S->decl_begin(), S->decl_end());
-    addData(static_cast<unsigned>(numDecls));
+    // CrossRef
+    addData(S->decls());
+
+    // FIXME? As this should be done by a using visitor
     for (const Decl *D : S->decls()) {
       if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
         addData(VD->getType());
@@ -222,6 +272,12 @@
     addData(S->isSimple());
     addData(S->isVolatile());
     addData(S->generateAsmString(Context));
+
+    // CrossRef + FIXME
+    addData(S->getNumInputs());
+    addData(S->getNumOutputs());
+    addData(S->getNumClobbers());
+
     for (unsigned i = 0; i < S->getNumInputs(); ++i) {
       addData(S->getInputConstraint(i));
     }
@@ -236,7 +292,21 @@
 class AttributedStmt {
   code Code = [{
     for (const Attr *A : S->getAttrs()) {
+       // We duplicate class Attr here to not rely on being integrated
+       // into a RecursiveASTVisitor.
+       std::string AttrString;      
+       llvm::raw_string_ostream OS(AttrString);
+       A->printPretty(OS, Context.getLangOpts());
+       OS.flush();
       addData(std::string(A->getSpelling()));
     }
   }];
 }
+
+class CompoundStmt {
+  code Code = [{
+    // CrossRef
+    addData(S->size());
+  }];
+}
+
Index: include/clang/AST/DeclDataCollectors.td
===================================================================
--- /dev/null
+++ include/clang/AST/DeclDataCollectors.td
@@ -0,0 +1,205 @@
+//--- Declarations -------------------------------------------------------//
+
+class Decl {
+  code Code = [{
+      // Every Declaration gets a tag field in the hash stream. It is
+      // hashed to add additional randomness to the hash
+      addData(llvm::hash_value(S->getKind()));
+
+      // CrossRef
+      addData(S->hasAttrs());
+      if (S->hasAttrs())
+        addData(S->attrs());
+  }];
+}
+
+class DeclContext  {
+   code Code = [{
+      // CrossRef
+      addData(S->decls());
+   }];
+}
+
+class BlockDecl  {
+   code Code = [{
+      // CrossRef
+      auto it = llvm::make_range(S->capture_begin(), S->capture_end());
+      addData(it);
+   }];
+}
+
+
+class ValueDecl  {
+  code Code = [{
+      addData(S->getType());
+      addData(S->isWeak());
+  }];
+}
+
+class NamedDecl {
+  code Code = [{
+      addData(S->getNameAsString());
+  }];
+}
+
+class TypeDecl {
+  code Code = [{
+      addData(QualType(S->getTypeForDecl(),0));
+  }];
+}
+
+class EnumDecl {
+  code Code = [{
+      addData(S->getNumPositiveBits());
+      addData(S->getNumNegativeBits());
+  }];
+}
+
+class EnumConstantDecl {
+  code Code = [{
+       /* Not every enum has a init expression. Therefore, 
+          we extract the actual enum value from it. */
+       addData(S->getInitVal().getExtValue());
+  }];
+}
+
+class TagDecl {
+  code Code = [{
+     addData(S->getTagKind());
+  }];
+}
+
+
+class TypedefNameDecl {
+  code Code = [{
+     addData(S->getUnderlyingType());
+  }];
+}
+
+class VarDecl {
+  code Code = [{
+      addData(S->getStorageClass());
+      addData(S->getTLSKind());
+      addData(S->isModulePrivate());
+      addData(S->isNRVOVariable());
+  }];
+}
+
+class ParmVarDecl {
+  code Code = [{
+       addData(S->isParameterPack());
+       addData(S->getOriginalType());
+  }];
+}
+
+class ImplicitParamDecl {
+  code Code = [{
+       addData(S->getParameterKind());
+  }];
+}
+
+class FunctionDecl {
+  code Code = [{
+       addData(S->isExternC());
+       addData(S->isGlobal());
+       addData(S->isNoReturn());
+       addData(S->getStorageClass());
+       addData(S->isInlineSpecified());
+       addData(S->isInlined());
+
+       // CrossRef
+       auto range = llvm::make_range(S->param_begin(), S->param_end());
+       addData(range);
+  }];
+}
+
+class LabelDecl {
+  code Code = [{
+       addData(S->isGnuLocal());
+       addData(S->isMSAsmLabel());
+       if (S->isMSAsmLabel()) {
+          addData(S->getMSAsmLabel());
+       }
+  }];
+}
+
+class CXXRecordDecl {
+  code Code = [{
+       // CrossRef
+       if (S->isCompleteDefinition()) {
+           addData(S->bases());
+       }
+  }];
+}
+
+class CXXConstructorDecl {
+  code Code = [{
+       // CrossRef
+       addData(S->inits());
+  }];
+}
+
+class FieldDecl {
+  code Code = [{
+      addData(S->isBitField());
+  }];
+}
+
+class CapturedDecl {
+  code Code = [{
+      addData(S->isNothrow());
+  }];
+}
+
+class DecompositionDecl {
+  code Code = [{
+      // CrossRef
+      addData(S->bindings());
+  }];
+}
+
+
+//--- Obj-C ---------------------------------------------------------//
+
+class ObjCCategoryDecl {
+  code Code = [{
+     // CrossRef
+     if (auto *it = S->getTypeParamList()) {
+       auto range = llvm::make_range(it->begin(), it->end());
+       addData(range);
+     }
+  }];
+}
+
+class ObjCInterfaceDecl {
+  code Code = [{
+     // CrossRef
+     if (auto *it = S->getTypeParamListAsWritten()) {
+       auto range = llvm::make_range(it->begin(), it->end());
+       addData(range);
+     }
+  }];
+}
+
+class ObjCMethodDecl {
+  code Code = [{
+     // CrossRef
+     auto range = llvm::make_range(S->param_begin(), S->param_end());
+     addData(range);
+  }];
+}
+
+//--- Templates -----------------------------------------------------//
+
+class FriendTemplateDecl {
+  code Code = [{
+      // CrossRef
+      addData(S->getNumTemplateParameters());
+      for (unsigned I = 0, E = S->getNumTemplateParameters(); I < E; ++I) {
+          auto TPL = S->getTemplateParameterList(I);
+          auto it  = llvm::make_range(TPL->begin(), TPL->end());
+          addData(it);
+      }
+  }];
+}
+
Index: include/clang/AST/DataCollection.h
===================================================================
--- include/clang/AST/DataCollection.h
+++ include/clang/AST/DataCollection.h
@@ -59,6 +59,16 @@
   DataConsumer.update(StringRef(reinterpret_cast<char *>(&Data), sizeof(Data)));
 }
 
+template <class T, class Type>
+void addDataToConsumer(T &DataConsumer, const llvm::iterator_range<Type> &R) {
+  addDataToConsumer(DataConsumer, std::distance(R.begin(), R.end()));
+}
+
+template <class T, class Type>
+void addDataToConsumer(T &DataConsumer, const llvm::ArrayRef<Type> &R) {
+  addDataToConsumer(DataConsumer, R.size());
+}
+
 } // end namespace data_collection
 } // end namespace clang
 
Index: include/clang/AST/CMakeLists.txt
===================================================================
--- include/clang/AST/CMakeLists.txt
+++ include/clang/AST/CMakeLists.txt
@@ -53,3 +53,15 @@
 clang_tablegen(StmtDataCollectors.inc -gen-clang-data-collectors
   SOURCE StmtDataCollectors.td
   TARGET StmtDataCollectors)
+
+clang_tablegen(DeclDataCollectors.inc -gen-clang-data-collectors
+  SOURCE DeclDataCollectors.td
+  TARGET DeclDataCollectors)
+
+clang_tablegen(AttrDataCollectors.inc -gen-clang-data-collectors
+  SOURCE AttrDataCollectors.td
+  TARGET AttrDataCollectors)
+
+clang_tablegen(TypeDataCollectors.inc -gen-clang-data-collectors
+  SOURCE TypeDataCollectors.td
+  TARGET TypeDataCollectors)
Index: include/clang/AST/CHashVisitor.h
===================================================================
--- /dev/null
+++ include/clang/AST/CHashVisitor.h
@@ -0,0 +1,331 @@
+//===--- CHashVisitor.h - Stable AST Hashing -----*- 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 CHashVisitor class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_CHASHVISITOR_H
+#define LLVM_CLANG_AST_CHASHVISITOR_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DataCollection.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "llvm/Support/MD5.h"
+#include <map>
+#include <string>
+
+namespace clang {
+
+
+template <typename H = llvm::MD5, typename HR = llvm::MD5::MD5Result>
+class CHashVisitor : public clang::RecursiveASTVisitor<CHashVisitor<H, HR>> {
+
+  using Inherited = clang::RecursiveASTVisitor<CHashVisitor<H, HR>>;
+
+public:
+  using Hash = H;
+  using HashResult = HR;
+
+  /// Configure the RecursiveASTVisitor
+  bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+protected:
+  ASTContext &Context;
+
+  // For the DataCollector, we implement a few addData() functions
+  void addData(uint64_t data) { topHash().update(data); }
+  void addData(const StringRef &str) { topHash().update(str); }
+  // On our way down, we meet a lot of qualified types.
+  void addData(const QualType &T) {
+    // 1. Hash referenced type
+    const Type *const ActualType = T.getTypePtr();
+    assert(ActualType != nullptr);
+
+    // FIXME: Structural hash
+    // 1.1 Was it already hashed?
+    const HashResult *const SavedDigest = getHash(ActualType);
+    if (SavedDigest) {
+      // 1.1.1 Use cached value
+      topHash().update(SavedDigest->Bytes);
+    } else {
+      // 1.1.2 Calculate hash for type
+      const Hash *const CurrentHash = pushHash();
+      Inherited::TraverseType(T); // Uses getTypePtr() internally
+      const HashResult TypeDigest = popHash(CurrentHash);
+      topHash().update(TypeDigest.Bytes);
+
+      // Store hash for underlying type
+      storeHash(ActualType, TypeDigest);
+    }
+
+    // Add the qulaifiers at this specific usage of the type
+    addData(T.getCVRQualifiers());
+  }
+
+  /// The RecursiveASTVisitor often visits several children of a node
+  /// in a for loop. However, we also have to include the number of
+  /// children into the hash in order to avoid collisions. Therefore,
+  /// the following addData(..) methods only include the lengths into
+  /// the hash. The actual children are visited by the
+  /// RecursiveASTVistor.
+  template<typename T>
+  void addData(const llvm::iterator_range<T> &x) {
+    addData(std::distance(x.begin(), x.end()));
+  }
+  template<typename T>
+  void addData(const llvm::ArrayRef<T> &x) {
+    addData(x.size());
+  }
+
+public:
+#define DEF_ADD_DATA_STORED(CLASS, CODE)                                       \
+  template <class = void> bool Visit##CLASS(const CLASS *S) {                  \
+    CODE;                                                                      \
+    return true;                                                               \
+  }
+#define DEF_ADD_DATA(CLASS, CODE) DEF_ADD_DATA_STORED(CLASS, CODE)
+#include "clang/AST/StmtDataCollectors.inc"
+#define DEF_ADD_DATA(CLASS, CODE) DEF_ADD_DATA_STORED(CLASS, CODE)
+#include "clang/AST/AttrDataCollectors.inc"
+#define DEF_ADD_DATA(CLASS, CODE) DEF_ADD_DATA_STORED(CLASS, CODE)
+#include "clang/AST/DeclDataCollectors.inc"
+#define DEF_ADD_DATA(CLASS, CODE) DEF_ADD_DATA_STORED(CLASS, CODE)
+#include "clang/AST/TypeDataCollectors.inc"
+
+  CHashVisitor(ASTContext &Context) : Context(Context) {}
+
+  /// For some special nodes, override the traverse function, since
+  /// we need both pre- and post order traversal
+  bool TraverseTranslationUnitDecl(TranslationUnitDecl *TU) {
+    if (!TU)
+      return true;
+    // First, we push a new hash onto the hashing stack. This hash
+    // will capture everythin within the TU*/
+    Hash *CurrentHash = pushHash();
+
+    Inherited::WalkUpFromTranslationUnitDecl(TU);
+
+    // Do recursion on our own, since we want to exclude some children
+    const auto DC = cast<DeclContext>(TU);
+    for (auto *Child : DC->noload_decls()) {
+      if (isa<TypedefDecl>(Child) || isa<RecordDecl>(Child) ||
+          isa<EnumDecl>(Child))
+        continue;
+
+      // Extern variable definitions at the top-level
+      if (const auto VD = dyn_cast<VarDecl>(Child)) {
+        if (VD->hasExternalStorage()) {
+          continue;
+        }
+      }
+
+      if (const auto FD = dyn_cast<FunctionDecl>(Child)) {
+        // We try to avoid hashing of declarations that have no definition
+        if (!FD->isThisDeclarationADefinition()) {
+          bool doHashing = false;
+          // HOWEVER! If this declaration is an alias Declaration, we
+          // hash it no matter what
+          if (FD->hasAttrs()) {
+            for (const Attr *const A : FD->getAttrs()) {
+              if (A->getKind() == attr::Kind::Alias) {
+                doHashing = true;
+                break;
+              }
+            }
+          }
+          if (!doHashing)
+            continue;
+        }
+      }
+
+      TraverseDecl(Child);
+    }
+
+    storeHash(TU, popHash(CurrentHash));
+
+    return true;
+  }
+
+  bool TraverseDecl(Decl *D) {
+    if (!D)
+      return true;
+    /* For some declarations, we store the calculated hash value. */
+    bool CacheHash = false;
+    if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDefined())
+      CacheHash = true;
+    if (isa<VarDecl>(D) && cast<VarDecl>(D)->hasGlobalStorage())
+      CacheHash = true;
+    if (isa<RecordDecl>(D) && dyn_cast<RecordDecl>(D)->isCompleteDefinition())
+      CacheHash = true;
+
+    if (!CacheHash) {
+      return Inherited::TraverseDecl(D);
+    }
+
+    const HashResult *const SavedDigest = getHash(D);
+    if (SavedDigest) {
+      topHash().update(SavedDigest->Bytes);
+      return true;
+    }
+    Hash *CurrentHash = pushHash();
+    bool Ret = Inherited::TraverseDecl(D);
+    HashResult CurrentHashResult = popHash(CurrentHash);
+    storeHash(D, CurrentHashResult);
+    if (!isa<TranslationUnitDecl>(D)) {
+      topHash().update(CurrentHashResult.Bytes);
+    }
+
+    return Ret;
+  }
+
+  /// When doing a semantic hash, we have to use cross-tree links to
+  /// other parts of the AST, here we establish these links
+
+#define DEF_TYPE_GOTO_DECL(CLASS, EXPR)                                 \
+  bool Visit##CLASS(CLASS *T) {                                                \
+    Inherited::Visit##CLASS(T);                                                \
+    return TraverseDecl(EXPR);                                                 \
+  }
+
+  DEF_TYPE_GOTO_DECL(TypedefType, T->getDecl());
+  DEF_TYPE_GOTO_DECL(RecordType, T->getDecl());
+  // The EnumType forwards to the declaration. The declaration does
+  // not hand back to the type.
+  DEF_TYPE_GOTO_DECL(EnumType, T->getDecl());
+  bool TraverseEnumDecl(EnumDecl *E) {
+    /* In the original RecursiveASTVisitor
+       > if (D->getTypeForDecl()) {
+       >    TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+       > }
+       => NO, NO, NO, to avoid endless recursion
+    */
+    return Inherited::WalkUpFromEnumDecl(E);
+  }
+
+  bool VisitTypeDecl(TypeDecl *D) {
+    // If we would hash the resulting type for a typedef, we
+    // would get into an endless recursion.
+    if (!isa<TypedefNameDecl>(D) && !isa<RecordDecl>(D) && !isa<EnumDecl>(D)) {
+      addData(QualType(D->getTypeForDecl(), 0));
+    }
+    return true;
+  }
+
+  bool VisitDeclRefExpr(DeclRefExpr *E) {
+    ValueDecl *ValDecl = E->getDecl();
+    // Function Declarations are handled in VisitCallExpr
+    if (!ValDecl) {
+      return true;
+    }
+    if (isa<VarDecl>(ValDecl)) {
+      /* We emulate TraverseDecl here for VarDecl, because we
+       * are not allowed to call TraverseDecl here, since the
+       * initial expression of a DeclRefExpr might reference a
+       * sourronding Declaration itself. For example:
+       *
+       * struct foo {int N;}
+       * struct foo a = { sizeof(a) };
+       */
+      VarDecl *VD = static_cast<VarDecl *>(ValDecl);
+      VisitNamedDecl(VD);
+      Inherited::TraverseType(VD->getType());
+      VisitVarDecl(VD);
+    } else if (isa<FunctionDecl>(ValDecl)) {
+      /* Hash Functions without their body */
+      FunctionDecl *FD = static_cast<FunctionDecl *>(ValDecl);
+      Stmt *Body = FD->getBody();
+      FD->setBody(nullptr);
+      TraverseDecl(FD);
+      FD->setBody(Body);
+    } else {
+      TraverseDecl(ValDecl);
+    }
+    return true;
+  }
+
+  bool VisitValueDecl(ValueDecl *D) {
+    /* Field Declarations can induce recursions */
+    if (isa<FieldDecl>(D)) {
+      addData(std::string(D->getType().getAsString()));
+    } else {
+      addData(D->getType());
+    }
+    addData(D->isWeak());
+    return true;
+  }
+
+  bool VisitExpr(Expr *E) {
+    // We specially treat the CXXThisExpr, as we cannot call
+    // addData(QualType) directly on this, because it would reference
+    // back to the enclosing CXXRecord and result in a recursion.
+    if (isa<CXXThisExpr>(E)) {
+      addData(std::string(E->getType().getAsString()));
+    } else {
+      Inherited::VisitExpr(E);
+    }
+    return true;
+  }
+
+  /// For performance reasons, we cache some of the hashes for types
+  /// and declarations.
+
+public:
+  // We store hashes for declarations and types in separate maps.
+  std::map<const Type *, HashResult> TypeSilo;
+  std::map<const Decl *, HashResult> DeclSilo;
+
+  void storeHash(const Type *Obj, HashResult Dig) { TypeSilo[Obj] = Dig; }
+
+  void storeHash(const Decl *Obj, HashResult Dig) { DeclSilo[Obj] = Dig; }
+
+  const HashResult *getHash(const Type *Obj) {
+    if (TypeSilo.find(Obj) != TypeSilo.end()) {
+      return &TypeSilo[Obj];
+    }
+    return nullptr;
+  }
+
+  const HashResult *getHash(const Decl *Obj) {
+    if (DeclSilo.find(Obj) != DeclSilo.end()) {
+      return &DeclSilo[Obj];
+    }
+    return nullptr;
+  }
+
+  /// In order to produce hashes for subtrees on the way, a hash
+  /// stack is used. When a new subhash is meant to be calculated,
+  /// we push a new stack on the hash. All hashing functions use
+  /// always the top of the hashing stack.
+
+protected:
+  llvm::SmallVector<Hash, 32> HashStack;
+
+public:
+  Hash *pushHash() {
+    HashStack.push_back(Hash());
+    return &HashStack.back();
+  }
+
+  HashResult popHash(const Hash *ShouldBe = nullptr) {
+    assert(!ShouldBe || ShouldBe == &HashStack.back());
+
+    // Finalize the Hash and return the digest.
+    HashResult CurrentDigest;
+    topHash().final(CurrentDigest);
+    HashStack.pop_back();
+    return CurrentDigest;
+  }
+
+  Hash &topHash() { return HashStack.back(); }
+};
+
+} // namespace clang
+#endif // LLVM_CLANG_AST_CHASHVISITOR_H
Index: include/clang/AST/AttrDataCollectors.td
===================================================================
--- /dev/null
+++ include/clang/AST/AttrDataCollectors.td
@@ -0,0 +1,10 @@
+//--- Attributes ---------------------------------------------------------//
+class Attr {
+  code Code = [{
+    std::string AttrString;      
+    llvm::raw_string_ostream OS(AttrString);
+    S->printPretty(OS, Context.getLangOpts());
+    OS.flush();
+    addData(AttrString);
+  }];
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to