Skip to content

[ModuleInterface] Print missing imports in swiftinterface #60659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5849,6 +5849,10 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
"%4 was not imported by this file}5",
(DeclName, StringRef, StringRef, unsigned, Identifier, unsigned))

NOTE(missing_import_inserted,
none, "The missing import of module %0 will be added implicitly",
(Identifier))

ERROR(availability_macro_in_inlinable, none,
"availability macro cannot be used in " FRAGILE_FUNC_KIND "0",
(unsigned))
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/FileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
getImportedModules(SmallVectorImpl<ImportedModule> &imports,
ModuleDecl::ImportFilter filter) const {}

/// Lists modules that are not imported from this file and used in API.
virtual void
getMissingImportedModules(SmallVectorImpl<ImportedModule> &imports) const {}

/// \see ModuleDecl::getImportedModulesForLookup
virtual void getImportedModulesForLookup(
SmallVectorImpl<ImportedModule> &imports) const {
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,10 @@ class ModuleDecl
void getImportedModules(SmallVectorImpl<ImportedModule> &imports,
ImportFilter filter = ImportFilterKind::Exported) const;

/// Lists modules that are not imported from a file and used in API.
void
getMissingImportedModules(SmallVectorImpl<ImportedModule> &imports) const;

/// Looks up which modules are imported by this module, ignoring any that
/// won't contain top-level decls.
///
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,15 @@ class SourceFile final : public FileUnit {

SWIFT_DEBUG_DUMPER(dumpSeparatelyImportedOverlays());

llvm::SmallDenseSet<ImportedModule> MissingImportedModules;

void addMissingImportedModule(ImportedModule module) const {
const_cast<SourceFile *>(this)->MissingImportedModules.insert(module);
}

void getMissingImportedModules(
SmallVectorImpl<ImportedModule> &imports) const override;

void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
const SmallVectorImpl<ValueDecl *> &getCachedVisibleDecls() const;

Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/ModuleInterfaceSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ struct ModuleInterfaceOptions {
/// when PrintSPIs is true.
bool ExperimentalSPIImports = false;

/// Print imports that are missing from the source and used in API.
bool PrintMissingImports = true;

/// Intentionally print invalid syntax into the file.
bool DebugPrintInvalidSyntax = false;

Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,11 @@ def experimental_spi_imports :
Flag<["-"], "experimental-spi-imports">,
HelpText<"Enable experimental support for SPI imports">;

def disable_print_missing_imports_in_module_interface :
Flag<["-"], "disable-print-missing-imports-in-module-interface">,
HelpText<"Disable adding to the module interface imports used from API and "
"missing from the sources">;

// [FIXME: Clang-type-plumbing] Make this a SIL-only option once we start
// unconditionally emitting non-canonical Clang types in swiftinterfaces.
def experimental_print_full_convention :
Expand Down
26 changes: 26 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1570,6 +1570,11 @@ void ModuleDecl::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
FORWARD(getImportedModules, (modules, filter));
}

void ModuleDecl::getMissingImportedModules(
SmallVectorImpl<ImportedModule> &imports) const {
FORWARD(getMissingImportedModules, (imports));
}

void
SourceFile::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
ModuleDecl::ImportFilter filter) const {
Expand Down Expand Up @@ -1604,6 +1609,12 @@ SourceFile::getImportedModules(SmallVectorImpl<ImportedModule> &modules,
}
}

void SourceFile::getMissingImportedModules(
SmallVectorImpl<ImportedModule> &modules) const {
for (auto module : MissingImportedModules)
modules.push_back(module);
}

void SourceFile::dumpSeparatelyImportedOverlays() const {
for (auto &pair : separatelyImportedOverlays) {
auto &underlying = std::get<0>(pair);
Expand Down Expand Up @@ -2535,6 +2546,21 @@ RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *modul
if (imports.isImportedBy(module, getParentModule()))
return RestrictedImportKind::None;

if (importKind == RestrictedImportKind::Implicit &&
(module->getLibraryLevel() == LibraryLevel::API ||
getParentModule()->getLibraryLevel() != LibraryLevel::API)) {
// Hack to fix swiftinterfaces in case of missing imports.
// We can get rid of this logic when we don't leak the use of non-locally
// imported things in API.
ImportPath::Element pathElement = {module->getName(), SourceLoc()};
auto pathArray = getASTContext().AllocateCopy(
llvm::makeArrayRef(pathElement));
auto missingImport = ImportedModule(
ImportPath::Access(pathArray),
const_cast<ModuleDecl *>(module));
addMissingImportedModule(missingImport);
}

return importKind;
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts,
Args.hasArg(OPT_experimental_spi_imports);
Opts.DebugPrintInvalidSyntax |=
Args.hasArg(OPT_debug_emit_invalid_swiftinterface_syntax);
Opts.PrintMissingImports =
!Args.hasArg(OPT_disable_print_missing_imports_in_module_interface);

if (const Arg *A = Args.getLastArg(OPT_library_level)) {
StringRef contents = A->getValue();
Expand Down
4 changes: 4 additions & 0 deletions lib/Frontend/ModuleInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ static void printImports(raw_ostream &out,

SmallVector<ImportedModule, 8> allImports;
M->getImportedModules(allImports, allImportFilter);

if (Opts.PrintMissingImports)
M->getMissingImportedModules(allImports);

ImportedModule::removeDuplicates(allImports);
diagnoseScopedImports(M->getASTContext().Diags, allImports);

Expand Down
16 changes: 16 additions & 0 deletions lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
}
D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);

if (originKind == DisallowedOriginKind::ImplicitlyImported &&
!ctx.LangOpts.isSwiftVersionAtLeast(6))
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
definingModule->getName());

return true;
}

Expand Down Expand Up @@ -190,7 +195,13 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
D->getDescriptiveKind(), D->getName(),
fragileKind.getSelector(), definingModule->getName(),
static_cast<unsigned>(originKind));

