Skip to content

Commit c6c96c4

Browse files
committed
[Sema] Report inconsistent access level on import
When targeting Swift 5 and earlier, the default import is public. To notify of a possible unintentional public import, raise errors on default imports when other imports of the same target have an explicit type. This check isn't necessary in the tentative Swift 6 mode.
1 parent 91989ec commit c6c96c4

File tree

6 files changed

+141
-0
lines changed

6 files changed

+141
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3160,6 +3160,18 @@ ERROR(error_public_import_of_private_module,none,
31603160
"private module %0 is imported publicly from the public module %1",
31613161
(Identifier, Identifier))
31623162

3163+
ERROR(inconsistent_implicit_access_level_on_import,none,
3164+
"ambiguous implicit access level for import of %0; it is imported as "
3165+
"'%select{private|fileprivate|internal|package|public|%error}1' elsewhere",
3166+
(Identifier, AccessLevel))
3167+
NOTE(inconsistent_implicit_access_level_on_import_here,none,
3168+
"imported "
3169+
"'%select{private|fileprivate|internal|package|public|%error}0' here",
3170+
(AccessLevel))
3171+
FIXIT(inconsistent_implicit_access_level_on_import_fixit,
3172+
"%select{private|fileprivate|internal|package|public|%error}0 ",
3173+
(AccessLevel))
3174+
31633175
ERROR(implementation_only_decl_non_override,none,
31643176
"'@_implementationOnly' can only be used on overrides", ())
31653177
ERROR(implementation_only_override_changed_type,none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3367,6 +3367,27 @@ class CheckInconsistentSPIOnlyImportsRequest
33673367
bool isCached() const { return true; }
33683368
};
33693369

