Index: lib/Tooling/ASTDiff/ASTDiff.cpp =================================================================== --- lib/Tooling/ASTDiff/ASTDiff.cpp +++ lib/Tooling/ASTDiff/ASTDiff.cpp @@ -149,6 +149,7 @@ // Maps preorder indices to postorder ones. std::vector PostorderIds; std::vector NodesBfs; + std::map TemplateArgumentLocations; int getSize() const { return Nodes.size(); } NodeId getRootId() const { return 0; } @@ -186,6 +187,14 @@ static bool isSpecializedNodeExcluded(CXXCtorInitializer *I) { return !I->isWritten(); } +static bool isSpecializedNodeExcluded(const TemplateArgumentLoc *S) { + return false; +} + +static bool isNodeExcluded(ASTUnit &AST, TemplateName *Template) { + // TODO what if it is from another file + return false; +} template static bool isNodeExcluded(ASTUnit &AST, T *N) { const SourceManager &SrcMgr = AST.getSourceManager(); @@ -203,9 +212,8 @@ return isSpecializedNodeExcluded(N); } -static SourceRange getSourceRange(const ASTUnit &AST, const DynTypedNode &DTN) { +static SourceRange getSourceExtent(const ASTUnit &AST, SourceRange Range) { const SourceManager &SrcMgr = AST.getSourceManager(); - SourceRange Range = DTN.getSourceRange(); SourceLocation BeginLoc = Range.getBegin(); SourceLocation EndLoc; if (BeginLoc.isMacroID()) @@ -214,10 +222,6 @@ EndLoc = Range.getEnd(); EndLoc = Lexer::getLocForEndOfToken(EndLoc, /*Offset=*/0, SrcMgr, AST.getLangOpts()); - if (auto *ThisExpr = DTN.get()) { - if (ThisExpr->isImplicit()) - EndLoc = BeginLoc; - } return {SrcMgr.getExpansionLoc(BeginLoc), SrcMgr.getExpansionLoc(EndLoc)}; } @@ -230,13 +234,13 @@ PreorderVisitor(SyntaxTree::Impl &Tree) : Tree(Tree) {} - template std::tuple PreTraverse(T *ASTNode) { + template std::tuple PreTraverse(const T &ASTNode) { NodeId MyId = Id; Tree.Nodes.emplace_back(); Node &N = Tree.getMutableNode(MyId); N.Parent = Parent; N.Depth = Depth; - N.ASTNode = DynTypedNode::create(*ASTNode); + N.ASTNode = DynTypedNode::create(ASTNode); assert(!N.ASTNode.getNodeKind().isNone() && "Expected nodes to have a valid kind."); if (Parent.isValid()) { @@ -268,7 +272,7 @@ bool TraverseDecl(Decl *D) { if (isNodeExcluded(Tree.AST, D)) return true; - auto SavedState = PreTraverse(D); + auto SavedState = PreTraverse(*D); RecursiveASTVisitor::TraverseDecl(D); PostTraverse(SavedState); return true; @@ -278,7 +282,7 @@ S = S->IgnoreImplicit(); if (isNodeExcluded(Tree.AST, S)) return true; - auto SavedState = PreTraverse(S); + auto SavedState = PreTraverse(*S); RecursiveASTVisitor::TraverseStmt(S); PostTraverse(SavedState); return true; @@ -287,11 +291,41 @@ bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { if (isNodeExcluded(Tree.AST, Init)) return true; - auto SavedState = PreTraverse(Init); + auto SavedState = PreTraverse(*Init); RecursiveASTVisitor::TraverseConstructorInitializer(Init); PostTraverse(SavedState); return true; } +// We want nodes to be in the same order as in the source code. +// So we traverse template parameters before the remainder of the declaration. +#define DEF_TRAVERSE_TMPL_DECL_(TMPLDECLKIND) \ + bool Traverse##TMPLDECLKIND##TemplateDecl(TMPLDECLKIND##TemplateDecl *TD) { \ + /* No need to check return value, always true. */ \ + TraverseTemplateParameterListHelper(TD->getTemplateParameters()); \ + TraverseDecl(TD->getTemplatedDecl()); \ + return true; \ + } + DEF_TRAVERSE_TMPL_DECL_(Class) + DEF_TRAVERSE_TMPL_DECL_(Var) + DEF_TRAVERSE_TMPL_DECL_(Function) +#undef DEF_TRAVERSE_TMPL_DECL_ + bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) { + if (isNodeExcluded(Tree.AST, &ArgLoc)) + return true; + Tree.TemplateArgumentLocations.emplace(Id, ArgLoc.getSourceRange()); + auto SavedState = PreTraverse(ArgLoc.getArgument()); + RecursiveASTVisitor::TraverseTemplateArgumentLoc(ArgLoc); + PostTraverse(SavedState); + return true; + } + bool TraverseTemplateName(TemplateName Template) { + if (isNodeExcluded(Tree.AST, &Template)) + return true; + auto SavedState = PreTraverse(Template); + RecursiveASTVisitor::TraverseTemplateName(Template); + PostTraverse(SavedState); + return true; + } }; } // end anonymous namespace @@ -441,6 +475,13 @@ llvm_unreachable("Unknown initializer type"); } +template static std::string dumpToString(const T &Object) { + std::string Str; + llvm::raw_string_ostream OS(Str); + Object.dump(OS); + return OS.str(); +} + std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const { return getNodeValue(getNode(Id)); } @@ -448,7 +489,8 @@ std::string SyntaxTree::Impl::getNodeValue(const Node &N) const { const DynTypedNode &DTN = N.ASTNode; if (N.isMacro()) { - CharSourceRange Range(getSourceRange(AST, N.ASTNode), false); + CharSourceRange Range(getSourceExtent(AST, N.ASTNode.getSourceRange()), + false); return Lexer::getSourceText(Range, AST.getSourceManager(), AST.getLangOpts()); } @@ -458,7 +500,11 @@ return getDeclValue(D); if (auto *Init = DTN.get()) return getInitializerValue(Init, TypePP); - llvm_unreachable("Fatal: unhandled AST node.\n"); + if (auto *Template = DTN.get()) + return dumpToString(*Template); + if (auto *Arg = DTN.get()) + return dumpToString(*Arg); + llvm_unreachable("getNodeValue: unhandled AST node.\n"); } std::string SyntaxTree::Impl::getDeclValue(const Decl *D) const { @@ -532,8 +578,12 @@ ConstDeclVisitor::Visit(D); else if (auto *Init = DTN.get()) addData(getInitializerValue(Init, Tree.TypePP)); + else if (auto *Template = DTN.get()) + addData(dumpToString(*Template)); + else if (auto *Arg = DTN.get()) + addData(dumpToString(*Arg)); else - llvm_unreachable("Fatal: unhandled AST node.\n"); + llvm_unreachable("DataConsumer: unhandled AST node.\n"); } #define DEF_ADD_DATA(CLASS, CODE) \ @@ -587,7 +637,8 @@ HashType SyntaxTree::Impl::hashNode(const Node &N) const { const DynTypedNode &DTN = N.ASTNode; if (N.isMacro()) { - CharSourceRange Range(getSourceRange(AST, N.ASTNode), false); + CharSourceRange Range(getSourceExtent(AST, N.ASTNode.getSourceRange()), + false); return hashString( Lexer::getSourceText(Range, AST.getSourceManager(), AST.getLangOpts())); } @@ -1201,7 +1252,16 @@ std::pair SyntaxTree::getSourceRangeOffsets(const Node &N) const { const SourceManager &SrcMgr = TreeImpl->AST.getSourceManager(); - SourceRange Range = getSourceRange(TreeImpl->AST, N.ASTNode); + SourceRange Range; + if (auto *Arg = N.ASTNode.get()) + Range = TreeImpl->TemplateArgumentLocations.at(&N - &TreeImpl->Nodes[0]); + else { + Range = N.ASTNode.getSourceRange(); + if (auto *ThisExpr = N.ASTNode.get()) + if (ThisExpr->isImplicit()) + Range.setEnd(Range.getBegin()); + } + Range = getSourceExtent(TreeImpl->AST, Range); unsigned Begin = SrcMgr.getFileOffset(Range.getBegin()); unsigned End = SrcMgr.getFileOffset(Range.getEnd()); return {Begin, End}; Index: test/Tooling/Inputs/clang-diff-basic-src.cpp =================================================================== --- test/Tooling/Inputs/clang-diff-basic-src.cpp +++ test/Tooling/Inputs/clang-diff-basic-src.cpp @@ -41,3 +41,16 @@ M1; F(1, 1); } + +template +U visit(T &t) { + int x = t; + return U(); +} + +void tmp() { + int x; + visit(x); +} + +int x = 1; Index: test/Tooling/clang-diff-ast.cpp =================================================================== --- test/Tooling/clang-diff-ast.cpp +++ test/Tooling/clang-diff-ast.cpp @@ -92,3 +92,16 @@ // CHECK-NEXT: FunctionDecl: sentinel void sentinel(); #endif + +// CHECK-NEXT: ClassTemplateDecl: C +// CHECK-NEXT: TemplateTypeParmDecl +// CHECK-NEXT: CXXRecordDecl +template class C { + // CHECK-NEXT: FieldDecl + T t; +}; + +// CHECK-NEXT: CXXRecordDecl +// CHECK-NEXT: TemplateName +// CHECK-NEXT: TemplateArgument +class I : C {}; Index: test/Tooling/clang-diff-basic.cpp =================================================================== --- test/Tooling/clang-diff-basic.cpp +++ test/Tooling/clang-diff-basic.cpp @@ -1,4 +1,4 @@ -// RUN: clang-diff -dump-matches %S/Inputs/clang-diff-basic-src.cpp %s -- | FileCheck %s +// RUN: clang-diff -dump-matches %S/Inputs/clang-diff-basic-src.cpp %s -- -std=c++11 | FileCheck %s // CHECK: Match TranslationUnitDecl{{.*}} to TranslationUnitDecl // CHECK: Match NamespaceDecl: src{{.*}} to NamespaceDecl: dst @@ -68,5 +68,18 @@ F(1, /*b=*/1); } +// CHECK: Update TemplateTypeParmDecl: T{{.*}} to Type +template +U visit(Type &t) { + int x = t; + return U(); +} + +void tmp() { + long x; + // CHECK: Update TemplateArgument: int{{.*}} to long + visit(x); +} + // CHECK: Delete AccessSpecDecl: public // CHECK: Delete CXXMethodDecl Index: test/Tooling/clang-diff-html.test =================================================================== --- test/Tooling/clang-diff-html.test +++ test/Tooling/clang-diff-html.test @@ -1,4 +1,4 @@ -// RUN: clang-diff -html %S/Inputs/clang-diff-basic-src.cpp %S/clang-diff-basic.cpp -- | FileCheck %s +// RUN: clang-diff -html %S/Inputs/clang-diff-basic-src.cpp %S/clang-diff-basic.cpp -- -std=c++11 | FileCheck %s // CHECK: