Index: include/clang/Tooling/ASTDiff/ASTDiff.h =================================================================== --- include/clang/Tooling/ASTDiff/ASTDiff.h +++ include/clang/Tooling/ASTDiff/ASTDiff.h @@ -139,7 +139,21 @@ int findPositionInParent() const; - // Returns the starting and ending offset of the node in its source file. + /// Returns the range that contains the text that is associated with this + /// node. This is based on DynTypedNode::getSourceRange() with a couple of + /// workarounds. + /// + /// The range for statements includes the trailing semicolon. + /// + /// The range for implicit ThisExpression nodes is empty. + /// + /// If it is a CXXConstructExpr that is not a temporary, then we exclude + /// the class name from the range by returning + /// CXXConstructExpr::getParentOrBraceRange(). So the class name will belong + /// to the parent. + CharSourceRange getSourceRange() const; + + /// Returns the offsets for the range returned by getSourceRange(). std::pair getSourceRangeOffsets() const; }; Index: lib/Tooling/ASTDiff/ASTDiff.cpp =================================================================== --- lib/Tooling/ASTDiff/ASTDiff.cpp +++ lib/Tooling/ASTDiff/ASTDiff.cpp @@ -197,24 +197,6 @@ return isSpecializedNodeExcluded(N); } -static SourceRange getSourceRange(const ASTUnit &AST, const DynTypedNode &DTN) { - const SourceManager &SM = AST.getSourceManager(); - SourceRange Range = DTN.getSourceRange(); - SourceLocation BeginLoc = Range.getBegin(); - SourceLocation EndLoc; - if (BeginLoc.isMacroID()) - EndLoc = SM.getExpansionRange(BeginLoc).second; - else - EndLoc = Range.getEnd(); - EndLoc = - Lexer::getLocForEndOfToken(EndLoc, /*Offset=*/0, SM, AST.getLangOpts()); - if (auto *ThisExpr = DTN.get()) { - if (ThisExpr->isImplicit()) - EndLoc = BeginLoc; - } - return {SM.getExpansionLoc(BeginLoc), SM.getExpansionLoc(EndLoc)}; -} - namespace { // Sets Height, Parent and Children for each node. struct PreorderVisitor : public RecursiveASTVisitor { @@ -420,8 +402,7 @@ assert(&N.Tree == this); const DynTypedNode &DTN = N.ASTNode; if (N.isMacro()) { - CharSourceRange Range(getSourceRange(AST, N.ASTNode), false); - return Lexer::getSourceText(Range, AST.getSourceManager(), + return Lexer::getSourceText(N.getSourceRange(), AST.getSourceManager(), AST.getLangOpts()); } if (auto *S = DTN.get()) @@ -756,9 +737,59 @@ Siblings.begin(); } +static SourceRange getSourceRangeImpl(NodeRef N) { + const DynTypedNode &DTN = N.ASTNode; + SyntaxTree::Impl &Tree = N.Tree; + SourceManager &SM = Tree.AST.getSourceManager(); + const LangOptions &LangOpts = Tree.AST.getLangOpts(); + auto EndOfToken = [&](SourceLocation Loc) { + return Lexer::getLocForEndOfToken(Loc, /*Offset=*/0, SM, LangOpts); + }; + auto TokenToCharRange = [&](SourceRange Range) -> SourceRange { + return {Range.getBegin(), EndOfToken(Range.getEnd())}; + }; + SourceRange Range = DTN.getSourceRange(); + if (N.isMacro()) { + SourceLocation BeginLoc = Range.getBegin(); + SourceLocation End = SM.getExpansionRange(BeginLoc).second; + End = EndOfToken(End); + return {SM.getExpansionLoc(BeginLoc), SM.getExpansionLoc(End)}; + } + if (auto *ThisExpr = DTN.get()) + if (ThisExpr->isImplicit()) + return {Range.getBegin(), Range.getBegin()}; + if (auto *CE = DTN.get()) { + if (!isa(CE)) + return CE->getParenOrBraceRange(); + } else if (DTN.get()) { + return TokenToCharRange(Range); + } else if (DTN.get() || DTN.get() || + DTN.get() || + (DTN.get() && + N.getParent()->ASTNode.get()) || + (DTN.get() && + !DTN.get()->isThisDeclarationADefinition()) || + DTN.get() || DTN.get() || + DTN.get()) { + SourceLocation End = Range.getEnd(); + if (DTN.get()) + End = End.getLocWithOffset(-1); + SourceLocation SemicolonLoc = Lexer::findLocationAfterToken( + End, tok::semi, SM, LangOpts, + /*SkipTrailingWhitespaceAndNewLine=*/false); + Range.setEnd(SemicolonLoc); + return Range; + } + return TokenToCharRange(Range); +} + +CharSourceRange Node::getSourceRange() const { + return CharSourceRange::getCharRange(getSourceRangeImpl(*this)); +} + std::pair Node::getSourceRangeOffsets() const { const SourceManager &SM = Tree.AST.getSourceManager(); - SourceRange Range = getSourceRange(Tree.AST, ASTNode); + CharSourceRange Range = getSourceRange(); unsigned Begin = SM.getFileOffset(Range.getBegin()); unsigned End = SM.getFileOffset(Range.getEnd()); return {Begin, End}; Index: test/Tooling/clang-diff-json.cpp =================================================================== --- test/Tooling/clang-diff-json.cpp +++ test/Tooling/clang-diff-json.cpp @@ -2,10 +2,10 @@ // RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \ // RUN: | FileCheck %s -// CHECK: "begin": 299, -// CHECK: "type": "FieldDecl", -// CHECK: "end": 319, -// CHECK: "type": "CXXRecordDecl", +// CHECK: "begin": 297, +// CHECK: "type": "FieldDecl" +// CHECK: "end": 318, +// CHECK: "type": "CXXRecordDecl" class A { int x; };