mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 12:06:09 +00:00

This patch is the Part-1 (FE Clang) implementation of HW Exception handling. This new feature adds the support of Hardware Exception for Microsoft Windows SEH (Structured Exception Handling). This is the first step of this project; only X86_64 target is enabled in this patch. Compiler options: For clang-cl.exe, the option is -EHa, the same as MSVC. For clang.exe, the extra option is -fasync-exceptions, plus -triple x86_64-windows -fexceptions and -fcxx-exceptions as usual. NOTE:: Without the -EHa or -fasync-exceptions, this patch is a NO-DIFF change. The rules for C code: For C-code, one way (MSVC approach) to achieve SEH -EHa semantic is to follow three rules: * First, no exception can move in or out of _try region., i.e., no "potential faulty instruction can be moved across _try boundary. * Second, the order of exceptions for instructions 'directly' under a _try must be preserved (not applied to those in callees). * Finally, global states (local/global/heap variables) that can be read outside of _try region must be updated in memory (not just in register) before the subsequent exception occurs. The impact to C++ code: Although SEH is a feature for C code, -EHa does have a profound effect on C++ side. When a C++ function (in the same compilation unit with option -EHa ) is called by a SEH C function, a hardware exception occurs in C++ code can also be handled properly by an upstream SEH _try-handler or a C++ catch(...). As such, when that happens in the middle of an object's life scope, the dtor must be invoked the same way as C++ Synchronous Exception during unwinding process. Design: A natural way to achieve the rules above in LLVM today is to allow an EH edge added on memory/computation instruction (previous iload/istore idea) so that exception path is modeled in Flow graph preciously. However, tracking every single memory instruction and potential faulty instruction can create many Invokes, complicate flow graph and possibly result in negative performance impact for downstream optimization and code generation. Making all optimizations be aware of the new semantic is also substantial. This design does not intend to model exception path at instruction level. Instead, the proposed design tracks and reports EH state at BLOCK-level to reduce the complexity of flow graph and minimize the performance-impact on CPP code under -EHa option. One key element of this design is the ability to compute State number at block-level. Our algorithm is based on the following rationales: A _try scope is always a SEME (Single Entry Multiple Exits) region as jumping into a _try is not allowed. The single entry must start with a seh_try_begin() invoke with a correct State number that is the initial state of the SEME. Through control-flow, state number is propagated into all blocks. Side exits marked by seh_try_end() will unwind to parent state based on existing SEHUnwindMap[]. Note side exits can ONLY jump into parent scopes (lower state number). Thus, when a block succeeds various states from its predecessors, the lowest State triumphs others. If some exits flow to unreachable, propagation on those paths terminate, not affecting remaining blocks. For CPP code, object lifetime region is usually a SEME as SEH _try. However there is one rare exception: jumping into a lifetime that has Dtor but has no Ctor is warned, but allowed: Warning: jump bypasses variable with a non-trivial destructor In that case, the region is actually a MEME (multiple entry multiple exits). Our solution is to inject a eha_scope_begin() invoke in the side entry block to ensure a correct State. Implementation: Part-1: Clang implementation described below. Two intrinsic are created to track CPP object scopes; eha_scope_begin() and eha_scope_end(). _scope_begin() is immediately added after ctor() is called and EHStack is pushed. So it must be an invoke, not a call. With that it's also guaranteed an EH-cleanup-pad is created regardless whether there exists a call in this scope. _scope_end is added before dtor(). These two intrinsics make the computation of Block-State possible in downstream code gen pass, even in the presence of ctor/dtor inlining. Two intrinsic, seh_try_begin() and seh_try_end(), are added for C-code to mark _try boundary and to prevent from exceptions being moved across _try boundary. All memory instructions inside a _try are considered as 'volatile' to assure 2nd and 3rd rules for C-code above. This is a little sub-optimized. But it's acceptable as the amount of code directly under _try is very small. Part-2 (will be in Part-2 patch): LLVM implementation described below. For both C++ & C-code, the state of each block is computed at the same place in BE (WinEHPreparing pass) where all other EH tables/maps are calculated. In addition to _scope_begin & _scope_end, the computation of block state also rely on the existing State tracking code (UnwindMap and InvokeStateMap). For both C++ & C-code, the state of each block with potential trap instruction is marked and reported in DAG Instruction Selection pass, the same place where the state for -EHsc (synchronous exceptions) is done. If the first instruction in a reported block scope can trap, a Nop is injected before this instruction. This nop is needed to accommodate LLVM Windows EH implementation, in which the address in IPToState table is offset by +1. (note the purpose of that is to ensure the return address of a call is in the same scope as the call address. The handler for catch(...) for -EHa must handle HW exception. So it is 'adjective' flag is reset (it cannot be IsStdDotDot (0x40) that only catches C++ exceptions). Suppress push/popTerminate() scope (from noexcept/noTHrow) so that HW exceptions can be passed through. Original llvm-dev [RFC] discussions can be found in these two threads below: https://lists.llvm.org/pipermail/llvm-dev/2020-March/140541.html https://lists.llvm.org/pipermail/llvm-dev/2020-April/141338.html Differential Revision: https://reviews.llvm.org/D80344/new/
1635 lines
58 KiB
C++
1635 lines
58 KiB
C++
#include "clang/AST/JSONNodeDumper.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/Specifiers.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
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_RValue: Category = "rvalue"; 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(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 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 APValue &Value, QualType Ty) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
Value.printPretty(OS, Ctx, Ty);
|
|
JOS.attribute("value", OS.str());
|
|
}
|
|
|
|
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();
|
|
llvm::json::Object Ret{{"qualType", QualType::getAsString(SQT, PrintPolicy)}};
|
|
|
|
if (Desugar && !QT.isNull()) {
|
|
SplitQualType DSQT = QT.getSplitDesugaredType();
|
|
if (DSQT != SQT)
|
|
Ret["desugaredQualType"] = QualType::getAsString(DSQT, PrintPolicy);
|
|
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::VisitTypedefType(const TypedefType *TT) {
|
|
JOS.attribute("decl", createBareDeclRef(TT->getDecl()));
|
|
}
|
|
|
|
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 ArrayType::Star:
|
|
JOS.attribute("sizeModifier", "*");
|
|
break;
|
|
case ArrayType::Static:
|
|
JOS.attribute("sizeModifier", "static");
|
|
break;
|
|
case ArrayType::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->getSize().getSExtValue());
|
|
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 VectorType::GenericVector:
|
|
break;
|
|
case VectorType::AltiVecVector:
|
|
JOS.attribute("vectorKind", "altivec");
|
|
break;
|
|
case VectorType::AltiVecPixel:
|
|
JOS.attribute("vectorKind", "altivec pixel");
|
|
break;
|
|
case VectorType::AltiVecBool:
|
|
JOS.attribute("vectorKind", "altivec bool");
|
|
break;
|
|
case VectorType::NeonVector:
|
|
JOS.attribute("vectorKind", "neon");
|
|
break;
|
|
case VectorType::NeonPolyVector:
|
|
JOS.attribute("vectorKind", "neon poly");
|
|
break;
|
|
case VectorType::SveFixedLengthDataVector:
|
|
JOS.attribute("vectorKind", "fixed-length sve data vector");
|
|
break;
|
|
case VectorType::SveFixedLengthPredicateVector:
|
|
JOS.attribute("vectorKind", "fixed-length sve predicate vector");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *UUT) {
|
|
JOS.attribute("decl", createBareDeclRef(UUT->getDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitUnaryTransformType(const UnaryTransformType *UTT) {
|
|
switch (UTT->getUTTKind()) {
|
|
case UnaryTransformType::EnumUnderlyingType:
|
|
JOS.attribute("transformKind", "underlying_type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
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::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", OS.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 (llvm::Optional<unsigned> 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", OS.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());
|
|
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());
|
|
if (!ND->isOriginalNamespace())
|
|
JOS.attribute("originalNamespace",
|
|
createBareDeclRef(ND->getOriginalNamespace()));
|
|
}
|
|
|
|
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::VisitUsingShadowDecl(const UsingShadowDecl *USD) {
|
|
JOS.attribute("target", createBareDeclRef(USD->getTargetDecl()));
|
|
}
|
|
|
|
void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) {
|
|
VisitNamedDecl(VD);
|
|
JOS.attribute("type", createQualType(VD->getType()));
|
|
|
|
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;
|
|
}
|
|
}
|
|
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->isPure());
|
|
attributeOnlyIfTrue("explicitlyDeleted", FD->isDeletedAsWritten());
|
|
attributeOnlyIfTrue("constexpr", FD->isConstexpr());
|
|
attributeOnlyIfTrue("variadic", FD->isVariadic());
|
|
|
|
if (FD->isDefaulted())
|
|
JOS.attribute("explicitlyDefaulted",
|
|
FD->isDeleted() ? "deleted" : "default");
|
|
}
|
|
|
|
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);
|
|
|
|
// 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::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(), 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(), 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 LinkageSpecDecl::lang_c: Lang = "C"; break;
|
|
case LinkageSpecDecl::lang_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()));
|
|
}
|
|
|
|
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::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", OS.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", OS.str());
|
|
}
|
|
}
|
|
|
|
void JSONNodeDumper::VisitObjCSelectorExpr(const ObjCSelectorExpr *OSE) {
|
|
std::string Str;
|
|
llvm::raw_string_ostream OS(Str);
|
|
|
|
OSE->getSelector().print(OS);
|
|
JOS.attribute("selector", OS.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;
|
|
}
|
|
}
|
|
|
|
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 CXXNewExpr::NoInit: break;
|
|
case CXXNewExpr::CallInit: JOS.attribute("initStyle", "call"); break;
|
|
case CXXNewExpr::ListInit: 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());
|
|
|
|
switch (CE->getConstructionKind()) {
|
|
case CXXConstructExpr::CK_Complete:
|
|
JOS.attribute("constructionKind", "complete");
|
|
break;
|
|
case CXXConstructExpr::CK_Delegating:
|
|
JOS.attribute("constructionKind", "delegating");
|
|
break;
|
|
case CXXConstructExpr::CK_NonVirtualBase:
|
|
JOS.attribute("constructionKind", "non-virtual base");
|
|
break;
|
|
case CXXConstructExpr::CK_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 = CO.dyn_cast<BlockDecl *>()) {
|
|
JOS.value(createBareDeclRef(BD));
|
|
} else if (auto *CLE = CO.dyn_cast<CompoundLiteralExpr *>()) {
|
|
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::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::VisitIntegerLiteral(const IntegerLiteral *IL) {
|
|
JOS.attribute("value",
|
|
IL->getValue().toString(
|
|
/*Radix=*/10, IL->getType()->isSignedIntegerType()));
|
|
}
|
|
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", SS.str());
|
|
}
|
|
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());
|
|
}
|
|
|
|
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::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);
|
|
}
|
|
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::InlineCommandComment::RenderNormal:
|
|
JOS.attribute("renderKind", "normal");
|
|
break;
|
|
case comments::InlineCommandComment::RenderBold:
|
|
JOS.attribute("renderKind", "bold");
|
|
break;
|
|
case comments::InlineCommandComment::RenderEmphasized:
|
|
JOS.attribute("renderKind", "emphasized");
|
|
break;
|
|
case comments::InlineCommandComment::RenderMonospaced:
|
|
JOS.attribute("renderKind", "monospaced");
|
|
break;
|
|
case comments::InlineCommandComment::RenderAnchor:
|
|
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::ParamCommandComment::In:
|
|
JOS.attribute("direction", "in");
|
|
break;
|
|
case comments::ParamCommandComment::Out:
|
|
JOS.attribute("direction", "out");
|
|
break;
|
|
case comments::ParamCommandComment::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());
|
|
}
|