Skip to content

Commit ab43437

Browse files
authored
Merge pull request swiftlang#78187 from DougGregor/suggest-safe-unchecked
Suggest `@safe(unchecked)` when only the body of a declaration uses unsafe constructs
2 parents 1029764 + bd4a477 commit ab43437

File tree

9 files changed

+40
-25
lines changed

9 files changed

+40
-25
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8091,14 +8091,17 @@ GROUPED_WARNING(reference_to_unsafe_typed_decl,Unsafe,none,
80918091
(bool, const ValueDecl *, Type))
80928092
NOTE(unsafe_decl_here,none,
80938093
"unsafe %kindbase0 declared here", (const ValueDecl *))
8094+
NOTE(encapsulate_unsafe_in_enclosing_context,none,
8095+
"make %kindbase0 @safe(unchecked) to allow it to use unsafe constructs in its definition",
8096+
(const Decl *))
80948097
NOTE(make_enclosing_context_unsafe,none,
8095-
"mark the enclosing %kindbase0 '@unsafe' to allow it to use unsafe constructs",
8098+
"make %kindbase0 @unsafe to indicate that its use is not memory-safe",
80968099
(const Decl *))
80978100
NOTE(make_subclass_unsafe,none,
8098-
"mark the class %0 '@unsafe' to allow unsafe overrides of safe superclass methods",
8101+
"make class %0 @unsafe to allow unsafe overrides of safe superclass methods",
80998102
(DeclName))
81008103
NOTE(make_conforming_context_unsafe,none,
8101-
"mark the enclosing %0 with '@unsafe' to allow unsafe conformance to protocol %1",
8104+
"make the enclosing %0 @unsafe to allow unsafe conformance to protocol %1",
81028105
(DescriptiveDeclKind, DeclName))
81038106

81048107
//===----------------------------------------------------------------------===//

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4073,8 +4073,17 @@ static void suggestUnsafeOnEnclosingDecl(
40734073
if (!decl)
40744074
return;
40754075

4076-
decl->diagnose(diag::make_enclosing_context_unsafe, decl)
4077-
.fixItInsert(decl->getAttributeInsertionLoc(false), "@unsafe ");
4076+
if (versionCheckNode.has_value()) {
4077+
// The unsafe construct is inside the body of the entity, so suggest
4078+
// @safe(unchecked) on the declaration.
4079+
decl->diagnose(diag::encapsulate_unsafe_in_enclosing_context, decl)
4080+
.fixItInsert(decl->getAttributeInsertionLoc(false),
4081+
"@safe(unchecked) ");
4082+
} else {
4083+
// The unsafe construct is not part of the body, so
4084+
decl->diagnose(diag::make_enclosing_context_unsafe, decl)
4085+
.fixItInsert(decl->getAttributeInsertionLoc(false), "@unsafe ");
4086+
}
40784087
}
40794088

40804089
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic

