Skip to content

Commit ae9bc59

Browse files
authored
Merge pull request #60659 from xymus/print-missing-imports
[ModuleInterface] Print missing imports in swiftinterface
2 parents 982fb87 + 66a994b commit ae9bc59

12 files changed

+111
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5855,6 +5855,10 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
58555855
"%4 was not imported by this file}5",
58565856
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))
58575857

5858+
NOTE(missing_import_inserted,
5859+
none, "The missing import of module %0 will be added implicitly",
5860+
(Identifier))
5861+
58585862
ERROR(availability_macro_in_inlinable, none,
58595863
"availability macro cannot be used in " FRAGILE_FUNC_KIND "0",
58605864
(unsigned))

include/swift/AST/FileUnit.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
271271
getImportedModules(SmallVectorImpl<ImportedModule> &imports,
272272
ModuleDecl::ImportFilter filter) const {}
273273

274+
/// Lists modules that are not imported from this file and used in API.
275+
virtual void
276+
getMissingImportedModules(SmallVectorImpl<ImportedModule> &imports) const {}
277+
274278
/// \see ModuleDecl::getImportedModulesForLookup
275279
virtual void getImportedModulesForLookup(
276280
SmallVectorImpl<ImportedModule> &imports) const {

include/swift/AST/Module.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,10 @@ class ModuleDecl
712712
void getImportedModules(SmallVectorImpl<ImportedModule> &imports,
713713
ImportFilter filter = ImportFilterKind::Exported) const;
714714

715+
/// Lists modules that are not imported from a file and used in API.
716+
void
717+
getMissingImportedModules(SmallVectorImpl<ImportedModule> &imports) const;
718+
715719
/// Looks up which modules are imported by this module, ignoring any that
716720
/// won't contain top-level decls.
717721
///

include/swift/AST/SourceFile.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,15 @@ class SourceFile final : public FileUnit {
399399

400400
SWIFT_DEBUG_DUMPER(dumpSeparatelyImportedOverlays());
401401

402+
llvm::SmallDenseSet<ImportedModule> MissingImportedModules;
403+
404+
void addMissingImportedModule(ImportedModule module) const {
405+
const_cast<SourceFile *>(this)->MissingImportedModules.insert(module);
406+
}
407+
408+
void getMissingImportedModules(
409+
SmallVectorImpl<ImportedModule> &imports) const override;
410+
402411
void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
403412
const SmallVectorImpl<ValueDecl *> &getCachedVisibleDecls() const;
404413

include/swift/Frontend/ModuleInterfaceSupport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ struct ModuleInterfaceOptions {
5353
/// when PrintSPIs is true.
5454
bool ExperimentalSPIImports = false;
5555

56+
/// Print imports that are missing from the source and used in API.
57+
bool PrintMissingImports = true;
58+
5659
/// Intentionally print invalid syntax into the file.
5760
bool DebugPrintInvalidSyntax = false;
5861

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,11 @@ def experimental_spi_imports :
900900
Flag<["-"], "experimental-spi-imports">,
901901
HelpText<"Enable experimental support for SPI imports">;
902902

903+
def disable_print_missing_imports_in_module_interface :
904+
Flag<["-"], "disable-print-missing-imports-in-module-interface">,
905+
HelpText<"Disable adding to the module interface imports used from API and "
906+
"missing from the sources">;
907+
903908
// [FIXME: Clang-type-plumbing] Make this a SIL-only option once we start
904909
// unconditionally emitting non-canonical Clang types in swiftinterfaces.
905910
def experimental_print_full_convention :

lib/AST/Module.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,11 @@ void ModuleDecl::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
15701570
FORWARD(getImportedModules, (modules, filter));
15711571
}
15721572

1573+
void ModuleDecl::getMissingImportedModules(
1574+
SmallVectorImpl<ImportedModule> &imports) const {
1575+
FORWARD(getMissingImportedModules, (imports));
1576+
}
1577+
15731578
void
15741579
SourceFile::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
15751580
ModuleDecl::ImportFilter filter) const {
@@ -1604,6 +1609,12 @@ SourceFile::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
16041609
}
16051610
}
16061611

