diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index 0417ea846368c..a736e10bbbd27 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -16,6 +16,7 @@ #include "swift/AST/Module.h" #include "swift/AST/RawComment.h" #include "swift/Basic/BasicSourceInfo.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/PointerIntPair.h" @@ -188,6 +189,8 @@ class FileUnit : public DeclContext, public ASTAllocated { virtual Identifier getDiscriminatorForPrivateValue(const ValueDecl *D) const = 0; + virtual bool shouldCollectDisplayDecls() const { return true; } + /// Finds all top-level decls in this file. /// /// This does a simple local lookup, not recursively looking through imports. @@ -308,6 +311,9 @@ class FileUnit : public DeclContext, public ASTAllocated { return getParentModule()->getRealName().str(); } + SWIFT_DEBUG_DUMPER(dumpDisplayDecls()); + SWIFT_DEBUG_DUMPER(dumpTopLevelDecls()); + /// Traverse the decls within this file. /// /// \returns true if traversal was aborted, false if it completed diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 46ed457ba8bce..31e7c6391ec88 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -26,6 +26,7 @@ #include "swift/AST/Type.h" #include "swift/Basic/BasicSourceInfo.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/OptionSet.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceLoc.h" @@ -759,6 +760,15 @@ class ModuleDecl /// The order of the results is not guaranteed to be meaningful. void getPrecedenceGroups(SmallVectorImpl &Results) const; + /// Determines whether this module should be recursed into when calling + /// \c getDisplayDecls. + /// + /// Some modules should not call \c getDisplayDecls, due to assertions + /// in their implementation. These are usually implicit imports that would be + /// recursed into for parsed modules. This function provides a guard against + /// recusing into modules that should not have decls collected. + bool shouldCollectDisplayDecls() const; + /// Finds all top-level decls that should be displayed to a client of this /// module. /// @@ -856,6 +866,9 @@ class ModuleDecl /// transferred from module files to the dSYMs, remove this. bool isExternallyConsumed() const; + SWIFT_DEBUG_DUMPER(dumpDisplayDecls()); + SWIFT_DEBUG_DUMPER(dumpTopLevelDecls()); + SourceRange getSourceRange() const { return SourceRange(); } static bool classof(const DeclContext *DC) { diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 99136c7051dba..9b7d221a60c45 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -288,6 +288,10 @@ class SourceFile final : public FileUnit { ~SourceFile(); + bool hasImports() const { + return Imports.hasValue(); + } + /// Retrieve an immutable view of the source file's imports. ArrayRef> getImports() const { return *Imports; diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h index c714765946170..21e93ba159f57 100644 --- a/include/swift/ClangImporter/ClangModule.h +++ b/include/swift/ClangImporter/ClangModule.h @@ -87,6 +87,8 @@ class ClangModuleUnit final : public LoadedFile { ObjCSelector selector, SmallVectorImpl &results) const override; + virtual bool shouldCollectDisplayDecls() const override; + virtual void getTopLevelDecls(SmallVectorImpl &results) const override; virtual void getDisplayDecls(SmallVectorImpl &results) const override; diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index a17980982965e..4da86aed85e8f 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -780,6 +780,30 @@ void SourceFile::lookupObjCMethods( results.append(known->second.begin(), known->second.end()); } +bool ModuleDecl::shouldCollectDisplayDecls() const { + for (const FileUnit *file : Files) { + if (!file->shouldCollectDisplayDecls()) + return false; + } + return true; +} + +static void collectParsedExportedImports(const ModuleDecl *M, SmallPtrSetImpl &Imports) { + for (const FileUnit *file : M->getFiles()) { + if (const SourceFile *source = dyn_cast(file)) { + if (source->hasImports()) { + for (auto import : source->getImports()) { + if (import.options.contains(ImportFlags::Exported) && + !Imports.contains(import.module.importedModule) && + import.module.importedModule->shouldCollectDisplayDecls()) { + Imports.insert(import.module.importedModule); + } + } + } + } + } +} + void ModuleDecl::getLocalTypeDecls(SmallVectorImpl &Results) const { FORWARD(getLocalTypeDecls, (Results)); } @@ -788,6 +812,24 @@ void ModuleDecl::getTopLevelDecls(SmallVectorImpl &Results) const { FORWARD(getTopLevelDecls, (Results)); } +void ModuleDecl::dumpDisplayDecls() const { + SmallVector Decls; + getDisplayDecls(Decls); + for (auto *D : Decls) { + D->dump(llvm::errs()); + llvm::errs() << "\n"; + } +} + +void ModuleDecl::dumpTopLevelDecls() const { + SmallVector Decls; + getTopLevelDecls(Decls); + for (auto *D : Decls) { + D->dump(llvm::errs()); + llvm::errs() << "\n"; + } +} + void ModuleDecl::getExportedPrespecializations( SmallVectorImpl &Results) const { FORWARD(getExportedPrespecializations, (Results)); @@ -908,8 +950,23 @@ SourceFile::getExternalRawLocsForDecl(const Decl *D) const { } void ModuleDecl::getDisplayDecls(SmallVectorImpl &Results) const { + if (isParsedModule(this)) { + SmallPtrSet Modules; + collectParsedExportedImports(this, Modules); + for (const ModuleDecl *import : Modules) { + import->getDisplayDecls(Results); + } + } // FIXME: Should this do extra access control filtering? FORWARD(getDisplayDecls, (Results)); + +#ifndef NDEBUG + llvm::DenseSet visited; + for (auto *D : Results) { + auto inserted = visited.insert(D).second; + assert(inserted && "there should be no duplicate decls"); + } +#endif } ProtocolConformanceRef @@ -3066,6 +3123,22 @@ void FileUnit::getTopLevelDeclsWhereAttributesMatch( Results.erase(newEnd, Results.end()); } +void FileUnit::dumpDisplayDecls() const { + SmallVector Decls; + getDisplayDecls(Decls); + for (auto *D : Decls) { + D->dump(llvm::errs()); + } +} + +void FileUnit::dumpTopLevelDecls() const { + SmallVector Decls; + getTopLevelDecls(Decls); + for (auto *D : Decls) { + D->dump(llvm::errs()); + } +} + void swift::simple_display(llvm::raw_ostream &out, const FileUnit *file) { if (!file) { out << "(null)"; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 40a852d2161db..a2fb22a20a0b5 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3120,6 +3120,9 @@ class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer { }; } // unnamed namespace +// FIXME: should submodules still be crawled for the symbol graph? (SR-15753) +bool ClangModuleUnit::shouldCollectDisplayDecls() const { return isTopLevel(); } + void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl &results) const { VectorDeclPtrConsumer consumer(results); FilteringDeclaredDeclConsumer filterConsumer(consumer, this); diff --git a/test/SymbolGraph/ClangImporter/EmitWhileBuilding.swift b/test/SymbolGraph/ClangImporter/EmitWhileBuilding.swift index f44a3bdcf9b62..7c39f50551537 100644 --- a/test/SymbolGraph/ClangImporter/EmitWhileBuilding.swift +++ b/test/SymbolGraph/ClangImporter/EmitWhileBuilding.swift @@ -1,8 +1,9 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework %t -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module-path %t/EmitWhileBuilding.framework/Modules/EmitWhileBuilding.swiftmodule/%target-swiftmodule-name -import-underlying-module -F %t -module-name EmitWhileBuilding -disable-objc-attr-requires-foundation-module %s -emit-symbol-graph -emit-symbol-graph-dir %t +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module-path %t/EmitWhileBuilding.framework/Modules/EmitWhileBuilding.swiftmodule/%target-swiftmodule-name -import-underlying-module -F %t -module-name EmitWhileBuilding -disable-objc-attr-requires-foundation-module %s %S/Inputs/EmitWhileBuilding/Extra.swift -emit-symbol-graph -emit-symbol-graph-dir %t // RUN: %{python} -m json.tool %t/EmitWhileBuilding.symbols.json %t/EmitWhileBuilding.formatted.symbols.json // RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.formatted.symbols.json +// RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.formatted.symbols.json --check-prefix HEADER // REQUIRES: objc_interop @@ -10,6 +11,9 @@ import Foundation public enum SwiftEnum {} +// HEADER: "precise": "c:@testVariable" + +// CHECK: "precise": "s:17EmitWhileBuilding9SwiftEnumO", // CHECK: "declarationFragments": [ // CHECK-NEXT: { // CHECK-NEXT: "kind": "keyword", diff --git a/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/Extra.swift b/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/Extra.swift new file mode 100644 index 0000000000000..7a0e59c907c0b --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/Extra.swift @@ -0,0 +1 @@ +public struct SomeStruct {} diff --git a/test/SymbolGraph/ClangImporter/Inputs/Submodules/Mixed.h b/test/SymbolGraph/ClangImporter/Inputs/Submodules/Mixed.h new file mode 100644 index 0000000000000..8b6286de54516 --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/Submodules/Mixed.h @@ -0,0 +1 @@ +double outerVar = 1.0; diff --git a/test/SymbolGraph/ClangImporter/Inputs/Submodules/Submodule.h b/test/SymbolGraph/ClangImporter/Inputs/Submodules/Submodule.h new file mode 100644 index 0000000000000..84aa726df85f8 --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/Submodules/Submodule.h @@ -0,0 +1 @@ +double innerVar = 2.0; diff --git a/test/SymbolGraph/ClangImporter/Inputs/Submodules/module.modulemap b/test/SymbolGraph/ClangImporter/Inputs/Submodules/module.modulemap new file mode 100644 index 0000000000000..39d64cf87a85f --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/Submodules/module.modulemap @@ -0,0 +1,9 @@ +module Mixed { + header "Mixed.h" + export * + + explicit module Submodule { + header "Submodule.h" + export * + } +} diff --git a/test/SymbolGraph/ClangImporter/Submodules.swift b/test/SymbolGraph/ClangImporter/Submodules.swift new file mode 100644 index 0000000000000..27e6c125def1f --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Submodules.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/Submodules -emit-module-path %t/Submodules.swiftmodule -enable-objc-interop -module-name Submodules %s -emit-symbol-graph -emit-symbol-graph-dir %t + +// REQUIRES: objc_interop + +// Don't crash when a module declared an `@_exported import` for a Clang non-top-level module. + +@_exported import Mixed +@_exported import Mixed.Submodule + +public func someFunc() {}