Skip to content

Commit 884e72a

Browse files
committed
Warn about an unused @_predatesConcurrency attribute on an import.
We'll already suggest the addition of `@_predatesConcurrency` on an import to downgrade or silence `Sendable`-related diagnostics, so we'll be adding these to people's code. Over time, this attribute can go stale, if the imported module's adopt `Sendable`. When the attribute isn't doing anything, because it didn't suppress any `Sendable`-related diagnostics, suggest that it be removed.
1 parent 7a3da5a commit 884e72a

File tree

5 files changed

+55
-9
lines changed

5 files changed

+55
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,8 @@ REMARK(add_predates_concurrency_import,none,
19561956
"add '@_predatesConcurrency' to %select{suppress|treat}0 "
19571957
"'Sendable'-related %select{warnings|errors}0 from module %1"
19581958
"%select{| as warnings}0", (bool, Identifier))
1959+
REMARK(remove_predates_concurrency_import,none,
1960+
"'@_predatesConcurrency' attribute on module %0 is unused", (Identifier))
19591961
WARNING(public_decl_needs_sendable,none,
19601962
"public %0 %1 does not specify whether it is 'Sendable' or not",
19611963
(DescriptiveDeclKind, DeclName))

include/swift/AST/Import.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,11 @@ struct AttributedImport {
556556

557557
AttributedImport(ModuleInfo module, SourceLoc importLoc = SourceLoc(),
558558
ImportOptions options = ImportOptions(),
559-
StringRef filename = {}, ArrayRef<Identifier> spiGroups = {})
559+
StringRef filename = {}, ArrayRef<Identifier> spiGroups = {},
560+
SourceRange predatesConcurrencyRange = {})
560561
: module(module), importLoc(importLoc), options(options),
561-
sourceFileArg(filename), spiGroups(spiGroups) {
562+
sourceFileArg(filename), spiGroups(spiGroups),
563+
predatesConcurrencyRange(predatesConcurrencyRange) {
562564
assert(!(options.contains(ImportFlags::Exported) &&
563565
options.contains(ImportFlags::ImplementationOnly)) ||
564566
options.contains(ImportFlags::Reserved));
@@ -567,7 +569,8 @@ struct AttributedImport {
567569
template<class OtherModuleInfo>
568570
AttributedImport(ModuleInfo module, AttributedImport<OtherModuleInfo> other)
569571
: AttributedImport(module, other.importLoc, other.options,
570-
other.sourceFileArg, other.spiGroups) { }
572+
other.sourceFileArg, other.spiGroups,
573+
other.predatesConcurrencyRange) { }
571574

572575
friend bool operator==(const AttributedImport<ModuleInfo> &lhs,
573576
const AttributedImport<ModuleInfo> &rhs) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,14 @@ static bool diagnoseSingleNonSendableType(
752752

753753
bool wasSuppressed = diagnose(type, behavior);
754754

755+
// If this type was imported from another module, try to find the
756+
// corresponding import.
757+
Optional<AttributedImport<swift::ImportedModule>> import;
758+
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
759+
if (nominal && nominal->getParentModule() != module) {
760+
import = findImportFor(nominal, fromContext.fromDC);
761+
}
762+
755763
if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
756764
// Don't emit any other diagnostics.
757765
} else if (type->is<FunctionType>()) {
@@ -775,12 +783,10 @@ static bool diagnoseSingleNonSendableType(
775783
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
776784
nominal->getName());
777785

778-
// If we can find an import in this context that makes this nominal
779-
// type visible, remark that it can be `@_predatesConcurrency` import.
786+
// If we found the import that makes this nominal type visible, remark
787+
// that it can be @_predatesConcurrency import.
780788
// Only emit this remark once per source file, because it can happen a
781789
// lot.
782-
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
783-
auto import = findImportFor(nominal, fromContext.fromDC);
784790
if (import && !import->options.contains(ImportFlags::PredatesConcurrency) &&
785791
import->importLoc.isValid() && sourceFile &&
786792
!sourceFile->hasImportUsedPredatesConcurrency(*import)) {
@@ -795,6 +801,14 @@ static bool diagnoseSingleNonSendableType(
795801
}
796802
}
797803

804+
// If we found an import that makes this nominal type visible, and that
805+
// was a @_predatesConcurrency import, note that we have made use of the
806+
// attribute.
807+
if (import && import->options.contains(ImportFlags::PredatesConcurrency) &&
808+
sourceFile) {
809+
sourceFile->setImportUsedPredatesConcurrency(*import);
810+
}
811+
798812
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
799813
}
800814

lib/Sema/TypeChecker.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,23 @@ void swift::performTypeChecking(SourceFile &SF) {
264264
TypeCheckSourceFileRequest{&SF}, {});
265265
}
266266

267+
/// If any of the imports in this source file was @_predatesConcurrency but
268+
/// there were no diagnostics downgraded or suppressed due to that
269+
/// @_predatesConcurrency, suggest that the attribute be removed.
270+
static void diagnoseUnnecessaryPredatesConcurrencyImports(SourceFile &sf) {
271+
ASTContext &ctx = sf.getASTContext();
272+
for (const auto &import : sf.getImports()) {
273+
if (import.options.contains(ImportFlags::PredatesConcurrency) &&
274+
import.importLoc.isValid() &&
275+
!sf.hasImportUsedPredatesConcurrency(import)) {
276+
ctx.Diags.diagnose(
277+
import.importLoc, diag::remove_predates_concurrency_import,
278+
import.module.importedModule->getName())
279+
.fixItRemove(import.predatesConcurrencyRange);
280+
}
281+
}
282+
}
283+
267284
evaluator::SideEffect
268285
TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
269286
assert(SF && "Source file cannot be null!");
@@ -305,6 +322,8 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
305322
typeCheckDelayedFunctions(*SF);
306323
}
307324

325+
diagnoseUnnecessaryPredatesConcurrencyImports(*SF);
326+
308327
// Check to see if there's any inconsistent @_implementationOnly imports.
309328
evaluateOrDefault(
310329
Ctx.evaluator,
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
33
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
45

5-
// RUN: %target-typecheck-verify-swift -typecheck -I %t %s
6+
// RUN: %target-typecheck-verify-swift -typecheck -I %t
67

78
@_predatesConcurrency import NonStrictModule
89
@_predatesConcurrency import StrictModule
10+
@_predatesConcurrency import OtherActors
11+
// expected-remark@-1{{'@_predatesConcurrency' attribute on module 'OtherActors' is unused}}{{1-23=}}
912

1013
func acceptSendable<T: Sendable>(_: T) { }
1114

1215
@available(SwiftStdlib 5.1, *)
13-
func test(ss: StrictStruct, ns: NonStrictClass) async {
16+
func test(
17+
ss: StrictStruct, ns: NonStrictClass, oma: OtherModuleActor,
18+
ssc: SomeSendableClass
19+
) async {
1420
acceptSendable(ss) // expected-warning{{type 'StrictStruct' does not conform to the 'Sendable' protocol}}
1521
acceptSendable(ns) // silence issue entirely
22+
acceptSendable(oma) // okay
23+
acceptSendable(ssc) // okay
1624
}

0 commit comments

Comments
 (0)