diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp --- a/clang/lib/Tooling/Syntax/BuildTree.cpp +++ b/clang/lib/Tooling/Syntax/BuildTree.cpp @@ -44,8 +44,80 @@ using namespace clang; +static Expr *ignoreExprNodesImpl(Expr *E) { return E; } +template +static Expr *ignoreExprNodesImpl(Expr *E, FnTy &&Fn, FnTys &&...Fns) { + return ignoreExprNodesImpl(Fn(E), std::forward(Fns)...); +} + +template +static Expr *ignoreUntilFixedPoint(Expr *E, FnTys &&...Fns) { + Expr *LastE = nullptr; + while (E != LastE) { + LastE = E; + E = ignoreExprNodesImpl(E, std::forward(Fns)...); + assert(E->getSourceRange() == LastE->getSourceRange()); + } + return E; +} + +static Expr *ignoreImplicitCXXConstructExpr(Expr *E) { + if (auto *C = dyn_cast(E)) { + auto NumArgs = C->getNumArgs(); + if (NumArgs == 1 || (NumArgs > 1 && isa(C->getArg(1)))) { + auto *A = C->getArg(0); + if (A->getSourceRange() == E->getSourceRange()) + return A; + } + } + return E; +} + +static Expr *ignoreImplicitCast(Expr *E) { + if (auto *ICE = dyn_cast(E)) { + + auto *UnderlyingE = ICE->getSubExpr(); + assert(UnderlyingE->getSourceRange() == E->getSourceRange()); + return UnderlyingE; + } + return E; +} + +static Expr *ignoreFull(Expr *E) { + if (auto *FE = dyn_cast(E)) { + auto *UnderlyingE = FE->getSubExpr(); + assert(UnderlyingE->getSourceRange() == E->getSourceRange()); + return UnderlyingE; + } + return E; +} + +static Expr *ignoreMaterializeTemporary(Expr *E) { + if (auto *MTE = dyn_cast(E)) { + auto *UnderlyingE = MTE->getSubExpr(); + assert(UnderlyingE->getSourceRange() == E->getSourceRange()); + return UnderlyingE; + } + return E; +} + +static Expr *ignoreCXXBindTemporary(Expr *E) { + if (auto *BTE = dyn_cast(E)) { + auto *UnderlyingE = BTE->getSubExpr(); + assert(UnderlyingE->getSourceRange() == E->getSourceRange()); + return UnderlyingE; + } + return E; +} + +static Expr *ignoreImplicit(Expr *E) { + return ignoreUntilFixedPoint(E, ignoreCXXBindTemporary, ignoreImplicitCast, + ignoreMaterializeTemporary, ignoreFull, + ignoreImplicitCXXConstructExpr); +} + LLVM_ATTRIBUTE_UNUSED -static bool isImplicitExpr(Expr *E) { return E->IgnoreImplicit() != E; } +static bool isImplicitExpr(Expr *E) { return ignoreImplicit(E) != E; } namespace { /// Get start location of the Declarator from the TypeLoc. @@ -703,7 +775,7 @@ for (auto *D : DS->decls()) Builder.noticeDeclWithoutSemicolon(D); } else if (auto *E = dyn_cast_or_null(S)) { - return RecursiveASTVisitor::TraverseStmt(E->IgnoreImplicit()); + return RecursiveASTVisitor::TraverseStmt(ignoreImplicit(E)); } return RecursiveASTVisitor::TraverseStmt(S); } @@ -1575,7 +1647,7 @@ void syntax::TreeBuilder::markExprChild(Expr *Child, NodeRole Role) { if (!Child) return; - Child = Child->IgnoreImplicit(); + Child = ignoreImplicit(Child); syntax::Tree *ChildNode = Mapping.find(Child); assert(ChildNode != nullptr); diff --git a/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp b/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp --- a/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp @@ -1745,19 +1745,15 @@ struct X { friend X operator+(X, const X&); }; -// FIXME: Remove additional `UnknownExpression` wrapping `x`. For that, ignore -// implicit copy constructor called on `x`. This should've been ignored already, -// as we `IgnoreImplicit` when traversing an `Stmt`. void test(X x, X y) { [[x + y]]; } )cpp", {R"txt( BinaryOperatorExpression Expression -|-UnknownExpression LeftHandSide -| `-IdExpression -| `-UnqualifiedId UnqualifiedId -| `-'x' +|-IdExpression LeftHandSide +| `-UnqualifiedId UnqualifiedId +| `-'x' |-'+' OperatorToken `-IdExpression RightHandSide `-UnqualifiedId UnqualifiedId