llvm-project/clang/include/clang/AST/ASTNodeTraverser.h
Matheus Izvekov 91cdd35008
[clang] Improve nested name specifier AST representation (#147835)
This is a major change on how we represent nested name qualifications in
the AST.

* The nested name specifier itself and how it's stored is changed. The
prefixes for types are handled within the type hierarchy, which makes
canonicalization for them super cheap, no memory allocation required.
Also translating a type into nested name specifier form becomes a no-op.
An identifier is stored as a DependentNameType. The nested name
specifier gains a lightweight handle class, to be used instead of
passing around pointers, which is similar to what is implemented for
TemplateName. There is still one free bit available, and this handle can
be used within a PointerUnion and PointerIntPair, which should keep
bit-packing aficionados happy.
* The ElaboratedType node is removed, all type nodes in which it could
previously apply to can now store the elaborated keyword and name
qualifier, tail allocating when present.
* TagTypes can now point to the exact declaration found when producing
these, as opposed to the previous situation of there only existing one
TagType per entity. This increases the amount of type sugar retained,
and can have several applications, for example in tracking module
ownership, and other tools which care about source file origins, such as
IWYU. These TagTypes are lazily allocated, in order to limit the
increase in AST size.

This patch offers a great performance benefit.

It greatly improves compilation time for
[stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for
`test_on2.cpp` in that project, which is the slowest compiling test,
this patch improves `-c` compilation time by about 7.2%, with the
`-fsyntax-only` improvement being at ~12%.

This has great results on compile-time-tracker as well:

![image](https://github.com/user-attachments/assets/700dce98-2cab-4aa8-97d1-b038c0bee831)

This patch also further enables other optimziations in the future, and
will reduce the performance impact of template specialization resugaring
when that lands.

It has some other miscelaneous drive-by fixes.

About the review: Yes the patch is huge, sorry about that. Part of the
reason is that I started by the nested name specifier part, before the
ElaboratedType part, but that had a huge performance downside, as
ElaboratedType is a big performance hog. I didn't have the steam to go
back and change the patch after the fact.

There is also a lot of internal API changes, and it made sense to remove
ElaboratedType in one go, versus removing it from one type at a time, as
that would present much more churn to the users. Also, the nested name
specifier having a different API avoids missing changes related to how
prefixes work now, which could make existing code compile but not work.

How to review: The important changes are all in
`clang/include/clang/AST` and `clang/lib/AST`, with also important
changes in `clang/lib/Sema/TreeTransform.h`.

The rest and bulk of the changes are mostly consequences of the changes
in API.

PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just
for easier to rebasing. I plan to rename it back after this lands.

Fixes #136624
Fixes https://github.com/llvm/llvm-project/issues/43179
Fixes https://github.com/llvm/llvm-project/issues/68670
Fixes https://github.com/llvm/llvm-project/issues/92757
2025-08-09 05:06:53 -03:00

1023 lines
31 KiB
C++

//===--- ASTNodeTraverser.h - Traversal of AST nodes ----------------------===//
//
// 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 the AST traversal facilities. Other users
// of this class may make use of the same traversal logic by inheriting it,
// similar to RecursiveASTVisitor.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_ASTNODETRAVERSER_H
#define LLVM_CLANG_AST_ASTNODETRAVERSER_H
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/AttrVisitor.h"
#include "clang/AST/CommentVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/LocInfoType.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateArgumentVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/AST/TypeVisitor.h"
#include "llvm/Support/SaveAndRestore.h"
namespace clang {
class APValue;
/**
ASTNodeTraverser traverses the Clang AST for dumping purposes.
The `Derived::doGetNodeDelegate()` method is required to be an accessible member
which returns a reference of type `NodeDelegateType &` which implements the
following interface:
struct {
template <typename Fn> void AddChild(Fn DoAddChild);
template <typename Fn> void AddChild(StringRef Label, Fn DoAddChild);
void Visit(const comments::Comment *C, const comments::FullComment *FC);
void Visit(const Attr *A);
void Visit(const TemplateArgument &TA, SourceRange R = {},
const Decl *From = nullptr, StringRef Label = {});
void Visit(const Stmt *Node);
void Visit(const Type *T);
void Visit(QualType T);
void Visit(TypeLoc);
void Visit(const Decl *D);
void Visit(const CXXCtorInitializer *Init);
void Visit(const OpenACCClause *C);
void Visit(const OMPClause *C);
void Visit(const BlockDecl::Capture &C);
void Visit(const GenericSelectionExpr::ConstAssociation &A);
void Visit(const concepts::Requirement *R);
void Visit(const APValue &Value, QualType Ty);
};
*/
template <typename Derived, typename NodeDelegateType>
class ASTNodeTraverser
: public ConstDeclVisitor<Derived>,
public ConstStmtVisitor<Derived>,
public comments::ConstCommentVisitor<Derived, void,
const comments::FullComment *>,
public TypeVisitor<Derived>,
public TypeLocVisitor<Derived>,
public ConstAttrVisitor<Derived>,
public ConstTemplateArgumentVisitor<Derived> {
/// Indicates whether we should trigger deserialization of nodes that had
/// not already been loaded.
bool Deserialize = false;
/// Tracks whether we should dump TypeLocs etc.
///
/// Detailed location information such as TypeLoc nodes is not usually
/// included in the dump (too verbose).
/// But when explicitly asked to dump a Loc node, we do so recursively,
/// including e.g. FunctionTypeLoc => ParmVarDecl => TypeLoc.
bool VisitLocs = false;
TraversalKind Traversal = TraversalKind::TK_AsIs;
NodeDelegateType &getNodeDelegate() {
return getDerived().doGetNodeDelegate();
}
Derived &getDerived() { return *static_cast<Derived *>(this); }
public:
void setDeserialize(bool D) { Deserialize = D; }
bool getDeserialize() const { return Deserialize; }
void SetTraversalKind(TraversalKind TK) { Traversal = TK; }
TraversalKind GetTraversalKind() const { return Traversal; }
void Visit(const Decl *D, bool VisitLocs = false) {
if (Traversal == TK_IgnoreUnlessSpelledInSource && D && D->isImplicit())
return;
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(D);
if (!D)
return;
{
llvm::SaveAndRestore RestoreVisitLocs(this->VisitLocs, VisitLocs);
ConstDeclVisitor<Derived>::Visit(D);
}
for (const auto &A : D->attrs())
Visit(A);
if (const comments::FullComment *Comment =
D->getASTContext().getLocalCommentForDeclUncached(D))
Visit(Comment, Comment);
// Decls within functions are visited by the body.
if (!isa<FunctionDecl, ObjCMethodDecl, BlockDecl>(*D)) {
if (Traversal != TK_AsIs) {
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
auto SK = CTSD->getSpecializationKind();
if (SK == TSK_ExplicitInstantiationDeclaration ||
SK == TSK_ExplicitInstantiationDefinition)
return;
}
}
if (const auto *DC = dyn_cast<DeclContext>(D))
dumpDeclContext(DC);
}
});
}
void Visit(const Stmt *Node, StringRef Label = {}) {
getNodeDelegate().AddChild(Label, [=] {
const Stmt *S = Node;
if (auto *E = dyn_cast_or_null<Expr>(S)) {
switch (Traversal) {
case TK_AsIs:
break;
case TK_IgnoreUnlessSpelledInSource:
S = E->IgnoreUnlessSpelledInSource();
break;
}
}
getNodeDelegate().Visit(S);
if (!S) {
return;
}
ConstStmtVisitor<Derived>::Visit(S);
// Some statements have custom mechanisms for dumping their children.
if (isa<DeclStmt, GenericSelectionExpr, RequiresExpr,
OpenACCWaitConstruct, SYCLKernelCallStmt>(S))
return;
if (Traversal == TK_IgnoreUnlessSpelledInSource &&
isa<LambdaExpr, CXXForRangeStmt, CallExpr,
CXXRewrittenBinaryOperator>(S))
return;
for (const Stmt *SubStmt : S->children())
Visit(SubStmt);
});
}
void Visit(QualType T) {
SplitQualType SQT = T.split();
if (!SQT.Quals.hasQualifiers())
return Visit(SQT.Ty);
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(T);
Visit(T.split().Ty);
});
}
void Visit(const Type *T) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(T);
if (!T)
return;
TypeVisitor<Derived>::Visit(T);
QualType SingleStepDesugar =
T->getLocallyUnqualifiedSingleStepDesugaredType();
if (SingleStepDesugar != QualType(T, 0))
Visit(SingleStepDesugar);
});
}
void Visit(TypeLoc T) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(T);
if (T.isNull())
return;
TypeLocVisitor<Derived>::Visit(T);
if (auto Inner = T.getNextTypeLoc())
Visit(Inner);
});
}
void Visit(const Attr *A) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(A);
ConstAttrVisitor<Derived>::Visit(A);
});
}
void Visit(const CXXCtorInitializer *Init) {
if (Traversal == TK_IgnoreUnlessSpelledInSource && !Init->isWritten())
return;
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(Init);
Visit(Init->getInit());
});
}
void Visit(const TemplateArgument &A, SourceRange R = {},
const Decl *From = nullptr, const char *Label = nullptr) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(A, R, From, Label);
ConstTemplateArgumentVisitor<Derived>::Visit(A);
});
}
void Visit(const BlockDecl::Capture &C) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(C);
if (C.hasCopyExpr())
Visit(C.getCopyExpr());
});
}
void Visit(const OpenACCClause *C) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(C);
for (const auto *S : C->children())
Visit(S);
});
}
void Visit(const OMPClause *C) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(C);
for (const auto *S : C->children())
Visit(S);
});
}
void Visit(const GenericSelectionExpr::ConstAssociation &A) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(A);
if (const TypeSourceInfo *TSI = A.getTypeSourceInfo())
Visit(TSI->getType());
Visit(A.getAssociationExpr());
});
}
void Visit(const concepts::Requirement *R) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(R);
if (!R)
return;
if (auto *TR = dyn_cast<concepts::TypeRequirement>(R)) {
if (!TR->isSubstitutionFailure())
Visit(TR->getType()->getType().getTypePtr());
} else if (auto *ER = dyn_cast<concepts::ExprRequirement>(R)) {
if (!ER->isExprSubstitutionFailure())
Visit(ER->getExpr());
if (!ER->getReturnTypeRequirement().isEmpty())
Visit(ER->getReturnTypeRequirement()
.getTypeConstraint()
->getImmediatelyDeclaredConstraint());
} else if (auto *NR = dyn_cast<concepts::NestedRequirement>(R)) {
if (!NR->hasInvalidConstraint())
Visit(NR->getConstraintExpr());
}
});
}
void Visit(const ConceptReference *R) {
getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(R); });
}
void Visit(const APValue &Value, QualType Ty) {
getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(Value, Ty); });
}
void Visit(const comments::Comment *C, const comments::FullComment *FC) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(C, FC);
if (!C) {
return;
}
comments::ConstCommentVisitor<Derived, void,
const comments::FullComment *>::visit(C,
FC);
for (comments::Comment::child_iterator I = C->child_begin(),
E = C->child_end();
I != E; ++I)
Visit(*I, FC);
});
}
void Visit(const DynTypedNode &N) {
// FIXME: Improve this with a switch or a visitor pattern.
if (const auto *D = N.get<Decl>())
Visit(D);
else if (const auto *S = N.get<Stmt>())
Visit(S);
else if (const auto *QT = N.get<QualType>())
Visit(*QT);
else if (const auto *T = N.get<Type>())
Visit(T);
else if (const auto *TL = N.get<TypeLoc>())
Visit(*TL);
else if (const auto *C = N.get<CXXCtorInitializer>())
Visit(C);
else if (const auto *C = N.get<OMPClause>())
Visit(C);
else if (const auto *T = N.get<TemplateArgument>())
Visit(*T);
else if (const auto *CR = N.get<ConceptReference>())
Visit(CR);
}
void dumpDeclContext(const DeclContext *DC) {
if (!DC)
return;
for (const auto *D : (Deserialize ? DC->decls() : DC->noload_decls()))
Visit(D);
}
void dumpTemplateParameters(const TemplateParameterList *TPL) {
if (!TPL)
return;
for (const auto &TP : *TPL)
Visit(TP);
if (const Expr *RC = TPL->getRequiresClause())
Visit(RC);
}
void
dumpASTTemplateArgumentListInfo(const ASTTemplateArgumentListInfo *TALI) {
if (!TALI)
return;
for (const auto &TA : TALI->arguments())
dumpTemplateArgumentLoc(TA);
}
void dumpTemplateArgumentLoc(const TemplateArgumentLoc &A,
const Decl *From = nullptr,
const char *Label = nullptr) {
Visit(A.getArgument(), A.getSourceRange(), From, Label);
}
void dumpTemplateArgumentList(const TemplateArgumentList &TAL) {
for (unsigned i = 0, e = TAL.size(); i < e; ++i)
Visit(TAL[i]);
}
void dumpObjCTypeParamList(const ObjCTypeParamList *typeParams) {
if (!typeParams)
return;
for (const auto &typeParam : *typeParams) {
Visit(typeParam);
}
}
void VisitComplexType(const ComplexType *T) { Visit(T->getElementType()); }
void VisitLocInfoType(const LocInfoType *T) {
Visit(T->getTypeSourceInfo()->getTypeLoc());
}
void VisitPointerType(const PointerType *T) { Visit(T->getPointeeType()); }
void VisitBlockPointerType(const BlockPointerType *T) {
Visit(T->getPointeeType());
}
void VisitReferenceType(const ReferenceType *T) {
Visit(T->getPointeeType());
}
void VisitMemberPointerType(const MemberPointerType *T) {
// FIXME: Provide a NestedNameSpecifier visitor.
NestedNameSpecifier Qualifier = T->getQualifier();
if (NestedNameSpecifier::Kind K = Qualifier.getKind();
K == NestedNameSpecifier::Kind::Type)
Visit(Qualifier.getAsType());
if (T->isSugared())
Visit(cast<MemberPointerType>(T->getCanonicalTypeUnqualified())
->getQualifier()
.getAsType());
Visit(T->getPointeeType());
}
void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); }
void VisitVariableArrayType(const VariableArrayType *T) {
VisitArrayType(T);
Visit(T->getSizeExpr());
}
void VisitDependentSizedArrayType(const DependentSizedArrayType *T) {
Visit(T->getElementType());
Visit(T->getSizeExpr());
}
void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) {
Visit(T->getElementType());
Visit(T->getSizeExpr());
}
void VisitVectorType(const VectorType *T) { Visit(T->getElementType()); }
void VisitFunctionType(const FunctionType *T) { Visit(T->getReturnType()); }
void VisitFunctionProtoType(const FunctionProtoType *T) {
VisitFunctionType(T);
for (const QualType &PT : T->getParamTypes())
Visit(PT);
}
void VisitTypeOfExprType(const TypeOfExprType *T) {
Visit(T->getUnderlyingExpr());
}
void VisitDecltypeType(const DecltypeType *T) {
Visit(T->getUnderlyingExpr());
}
void VisitPackIndexingType(const PackIndexingType *T) {
Visit(T->getPattern());
Visit(T->getIndexExpr());
}
void VisitUnaryTransformType(const UnaryTransformType *T) {
Visit(T->getBaseType());
}
void VisitAttributedType(const AttributedType *T) {
// FIXME: AttrKind
if (T->getModifiedType() != T->getEquivalentType())
Visit(T->getModifiedType());
}
void VisitBTFTagAttributedType(const BTFTagAttributedType *T) {
Visit(T->getWrappedType());
}
void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) {
QualType Contained = T->getContainedType();
if (!Contained.isNull())
Visit(Contained);
}
void VisitHLSLInlineSpirvType(const HLSLInlineSpirvType *T) {
for (auto &Operand : T->getOperands()) {
using SpirvOperandKind = SpirvOperand::SpirvOperandKind;
switch (Operand.getKind()) {
case SpirvOperandKind::ConstantId:
case SpirvOperandKind::Literal:
break;
case SpirvOperandKind::TypeId:
Visit(Operand.getResultType());
break;
default:
llvm_unreachable("Invalid SpirvOperand kind!");
}
}
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *) {}
void
VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
Visit(T->getArgumentPack());
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
for (const auto &Arg : T->template_arguments())
Visit(Arg);
}
void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) {
Visit(T->getPointeeType());
}
void VisitAtomicType(const AtomicType *T) { Visit(T->getValueType()); }
void VisitPipeType(const PipeType *T) { Visit(T->getElementType()); }
void VisitAdjustedType(const AdjustedType *T) { Visit(T->getOriginalType()); }
void VisitPackExpansionType(const PackExpansionType *T) {
if (!T->isSugared())
Visit(T->getPattern());
}
void VisitAutoType(const AutoType *T) {
for (const auto &Arg : T->getTypeConstraintArguments())
Visit(Arg);
}
// FIXME: ElaboratedType, DependentNameType,
// DependentTemplateSpecializationType, ObjCObjectType
// For TypeLocs, we automatically visit the inner type loc (pointee type etc).
// We must explicitly visit other lexically-nested nodes.
void VisitFunctionProtoTypeLoc(FunctionProtoTypeLoc TL) {
TypeLocVisitor<Derived>::VisitFunctionTypeLoc(TL);
for (const auto *Param : TL.getParams())
Visit(Param, /*VisitTypeLocs=*/true);
}
void VisitAutoTypeLoc(AutoTypeLoc TL) {
if (const auto *CR = TL.getConceptReference()) {
if (auto *Args = CR->getTemplateArgsAsWritten())
for (const auto &Arg : Args->arguments())
dumpTemplateArgumentLoc(Arg);
}
}
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
// FIXME: Provide NestedNamespecifierLoc visitor.
Visit(TL.getQualifierLoc().castAsTypeLoc());
}
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
}
void VisitDependentSizedArrayTypeLoc(DependentSizedArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
}
void VisitDependentSizedExtVectorTypeLoc(DependentSizedExtVectorTypeLoc TL) {
Visit(cast<DependentSizedExtVectorType>(TL.getType())->getSizeExpr());
}
void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
Visit(TL.getUnderlyingExpr());
}
void VisitDecltypeType(DecltypeType TL) {
Visit(TL.getUnderlyingExpr());
}
void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I)
dumpTemplateArgumentLoc(TL.getArgLoc(I));
}
void VisitDependentTemplateSpecializationTypeLoc(
DependentTemplateSpecializationTypeLoc TL) {
for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I)
dumpTemplateArgumentLoc(TL.getArgLoc(I));
}
void VisitTypedefDecl(const TypedefDecl *D) { Visit(D->getUnderlyingType()); }
void VisitEnumConstantDecl(const EnumConstantDecl *D) {
if (const Expr *Init = D->getInitExpr())
Visit(Init);
}
void VisitFunctionDecl(const FunctionDecl *D) {
if (FunctionTemplateSpecializationInfo *FTSI =
D->getTemplateSpecializationInfo())
dumpTemplateArgumentList(*FTSI->TemplateArguments);
else if (DependentFunctionTemplateSpecializationInfo *DFTSI =
D->getDependentSpecializationInfo())
dumpASTTemplateArgumentListInfo(DFTSI->TemplateArgumentsAsWritten);
if (D->param_begin())
for (const auto *Parameter : D->parameters())
Visit(Parameter);
if (const AssociatedConstraint &TRC = D->getTrailingRequiresClause())
Visit(TRC.ConstraintExpr);
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isDefaulted())
return;
if (const auto *C = dyn_cast<CXXConstructorDecl>(D))
for (const auto *I : C->inits())
Visit(I);
if (D->doesThisDeclarationHaveABody())
Visit(D->getBody());
}
void VisitFieldDecl(const FieldDecl *D) {
if (D->isBitField())
Visit(D->getBitWidth());
if (Expr *Init = D->getInClassInitializer())
Visit(Init);
}
void VisitVarDecl(const VarDecl *D) {
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
return;
if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
Visit(TSI->getTypeLoc());
if (D->hasInit())
Visit(D->getInit());
}
void VisitDecompositionDecl(const DecompositionDecl *D) {
VisitVarDecl(D);
for (const auto *B : D->bindings())
Visit(B);
}
void VisitBindingDecl(const BindingDecl *D) {
if (Traversal == TK_IgnoreUnlessSpelledInSource)
return;
if (const auto *V = D->getHoldingVar())
Visit(V);
if (const auto *E = D->getBinding())
Visit(E);
}
void VisitFileScopeAsmDecl(const FileScopeAsmDecl *D) {
Visit(D->getAsmStringExpr());
}
void VisitTopLevelStmtDecl(const TopLevelStmtDecl *D) { Visit(D->getStmt()); }
void VisitOutlinedFunctionDecl(const OutlinedFunctionDecl *D) {
for (const ImplicitParamDecl *Parameter : D->parameters())
Visit(Parameter);
Visit(D->getBody());
}
void VisitCapturedDecl(const CapturedDecl *D) { Visit(D->getBody()); }
void VisitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D) {
for (const auto *E : D->varlist())
Visit(E);
}
void VisitOMPDeclareReductionDecl(const OMPDeclareReductionDecl *D) {
Visit(D->getCombiner());
if (const auto *Initializer = D->getInitializer())
Visit(Initializer);
}
void VisitOMPDeclareMapperDecl(const OMPDeclareMapperDecl *D) {
for (const auto *C : D->clauselists())
Visit(C);
}
void VisitOMPCapturedExprDecl(const OMPCapturedExprDecl *D) {
Visit(D->getInit());
}
void VisitOMPAllocateDecl(const OMPAllocateDecl *D) {
for (const auto *E : D->varlist())
Visit(E);
for (const auto *C : D->clauselists())
Visit(C);
}
template <typename SpecializationDecl>
void dumpTemplateDeclSpecialization(const SpecializationDecl *D) {
for (const auto *RedeclWithBadType : D->redecls()) {
// FIXME: The redecls() range sometimes has elements of a less-specific
// type. (In particular, ClassTemplateSpecializationDecl::redecls() gives
// us TagDecls, and should give CXXRecordDecls).
auto *Redecl = dyn_cast<SpecializationDecl>(RedeclWithBadType);
if (!Redecl) {
// Found the injected-class-name for a class template. This will be
// dumped as part of its surrounding class so we don't need to dump it
// here.
assert(isa<CXXRecordDecl>(RedeclWithBadType) &&
"expected an injected-class-name");
continue;
}
Visit(Redecl);
}
}
template <typename TemplateDecl>
void dumpTemplateDecl(const TemplateDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
Visit(D->getTemplatedDecl());
if (Traversal == TK_AsIs) {
for (const auto *Child : D->specializations())
dumpTemplateDeclSpecialization(Child);
}
}
void VisitTypeAliasDecl(const TypeAliasDecl *D) {
Visit(D->getUnderlyingType());
}
void VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
Visit(D->getTemplatedDecl());
}
void VisitStaticAssertDecl(const StaticAssertDecl *D) {
Visit(D->getAssertExpr());
Visit(D->getMessage());
}
void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
dumpTemplateDecl(D);
}
void VisitClassTemplateDecl(const ClassTemplateDecl *D) {
dumpTemplateDecl(D);
}
void VisitClassTemplateSpecializationDecl(
const ClassTemplateSpecializationDecl *D) {
dumpTemplateArgumentList(D->getTemplateArgs());
}
void VisitClassTemplatePartialSpecializationDecl(
const ClassTemplatePartialSpecializationDecl *D) {
VisitClassTemplateSpecializationDecl(D);
dumpTemplateParameters(D->getTemplateParameters());
}
void VisitVarTemplateDecl(const VarTemplateDecl *D) { dumpTemplateDecl(D); }
void VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
}
void
VisitVarTemplateSpecializationDecl(const VarTemplateSpecializationDecl *D) {
dumpTemplateArgumentList(D->getTemplateArgs());
VisitVarDecl(D);
}
void VisitVarTemplatePartialSpecializationDecl(
const VarTemplatePartialSpecializationDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
VisitVarTemplateSpecializationDecl(D);
}
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
if (const auto *TC = D->getTypeConstraint())
Visit(TC->getImmediatelyDeclaredConstraint());
if (D->hasDefaultArgument())
Visit(D->getDefaultArgument().getArgument(), SourceRange(),
D->getDefaultArgStorage().getInheritedFrom(),
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
}
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
if (const auto *E = D->getPlaceholderTypeConstraint())
Visit(E);
if (D->hasDefaultArgument())
dumpTemplateArgumentLoc(
D->getDefaultArgument(), D->getDefaultArgStorage().getInheritedFrom(),
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
}
void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
if (D->hasDefaultArgument())
dumpTemplateArgumentLoc(
D->getDefaultArgument(), D->getDefaultArgStorage().getInheritedFrom(),
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
}
void VisitConceptDecl(const ConceptDecl *D) {
dumpTemplateParameters(D->getTemplateParameters());
Visit(D->getConstraintExpr());
}
void VisitImplicitConceptSpecializationDecl(
const ImplicitConceptSpecializationDecl *CSD) {
for (const TemplateArgument &Arg : CSD->getTemplateArguments())
Visit(Arg);
}
void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *CSE) {
Visit(CSE->getSpecializationDecl());
if (CSE->hasExplicitTemplateArgs())
for (const auto &ArgLoc : CSE->getTemplateArgsAsWritten()->arguments())
dumpTemplateArgumentLoc(ArgLoc);
}
void VisitUsingShadowDecl(const UsingShadowDecl *D) {
Visit(D->getTargetDecl());
}
void VisitFriendDecl(const FriendDecl *D) {
if (D->getFriendType()) {
// Traverse any CXXRecordDecl owned by this type, since
// it will not be in the parent context:
if (auto *TT = D->getFriendType()->getType()->getAs<TagType>())
if (TT->isTagOwned())
Visit(TT->getOriginalDecl());
} else {
Visit(D->getFriendDecl());
}
}
void VisitObjCMethodDecl(const ObjCMethodDecl *D) {
if (D->isThisDeclarationADefinition())
dumpDeclContext(D);
else
for (const ParmVarDecl *Parameter : D->parameters())
Visit(Parameter);
if (D->hasBody())
Visit(D->getBody());
}
void VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
dumpObjCTypeParamList(D->getTypeParamList());
}
void VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
dumpObjCTypeParamList(D->getTypeParamListAsWritten());
}
void VisitObjCImplementationDecl(const ObjCImplementationDecl *D) {
for (const auto &I : D->inits())
Visit(I);
}
void VisitBlockDecl(const BlockDecl *D) {
for (const auto &I : D->parameters())
Visit(I);
for (const auto &I : D->captures())
Visit(I);
Visit(D->getBody());
}
void VisitDeclStmt(const DeclStmt *Node) {
for (const auto &D : Node->decls())
Visit(D);
}
void VisitAttributedStmt(const AttributedStmt *Node) {
for (const auto *A : Node->getAttrs())
Visit(A);
}
void VisitLabelStmt(const LabelStmt *Node) {
if (Node->getDecl()->hasAttrs()) {
for (const auto *A : Node->getDecl()->getAttrs())
Visit(A);
}
}
void VisitCXXCatchStmt(const CXXCatchStmt *Node) {
Visit(Node->getExceptionDecl());
}
void VisitCapturedStmt(const CapturedStmt *Node) {
Visit(Node->getCapturedDecl());
}
void VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *Node) {
Visit(Node->getOriginalStmt());
if (Traversal != TK_IgnoreUnlessSpelledInSource)
Visit(Node->getOutlinedFunctionDecl());
}
void VisitOMPExecutableDirective(const OMPExecutableDirective *Node) {
for (const auto *C : Node->clauses())
Visit(C);
}
void VisitOpenACCConstructStmt(const OpenACCConstructStmt *Node) {
for (const auto *C : Node->clauses())
Visit(C);
}
void VisitOpenACCWaitConstruct(const OpenACCWaitConstruct *Node) {
// Needs custom child checking to put clauses AFTER the children, which are
// the expressions in the 'wait' construct. Others likely need this as well,
// and might need to do the associated statement after it.
for (const Stmt *S : Node->children())
Visit(S);
for (const auto *C : Node->clauses())
Visit(C);
}
void VisitInitListExpr(const InitListExpr *ILE) {
if (auto *Filler = ILE->getArrayFiller()) {
Visit(Filler, "array_filler");
}
}
void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE) {
if (auto *Filler = PLIE->getArrayFiller()) {
Visit(Filler, "array_filler");
}
}
void VisitBlockExpr(const BlockExpr *Node) { Visit(Node->getBlockDecl()); }
void VisitOpaqueValueExpr(const OpaqueValueExpr *Node) {
if (Expr *Source = Node->getSourceExpr())
Visit(Source);
}
void VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
if (E->isExprPredicate()) {
Visit(E->getControllingExpr());
Visit(E->getControllingExpr()->getType()); // FIXME: remove
} else
Visit(E->getControllingType()->getType());
for (const auto Assoc : E->associations()) {
Visit(Assoc);
}
}
void VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *E) {
if (E->hasExplicitTemplateArgs())
for (auto Arg : E->template_arguments())
Visit(Arg.getArgument());
}
void VisitRequiresExpr(const RequiresExpr *E) {
for (auto *D : E->getLocalParameters())
Visit(D);
for (auto *R : E->getRequirements())
Visit(R);
}
void VisitTypeTraitExpr(const TypeTraitExpr *E) {
// Argument types are not children of the TypeTraitExpr.
for (auto *A : E->getArgs())
Visit(A->getType());
}
void VisitLambdaExpr(const LambdaExpr *Node) {
if (Traversal == TK_IgnoreUnlessSpelledInSource) {
for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {
const auto *C = Node->capture_begin() + I;
if (!C->isExplicit())
continue;
if (Node->isInitCapture(C))
Visit(C->getCapturedVar());
else
Visit(Node->capture_init_begin()[I]);
}
dumpTemplateParameters(Node->getTemplateParameterList());
for (const auto *P : Node->getCallOperator()->parameters())
Visit(P);
Visit(Node->getBody());
} else {
return Visit(Node->getLambdaClass());
}
}
void VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {
if (Node->isPartiallySubstituted())
for (const auto &A : Node->getPartialArguments())
Visit(A);
}
void VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) {
Visit(E->getParameter());
}
void VisitSubstNonTypeTemplateParmPackExpr(
const SubstNonTypeTemplateParmPackExpr *E) {
Visit(E->getParameterPack());
Visit(E->getArgumentPack());
}
void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node) {
if (const VarDecl *CatchParam = Node->getCatchParamDecl())
Visit(CatchParam);
}
void VisitCXXForRangeStmt(const CXXForRangeStmt *Node) {
if (Traversal == TK_IgnoreUnlessSpelledInSource) {
Visit(Node->getInit());
Visit(Node->getLoopVariable());
Visit(Node->getRangeInit());
Visit(Node->getBody());
}
}
void VisitCallExpr(const CallExpr *Node) {
for (const auto *Child :
make_filter_range(Node->children(), [this](const Stmt *Child) {
if (Traversal != TK_IgnoreUnlessSpelledInSource)
return false;
return !isa<CXXDefaultArgExpr>(Child);
})) {
Visit(Child);
}
}
void VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *Node) {
if (Traversal == TK_IgnoreUnlessSpelledInSource) {
Visit(Node->getLHS());
Visit(Node->getRHS());
} else {
ConstStmtVisitor<Derived>::VisitCXXRewrittenBinaryOperator(Node);
}
}
void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
Visit(TA.getAsExpr());
}
void VisitTypeTemplateArgument(const TemplateArgument &TA) {
Visit(TA.getAsType());
}
void VisitPackTemplateArgument(const TemplateArgument &TA) {
for (const auto &TArg : TA.pack_elements())
Visit(TArg);
}
void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node) {
Visit(Node->getExpr());
}
void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node) {
Visit(Node->getExpr());
}
// Implements Visit methods for Attrs.
#include "clang/AST/AttrNodeTraverse.inc"
};
} // namespace clang
#endif // LLVM_CLANG_AST_ASTNODETRAVERSER_H