[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...
This commit is contained in:
parent
c651b2b0d9
commit
8d1b1c9b97
@ -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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
|
||||
return;
|
||||
|
||||
if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
|
||||
Visit(TSI->getTypeLoc());
|
||||
if (D->hasInit())
|
||||
Visit(D->getInit());
|
||||
}
|
||||
|
@ -197,6 +197,7 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
|
@ -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 @@ public:
|
||||
|
||||
void Visit(QualType T);
|
||||
|
||||
void Visit(TypeLoc);
|
||||
|
||||
void Visit(const Decl *D);
|
||||
|
||||
void Visit(const CXXCtorInitializer *Init);
|
||||
@ -339,6 +343,8 @@ public:
|
||||
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);
|
||||
|
@ -229,6 +229,9 @@ public:
|
||||
/// __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;
|
||||
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
146
clang/unittests/AST/ASTDumperTest.cpp
Normal file
146
clang/unittests/AST/ASTDumperTest.cpp
Normal file
@ -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
|
@ -9,6 +9,7 @@ add_subdirectory(Interp)
|
||||
|
||||
add_clang_unittest(ASTTests
|
||||
ASTContextParentMapTest.cpp
|
||||
ASTDumperTest.cpp
|
||||
ASTExprTest.cpp
|
||||
ASTImporterFixtures.cpp
|
||||
ASTImporterTest.cpp
|
||||
|
Loading…
x
Reference in New Issue
Block a user