[clang] [AST] Add dump() method to TypeLoc (PR #65484)

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 11 13:51:42 PDT 2023


https://github.com/sam-mccall updated https://github.com/llvm/llvm-project/pull/65484:

>From f2269d93e313378581085bca418914229316bfc6 Mon Sep 17 00:00:00 2001
From: Sam McCall <sam.mccall at gmail.com>
Date: Mon, 4 Sep 2023 15:48:47 +0200
Subject: [PATCH 1/3] [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

>From 5cae724e13189fdab062336a788ae453fce232c8 Mon Sep 17 00:00:00 2001
From: Sam McCall <sam.mccall at gmail.com>
Date: Wed, 6 Sep 2023 16:16:33 +0200
Subject: [PATCH 2/3] add test

---
 clang/unittests/AST/ASTDumperTest.cpp | 86 +++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 clang/unittests/AST/ASTDumperTest.cpp

diff --git a/clang/unittests/AST/ASTDumperTest.cpp b/clang/unittests/AST/ASTDumperTest.cpp
new file mode 100644
index 000000000000000..d3a421ee46f36ab
--- /dev/null
+++ b/clang/unittests/AST/ASTDumperTest.cpp
@@ -0,0 +1,86 @@
+//===- unittests/AST/ASTDumperTest.cpp --- Test of AST node dump() methods ===//
+//
+// 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 contains tests for TypeLoc::dump() and related methods.
+// Most of these are lit tests via clang -ast-dump. However some nodes are not
+// included in dumps of (TranslationUnit)Decl, but still relevant when dumped
+// directly.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTPrint.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/Testing/TestAST.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+
+namespace {
+using testing::ElementsAre;
+using testing::StartsWith;
+
+std::vector<std::string> dumpTypeLoc(llvm::StringRef Name, ASTContext &Ctx) {
+  auto Lookup = Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get(Name));
+  auto *D = Lookup.find_first<DeclaratorDecl>();
+  EXPECT_NE(D, nullptr) << Name;
+  if (!D)
+    return {};
+  EXPECT_NE(D->getTypeSourceInfo(), nullptr);
+  if (!D->getTypeSourceInfo())
+    return {};
+  std::string S;
+  {
+    llvm::raw_string_ostream OS(S);
+    D->getTypeSourceInfo()->getTypeLoc().dump(OS, Ctx);
+  }
+  // Split result into lines.
+  std::vector<std::string> Result;
+  auto Remaining = llvm::StringRef(S).trim("\n");
+  while (!Remaining.empty()) {
+    auto [First, Rest] = Remaining.split('\n');
+    Result.push_back(First.str());
+    Remaining = Rest;
+  }
+  return Result;
+}
+
+TEST(ASTDumper, TypeLocChain) {
+  TestAST AST(R"cc(
+    const int **x;
+  )cc");
+  EXPECT_THAT(
+      dumpTypeLoc("x", AST.context()),
+      ElementsAre(""
+                  "PointerTypeLoc <input.mm:2:11, col:16> 'const int **'",
+                  "`-PointerTypeLoc <col:11, col:15> 'const int *'",
+                  "  `-QualifiedTypeLoc <col:11> 'const int'",
+                  "    `-BuiltinTypeLoc <col:11> 'int'"));
+}
+
+TEST(ASTDumper, FunctionTypeLoc) {
+  TestAST AST(R"cc(
+    void x(int, double *y);
+  )cc");
+  EXPECT_THAT(
+      dumpTypeLoc("x", AST.context()),
+      ElementsAre(""
+                  "FunctionProtoTypeLoc <input.mm:2:5, col:26> 'void (int, "
+                  "double *)' cdecl",
+                  StartsWith("|-ParmVarDecl"),
+                  "| `-BuiltinTypeLoc <col:12> 'int'",
+                  StartsWith("|-ParmVarDecl"),
+                  "| `-PointerTypeLoc <col:17, col:24> 'double *'",
+                  "|   `-BuiltinTypeLoc <col:17> 'double'",
+                  "`-BuiltinTypeLoc <col:5> 'void'"));
+}
+
+} // namespace
\ No newline at end of file

>From cc5761a45947c5438d227e9c6e2ba22008bdabad Mon Sep 17 00:00:00 2001
From: Sam McCall <sam.mccall at gmail.com>
Date: Mon, 11 Sep 2023 22:51:21 +0200
Subject: [PATCH 3/3] Address review comments

---
 clang/include/clang/AST/ASTNodeTraverser.h |  5 +-
 clang/lib/AST/JSONNodeDumper.cpp           |  4 +-
 clang/unittests/AST/ASTDumperTest.cpp      | 64 +++++++++++++++++++++-
 3 files changed, 66 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a51b042d8f8bd5e..ed11b92b78791cf 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -532,9 +532,8 @@ class ASTNodeTraverser
     if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
       return;
 
-    if (VisitLocs)
-      if (const auto *TSI = D->getTypeSourceInfo())
-        Visit(TSI->getTypeLoc());
+    if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
+      Visit(TSI->getTypeLoc());
     if (D->hasInit())
       Visit(D->getInit());
   }
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index ed7e79a4fce482d..095c80eaf1f2d71 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -77,7 +77,7 @@ void JSONNodeDumper::Visit(const Type *T) {
     return;
 
   JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str());
