diff --git a/clang/include/clang/AST/ASTDumper.h b/clang/include/clang/AST/ASTDumper.h --- a/clang/include/clang/AST/ASTDumper.h +++ b/clang/include/clang/AST/ASTDumper.h @@ -32,6 +32,7 @@ TextNodeDumper &doGetNodeDelegate() { return NodeDumper; } + void dumpInvalidDeclContext(const DeclContext *DC); void dumpLookups(const DeclContext *DC, bool DumpDecls); template diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1906,6 +1906,10 @@ public: ~DeclContext(); + // For use when debugging; hasValidDeclKind() will always return true for + // a correctly constructed object within its lifetime. + bool hasValidDeclKind() const; + Decl::Kind getDeclKind() const { return static_cast(DeclContextBits.DeclKind); } @@ -2527,6 +2531,8 @@ static bool classof(const Decl *D); static bool classof(const DeclContext *D) { return true; } + void dumpAsDecl() const; + void dumpAsDecl(const ASTContext *Ctx) const; void dumpDeclContext() const; void dumpLookups() const; void dumpLookups(llvm::raw_ostream &OS, bool DumpDecls = false, diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -19,9 +19,37 @@ #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/raw_ostream.h" + using namespace clang; using namespace clang::comments; +void ASTDumper::dumpInvalidDeclContext(const DeclContext *DC) { + NodeDumper.AddChild([=] { + if (!DC) { + ColorScope Color(OS, ShowColors, NullColor); + OS << "<<>>"; + return; + } + // An invalid DeclContext is one for which a dyn_cast() from a DeclContext + // pointer to a Decl pointer would fail an assertion or otherwise fall prey + // to undefined behavior as a result of an invalid associated DeclKind. + // Such invalidity is not supposed to happen of course, but, when it does, + // the information provided below is intended to provide some hints about + // what might have gone awry. + { + ColorScope Color(OS, ShowColors, DeclKindNameColor); + OS << "DeclContext"; + } + NodeDumper.dumpPointer(DC); + OS << " <"; + { + ColorScope Color(OS, ShowColors, DeclNameColor); + OS << "unrecognized Decl kind " << (unsigned)DC->getDeclKind(); + } + OS << ">"; + }); +} + void ASTDumper::dumpLookups(const DeclContext *DC, bool DumpDecls) { NodeDumper.AddChild([=] { OS << "StoredDeclsMap "; @@ -200,6 +228,37 @@ P.Visit(this); } +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl() const { + dumpAsDecl(nullptr); +} + +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const { + // By design, every DeclContext instance is required to be a base class of + // some class that derives from Decl. Thus, it should always be possible to + // dyn_cast() from a DeclContext pointer to a Decl pointer and, indeed, + // the innerworkings of dyn_cast() do assert that to be the case! Alas, + // strange and unfortunate things do occasionally occur that lead to folk + // like yourself, dear reader, running a debugger and feeling extraordinarily + // curious about the origin of a DeclContext instance for which you have + // little knowledge. This function has been carefully designed to provide you, + // yes you, the answers you desperately seek and deserve with minimal risk + // that simply asking the question will upend your debugging experience. The + // call to dyn_cast() below is guarded by a validity check that ensures its + // success, thus preventing an otherwise potentially volatile (no, not that + // kind of volatile) situation. + if (hasValidDeclKind()) { + const auto *D = dyn_cast(this); + D->dump(); + } else { + // If an ASTContext is not available, a less capable ASTDumper is + // constructed for which color diagnostics are, regrettably, disabled. + ASTDumper P = Ctx ? ASTDumper(llvm::errs(), *Ctx, + Ctx->getDiagnostics().getShowColors()) + : ASTDumper(llvm::errs(), /*ShowColors*/ false); + P.dumpInvalidDeclContext(this); + } +} + LLVM_DUMP_METHOD void DeclContext::dumpLookups() const { dumpLookups(llvm::errs()); } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -152,6 +152,15 @@ } } +bool DeclContext::hasValidDeclKind() const { + switch (getDeclKind()) { +#define DECL(DERIVED, BASE) case Decl::DERIVED: return true; +#define ABSTRACT_DECL(DECL) +#include "clang/AST/DeclNodes.inc" + } + return false; +} + const char *DeclContext::getDeclKindName() const { switch (getDeclKind()) { #define DECL(DERIVED, BASE) case Decl::DERIVED: return #DERIVED;