Index: clang/include/clang/AST/ASTNodeTraverser.h =================================================================== --- clang/include/clang/AST/ASTNodeTraverser.h +++ clang/include/clang/AST/ASTNodeTraverser.h @@ -79,7 +79,10 @@ Derived &getDerived() { return *static_cast(this); } public: - void setDeserialize(bool D) { Deserialize = D; } + void setDeserialize(bool D) { + Deserialize = D; + getNodeDelegate().setDeserialize(Deserialize); + } bool getDeserialize() const { return Deserialize; } void SetTraversalKind(TraversalKind TK) { Traversal = TK; } Index: clang/include/clang/AST/TextNodeDumper.h =================================================================== --- clang/include/clang/AST/TextNodeDumper.h +++ clang/include/clang/AST/TextNodeDumper.h @@ -43,10 +43,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/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -2026,7 +2026,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 @@ -49,7 +49,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 {{.*}} @@ -61,7 +61,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 *' @@ -76,7 +76,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' @@ -95,7 +95,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 @@ -109,7 +109,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 {{.*}} @@ -125,7 +125,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...' @@ -140,7 +140,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 {{.*}} @@ -152,7 +152,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 {{.*}} @@ -168,7 +168,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 {{.*}} @@ -180,7 +180,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 {{.*}} @@ -196,7 +196,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 {{.*}} @@ -225,7 +225,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...' @@ -242,7 +242,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 @@ -256,7 +256,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 @@ -270,7 +270,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 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, TU_Complete) { + 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 ASTImporterObjCTest.cpp