1612+
void SourceFile::getMissingImportedModules(
1613+
SmallVectorImpl<ImportedModule> &modules) const {
1614+
for (auto module : MissingImportedModules)
1615+
modules.push_back(module);
1616+
}
1617+
16071618
void SourceFile::dumpSeparatelyImportedOverlays() const {
16081619
for (auto &pair : separatelyImportedOverlays) {
16091620
auto &underlying = std::get<0>(pair);
@@ -2535,6 +2546,21 @@ RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *modul
25352546
if (imports.isImportedBy(module, getParentModule()))
25362547
return RestrictedImportKind::None;
25372548

2549+
if (importKind == RestrictedImportKind::Implicit &&
2550+
(module->getLibraryLevel() == LibraryLevel::API ||
2551+
getParentModule()->getLibraryLevel() != LibraryLevel::API)) {
2552+
// Hack to fix swiftinterfaces in case of missing imports.
2553+
// We can get rid of this logic when we don't leak the use of non-locally
2554+
// imported things in API.
2555+
ImportPath::Element pathElement = {module->getName(), SourceLoc()};
2556+
auto pathArray = getASTContext().AllocateCopy(
2557+
llvm::makeArrayRef(pathElement));
2558+
auto missingImport = ImportedModule(
2559+
ImportPath::Access(pathArray),
2560+
const_cast<ModuleDecl *>(module));
2561+
addMissingImportedModule(missingImport);
2562+
}
2563+
25382564
return importKind;
25392565
}
25402566

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts,
373373
Args.hasArg(OPT_experimental_spi_imports);
374374
Opts.DebugPrintInvalidSyntax |=
375375
Args.hasArg(OPT_debug_emit_invalid_swiftinterface_syntax);
376+
Opts.PrintMissingImports =
377+
!Args.hasArg(OPT_disable_print_missing_imports_in_module_interface);
376378

377379
if (const Arg *A = Args.getLastArg(OPT_library_level)) {
378380
StringRef contents = A->getValue();

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ static void printImports(raw_ostream &out,
219219

220220
SmallVector<ImportedModule, 8> allImports;
221221
M->getImportedModules(allImports, allImportFilter);
222+
223+
if (Opts.PrintMissingImports)
224+
M->getMissingImportedModules(allImports);
225+
222226
ImportedModule::removeDuplicates(allImports);
223227
diagnoseScopedImports(M->getASTContext().Diags, allImports);
224228

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
144144
}
145145
D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
146146

147+
if (originKind == DisallowedOriginKind::ImplicitlyImported &&
148+
!ctx.LangOpts.isSwiftVersionAtLeast(6))
149+
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
150+
definingModule->getName());
151+
147152
return true;
148153
}
149154

@@ -190,7 +195,13 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
190195
D->getDescriptiveKind(), D->getName(),
191196
fragileKind.getSelector(), definingModule->getName(),
192197
static_cast<unsigned>(originKind));
198+
199+
if (originKind == DisallowedOriginKind::ImplicitlyImported &&
200+
downgradeToWarning == DowngradeToWarning::Yes)
201+
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
202+
definingModule->getName());
193203
}
204+
194205
return true;
195206
}
196207

@@ -245,5 +256,10 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
245256
!ctx.LangOpts.EnableConformanceAvailabilityErrors) ||
246257
originKind == DisallowedOriginKind::ImplicitlyImported,
247258
6);
259+
260+
if (originKind == DisallowedOriginKind::ImplicitlyImported &&
261+
!ctx.LangOpts.isSwiftVersionAtLeast(6))
262+
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
263+
M->getName());
248264
return true;
249265
}

test/Sema/implicit-import-in-inlinable-code.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
/// In Swift 6, it's an error.
1717
// RUN: %target-swift-frontend -emit-module %t/clientFileA-Swift6.swift %t/clientFileB.swift -module-name client -o %t/client.swiftmodule -I %t -verify -swift-version 6
1818

19+
/// The swiftinterface is broken by the missing import without the workaround.
20+
// RUN: %target-swift-emit-module-interface(%t/ClientBroken.swiftinterface) %t/clientFileA-Swift5.swift %t/clientFileB.swift -I %t -disable-print-missing-imports-in-module-interface
21+
// RUN: not %target-swift-typecheck-module-from-interface(%t/ClientBroken.swiftinterface) -I %t
22+
23+
/// The swiftinterface parses fine with the workaround adding the missing imports.
24+
// RUN: %target-swift-emit-module-interface(%t/ClientFixed.swiftinterface) %t/clientFileA-Swift5.swift %t/clientFileB.swift -I %t
25+
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientFixed.swiftinterface) -I %t
26+
1927
// REQUIRES: asserts
2028

2129
// BEGIN empty.swift
@@ -45,11 +53,13 @@ import libA
4553
@inlinable public func bar() {
4654
let a = ImportedType()
4755
a.implicitlyImportedMethod() // expected-warning {{instance method 'implicitlyImportedMethod()' cannot be used in an '@inlinable' function because 'libB' was not imported by this file; this is an error in Swift 6}}
56+
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}
4857

