Skip to content

Commit 8efd30e

Browse files
authored
Merge pull request #70675 from xymus/access-level-import-same-file
2 parents 3e73445 + 4237a97 commit 8efd30e

8 files changed

+216
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3675,6 +3675,13 @@ NOTE(inconsistent_implicit_access_level_on_import_here,none,
36753675
FIXIT(inconsistent_implicit_access_level_on_import_fixit,
36763676
"%select{private|fileprivate|internal|package|public|%error}0 ",
36773677
(AccessLevel))
3678+
WARNING(inconsistent_import_access_levels,none,
3679+
"module %0 is imported as "
3680+
"'%select{private|fileprivate|internal|package|public|%error}1' "
3681+
"from the same file; "
3682+
"this '%select{private|fileprivate|internal|package|public|%error}2' "
3683+
"access level will be ignored",
3684+
(const ModuleDecl *, AccessLevel, AccessLevel))
36783685

36793686
ERROR(implementation_only_decl_non_override,none,
36803687
"'@_implementationOnly' can only be used on overrides", ())

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,6 +3708,25 @@ class CheckInconsistentSPIOnlyImportsRequest
37083708
bool isCached() const { return true; }
37093709
};
37103710

3711+
// Check that imports of the same module from the same file have the same
3712+
// access-level.
3713+
class CheckInconsistentAccessLevelOnImportSameFileRequest
3714+
: public SimpleRequest<CheckInconsistentAccessLevelOnImportSameFileRequest,
3715+
evaluator::SideEffect(SourceFile *),
3716+
RequestFlags::Cached> {
3717+
public:
3718+
using SimpleRequest::SimpleRequest;
3719+
3720+
private:
3721+
friend SimpleRequest;
3722+
3723+
evaluator::SideEffect evaluate(Evaluator &evaluator, SourceFile *mod) const;
3724+
3725+
public:
3726+
// Cached.
3727+
bool isCached() const { return true; }
3728+
};
3729+
37113730
/// Report default imports if other imports of the same target from this
37123731
/// module have an explicitly defined access level. In such a case, all imports
37133732
/// of the target module need an explicit access level or it may be made public

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ SWIFT_REQUEST(TypeChecker, CheckInconsistentSPIOnlyImportsRequest,
3737
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
3838
SWIFT_REQUEST(TypeChecker, CheckInconsistentAccessLevelOnImport,
3939
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
40+
SWIFT_REQUEST(TypeChecker, CheckInconsistentAccessLevelOnImportSameFileRequest,
41+
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
4042
SWIFT_REQUEST(TypeChecker, CheckInconsistentWeakLinkedImportsRequest,
4143
evaluator::SideEffect(ModuleDecl *), Cached, NoLocationInfo)
4244
SWIFT_REQUEST(TypeChecker, CheckRedeclarationRequest,

lib/Sema/ImportResolution.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,58 @@ CheckInconsistentSPIOnlyImportsRequest::evaluate(
971971
return {};
972972
}
973973

974+
evaluator::SideEffect
975+
CheckInconsistentAccessLevelOnImportSameFileRequest::evaluate(
976+
Evaluator &evaluator, SourceFile *SF) const {
977+
978+
// Gather the most permissive import decl for each imported module.
979+
llvm::DenseMap<ModuleDecl *, const ImportDecl *> mostPermissiveImports;
980+
for (auto *topLevelDecl : SF->getTopLevelDecls()) {
981+
auto *importDecl = dyn_cast<ImportDecl>(topLevelDecl);
982+
if (!importDecl)
983+
continue;
984+
985+
ModuleDecl *importedModule = importDecl->getModule();
986+
if (!importedModule)
987+
continue;
988+
989+
auto otherImportDecl = mostPermissiveImports.find(importedModule);
990+
if (otherImportDecl == mostPermissiveImports.end() ||
991+
otherImportDecl->second->getAccessLevel() <
992+
importDecl->getAccessLevel()) {
993+
mostPermissiveImports[importedModule] = importDecl;
994+
}
995+
}
996+
997+
// Report import decls that are not the most permissive.
998+
auto &diags = SF->getASTContext().Diags;
999+
for (auto *topLevelDecl : SF->getTopLevelDecls()) {
1000+
auto *importDecl = dyn_cast<ImportDecl>(topLevelDecl);
1001+
if (!importDecl)
1002+
continue;
1003+
1004+
ModuleDecl *importedModule = importDecl->getModule();
1005+
if (!importedModule)
1006+
continue;
1007+
1008+
auto otherImportDecl = mostPermissiveImports.find(importedModule);
1009+
if (otherImportDecl != mostPermissiveImports.end() &&
1010+
otherImportDecl->second != importDecl &&
1011+
otherImportDecl->second->getAccessLevel() >
1012+
importDecl->getAccessLevel()) {
1013+
diags.diagnose(importDecl, diag::inconsistent_import_access_levels,
1014+
importedModule,
1015+
otherImportDecl->second->getAccessLevel(),
1016+
importDecl->getAccessLevel());
1017+
diags.diagnose(otherImportDecl->second,
1018+
diag::inconsistent_implicit_access_level_on_import_here,
1019+
otherImportDecl->second->getAccessLevel());
1020+
}
1021+
}
1022+
1023+
return {};
1024+
}
1025+
9741026
evaluator::SideEffect
9751027
CheckInconsistentAccessLevelOnImport::evaluate(
9761028
Evaluator &evaluator, SourceFile *SF) const {

lib/Sema/TypeChecker.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,23 +314,34 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
314314
diagnoseUnnecessaryPublicImports(*SF);
315315

316316
// Check to see if there are any inconsistent imports.
317+
// Whole-module @_implementationOnly imports.
317318
evaluateOrDefault(
318319
Ctx.evaluator,
319320
CheckInconsistentImplementationOnlyImportsRequest{SF->getParentModule()},
320321
{});
321322

323+
// Whole-module @_spiOnly imports.
322324
evaluateOrDefault(
323325
Ctx.evaluator,
324326
CheckInconsistentSPIOnlyImportsRequest{SF},
325327
{});
326328

329+
// Whole-module ambiguous bare imports defaulting to public, when other
330+
// imports are marked 'internal'.
327331
if (!Ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault)) {
328332
evaluateOrDefault(
329333
Ctx.evaluator,
330334
CheckInconsistentAccessLevelOnImport{SF},
331335
{});
332336
}
333337

338+
// Per-file inconsistent access-levels on imports of the same module.
339+
evaluateOrDefault(
340+
Ctx.evaluator,
341+
CheckInconsistentAccessLevelOnImportSameFileRequest{SF},
342+
{});
343+
344+
// Whole-module inconsistent @_weakLinked.
334345
evaluateOrDefault(
335346
Ctx.evaluator,
336347
CheckInconsistentWeakLinkedImportsRequest{SF->getParentModule()}, {});

test/Sema/access-level-import-inconsistencies.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,15 @@ public struct LibType {}
2424
// RUN: -package-name package -verify
2525
//--- OneFile_AllExplicit.swift
2626
public import Lib // expected-warning {{public import of 'Lib' was not used in public declarations or inlinable code}}
27+
// expected-note @-1 4 {{imported 'public' here}}
2728
package import Lib // expected-warning {{package import of 'Lib' was not used in package declarations}}
29+
// expected-warning @-1 {{module 'Lib' is imported as 'public' from the same file; this 'package' access level will be ignored}}
2830
internal import Lib
31+
// expected-warning @-1 {{module 'Lib' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
2932
fileprivate import Lib
33+
// expected-warning @-1 {{module 'Lib' is imported as 'public' from the same file; this 'fileprivate' access level will be ignored}}
3034
private import Lib
35+
// expected-warning @-1 {{module 'Lib' is imported as 'public' from the same file; this 'private' access level will be ignored}}
3136

3237
// RUN: %target-swift-frontend -typecheck %t/ManyFiles_AllExplicit_File?.swift -I %t \
3338
// RUN: -package-name package -verify
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Build the library.
5+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib1 -o %t
6+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib2 -o %t
7+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib3 -o %t
8+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib4 -o %t
9+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib5 -o %t
10+
11+
/// Test main cases.
12+
// RUN: %target-swift-frontend -typecheck -verify %t/Client.swift -I %t
13+
// RUN: %target-swift-frontend -typecheck -verify %t/Client_Scoped.swift -I %t
14+
// RUN: %target-swift-frontend -typecheck -verify %t/Client_Clang.swift -I %t
15+
16+
/// Test language mode specific variants.
17+
// RUN: %target-swift-frontend -typecheck -verify %t/Client_Swift6.swift -I %t \
18+
// RUN: -enable-upcoming-feature InternalImportsByDefault
19+
// RUN: %target-swift-frontend -typecheck -verify %t/Client_Swift5.swift -I %t \
20+
// RUN: -swift-version 5
21+
22+
//--- Lib.swift
23+
24+
public struct Type1 {}
25+
public struct Type2 {}
26+
27+
//--- Client.swift
28+
29+
/// Simple public vs internal.
30+
public import Lib1 // expected-note {{imported 'public' here}}
31+
internal import Lib1 // expected-warning {{module 'Lib1' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
32+
33+
/// Simple public vs internal, inverted.
34+
internal import Lib2 // expected-warning {{module 'Lib2' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
35+
public import Lib2 // expected-note {{imported 'public' here}}
36+
37+
/// 3 different ones.
38+
public import Lib3 // expected-note 2 {{imported 'public' here}}
39+
internal import Lib3 // expected-warning {{module 'Lib3' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
40+
private import Lib3 // expected-warning {{module 'Lib3' is imported as 'public' from the same file; this 'private' access level will be ignored}}
41+
42+
/// private vs fileprivate, we don't really need this warning but it may point to unintended stylistic inconsistencies.
43+
fileprivate import Lib4 // expected-note {{imported 'fileprivate' here}}
44+
private import Lib4 // expected-warning {{module 'Lib4' is imported as 'fileprivate' from the same file; this 'private' access level will be ignored}}
45+
46+
// Don't complain about repeated imports. As far as this diagnostic
47+
// is concerned we may see this with scoped imports.
48+
internal import Lib5
49+
internal import Lib5
50+
internal import Lib5
51+
internal import Lib5
52+
53+
public func dummyAPI(t1: Lib1.Type1, t2: Lib2.Type1, t3: Lib3.Type1) {}
54+
55+
//--- Client_Swift5.swift
56+
57+
/// Simple public vs internal, imports defaults to public.
58+
import Lib1 // expected-note {{imported 'public' here}}
59+
// expected-error @-1 {{ambiguous implicit access level for import of 'Lib1'; it is imported as 'internal' elsewhere}}
60+
internal import Lib1 // expected-warning {{module 'Lib1' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
61+
// expected-note @-1 {{imported 'internal' here}}
62+
63+
// There's no warning about "will be ignored" for a matching implicit access level.
64+
public import Lib2
65+
// expected-note @-1 {{imported 'public' here}}
66+
import Lib2
67+
// expected-error @-1 {{ambiguous implicit access level for import of 'Lib2'; it is imported as 'public' elsewhere}}
68+
69+
public func dummyAPI(t: Lib1.Type1, t2: Lib2.Type1) {}
70+
71+
//--- Client_Swift6.swift
72+
73+
/// Simple public vs internal, imports default to internal.
74+
public import Lib1 // expected-note {{imported 'public' here}}
75+
import Lib1 // expected-warning {{module 'Lib1' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
76+
77+
// There's no warning about "will be ignored" for a matching implicit access level.
78+
import Lib2
79+
internal import Lib2
80+
81+
public func dummyAPI(t: Lib1.Type1) {}
82+
83+
//--- Client_Scoped.swift
84+
85+
/// Access level on scoped imports still import the whole module.
86+
public import struct Lib1.Type1 // expected-note {{imported 'public' here}}
87+
internal import struct Lib1.Type2 // expected-warning {{module 'Lib1' is imported as 'public' from the same file; this 'internal' access level will be ignored}}
88+
89+
public func dummyAPI(t: Lib1.Type1) {}
90+
91+
//--- Client_Clang.swift
92+
93+
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}}
95+
private import ClangLib.Sub1 // expected-warning {{module 'Sub1' is imported as 'public' from the same file; this 'private' access level will be ignored}}
96+
internal import ClangLib.Sub2
97+
98+
public func dummyAPI(t1: ClangType1, t2: ClangType2) {}
99+
100+
//--- module.modulemap
101+
102+
module ClangLib {
103+
header "ClangLib1.h"
104+
105+
explicit module Sub1 {
106+
header "ClangLib2.h"
107+
}
108+
109+
explicit module Sub2 {
110+
header "ClangLib3.h"
111+
}
112+
}
113+
114+
//--- ClangLib1.h
115+
struct ClangType1 {};
116+
//--- ClangLib2.h
117+
struct ClangType2 {};
118+
//--- ClangLib3.h

test/Sema/superfluously-public-imports.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ public import ExtendedDefinitionNonPublic // expected-warning {{public import of
122122

123123
/// Repeat some imports to make sure we report all of them.
124124
public import UnusedImport // expected-warning {{public import of 'UnusedImport' was not used in public declarations or inlinable code}} {{1-8=}}
125+
// expected-note @-1 {{imported 'public' here}}
125126
public import UnusedImport // expected-warning {{public import of 'UnusedImport' was not used in public declarations or inlinable code}} {{1-8=}}
126127
package import UnusedImport // expected-warning {{package import of 'UnusedImport' was not used in package declarations}} {{1-9=}}
128+
// expected-warning @-1 {{module 'UnusedImport' is imported as 'public' from the same file; this 'package' access level will be ignored}}
127129

128130
package import UnusedPackageImport // expected-warning {{package import of 'UnusedPackageImport' was not used in package declarations}} {{1-9=}}
129131
public import ImportNotUseFromAPI // expected-warning {{public import of 'ImportNotUseFromAPI' was not used in public declarations or inlinable code}} {{1-8=}}

0 commit comments

Comments
 (0)