if (originKind == DisallowedOriginKind::ImplicitlyImported &&
downgradeToWarning == DowngradeToWarning::Yes)
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
definingModule->getName());
}

return true;
}

Expand Down Expand Up @@ -244,5 +255,10 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
.warnUntilSwiftVersionIf(useConformanceAvailabilityErrorsOption &&
!ctx.LangOpts.EnableConformanceAvailabilityErrors,
6);

if (originKind == DisallowedOriginKind::ImplicitlyImported &&
!ctx.LangOpts.isSwiftVersionAtLeast(6))
ctx.Diags.diagnose(loc, diag::missing_import_inserted,
M->getName());
return true;
}
11 changes: 11 additions & 0 deletions test/Sema/implicit-import-in-inlinable-code.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
/// In Swift 6, it's an error.
// 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

/// The swiftinterface is broken by the missing import without the workaround.
// 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
// RUN: not %target-swift-typecheck-module-from-interface(%t/ClientBroken.swiftinterface) -I %t

/// The swiftinterface parses fine with the workaround adding the missing imports.
// RUN: %target-swift-emit-module-interface(%t/ClientFixed.swiftinterface) %t/clientFileA-Swift5.swift %t/clientFileB.swift -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientFixed.swiftinterface) -I %t

// REQUIRES: asserts

// BEGIN empty.swift
Expand Down Expand Up @@ -45,11 +53,13 @@ import libA
@inlinable public func bar() {
let a = ImportedType()
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}}
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}

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

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}}
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}
}

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

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}}
// expected-note@-1 {{The missing import of module 'libB' will be added implicitly}}
}

// BEGIN clientFileA-Swift6.swift
Expand Down
28 changes: 23 additions & 5 deletions test/Sema/implicit-import-typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
// RUN: %target-swift-frontend -typecheck -verify %t/UsesAliasesImplementationOnlyImport.swift -I %t
// RUN: %target-swift-frontend -typecheck -verify %t/UsesAliasesWithImport.swift -I %t

/// The swiftinterface is broken by the missing import without the workaround.
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesNoImport.swiftinterface) %t/UsesAliasesNoImport.swift -I %t \
// RUN: -disable-print-missing-imports-in-module-interface
// RUN: not %target-swift-typecheck-module-from-interface(%t/UsesAliasesNoImport.swiftinterface) -I %t

/// The swiftinterface parses fine with the workaround adding the missing imports.
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesNoImportFixed.swiftinterface) %t/UsesAliasesNoImport.swift -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/UsesAliasesNoImportFixed.swiftinterface) -I %t

/// The module with an implementation-only import is not affected by the workaround and remains broken.
// RUN: %target-swift-emit-module-interface(%t/UsesAliasesImplementationOnlyImport.swiftinterface) %t/UsesAliasesImplementationOnlyImport.swift -I %t \
// RUN: -disable-print-missing-imports-in-module-interface
// RUN: not %target-swift-typecheck-module-from-interface(%t/UsesAliasesImplementationOnlyImport.swiftinterface) -I %t

//--- Original.swift

Expand Down Expand Up @@ -47,23 +60,28 @@ public typealias WrapperAlias = Wrapper

import Aliases

// 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}}
// 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}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
public class InheritsFromClazzAlias: ClazzAlias {}

@inlinable public func inlinableFunc() {
// 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}}
// 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}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
_ = StructAlias.self
}

// 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}}
// 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}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
public func takesGeneric<T: ProtoAlias>(_ t: T) {}

public struct HasMembers {
// 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}}
// 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}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
@WrapperAlias public var wrapped: Int
}

// 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}}
// 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}}
// expected-note@+1 {{The missing import of module 'Original' will be added implicitly}}
extension StructAlias {
public func someFunc() {}
}
Expand Down