Index: clang/include/clang/AST/TextNodeDumper.h =================================================================== --- clang/include/clang/AST/TextNodeDumper.h +++ clang/include/clang/AST/TextNodeDumper.h @@ -42,10 +42,16 @@ /// Indicates if we're handling the first child after entering a new depth. bool FirstChild = true; + /// Indicates if we can deserialize declarations from the ExternalASTSource. + bool Deserialize = true; + /// Prefix for currently-being-dumped entity. std::string Prefix; public: + void setDeserialize(bool D) { Deserialize = D; } + bool getDeserialize() const { return Deserialize; } + /// Add a child of the current node. Calls DoAddChild without arguments template void AddChild(Fn DoAddChild) { return AddChild("", DoAddChild); Index: clang/lib/AST/ASTDumper.cpp =================================================================== --- clang/lib/AST/ASTDumper.cpp +++ clang/lib/AST/ASTDumper.cpp @@ -196,6 +196,7 @@ } else { ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors()); P.setDeserialize(Deserialize); + P.doGetNodeDelegate().setDeserialize(Deserialize); P.Visit(this); } } Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -1972,7 +1972,12 @@ FLAG(hasTrivialDestructor, trivial); FLAG(hasNonTrivialDestructor, non_trivial); FLAG(hasUserDeclaredDestructor, user_declared); - FLAG(hasConstexprDestructor, constexpr); + // Avoid calls to the external source unless we are explicitly allowed + // to do so. + if (!D->hasExternalVisibleStorage() || getDeserialize()) { + FLAG(hasConstexprDestructor, constexpr); + } else + OS << " maybe_constexpr"; FLAG(needsImplicitDestructor, needs_implicit); FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution); if (!D->needsOverloadResolutionForDestructor()) Index: clang/test/AST/ast-dump-lambda.cpp =================================================================== --- clang/test/AST/ast-dump-lambda.cpp +++ clang/test/AST/ast-dump-lambda.cpp @@ -48,7 +48,7 @@ // CHECK-NEXT: | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | |-MoveAssignment exists simple trivial needs_implicit -// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | |-CXXRecordDecl {{.*}} col:10{{( imported)?}} implicit struct V // CHECK-NEXT: | `-CXXMethodDecl {{.*}} line:17:10{{( imported)?}} f 'void ()' // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -60,7 +60,7 @@ // CHECK-NEXT: | | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | | |-MoveAssignment -// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | | |-CXXMethodDecl {{.*}} col:7{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | | `-FieldDecl {{.*}} col:8{{( imported)?}} implicit 'V *' @@ -75,7 +75,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:7{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-FieldDecl {{.*}} col:8{{( imported)?}} implicit 'V' @@ -94,7 +94,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -108,7 +108,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto (int, ...) const' inline // CHECK-NEXT: | | | |-ParmVarDecl {{.*}} col:10{{( imported)?}} a 'int' // CHECK-NEXT: | | | `-CompoundStmt {{.*}} @@ -124,7 +124,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-FieldDecl {{.*}} col:4{{( imported)?}} implicit 'Ts...' @@ -139,7 +139,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -151,7 +151,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} @@ -167,7 +167,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -179,7 +179,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} @@ -195,7 +195,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | | `-ReturnStmt {{.*}} @@ -224,7 +224,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-FieldDecl {{.*}} col:4{{( imported)?}} implicit 'Ts...' @@ -241,7 +241,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} constexpr operator() 'auto () const' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -255,7 +255,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto ()' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -269,7 +269,7 @@ // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const noexcept' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() noexcept 'auto (*() const noexcept)() noexcept' inline @@ -283,7 +283,7 @@ // CHECK-NEXT: | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | |-MoveAssignment -// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> int' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} Index: clang/test/AST/ast-dump-records.cpp =================================================================== --- clang/test/AST/ast-dump-records.cpp +++ clang/test/AST/ast-dump-records.cpp @@ -22,7 +22,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct A int a; @@ -57,7 +57,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct C struct { @@ -68,7 +68,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int a; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 a 'int' } b; @@ -82,7 +82,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int c; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 c 'int' float d; @@ -104,7 +104,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int e, f; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 e 'int' // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:12 f 'int' @@ -126,7 +126,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct D int a; @@ -151,7 +151,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:7 implicit union E int a; @@ -186,7 +186,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:7 implicit union G struct { @@ -197,7 +197,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int a; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 a 'int' @@ -214,7 +214,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int c; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 c 'int' @@ -237,7 +237,7 @@ // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int e, f; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 e 'int' Index: clang/unittests/AST/ASTDumpTest.cpp =================================================================== --- /dev/null +++ clang/unittests/AST/ASTDumpTest.cpp @@ -0,0 +1,182 @@ +//===- unittests/AST/ASTDumpTest.cpp --- Declaration tests ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Tests Decl::dump(). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "gtest/gtest.h" + +using namespace clang; + +namespace clang { +namespace ast { + +namespace { +/// An ExternalASTSource that asserts if it is queried for information about +/// any declaration. +class TrappingExternalASTSource : public ExternalASTSource { + ~TrappingExternalASTSource() override = default; + bool FindExternalVisibleDeclsByName(const DeclContext *, + DeclarationName) override { + assert(false && "Unexpected call to FindExternalVisibleDeclsByName"); + return true; + } + + void FindExternalLexicalDecls(const DeclContext *, + llvm::function_ref, + SmallVectorImpl &) override { + assert(false && "Unexpected call to FindExternalLexicalDecls"); + } + + void completeVisibleDeclsMap(const DeclContext *) override { + assert(false && "Unexpected call to completeVisibleDeclsMap"); + } + + void CompleteRedeclChain(const Decl *) override { + assert(false && "Unexpected call to CompleteRedeclChain"); + } + + void CompleteType(TagDecl *) override { + assert(false && "Unexpected call to CompleteType(Tag Decl*)"); + } + + void CompleteType(ObjCInterfaceDecl *) override { + assert(false && "Unexpected call to CompleteType(ObjCInterfaceDecl *)"); + } +}; + +/// An ExternalASTSource that records which DeclContexts were queried so far. +class RecordingExternalASTSource : public ExternalASTSource { +public: + llvm::DenseSet QueriedDCs; + ~RecordingExternalASTSource() override = default; + + void FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref, + SmallVectorImpl &) override { + QueriedDCs.insert(DC); + } +}; + + +class ASTDumpTestBase : public ::testing::Test { +protected: + ASTDumpTestBase(clang::ExternalASTSource *Source) + : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), Idents(LangOpts, nullptr), + Ctxt(LangOpts, SourceMgr, Idents, Sels, Builtins) { + Ctxt.setExternalSource(Source); + } + + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + IntrusiveRefCntPtr DiagID; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + LangOptions LangOpts; + IdentifierTable Idents; + SelectorTable Sels; + Builtin::Context Builtins; + ASTContext Ctxt; +}; + +/// Tests that Decl::dump doesn't load additional declarations from the +/// ExternalASTSource. +class NoDeserializeTest : public ASTDumpTestBase { +protected: + NoDeserializeTest() : ASTDumpTestBase(new TrappingExternalASTSource()) {} +}; + +/// Tests which declarations Decl::dump deserializes; +class DeserializeTest : public ASTDumpTestBase { +protected: + DeserializeTest() : ASTDumpTestBase(Recorder = new RecordingExternalASTSource()) {} + RecordingExternalASTSource *Recorder; +}; +} // unnamed namespace + +/// Set all flags that activate queries to the ExternalASTSource. +static void setExternalStorageFlags(DeclContext *DC) { + DC->setHasExternalLexicalStorage(); + DC->setHasExternalVisibleStorage(); + DC->setMustBuildLookupTable(); +} + +/// Dumps the given Decl. +static void dumpDecl(Decl *D, bool Deserialize) { + // Try dumping the decl which shouldn't trigger any calls to the + // ExternalASTSource. + + std::string Out; + llvm::raw_string_ostream OS(Out); + D->dump(OS, Deserialize); +} + +TEST_F(NoDeserializeTest, DumpObjCInterfaceDecl) { + // Define an Objective-C interface. + ObjCInterfaceDecl *I = ObjCInterfaceDecl::Create( + Ctxt, Ctxt.getTranslationUnitDecl(), SourceLocation(), + &Ctxt.Idents.get("c"), nullptr, nullptr); + Ctxt.getTranslationUnitDecl()->addDecl(I); + + setExternalStorageFlags(I); + dumpDecl(I, /*Deserialize*/false); +} + +TEST_F(NoDeserializeTest, DumpRecordDecl) { + // Define a struct. + RecordDecl *R = RecordDecl::Create( + Ctxt, TagDecl::TagKind::TTK_Class, Ctxt.getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), &Ctxt.Idents.get("c")); + R->startDefinition(); + R->completeDefinition(); + Ctxt.getTranslationUnitDecl()->addDecl(R); + + setExternalStorageFlags(R); + dumpDecl(R, /*Deserialize*/false); +} + +TEST_F(NoDeserializeTest, DumpCXXRecordDecl) { + // Define a class. + CXXRecordDecl *R = CXXRecordDecl::Create( + Ctxt, TagDecl::TagKind::TTK_Class, Ctxt.getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), &Ctxt.Idents.get("c")); + R->startDefinition(); + R->completeDefinition(); + Ctxt.getTranslationUnitDecl()->addDecl(R); + + setExternalStorageFlags(R); + dumpDecl(R, /*Deserialize*/false); +} + +TEST_F(DeserializeTest, DumpCXXRecordDecl) { + // Define a class. + CXXRecordDecl *R = CXXRecordDecl::Create( + Ctxt, TagDecl::TagKind::TTK_Class, Ctxt.getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), &Ctxt.Idents.get("c")); + R->startDefinition(); + R->completeDefinition(); + Ctxt.getTranslationUnitDecl()->addDecl(R); + + setExternalStorageFlags(R); + // Check that our record gets deserialized while dumping. + EXPECT_FALSE(Recorder->QueriedDCs.contains(R)); + dumpDecl(R, /*Deserialize*/true); + EXPECT_TRUE(Recorder->QueriedDCs.contains(R)); +} + +} // end namespace ast +} // end namespace clang Index: clang/unittests/AST/CMakeLists.txt =================================================================== --- clang/unittests/AST/CMakeLists.txt +++ clang/unittests/AST/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_unittest(ASTTests ASTContextParentMapTest.cpp + ASTDumpTest.cpp ASTImporterFixtures.cpp ASTImporterTest.cpp ASTImporterGenericRedeclTest.cpp