Skip to content

Commit ff797cf

Browse files
committed
Sema: Warn on resilient uses of @_implementationOnly as deprecated
Report uses of `@_implementationOnly` in resilient modules as deprecated. With a fixit to replace it with `internal` or delete it when imports are internal by default. Uses of `@_implementationOnly` in non-resilient modules is already reported as being unsafe.
1 parent f4d7327 commit ff797cf

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,14 @@ WARNING(implementation_only_requires_library_evolution,none,
11671167
"using '@_implementationOnly' without enabling library evolution "
11681168
"for %0 may lead to instability during execution",
11691169
(Identifier))
1170+
WARNING(implementation_only_deprecated_explicit,none,
1171+
"'@_implementationOnly' is deprecated, use 'internal import' "
1172+
"and family instead",
1173+
())
1174+
WARNING(implementation_only_deprecated_implicit,none,
1175+
"'@_implementationOnly' is deprecated, use a bare import "
1176+
"as 'InternalImportsByDefault' is enabled",
1177+
())
11701178

11711179
ERROR(module_allowable_client_violation,none,
11721180
"module %0 doesn't allow importation from module %1",

include/swift/AST/Import.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -592,17 +592,22 @@ struct AttributedImport {
592592
/// if the access level was implicit or explicit.
593593
SourceRange accessLevelRange;
594594

595+
/// Location of the `@_implementationOnly` attribute if set.
596+
SourceRange implementationOnlyRange;
597+
595598
AttributedImport(ModuleInfo module, SourceLoc importLoc = SourceLoc(),
596599
ImportOptions options = ImportOptions(),
597600
StringRef filename = {}, ArrayRef<Identifier> spiGroups = {},
598601
SourceRange preconcurrencyRange = {},
599602
std::optional<AccessLevel> docVisibility = std::nullopt,
600603
AccessLevel accessLevel = AccessLevel::Public,
601-
SourceRange accessLevelRange = SourceRange())
604+
SourceRange accessLevelRange = SourceRange(),
605+
SourceRange implementationOnlyRange = SourceRange())
602606
: module(module), importLoc(importLoc), options(options),
603607
sourceFileArg(filename), spiGroups(spiGroups),
604608
preconcurrencyRange(preconcurrencyRange), docVisibility(docVisibility),
605-
accessLevel(accessLevel), accessLevelRange(accessLevelRange) {
609+
accessLevel(accessLevel), accessLevelRange(accessLevelRange),
610+
implementationOnlyRange(implementationOnlyRange) {
606611
assert(!(options.contains(ImportFlags::Exported) &&
607612
options.contains(ImportFlags::ImplementationOnly)) ||
608613
options.contains(ImportFlags::Reserved));
@@ -613,7 +618,8 @@ struct AttributedImport {
613618
: AttributedImport(module, other.importLoc, other.options,
614619
other.sourceFileArg, other.spiGroups,
615620
other.preconcurrencyRange, other.docVisibility,
616-
other.accessLevel, other.accessLevelRange) { }
621+
other.accessLevel, other.accessLevelRange,
622+
other.implementationOnlyRange) { }
617623

618624
friend bool operator==(const AttributedImport<ModuleInfo> &lhs,
619625
const AttributedImport<ModuleInfo> &rhs) {
@@ -623,7 +629,8 @@ struct AttributedImport {
623629
lhs.spiGroups == rhs.spiGroups &&
624630
lhs.docVisibility == rhs.docVisibility &&
625631
lhs.accessLevel == rhs.accessLevel &&
626-
lhs.accessLevelRange == rhs.accessLevelRange;
632+
lhs.accessLevelRange == rhs.accessLevelRange &&
633+
lhs.implementationOnlyRange == rhs.implementationOnlyRange;
627634
}
628635

629636
AttributedImport<ImportedModule> getLoaded(ModuleDecl *loadedModule) const {

lib/Sema/ImportResolution.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,10 @@ UnboundImport::UnboundImport(ImportDecl *ID)
586586
if (ID->getAttrs().hasAttribute<TestableAttr>())
587587
import.options |= ImportFlags::Testable;
588588

589-
if (ID->getAttrs().hasAttribute<ImplementationOnlyAttr>())
589+
if (auto attr = ID->getAttrs().getAttribute<ImplementationOnlyAttr>()) {
590590
import.options |= ImportFlags::ImplementationOnly;
591+
import.implementationOnlyRange = attr->Range;
592+
}
591593

592594
import.accessLevel = ID->getAccessLevel();
593595
if (auto attr = ID->getAttrs().getAttribute<AccessControlAttr>()) {
@@ -811,7 +813,21 @@ void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule,
811813
// We exempt some imports using @_implementationOnly in a safe way from
812814
// packages that cannot be resilient.
813815
if (import.options.contains(ImportFlags::ImplementationOnly) &&
814-
!SF.getParentModule()->isResilient() && topLevelModule &&
816+
import.implementationOnlyRange.isValid()) {
817+
if (SF.getParentModule()->isResilient()) {
818+
// Encourage replacing `@_implementationOnly` with `internal import`.
819+
if (ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault)) {
820+
auto inFlight =
821+
ctx.Diags.diagnose(import.importLoc,
822+
diag::implementation_only_deprecated_implicit);
823+
inFlight.fixItRemove(import.implementationOnlyRange);
824+
} else {
825+
auto inFlight =
826+
ctx.Diags.diagnose(import.importLoc,
827+
diag::implementation_only_deprecated_explicit);
828+
inFlight.fixItReplace(import.implementationOnlyRange, "internal");
829+
}
830+
} else if ( // Non-resilient
815831
!(((targetName.str() == "CCryptoBoringSSL" ||
816832
targetName.str() == "CCryptoBoringSSLShims") &&
817833
(importerName.str() == "Crypto" ||
@@ -820,11 +836,13 @@ void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule,
820836
((targetName.str() == "CNIOBoringSSL" ||
821837
targetName.str() == "CNIOBoringSSLShims") &&
822838
importerName.str() == "NIOSSL"))) {
823-
ctx.Diags.diagnose(import.importLoc,
824-
diag::implementation_only_requires_library_evolution,
825-
importerName);
839+
ctx.Diags.diagnose(import.importLoc,
840+
diag::implementation_only_requires_library_evolution,
841+
importerName);
842+
}
826843
}
827844

845+
// Report public imports of non-resilient modules from a resilient module.
828846
if (import.options.contains(ImportFlags::ImplementationOnly) ||
829847
import.accessLevel < AccessLevel::Public)
830848
return;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t --leading-lines
3+
4+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift \
5+
// RUN: -enable-library-evolution -swift-version 5 \
6+
// RUN: -emit-module-path %t/Lib.swiftmodule
7+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
8+
// RUN: -enable-library-evolution -swift-version 5 \
9+
// RUN: -verify -verify-additional-prefix swift-5-
10+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
11+
// RUN: -enable-library-evolution -swift-version 5 \
12+
// RUN: -enable-upcoming-feature InternalImportsByDefault \
13+
// RUN: -verify -verify-additional-prefix default-to-internal-
14+
15+
//--- Lib.swift
16+
public struct SomeType {}
17+
18+
//--- Client.swift
19+
@_implementationOnly import Lib
20+
// expected-swift-5-warning @-1 {{'@_implementationOnly' is deprecated, use 'internal import' and family instead}} {{1-21=internal}}
21+
// expected-default-to-internal-warning @-2 {{'@_implementationOnly' is deprecated, use a bare import as 'InternalImportsByDefault' is enabled}} {{1-22=}}
22+
23+
internal func foo(_: SomeType) {}

0 commit comments

Comments
 (0)