4958
// Expected implicit imports are still fine
5059
a.localModuleMethod()
5160

5261
conformanceUse(a) // expected-warning {{cannot use conformance of 'ImportedType' to 'SomeProtocol' here; 'libB' was not imported by this file; this is an error in Swift 6}}
62+
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}
5363
}
5464

5565
// BEGIN clientFileA-OldCheck.swift
@@ -64,6 +74,7 @@ import libA
6474
a.localModuleMethod()
6575

6676
conformanceUse(a) // expected-warning {{cannot use conformance of 'ImportedType' to 'SomeProtocol' here; 'libB' was not imported by this file; this is an error in Swift 6}}
77+
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}
6778
}
6879

6980
// BEGIN clientFileA-Swift6.swift

test/Sema/implicit-import-typealias.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
// RUN: %target-swift-frontend -typecheck -verify %t/UsesAliasesImplementationOnlyImport.swift -I %t
1212
// RUN: %target-swift-frontend -typecheck -verify %t/UsesAliasesWithImport.swift -I %t
1313

14+
/// The swiftinterface is broken by the missing import without the workaround.
15+
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesNoImport.swiftinterface) %t/UsesAliasesNoImport.swift -I %t \
16+
// RUN: -disable-print-missing-imports-in-module-interface
17+
// RUN: not %target-swift-typecheck-module-from-interface(%t/UsesAliasesNoImport.swiftinterface) -I %t
18+
19+
/// The swiftinterface parses fine with the workaround adding the missing imports.
20+
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesNoImportFixed.swiftinterface) %t/UsesAliasesNoImport.swift -I %t
21+
// RUN: %target-swift-typecheck-module-from-interface(%t/UsesAliasesNoImportFixed.swiftinterface) -I %t
22+
23+
/// The module with an implementation-only import is not affected by the workaround and remains broken.
24+
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesImplementationOnlyImport.swiftinterface) %t/UsesAliasesImplementationOnlyImport.swift -I %t \
25+
// RUN: -disable-print-missing-imports-in-module-interface
26+
// RUN: not %target-swift-typecheck-module-from-interface(%t/UsesAliasesImplementationOnlyImport.swiftinterface) -I %t
1427

1528
//--- Original.swift
1629

@@ -47,23 +60,28 @@ public typealias WrapperAlias = Wrapper
4760

4861
import Aliases
4962

50-
// expected-warning@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported by this file; this is an error in Swift 6}}
63+
// expected-warning@+2 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported by this file; this is an error in Swift 6}}
64+
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
5165
public class InheritsFromClazzAlias: ClazzAlias {}
5266

5367
@inlinable public func inlinableFunc() {
54-
// expected-warning@+1 {{'StructAlias' aliases 'Original.Struct' and cannot be used in an '@inlinable' function because 'Original' was not imported by this file; this is an error in Swift 6}}
68+
// expected-warning@+2 {{'StructAlias' aliases 'Original.Struct' and cannot be used in an '@inlinable' function because 'Original' was not imported by this file; this is an error in Swift 6}}
69+
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
5570
_ = StructAlias.self
5671
}
5772

58-
// expected-warning@+1 {{'ProtoAlias' aliases 'Original.Proto' and cannot be used here because 'Original' was not imported by this file; this is an error in Swift 6}}
73+
// expected-warning@+2 {{'ProtoAlias' aliases 'Original.Proto' and cannot be used here because 'Original' was not imported by this file; this is an error in Swift 6}}
74+
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
5975
public func takesGeneric<T: ProtoAlias>(_ t: T) {}
6076

6177
public struct HasMembers {
62-
// expected-warning@+1 {{'WrapperAlias' aliases 'Original.Wrapper' and cannot be used as property wrapper here because 'Original' was not imported by this file; this is an error in Swift 6}}
78+
// expected-warning@+2 {{'WrapperAlias' aliases 'Original.Wrapper' and cannot be used as property wrapper here because 'Original' was not imported by this file; this is an error in Swift 6}}
79+
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
6380
@WrapperAlias public var wrapped: Int
6481
}
6582

66-
// expected-warning@+1 {{'StructAlias' aliases 'Original.Struct' and cannot be used in an extension with public or '@usableFromInline' members because 'Original' was not imported by this file; this is an error in Swift 6}}
83+
// expected-warning@+2 {{'StructAlias' aliases 'Original.Struct' and cannot be used in an extension with public or '@usableFromInline' members because 'Original' was not imported by this file; this is an error in Swift 6}}
84+
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
6785
extension StructAlias {
6886
public func someFunc() {}
6987
}

0 commit comments

Comments
 (0)