Skip to content

Commit e32c05d

Browse files
authored
Merge pull request #70700 from xymus/fix-submodule-import-warnings
Sema: Don't report public imports of submodules as being unused in API
2 parents 4609f0d + ab769e5 commit e32c05d

File tree

6 files changed

+96
-32
lines changed

6 files changed

+96
-32
lines changed

include/swift/AST/SourceFile.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,7 @@ class SourceFile final : public FileUnit {
149149
PreconcurrencyImportsUsed;
150150

151151
/// The highest access level of declarations referencing each import.
152-
llvm::DenseMap<AttributedImport<ImportedModule>, AccessLevel>
153-
ImportsUseAccessLevel;
152+
llvm::DenseMap<const ModuleDecl *, AccessLevel> ImportsUseAccessLevel;
154153

155154
/// A unique identifier representing this file; used to mark private decls
156155
/// within the file to keep them from conflicting with other files in the
@@ -418,7 +417,7 @@ class SourceFile final : public FileUnit {
418417
/// Return the highest access level of the declarations referencing
419418
/// this import in signature or inlinable code.
420419
AccessLevel
421-
getMaxAccessLevelUsingImport(AttributedImport<ImportedModule> import) const;
420+
getMaxAccessLevelUsingImport(const ModuleDecl *import) const;
422421

423422
/// Register the use of \p import from an API with \p accessLevel.
424423
void registerAccessLevelUsingImport(AttributedImport<ImportedModule> import,

lib/AST/Module.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,8 +3192,8 @@ void SourceFile::setImportUsedPreconcurrency(
31923192

31933193
AccessLevel
31943194
SourceFile::getMaxAccessLevelUsingImport(
3195-
AttributedImport<ImportedModule> import) const {
3196-
auto known = ImportsUseAccessLevel.find(import);
3195+
const ModuleDecl *mod) const {
3196+
auto known = ImportsUseAccessLevel.find(mod);
31973197
if (known == ImportsUseAccessLevel.end())
31983198
return AccessLevel::Internal;
31993199
return known->second;
@@ -3202,11 +3202,12 @@ SourceFile::getMaxAccessLevelUsingImport(
32023202
void SourceFile::registerAccessLevelUsingImport(
32033203
AttributedImport<ImportedModule> import,
32043204
AccessLevel accessLevel) {
3205-
auto known = ImportsUseAccessLevel.find(import);
3205+
auto mod = import.module.importedModule;
3206+
auto known = ImportsUseAccessLevel.find(mod);
32063207
if (known == ImportsUseAccessLevel.end())
3207-
ImportsUseAccessLevel[import] = accessLevel;
3208+
ImportsUseAccessLevel[mod] = accessLevel;
32083209
else
3209-
ImportsUseAccessLevel[import] = std::max(accessLevel, known->second);
3210+
ImportsUseAccessLevel[mod] = std::max(accessLevel, known->second);
32103211
}
32113212

32123213
bool HasImportsMatchingFlagRequest::evaluate(Evaluator &evaluator,

lib/Sema/TypeCheckAccess.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,24 +2440,30 @@ void swift::diagnoseUnnecessaryPublicImports(SourceFile &SF) {
24402440
import.accessLevel <= AccessLevel::Internal)
24412441
continue;
24422442

2443-
AccessLevel levelUsed = SF.getMaxAccessLevelUsingImport(import);
2444-
if (import.accessLevel > levelUsed) {
2445-
auto diagId = import.accessLevel == AccessLevel::Public ?
2446-
diag::remove_public_import :
2447-
diag::remove_package_import;
2448-
2449-
auto inFlight = ctx.Diags.diagnose(import.importLoc,
2450-
diagId,
2451-
import.module.importedModule);
2452-
2453-
if (levelUsed == AccessLevel::Package) {
2454-
inFlight.fixItReplace(import.accessLevelRange, "package");
2455-
} else if (ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault)) {
2456-
// Let it default to internal.
2457-
inFlight.fixItRemove(import.accessLevelRange);
2458-
} else {
2459-
inFlight.fixItReplace(import.accessLevelRange, "internal");
2460-
}
2443+
// Ignore submodules as we associate decls with the top module.
2444+
// The top module will be reported if it's not used.
2445+
auto importedModule = import.module.importedModule;
2446+
if (importedModule->getTopLevelModule() != importedModule)
2447+
continue;
2448+
2449+
AccessLevel levelUsed = SF.getMaxAccessLevelUsingImport(importedModule);
2450+
if (import.accessLevel <= levelUsed)
2451+
continue;
2452+
2453+
auto diagId = import.accessLevel == AccessLevel::Public ?
2454+
diag::remove_public_import :
2455+
diag::remove_package_import;
2456+
auto inFlight = ctx.Diags.diagnose(import.importLoc,
2457+
diagId,
2458+
importedModule);
2459+
2460+
if (levelUsed == AccessLevel::Package) {
2461+
inFlight.fixItReplace(import.accessLevelRange, "package");
2462+
} else if (ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault)) {
2463+
// Let it default to internal.
2464+
inFlight.fixItRemove(import.accessLevelRange);
2465+
} else {
2466+
inFlight.fixItReplace(import.accessLevelRange, "internal");
24612467
}
24622468
}
24632469
}

test/ModuleInterface/imports-swift6.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919

2020
//--- main.swift
2121
@_exported public import resilient // expected-warning {{public import of 'resilient' was not used in public declarations or inlinable code}}
22-
public import B.B2 // expected-warning {{public import of 'B2' was not used in public declarations or inlinable code}}
23-
// expected-warning @-1 {{public import of 'B' was not used in public declarations or inlinable code}}
24-
// FIXME: We don't want this last warning.
22+
public import B.B2 // expected-warning {{public import of 'B' was not used in public declarations or inlinable code}}
2523

2624
public import func C.c // expected-warning {{public import of 'C' was not used in public declarations or inlinable code}}
2725
// expected-warning @-1 {{scoped imports are not yet supported in module interfaces}}
@@ -34,8 +32,7 @@ public import NotSoSecret2 // expected-warning {{'NotSoSecret2' inconsistently i
3432

3533
//--- main-other.swift
3634
public import A // expected-warning {{public import of 'A' was not used in public declarations or inlinable code}}
37-
public import B.B3 // expected-warning {{public import of 'B3' was not used in public declarations or inlinable code}}
38-
// expected-warning @-1 {{public import of 'B' was not used in public declarations or inlinable code}}
35+
public import B.B3 // expected-warning {{public import of 'B' was not used in public declarations or inlinable code}}
3936
public import D // expected-warning {{public import of 'D' was not used in public declarations or inlinable code}}
4037

4138
public import NotSoSecret // expected-warning {{'NotSoSecret' inconsistently imported as implementation-only}}

test/Sema/access-level-import-inconsistent-same-file.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ public func dummyAPI(t: Lib1.Type1) {}
9191
//--- Client_Clang.swift
9292

9393
public import ClangLib.Sub1 // expected-note {{imported 'public' here}}
94-
// expected-warning @-1 {{public import of 'Sub1' was not used in public declarations or inlinable code}}
9594
private import ClangLib.Sub1 // expected-warning {{module 'Sub1' is imported as 'public' from the same file; this 'private' access level will be ignored}}
9695
internal import ClangLib.Sub2
9796

test/Sema/superfluously-public-imports.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
/// Check diagnostics.
2525
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
2626
// RUN: -package-name pkg -Rmodule-api-import -swift-version 6 -verify
27+
// RUN: %target-swift-frontend -typecheck %t/ClientOfClangModules.swift -I %t \
28+
// RUN: -package-name pkg -Rmodule-api-import -swift-version 6 -verify
2729
// RUN: %target-swift-frontend -typecheck %t/Client_Swift5.swift -I %t \
2830
// RUN: -swift-version 5 -verify
2931

@@ -211,3 +213,63 @@ func implicitlyInternalFunc(a: NotAnAPIType = notAnAPIFunc()) {}
211213

212214
// For package decls we only remark on types used in signatures, not for inlinable code.
213215
package func packageFunc(a: PackageType = packageFunc()) {} // expected-remark {{struct 'PackageType' is imported via 'ImportUsedInPackage'}}
216+
217+
/// Tests for imports of clang modules.
218+
//--- module.modulemap
219+
module ClangSimpleUnused {
220+
header "ClangSimpleUnused.h"
221+
}
222+
module ClangSimple {
223+
header "ClangSimple.h"
224+
}
225+
226+
module ClangSubmodule {
227+
header "ClangSubmodule.h"
228+
229+
module ClangSubmoduleSubmodule {
230+
header "ClangSubmoduleSubmodule.h"
231+
}
232+
}
233+
234+
module ClangSubmoduleUnused {
235+
header "ClangSubmoduleUnused.h"
236+
237+
module ClangSubmoduleUnsuedSubmodule {
238+
header "ClangSubmoduleUnusedSubmodule.h"
239+
}
240+
}
241+
242+
module ClangTopModule {
243+
header "ClangTopModule.h"
244+
module ClangTopModuleSubmodule {
245+
header "ClangTopModuleSubmodule.h"
246+
}
247+
}
248+
249+
//--- ClangSimpleUnused.h
250+
//--- ClangSimple.h
251+
struct ClangSimpleType {};
252+
253+
//--- ClangSubmodule.h
254+
//--- ClangSubmoduleSubmodule.h
255+
struct ClangSubmoduleSubmoduleType {};
256+
257+
//--- ClangSubmoduleUnused.h
258+
//--- ClangSubmoduleUnusedSubmodule.h
259+
260+
//--- ClangTopModule.h
261+
struct ClangTopModuleType {};
262+
//--- ClangTopModuleSubmodule.h
263+
264+
//--- ClientOfClangModules.swift
265+
public import ClangSimple
266+
public import ClangSimpleUnused // expected-warning {{public import of 'ClangSimpleUnused' was not used in public declarations or inlinable code}}
267+
public import ClangSubmodule.ClangSubmoduleSubmodule
268+
public import ClangSubmoduleUnused.ClangSubmoduleUnsuedSubmodule // expected-warning {{public import of 'ClangSubmoduleUnused' was not used in public declarations or inlinable code}}
269+
270+
// Only the top-level module is used, but we can't detect whether the submodule was used or not.
271+
public import ClangTopModule.ClangTopModuleSubmodule
272+
273+
public func clangUser(a: ClangSimpleType) {} // expected-remark {{struct 'ClangSimpleType' is imported via 'ClangSimple'}}
274+
public func clangUser(a: ClangSubmoduleSubmoduleType) {} // expected-remark {{struct 'ClangSubmoduleSubmoduleType' is imported via 'ClangSubmodule'}}
275+
public func clangUser(a: ClangTopModuleType) {} // expected-remark {{struct 'ClangTopModuleType' is imported via 'ClangTopModule'}}

0 commit comments

Comments
 (0)