-  JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar*/ false));
+  JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar=*/false));
   attributeOnlyIfTrue("containsErrors", T->containsErrors());
   attributeOnlyIfTrue("isDependent", T->isDependentType());
   attributeOnlyIfTrue("isInstantiationDependent",
@@ -106,7 +106,7 @@ void JSONNodeDumper::Visit(TypeLoc TL) {
                  "TypeLoc")
                     .str());
   JOS.attribute("type",
-                createQualType(QualType(TL.getType()), /*Desugar*/ false));
+                createQualType(QualType(TL.getType()), /*Desugar=*/false));
   JOS.attributeObject("range",
                       [TL, this] { writeSourceRange(TL.getSourceRange()); });
 }
diff --git a/clang/unittests/AST/ASTDumperTest.cpp b/clang/unittests/AST/ASTDumperTest.cpp
index d3a421ee46f36ab..f9ca059fc13c9d9 100644
--- a/clang/unittests/AST/ASTDumperTest.cpp
+++ b/clang/unittests/AST/ASTDumperTest.cpp
@@ -30,7 +30,11 @@ using testing::StartsWith;
 
 std::vector<std::string> dumpTypeLoc(llvm::StringRef Name, ASTContext &Ctx) {
   auto Lookup = Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get(Name));
-  auto *D = Lookup.find_first<DeclaratorDecl>();
+  DeclaratorDecl *D = nullptr;
+  if ((D = Lookup.find_first<DeclaratorDecl>()))
+    ;
+  else if (auto *TD = Lookup.find_first<FunctionTemplateDecl>())
+    D = TD->getTemplatedDecl();
   EXPECT_NE(D, nullptr) << Name;
   if (!D)
     return {};
@@ -66,9 +70,34 @@ TEST(ASTDumper, TypeLocChain) {
                   "    `-BuiltinTypeLoc <col:11> 'int'"));
 }
 
+TEST(ASTDumper, AutoType) {
+  TestInputs Inputs(R"cc(
+    template <class, class> concept C = true;
+    C<int> auto str1 = "hello";
+    auto str2 = "hello";
+  )cc");
+  Inputs.ExtraArgs.push_back("-std=c++20");
+  TestAST AST(Inputs);
+  EXPECT_THAT(
+      dumpTypeLoc("str1", AST.context()),
+      ElementsAre(""
+                  "AutoTypeLoc <input.mm:3:5, col:12> 'C<int> auto' undeduced",
+                  StartsWith("|-Concept"), //
+                  "`-TemplateArgument <col:7> type 'int'",
+                  StartsWith("  `-BuiltinType")));
+  EXPECT_THAT(dumpTypeLoc("str2", AST.context()),
+              ElementsAre(""
+                          "AutoTypeLoc <input.mm:4:5> 'auto' undeduced"));
+}
+
+
 TEST(ASTDumper, FunctionTypeLoc) {
   TestAST AST(R"cc(
     void x(int, double *y);
+
+    auto trailing() -> int;
+
+    template <class T> int tmpl(T&&);
   )cc");
   EXPECT_THAT(
       dumpTypeLoc("x", AST.context()),
@@ -81,6 +110,37 @@ TEST(ASTDumper, FunctionTypeLoc) {
                   "| `-PointerTypeLoc <col:17, col:24> 'double *'",
                   "|   `-BuiltinTypeLoc <col:17> 'double'",
                   "`-BuiltinTypeLoc <col:5> 'void'"));
+
+  EXPECT_THAT(dumpTypeLoc("trailing", AST.context()),
+              ElementsAre(""
+                          "FunctionProtoTypeLoc <input.mm:4:5, col:24> "
+                          "'auto () -> int' trailing_return cdecl",
+                          "`-BuiltinTypeLoc <col:24> 'int'"));
+
+  EXPECT_THAT(
+      dumpTypeLoc("tmpl", AST.context()),
+      ElementsAre(""
+                  "FunctionProtoTypeLoc <input.mm:6:24, col:36> "
+                  "'int (T &&)' cdecl",
+                  StartsWith("|-ParmVarDecl"),
+                  "| `-RValueReferenceTypeLoc <col:33, col:34> 'T &&'",
+                  "|   `-TemplateTypeParmTypeLoc <col:33> 'T' depth 0 index 0",
+                  StartsWith("|     `-TemplateTypeParm"),
+                  "`-BuiltinTypeLoc <col:24> 'int'"));
+
+  // Dynamic-exception-spec needs C++14 or earlier.
+  TestInputs Throws(R"cc(
+    void throws() throw(int);
+  )cc");
+  Throws.ExtraArgs.push_back("-std=c++14");
+  AST = TestAST(Throws);
+  EXPECT_THAT(dumpTypeLoc("throws", AST.context()),
+              ElementsAre(""
+                          "FunctionProtoTypeLoc <input.mm:2:5, col:28> "
+                          "'void () throw(int)' exceptionspec_dynamic cdecl",
+                          // FIXME: include TypeLoc for int
+                          "|-Exceptions: 'int'",
+                          "`-BuiltinTypeLoc <col:5> 'void'"));
 }
 
-} // namespace
\ No newline at end of file
+} // namespace



More information about the cfe-commits mailing list