stettberger created this revision.
Herald added subscribers: cfe-commits, aprantl, mgorny.
The CHashVisitor can be used to determine a unique hash for a translation unit.
The hash is stable across compiler invocations and if two translation units
have the same hash, the resulting object file is semantically equal (code,
data, etc. are equal; debug information may be different).
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/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
@@ -18,6 +18,7 @@
PostOrderASTVisitor.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,91 @@
+//===- 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 {}
+
+};
+
+class CHashAction : public ASTFrontendAction {
+public: // We are all friends
+ 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 struct
+ "struct foobar { int a0; char a1; unsigned long a2; };",
+ "struct foobar { int a0; char a1;};"
+ ));
+
+}
+
+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
===================================================================
--- include/clang/AST/TypeDataCollectors.td
+++ include/clang/AST/TypeDataCollectors.td
@@ -0,0 +1,66 @@
+//--- Types ---------------------------------------------------------------//
+class Type {
+ code Code = [{
+ addData(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());
+ }];
+}
+
+class UnaryTransformType {
+ code Code = [{
+ addData(S->getUTTKind());
+ }];
+}
+
+class AttributedType {
+ code Code = [{
+ addData(S->getAttrKind());
+ }];
+}
+
+class ElaboratedType {
+ code Code = [{
+ addData(S->getKeyword());
+ }];
+}
Index: include/clang/AST/StmtDataCollectors.td
===================================================================
--- include/clang/AST/StmtDataCollectors.td
+++ include/clang/AST/StmtDataCollectors.td
@@ -28,6 +28,7 @@
class PredefinedExpr {
code Code = [{
addData(S->getIdentType());
+ addData(S->getFunctionName()->getString());
}];
}
class TypeTraitExpr {
@@ -38,6 +39,16 @@
}];
}
+class UnaryExprOrTypeTraitExpr {
+ code Code = [{
+ addData(S->getKind());
+ if (S->isArgumentType()) {
+ addData(S->getArgumentType());
+ }
+ }];
+}
+
+
//--- Calls --------------------------------------------------------------//
class CallExpr {
code Code = [{
@@ -72,6 +83,7 @@
}
class MemberExpr {
code Code = [{
+ // I suspect this should be included: addData(S->isArrow());
addData(S->getMemberDecl()->getName());
}];
}
@@ -124,6 +136,12 @@
}];
}
+class CastExpr {
+ code Code = [{
+ addData(S->getCastKind());
+ }];
+}
+
//--- Miscellaneous Exprs ------------------------------------------------//
class BinaryOperator {
code Code = [{
@@ -136,6 +154,12 @@
}];
}
+class VAArgExpr {
+ code Code = [{
+ addData(S->isMicrosoftABI());
+ }];
+}
+
//--- Control flow -------------------------------------------------------//
class GotoStmt {
code Code = [{
@@ -210,6 +234,7 @@
code Code = [{
auto numDecls = std::distance(S->decl_begin(), S->decl_end());
addData(static_cast<unsigned>(numDecls));
+ // FIXME?
for (const Decl *D : S->decls()) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
addData(VD->getType());
@@ -236,7 +261,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 = [{
+ addData(S->size());
+ }];
+}
+
+
Index: include/clang/AST/DeclDataCollectors.td
===================================================================
--- include/clang/AST/DeclDataCollectors.td
+++ include/clang/AST/DeclDataCollectors.td
@@ -0,0 +1,105 @@
+//--- Declarations -------------------------------------------------------//
+
+class ValueDecl {
+ code Code = [{
+ addData(S->getType());
+ addData(S->isWeak());
+ }];
+}
+
+class NamedDecl {
+ code Code = [{
+ addData(S->getName());
+ }];
+}
+
+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());
+ }];
+}
+
+class LabelDecl {
+ code Code = [{
+ addData(S->isGnuLocal());
+ addData(S->isMSAsmLabel());
+ if (S->isMSAsmLabel()) {
+ addData(S->getMSAsmLabel());
+ }
+ }];
+}
+
+class FieldDecl {
+ code Code = [{
+ addData(S->isBitField());
+ }];
+}
+
+class CapturedDecl {
+ code Code = [{
+ addData(S->isNothrow());
+ }];
+}
+
+
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,451 @@
+#ifndef __CHASH_VISITOR
+#define __CHASH_VISITOR
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/DataCollection.h"
+#include "llvm/Support/MD5.h"
+
+
+
+#include <string>
+#include <map>
+
+
+namespace clang {
+
+ namespace CHashConstants {
+ enum {
+ Attr = 0x56b6cba9,
+ InheritableAttr = 0x7c0b04ce,
+ InheritableParamAttr = 0x6a4fdb90,
+
+ NamedDecl = 0,
+ TypeDecl = 0,
+ Decl = 0,
+ TagDecl = 0,
+ VarDecl = 0xb19c2ee2,
+ ImplicitParamDecl = 0xd04f138f,
+ ParmVarDecl = 0x1fe2fcb9,
+ TypedefNameDecl = 0xe8cca403,
+ BlockDecl = 0x761e230f,
+ FunctionDecl = 0x2a34b689,
+ LabelDecl = 0xff6db781,
+ EnumDecl = 0xc564aed1,
+ EnumConstantDecl = 0x11050d85,
+ IndirectFieldDecl = 0x937408ea,
+ ValueDecl = 0xbb06d011,
+ FileScopeAsmDecl = 0x381879fa,
+ CapturedDecl = 0xa3a884ed,
+ FieldDecl = 0xac0c83d4,
+ RecordDecl = 0x27892cea,
+
+ Expr = 0,
+ StmtExpr = 0xf4bb377e,
+ CastExpr = 0x7c505e88,
+ DeclRefExpr = 0xa33a24f3,
+ PredefinedExpr = 0xffb3cc20,
+ InitListExpr = 0xe23aaddd,
+ UnaryExprOrTypeTraitExpr = 0xb4995380,
+ MemberExpr = 0xe682fc67,
+ AddrLabelExpr = 0xe511b92e,
+ CompoundLiteralExpr = 0xc54ffefa,
+ CallExpr = 0x427cc6e8,
+ OffsetOfExpr = 0x48232f36,
+ ParenExpr = 0xf1a9c911,
+ AtomicExpr = 0x7e5497b7,
+ ParenListExpr = 0x64600f,
+ DesignatedInitExpr = 0x8d017154,
+ ArraySubscriptExpr = 0x8c7ab6b2,
+ ImplicitValueInitExpr = 0xfe7647fa,
+ VAArgExpr = 0xdf10fedc,
+ BlockExpr = 0xcc75aacd,
+ ShuffleVectorExpr = 0x2e2321ad,
+ ConvertVectorExpr = 0xfe447195,
+ TypeTraitExpr = 0xe9bda7a,
+ ArrayTypeTraitExpr = 0xd6b02f4,
+ CXXBoolLiteralExpr = 0x45b2a746,
+ CXXDeleteExpr = 0xbcfa92ec,
+ CXXFoldExpr = 0x1cc7935,
+ ObjCPropertyRefExpr = 0x6636c2c,
+ ObjCIndirectCopyRestoreExpr = 0xb53e833,
+ ObjCBridgedCastExpr = 0xcc79223,
+ LambdaExpr = 0xd799f74,
+ GenericSelectionExpr = 0x51b395c,
+ ExpressionTraitExpr = 0x8f308a7,
+ CharacterLiteral = 0x2a1c033f,
+ IntegerLiteral = 0x7b2daa87,
+ FloatingLiteral = 0xceee8473,
+ StringLiteral = 0xe5846c45,
+ ImaginaryLiteral = 0xe340180e,
+
+ UnaryOperator = 0x496a1fb5,
+ BinaryOperator = 0xa6339d46,
+ CompoundAssignmentOperator = 0x9c582bf3,
+ AbstractConditionalOperator = 0x151982b7,
+ BinaryConditionalOperator = 0x40d2aa93,
+
+ Stmt = 0,
+ ForStmt = 0xec4e334f,
+ IfStmt = 0x3de06c3c,
+ NullStmt = 0x777400e0,
+ DoStmt = 0xa80405bd,
+ GotoStmt = 0xec2a6be8,
+ ContinueStmt = 0x2c518360,
+ ReturnStmt = 0x1cf8354e,
+ WhileStmt = 0x6cb85f96,
+ LabelStmt = 0xe3d17613,
+ SwitchStmt = 0x6ef423db,
+ CaseStmt = 0x9640cc21,
+ DefaultStmt = 0x2f6febe9,
+ DeclStmt = 0xbe748556,
+ CompoundStmt = 0x906b6fb4,
+ BreakStmt = 0x530ae0a9,
+ GCCAsmStmt = 0x652782d6,
+ MSAsmStmt = 0xccd123ef,
+ AttributedStmt = 0x8e36d148,
+ CaptureStmt = 0x1cafe3db,
+ IndirectGotoStmt = 0x98888356,
+ AsmStmt = 0xe8cca40,
+ CXXCatchStmt = 0xc853e2ac,
+ ObjCAtCatchStmt = 0xd6ce349,
+ MSDependentExistsStmt = 0xf2097b9,
+
+ Type = 0xf13daabe,
+ PointerType = 0x5b868718,
+ ArrayType = 0xd0b37bef,
+ ConstantArrayType = 0x6439c9ef,
+ VariableArrayType = 0x74887cd4,
+ ComplexType = 0x75d5304a,
+ AtomicType = 0x8a024d89,
+ TypeOfExprType = 0x3417cfda,
+ TypeOfType = 0x98090139,
+ ParenType = 0x7c2df2fc,
+ FunctionType = 0x8647819b,
+ FunctionProtoType = 0x4dd5f204,
+ EnumType = 0x4acd4cde,
+ TagType = 0x94c7a399,
+ AttributedType = 0xddc8426,
+ UnaryTransformType = 0xca8afa5b,
+ DecayedType = 0x707c703e,
+ AdjustedType = 0x9936193,
+ ElaboratedType = 0x96681107,
+ StructureType = 0xa5b0d36d,
+ UnionType = 0x5057c896,
+ VectorType = 0x4ed393c3,
+ BuiltinType = 0xb190dc73,
+ PipeType = 0xe9bb85af,
+ RValueReferenceType = 0xa6c4b308,
+ LValueReferenceType = 0xdb0b2b7d,
+ FunctionNoProtoType = 0x6a185f1b,
+ ExtVectorType = 0x7d816c99,
+ IncompleteArrayType = 0xb07dce69,
+ MemberPointerType = 0x68ce4241,
+ DependentAddressSpaceType = 0x4bfa546,
+ BlockPointerType = 0x6ab898b3,
+ DependentSizedExtVectorType = 0xb5eb2dc1,
+ DependentSizedArrayType = 0xfbbc5e13,
+ DecltypeType = 0xe61ede42,
+ AutoType = 0x15937adc,
+ DependentSizedExtVector = 0x80161e92,
+ };
+ }
+
+ template<typename H=llvm::MD5, typename HR=llvm::MD5::MD5Result>
+ class CHashVisitor
+ : public clang::RecursiveASTVisitor<CHashVisitor<H, HR>> {
+
+ typedef clang::RecursiveASTVisitor<CHashVisitor<H, HR>> Inherited;
+ public:
+ typedef H Hash;
+ typedef HR HashResult;
+
+ /// 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());
+ }
+ public:
+
+#define DEF_ADD_DATA_STORED(CLASS, CODE) \
+ template<class=void> \
+ bool Visit##CLASS(const CLASS *S) { \
+ unsigned tag = CHashConstants::CLASS; \
+ if (tag != 0) { \
+ addData(tag); \
+ } \
+ 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;
+ }
+
+ /* For some special nodes, override the traverse function, since
+ we need both pre- and post order traversal. Storing of type
+ hashes is done in addData() */
+ 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) {
+ addData(CHashConstants::TypeDecl);
+ // 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) {
+ addData(CHashConstants::DeclRefExpr);
+ 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) {
+ addData(CHashConstants::ValueDecl);
+ /* Field Declarations can induce recursions */
+ if (isa<FieldDecl>(D)) {
+ addData(std::string(D->getType().getAsString()));
+ } else {
+ addData(D->getType());
+ }
+ addData(D->isWeak());
+ return true;
+ }
+
+ /*****************************************************************
+ * For performance reasons, we cache some of the hashes for types
+ * and declarations.
+ */
+ // We store hashes for declarations and types in separate maps.
+ public:
+ 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(); }
+
+
+ };
+
+}
+#endif
Index: include/clang/AST/AttrDataCollectors.td
===================================================================
--- include/clang/AST/AttrDataCollectors.td
+++ 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits