https://github.com/sam-mccall created https://github.com/llvm/llvm-project/pull/65484:
The ability to dump AST nodes is important to ad-hoc debugging, and the fact this doesn't work with TypeLoc nodes is an obvious missing feature in e.g. clang-query (`set output dump` simply does nothing). Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes seems like a clear win. It looks like this: ``` int main(int argc, char **argv); FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl |-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int' | `-BuiltinTypeLoc <col:10> 'int' |-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **' | `-PointerTypeLoc <col:20, col:26> 'char **' | `-PointerTypeLoc <col:20, col:25> 'char *' | `-BuiltinTypeLoc <col:20> 'char' `-BuiltinTypeLoc <col:1> 'int' ``` It dumps the lexically nested tree of type locs. This often looks similar to how types are dumped, but unlike types we don't look at desugaring e.g. typedefs, as their underlying types are not lexically spelled here. --- Less clear is exactly when to include these nodes in existing text AST dumps rooted at (TranslationUnit)Decls. These already omit supported nodes sometimes, e.g. NestedNameSpecifiers are often mentioned but not recursively dumped. TypeLocs are a more extreme case: they're ~always more verbose than the current AST dump. So this patch punts on that, TypeLocs are only ever printed recursively as part of a TypeLoc::dump() call. It would also be nice to be able to invoke `clang` to dump a typeloc somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the best verison of that is, so this patch doesn't do it. --- There are similar (less critical!) nodes: TemplateArgumentLoc etc, these also don't have dump() functions today and are obvious extensions. I suspect that we should add these, and Loc nodes should dump each other (e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump the NestedNameSpecifierLoc `vector<int>::`, which dumps the TemplateSpecializationTypeLoc `vector<int>::` etc). Maybe this generalizes further to a "full syntactic dump" mode, where even Decls and Stmts would print the TypeLocs they lexically contain. But this may be more complex than useful. >From f2269d93e313378581085bca418914229316bfc6 Mon Sep 17 00:00:00 2001 From: Sam McCall <sam.mcc...@gmail.com> Date: Mon, 4 Sep 2023 15:48:47 +0200 Subject: [PATCH] [AST] Add dump() method to TypeLoc The ability to dump AST nodes is important to ad-hoc debugging, and the fact this doesn't work with TypeLoc nodes is an obvious missing feature in e.g. clang-query (`set output dump` simply does nothing). Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes seems like a clear win. It looks like this: ``` int main(int argc, char **argv); FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl |-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int' | `-BuiltinTypeLoc <col:10> 'int' |-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **' | `-PointerTypeLoc <col:20, col:26> 'char **' | `-PointerTypeLoc <col:20, col:25> 'char *' | `-BuiltinTypeLoc <col:20> 'char' `-BuiltinTypeLoc <col:1> 'int' ``` It dumps the lexically nested tree of type locs. This often looks similar to how types are dumped, but unlike types we don't look at desugaring e.g. typedefs, as their underlying types are not lexically spelled here. --- Less clear is exactly when to include these nodes in existing text AST dumps rooted at (TranslationUnit)Decls. These already omit supported nodes sometimes, e.g. NestedNameSpecifiers are often mentioned but not recursively dumped. TypeLocs are a more extreme case: they're ~always more verbose than the current AST dump. So this patch punts on that, TypeLocs are only ever printed recursively as part of a TypeLoc::dump() call. It would also be nice to be able to invoke `clang` to dump a typeloc somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the best verison of that is, so this patch doesn't do it. --- There are similar (less critical!) nodes: TemplateArgumentLoc etc, these also don't have dump() functions today and are obvious extensions. I suspect that we should add these, and Loc nodes should dump each other (e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump the NestedNameSpecifierLoc `vector<int>::`, which dumps the TemplateSpecializationTypeLoc `vector<int>::` etc). Maybe this generalizes further to a "full syntactic dump" mode, where even Decls and Stmts would print the TypeLocs they lexically contain. But this may be more complex than useful. --- clang/include/clang/AST/ASTNodeTraverser.h | 83 +++++++++++++++++++++- clang/include/clang/AST/JSONNodeDumper.h | 2 + clang/include/clang/AST/TextNodeDumper.h | 6 ++ clang/include/clang/AST/TypeLoc.h | 3 + clang/lib/AST/ASTDumper.cpp | 13 ++++ clang/lib/AST/ASTTypeTraits.cpp | 2 + clang/lib/AST/JSONNodeDumper.cpp | 31 ++++++++ clang/lib/AST/TextNodeDumper.cpp | 34 +++++++-- clang/unittests/AST/CMakeLists.txt | 1 + 9 files changed, 168 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 1151a756ff377b6..a51b042d8f8bd5e 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -23,7 +23,9 @@ #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 { @@ -48,6 +50,7 @@ struct { 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 OMPClause *C); @@ -64,6 +67,7 @@ class ASTNodeTraverser public comments::ConstCommentVisitor<Derived, void, const comments::FullComment *>, public TypeVisitor<Derived>, + public TypeLocVisitor<Derived>, public ConstAttrVisitor<Derived>, public ConstTemplateArgumentVisitor<Derived> { @@ -71,6 +75,14 @@ class ASTNodeTraverser /// 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() { @@ -85,7 +97,7 @@ class ASTNodeTraverser void SetTraversalKind(TraversalKind TK) { Traversal = TK; } TraversalKind GetTraversalKind() const { return Traversal; } - void Visit(const Decl *D) { + void Visit(const Decl *D, bool VisitLocs = false) { if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isImplicit()) return; @@ -94,7 +106,10 @@ class ASTNodeTraverser if (!D) return; - ConstDeclVisitor<Derived>::Visit(D); + { + llvm::SaveAndRestore RestoreVisitLocs(this->VisitLocs, VisitLocs); + ConstDeclVisitor<Derived>::Visit(D); + } for (const auto &A : D->attrs()) Visit(A); @@ -181,6 +196,17 @@ class ASTNodeTraverser }); } + 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); @@ -286,6 +312,8 @@ class ASTNodeTraverser 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>()) @@ -346,7 +374,7 @@ class ASTNodeTraverser void VisitComplexType(const ComplexType *T) { Visit(T->getElementType()); } void VisitLocInfoType(const LocInfoType *T) { - Visit(T->getTypeSourceInfo()->getType()); + Visit(T->getTypeSourceInfo()->getTypeLoc()); } void VisitPointerType(const PointerType *T) { Visit(T->getPointeeType()); } void VisitBlockPointerType(const BlockPointerType *T) { @@ -415,9 +443,55 @@ class ASTNodeTraverser 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) { + Visit(TL.getClassTInfo()->getTypeLoc()); + } + 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) { @@ -458,6 +532,9 @@ class ASTNodeTraverser if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl()) return; + if (VisitLocs) + if (const auto *TSI = D->getTypeSourceInfo()) + Visit(TSI->getTypeLoc()); if (D->hasInit()) Visit(D->getInit()); } diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 4def5389137fa43..dde70dde2fa2be1 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -197,6 +197,7 @@ class JSONNodeDumper void Visit(const Type *T); void Visit(QualType T); void Visit(const Decl *D); + void Visit(TypeLoc TL); void Visit(const comments::Comment *C, const comments::FullComment *FC); void Visit(const TemplateArgument &TA, SourceRange R = {}, @@ -207,6 +208,7 @@ class JSONNodeDumper void Visit(const GenericSelectionExpr::ConstAssociation &A); void Visit(const concepts::Requirement *R); void Visit(const APValue &Value, QualType Ty); + void Visit(const ConceptReference *); void VisitAliasAttr(const AliasAttr *AA); void VisitCleanupAttr(const CleanupAttr *CA); diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 2f4ed082a0c7ad4..9f1a4e0b2c38e13 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -24,6 +24,7 @@ #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" namespace clang { @@ -132,6 +133,7 @@ class TextNodeDumper public ConstTemplateArgumentVisitor<TextNodeDumper>, public ConstStmtVisitor<TextNodeDumper>, public TypeVisitor<TextNodeDumper>, + public TypeLocVisitor<TextNodeDumper>, public ConstDeclVisitor<TextNodeDumper> { raw_ostream &OS; const bool ShowColors; @@ -179,6 +181,8 @@ class TextNodeDumper void Visit(QualType T); + void Visit(TypeLoc); + void Visit(const Decl *D); void Visit(const CXXCtorInitializer *Init); @@ -335,6 +339,8 @@ class TextNodeDumper void VisitObjCInterfaceType(const ObjCInterfaceType *T); void VisitPackExpansionType(const PackExpansionType *T); + void VisitTypeLoc(TypeLoc TL); + void VisitLabelDecl(const LabelDecl *D); void VisitTypedefDecl(const TypedefDecl *D); void VisitEnumDecl(const EnumDecl *D); diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 98427a8dcbfe660..78f665da7897c8d 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -229,6 +229,9 @@ class TypeLoc { /// __nullable, or __null_unspecifier), if there is one. SourceLocation findNullabilityLoc() const; + void dump() const; + void dump(llvm::raw_ostream &, const ASTContext &) const; + private: static bool isKind(const TypeLoc&) { return true; diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index cc9a84eecaadba6..6efc5bb92e28d2b 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -200,6 +200,19 @@ LLVM_DUMP_METHOD void Type::dump(llvm::raw_ostream &OS, QualType(this, 0).dump(OS, Context); } +//===----------------------------------------------------------------------===// +// TypeLoc method implementations +//===----------------------------------------------------------------------===// + +LLVM_DUMP_METHOD void TypeLoc::dump() const { + ASTDumper(llvm::errs(), /*ShowColors=*/false).Visit(*this); +} + +LLVM_DUMP_METHOD void TypeLoc::dump(llvm::raw_ostream &OS, + const ASTContext &Context) const { + ASTDumper(OS, Context, Context.getDiagnostics().getShowColors()).Visit(*this); +} + //===----------------------------------------------------------------------===// // Decl method implementations //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp index 4c7496c699beffd..99916f523aa95ef 100644 --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -228,6 +228,8 @@ void DynTypedNode::dump(llvm::raw_ostream &OS, T->dump(OS, Context); else if (const ConceptReference *C = get<ConceptReference>()) C->dump(OS); + else if (const TypeLoc *TL = get<TypeLoc>()) + TL->dump(OS, Context); else OS << "Unable to dump values of type " << NodeKind.asStringRef() << "\n"; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index e67c2c7e216dcea..ed7e79a4fce482d 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -96,6 +96,21 @@ void JSONNodeDumper::Visit(QualType T) { JOS.attribute("qualifiers", T.split().Quals.getAsString()); } +void JSONNodeDumper::Visit(TypeLoc TL) { + if (TL.isNull()) + return; + JOS.attribute("kind", + (llvm::Twine(TL.getTypeLocClass() == TypeLoc::Qualified + ? "Qualified" + : TL.getTypePtr()->getTypeClassName()) + + "TypeLoc") + .str()); + JOS.attribute("type", + createQualType(QualType(TL.getType()), /*Desugar*/ false)); + JOS.attributeObject("range", + [TL, this] { writeSourceRange(TL.getSourceRange()); }); +} + void JSONNodeDumper::Visit(const Decl *D) { JOS.attribute("id", createPointerRepresentation(D)); @@ -223,6 +238,22 @@ void JSONNodeDumper::Visit(const APValue &Value, QualType Ty) { JOS.attribute("value", OS.str()); } +void JSONNodeDumper::Visit(const ConceptReference *CR) { + JOS.attribute("kind", "ConceptReference"); + JOS.attribute("id", createPointerRepresentation(CR->getNamedConcept())); + if (const auto *Args = CR->getTemplateArgsAsWritten()) { + JOS.attributeArray("templateArgsAsWritten", [Args, this] { + for (const TemplateArgumentLoc &TAL : Args->arguments()) + JOS.object( + [&TAL, this] { Visit(TAL.getArgument(), TAL.getSourceRange()); }); + }); + } + JOS.attributeObject("loc", + [CR, this] { writeSourceLocation(CR->getLocation()); }); + JOS.attributeObject("range", + [CR, this] { writeSourceRange(CR->getSourceRange()); }); +} + void JSONNodeDumper::writeIncludeStack(PresumedLoc Loc, bool JustFirst) { if (Loc.isInvalid()) return; diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 5c8600035638b3b..714a05fee743973 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -18,6 +18,7 @@ #include "clang/AST/LocInfoType.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLocVisitor.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" @@ -240,6 +241,27 @@ void TextNodeDumper::Visit(QualType T) { OS << " " << T.split().Quals.getAsString(); } +void TextNodeDumper::Visit(TypeLoc TL) { + if (!TL) { + ColorScope Color(OS, ShowColors, NullColor); + OS << "<<<NULL>>>"; + return; + } + + { + ColorScope Color(OS, ShowColors, TypeColor); + OS << (TL.getTypeLocClass() == TypeLoc::Qualified + ? "Qualified" + : TL.getType()->getTypeClassName()) + << "TypeLoc"; + } + dumpSourceRange(TL.getSourceRange()); + OS << ' '; + dumpBareType(TL.getType(), /*Desugar=*/false); + + TypeLocVisitor<TextNodeDumper>::Visit(TL); +} + void TextNodeDumper::Visit(const Decl *D) { if (!D) { ColorScope Color(OS, ShowColors, NullColor); @@ -1763,11 +1785,8 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) { OS << " decltype(auto)"; if (!T->isDeduced()) OS << " undeduced"; - if (T->isConstrained()) { + if (T->isConstrained()) dumpDeclRef(T->getTypeConstraintConcept()); - for (const auto &Arg : T->getTypeConstraintArguments()) - VisitTemplateArgument(Arg); - } } void TextNodeDumper::VisitDeducedTemplateSpecializationType( @@ -1800,6 +1819,13 @@ void TextNodeDumper::VisitPackExpansionType(const PackExpansionType *T) { OS << " expansions " << *N; } +void TextNodeDumper::VisitTypeLoc(TypeLoc TL) { + // By default, add extra Type details with no extra loc info. + TypeVisitor<TextNodeDumper>::Visit(TL.getTypePtr()); +} +// FIXME: override behavior for TypeLocs that have interesting location +// information, such as the qualifier in ElaboratedTypeLoc. + void TextNodeDumper::VisitLabelDecl(const LabelDecl *D) { dumpName(D); } void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) { diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 12484be9206e23c..52ad440d9ad0e52 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(ASTTests ASTContextParentMapTest.cpp + ASTDumperTest.cpp ASTExprTest.cpp ASTImporterFixtures.cpp ASTImporterTest.cpp _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits