diff --git a/clang/include/clang/Frontend/ASTConsumers.h b/clang/include/clang/Frontend/ASTConsumers.h index 0e068bf5cccb5..890701b6ff188 100644 --- a/clang/include/clang/Frontend/ASTConsumers.h +++ b/clang/include/clang/Frontend/ASTConsumers.h @@ -35,6 +35,11 @@ CreateASTDumper(std::unique_ptr OS, StringRef FilterString, bool DumpDecls, bool Deserialize, bool DumpLookups, bool DumpDeclTypes, ASTDumpOutputFormat Format); +std::unique_ptr +CreateASTDumper(raw_ostream &OS, StringRef FilterString, bool DumpDecls, + bool Deserialize, bool DumpLookups, bool DumpDeclTypes, + ASTDumpOutputFormat Format); + // AST Decl node lister: prints qualified names of all filterable AST Decl // nodes. std::unique_ptr CreateASTDeclNodeLister(); diff --git a/clang/lib/Frontend/ASTConsumers.cpp b/clang/lib/Frontend/ASTConsumers.cpp index 7b58eaa04df95..99a9eb11e981e 100644 --- a/clang/lib/Frontend/ASTConsumers.cpp +++ b/clang/lib/Frontend/ASTConsumers.cpp @@ -41,6 +41,13 @@ namespace { OutputKind(K), OutputFormat(Format), FilterString(FilterString), DumpLookups(DumpLookups), DumpDeclTypes(DumpDeclTypes) {} + ASTPrinter(raw_ostream &Out, Kind K, ASTDumpOutputFormat Format, + StringRef FilterString, bool DumpLookups = false, + bool DumpDeclTypes = false) + : Out(Out), OwnedOut(nullptr), OutputKind(K), OutputFormat(Format), + FilterString(FilterString), DumpLookups(DumpLookups), + DumpDeclTypes(DumpDeclTypes) {} + void HandleTranslationUnit(ASTContext &Context) override { TranslationUnitDecl *D = Context.getTranslationUnitDecl(); @@ -175,6 +182,19 @@ clang::CreateASTDumper(std::unique_ptr Out, StringRef FilterString, Format, FilterString, DumpLookups, DumpDeclTypes); } +std::unique_ptr +clang::CreateASTDumper(raw_ostream &Out, StringRef FilterString, bool DumpDecls, + bool Deserialize, bool DumpLookups, bool DumpDeclTypes, + ASTDumpOutputFormat Format) { + assert((DumpDecls || Deserialize || DumpLookups) && "nothing to dump"); + return std::make_unique(Out, + Deserialize ? ASTPrinter::DumpFull + : DumpDecls ? ASTPrinter::Dump + : ASTPrinter::None, + Format, FilterString, DumpLookups, + DumpDeclTypes); +} + std::unique_ptr clang::CreateASTDeclNodeLister() { return std::make_unique(nullptr); } diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 8d6f090605628..c6fa11a6bccc6 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -302,7 +302,7 @@ class SymbolFile : public PluginInterface { lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list); - virtual void DumpClangAST(Stream &s) {} + virtual void DumpClangAST(Stream &s, llvm::StringRef filter) {} virtual void FindGlobalVariables(ConstString name, const CompilerDeclContext &parent_decl_ctx, uint32_t max_matches, diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h index 7a366bfabec86..0c769142993c0 100644 --- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h +++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h @@ -127,7 +127,7 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile { lldb_private::SymbolContextList &sc_list) override; void Dump(lldb_private::Stream &s) override; - void DumpClangAST(lldb_private::Stream &s) override; + void DumpClangAST(lldb_private::Stream &s, llvm::StringRef filter) override; void FindGlobalVariables(lldb_private::ConstString name, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index c3f32ca8d97bf..d1c336573b327 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -482,7 +482,11 @@ class TypeSystem : public PluginInterface, /// given stream. /// /// This should not modify the state of the TypeSystem if possible. - virtual void Dump(llvm::raw_ostream &output) = 0; + /// + /// \param[out] output Stream to dup the AST into. + /// \param[in] filter If empty, dump whole AST. If non-empty, will only + /// dump decls whose names contain \c filter. + virtual void Dump(llvm::raw_ostream &output, llvm::StringRef filter) = 0; /// This is used by swift. virtual bool IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) = 0; diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 446d2492caf89..322e97d88e789 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -2237,11 +2237,23 @@ class CommandObjectTargetModulesDumpClangAST : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules dump ast", "Dump the clang ast for a given module's symbol file.", - //"target modules dump ast [ ...]") - nullptr, eCommandRequiresTarget) {} + "target modules dump ast [--filter ] [ ...]", + eCommandRequiresTarget), + m_filter(LLDB_OPT_SET_1, false, "filter", 'f', 0, eArgTypeName, + "Dump only the decls whose names contain the specified filter " + "string.", + /*default_value=*/"") { + m_option_group.Append(&m_filter, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + Options *GetOptions() override { return &m_option_group; } ~CommandObjectTargetModulesDumpClangAST() override = default; + OptionGroupOptions m_option_group; + OptionGroupString m_filter; + protected: void DoExecute(Args &command, CommandReturnObject &result) override { Target *target = &GetTarget(); @@ -2253,6 +2265,8 @@ class CommandObjectTargetModulesDumpClangAST return; } + llvm::StringRef filter = m_filter.GetOptionValue().GetCurrentValueAsRef(); + if (command.GetArgumentCount() == 0) { // Dump all ASTs for all modules images result.GetOutputStream().Format("Dumping clang ast for {0} modules.\n", @@ -2261,7 +2275,7 @@ class CommandObjectTargetModulesDumpClangAST if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping clang ast")) break; if (SymbolFile *sf = module_sp->GetSymbolFile()) - sf->DumpClangAST(result.GetOutputStream()); + sf->DumpClangAST(result.GetOutputStream(), filter); } result.SetStatus(eReturnStatusSuccessFinishResult); return; @@ -2290,7 +2304,7 @@ class CommandObjectTargetModulesDumpClangAST Module *m = module_list.GetModulePointerAtIndex(i); if (SymbolFile *sf = m->GetSymbolFile()) - sf->DumpClangAST(result.GetOutputStream()); + sf->DumpClangAST(result.GetOutputStream(), filter); } } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -5290,7 +5304,7 @@ class CommandObjectTargetDumpTypesystem : public CommandObjectParsed { // Go over every scratch TypeSystem and dump to the command output. for (lldb::TypeSystemSP ts : GetTarget().GetScratchTypeSystems()) if (ts) - ts->Dump(result.GetOutputStream().AsRawOstream()); + ts->Dump(result.GetOutputStream().AsRawOstream(), ""); result.SetStatus(eReturnStatusSuccessFinishResult); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 83f4416b59d78..62be7d3f80a26 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -4355,7 +4355,7 @@ void SymbolFileDWARF::Dump(Stream &s) { m_index->Dump(s); } -void SymbolFileDWARF::DumpClangAST(Stream &s) { +void SymbolFileDWARF::DumpClangAST(Stream &s, llvm::StringRef filter) { auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); if (!ts_or_err) return; @@ -4363,7 +4363,7 @@ void SymbolFileDWARF::DumpClangAST(Stream &s) { TypeSystemClang *clang = llvm::dyn_cast_or_null(ts.get()); if (!clang) return; - clang->Dump(s.AsRawOstream()); + clang->Dump(s.AsRawOstream(), filter); } bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 44354594e847d..1e15f926997dc 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -299,7 +299,7 @@ class SymbolFileDWARF : public SymbolFileCommon { void Dump(Stream &s) override; - void DumpClangAST(Stream &s) override; + void DumpClangAST(Stream &s, llvm::StringRef filter) override; /// List separate dwo files. bool GetSeparateDebugInfo(StructuredData::Dictionary &d, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 42c16b941e8d6..377ad47f7d7fb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1280,9 +1280,9 @@ bool SymbolFileDWARFDebugMap::GetCompileOption(const char *option, return success; } -void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) { +void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s, llvm::StringRef filter) { ForEachSymbolFile([&s](SymbolFileDWARF *oso_dwarf) { - oso_dwarf->DumpClangAST(s); + oso_dwarf->DumpClangAST(s, filter); // The underlying assumption is that DumpClangAST(...) will obtain the // AST from the underlying TypeSystem and therefore we only need to do // this once and can stop after the first iteration hence we return true. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index f4b4bfb152786..44188d2abbfa3 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -139,7 +139,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon { std::vector GetASTData(lldb::LanguageType language) override; - void DumpClangAST(Stream &s) override; + void DumpClangAST(Stream &s, llvm::StringRef filter) override; /// List separate oso files. bool GetSeparateDebugInfo(StructuredData::Dictionary &d, diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index b79d3e63f72b1..54d3111fe7bb6 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -1449,6 +1449,6 @@ PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { return static_cast(context.GetOpaqueDeclContext()); } -void PdbAstBuilder::Dump(Stream &stream) { - m_clang.Dump(stream.AsRawOstream()); +void PdbAstBuilder::Dump(Stream &stream, llvm::StringRef filter) { + m_clang.Dump(stream.AsRawOstream(), filter); } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h index b7cad30c69c0c..66a3836fac053 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -87,7 +87,7 @@ class PdbAstBuilder { TypeSystemClang &clang() { return m_clang; } ClangASTImporter &GetClangASTImporter() { return m_importer; } - void Dump(Stream &stream); + void Dump(Stream &stream, llvm::StringRef filter); private: clang::Decl *TryGetDecl(PdbSymUid uid) const; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 4e50b5d961436..cd3f073db2327 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -1640,7 +1640,7 @@ size_t SymbolFileNativePDB::ParseSymbolArrayInScope( return count; } -void SymbolFileNativePDB::DumpClangAST(Stream &s) { +void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) { auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); if (!ts_or_err) return; @@ -1648,7 +1648,7 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s) { TypeSystemClang *clang = llvm::dyn_cast_or_null(ts.get()); if (!clang) return; - clang->GetNativePDBParser()->Dump(s); + clang->GetNativePDBParser()->Dump(s, filter); } void SymbolFileNativePDB::FindGlobalVariables( diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h index 669c44aa131ed..03777678431ce 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -157,7 +157,7 @@ class SymbolFileNativePDB : public SymbolFileCommon { PdbIndex &GetIndex() { return *m_index; }; - void DumpClangAST(Stream &s) override; + void DumpClangAST(Stream &s, llvm::StringRef filter) override; std::optional GetParentType(llvm::codeview::TypeIndex ti); diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp index 18f5080573f16..9dc919ae09e05 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -1446,7 +1446,7 @@ void SymbolFilePDB::AddSymbols(lldb_private::Symtab &symtab) { symtab.Finalize(); } -void SymbolFilePDB::DumpClangAST(Stream &s) { +void SymbolFilePDB::DumpClangAST(Stream &s, llvm::StringRef filter) { auto type_system_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); if (auto err = type_system_or_err.takeError()) { @@ -1460,7 +1460,7 @@ void SymbolFilePDB::DumpClangAST(Stream &s) { llvm::dyn_cast_or_null(ts.get()); if (!clang_type_system) return; - clang_type_system->Dump(s.AsRawOstream()); + clang_type_system->Dump(s.AsRawOstream(), filter); } void SymbolFilePDB::FindTypesByRegex( diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h index ea495c575f1f1..c0b25b6ee4055 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h +++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -157,7 +157,7 @@ class SymbolFilePDB : public lldb_private::SymbolFileCommon { const llvm::pdb::IPDBSession &GetPDBSession() const; - void DumpClangAST(lldb_private::Stream &s) override; + void DumpClangAST(lldb_private::Stream &s, llvm::StringRef filter) override; private: struct SecContribInfo { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 3dc6e391e5d83..5d046942d4ba7 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -11,6 +11,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/Frontend/ASTConsumers.h" #include "llvm/ADT/STLForwardCompat.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FormatAdapters.h" @@ -8819,8 +8820,16 @@ TypeSystemClang::dump(lldb::opaque_compiler_type_t type) const { } #endif -void TypeSystemClang::Dump(llvm::raw_ostream &output) { - GetTranslationUnitDecl()->dump(output); +void TypeSystemClang::Dump(llvm::raw_ostream &output, llvm::StringRef filter) { + auto consumer = + clang::CreateASTDumper(output, filter, + /*DumpDecls=*/true, + /*Deserialize=*/false, + /*DumpLookups=*/false, + /*DumpDeclTypes=*/false, clang::ADOF_Default); + assert(consumer); + assert(m_ast_up); + consumer->HandleTranslationUnit(*m_ast_up); } void TypeSystemClang::DumpFromSymbolFile(Stream &s, @@ -10029,10 +10038,11 @@ GetNameForIsolatedASTKind(ScratchTypeSystemClang::IsolatedASTKind kind) { llvm_unreachable("Unimplemented IsolatedASTKind?"); } -void ScratchTypeSystemClang::Dump(llvm::raw_ostream &output) { +void ScratchTypeSystemClang::Dump(llvm::raw_ostream &output, + llvm::StringRef filter) { // First dump the main scratch AST. output << "State of scratch Clang type system:\n"; - TypeSystemClang::Dump(output); + TypeSystemClang::Dump(output, filter); // Now sort the isolated sub-ASTs. typedef std::pair KeyAndTS; @@ -10047,7 +10057,7 @@ void ScratchTypeSystemClang::Dump(llvm::raw_ostream &output) { static_cast(a.first); output << "State of scratch Clang type subsystem " << GetNameForIsolatedASTKind(kind) << ":\n"; - a.second->Dump(output); + a.second->Dump(output, filter); } } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 778f444b9e1bc..d12cdf0012f27 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -1131,7 +1131,7 @@ class TypeSystemClang : public TypeSystem { #endif /// \see lldb_private::TypeSystem::Dump - void Dump(llvm::raw_ostream &output) override; + void Dump(llvm::raw_ostream &output, llvm::StringRef filter) override; /// Dump clang AST types from the symbol file. /// @@ -1392,7 +1392,7 @@ class ScratchTypeSystemClang : public TypeSystemClang { } /// \see lldb_private::TypeSystem::Dump - void Dump(llvm::raw_ostream &output) override; + void Dump(llvm::raw_ostream &output, llvm::StringRef filter) override; UserExpression *GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix, diff --git a/lldb/source/Symbol/SymbolFileOnDemand.cpp b/lldb/source/Symbol/SymbolFileOnDemand.cpp index 94979b2fb1c22..807c2124e48d9 100644 --- a/lldb/source/Symbol/SymbolFileOnDemand.cpp +++ b/lldb/source/Symbol/SymbolFileOnDemand.cpp @@ -305,13 +305,14 @@ void SymbolFileOnDemand::Dump(lldb_private::Stream &s) { return m_sym_file_impl->Dump(s); } -void SymbolFileOnDemand::DumpClangAST(lldb_private::Stream &s) { +void SymbolFileOnDemand::DumpClangAST(lldb_private::Stream &s, + llvm::StringRef filter) { if (!m_debug_info_enabled) { LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); return; } - return m_sym_file_impl->DumpClangAST(s); + return m_sym_file_impl->DumpClangAST(s, filter); } void SymbolFileOnDemand::FindGlobalVariables(const RegularExpression ®ex, diff --git a/lldb/test/Shell/Commands/command-image-dump-ast.test b/lldb/test/Shell/Commands/command-image-dump-ast.test new file mode 100644 index 0000000000000..ca57570ab7224 --- /dev/null +++ b/lldb/test/Shell/Commands/command-image-dump-ast.test @@ -0,0 +1,70 @@ +# Test `image dump ast` command. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp + +void A() {} +void A1() {} +void BA1() {} +void AB() {} + +int main() { + A(); + A1(); + BA1(); + AB(); +} + +#--- commands.input + +break set -n main +run +expr A(); A1(); BA1(); AB() + +image dump ast + +# CHECK: image dump ast +# CHECK-DAG: FunctionDecl {{.*}} main +# CHECK-DAG: FunctionDecl {{.*}} A +# CHECK-DAG: FunctionDecl {{.*}} A1 +# CHECK-DAG: FunctionDecl {{.*}} BA1 +# CHECK-DAG: FunctionDecl {{.*}} AB + +image dump ast --filter A + +# CHECK: image dump ast --filter A +# CHECK: Dumping A +# CHECK-NOT: FunctionDecl {{.*}} main +# CHECK-DAG: FunctionDecl {{.*}} A1 +# CHECK-DAG: FunctionDecl {{.*}} BA1 +# CHECK-DAG: FunctionDecl {{.*}} AB + +image dump ast --filter A1 + +# CHECK: image dump ast --filter A1 +# CHECK: Dumping A +# CHECK-NOT: FunctionDecl {{.*}} main +# CHECK-NOT: FunctionDecl {{.*}} AB +# CHECK-DAG: FunctionDecl {{.*}} A1 +# CHECK-DAG: FunctionDecl {{.*}} BA1 + +image dump ast --filter "" + +# CHECK: image dump ast --filter "" +# CHECK-DAG: FunctionDecl {{.*}} main +# CHECK-DAG: FunctionDecl {{.*}} AB +# CHECK-DAG: FunctionDecl {{.*}} A1 +# CHECK-DAG: FunctionDecl {{.*}} BA1 + +image dump ast -f AB + +# CHECK: image dump ast -f AB +# CHECK: Dumping AB +# CHECK-NOT: FunctionDecl {{.*}} main +# CHECK-NOT: FunctionDecl {{.*}} A1 +# CHECK-NOT: FunctionDecl {{.*}} BA1 +# CHECK: FunctionDecl {{.*}} AB