Allow lldb-test to combine -find with -dump-clang-ast

This patch threads an lldb::DescriptionLevel through the typesystem to
allow dumping the full Clang AST (level=verbose) of any lldb::Type in
addition to the human-readable source description (default
level=full). This type dumping interface is currently not exposed
through the SBAPI.

The application is to let lldb-test dump the clang AST of search
results. I need this to test lazy type completion of clang types in
subsequent patches.

Differential Revision: https://reviews.llvm.org/D78329
This commit is contained in:
Adrian Prantl 2020-04-16 14:10:23 -07:00
parent dd24fb388b
commit 681466f5e6
12 changed files with 137 additions and 74 deletions

View File

@ -371,9 +371,15 @@ public:
size_t data_byte_size);
/// Dump to stdout.
void DumpTypeDescription() const;
void DumpTypeDescription(lldb::DescriptionLevel level =
lldb::eDescriptionLevelFull) const;
void DumpTypeDescription(Stream *s) const;
/// Print a description of the type to a stream. The exact implementation
/// varies, but the expectation is that eDescriptionLevelFull returns a
/// source-like representation of the type, whereas eDescriptionLevelVerbose
/// does a dump of the underlying AST if applicable.
void DumpTypeDescription(Stream *s, lldb::DescriptionLevel level =
lldb::eDescriptionLevelFull) const;
/// \}
bool GetValueAsScalar(const DataExtractor &data, lldb::offset_t data_offset,

View File

@ -103,7 +103,8 @@ public:
// they get an error.
Type();
void Dump(Stream *s, bool show_context);
void Dump(Stream *s, bool show_context,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull);
void DumpTypeName(Stream *s);

View File

@ -26,7 +26,8 @@ public:
void Clear();
void Dump(Stream *s, bool show_context);
void Dump(Stream *s, bool show_context,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull);
TypeMap FindTypes(ConstString name);

View File

@ -374,11 +374,18 @@ public:
uint32_t bitfield_bit_offset,
ExecutionContextScope *exe_scope) = 0;
virtual void
DumpTypeDescription(lldb::opaque_compiler_type_t type) = 0; // Dump to stdout
/// Dump the type to stdout.
virtual void DumpTypeDescription(
lldb::opaque_compiler_type_t type,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull) = 0;
virtual void DumpTypeDescription(lldb::opaque_compiler_type_t type,
Stream *s) = 0;
/// Print a description of the type to a stream. The exact implementation
/// varies, but the expectation is that eDescriptionLevelFull returns a
/// source-like representation of the type, whereas eDescriptionLevelVerbose
/// does a dump of the underlying AST if applicable.
virtual void DumpTypeDescription(
lldb::opaque_compiler_type_t type, Stream *s,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull) = 0;
// TODO: These methods appear unused. Should they be removed?

View File

