From 4247cdb568eca4c31b14d91105fe5ee140225036 Mon Sep 17 00:00:00 2001 From: Tom Honermann Date: Sat, 1 Oct 2022 04:06:58 -0700 Subject: [PATCH] [clang]: Add DeclContext::dumpAsDecl(). This change enables a declaration to be conveniently displayed within a debugger when only a pointer to its DeclContext is available. For example, in gdb: (gdb) p Ctx $1 = (const clang::DeclContext *) 0x14c1a580 (gdb) p Ctx->dumpAsDecl() ClassTemplateSpecializationDecl 0x14c1a540 line:2:8 struct ct `-TemplateArgument type 'int' `-BuiltinType 0x14bac420 'int' $2 = void In the event that the pointed to DeclContext is invalid (that it has an invalid DeclKind as a result of a dangling pointer, memory corruption, etc...) it is not possible to dump its associated declaration. In this case, the DeclContext will be reported as invalid. For example, in gdb: (gdb) p Ctx->dumpAsDecl() DeclContext 0x14c1a580 $3 = void --- clang/include/clang/AST/ASTDumper.h | 1 + clang/include/clang/AST/DeclBase.h | 6 +++++ clang/lib/AST/ASTDumper.cpp | 53 +++++++++++++++++++++++++++++++++++++ clang/lib/AST/DeclBase.cpp | 9 +++++++ 4 files changed, 69 insertions(+) diff --git a/clang/include/clang/AST/ASTDumper.h b/clang/include/clang/AST/ASTDumper.h index a154bc2..71ac467 100644 --- a/clang/include/clang/AST/ASTDumper.h +++ b/clang/include/clang/AST/ASTDumper.h @@ -32,6 +32,7 @@ public: 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 index 1332b00..8a5f755 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1909,6 +1909,10 @@ protected: 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); } @@ -2530,6 +2534,8 @@ public: 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 index 60700f7..9900efb 100644 --- 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,31 @@ LLVM_DUMP_METHOD void Decl::dumpColor() const { P.Visit(this); } +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl() const { + dumpAsDecl(nullptr); +} + +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const { + // By design, DeclContext 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 Decl::castFromDeclContext() + // asserts that to be the case. Since this function is intended for use in a + // debugger, it performs an additional check in order to prevent a failed + // cast and assertion. If that check fails, then the (invalid) DeclContext + // is dumped with an indication of its invalidity. + if (hasValidDeclKind()) { + const auto *D = 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 index e062353..a9ac441 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -152,6 +152,15 @@ void Decl::setInvalidDecl(bool Invalid) { } } +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; -- 2.7.4