[clang]: Add DeclContext::dumpAsDecl().
authorTom Honermann <tom.honermann@intel.com>
Sat, 1 Oct 2022 11:06:58 +0000 (04:06 -0700)
committerTom Honermann <tom@honermann.net>
Mon, 3 Oct 2022 21:25:44 +0000 (17:25 -0400)
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

clang/include/clang/AST/ASTDumper.h
clang/include/clang/AST/DeclBase.h
clang/lib/AST/ASTDumper.cpp
clang/lib/AST/DeclBase.cpp

index a154bc2..71ac467 100644 (file)
@@ -32,6 +32,7 @@ public:
 
   TextNodeDumper &doGetNodeDelegate() { return NodeDumper; }
 
+  void dumpInvalidDeclContext(const DeclContext *DC);
   void dumpLookups(const DeclContext *DC, bool DumpDecls);
 
   template <typename SpecializationDecl>
index 1332b00..8a5f755 100644 (file)
@@ -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<Decl::Kind>(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,
index 60700f7..9900efb 100644 (file)
 #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 << "<<<NULL>>>";
+      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<Decl>(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());
 }
index e062353..a9ac441 100644 (file)
@@ -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;