mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 00:16:05 +00:00

This patch extends the canonicalization printing policy to cover expressions and template names, and wires that up to the template argument printer, covering expressions, and to the expression within a dependent decltype. This is helpful for debugging, or if these expressions somehow end up in diagnostics, as without this patch they can print as completely unrelated expressions, which can be quite confusing. This is because expressions are not uniqued, unlike types, and when a template specialization containing an expression is the first to be canonicalized, the expression ends up appearing in the canonical type of subsequent equivalent specializations. Fixes https://github.com/llvm/llvm-project/issues/92292
1879 lines
67 KiB
C++
1879 lines
67 KiB
C++
#include "clang/AST/JSONNodeDumper.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/Specifiers.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include <optional>
|
|
|
|
using namespace clang;
|
|
|
|
void JSONNodeDumper::addPreviousDeclaration(const Decl *D) {
|
|
switch (D->getKind()) {
|
|
#define DECL(DERIVED, BASE) \
|
|
case Decl::DERIVED: \
|
|
return writePreviousDeclImpl(cast<DERIVED##Decl>(D));
|
|
#define ABSTRACT_DECL(DECL)
|
|
#include "clang/AST/DeclNodes.inc"
|
|
#undef ABSTRACT_DECL
|
|
#undef DECL
|
|
}
|
|
llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const Attr *A) {
|
|
const char *AttrName = nullptr;
|
|
switch (A->getKind()) {
|
|
#define ATTR(X) \
|
|
case attr::X: \
|
|
AttrName = #X"Attr"; \
|
|
break;
|
|
#include "clang/Basic/AttrList.inc"
|
|
#undef ATTR
|
|
}
|
|
JOS.attribute("id", createPointerRepresentation(A));
|
|
JOS.attribute("kind", AttrName);
|
|
JOS.attributeObject("range", [A, this] { writeSourceRange(A->getRange()); });
|
|
attributeOnlyIfTrue("inherited", A->isInherited());
|
|
attributeOnlyIfTrue("implicit", A->isImplicit());
|
|
|
|
// FIXME: it would be useful for us to output the spelling kind as well as
|
|
// the actual spelling. This would allow us to distinguish between the
|
|
// various attribute syntaxes, but we don't currently track that information
|
|
// within the AST.
|
|
//JOS.attribute("spelling", A->getSpelling());
|
|
|
|
InnerAttrVisitor::Visit(A);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const Stmt *S) {
|
|
if (!S)
|
|
return;
|
|
|
|
JOS.attribute("id", createPointerRepresentation(S));
|
|
JOS.attribute("kind", S->getStmtClassName());
|
|
JOS.attributeObject("range",
|
|
[S, this] { writeSourceRange(S->getSourceRange()); });
|
|
|
|
if (const auto *E = dyn_cast<Expr>(S)) {
|
|
JOS.attribute("type", createQualType(E->getType()));
|
|
const char *Category = nullptr;
|
|
switch (E->getValueKind()) {
|
|
case VK_LValue: Category = "lvalue"; break;
|
|
case VK_XValue: Category = "xvalue"; break;
|
|
case VK_PRValue:
|
|
Category = "prvalue";
|
|
break;
|
|
}
|
|
JOS.attribute("valueCategory", Category);
|
|
}
|
|
InnerStmtVisitor::Visit(S);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const Type *T) {
|
|
JOS.attribute("id", createPointerRepresentation(T));
|
|
|
|
if (!T)
|
|
return;
|
|
|
|
JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str());
|
|
JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar=*/false));
|
|
attributeOnlyIfTrue("containsErrors", T->containsErrors());
|
|
attributeOnlyIfTrue("isDependent", T->isDependentType());
|
|
attributeOnlyIfTrue("isInstantiationDependent",
|
|
T->isInstantiationDependentType());
|
|
attributeOnlyIfTrue("isVariablyModified", T->isVariablyModifiedType());
|
|
attributeOnlyIfTrue("containsUnexpandedPack",
|
|
T->containsUnexpandedParameterPack());
|
|
attributeOnlyIfTrue("isImported", T->isFromAST());
|
|
InnerTypeVisitor::Visit(T);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(QualType T) {
|
|
JOS.attribute("id", createPointerRepresentation(T.getAsOpaquePtr()));
|
|
JOS.attribute("kind", "QualType");
|
|
JOS.attribute("type", createQualType(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));
|
|
|
|
if (!D)
|
|
return;
|
|
|
|
JOS.attribute("kind", (llvm::Twine(D->getDeclKindName()) + "Decl").str());
|
|
JOS.attributeObject("loc",
|
|
[D, this] { writeSourceLocation(D->getLocation()); });
|
|
JOS.attributeObject("range",
|
|
[D, this] { writeSourceRange(D->getSourceRange()); });
|
|
attributeOnlyIfTrue("isImplicit", D->isImplicit());
|
|
attributeOnlyIfTrue("isInvalid", D->isInvalidDecl());
|
|
|
|
if (D->isUsed())
|
|
JOS.attribute("isUsed", true);
|
|
else if (D->isThisDeclarationReferenced())
|
|
JOS.attribute("isReferenced", true);
|
|
|
|
if (const auto *ND = dyn_cast<NamedDecl>(D))
|
|
attributeOnlyIfTrue("isHidden", !ND->isUnconditionallyVisible());
|
|
|
|
if (D->getLexicalDeclContext() != D->getDeclContext()) {
|
|
// Because of multiple inheritance, a DeclContext pointer does not produce
|
|
// the same pointer representation as a Decl pointer that references the
|
|
// same AST Node.
|
|
const auto *ParentDeclContextDecl = dyn_cast<Decl>(D->getDeclContext());
|
|
JOS.attribute("parentDeclContextId",
|
|
createPointerRepresentation(ParentDeclContextDecl));
|
|
}
|
|
|
|
addPreviousDeclaration(D);
|
|
InnerDeclVisitor::Visit(D);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const comments::Comment *C,
|
|
const comments::FullComment *FC) {
|
|
if (!C)
|
|
return;
|
|
|
|
JOS.attribute("id", createPointerRepresentation(C));
|
|
JOS.attribute("kind", C->getCommentKindName());
|
|
JOS.attributeObject("loc",
|
|
[C, this] { writeSourceLocation(C->getLocation()); });
|
|
JOS.attributeObject("range",
|
|
[C, this] { writeSourceRange(C->getSourceRange()); });
|
|
|
|
InnerCommentVisitor::visit(C, FC);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const TemplateArgument &TA, SourceRange R,
|
|
const Decl *From, StringRef Label) {
|
|
JOS.attribute("kind", "TemplateArgument");
|
|
if (R.isValid())
|
|
JOS.attributeObject("range", [R, this] { writeSourceRange(R); });
|
|
|
|
if (From)
|
|
JOS.attribute(Label.empty() ? "fromDecl" : Label, createBareDeclRef(From));
|
|
|
|
InnerTemplateArgVisitor::Visit(TA);
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const CXXCtorInitializer *Init) {
|
|
JOS.attribute("kind", "CXXCtorInitializer");
|
|
if (Init->isAnyMemberInitializer())
|
|
JOS.attribute("anyInit", createBareDeclRef(Init->getAnyMember()));
|
|
else if (Init->isBaseInitializer())
|
|
JOS.attribute("baseInit",
|
|
createQualType(QualType(Init->getBaseClass(), 0)));
|
|
else if (Init->isDelegatingInitializer())
|
|
JOS.attribute("delegatingInit",
|
|
createQualType(Init->getTypeSourceInfo()->getType()));
|
|
else
|
|
llvm_unreachable("Unknown initializer type");
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const OpenACCClause *C) {}
|
|
|
|
void JSONNodeDumper::Visit(const OMPClause *C) {}
|
|
|
|
void JSONNodeDumper::Visit(const BlockDecl::Capture &C) {
|
|
JOS.attribute("kind", "Capture");
|
|
attributeOnlyIfTrue("byref", C.isByRef());
|
|
attributeOnlyIfTrue("nested", C.isNested());
|
|
if (C.getVariable())
|
|
JOS.attribute("var", createBareDeclRef(C.getVariable()));
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) {
|
|
JOS.attribute("associationKind", A.getTypeSourceInfo() ? "case" : "default");
|
|
attributeOnlyIfTrue("selected", A.isSelected());
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const concepts::Requirement *R) {
|
|
if (!R)
|
|
return;
|
|
|
|
switch (R->getKind()) {
|
|
case concepts::Requirement::RK_Type:
|
|
JOS.attribute("kind", "TypeRequirement");
|
|
break;
|
|
case concepts::Requirement::RK_Simple:
|
|
JOS.attribute("kind", "SimpleRequirement");
|
|
break;
|
|
case concepts::Requirement::RK_Compound:
|
|
JOS.attribute("kind", "CompoundRequirement");
|
|
break;
|
|
case concepts::Requirement::RK_Nested:
|
|
JOS.attribute("kind", "NestedRequirement");
|
|
break;
|
|
}
|
|
|
|
if (auto *ER = dyn_cast<concepts::ExprRequirement>(R))
|
|
attributeOnlyIfTrue("noexcept", ER->hasNoexceptRequirement());
|
|
|
|
attributeOnlyIfTrue("isDependent", R->isDependent());
|
|
if (!R->isDependent())
|
|
JOS.attribute("satisfied", R->isSatisfied());
|
|
attributeOnlyIfTrue("containsUnexpandedPack",
|
|
R->containsUnexpandedParameterPack());
|
|
}
|
|
|
|
void JSONNodeDumper::Visit(const APValue &Value, QualType Ty) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
Value.printPretty(OS, Ctx, Ty);
|
|
JOS.attribute("value", 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;
|
|
|
|
JOS.attributeBegin("includedFrom");
|
|
JOS.objectBegin();
|
|
|
|
if (!JustFirst) {
|
|
// Walk the stack recursively, then print out the presumed location.
|
|
writeIncludeStack(SM.getPresumedLoc(Loc.getIncludeLoc()));
|
|
}
|
|
|
|
JOS.attribute("file", Loc.getFilename());
|
|
JOS.objectEnd();
|
|
JOS.attributeEnd();
|
|
}
|
|
|
|
void JSONNodeDumper::writeBareSourceLocation(SourceLocation Loc,
|
|
bool IsSpelling) {
|
|
PresumedLoc Presumed = SM.getPresumedLoc(Loc);
|
|
unsigned ActualLine = IsSpelling ? SM.getSpellingLineNumber(Loc)
|
|
: SM.getExpansionLineNumber(Loc);
|
|
StringRef ActualFile = SM.getBufferName(Loc);
|
|
|
|
if (Presumed.isValid()) {
|
|
JOS.attribute("offset", SM.getDecomposedLoc(Loc).second);
|
|
if (LastLocFilename != ActualFile) {
|
|
JOS.attribute("file", ActualFile);
|
|
JOS.attribute("line", ActualLine);
|
|
} else if (LastLocLine != ActualLine)
|
|
JOS.attribute("line", ActualLine);
|
|
|
|
StringRef PresumedFile = Presumed.getFilename();
|
|
if (PresumedFile != ActualFile && LastLocPresumedFilename != PresumedFile)
|
|
JOS.attribute("presumedFile", PresumedFile);
|
|
|
|
unsigned PresumedLine = Presumed.getLine();
|
|
if (ActualLine != PresumedLine && LastLocPresumedLine != PresumedLine)
|
|
JOS.attribute("presumedLine", PresumedLine);
|
|
|
|
JOS.attribute("col", Presumed.getColumn());
|
|
JOS.attribute("tokLen",
|
|
Lexer::MeasureTokenLength(Loc, SM, Ctx.getLangOpts()));
|
|
LastLocFilename = ActualFile;
|
|
LastLocPresumedFilename = PresumedFile;
|
|
LastLocPresumedLine = PresumedLine;
|
|
LastLocLine = ActualLine;
|
|
|
|
// Orthogonal to the file, line, and column de-duplication is whether the
|
|
// given location was a result of an include. If so, print where the
|
|
// include location came from.
|
|
writeIncludeStack(SM.getPresumedLoc(Presumed.getIncludeLoc()),
|
|
/*JustFirst*/ true);
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::writeSourceLocation(SourceLocation Loc) {
|
|
SourceLocation Spelling = SM.getSpellingLoc(Loc);
|
|
SourceLocation Expansion = SM.getExpansionLoc(Loc);
|
|
|
|
if (Expansion != Spelling) {
|
|
// If the expansion and the spelling are different, output subobjects
|
|
// describing both locations.
|
|
JOS.attributeObject("spellingLoc", [Spelling, this] {
|
|
writeBareSourceLocation(Spelling, /*IsSpelling*/ true);
|
|
});
|
|
JOS.attributeObject("expansionLoc", [Expansion, Loc, this] {
|
|
writeBareSourceLocation(Expansion, /*IsSpelling*/ false);
|
|
// If there is a macro expansion, add extra information if the interesting
|
|
// bit is the macro arg expansion.
|
|
if (SM.isMacroArgExpansion(Loc))
|
|
JOS.attribute("isMacroArgExpansion", true);
|
|
});
|
|
} else
|
|
writeBareSourceLocation(Spelling, /*IsSpelling*/ true);
|
|
}
|
|
|
|
void JSONNodeDumper::writeSourceRange(SourceRange R) {
|
|
JOS.attributeObject("begin",
|
|
[R, this] { writeSourceLocation(R.getBegin()); });
|
|
JOS.attributeObject("end", [R, this] { writeSourceLocation(R.getEnd()); });
|
|
}
|
|
|
|
std::string JSONNodeDumper::createPointerRepresentation(const void *Ptr) {
|
|
// Because JSON stores integer values as signed 64-bit integers, trying to
|
|
// represent them as such makes for very ugly pointer values in the resulting
|
|
// output. Instead, we convert the value to hex and treat it as a string.
|
|
return "0x" + llvm::utohexstr(reinterpret_cast<uint64_t>(Ptr), true);
|
|
}
|
|
|
|
llvm::json::Object JSONNodeDumper::createQualType(QualType QT, bool Desugar) {
|
|
SplitQualType SQT = QT.split();
|
|
std::string SQTS = QualType::getAsString(SQT, PrintPolicy);
|
|
llvm::json::Object Ret{{"qualType", SQTS}};
|
|
|
|
if (Desugar && !QT.isNull()) {
|
|
SplitQualType DSQT = QT.getSplitDesugaredType();
|
|
if (DSQT != SQT) {
|
|
std::string DSQTS = QualType::getAsString(DSQT, PrintPolicy);
|
|
if (DSQTS != SQTS)
|
|
Ret["desugaredQualType"] = DSQTS;
|
|
}
|
|
if (const auto *TT = QT->getAs<TypedefType>())
|
|
Ret["typeAliasDeclId"] = createPointerRepresentation(TT->getDecl());
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
void JSONNodeDumper::writeBareDeclRef(const Decl *D) {
|
|
JOS.attribute("id", createPointerRepresentation(D));
|
|
if (!D)
|
|
return;
|
|
|
|
JOS.attribute("kind", (llvm::Twine(D->getDeclKindName()) + "Decl").str());
|
|
if (const auto *ND = dyn_cast<NamedDecl>(D))
|
|
JOS.attribute("name", ND->getDeclName().getAsString());
|
|
if (const auto *VD = dyn_cast<ValueDecl>(D))
|
|
JOS.attribute("type", createQualType(VD->getType()));
|
|
}
|
|
|
|
llvm::json::Object JSONNodeDumper::createBareDeclRef(const Decl *D) {
|
|
llvm::json::Object Ret{{"id", createPointerRepresentation(D)}};
|
|
if (!D)
|
|
return Ret;
|
|
|
|
Ret["kind"] = (llvm::Twine(D->getDeclKindName()) + "Decl").str();
|
|
if (const auto *ND = dyn_cast<NamedDecl>(D))
|
|
Ret["name"] = ND->getDeclName().getAsString();
|
|
if (const auto *VD = dyn_cast<ValueDecl>(D))
|
|
Ret["type"] = createQualType(VD->getType());
|
|
return Ret;
|
|
}
|
|
|
|
llvm::json::Array JSONNodeDumper::createCastPath(const CastExpr *C) {
|
|
llvm::json::Array Ret;
|
|
if (C->path_empty())
|
|
return Ret;
|
|
|
|
for (auto I = C->path_begin(), E = C->path_end(); I != E; ++I) {
|
|
const CXXBaseSpecifier *Base = *I;
|
|
const auto *RD =
|
|
cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());
|
|
|
|
llvm::json::Object Val{{"name", RD->getName()}};
|
|
if (Base->isVirtual())
|
|
Val["isVirtual"] = true;
|
|
Ret.push_back(std::move(Val));
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
#define FIELD2(Name, Flag) if (RD->Flag()) Ret[Name] = true
|
|
#define FIELD1(Flag) FIELD2(#Flag, Flag)
|
|
|
|
static llvm::json::Object
|
|
createDefaultConstructorDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("exists", hasDefaultConstructor);
|
|
FIELD2("trivial", hasTrivialDefaultConstructor);
|
|
FIELD2("nonTrivial", hasNonTrivialDefaultConstructor);
|
|
FIELD2("userProvided", hasUserProvidedDefaultConstructor);
|
|
FIELD2("isConstexpr", hasConstexprDefaultConstructor);
|
|
FIELD2("needsImplicit", needsImplicitDefaultConstructor);
|
|
FIELD2("defaultedIsConstexpr", defaultedDefaultConstructorIsConstexpr);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static llvm::json::Object
|
|
createCopyConstructorDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("simple", hasSimpleCopyConstructor);
|
|
FIELD2("trivial", hasTrivialCopyConstructor);
|
|
FIELD2("nonTrivial", hasNonTrivialCopyConstructor);
|
|
FIELD2("userDeclared", hasUserDeclaredCopyConstructor);
|
|
FIELD2("hasConstParam", hasCopyConstructorWithConstParam);
|
|
FIELD2("implicitHasConstParam", implicitCopyConstructorHasConstParam);
|
|
FIELD2("needsImplicit", needsImplicitCopyConstructor);
|
|
FIELD2("needsOverloadResolution", needsOverloadResolutionForCopyConstructor);
|
|
if (!RD->needsOverloadResolutionForCopyConstructor())
|
|
FIELD2("defaultedIsDeleted", defaultedCopyConstructorIsDeleted);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static llvm::json::Object
|
|
createMoveConstructorDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("exists", hasMoveConstructor);
|
|
FIELD2("simple", hasSimpleMoveConstructor);
|
|
FIELD2("trivial", hasTrivialMoveConstructor);
|
|
FIELD2("nonTrivial", hasNonTrivialMoveConstructor);
|
|
FIELD2("userDeclared", hasUserDeclaredMoveConstructor);
|
|
FIELD2("needsImplicit", needsImplicitMoveConstructor);
|
|
FIELD2("needsOverloadResolution", needsOverloadResolutionForMoveConstructor);
|
|
if (!RD->needsOverloadResolutionForMoveConstructor())
|
|
FIELD2("defaultedIsDeleted", defaultedMoveConstructorIsDeleted);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static llvm::json::Object
|
|
createCopyAssignmentDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("simple", hasSimpleCopyAssignment);
|
|
FIELD2("trivial", hasTrivialCopyAssignment);
|
|
FIELD2("nonTrivial", hasNonTrivialCopyAssignment);
|
|
FIELD2("hasConstParam", hasCopyAssignmentWithConstParam);
|
|
FIELD2("implicitHasConstParam", implicitCopyAssignmentHasConstParam);
|
|
FIELD2("userDeclared", hasUserDeclaredCopyAssignment);
|
|
FIELD2("needsImplicit", needsImplicitCopyAssignment);
|
|
FIELD2("needsOverloadResolution", needsOverloadResolutionForCopyAssignment);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static llvm::json::Object
|
|
createMoveAssignmentDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("exists", hasMoveAssignment);
|
|
FIELD2("simple", hasSimpleMoveAssignment);
|
|
FIELD2("trivial", hasTrivialMoveAssignment);
|
|
FIELD2("nonTrivial", hasNonTrivialMoveAssignment);
|
|
FIELD2("userDeclared", hasUserDeclaredMoveAssignment);
|
|
FIELD2("needsImplicit", needsImplicitMoveAssignment);
|
|
FIELD2("needsOverloadResolution", needsOverloadResolutionForMoveAssignment);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static llvm::json::Object
|
|
createDestructorDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
FIELD2("simple", hasSimpleDestructor);
|
|
FIELD2("irrelevant", hasIrrelevantDestructor);
|
|
FIELD2("trivial", hasTrivialDestructor);
|
|
FIELD2("nonTrivial", hasNonTrivialDestructor);
|
|
FIELD2("userDeclared", hasUserDeclaredDestructor);
|
|
FIELD2("needsImplicit", needsImplicitDestructor);
|
|
FIELD2("needsOverloadResolution", needsOverloadResolutionForDestructor);
|
|
if (!RD->needsOverloadResolutionForDestructor())
|
|
FIELD2("defaultedIsDeleted", defaultedDestructorIsDeleted);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
llvm::json::Object
|
|
JSONNodeDumper::createCXXRecordDefinitionData(const CXXRecordDecl *RD) {
|
|
llvm::json::Object Ret;
|
|
|
|
// This data is common to all C++ classes.
|
|
FIELD1(isGenericLambda);
|
|
FIELD1(isLambda);
|
|
FIELD1(isEmpty);
|
|
FIELD1(isAggregate);
|
|
FIELD1(isStandardLayout);
|
|
FIELD1(isTriviallyCopyable);
|
|
FIELD1(isPOD);
|
|
FIELD1(isTrivial);
|
|
FIELD1(isPolymorphic);
|
|
FIELD1(isAbstract);
|
|
FIELD1(isLiteral);
|
|
FIELD1(canPassInRegisters);
|
|
FIELD1(hasUserDeclaredConstructor);
|
|
FIELD1(hasConstexprNonCopyMoveConstructor);
|
|
FIELD1(hasMutableFields);
|
|
FIELD1(hasVariantMembers);
|
|
FIELD2("canConstDefaultInit", allowConstDefaultInit);
|
|
|
|
Ret["defaultCtor"] = createDefaultConstructorDefinitionData(RD);
|
|
Ret["copyCtor"] = createCopyConstructorDefinitionData(RD);
|
|
Ret["moveCtor"] = createMoveConstructorDefinitionData(RD);
|
|
Ret["copyAssign"] = createCopyAssignmentDefinitionData(RD);
|
|
Ret["moveAssign"] = createMoveAssignmentDefinitionData(RD);
|
|
Ret["dtor"] = createDestructorDefinitionData(RD);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
#undef FIELD1
|
|
#undef FIELD2
|
|
|
|
std::string JSONNodeDumper::createAccessSpecifier(AccessSpecifier AS) {
|
|
const auto AccessSpelling = getAccessSpelling(AS);
|
|
if (AccessSpelling.empty())
|
|
return "none";
|
|
return AccessSpelling.str();
|
|
}
|
|
|
|
llvm::json::Object
|
|
JSONNodeDumper::createCXXBaseSpecifier(const CXXBaseSpecifier &BS) {
|
|
llvm::json::Object Ret;
|
|
|
|
Ret["type"] = createQualType(BS.getType());
|
|
Ret["access"] = createAccessSpecifier(BS.getAccessSpecifier());
|
|
Ret["writtenAccess"] =
|
|
createAccessSpecifier(BS.getAccessSpecifierAsWritten());
|
|
if (BS.isVirtual())
|
|
Ret["isVirtual"] = true;
|
|
if (BS.isPackExpansion())
|
|
Ret["isPackExpansion"] = true;
|
|
|
|
return Ret;
|
|
}
|
|
|
|
void JSONNodeDumper::VisitAliasAttr(const AliasAttr *AA) {
|
|
JOS.attribute("aliasee", AA->getAliasee());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCleanupAttr(const CleanupAttr *CA) {
|
|
JOS.attribute("cleanup_function", createBareDeclRef(CA->getFunctionDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitDeprecatedAttr(const DeprecatedAttr *DA) {
|
|
if (!DA->getMessage().empty())
|
|
JOS.attribute("message", DA->getMessage());
|
|
if (!DA->getReplacement().empty())
|
|
JOS.attribute("replacement", DA->getReplacement());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnavailableAttr(const UnavailableAttr *UA) {
|
|
if (!UA->getMessage().empty())
|
|
JOS.attribute("message", UA->getMessage());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSectionAttr(const SectionAttr *SA) {
|
|
JOS.attribute("section_name", SA->getName());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitVisibilityAttr(const VisibilityAttr *VA) {
|
|
JOS.attribute("visibility", VisibilityAttr::ConvertVisibilityTypeToStr(
|
|
VA->getVisibility()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTLSModelAttr(const TLSModelAttr *TA) {
|
|
JOS.attribute("tls_model", TA->getModel());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTypedefType(const TypedefType *TT) {
|
|
JOS.attribute("decl", createBareDeclRef(TT->getDecl()));
|
|
if (!TT->typeMatchesDecl())
|
|
JOS.attribute("type", createQualType(TT->desugar()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUsingType(const UsingType *TT) {
|
|
JOS.attribute("decl", createBareDeclRef(TT->getFoundDecl()));
|
|
if (!TT->typeMatchesDecl())
|
|
JOS.attribute("type", createQualType(TT->desugar()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitFunctionType(const FunctionType *T) {
|
|
FunctionType::ExtInfo E = T->getExtInfo();
|
|
attributeOnlyIfTrue("noreturn", E.getNoReturn());
|
|
attributeOnlyIfTrue("producesResult", E.getProducesResult());
|
|
if (E.getHasRegParm())
|
|
JOS.attribute("regParm", E.getRegParm());
|
|
JOS.attribute("cc", FunctionType::getNameForCallConv(E.getCC()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
|
|
FunctionProtoType::ExtProtoInfo E = T->getExtProtoInfo();
|
|
attributeOnlyIfTrue("trailingReturn", E.HasTrailingReturn);
|
|
attributeOnlyIfTrue("const", T->isConst());
|
|
attributeOnlyIfTrue("volatile", T->isVolatile());
|
|
attributeOnlyIfTrue("restrict", T->isRestrict());
|
|
attributeOnlyIfTrue("variadic", E.Variadic);
|
|
switch (E.RefQualifier) {
|
|
case RQ_LValue: JOS.attribute("refQualifier", "&"); break;
|
|
case RQ_RValue: JOS.attribute("refQualifier", "&&"); break;
|
|
case RQ_None: break;
|
|
}
|
|
switch (E.ExceptionSpec.Type) {
|
|
case EST_DynamicNone:
|
|
case EST_Dynamic: {
|
|
JOS.attribute("exceptionSpec", "throw");
|
|
llvm::json::Array Types;
|
|
for (QualType QT : E.ExceptionSpec.Exceptions)
|
|
Types.push_back(createQualType(QT));
|
|
JOS.attribute("exceptionTypes", std::move(Types));
|
|
} break;
|
|
case EST_MSAny:
|
|
JOS.attribute("exceptionSpec", "throw");
|
|
JOS.attribute("throwsAny", true);
|
|
break;
|
|
case EST_BasicNoexcept:
|
|
JOS.attribute("exceptionSpec", "noexcept");
|
|
break;
|
|
case EST_NoexceptTrue:
|
|
case EST_NoexceptFalse:
|
|
JOS.attribute("exceptionSpec", "noexcept");
|
|
JOS.attribute("conditionEvaluatesTo",
|
|
E.ExceptionSpec.Type == EST_NoexceptTrue);
|
|
//JOS.attributeWithCall("exceptionSpecExpr",
|
|
// [this, E]() { Visit(E.ExceptionSpec.NoexceptExpr); });
|
|
break;
|
|
case EST_NoThrow:
|
|
JOS.attribute("exceptionSpec", "nothrow");
|
|
break;
|
|
// FIXME: I cannot find a way to trigger these cases while dumping the AST. I
|
|
// suspect you can only run into them when executing an AST dump from within
|
|
// the debugger, which is not a use case we worry about for the JSON dumping
|
|
// feature.
|
|
case EST_DependentNoexcept:
|
|
case EST_Unevaluated:
|
|
case EST_Uninstantiated:
|
|
case EST_Unparsed:
|
|
case EST_None: break;
|
|
}
|
|
VisitFunctionType(T);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitRValueReferenceType(const ReferenceType *RT) {
|
|
attributeOnlyIfTrue("spelledAsLValue", RT->isSpelledAsLValue());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitArrayType(const ArrayType *AT) {
|
|
switch (AT->getSizeModifier()) {
|
|
case ArraySizeModifier::Star:
|
|
JOS.attribute("sizeModifier", "*");
|
|
break;
|
|
case ArraySizeModifier::Static:
|
|
JOS.attribute("sizeModifier", "static");
|
|
break;
|
|
case ArraySizeModifier::Normal:
|
|
break;
|
|
}
|
|
|
|
std::string Str = AT->getIndexTypeQualifiers().getAsString();
|
|
if (!Str.empty())
|
|
JOS.attribute("indexTypeQualifiers", Str);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitConstantArrayType(const ConstantArrayType *CAT) {
|
|
// FIXME: this should use ZExt instead of SExt, but JSON doesn't allow a
|
|
// narrowing conversion to int64_t so it cannot be expressed.
|
|
JOS.attribute("size", CAT->getSExtSize());
|
|
VisitArrayType(CAT);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitDependentSizedExtVectorType(
|
|
const DependentSizedExtVectorType *VT) {
|
|
JOS.attributeObject(
|
|
"attrLoc", [VT, this] { writeSourceLocation(VT->getAttributeLoc()); });
|
|
}
|
|
|
|
void JSONNodeDumper::VisitVectorType(const VectorType *VT) {
|
|
JOS.attribute("numElements", VT->getNumElements());
|
|
switch (VT->getVectorKind()) {
|
|
case VectorKind::Generic:
|
|
break;
|
|
case VectorKind::AltiVecVector:
|
|
JOS.attribute("vectorKind", "altivec");
|
|
break;
|
|
case VectorKind::AltiVecPixel:
|
|
JOS.attribute("vectorKind", "altivec pixel");
|
|
break;
|
|
case VectorKind::AltiVecBool:
|
|
JOS.attribute("vectorKind", "altivec bool");
|
|
break;
|
|
case VectorKind::Neon:
|
|
JOS.attribute("vectorKind", "neon");
|
|
break;
|
|
case VectorKind::NeonPoly:
|
|
JOS.attribute("vectorKind", "neon poly");
|
|
break;
|
|
case VectorKind::SveFixedLengthData:
|
|
JOS.attribute("vectorKind", "fixed-length sve data vector");
|
|
break;
|
|
case VectorKind::SveFixedLengthPredicate:
|
|
JOS.attribute("vectorKind", "fixed-length sve predicate vector");
|
|
break;
|
|
case VectorKind::RVVFixedLengthData:
|
|
JOS.attribute("vectorKind", "fixed-length rvv data vector");
|
|
break;
|
|
case VectorKind::RVVFixedLengthMask:
|
|
case VectorKind::RVVFixedLengthMask_1:
|
|
case VectorKind::RVVFixedLengthMask_2:
|
|
case VectorKind::RVVFixedLengthMask_4:
|
|
JOS.attribute("vectorKind", "fixed-length rvv mask vector");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *UUT) {
|
|
JOS.attribute("decl", createBareDeclRef(UUT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnaryTransformType(const UnaryTransformType *UTT) {
|
|
switch (UTT->getUTTKind()) {
|
|
#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \
|
|
case UnaryTransformType::Enum: \
|
|
JOS.attribute("transformKind", #Trait); \
|
|
break;
|
|
#include "clang/Basic/TransformTypeTraits.def"
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTagType(const TagType *TT) {
|
|
JOS.attribute("decl", createBareDeclRef(TT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTemplateTypeParmType(
|
|
const TemplateTypeParmType *TTPT) {
|
|
JOS.attribute("depth", TTPT->getDepth());
|
|
JOS.attribute("index", TTPT->getIndex());
|
|
attributeOnlyIfTrue("isPack", TTPT->isParameterPack());
|
|
JOS.attribute("decl", createBareDeclRef(TTPT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSubstTemplateTypeParmType(
|
|
const SubstTemplateTypeParmType *STTPT) {
|
|
JOS.attribute("index", STTPT->getIndex());
|
|
if (auto PackIndex = STTPT->getPackIndex())
|
|
JOS.attribute("pack_index", *PackIndex);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSubstTemplateTypeParmPackType(
|
|
const SubstTemplateTypeParmPackType *T) {
|
|
JOS.attribute("index", T->getIndex());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitAutoType(const AutoType *AT) {
|
|
JOS.attribute("undeduced", !AT->isDeduced());
|
|
switch (AT->getKeyword()) {
|
|
case AutoTypeKeyword::Auto:
|
|
JOS.attribute("typeKeyword", "auto");
|
|
break;
|
|
case AutoTypeKeyword::DecltypeAuto:
|
|
JOS.attribute("typeKeyword", "decltype(auto)");
|
|
break;
|
|
case AutoTypeKeyword::GNUAutoType:
|
|
JOS.attribute("typeKeyword", "__auto_type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTemplateSpecializationType(
|
|
const TemplateSpecializationType *TST) {
|
|
attributeOnlyIfTrue("isAlias", TST->isTypeAlias());
|
|
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
TST->getTemplateName().print(OS, PrintPolicy);
|
|
JOS.attribute("templateName", Str);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitInjectedClassNameType(
|
|
const InjectedClassNameType *ICNT) {
|
|
JOS.attribute("decl", createBareDeclRef(ICNT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCInterfaceType(const ObjCInterfaceType *OIT) {
|
|
JOS.attribute("decl", createBareDeclRef(OIT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitPackExpansionType(const PackExpansionType *PET) {
|
|
if (UnsignedOrNone N = PET->getNumExpansions())
|
|
JOS.attribute("numExpansions", *N);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitElaboratedType(const ElaboratedType *ET) {
|
|
if (const NestedNameSpecifier *NNS = ET->getQualifier()) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
NNS->print(OS, PrintPolicy, /*ResolveTemplateArgs*/ true);
|
|
JOS.attribute("qualifier", Str);
|
|
}
|
|
if (const TagDecl *TD = ET->getOwnedTagDecl())
|
|
JOS.attribute("ownedTagDecl", createBareDeclRef(TD));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitMacroQualifiedType(const MacroQualifiedType *MQT) {
|
|
JOS.attribute("macroName", MQT->getMacroIdentifier()->getName());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitMemberPointerType(const MemberPointerType *MPT) {
|
|
attributeOnlyIfTrue("isData", MPT->isMemberDataPointer());
|
|
attributeOnlyIfTrue("isFunction", MPT->isMemberFunctionPointer());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitNamedDecl(const NamedDecl *ND) {
|
|
if (ND && ND->getDeclName()) {
|
|
JOS.attribute("name", ND->getNameAsString());
|
|
// FIXME: There are likely other contexts in which it makes no sense to ask
|
|
// for a mangled name.
|
|
if (isa<RequiresExprBodyDecl>(ND->getDeclContext()))
|
|
return;
|
|
|
|
// If the declaration is dependent or is in a dependent context, then the
|
|
// mangling is unlikely to be meaningful (and in some cases may cause
|
|
// "don't know how to mangle this" assertion failures.
|
|
if (ND->isTemplated())
|
|
return;
|
|
|
|
// Mangled names are not meaningful for locals, and may not be well-defined
|
|
// in the case of VLAs.
|
|
auto *VD = dyn_cast<VarDecl>(ND);
|
|
if (VD && VD->hasLocalStorage())
|
|
return;
|
|
|
|
// Do not mangle template deduction guides.
|
|
if (isa<CXXDeductionGuideDecl>(ND))
|
|
return;
|
|
|
|
std::string MangledName = ASTNameGen.getName(ND);
|
|
if (!MangledName.empty())
|
|
JOS.attribute("mangledName", MangledName);
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTypedefDecl(const TypedefDecl *TD) {
|
|
VisitNamedDecl(TD);
|
|
JOS.attribute("type", createQualType(TD->getUnderlyingType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTypeAliasDecl(const TypeAliasDecl *TAD) {
|
|
VisitNamedDecl(TAD);
|
|
JOS.attribute("type", createQualType(TAD->getUnderlyingType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitNamespaceDecl(const NamespaceDecl *ND) {
|
|
VisitNamedDecl(ND);
|
|
attributeOnlyIfTrue("isInline", ND->isInline());
|
|
attributeOnlyIfTrue("isNested", ND->isNested());
|
|
if (!ND->isFirstDecl())
|
|
JOS.attribute("originalNamespace", createBareDeclRef(ND->getFirstDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUsingDirectiveDecl(const UsingDirectiveDecl *UDD) {
|
|
JOS.attribute("nominatedNamespace",
|
|
createBareDeclRef(UDD->getNominatedNamespace()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitNamespaceAliasDecl(const NamespaceAliasDecl *NAD) {
|
|
VisitNamedDecl(NAD);
|
|
JOS.attribute("aliasedNamespace",
|
|
createBareDeclRef(NAD->getAliasedNamespace()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUsingDecl(const UsingDecl *UD) {
|
|
std::string Name;
|
|
if (const NestedNameSpecifier *NNS = UD->getQualifier()) {
|
|
llvm::raw_string_ostream SOS(Name);
|
|
NNS->print(SOS, UD->getASTContext().getPrintingPolicy());
|
|
}
|
|
Name += UD->getNameAsString();
|
|
JOS.attribute("name", Name);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *UED) {
|
|
JOS.attribute("target", createBareDeclRef(UED->getEnumDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *USD) {
|
|
JOS.attribute("target", createBareDeclRef(USD->getTargetDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) {
|
|
VisitNamedDecl(VD);
|
|
JOS.attribute("type", createQualType(VD->getType()));
|
|
if (const auto *P = dyn_cast<ParmVarDecl>(VD))
|
|
attributeOnlyIfTrue("explicitObjectParameter",
|
|
P->isExplicitObjectParameter());
|
|
|
|
StorageClass SC = VD->getStorageClass();
|
|
if (SC != SC_None)
|
|
JOS.attribute("storageClass", VarDecl::getStorageClassSpecifierString(SC));
|
|
switch (VD->getTLSKind()) {
|
|
case VarDecl::TLS_Dynamic: JOS.attribute("tls", "dynamic"); break;
|
|
case VarDecl::TLS_Static: JOS.attribute("tls", "static"); break;
|
|
case VarDecl::TLS_None: break;
|
|
}
|
|
attributeOnlyIfTrue("nrvo", VD->isNRVOVariable());
|
|
attributeOnlyIfTrue("inline", VD->isInline());
|
|
attributeOnlyIfTrue("constexpr", VD->isConstexpr());
|
|
attributeOnlyIfTrue("modulePrivate", VD->isModulePrivate());
|
|
if (VD->hasInit()) {
|
|
switch (VD->getInitStyle()) {
|
|
case VarDecl::CInit: JOS.attribute("init", "c"); break;
|
|
case VarDecl::CallInit: JOS.attribute("init", "call"); break;
|
|
case VarDecl::ListInit: JOS.attribute("init", "list"); break;
|
|
case VarDecl::ParenListInit:
|
|
JOS.attribute("init", "paren-list");
|
|
break;
|
|
}
|
|
}
|
|
attributeOnlyIfTrue("isParameterPack", VD->isParameterPack());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitFieldDecl(const FieldDecl *FD) {
|
|
VisitNamedDecl(FD);
|
|
JOS.attribute("type", createQualType(FD->getType()));
|
|
attributeOnlyIfTrue("mutable", FD->isMutable());
|
|
attributeOnlyIfTrue("modulePrivate", FD->isModulePrivate());
|
|
attributeOnlyIfTrue("isBitfield", FD->isBitField());
|
|
attributeOnlyIfTrue("hasInClassInitializer", FD->hasInClassInitializer());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitFunctionDecl(const FunctionDecl *FD) {
|
|
VisitNamedDecl(FD);
|
|
JOS.attribute("type", createQualType(FD->getType()));
|
|
StorageClass SC = FD->getStorageClass();
|
|
if (SC != SC_None)
|
|
JOS.attribute("storageClass", VarDecl::getStorageClassSpecifierString(SC));
|
|
attributeOnlyIfTrue("inline", FD->isInlineSpecified());
|
|
attributeOnlyIfTrue("virtual", FD->isVirtualAsWritten());
|
|
attributeOnlyIfTrue("pure", FD->isPureVirtual());
|
|
attributeOnlyIfTrue("explicitlyDeleted", FD->isDeletedAsWritten());
|
|
attributeOnlyIfTrue("constexpr", FD->isConstexpr());
|
|
attributeOnlyIfTrue("variadic", FD->isVariadic());
|
|
attributeOnlyIfTrue("immediate", FD->isImmediateFunction());
|
|
|
|
if (FD->isDefaulted())
|
|
JOS.attribute("explicitlyDefaulted",
|
|
FD->isDeleted() ? "deleted" : "default");
|
|
|
|
if (StringLiteral *Msg = FD->getDeletedMessage())
|
|
JOS.attribute("deletedMessage", Msg->getString());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitEnumDecl(const EnumDecl *ED) {
|
|
VisitNamedDecl(ED);
|
|
if (ED->isFixed())
|
|
JOS.attribute("fixedUnderlyingType", createQualType(ED->getIntegerType()));
|
|
if (ED->isScoped())
|
|
JOS.attribute("scopedEnumTag",
|
|
ED->isScopedUsingClassTag() ? "class" : "struct");
|
|
}
|
|
void JSONNodeDumper::VisitEnumConstantDecl(const EnumConstantDecl *ECD) {
|
|
VisitNamedDecl(ECD);
|
|
JOS.attribute("type", createQualType(ECD->getType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitRecordDecl(const RecordDecl *RD) {
|
|
VisitNamedDecl(RD);
|
|
JOS.attribute("tagUsed", RD->getKindName());
|
|
attributeOnlyIfTrue("completeDefinition", RD->isCompleteDefinition());
|
|
}
|
|
void JSONNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *RD) {
|
|
VisitRecordDecl(RD);
|
|
|
|
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
|
|
if (CTSD->hasStrictPackMatch())
|
|
JOS.attribute("strict-pack-match", true);
|
|
}
|
|
|
|
// All other information requires a complete definition.
|
|
if (!RD->isCompleteDefinition())
|
|
return;
|
|
|
|
JOS.attribute("definitionData", createCXXRecordDefinitionData(RD));
|
|
if (RD->getNumBases()) {
|
|
JOS.attributeArray("bases", [this, RD] {
|
|
for (const auto &Spec : RD->bases())
|
|
JOS.value(createCXXBaseSpecifier(Spec));
|
|
});
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("bufferKind", D->isCBuffer() ? "cbuffer" : "tbuffer");
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("tagUsed", D->wasDeclaredWithTypename() ? "typename" : "class");
|
|
JOS.attribute("depth", D->getDepth());
|
|
JOS.attribute("index", D->getIndex());
|
|
attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
|
|
|
|
if (D->hasDefaultArgument())
|
|
JOS.attributeObject("defaultArg", [=] {
|
|
Visit(D->getDefaultArgument().getArgument(), SourceRange(),
|
|
D->getDefaultArgStorage().getInheritedFrom(),
|
|
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
|
|
});
|
|
}
|
|
|
|
void JSONNodeDumper::VisitNonTypeTemplateParmDecl(
|
|
const NonTypeTemplateParmDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("type", createQualType(D->getType()));
|
|
JOS.attribute("depth", D->getDepth());
|
|
JOS.attribute("index", D->getIndex());
|
|
attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
|
|
|
|
if (D->hasDefaultArgument())
|
|
JOS.attributeObject("defaultArg", [=] {
|
|
Visit(D->getDefaultArgument().getArgument(), SourceRange(),
|
|
D->getDefaultArgStorage().getInheritedFrom(),
|
|
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
|
|
});
|
|
}
|
|
|
|
void JSONNodeDumper::VisitTemplateTemplateParmDecl(
|
|
const TemplateTemplateParmDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("depth", D->getDepth());
|
|
JOS.attribute("index", D->getIndex());
|
|
attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
|
|
|
|
if (D->hasDefaultArgument())
|
|
JOS.attributeObject("defaultArg", [=] {
|
|
const auto *InheritedFrom = D->getDefaultArgStorage().getInheritedFrom();
|
|
Visit(D->getDefaultArgument().getArgument(),
|
|
InheritedFrom ? InheritedFrom->getSourceRange() : SourceLocation{},
|
|
InheritedFrom,
|
|
D->defaultArgumentWasInherited() ? "inherited from" : "previous");
|
|
});
|
|
}
|
|
|
|
void JSONNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *LSD) {
|
|
StringRef Lang;
|
|
switch (LSD->getLanguage()) {
|
|
case LinkageSpecLanguageIDs::C:
|
|
Lang = "C";
|
|
break;
|
|
case LinkageSpecLanguageIDs::CXX:
|
|
Lang = "C++";
|
|
break;
|
|
}
|
|
JOS.attribute("language", Lang);
|
|
attributeOnlyIfTrue("hasBraces", LSD->hasBraces());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) {
|
|
JOS.attribute("access", createAccessSpecifier(ASD->getAccess()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
|
|
if (const TypeSourceInfo *T = FD->getFriendType())
|
|
JOS.attribute("type", createQualType(T->getType()));
|
|
attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("type", createQualType(D->getType()));
|
|
attributeOnlyIfTrue("synthesized", D->getSynthesize());
|
|
switch (D->getAccessControl()) {
|
|
case ObjCIvarDecl::None: JOS.attribute("access", "none"); break;
|
|
case ObjCIvarDecl::Private: JOS.attribute("access", "private"); break;
|
|
case ObjCIvarDecl::Protected: JOS.attribute("access", "protected"); break;
|
|
case ObjCIvarDecl::Public: JOS.attribute("access", "public"); break;
|
|
case ObjCIvarDecl::Package: JOS.attribute("access", "package"); break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCMethodDecl(const ObjCMethodDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("returnType", createQualType(D->getReturnType()));
|
|
JOS.attribute("instance", D->isInstanceMethod());
|
|
attributeOnlyIfTrue("variadic", D->isVariadic());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("type", createQualType(D->getUnderlyingType()));
|
|
attributeOnlyIfTrue("bounded", D->hasExplicitBound());
|
|
switch (D->getVariance()) {
|
|
case ObjCTypeParamVariance::Invariant:
|
|
break;
|
|
case ObjCTypeParamVariance::Covariant:
|
|
JOS.attribute("variance", "covariant");
|
|
break;
|
|
case ObjCTypeParamVariance::Contravariant:
|
|
JOS.attribute("variance", "contravariant");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
|
|
JOS.attribute("implementation", createBareDeclRef(D->getImplementation()));
|
|
|
|
llvm::json::Array Protocols;
|
|
for (const auto* P : D->protocols())
|
|
Protocols.push_back(createBareDeclRef(P));
|
|
if (!Protocols.empty())
|
|
JOS.attribute("protocols", std::move(Protocols));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
|
|
JOS.attribute("categoryDecl", createBareDeclRef(D->getCategoryDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCProtocolDecl(const ObjCProtocolDecl *D) {
|
|
VisitNamedDecl(D);
|
|
|
|
llvm::json::Array Protocols;
|
|
for (const auto *P : D->protocols())
|
|
Protocols.push_back(createBareDeclRef(P));
|
|
if (!Protocols.empty())
|
|
JOS.attribute("protocols", std::move(Protocols));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("super", createBareDeclRef(D->getSuperClass()));
|
|
JOS.attribute("implementation", createBareDeclRef(D->getImplementation()));
|
|
|
|
llvm::json::Array Protocols;
|
|
for (const auto* P : D->protocols())
|
|
Protocols.push_back(createBareDeclRef(P));
|
|
if (!Protocols.empty())
|
|
JOS.attribute("protocols", std::move(Protocols));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCImplementationDecl(
|
|
const ObjCImplementationDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("super", createBareDeclRef(D->getSuperClass()));
|
|
JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCCompatibleAliasDecl(
|
|
const ObjCCompatibleAliasDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
|
|
VisitNamedDecl(D);
|
|
JOS.attribute("type", createQualType(D->getType()));
|
|
|
|
switch (D->getPropertyImplementation()) {
|
|
case ObjCPropertyDecl::None: break;
|
|
case ObjCPropertyDecl::Required: JOS.attribute("control", "required"); break;
|
|
case ObjCPropertyDecl::Optional: JOS.attribute("control", "optional"); break;
|
|
}
|
|
|
|
ObjCPropertyAttribute::Kind Attrs = D->getPropertyAttributes();
|
|
if (Attrs != ObjCPropertyAttribute::kind_noattr) {
|
|
if (Attrs & ObjCPropertyAttribute::kind_getter)
|
|
JOS.attribute("getter", createBareDeclRef(D->getGetterMethodDecl()));
|
|
if (Attrs & ObjCPropertyAttribute::kind_setter)
|
|
JOS.attribute("setter", createBareDeclRef(D->getSetterMethodDecl()));
|
|
attributeOnlyIfTrue("readonly",
|
|
Attrs & ObjCPropertyAttribute::kind_readonly);
|
|
attributeOnlyIfTrue("assign", Attrs & ObjCPropertyAttribute::kind_assign);
|
|
attributeOnlyIfTrue("readwrite",
|
|
Attrs & ObjCPropertyAttribute::kind_readwrite);
|
|
attributeOnlyIfTrue("retain", Attrs & ObjCPropertyAttribute::kind_retain);
|
|
attributeOnlyIfTrue("copy", Attrs & ObjCPropertyAttribute::kind_copy);
|
|
attributeOnlyIfTrue("nonatomic",
|
|
Attrs & ObjCPropertyAttribute::kind_nonatomic);
|
|
attributeOnlyIfTrue("atomic", Attrs & ObjCPropertyAttribute::kind_atomic);
|
|
attributeOnlyIfTrue("weak", Attrs & ObjCPropertyAttribute::kind_weak);
|
|
attributeOnlyIfTrue("strong", Attrs & ObjCPropertyAttribute::kind_strong);
|
|
attributeOnlyIfTrue("unsafe_unretained",
|
|
Attrs & ObjCPropertyAttribute::kind_unsafe_unretained);
|
|
attributeOnlyIfTrue("class", Attrs & ObjCPropertyAttribute::kind_class);
|
|
attributeOnlyIfTrue("direct", Attrs & ObjCPropertyAttribute::kind_direct);
|
|
attributeOnlyIfTrue("nullability",
|
|
Attrs & ObjCPropertyAttribute::kind_nullability);
|
|
attributeOnlyIfTrue("null_resettable",
|
|
Attrs & ObjCPropertyAttribute::kind_null_resettable);
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) {
|
|
VisitNamedDecl(D->getPropertyDecl());
|
|
JOS.attribute("implKind", D->getPropertyImplementation() ==
|
|
ObjCPropertyImplDecl::Synthesize
|
|
? "synthesize"
|
|
: "dynamic");
|
|
JOS.attribute("propertyDecl", createBareDeclRef(D->getPropertyDecl()));
|
|
JOS.attribute("ivarDecl", createBareDeclRef(D->getPropertyIvarDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitBlockDecl(const BlockDecl *D) {
|
|
attributeOnlyIfTrue("variadic", D->isVariadic());
|
|
attributeOnlyIfTrue("capturesThis", D->capturesCXXThis());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitAtomicExpr(const AtomicExpr *AE) {
|
|
JOS.attribute("name", AE->getOpAsString());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCEncodeExpr(const ObjCEncodeExpr *OEE) {
|
|
JOS.attribute("encodedType", createQualType(OEE->getEncodedType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *OME) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
|
|
OME->getSelector().print(OS);
|
|
JOS.attribute("selector", Str);
|
|
|
|
switch (OME->getReceiverKind()) {
|
|
case ObjCMessageExpr::Instance:
|
|
JOS.attribute("receiverKind", "instance");
|
|
break;
|
|
case ObjCMessageExpr::Class:
|
|
JOS.attribute("receiverKind", "class");
|
|
JOS.attribute("classType", createQualType(OME->getClassReceiver()));
|
|
break;
|
|
case ObjCMessageExpr::SuperInstance:
|
|
JOS.attribute("receiverKind", "super (instance)");
|
|
JOS.attribute("superType", createQualType(OME->getSuperType()));
|
|
break;
|
|
case ObjCMessageExpr::SuperClass:
|
|
JOS.attribute("receiverKind", "super (class)");
|
|
JOS.attribute("superType", createQualType(OME->getSuperType()));
|
|
break;
|
|
}
|
|
|
|
QualType CallReturnTy = OME->getCallReturnType(Ctx);
|
|
if (OME->getType() != CallReturnTy)
|
|
JOS.attribute("callReturnType", createQualType(CallReturnTy));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCBoxedExpr(const ObjCBoxedExpr *OBE) {
|
|
if (const ObjCMethodDecl *MD = OBE->getBoxingMethod()) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
|
|
MD->getSelector().print(OS);
|
|
JOS.attribute("selector", Str);
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCSelectorExpr(const ObjCSelectorExpr *OSE) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
|
|
OSE->getSelector().print(OS);
|
|
JOS.attribute("selector", Str);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCProtocolExpr(const ObjCProtocolExpr *OPE) {
|
|
JOS.attribute("protocol", createBareDeclRef(OPE->getProtocol()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *OPRE) {
|
|
if (OPRE->isImplicitProperty()) {
|
|
JOS.attribute("propertyKind", "implicit");
|
|
if (const ObjCMethodDecl *MD = OPRE->getImplicitPropertyGetter())
|
|
JOS.attribute("getter", createBareDeclRef(MD));
|
|
if (const ObjCMethodDecl *MD = OPRE->getImplicitPropertySetter())
|
|
JOS.attribute("setter", createBareDeclRef(MD));
|
|
} else {
|
|
JOS.attribute("propertyKind", "explicit");
|
|
JOS.attribute("property", createBareDeclRef(OPRE->getExplicitProperty()));
|
|
}
|
|
|
|
attributeOnlyIfTrue("isSuperReceiver", OPRE->isSuperReceiver());
|
|
attributeOnlyIfTrue("isMessagingGetter", OPRE->isMessagingGetter());
|
|
attributeOnlyIfTrue("isMessagingSetter", OPRE->isMessagingSetter());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCSubscriptRefExpr(
|
|
const ObjCSubscriptRefExpr *OSRE) {
|
|
JOS.attribute("subscriptKind",
|
|
OSRE->isArraySubscriptRefExpr() ? "array" : "dictionary");
|
|
|
|
if (const ObjCMethodDecl *MD = OSRE->getAtIndexMethodDecl())
|
|
JOS.attribute("getter", createBareDeclRef(MD));
|
|
if (const ObjCMethodDecl *MD = OSRE->setAtIndexMethodDecl())
|
|
JOS.attribute("setter", createBareDeclRef(MD));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE) {
|
|
JOS.attribute("decl", createBareDeclRef(OIRE->getDecl()));
|
|
attributeOnlyIfTrue("isFreeIvar", OIRE->isFreeIvar());
|
|
JOS.attribute("isArrow", OIRE->isArrow());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *OBLE) {
|
|
JOS.attribute("value", OBLE->getValue() ? "__objc_yes" : "__objc_no");
|
|
}
|
|
|
|
void JSONNodeDumper::VisitDeclRefExpr(const DeclRefExpr *DRE) {
|
|
JOS.attribute("referencedDecl", createBareDeclRef(DRE->getDecl()));
|
|
if (DRE->getDecl() != DRE->getFoundDecl())
|
|
JOS.attribute("foundReferencedDecl",
|
|
createBareDeclRef(DRE->getFoundDecl()));
|
|
switch (DRE->isNonOdrUse()) {
|
|
case NOUR_None: break;
|
|
case NOUR_Unevaluated: JOS.attribute("nonOdrUseReason", "unevaluated"); break;
|
|
case NOUR_Constant: JOS.attribute("nonOdrUseReason", "constant"); break;
|
|
case NOUR_Discarded: JOS.attribute("nonOdrUseReason", "discarded"); break;
|
|
}
|
|
attributeOnlyIfTrue("isImmediateEscalating", DRE->isImmediateEscalating());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSYCLUniqueStableNameExpr(
|
|
const SYCLUniqueStableNameExpr *E) {
|
|
JOS.attribute("typeSourceInfo",
|
|
createQualType(E->getTypeSourceInfo()->getType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitOpenACCAsteriskSizeExpr(
|
|
const OpenACCAsteriskSizeExpr *E) {}
|
|
|
|
void JSONNodeDumper::VisitOpenACCDeclareDecl(const OpenACCDeclareDecl *D) {}
|
|
void JSONNodeDumper::VisitOpenACCRoutineDecl(const OpenACCRoutineDecl *D) {}
|
|
|
|
void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {
|
|
JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnaryOperator(const UnaryOperator *UO) {
|
|
JOS.attribute("isPostfix", UO->isPostfix());
|
|
JOS.attribute("opcode", UnaryOperator::getOpcodeStr(UO->getOpcode()));
|
|
if (!UO->canOverflow())
|
|
JOS.attribute("canOverflow", false);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitBinaryOperator(const BinaryOperator *BO) {
|
|
JOS.attribute("opcode", BinaryOperator::getOpcodeStr(BO->getOpcode()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCompoundAssignOperator(
|
|
const CompoundAssignOperator *CAO) {
|
|
VisitBinaryOperator(CAO);
|
|
JOS.attribute("computeLHSType", createQualType(CAO->getComputationLHSType()));
|
|
JOS.attribute("computeResultType",
|
|
createQualType(CAO->getComputationResultType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitMemberExpr(const MemberExpr *ME) {
|
|
// Note, we always write this Boolean field because the information it conveys
|
|
// is critical to understanding the AST node.
|
|
ValueDecl *VD = ME->getMemberDecl();
|
|
JOS.attribute("name", VD && VD->getDeclName() ? VD->getNameAsString() : "");
|
|
JOS.attribute("isArrow", ME->isArrow());
|
|
JOS.attribute("referencedMemberDecl", createPointerRepresentation(VD));
|
|
switch (ME->isNonOdrUse()) {
|
|
case NOUR_None: break;
|
|
case NOUR_Unevaluated: JOS.attribute("nonOdrUseReason", "unevaluated"); break;
|
|
case NOUR_Constant: JOS.attribute("nonOdrUseReason", "constant"); break;
|
|
case NOUR_Discarded: JOS.attribute("nonOdrUseReason", "discarded"); break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXNewExpr(const CXXNewExpr *NE) {
|
|
attributeOnlyIfTrue("isGlobal", NE->isGlobalNew());
|
|
attributeOnlyIfTrue("isArray", NE->isArray());
|
|
attributeOnlyIfTrue("isPlacement", NE->getNumPlacementArgs() != 0);
|
|
switch (NE->getInitializationStyle()) {
|
|
case CXXNewInitializationStyle::None:
|
|
break;
|
|
case CXXNewInitializationStyle::Parens:
|
|
JOS.attribute("initStyle", "call");
|
|
break;
|
|
case CXXNewInitializationStyle::Braces:
|
|
JOS.attribute("initStyle", "list");
|
|
break;
|
|
}
|
|
if (const FunctionDecl *FD = NE->getOperatorNew())
|
|
JOS.attribute("operatorNewDecl", createBareDeclRef(FD));
|
|
if (const FunctionDecl *FD = NE->getOperatorDelete())
|
|
JOS.attribute("operatorDeleteDecl", createBareDeclRef(FD));
|
|
}
|
|
void JSONNodeDumper::VisitCXXDeleteExpr(const CXXDeleteExpr *DE) {
|
|
attributeOnlyIfTrue("isGlobal", DE->isGlobalDelete());
|
|
attributeOnlyIfTrue("isArray", DE->isArrayForm());
|
|
attributeOnlyIfTrue("isArrayAsWritten", DE->isArrayFormAsWritten());
|
|
if (const FunctionDecl *FD = DE->getOperatorDelete())
|
|
JOS.attribute("operatorDeleteDecl", createBareDeclRef(FD));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXThisExpr(const CXXThisExpr *TE) {
|
|
attributeOnlyIfTrue("implicit", TE->isImplicit());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCastExpr(const CastExpr *CE) {
|
|
JOS.attribute("castKind", CE->getCastKindName());
|
|
llvm::json::Array Path = createCastPath(CE);
|
|
if (!Path.empty())
|
|
JOS.attribute("path", std::move(Path));
|
|
// FIXME: This may not be useful information as it can be obtusely gleaned
|
|
// from the inner[] array.
|
|
if (const NamedDecl *ND = CE->getConversionFunction())
|
|
JOS.attribute("conversionFunc", createBareDeclRef(ND));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
|
|
VisitCastExpr(ICE);
|
|
attributeOnlyIfTrue("isPartOfExplicitCast", ICE->isPartOfExplicitCast());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCallExpr(const CallExpr *CE) {
|
|
attributeOnlyIfTrue("adl", CE->usesADL());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnaryExprOrTypeTraitExpr(
|
|
const UnaryExprOrTypeTraitExpr *TTE) {
|
|
JOS.attribute("name", getTraitSpelling(TTE->getKind()));
|
|
if (TTE->isArgumentType())
|
|
JOS.attribute("argType", createQualType(TTE->getArgumentType()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *SOPE) {
|
|
VisitNamedDecl(SOPE->getPack());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnresolvedLookupExpr(
|
|
const UnresolvedLookupExpr *ULE) {
|
|
JOS.attribute("usesADL", ULE->requiresADL());
|
|
JOS.attribute("name", ULE->getName().getAsString());
|
|
|
|
JOS.attributeArray("lookups", [this, ULE] {
|
|
for (const NamedDecl *D : ULE->decls())
|
|
JOS.value(createBareDeclRef(D));
|
|
});
|
|
}
|
|
|
|
void JSONNodeDumper::VisitAddrLabelExpr(const AddrLabelExpr *ALE) {
|
|
JOS.attribute("name", ALE->getLabel()->getName());
|
|
JOS.attribute("labelDeclId", createPointerRepresentation(ALE->getLabel()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXTypeidExpr(const CXXTypeidExpr *CTE) {
|
|
if (CTE->isTypeOperand()) {
|
|
QualType Adjusted = CTE->getTypeOperand(Ctx);
|
|
QualType Unadjusted = CTE->getTypeOperandSourceInfo()->getType();
|
|
JOS.attribute("typeArg", createQualType(Unadjusted));
|
|
if (Adjusted != Unadjusted)
|
|
JOS.attribute("adjustedTypeArg", createQualType(Adjusted));
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitConstantExpr(const ConstantExpr *CE) {
|
|
if (CE->getResultAPValueKind() != APValue::None)
|
|
Visit(CE->getAPValueResult(), CE->getType());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitInitListExpr(const InitListExpr *ILE) {
|
|
if (const FieldDecl *FD = ILE->getInitializedFieldInUnion())
|
|
JOS.attribute("field", createBareDeclRef(FD));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitGenericSelectionExpr(
|
|
const GenericSelectionExpr *GSE) {
|
|
attributeOnlyIfTrue("resultDependent", GSE->isResultDependent());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXUnresolvedConstructExpr(
|
|
const CXXUnresolvedConstructExpr *UCE) {
|
|
if (UCE->getType() != UCE->getTypeAsWritten())
|
|
JOS.attribute("typeAsWritten", createQualType(UCE->getTypeAsWritten()));
|
|
attributeOnlyIfTrue("list", UCE->isListInitialization());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXConstructExpr(const CXXConstructExpr *CE) {
|
|
CXXConstructorDecl *Ctor = CE->getConstructor();
|
|
JOS.attribute("ctorType", createQualType(Ctor->getType()));
|
|
attributeOnlyIfTrue("elidable", CE->isElidable());
|
|
attributeOnlyIfTrue("list", CE->isListInitialization());
|
|
attributeOnlyIfTrue("initializer_list", CE->isStdInitListInitialization());
|
|
attributeOnlyIfTrue("zeroing", CE->requiresZeroInitialization());
|
|
attributeOnlyIfTrue("hadMultipleCandidates", CE->hadMultipleCandidates());
|
|
attributeOnlyIfTrue("isImmediateEscalating", CE->isImmediateEscalating());
|
|
|
|
switch (CE->getConstructionKind()) {
|
|
case CXXConstructionKind::Complete:
|
|
JOS.attribute("constructionKind", "complete");
|
|
break;
|
|
case CXXConstructionKind::Delegating:
|
|
JOS.attribute("constructionKind", "delegating");
|
|
break;
|
|
case CXXConstructionKind::NonVirtualBase:
|
|
JOS.attribute("constructionKind", "non-virtual base");
|
|
break;
|
|
case CXXConstructionKind::VirtualBase:
|
|
JOS.attribute("constructionKind", "virtual base");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *EWC) {
|
|
attributeOnlyIfTrue("cleanupsHaveSideEffects",
|
|
EWC->cleanupsHaveSideEffects());
|
|
if (EWC->getNumObjects()) {
|
|
JOS.attributeArray("cleanups", [this, EWC] {
|
|
for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects())
|
|
if (auto *BD = dyn_cast<BlockDecl *>(CO)) {
|
|
JOS.value(createBareDeclRef(BD));
|
|
} else if (auto *CLE = dyn_cast<CompoundLiteralExpr *>(CO)) {
|
|
llvm::json::Object Obj;
|
|
Obj["id"] = createPointerRepresentation(CLE);
|
|
Obj["kind"] = CLE->getStmtClassName();
|
|
JOS.value(std::move(Obj));
|
|
} else {
|
|
llvm_unreachable("unexpected cleanup object type");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXBindTemporaryExpr(
|
|
const CXXBindTemporaryExpr *BTE) {
|
|
const CXXTemporary *Temp = BTE->getTemporary();
|
|
JOS.attribute("temp", createPointerRepresentation(Temp));
|
|
if (const CXXDestructorDecl *Dtor = Temp->getDestructor())
|
|
JOS.attribute("dtor", createBareDeclRef(Dtor));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitMaterializeTemporaryExpr(
|
|
const MaterializeTemporaryExpr *MTE) {
|
|
if (const ValueDecl *VD = MTE->getExtendingDecl())
|
|
JOS.attribute("extendingDecl", createBareDeclRef(VD));
|
|
|
|
switch (MTE->getStorageDuration()) {
|
|
case SD_Automatic:
|
|
JOS.attribute("storageDuration", "automatic");
|
|
break;
|
|
case SD_Dynamic:
|
|
JOS.attribute("storageDuration", "dynamic");
|
|
break;
|
|
case SD_FullExpression:
|
|
JOS.attribute("storageDuration", "full expression");
|
|
break;
|
|
case SD_Static:
|
|
JOS.attribute("storageDuration", "static");
|
|
break;
|
|
case SD_Thread:
|
|
JOS.attribute("storageDuration", "thread");
|
|
break;
|
|
}
|
|
|
|
attributeOnlyIfTrue("boundToLValueRef", MTE->isBoundToLvalueReference());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node) {
|
|
attributeOnlyIfTrue("hasRewrittenInit", Node->hasRewrittenInit());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node) {
|
|
attributeOnlyIfTrue("hasRewrittenInit", Node->hasRewrittenInit());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCXXDependentScopeMemberExpr(
|
|
const CXXDependentScopeMemberExpr *DSME) {
|
|
JOS.attribute("isArrow", DSME->isArrow());
|
|
JOS.attribute("member", DSME->getMember().getAsString());
|
|
attributeOnlyIfTrue("hasTemplateKeyword", DSME->hasTemplateKeyword());
|
|
attributeOnlyIfTrue("hasExplicitTemplateArgs",
|
|
DSME->hasExplicitTemplateArgs());
|
|
|
|
if (DSME->getNumTemplateArgs()) {
|
|
JOS.attributeArray("explicitTemplateArgs", [DSME, this] {
|
|
for (const TemplateArgumentLoc &TAL : DSME->template_arguments())
|
|
JOS.object(
|
|
[&TAL, this] { Visit(TAL.getArgument(), TAL.getSourceRange()); });
|
|
});
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitRequiresExpr(const RequiresExpr *RE) {
|
|
if (!RE->isValueDependent())
|
|
JOS.attribute("satisfied", RE->isSatisfied());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitIntegerLiteral(const IntegerLiteral *IL) {
|
|
llvm::SmallString<16> Buffer;
|
|
IL->getValue().toString(Buffer,
|
|
/*Radix=*/10, IL->getType()->isSignedIntegerType());
|
|
JOS.attribute("value", Buffer);
|
|
}
|
|
void JSONNodeDumper::VisitCharacterLiteral(const CharacterLiteral *CL) {
|
|
// FIXME: This should probably print the character literal as a string,
|
|
// rather than as a numerical value. It would be nice if the behavior matched
|
|
// what we do to print a string literal; right now, it is impossible to tell
|
|
// the difference between 'a' and L'a' in C from the JSON output.
|
|
JOS.attribute("value", CL->getValue());
|
|
}
|
|
void JSONNodeDumper::VisitFixedPointLiteral(const FixedPointLiteral *FPL) {
|
|
JOS.attribute("value", FPL->getValueAsString(/*Radix=*/10));
|
|
}
|
|
void JSONNodeDumper::VisitFloatingLiteral(const FloatingLiteral *FL) {
|
|
llvm::SmallString<16> Buffer;
|
|
FL->getValue().toString(Buffer);
|
|
JOS.attribute("value", Buffer);
|
|
}
|
|
void JSONNodeDumper::VisitStringLiteral(const StringLiteral *SL) {
|
|
std::string Buffer;
|
|
llvm::raw_string_ostream SS(Buffer);
|
|
SL->outputString(SS);
|
|
JOS.attribute("value", Buffer);
|
|
}
|
|
void JSONNodeDumper::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *BLE) {
|
|
JOS.attribute("value", BLE->getValue());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitIfStmt(const IfStmt *IS) {
|
|
attributeOnlyIfTrue("hasInit", IS->hasInitStorage());
|
|
attributeOnlyIfTrue("hasVar", IS->hasVarStorage());
|
|
attributeOnlyIfTrue("hasElse", IS->hasElseStorage());
|
|
attributeOnlyIfTrue("isConstexpr", IS->isConstexpr());
|
|
attributeOnlyIfTrue("isConsteval", IS->isConsteval());
|
|
attributeOnlyIfTrue("constevalIsNegated", IS->isNegatedConsteval());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitSwitchStmt(const SwitchStmt *SS) {
|
|
attributeOnlyIfTrue("hasInit", SS->hasInitStorage());
|
|
attributeOnlyIfTrue("hasVar", SS->hasVarStorage());
|
|
}
|
|
void JSONNodeDumper::VisitCaseStmt(const CaseStmt *CS) {
|
|
attributeOnlyIfTrue("isGNURange", CS->caseStmtIsGNURange());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitLabelStmt(const LabelStmt *LS) {
|
|
JOS.attribute("name", LS->getName());
|
|
JOS.attribute("declId", createPointerRepresentation(LS->getDecl()));
|
|
attributeOnlyIfTrue("sideEntry", LS->isSideEntry());
|
|
}
|
|
void JSONNodeDumper::VisitGotoStmt(const GotoStmt *GS) {
|
|
JOS.attribute("targetLabelDeclId",
|
|
createPointerRepresentation(GS->getLabel()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitWhileStmt(const WhileStmt *WS) {
|
|
attributeOnlyIfTrue("hasVar", WS->hasVarStorage());
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCAtCatchStmt(const ObjCAtCatchStmt* OACS) {
|
|
// FIXME: it would be nice for the ASTNodeTraverser would handle the catch
|
|
// parameter the same way for C++ and ObjC rather. In this case, C++ gets a
|
|
// null child node and ObjC gets no child node.
|
|
attributeOnlyIfTrue("isCatchAll", OACS->getCatchParamDecl() == nullptr);
|
|
}
|
|
|
|
void JSONNodeDumper::VisitNullTemplateArgument(const TemplateArgument &TA) {
|
|
JOS.attribute("isNull", true);
|
|
}
|
|
void JSONNodeDumper::VisitTypeTemplateArgument(const TemplateArgument &TA) {
|
|
JOS.attribute("type", createQualType(TA.getAsType()));
|
|
}
|
|
void JSONNodeDumper::VisitDeclarationTemplateArgument(
|
|
const TemplateArgument &TA) {
|
|
JOS.attribute("decl", createBareDeclRef(TA.getAsDecl()));
|
|
}
|
|
void JSONNodeDumper::VisitNullPtrTemplateArgument(const TemplateArgument &TA) {
|
|
JOS.attribute("isNullptr", true);
|
|
}
|
|
void JSONNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) {
|
|
JOS.attribute("value", TA.getAsIntegral().getSExtValue());
|
|
}
|
|
void JSONNodeDumper::VisitStructuralValueTemplateArgument(
|
|
const TemplateArgument &TA) {
|
|
Visit(TA.getAsStructuralValue(), TA.getStructuralValueType());
|
|
}
|
|
void JSONNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) {
|
|
// FIXME: cannot just call dump() on the argument, as that doesn't specify
|
|
// the output format.
|
|
}
|
|
void JSONNodeDumper::VisitTemplateExpansionTemplateArgument(
|
|
const TemplateArgument &TA) {
|
|
// FIXME: cannot just call dump() on the argument, as that doesn't specify
|
|
// the output format.
|
|
}
|
|
void JSONNodeDumper::VisitExpressionTemplateArgument(
|
|
const TemplateArgument &TA) {
|
|
JOS.attribute("isExpr", true);
|
|
if (TA.isCanonicalExpr())
|
|
JOS.attribute("isCanonical", true);
|
|
}
|
|
void JSONNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
|
|
JOS.attribute("isPack", true);
|
|
}
|
|
|
|
StringRef JSONNodeDumper::getCommentCommandName(unsigned CommandID) const {
|
|
if (Traits)
|
|
return Traits->getCommandInfo(CommandID)->Name;
|
|
if (const comments::CommandInfo *Info =
|
|
comments::CommandTraits::getBuiltinCommandInfo(CommandID))
|
|
return Info->Name;
|
|
return "<invalid>";
|
|
}
|
|
|
|
void JSONNodeDumper::visitTextComment(const comments::TextComment *C,
|
|
const comments::FullComment *) {
|
|
JOS.attribute("text", C->getText());
|
|
}
|
|
|
|
void JSONNodeDumper::visitInlineCommandComment(
|
|
const comments::InlineCommandComment *C, const comments::FullComment *) {
|
|
JOS.attribute("name", getCommentCommandName(C->getCommandID()));
|
|
|
|
switch (C->getRenderKind()) {
|
|
case comments::InlineCommandRenderKind::Normal:
|
|
JOS.attribute("renderKind", "normal");
|
|
break;
|
|
case comments::InlineCommandRenderKind::Bold:
|
|
JOS.attribute("renderKind", "bold");
|
|
break;
|
|
case comments::InlineCommandRenderKind::Emphasized:
|
|
JOS.attribute("renderKind", "emphasized");
|
|
break;
|
|
case comments::InlineCommandRenderKind::Monospaced:
|
|
JOS.attribute("renderKind", "monospaced");
|
|
break;
|
|
case comments::InlineCommandRenderKind::Anchor:
|
|
JOS.attribute("renderKind", "anchor");
|
|
break;
|
|
}
|
|
|
|
llvm::json::Array Args;
|
|
for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
|
|
Args.push_back(C->getArgText(I));
|
|
|
|
if (!Args.empty())
|
|
JOS.attribute("args", std::move(Args));
|
|
}
|
|
|
|
void JSONNodeDumper::visitHTMLStartTagComment(
|
|
const comments::HTMLStartTagComment *C, const comments::FullComment *) {
|
|
JOS.attribute("name", C->getTagName());
|
|
attributeOnlyIfTrue("selfClosing", C->isSelfClosing());
|
|
attributeOnlyIfTrue("malformed", C->isMalformed());
|
|
|
|
llvm::json::Array Attrs;
|
|
for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I)
|
|
Attrs.push_back(
|
|
{{"name", C->getAttr(I).Name}, {"value", C->getAttr(I).Value}});
|
|
|
|
if (!Attrs.empty())
|
|
JOS.attribute("attrs", std::move(Attrs));
|
|
}
|
|
|
|
void JSONNodeDumper::visitHTMLEndTagComment(
|
|
const comments::HTMLEndTagComment *C, const comments::FullComment *) {
|
|
JOS.attribute("name", C->getTagName());
|
|
}
|
|
|
|
void JSONNodeDumper::visitBlockCommandComment(
|
|
const comments::BlockCommandComment *C, const comments::FullComment *) {
|
|
JOS.attribute("name", getCommentCommandName(C->getCommandID()));
|
|
|
|
llvm::json::Array Args;
|
|
for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
|
|
Args.push_back(C->getArgText(I));
|
|
|
|
if (!Args.empty())
|
|
JOS.attribute("args", std::move(Args));
|
|
}
|
|
|
|
void JSONNodeDumper::visitParamCommandComment(
|
|
const comments::ParamCommandComment *C, const comments::FullComment *FC) {
|
|
switch (C->getDirection()) {
|
|
case comments::ParamCommandPassDirection::In:
|
|
JOS.attribute("direction", "in");
|
|
break;
|
|
case comments::ParamCommandPassDirection::Out:
|
|
JOS.attribute("direction", "out");
|
|
break;
|
|
case comments::ParamCommandPassDirection::InOut:
|
|
JOS.attribute("direction", "in,out");
|
|
break;
|
|
}
|
|
attributeOnlyIfTrue("explicit", C->isDirectionExplicit());
|
|
|
|
if (C->hasParamName())
|
|
JOS.attribute("param", C->isParamIndexValid() ? C->getParamName(FC)
|
|
: C->getParamNameAsWritten());
|
|
|
|
if (C->isParamIndexValid() && !C->isVarArgParam())
|
|
JOS.attribute("paramIdx", C->getParamIndex());
|
|
}
|
|
|
|
void JSONNodeDumper::visitTParamCommandComment(
|
|
const comments::TParamCommandComment *C, const comments::FullComment *FC) {
|
|
if (C->hasParamName())
|
|
JOS.attribute("param", C->isPositionValid() ? C->getParamName(FC)
|
|
: C->getParamNameAsWritten());
|
|
if (C->isPositionValid()) {
|
|
llvm::json::Array Positions;
|
|
for (unsigned I = 0, E = C->getDepth(); I < E; ++I)
|
|
Positions.push_back(C->getIndex(I));
|
|
|
|
if (!Positions.empty())
|
|
JOS.attribute("positions", std::move(Positions));
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::visitVerbatimBlockComment(
|
|
const comments::VerbatimBlockComment *C, const comments::FullComment *) {
|
|
JOS.attribute("name", getCommentCommandName(C->getCommandID()));
|
|
JOS.attribute("closeName", C->getCloseName());
|
|
}
|
|
|
|
void JSONNodeDumper::visitVerbatimBlockLineComment(
|
|
const comments::VerbatimBlockLineComment *C,
|
|
const comments::FullComment *) {
|
|
JOS.attribute("text", C->getText());
|
|
}
|
|
|
|
void JSONNodeDumper::visitVerbatimLineComment(
|
|
const comments::VerbatimLineComment *C, const comments::FullComment *) {
|
|
JOS.attribute("text", C->getText());
|
|
}
|
|
|
|
llvm::json::Object JSONNodeDumper::createFPOptions(FPOptionsOverride FPO) {
|
|
llvm::json::Object Ret;
|
|
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
|
|
if (FPO.has##NAME##Override()) \
|
|
Ret.try_emplace(#NAME, static_cast<unsigned>(FPO.get##NAME##Override()));
|
|
#include "clang/Basic/FPOptions.def"
|
|
return Ret;
|
|
}
|
|
|
|
void JSONNodeDumper::VisitCompoundStmt(const CompoundStmt *S) {
|
|
VisitStmt(S);
|
|
if (S->hasStoredFPFeatures())
|
|
JOS.attribute("fpoptions", createFPOptions(S->getStoredFPFeatures()));
|
|
}
|