[clang] 8d1b1c9 - [AST] Add dump() method to TypeLoc (#65484)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 31 07:40:33 PST 2024


Author: Sam McCall
Date: 2024-01-31T10:40:29-05:00
New Revision: 8d1b1c9b97de557299e8148a79d756c1e8d9b7eb

URL: https://github.com/llvm/llvm-project/commit/8d1b1c9b97de557299e8148a79d756c1e8d9b7eb
DIFF: https://github.com/llvm/llvm-project/commit/8d1b1c9b97de557299e8148a79d756c1e8d9b7eb.diff

LOG: [AST] Add dump() method to TypeLoc (#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.

---

While here, ConceptReference JSON dumping must be implemented. It's not
totally clear to me why this implementation wasn't required before but
is now...

Added: 
    clang/unittests/AST/ASTDumperTest.cpp

Modified: 
    clang/include/clang/AST/ASTNodeTraverser.h
    clang/include/clang/AST/JSONNodeDumper.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/AST/TypeLoc.h
    clang/lib/AST/ASTDumper.cpp
    clang/lib/AST/ASTTypeTraits.cpp
    clang/lib/AST/JSONNodeDumper.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/unittests/AST/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 950da2b8d2c45..06d67e9cba953 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) {
@@ -421,9 +449,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) {
@@ -468,6 +542,8 @@ class ASTNodeTraverser
     if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
       return;
 
+    if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
+      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 4def5389137fa..dde70dde2fa2b 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 99183918dfde6..3c4283f657efa 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);
@@ -339,6 +343,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 a17b4af13d03f..b1b38f882d19f 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 cc9a84eecaadb..6efc5bb92e28d 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 4c7496c699bef..99916f523aa95 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 3c11b75d7472d..e27d44fc2ffe6 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",
@@ -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 748bfb0127a28..0000d26dd49eb 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);
@@ -1801,11 +1823,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(
@@ -1838,6 +1857,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/ASTDumperTest.cpp b/clang/unittests/AST/ASTDumperTest.cpp
new file mode 100644
index 0000000000000..f9ca059fc13c9
--- /dev/null
+++ b/clang/unittests/AST/ASTDumperTest.cpp
@@ -0,0 +1,146 @@
+//===- 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));
+  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 {};
+  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, 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()),
+      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'"));
+
+  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

diff  --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt
index 584cfb1bd2f11..54765e36db008 100644
--- a/clang/unittests/AST/CMakeLists.txt
+++ b/clang/unittests/AST/CMakeLists.txt
@@ -9,6 +9,7 @@ add_subdirectory(Interp)
 
 add_clang_unittest(ASTTests
   ASTContextParentMapTest.cpp
+  ASTDumperTest.cpp
   ASTExprTest.cpp
   ASTImporterFixtures.cpp
   ASTImporterTest.cpp


        


More information about the cfe-commits mailing list