test/Interop/Cxx/class/safe-interop-mode.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,20 @@ struct MyContainer {
5555
import Test
5656
import CoreFoundation
5757

58-
// expected-note@+1{{mark the enclosing global function 'useUnsafeParam' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
58+
// expected-note@+1{{make global function 'useUnsafeParam' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
5959
func useUnsafeParam(x: Unannotated) { // expected-warning{{reference to unsafe struct 'Unannotated'}}
6060
}
6161

62-
// expected-note@+2{{mark the enclosing global function 'useUnsafeParam2' '@unsafe' to allow it to use unsafe constructs}}{{10:1-1=@unsafe }}
62+
// expected-note@+2{{make global function 'useUnsafeParam2' @unsafe to indicate that its use is not memory-safe}}{{10:1-1=@unsafe }}
6363
@available(SwiftStdlib 5.8, *)
6464
func useUnsafeParam2(x: UnsafeReference) { // expected-warning{{reference to unsafe class 'UnsafeReference'}}
6565
}
6666

67-
// expected-note@+1{{mark the enclosing global function 'useUnsafeParam3' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
67+
// expected-note@+1{{make global function 'useUnsafeParam3' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
6868
func useUnsafeParam3(x: UnknownEscapabilityAggregate) { // expected-warning{{reference to unsafe struct 'UnknownEscapabilityAggregate'}}
6969
}
7070

71-
// expected-note@+1{{mark the enclosing global function 'useSafeParams' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
71+
// expected-note@+1{{make global function 'useSafeParams' @safe(unchecked) to allow it to use unsafe constructs in its definition}}{{1-1=@safe(unchecked) }}
7272
func useSafeParams(x: Owner, y: View, z: SafeEscapableAggregate, c: MyContainer) {
7373
let _ = c.__beginUnsafe() // expected-warning{{call to unsafe instance method '__beginUnsafe'}}
7474
}

test/Unsafe/safe.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func g() {
1919
unsafeFunction()
2020
}
2121

22-
// expected-note@+2{{mark the enclosing global function 'h' '@unsafe' to allow it to use unsafe constructs}}
22+
// expected-note@+2{{make global function 'h' @unsafe to indicate that its use is not memory-safe}}
2323
@safe(unchecked, message: "I was careful")
2424
func h(_: UnsafeType) { // expected-warning{{reference to unsafe struct 'UnsafeType' [Unsafe]}}
2525
unsafeFunction()

test/Unsafe/unsafe-suppression.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct UnsafeType { } // expected-note{{unsafe struct 'UnsafeType' declared here
1414

1515
// expected-warning@+1{{reference to unsafe struct 'UnsafeType' [Unsafe]}}
1616
func iAmImpliedUnsafe() -> UnsafeType? { nil }
17-
// expected-note@-1{{mark the enclosing global function 'iAmImpliedUnsafe' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
17+
// expected-note@-1{{make global function 'iAmImpliedUnsafe' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
1818

1919
@unsafe
2020
func labeledUnsafe(_: UnsafeType) {
@@ -27,7 +27,7 @@ class C {
2727
func method() { } // expected-note{{overridden declaration is here}}
2828
}
2929

30-
class D1: C { // expected-note{{mark the class 'D1' '@unsafe' to allow unsafe overrides of safe superclass methods}}{{1-1=@unsafe }}
30+
class D1: C { // expected-note{{make class 'D1' @unsafe to allow unsafe overrides of safe superclass methods}}{{1-1=@unsafe }}
3131
@unsafe
3232
override func method() { } // expected-warning{{override of safe instance method with unsafe instance method [Unsafe]}}
3333
}
@@ -42,7 +42,7 @@ protocol P {
4242
}
4343

4444
struct S1: P {
45-
// expected-note@-1{{mark the enclosing struct with '@unsafe' to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
45+
// expected-note@-1{{make the enclosing struct @unsafe to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
4646
@unsafe
4747
func protoMethod() { } // expected-warning{{unsafe instance method 'protoMethod()' cannot satisfy safe requirement}}
4848
}
@@ -56,7 +56,7 @@ struct S2: P {
5656
struct S3 { }
5757

5858
extension S3: P {
59-
// expected-note@-1{{mark the enclosing extension with '@unsafe' to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
59+
// expected-note@-1{{make the enclosing extension @unsafe to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
6060
@unsafe
6161
func protoMethod() { } // expected-warning{{unsafe instance method 'protoMethod()' cannot satisfy safe requirement}}
6262
}
@@ -100,7 +100,7 @@ struct UnsafeOuter { // expected-note{{unsafe struct 'UnsafeOuter' declared here
100100
}
101101
}
102102

103-
// expected-note@+1{{mark the enclosing extension of struct 'UnsafeOuter' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
103+
// expected-note@+1{{make extension of struct 'UnsafeOuter' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
104104
extension UnsafeOuter { // expected-warning{{reference to unsafe struct 'UnsafeOuter' [Unsafe]}}
105105
func h(_: UnsafeType) { }
106106
}

test/Unsafe/unsafe.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ protocol P {
2020
}
2121

2222
struct XP: P {
23-
// expected-note@-1{{mark the enclosing struct with '@unsafe' to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
23+
// expected-note@-1{{make the enclosing struct @unsafe to allow unsafe conformance to protocol 'P'}}{{1-1=@unsafe }}
2424
@unsafe func f() { } // expected-warning{{unsafe instance method 'f()' cannot satisfy safe requirement [Unsafe]}}
2525
@unsafe func g() { }
2626
}
@@ -34,7 +34,7 @@ protocol Ptrable2 {
3434
}
3535

3636
extension HasAPointerType: Ptrable2 { } // expected-warning{{unsafe type 'HasAPointerType.Ptr' (aka 'PointerType') cannot satisfy safe associated type 'Ptr'}}
37-
// expected-note@-1{{mark the enclosing extension with '@unsafe' to allow unsafe conformance to protocol 'Ptrable2'}}{{1-1=@unsafe }}
37+
// expected-note@-1{{make the enclosing extension @unsafe to allow unsafe conformance to protocol 'Ptrable2'}}{{1-1=@unsafe }}
3838

3939
// -----------------------------------------------------------------------
4040
// Overrides
@@ -44,7 +44,7 @@ class Super {
4444
@unsafe func g() { }
4545
}
4646

47-
class Sub: Super { // expected-note{{mark the class 'Sub' '@unsafe' to allow unsafe overrides of safe superclass methods}}{{1-1=@unsafe }}
47+
class Sub: Super { // expected-note{{make class 'Sub' @unsafe to allow unsafe overrides of safe superclass methods}}{{1-1=@unsafe }}
4848
@unsafe override func f() { } // expected-warning{{override of safe instance method with unsafe instance method [Unsafe]}}
4949
@unsafe override func g() { }
5050
}
@@ -55,7 +55,7 @@ class Sub: Super { // expected-note{{mark the class 'Sub' '@unsafe' to allow uns
5555
struct SuperHolder {
5656
unowned var s1: Super
5757
unowned(unsafe) var s2: Super // expected-warning{{unowned(unsafe) involves unsafe code}}
58-
// expected-note@-1{{mark the enclosing property 's2' '@unsafe' to allow it to use unsafe constructs}}{{3-3=@unsafe }}
58+
// expected-note@-1{{make property 's2' @unsafe to indicate that its use is not memory-safe}}{{3-3=@unsafe }}
5959
}
6060

6161
// -----------------------------------------------------------------------
@@ -66,15 +66,16 @@ struct SuperHolder {
6666
};
6767

6868
class UnsafeSub: UnsafeSuper { } // expected-warning{{reference to unsafe class 'UnsafeSuper'}}
69-
// expected-note@-1{{mark the enclosing class 'UnsafeSub' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
69+
// expected-note@-1{{make class 'UnsafeSub' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
7070

7171
// -----------------------------------------------------------------------
7272
// Declaration references
7373
// -----------------------------------------------------------------------
7474
@unsafe func unsafeF() { } // expected-note{{unsafe global function 'unsafeF' declared here}}
7575
@unsafe var unsafeVar: Int = 0 // expected-note{{'unsafeVar' declared here}}
7676

77-
// expected-note@+1 7{{mark the enclosing global function 'testMe' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
77+
// expected-note@+2 5{{make global function 'testMe' @safe(unchecked) to allow it to use unsafe constructs in its definition}}{{1-1=@safe(unchecked) }}
78+
// expected-note@+1 2{{make global function 'testMe' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
7879
func testMe(
7980
_ pointer: PointerType, // expected-warning{{reference to unsafe struct 'PointerType'}}
8081
_ unsafeSuper: UnsafeSuper // expected-warning{{reference to unsafe class 'UnsafeSuper'}}

test/Unsafe/unsafe_concurrency.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
// REQUIRES: swift_feature_WarnUnsafe
77

88
// expected-warning@+2{{@unchecked conformance involves unsafe code}}
9-
// expected-note@+1{{mark the enclosing class with '@unsafe' to allow unsafe conformance to protocol 'Sendable'}}{{1-1=@unsafe }}
9+
// expected-note@+1{{make the enclosing class @unsafe to allow unsafe conformance to protocol 'Sendable'}}{{1-1=@unsafe }}
1010
class C: @unchecked Sendable {
1111
var counter: Int = 0
1212
}
1313

1414
@available(SwiftStdlib 5.1, *)
1515
func f() async {
1616
// expected-warning@+2{{nonisolated(unsafe) involves unsafe code}}
17-
// expected-note@+1{{mark the enclosing var 'counter' '@unsafe' to allow it to use unsafe constructs}}
17+
// expected-note@+1{{make var 'counter' @unsafe to indicate that its use is not memory-safe}}
1818
nonisolated(unsafe) var counter = 0
1919
Task.detached {
2020
counter += 1

test/Unsafe/unsafe_imports.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import unsafe_decls
77

8-
// expected-note@+1 3{{mark the enclosing global function 'testUnsafe' '@unsafe' to allow it to use unsafe constructs}}
8+
// expected-note@+2 2{{make global function 'testUnsafe' @safe(unchecked) to allow it to use unsafe constructs in its definition}}
9+
// expected-note@+1{{make global function 'testUnsafe' @unsafe to indicate that its use is not memory-safe}}
910
func testUnsafe(_ ut: UnsafeType) { // expected-warning{{reference to unsafe struct 'UnsafeType'}}
1011
unsafe_c_function() // expected-warning{{call to unsafe global function 'unsafe_c_function'}}
1112

test/Unsafe/unsafe_stdlib.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
// REQUIRES: swift_feature_AllowUnsafeAttribute
77
// REQUIRES: swift_feature_WarnUnsafe
88

9-
// expected-note@+1 4{{mark the enclosing global function 'test' '@unsafe' to allow it to use unsafe constructs}}{{1-1=@unsafe }}
9+
// expected-note@+2 2{{make global function 'test' @safe(unchecked) to allow it to use unsafe constructs in its definition}}{{1-1=@safe(unchecked) }}
10+
// expected-note@+1 2{{make global function 'test' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
1011
func test(
1112
x: OpaquePointer, // expected-warning{{reference to unsafe struct 'OpaquePointer'}}
1213
other: UnsafeMutablePointer<Int> // expected-warning{{reference to unsafe generic struct 'UnsafeMutablePointer'}}

0 commit comments

Comments
 (0)