@ -8787,9 +8787,10 @@ void TypeSystemClang::DumpSummary(lldb::opaque_compiler_type_t type,
}
}
void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type) {
void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
lldb::DescriptionLevel level) {
StreamFile s(stdout, false);
DumpTypeDescription(type, &s);
DumpTypeDescription(type, &s, level);
CompilerType ct(this, type);
const clang::Type *clang_type = ClangUtil::GetQualType(ct).getTypePtr();
@ -8800,7 +8801,8 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type) {
}
void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
Stream *s) {
Stream *s,
lldb::DescriptionLevel level) {
if (type) {
clang::QualType qual_type =
RemoveWrappingTypes(GetQualType(type), {clang::Type::Typedef});
@ -8814,24 +8816,31 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
case clang::Type::ObjCInterface: {
GetCompleteType(type);
const clang::ObjCObjectType *objc_class_type =
auto *objc_class_type =
llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
assert(objc_class_type);
if (objc_class_type) {
clang::ObjCInterfaceDecl *class_interface_decl =
if (!objc_class_type)
break;
clang::ObjCInterfaceDecl *class_interface_decl =
objc_class_type->getInterface();
if (class_interface_decl) {
clang::PrintingPolicy policy = getASTContext().getPrintingPolicy();
class_interface_decl->print(llvm_ostrm, policy, s->GetIndentLevel());
}
}
if (!class_interface_decl)
break;
if (level == eDescriptionLevelVerbose)
class_interface_decl->dump(llvm_ostrm);
else
class_interface_decl->print(llvm_ostrm,
getASTContext().getPrintingPolicy(),
s->GetIndentLevel());
} break;
case clang::Type::Typedef: {
const clang::TypedefType *typedef_type =
qual_type->getAs<clang::TypedefType>();
if (typedef_type) {
const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl();
auto *typedef_type = qual_type->getAs<clang::TypedefType>();
if (!typedef_type)
break;
const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl();
if (level == eDescriptionLevelVerbose)
typedef_decl->dump(llvm_ostrm);
else {
std::string clang_typedef_name(
typedef_decl->getQualifiedNameAsString());
if (!clang_typedef_name.empty()) {
@ -8844,31 +8853,39 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
case clang::Type::Record: {
GetCompleteType(type);
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
auto *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr());
const clang::RecordDecl *record_decl = record_type->getDecl();
const clang::CXXRecordDecl *cxx_record_decl =
llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
if (cxx_record_decl)
cxx_record_decl->print(llvm_ostrm, getASTContext().getPrintingPolicy(),
s->GetIndentLevel());
else
record_decl->print(llvm_ostrm, getASTContext().getPrintingPolicy(),
s->GetIndentLevel());
if (level == eDescriptionLevelVerbose)
record_decl->dump(llvm_ostrm);
else {
if (auto *cxx_record_decl =
llvm::dyn_cast<clang::CXXRecordDecl>(record_decl))
cxx_record_decl->print(llvm_ostrm,
getASTContext().getPrintingPolicy(),
s->GetIndentLevel());
else
record_decl->print(llvm_ostrm, getASTContext().getPrintingPolicy(),
s->GetIndentLevel());
}
} break;
default: {
const clang::TagType *tag_type =
llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
if (tag_type) {
clang::TagDecl *tag_decl = tag_type->getDecl();
if (tag_decl)
tag_decl->print(llvm_ostrm, 0);
if (auto *tag_type =
llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr())) {
if (clang::TagDecl *tag_decl = tag_type->getDecl()) {
if (level == eDescriptionLevelVerbose)
tag_decl->dump(llvm_ostrm);
else
tag_decl->print(llvm_ostrm, 0);
}
} else {
std::string clang_type_name(qual_type.getAsString());
if (!clang_type_name.empty())
s->PutCString(clang_type_name);
if (level == eDescriptionLevelVerbose)
qual_type->dump(llvm_ostrm);
else {
std::string clang_type_name(qual_type.getAsString());
if (!clang_type_name.empty())
s->PutCString(clang_type_name);
}
}
}
}
@ -8876,7 +8893,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
if (buf.size() > 0) {
s->Write(buf.data(), buf.size());
}
}
}
}
void TypeSystemClang::DumpTypeName(const CompilerType &type) {

View File

@ -975,10 +975,12 @@ public:
lldb::offset_t data_offset, size_t data_byte_size) override;
void DumpTypeDescription(
lldb::opaque_compiler_type_t type) override; // Dump to stdout
lldb::opaque_compiler_type_t type,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull) override;
void DumpTypeDescription(lldb::opaque_compiler_type_t type,
Stream *s) override;
void DumpTypeDescription(
lldb::opaque_compiler_type_t type, Stream *s,
lldb::DescriptionLevel level = lldb::eDescriptionLevelFull) override;
static void DumpTypeName(const CompilerType &type);

View File