3370+
/// Report default imports if other imports of the same target from this
3371+
/// module have an explicitly defined access level. In such a case, all imports
3372+
/// of the target module need an explicit access level or it may be made public
3373+
/// by error. This applies only to pre-Swift 6 mode.
3374+
class CheckInconsistentAccessLevelOnImport
3375+
: public SimpleRequest<CheckInconsistentAccessLevelOnImport,
3376+
evaluator::SideEffect(SourceFile *),
3377+
RequestFlags::Cached> {
3378+
public:
3379+
using SimpleRequest::SimpleRequest;
3380+
3381+
private:
3382+
friend SimpleRequest;
3383+
3384+
evaluator::SideEffect evaluate(Evaluator &evaluator, SourceFile *mod) const;
3385+
3386+
public:
3387+
// Cached.
3388+
bool isCached() const { return true; }
3389+
};
3390+
33703391
/// Checks to see if any of the imports in a module use \c @_weakLinked
33713392
/// in one file and not in another.
33723393
///

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ SWIFT_REQUEST(TypeChecker, CheckInconsistentImplementationOnlyImportsRequest,
3535
evaluator::SideEffect(ModuleDecl *), Cached, NoLocationInfo)
3636
SWIFT_REQUEST(TypeChecker, CheckInconsistentSPIOnlyImportsRequest,
3737
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
38+
SWIFT_REQUEST(TypeChecker, CheckInconsistentAccessLevelOnImport,
39+
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
3840
SWIFT_REQUEST(TypeChecker, CheckInconsistentWeakLinkedImportsRequest,
3941
evaluator::SideEffect(ModuleDecl *), Cached, NoLocationInfo)
4042
SWIFT_REQUEST(TypeChecker, CheckRedeclarationRequest,

lib/Sema/ImportResolution.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,41 @@ CheckInconsistentSPIOnlyImportsRequest::evaluate(
917917
return {};
918918
}
919919

920+
evaluator::SideEffect
921+
CheckInconsistentAccessLevelOnImport::evaluate(
922+
Evaluator &evaluator, SourceFile *SF) const {
923+
924+
auto mod = SF->getParentModule();
925+
auto diagnose = [mod](const ImportDecl *implicitImport,
926+
const ImportDecl *otherImport) {
927+
auto otherAccessLevel = otherImport->getAccessLevel();
928+
929+
auto &diags = mod->getDiags();
930+
{
931+
InFlightDiagnostic error =
932+
diags.diagnose(implicitImport, diag::inconsistent_implicit_access_level_on_import,
933+
implicitImport->getModule()->getName(), otherAccessLevel);
934+
error.fixItInsert(implicitImport->getStartLoc(),
935+
diag::inconsistent_implicit_access_level_on_import_fixit,
936+
otherAccessLevel);
937+
}
938+
939+
SourceLoc accessLevelLoc = otherImport->getStartLoc();
940+
if (auto attr = otherImport->getAttrs().getAttribute<AccessControlAttr>())
941+
accessLevelLoc = attr->getLocation();
942+
diags.diagnose(accessLevelLoc,
943+
diag::inconsistent_implicit_access_level_on_import_here,
944+
otherAccessLevel);
945+
};
946+
947+
auto predicate = [](ImportDecl *decl) {
948+
return !decl->isAccessLevelImplicit();
949+
};
950+
951+
findInconsistentImportsAcrossModule(mod, predicate, diagnose);
952+
return {};
953+
}
954+
920955
evaluator::SideEffect
921956
CheckInconsistentWeakLinkedImportsRequest::evaluate(Evaluator &evaluator,
922957
ModuleDecl *mod) const {

lib/Sema/TypeChecker.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
313313
CheckInconsistentSPIOnlyImportsRequest{SF},
314314
{});
315315

316+
if (!Ctx.LangOpts.isSwiftVersionAtLeast(6)) {
317+
evaluateOrDefault(
318+
Ctx.evaluator,
319+
CheckInconsistentAccessLevelOnImport{SF},
320+
{});
321+
}
322+
316323
evaluateOrDefault(
317324
Ctx.evaluator,
318325
CheckInconsistentWeakLinkedImportsRequest{SF->getParentModule()}, {});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/// Import inconsistencies.
2+
///
3+
/// Swift 5 case, with slow adoption of access level on imports.
4+
/// Report default imports if any other import of the same module
5+
/// has an access level below public (the default).
6+
///
7+
/// Swift 6 case, with default imports as internal.
8+
/// Don't report any mismatch.
9+
///
10+
/// RUN: %empty-directory(%t)
11+
/// RUN: split-file %s %t
12+
13+
/// Build the library.
14+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -o %t
15+
16+
/// Check diagnostics.
17+
18+
//--- Lib.swift
19+
public struct LibType {}
20+
21+
// RUN: %target-swift-frontend -typecheck %t/OneFile_AllExplicit.swift -I %t \
22+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify
23+
//--- OneFile_AllExplicit.swift
24+
public import Lib
25+
package import Lib
26+
internal import Lib
27+
fileprivate import Lib
28+
private import Lib
29+
30+
// RUN: %target-swift-frontend -typecheck %t/ManyFiles_AllExplicit_File?.swift -I %t \
31+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify
32+
//--- ManyFiles_AllExplicit_FileA.swift
33+
public import Lib
34+
//--- ManyFiles_AllExplicit_FileB.swift
35+
package import Lib
36+
//--- ManyFiles_AllExplicit_FileC.swift
37+
internal import Lib
38+
//--- ManyFiles_AllExplicit_FileD.swift
39+
fileprivate import Lib
40+
//--- ManyFiles_AllExplicit_FileE.swift
41+
private import Lib
42+
43+
// RUN: %target-swift-frontend -typecheck %t/ManyFiles_ImplicitVsInternal_FileB.swift -I %t \
44+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify \
45+
// RUN: -primary-file %t/ManyFiles_ImplicitVsInternal_FileA.swift
46+
//--- ManyFiles_ImplicitVsInternal_FileA.swift
47+
import Lib // expected-error {{ambiguous implicit access level for import of 'Lib'; it is imported as 'internal' elsewhere}}
48+
//--- ManyFiles_ImplicitVsInternal_FileB.swift
49+
internal import Lib // expected-note {{imported 'internal' here}}
50+
51+
// RUN: %target-swift-frontend -typecheck %t/ManyFiles_ImplicitVsPackage_FileB.swift -I %t \
52+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify \
53+
// RUN: -primary-file %t/ManyFiles_ImplicitVsPackage_FileA.swift
54+
//--- ManyFiles_ImplicitVsPackage_FileA.swift
55+
import Lib // expected-error {{ambiguous implicit access level for import of 'Lib'; it is imported as 'package' elsewhere}}
56+
//--- ManyFiles_ImplicitVsPackage_FileB.swift
57+
package import Lib // expected-note {{imported 'package' here}} @:1
58+
59+
// RUN: %target-swift-frontend -typecheck %t/ManyFiles_AmbiguitySwift6_File?.swift -I %t \
60+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify -swift-version 6
61+
//--- ManyFiles_AmbiguitySwift6_FileA.swift
62+
import Lib
63+
//--- ManyFiles_AmbiguitySwift6_FileB.swift
64+
internal import Lib

0 commit comments

Comments
 (0)