diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -762,126 +762,73 @@ /// member expression. static QualType findBoundMemberType(const Expr *expr); - /// Skip past any invisble AST nodes which might surround this - /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes, - /// but also injected CXXMemberExpr and CXXConstructExpr which represent - /// implicit conversions. + /// Forwards to expr_traversal::DescendIgnoreUnlessSpelledInSource Expr *IgnoreUnlessSpelledInSource(); const Expr *IgnoreUnlessSpelledInSource() const { return const_cast(this)->IgnoreUnlessSpelledInSource(); } - /// Skip past any implicit casts which might surround this expression until - /// reaching a fixed point. Skips: - /// * ImplicitCastExpr - /// * FullExpr + /// Forwards to expr_traversal::DescendIgnoreImpCasts Expr *IgnoreImpCasts() LLVM_READONLY; const Expr *IgnoreImpCasts() const { return const_cast(this)->IgnoreImpCasts(); } - /// Skip past any casts which might surround this expression until reaching - /// a fixed point. Skips: - /// * CastExpr - /// * FullExpr - /// * MaterializeTemporaryExpr - /// * SubstNonTypeTemplateParmExpr + /// Forwards to expr_traversal::DescendIgnoreCasts Expr *IgnoreCasts() LLVM_READONLY; const Expr *IgnoreCasts() const { return const_cast(this)->IgnoreCasts(); } - /// Skip past any implicit AST nodes which might surround this expression - /// until reaching a fixed point. Skips: - /// * What IgnoreImpCasts() skips - /// * MaterializeTemporaryExpr - /// * CXXBindTemporaryExpr + /// Forwards to expr_traversal::DescendIgnoreImplicit Expr *IgnoreImplicit() LLVM_READONLY; const Expr *IgnoreImplicit() const { return const_cast(this)->IgnoreImplicit(); } - /// Skip past any implicit AST nodes which might surround this expression - /// until reaching a fixed point. Same as IgnoreImplicit, except that it - /// also skips over implicit calls to constructors and conversion functions. - /// - /// FIXME: Should IgnoreImplicit do this? + /// Forwards to expr_traversal::DescendIgnoreImplicitAsWritten Expr *IgnoreImplicitAsWritten() LLVM_READONLY; const Expr *IgnoreImplicitAsWritten() const { return const_cast(this)->IgnoreImplicitAsWritten(); } - /// Skip past any parentheses which might surround this expression until - /// reaching a fixed point. Skips: - /// * ParenExpr - /// * UnaryOperator if `UO_Extension` - /// * GenericSelectionExpr if `!isResultDependent()` - /// * ChooseExpr if `!isConditionDependent()` - /// * ConstantExpr + /// Forwards to expr_traversal::DescendIgnoreParens Expr *IgnoreParens() LLVM_READONLY; const Expr *IgnoreParens() const { return const_cast(this)->IgnoreParens(); } - /// Skip past any parentheses and implicit casts which might surround this - /// expression until reaching a fixed point. - /// FIXME: IgnoreParenImpCasts really ought to be equivalent to - /// IgnoreParens() + IgnoreImpCasts() until reaching a fixed point. However - /// this is currently not the case. Instead IgnoreParenImpCasts() skips: - /// * What IgnoreParens() skips - /// * What IgnoreImpCasts() skips - /// * MaterializeTemporaryExpr - /// * SubstNonTypeTemplateParmExpr + /// Forwards to expr_traversal::DescendIgnoreParenImpCasts Expr *IgnoreParenImpCasts() LLVM_READONLY; const Expr *IgnoreParenImpCasts() const { return const_cast(this)->IgnoreParenImpCasts(); } - /// Skip past any parentheses and casts which might surround this expression - /// until reaching a fixed point. Skips: - /// * What IgnoreParens() skips - /// * What IgnoreCasts() skips + /// Forwards to expr_traversal::DescendIgnoreParenCasts Expr *IgnoreParenCasts() LLVM_READONLY; const Expr *IgnoreParenCasts() const { return const_cast(this)->IgnoreParenCasts(); } - /// Skip conversion operators. If this Expr is a call to a conversion - /// operator, return the argument. + /// Forwards to expr_traversal::DescendIgnoreConversionOperator Expr *IgnoreConversionOperator() LLVM_READONLY; const Expr *IgnoreConversionOperator() const { return const_cast(this)->IgnoreConversionOperator(); } - /// Skip past any parentheses and lvalue casts which might surround this - /// expression until reaching a fixed point. Skips: - /// * What IgnoreParens() skips - /// * What IgnoreCasts() skips, except that only lvalue-to-rvalue - /// casts are skipped - /// FIXME: This is intended purely as a temporary workaround for code - /// that hasn't yet been rewritten to do the right thing about those - /// casts, and may disappear along with the last internal use. + /// Forwards to expr_traversal::DescendIgnoreParenLValueCasts Expr *IgnoreParenLValueCasts() LLVM_READONLY; const Expr *IgnoreParenLValueCasts() const { return const_cast(this)->IgnoreParenLValueCasts(); } - /// Skip past any parenthese and casts which do not change the value - /// (including ptr->int casts of the same size) until reaching a fixed point. - /// Skips: - /// * What IgnoreParens() skips - /// * CastExpr which do not change the value - /// * SubstNonTypeTemplateParmExpr + /// Forwards to expr_traversal::DescendIgnoreParenNoopCasts Expr *IgnoreParenNoopCasts(const ASTContext &Ctx) LLVM_READONLY; const Expr *IgnoreParenNoopCasts(const ASTContext &Ctx) const { return const_cast(this)->IgnoreParenNoopCasts(Ctx); } - /// Skip past any parentheses and derived-to-base casts until reaching a - /// fixed point. Skips: - /// * What IgnoreParens() skips - /// * CastExpr which represent a derived-to-base cast (CK_DerivedToBase, - /// CK_UncheckedDerivedToBase and CK_NoOp) + /// Forwards to expr_traversal::DescendIgnoreParenBaseCasts Expr *ignoreParenBaseCasts() LLVM_READONLY; const Expr *ignoreParenBaseCasts() const { return const_cast(this)->ignoreParenBaseCasts(); diff --git a/clang/include/clang/AST/ExprTraversal.h b/clang/include/clang/AST/ExprTraversal.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/AST/ExprTraversal.h @@ -0,0 +1,115 @@ +//===--- ExprTraversal.h - Simple expression traversal --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ExprTraversal class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_EXPR_TRAVERSAL_H +#define LLVM_CLANG_AST_EXPR_TRAVERSAL_H + +namespace clang { + +class ASTContext; +class Expr; + +namespace expr_traversal { + + /// Skip past any implicit casts which might surround this expression until + /// reaching a fixed point. Skips: + /// * ImplicitCastExpr + /// * FullExpr + Expr *DescendIgnoreImpCasts(Expr *E); + + /// Skip past any casts which might surround this expression until reaching + /// a fixed point. Skips: + /// * CastExpr + /// * FullExpr + /// * MaterializeTemporaryExpr + /// * SubstNonTypeTemplateParmExpr + Expr *DescendIgnoreCasts(Expr *E); + + /// Skip past any implicit AST nodes which might surround this expression + /// until reaching a fixed point. Skips: + /// * What IgnoreImpCasts() skips + /// * MaterializeTemporaryExpr + /// * CXXBindTemporaryExpr + Expr *DescendIgnoreImplicit(Expr *E); + + /// Skip past any implicit AST nodes which might surround this expression + /// until reaching a fixed point. Same as IgnoreImplicit, except that it + /// also skips over implicit calls to constructors and conversion functions. + /// + /// FIXME: Should IgnoreImplicit do this? + Expr *DescendIgnoreImplicitAsWritten(Expr *E); + + /// Skip past any parentheses which might surround this expression until + /// reaching a fixed point. Skips: + /// * ParenExpr + /// * UnaryOperator if `UO_Extension` + /// * GenericSelectionExpr if `!isResultDependent()` + /// * ChooseExpr if `!isConditionDependent()` + /// * ConstantExpr + Expr *DescendIgnoreParens(Expr *E); + + /// Skip past any parentheses and implicit casts which might surround this + /// expression until reaching a fixed point. + /// FIXME: IgnoreParenImpCasts really ought to be equivalent to + /// IgnoreParens() + IgnoreImpCasts() until reaching a fixed point. However + /// this is currently not the case. Instead IgnoreParenImpCasts() skips: + /// * What IgnoreParens() skips + /// * What IgnoreImpCasts() skips + /// * MaterializeTemporaryExpr + /// * SubstNonTypeTemplateParmExpr + Expr *DescendIgnoreParenImpCasts(Expr *E); + + /// Skip past any parentheses and casts which might surround this expression + /// until reaching a fixed point. Skips: + /// * What IgnoreParens() skips + /// * What IgnoreCasts() skips + Expr *DescendIgnoreParenCasts(Expr *E); + + /// Skip conversion operators. If this Expr is a call to a conversion + /// operator, return the argument. + Expr *DescendIgnoreConversionOperator(Expr *E); + + /// Skip past any parentheses and lvalue casts which might surround this + /// expression until reaching a fixed point. Skips: + /// * What IgnoreParens() skips + /// * What IgnoreCasts() skips, except that only lvalue-to-rvalue + /// casts are skipped + /// FIXME: This is intended purely as a temporary workaround for code + /// that hasn't yet been rewritten to do the right thing about those + /// casts, and may disappear along with the last internal use. + Expr *DescendIgnoreParenLValueCasts(Expr *E); + + /// Skip past any parentheses and derived-to-base casts until reaching a + /// fixed point. Skips: + /// * What IgnoreParens() skips + /// * CastExpr which represent a derived-to-base cast (CK_DerivedToBase, + /// CK_UncheckedDerivedToBase and CK_NoOp) + Expr *DescendIgnoreParenBaseCasts(Expr *E); + + /// Skip past any parenthese and casts which do not change the value + /// (including ptr->int casts of the same size) until reaching a fixed point. + /// Skips: + /// * What IgnoreParens() skips + /// * CastExpr which do not change the value + /// * SubstNonTypeTemplateParmExpr + Expr *DescendIgnoreParenNoopCasts(const ASTContext &Ctx, Expr *E); + + /// Skip past any invisble AST nodes which might surround this + /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes, + /// but also injected CXXMemberExpr and CXXConstructExpr which represent + /// implicit conversions. + Expr *DescendIgnoreUnlessSpelledInSource(Expr *E); +}; + +} // namespace clang + +#endif diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -50,6 +50,7 @@ ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp + ExprTraversal.cpp ExternalASTMerger.cpp ExternalASTSource.cpp FormatString.cpp diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprTraversal.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" @@ -2816,243 +2817,48 @@ return QualType(); } -static Expr *IgnoreImpCastsSingleStep(Expr *E) { - if (auto *ICE = dyn_cast(E)) - return ICE->getSubExpr(); - - if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImpCastsExtraSingleStep(Expr *E) { - // FIXME: Skip MaterializeTemporaryExpr and SubstNonTypeTemplateParmExpr in - // addition to what IgnoreImpCasts() skips to account for the current - // behaviour of IgnoreParenImpCasts(). - Expr *SubE = IgnoreImpCastsSingleStep(E); - if (SubE != E) - return SubE; - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - -static Expr *IgnoreCastsSingleStep(Expr *E) { - if (auto *CE = dyn_cast(E)) - return CE->getSubExpr(); - - if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - -static Expr *IgnoreLValueCastsSingleStep(Expr *E) { - // Skip what IgnoreCastsSingleStep skips, except that only - // lvalue-to-rvalue casts are skipped. - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() != CK_LValueToRValue) - return E; - - return IgnoreCastsSingleStep(E); -} - -static Expr *IgnoreBaseCastsSingleStep(Expr *E) { - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) - return CE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImplicitSingleStep(Expr *E) { - Expr *SubE = IgnoreImpCastsSingleStep(E); - if (SubE != E) - return SubE; - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *BTE = dyn_cast(E)) - return BTE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImplicitAsWrittenSingleStep(Expr *E) { - if (auto *ICE = dyn_cast(E)) - return ICE->getSubExprAsWritten(); - - return IgnoreImplicitSingleStep(E); -} - -static Expr *IgnoreParensOnlySingleStep(Expr *E) { - if (auto *PE = dyn_cast(E)) - return PE->getSubExpr(); - return E; -} - -static Expr *IgnoreParensSingleStep(Expr *E) { - if (auto *PE = dyn_cast(E)) - return PE->getSubExpr(); - - if (auto *UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_Extension) - return UO->getSubExpr(); - } - - else if (auto *GSE = dyn_cast(E)) { - if (!GSE->isResultDependent()) - return GSE->getResultExpr(); - } - - else if (auto *CE = dyn_cast(E)) { - if (!CE->isConditionDependent()) - return CE->getChosenSubExpr(); - } - - else if (auto *CE = dyn_cast(E)) - return CE->getSubExpr(); - - return E; -} - -static Expr *IgnoreNoopCastsSingleStep(const ASTContext &Ctx, Expr *E) { - if (auto *CE = dyn_cast(E)) { - // We ignore integer <-> casts that are of the same width, ptr<->ptr and - // ptr<->int casts of the same width. We also ignore all identity casts. - Expr *SubExpr = CE->getSubExpr(); - bool IsIdentityCast = - Ctx.hasSameUnqualifiedType(E->getType(), SubExpr->getType()); - bool IsSameWidthCast = - (E->getType()->isPointerType() || E->getType()->isIntegralType(Ctx)) && - (SubExpr->getType()->isPointerType() || - SubExpr->getType()->isIntegralType(Ctx)) && - (Ctx.getTypeSize(E->getType()) == Ctx.getTypeSize(SubExpr->getType())); - - if (IsIdentityCast || IsSameWidthCast) - return SubExpr; - } - - else if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - -static Expr *IgnoreExprNodesImpl(Expr *E) { return E; } -template -static Expr *IgnoreExprNodesImpl(Expr *E, FnTy &&Fn, FnTys &&... Fns) { - return IgnoreExprNodesImpl(Fn(E), std::forward(Fns)...); -} - -/// Given an expression E and functions Fn_1,...,Fn_n : Expr * -> Expr *, -/// Recursively apply each of the functions to E until reaching a fixed point. -/// Note that a null E is valid; in this case nothing is done. -template -static Expr *IgnoreExprNodes(Expr *E, FnTys &&... Fns) { - Expr *LastE = nullptr; - while (E != LastE) { - LastE = E; - E = IgnoreExprNodesImpl(E, std::forward(Fns)...); - } - return E; -} - Expr *Expr::IgnoreImpCasts() { - return IgnoreExprNodes(this, IgnoreImpCastsSingleStep); + return ExprTraversal::DescendIgnoreImpCasts(this); } -Expr *Expr::IgnoreCasts() { - return IgnoreExprNodes(this, IgnoreCastsSingleStep); -} +Expr *Expr::IgnoreCasts() { return ExprTraversal::DescendIgnoreCasts(this); } Expr *Expr::IgnoreImplicit() { - return IgnoreExprNodes(this, IgnoreImplicitSingleStep); + return ExprTraversal::DescendIgnoreImplicit(this); } Expr *Expr::IgnoreImplicitAsWritten() { - return IgnoreExprNodes(this, IgnoreImplicitAsWrittenSingleStep); + return ExprTraversal::DescendIgnoreImplicitAsWritten(this); } -Expr *Expr::IgnoreParens() { - return IgnoreExprNodes(this, IgnoreParensSingleStep); -} +Expr *Expr::IgnoreParens() { return ExprTraversal::DescendIgnoreParens(this); } Expr *Expr::IgnoreParenImpCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreImpCastsExtraSingleStep); + return ExprTraversal::DescendIgnoreParenImpCasts(this); } Expr *Expr::IgnoreParenCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep); + return ExprTraversal::DescendIgnoreParenCasts(this); } Expr *Expr::IgnoreConversionOperator() { - if (auto *MCE = dyn_cast(this)) { - if (MCE->getMethodDecl() && isa(MCE->getMethodDecl())) - return MCE->getImplicitObjectArgument(); - } - return this; + return ExprTraversal::DescendIgnoreConversionOperator(this); } Expr *Expr::IgnoreParenLValueCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreLValueCastsSingleStep); + return ExprTraversal::DescendIgnoreParenLValueCasts(this); } Expr *Expr::ignoreParenBaseCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreBaseCastsSingleStep); + return ExprTraversal::DescendIgnoreParenBaseCasts(this); } Expr *Expr::IgnoreParenNoopCasts(const ASTContext &Ctx) { - return IgnoreExprNodes(this, IgnoreParensSingleStep, [&Ctx](Expr *E) { - return IgnoreNoopCastsSingleStep(Ctx, E); - }); + return ExprTraversal::DescendIgnoreParenNoopCasts(Ctx, this); } Expr *Expr::IgnoreUnlessSpelledInSource() { - Expr *E = this; - - Expr *LastE = nullptr; - while (E != LastE) { - LastE = E; - E = IgnoreExprNodes(E, IgnoreImplicitSingleStep, IgnoreImpCastsSingleStep, - IgnoreParensOnlySingleStep); - - auto SR = E->getSourceRange(); - - if (auto *C = dyn_cast(E)) { - if (C->getNumArgs() == 1) { - Expr *A = C->getArg(0); - if (A->getSourceRange() == SR || !isa(C)) - E = A; - } - } - - if (auto *C = dyn_cast(E)) { - Expr *ExprNode = C->getImplicitObjectArgument()->IgnoreParenImpCasts(); - if (ExprNode->getSourceRange() == SR) - E = ExprNode; - } - } - - return E; + return ExprTraversal::DescendIgnoreUnlessSpelledInSource(this); } bool Expr::isDefaultArgument() const { diff --git a/clang/lib/AST/ExprTraversal.cpp b/clang/lib/AST/ExprTraversal.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/AST/ExprTraversal.cpp @@ -0,0 +1,254 @@ +//===--- ExprTraversal.cpp - Simple expression traversal ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements Expr traversal. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprTraversal.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" + +using namespace clang; + +static Expr *IgnoreImpCastsSingleStep(Expr *E) { + if (auto *ICE = dyn_cast(E)) + return ICE->getSubExpr(); + + if (auto *FE = dyn_cast(E)) + return FE->getSubExpr(); + + return E; +} + +static Expr *IgnoreImpCastsExtraSingleStep(Expr *E) { + // FIXME: Skip MaterializeTemporaryExpr and SubstNonTypeTemplateParmExpr in + // addition to what IgnoreImpCasts() skips to account for the current + // behaviour of IgnoreParenImpCasts(). + Expr *SubE = IgnoreImpCastsSingleStep(E); + if (SubE != E) + return SubE; + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; +} + +static Expr *IgnoreCastsSingleStep(Expr *E) { + if (auto *CE = dyn_cast(E)) + return CE->getSubExpr(); + + if (auto *FE = dyn_cast(E)) + return FE->getSubExpr(); + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; +} + +static Expr *IgnoreLValueCastsSingleStep(Expr *E) { + // Skip what IgnoreCastsSingleStep skips, except that only + // lvalue-to-rvalue casts are skipped. + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() != CK_LValueToRValue) + return E; + + return IgnoreCastsSingleStep(E); +} + +static Expr *IgnoreBaseCastsSingleStep(Expr *E) { + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) + return CE->getSubExpr(); + + return E; +} + +static Expr *IgnoreImplicitSingleStep(Expr *E) { + Expr *SubE = IgnoreImpCastsSingleStep(E); + if (SubE != E) + return SubE; + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *BTE = dyn_cast(E)) + return BTE->getSubExpr(); + + return E; +} + +static Expr *IgnoreImplicitAsWrittenSingleStep(Expr *E) { + if (auto *ICE = dyn_cast(E)) + return ICE->getSubExprAsWritten(); + + return IgnoreImplicitSingleStep(E); +} + +static Expr *IgnoreParensOnlySingleStep(Expr *E) { + if (auto *PE = dyn_cast(E)) + return PE->getSubExpr(); + return E; +} + +static Expr *IgnoreParensSingleStep(Expr *E) { + if (auto *PE = dyn_cast(E)) + return PE->getSubExpr(); + + if (auto *UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_Extension) + return UO->getSubExpr(); + } + + else if (auto *GSE = dyn_cast(E)) { + if (!GSE->isResultDependent()) + return GSE->getResultExpr(); + } + + else if (auto *CE = dyn_cast(E)) { + if (!CE->isConditionDependent()) + return CE->getChosenSubExpr(); + } + + else if (auto *CE = dyn_cast(E)) + return CE->getSubExpr(); + + return E; +} + +static Expr *IgnoreNoopCastsSingleStep(const ASTContext &Ctx, Expr *E) { + if (auto *CE = dyn_cast(E)) { + // We ignore integer <-> casts that are of the same width, ptr<->ptr and + // ptr<->int casts of the same width. We also ignore all identity casts. + Expr *SubExpr = CE->getSubExpr(); + bool IsIdentityCast = + Ctx.hasSameUnqualifiedType(E->getType(), SubExpr->getType()); + bool IsSameWidthCast = + (E->getType()->isPointerType() || E->getType()->isIntegralType(Ctx)) && + (SubExpr->getType()->isPointerType() || + SubExpr->getType()->isIntegralType(Ctx)) && + (Ctx.getTypeSize(E->getType()) == Ctx.getTypeSize(SubExpr->getType())); + + if (IsIdentityCast || IsSameWidthCast) + return SubExpr; + } + + else if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; +} + +static Expr *IgnoreExprNodesImpl(Expr *E) { return E; } +template +static Expr *IgnoreExprNodesImpl(Expr *E, FnTy &&Fn, FnTys &&... Fns) { + return IgnoreExprNodesImpl(Fn(E), std::forward(Fns)...); +} + +/// Given an expression E and functions Fn_1,...,Fn_n : Expr * -> Expr *, +/// Recursively apply each of the functions to E until reaching a fixed point. +/// Note that a null E is valid; in this case nothing is done. +template +static Expr *IgnoreExprNodes(Expr *E, FnTys &&... Fns) { + Expr *LastE = nullptr; + while (E != LastE) { + LastE = E; + E = IgnoreExprNodesImpl(E, std::forward(Fns)...); + } + return E; +} + +Expr *expr_traversal::DescendIgnoreImpCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreImpCastsSingleStep); +} + +Expr *expr_traversal::DescendIgnoreCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreCastsSingleStep); +} + +Expr *expr_traversal::DescendIgnoreImplicit(Expr *E) { + return IgnoreExprNodes(E, IgnoreImplicitSingleStep); +} + +Expr *expr_traversal::DescendIgnoreImplicitAsWritten(Expr *E) { + return IgnoreExprNodes(E, IgnoreImplicitAsWrittenSingleStep); +} + +Expr *expr_traversal::DescendIgnoreParens(Expr *E) { + return IgnoreExprNodes(E, IgnoreParensSingleStep); +} + +Expr *expr_traversal::DescendIgnoreParenImpCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreParensSingleStep, + IgnoreImpCastsExtraSingleStep); +} + +Expr *expr_traversal::DescendIgnoreParenCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreParensSingleStep, IgnoreCastsSingleStep); +} + +Expr *expr_traversal::DescendIgnoreConversionOperator(Expr *E) { + if (auto *MCE = dyn_cast(E)) { + if (MCE->getMethodDecl() && isa(MCE->getMethodDecl())) + return MCE->getImplicitObjectArgument(); + } + return E; +} + +Expr *expr_traversal::DescendIgnoreParenLValueCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreParensSingleStep, + IgnoreLValueCastsSingleStep); +} + +Expr *expr_traversal::DescendIgnoreParenBaseCasts(Expr *E) { + return IgnoreExprNodes(E, IgnoreParensSingleStep, IgnoreBaseCastsSingleStep); +} + +Expr *expr_traversal::DescendIgnoreParenNoopCasts(const ASTContext &Ctx, + Expr *Input) { + return IgnoreExprNodes(Input, IgnoreParensSingleStep, [&Ctx](Expr *E) { + return IgnoreNoopCastsSingleStep(Ctx, E); + }); +} + +Expr *expr_traversal::DescendIgnoreUnlessSpelledInSource(Expr *E) { + Expr *LastE = nullptr; + while (E != LastE) { + LastE = E; + E = IgnoreExprNodes(E, IgnoreImplicitSingleStep, IgnoreImpCastsSingleStep, + IgnoreParensOnlySingleStep); + + auto SR = E->getSourceRange(); + + if (auto *C = dyn_cast(E)) { + if (C->getNumArgs() == 1) { + Expr *A = C->getArg(0); + if (A->getSourceRange() == SR || !isa(C)) + E = A; + } + } + + if (auto *C = dyn_cast(E)) { + Expr *ExprNode = C->getImplicitObjectArgument()->IgnoreParenImpCasts(); + if (ExprNode->getSourceRange() == SR) + E = ExprNode; + } + } + + return E; +}