@ -744,14 +744,15 @@ void CompilerType::DumpSummary(ExecutionContext *exe_ctx, Stream *s,
data_byte_size);
}
void CompilerType::DumpTypeDescription() const {
void CompilerType::DumpTypeDescription(lldb::DescriptionLevel level) const {
if (IsValid())
m_type_system->DumpTypeDescription(m_type);
m_type_system->DumpTypeDescription(m_type, level);
}
void CompilerType::DumpTypeDescription(Stream *s) const {
void CompilerType::DumpTypeDescription(Stream *s,
lldb::DescriptionLevel level) const {
if (IsValid()) {
m_type_system->DumpTypeDescription(m_type, s);
m_type_system->DumpTypeDescription(m_type, s, level);
}
}

View File

@ -234,7 +234,7 @@ void Type::GetDescription(Stream *s, lldb::DescriptionLevel level,
}
}
void Type::Dump(Stream *s, bool show_context) {
void Type::Dump(Stream *s, bool show_context, lldb::DescriptionLevel level) {
s->Printf("%p: ", static_cast<void *>(this));
s->Indent();
*s << "Type" << static_cast<const UserID &>(*this) << ' ';
@ -255,7 +255,7 @@ void Type::Dump(Stream *s, bool show_context) {
if (m_compiler_type.IsValid()) {
*s << ", compiler_type = " << m_compiler_type.GetOpaqueQualType() << ' ';
GetForwardCompilerType().DumpTypeDescription(s);
GetForwardCompilerType().DumpTypeDescription(s, level);
} else if (m_encoding_uid != LLDB_INVALID_UID) {
s->Format(", type_data = {0:x-16}", m_encoding_uid);
switch (m_encoding_uid_type) {

View File

@ -121,9 +121,9 @@ bool TypeMap::Remove(const lldb::TypeSP &type_sp) {
return false;
}
void TypeMap::Dump(Stream *s, bool show_context) {
void TypeMap::Dump(Stream *s, bool show_context, lldb::DescriptionLevel level) {
for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
pos->second->Dump(s, show_context);
pos->second->Dump(s, show_context, level);
}
}

View File

@ -18,6 +18,7 @@ typedef enum Enum_e { a = 0 } Enum;
@interface SomeClass {
}
@property (readonly) int number;
@end
template <typename T> struct Template { T field; };

View File

@ -1,17 +1,22 @@
// RUN: %clang --target=x86_64-apple-macosx -g -gmodules \
// RUN: -fmodules -fmodules-cache-path=%t.cache \
// RUN: -c -o %t.o %s -I%S/Inputs
// RUN: lldb-test symbols -dump-clang-ast %t.o | FileCheck %s
// Verify that the owning module information from DWARF is preserved in the AST.
@import A;
Typedef t1;
// CHECK-DAG: TypedefDecl {{.*}} imported in A Typedef
// RUN: lldb-test symbols -dump-clang-ast -find type --language=ObjC++ \
// RUN: -compiler-context 'Module:A,Typedef:Typedef' %t.o \
// RUN: | FileCheck %s --check-prefix=CHECK-TYPEDEF
// CHECK-TYPEDEF: TypedefDecl {{.*}} imported in A Typedef
TopLevelStruct s1;
// CHECK-DAG: CXXRecordDecl {{.*}} imported in A struct TopLevelStruct
// CHECK-DAG: -FieldDecl {{.*}} in A a 'int'
// RUN: lldb-test symbols -dump-clang-ast -find type --language=ObjC++ \
// RUN: -compiler-context 'Module:A,Struct:TopLevelStruct' %t.o \
// RUN: | FileCheck %s --check-prefix=CHECK-TOPLEVELSTRUCT
// CHECK-TOPLEVELSTRUCT: CXXRecordDecl {{.*}} imported in A struct TopLevelStruct
// CHECK-TOPLEVELSTRUCT: -FieldDecl {{.*}} in A a 'int'
Struct s2;
// CHECK-DAG: CXXRecordDecl {{.*}} imported in A struct
@ -29,7 +34,13 @@ Enum e1;
// FIXME: -EnumConstantDecl {{.*}} imported in A a
SomeClass *obj1;
// CHECK-DAG: ObjCInterfaceDecl {{.*}} imported in A {{.*}} SomeClass
// RUN: lldb-test symbols -dump-clang-ast -find type --language=ObjC++ \
// RUN: -compiler-context 'Module:A,Struct:SomeClass' %t.o \
// RUN: | FileCheck %s --check-prefix=CHECK-OBJC
// CHECK-OBJC: ObjCInterfaceDecl {{.*}} imported in A SomeClass
// CHECK-OBJC: |-ObjCPropertyDecl {{.*}} imported in A number 'int' readonly
// CHECK-OBJC: | `-getter ObjCMethod {{.*}} 'number'
// CHECK-OBJC: `-ObjCMethodDecl {{.*}} imported in A implicit - number 'int'
// Template specializations are not yet supported, so they lack the ownership info:
Template<int> t2;

View File

@ -169,10 +169,13 @@ static FunctionNameType getFunctionNameFlags() {
static cl::opt<bool> DumpAST("dump-ast",
cl::desc("Dump AST restored from symbols."),
cl::sub(SymbolsSubcommand));
static cl::opt<bool>
DumpClangAST("dump-clang-ast",
cl::desc("Dump clang AST restored from symbols."),
cl::sub(SymbolsSubcommand));
static cl::opt<bool> DumpClangAST(
"dump-clang-ast",
cl::desc("Dump clang AST restored from symbols. When used on its own this "
"will dump the entire AST of all loaded symbols. When combined "
"with -find, it changes the presentation of the search results "
"from pretty-printing the types to an AST dump."),
cl::sub(SymbolsSubcommand));
static cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."),
cl::sub(SymbolsSubcommand));
@ -192,7 +195,7 @@ static Error findTypes(lldb_private::Module &Module);
static Error findVariables(lldb_private::Module &Module);
static Error dumpModule(lldb_private::Module &Module);
static Error dumpAST(lldb_private::Module &Module);
static Error dumpClangAST(lldb_private::Module &Module);
static Error dumpEntireClangAST(lldb_private::Module &Module);
static Error verify(lldb_private::Module &Module);
static Expected<Error (*)(lldb_private::Module &)> getAction();
@ -404,6 +407,10 @@ opts::symbols::getDeclContext(SymbolFile &Symfile) {
return List.GetVariableAtIndex(0)->GetDeclContext();
}
static lldb::DescriptionLevel GetDescriptionLevel() {
return opts::symbols::DumpClangAST ? eDescriptionLevelVerbose : eDescriptionLevelFull;
}
Error opts::symbols::findFunctions(lldb_private::Module &Module) {
SymbolFile &Symfile = *Module.GetSymbolFile();
SymbolContextList List;
@ -534,7 +541,12 @@ Error opts::symbols::findTypes(lldb_private::Module &Module) {
outs() << formatv("Found {0} types:\n", Map.GetSize());
StreamString Stream;
Map.Dump(&Stream, false);
// Resolve types to force-materialize typedef types.
Map.ForEach([&](TypeSP &type) {
type->GetFullCompilerType();
return false;
});
Map.Dump(&Stream, false, GetDescriptionLevel());
outs() << Stream.GetData() << "\n";
return Error::success();
}
@ -615,7 +627,7 @@ Error opts::symbols::dumpAST(lldb_private::Module &Module) {
return Error::success();
}
Error opts::symbols::dumpClangAST(lldb_private::Module &Module) {
Error opts::symbols::dumpEntireClangAST(lldb_private::Module &Module) {
Module.ParseAllDebugSymbols();
SymbolFile *symfile = Module.GetSymbolFile();
@ -719,13 +731,17 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() {
}
if (DumpClangAST) {
if (Find != FindType::None)
return make_string_error("Cannot both search and dump clang AST.");
if (Regex || !Context.empty() || !File.empty() || Line != 0)
return make_string_error(
"-regex, -context, -name, -file and -line options are not "
"applicable for dumping clang AST.");
return dumpClangAST;
if (Find == FindType::None) {
if (Regex || !Context.empty() || !File.empty() || Line != 0)
return make_string_error(
"-regex, -context, -name, -file and -line options are not "
"applicable for dumping the entire clang AST. Either combine with "
"-find, or use -dump-clang-ast as a standalone option.");
return dumpEntireClangAST;
}
if (Find != FindType::Type)
return make_string_error("This combination of -dump-clang-ast and -find "
"<kind> is not yet implemented.");
}
if (Regex && !Context.empty())