Skip to content

Commit 4247cdb

Browse files
committed
[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 <t.cpp:1:1, line:7:1> 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 <unrecognized Decl kind 127> $3 = void
1 parent 69a6417 commit 4247cdb

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

clang/include/clang/AST/ASTDumper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ASTDumper : public ASTNodeTraverser<ASTDumper, TextNodeDumper> {
3232

3333
TextNodeDumper &doGetNodeDelegate() { return NodeDumper; }
3434

35+
void dumpInvalidDeclContext(const DeclContext *DC);
3536
void dumpLookups(const DeclContext *DC, bool DumpDecls);
3637

3738
template <typename SpecializationDecl>

clang/include/clang/AST/DeclBase.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,10 @@ class DeclContext {
19091909
public:
19101910
~DeclContext();
19111911

1912+
// For use when debugging; hasValidDeclKind() will always return true for
1913+
// a correctly constructed object within its lifetime.
1914+
bool hasValidDeclKind() const;
1915+
19121916
Decl::Kind getDeclKind() const {
19131917
return static_cast<Decl::Kind>(DeclContextBits.DeclKind);
19141918
}
@@ -2530,6 +2534,8 @@ class DeclContext {
25302534
static bool classof(const Decl *D);
25312535
static bool classof(const DeclContext *D) { return true; }
25322536

2537+
void dumpAsDecl() const;
2538+
void dumpAsDecl(const ASTContext *Ctx) const;
25332539
void dumpDeclContext() const;
25342540
void dumpLookups() const;
25352541
void dumpLookups(llvm::raw_ostream &OS, bool DumpDecls = false,

clang/lib/AST/ASTDumper.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,37 @@
1919
#include "clang/Basic/Module.h"
2020
#include "clang/Basic/SourceManager.h"
2121
#include "llvm/Support/raw_ostream.h"
22+
2223
using namespace clang;
2324
using namespace clang::comments;
2425

26+
void ASTDumper::dumpInvalidDeclContext(const DeclContext *DC) {
27+
NodeDumper.AddChild([=] {
28+
if (!DC) {
29+
ColorScope Color(OS, ShowColors, NullColor);
30+
OS << "<<<NULL>>>";
31+
return;
32+
}
33+
// An invalid DeclContext is one for which a dyn_cast() from a DeclContext
34+
// pointer to a Decl pointer would fail an assertion or otherwise fall prey
35+
// to undefined behavior as a result of an invalid associated DeclKind.
36+
// Such invalidity is not supposed to happen of course, but, when it does,
37+
// the information provided below is intended to provide some hints about
38+
// what might have gone awry.
39+
{
40+
ColorScope Color(OS, ShowColors, DeclKindNameColor);
41+
OS << "DeclContext";
42+
}
43+
NodeDumper.dumpPointer(DC);
44+
OS << " <";
45+
{
46+
ColorScope Color(OS, ShowColors, DeclNameColor);
47+
OS << "unrecognized Decl kind " << (unsigned)DC->getDeclKind();
48+
}
49+
OS << ">";
50+
});
51+
}
52+
2553
void ASTDumper::dumpLookups(const DeclContext *DC, bool DumpDecls) {
2654
NodeDumper.AddChild([=] {
2755
OS << "StoredDeclsMap ";
@@ -200,6 +228,31 @@ LLVM_DUMP_METHOD void Decl::dumpColor() const {
200228
P.Visit(this);
201229
}
202230

231+
LLVM_DUMP_METHOD void DeclContext::dumpAsDecl() const {
232+
dumpAsDecl(nullptr);
233+
}
234+
235+
LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const {
236+
// By design, DeclContext is required to be a base class of some class that
237+
// derives from Decl. Thus, it should always be possible to dyn_cast() from
238+
// a DeclContext pointer to a Decl pointer and Decl::castFromDeclContext()
239+
// asserts that to be the case. Since this function is intended for use in a
240+
// debugger, it performs an additional check in order to prevent a failed
241+
// cast and assertion. If that check fails, then the (invalid) DeclContext
242+
// is dumped with an indication of its invalidity.
243+
if (hasValidDeclKind()) {
244+
const auto *D = cast<Decl>(this);
245+
D->dump();
246+
} else {
247+
// If an ASTContext is not available, a less capable ASTDumper is
248+
// constructed for which color diagnostics are, regrettably, disabled.
249+
ASTDumper P = Ctx ? ASTDumper(llvm::errs(), *Ctx,
250+
Ctx->getDiagnostics().getShowColors())
251+
: ASTDumper(llvm::errs(), /*ShowColors*/ false);
252+
P.dumpInvalidDeclContext(this);
253+
}
254+
}
255+
203256
LLVM_DUMP_METHOD void DeclContext::dumpLookups() const {
204257
dumpLookups(llvm::errs());
205258
}

clang/lib/AST/DeclBase.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ void Decl::setInvalidDecl(bool Invalid) {
152152
}
153153
}
154154

155+
bool DeclContext::hasValidDeclKind() const {
156+
switch (getDeclKind()) {
157+
#define DECL(DERIVED, BASE) case Decl::DERIVED: return true;
158+
#define ABSTRACT_DECL(DECL)
159+
#include "clang/AST/DeclNodes.inc"
160+
}
161+
return false;
162+
}
163+
155164
const char *DeclContext::getDeclKindName() const {
156165
switch (getDeclKind()) {
157166
#define DECL(DERIVED, BASE) case Decl::DERIVED: return #DERIVED;

0 commit comments

Comments
 (0)