Skip to content

Commit 543d649

Browse files
authored
[Diagnostics] Warn when the result of a Void-returning function is ignored (by assigning into '_') (#29576)
1 parent d0205d8 commit 543d649

22 files changed

+61
-37
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,6 +3193,9 @@ ERROR(object_literal_broken_proto,none,
31933193
ERROR(discard_expr_outside_of_assignment,none,
31943194
"'_' can only appear in a pattern or on the left side of an assignment",
31953195
())
3196+
WARNING(discard_expr_void_result_redundant, none,
3197+
"using '_' to ignore the result of a Void-returning function "
3198+
"is redundant", ())
31963199
ERROR(collection_literal_heterogeneous,none,
31973200
"heterogeneous collection literal could only be inferred to %0; add"
31983201
" explicit type annotation if this is intentional", (Type))

lib/Sema/MiscDiagnostics.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,24 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
221221
}
222222

223223
// If we have an assignment expression, scout ahead for acceptable _'s.
224-
if (auto *AE = dyn_cast<AssignExpr>(E))
225-
markAcceptableDiscardExprs(AE->getDest());
224+
if (auto *AE = dyn_cast<AssignExpr>(E)) {
225+
auto destExpr = AE->getDest();
226+
markAcceptableDiscardExprs(destExpr);
227+
// If the user is assigning the result of a function that returns
228+
// Void to _ then warn, because that is redundant.
229+
if (auto DAE = dyn_cast<DiscardAssignmentExpr>(destExpr)) {
230+
if (auto CE = dyn_cast<CallExpr>(AE->getSrc())) {
231+
if (CE->getCalledValue() && isa<FuncDecl>(CE->getCalledValue()) &&
232+
CE->getType()->isVoid()) {
233+
Ctx.Diags
234+
.diagnose(DAE->getLoc(),
235+
diag::discard_expr_void_result_redundant)
236+
.fixItRemoveChars(DAE->getStartLoc(),
237+
AE->getSrc()->getStartLoc());
238+
}
239+
}
240+
}
241+
}
226242

227243
/// Diagnose a '_' that isn't on the immediate LHS of an assignment.
228244
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E)) {
@@ -409,7 +425,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
409425
/// in simple pattern-like expressions, so we reject anything complex here.
410426
void markAcceptableDiscardExprs(Expr *E) {
411427
if (!E) return;
412-
428+
413429
if (auto *PE = dyn_cast<ParenExpr>(E))
414430
return markAcceptableDiscardExprs(PE->getSubExpr());
415431
if (auto *TE = dyn_cast<TupleExpr>(E)) {

test/ClangImporter/MixedSource/submodule.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
@_exported import Mixed.Submodule
55

66
func test() {
7-
_ = topLevel()
8-
_ = fromSubmodule()
7+
topLevel()
8+
fromSubmodule()
99
}

test/ClangImporter/private_frameworks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
import SomeKit
4040

4141
func testWidget(widget: SKWidget) {
42-
_ = widget.someObjCMethod()
43-
_ = widget.someObjCExtensionMethod()
42+
widget.someObjCMethod()
43+
widget.someObjCExtensionMethod()
4444

4545
let ext = widget.extensionMethod()
4646
ext.foo()

test/Compatibility/special_func_name.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ struct S3 {
3434
}
3535

3636
_ = S11(0) // expected-error {{argument passed to call that takes no arguments}}
37-
_ = S11.init(0)
38-
_ = S11.`init`(0)
37+
S11.init(0)
38+
S11.`init`(0)
3939

4040
_ = S12(0)
4141
_ = S12.init(0)
4242
_ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}}
4343

4444
_ = S21(0) // expected-error {{argument passed to call that takes no arguments}}
45-
_ = S21.init(0)
46-
_ = S21.`init`(0)
45+
S21.init(0)
46+
S21.`init`(0)
4747

4848
_ = S22(0)
4949
_ = S22.init(0)

test/Constraints/assignment.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,8 @@ func f23798944() {
7272
}
7373

7474
.sr_3506 = 0 // expected-error {{type 'Int' has no member 'sr_3506'}}
75+
76+
// SR-1553
77+
78+
func returnsVoid() {}
79+
_ = returnsVoid() // expected-warning {{using '_' to ignore the result of a Void-returning function is redundant}}{{1-5=}}

test/Constraints/dynamic_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ let anyValue: Any = X()
224224
_ = anyValue.bar() // expected-error {{value of type 'Any' has no member 'bar'}}
225225
// expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}}{{5-5=(}}{{13-13= as AnyObject)}}
226226
_ = (anyValue as AnyObject).bar()
227-
_ = (anyValue as! X).bar()
227+
(anyValue as! X).bar()
228228

229229
var anyDict: [String : Any] = Dictionary<String, Any>()
230230
anyDict["test"] = anyValue

test/Constraints/optional.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,8 @@ func sr8411() {
391391

392392
_ = S(&foo) // Ok
393393
_ = S.init(&foo) // Ok
394-
_ = S.foo(&foo) // Ok
395-
_ = S.bar(&foo, 42) // Ok
394+
S.foo(&foo) // Ok
395+
S.bar(&foo, 42) // Ok
396396
}
397397

398398
// SR-11104 - Slightly misleading diagnostics for contextual failures with multiple fixes

test/Constraints/override.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ class Sub: Base {
99
}
1010

1111
func removeOverrides<SomeSub: Sub>(concrete: Sub, generic: SomeSub) {
12-
_ = concrete.foo()
13-
_ = generic.foo()
12+
concrete.foo()
13+
generic.foo()
1414
}
1515

1616
class Base1 {

test/Constraints/rdar39931475.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ protocol P {
55
}
66

77
func foo<T: P>(_ bar: T) {
8-
_ = bar.b { a in Double((a, a += 1).0) }
8+
bar.b { a in Double((a, a += 1).0) }
99
}

test/Constraints/tuple_arguments.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,8 +1696,8 @@ class Mappable<T> {
16961696

16971697
let x = Mappable(())
16981698
// expected-note@-1 2{{'x' declared here}}
1699-
_ = x.map { (_: Void) in return () }
1700-
_ = x.map { (_: ()) in () }
1699+
x.map { (_: Void) in return () }
1700+
x.map { (_: ()) in () }
17011701

17021702
// https://bugs.swift.org/browse/SR-9470
17031703
do {

test/NameBinding/name_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ class ShadowingGenericParameter<T> {
609609
func foo (t : T) {}
610610
}
611611

612-
_ = ShadowingGenericParameter<String>().foo(t: "hi")
612+
ShadowingGenericParameter<String>().foo(t: "hi")
613613

614614
// rdar://problem/51266778
615615
struct PatternBindingWithTwoVars1 { var x = 3, y = x }

test/Sema/call_as_function_simple.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ func testMutating(_ x: Mutating, _ y: inout Mutating) {
106106
_ = x()
107107
// expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}}
108108
_ = x.callAsFunction()
109-
_ = y()
110-
_ = y.callAsFunction()
109+
y()
110+
y.callAsFunction()
111111
}
112112

113113
struct Inout {
@@ -185,8 +185,8 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!,
185185
_ = d()?.callAsFunction()?()
186186
_ = e()
187187
_ = e(1, 2, 3)
188-
_ = f()
189-
_ = g(&inoutInt)
188+
f()
189+
g(&inoutInt)
190190
_ = try? h()
191191
_ = try? h { throw DummyError() }
192192
}

test/Sema/implementation-only-import-inlinable-conformances.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ extension NormalProtoAssocHolder {
135135
}
136136

137137
@inlinable func testMultipleConformances() {
138-
_ = NormalProtoAssocHolder<NormalStruct>.testAnotherConformance(NormalClass.self)
138+
NormalProtoAssocHolder<NormalStruct>.testAnotherConformance(NormalClass.self)
139139
// expected-error@-1 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
140140
// expected-error@-2 {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
141141
}

test/Serialization/Recovery/typedefs-in-protocols.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public func testGenericDispatch<T: Proto>(user: T) {
4444
// CHECK-IR: [[METHOD:%.+]] = bitcast i8* [[RAW_METHOD]] to void (%swift.opaque*, %swift.type*, i8**)*
4545
// CHECK-IR-NOT: ret
4646
// CHECK-IR: call swiftcc void [[METHOD]](
47-
_ = user.lastMethod()
47+
user.lastMethod()
4848
} // CHECK-IR: ret void
4949

5050
#if VERIFY

test/Serialization/Recovery/typedefs.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public func testVTableBuilding(user: User) {
3939
// changes, please check that offset is still correct.
4040
// CHECK-IR-NOT: ret
4141
// CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, {{i64 28|i32 31}}
42-
_ = user.lastMethod()
42+
user.lastMethod()
4343
} // CHECK-IR: ret void
4444

4545
#if VERIFY

test/attr/attr_dynamic_callable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ struct A {
451451
}
452452

453453
func test9239() {
454-
_ = A()() // ok
454+
A()() // ok
455455
}
456456

457457
// SR-10313

test/decl/func/operator.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,10 @@ func testPrefixOperatorOnTuple() {
391391
_ = ()foo
392392
// expected-error@-1 {{consecutive statements on a line must be separated by ';'}}
393393
// expected-warning@-2 {{expression of type '(Int, Int)' is unused}}
394-
_ = ()(foo)
394+
()(foo)
395395
_ = (1, 2)
396396
_ = ()(1, 2) // expected-error {{operator function '∫' expects a single parameter of type '(Int, Int)'}}
397-
_ = ()((1, 2))
397+
()((1, 2))
398398
}
399399

400400
postfix operator §
@@ -412,9 +412,9 @@ func testPostfixOperatorOnTuple<A, B>(a: A, b: B) {
412412
// expected-error@-2 {{generic parameter 'T' could not be inferred}}
413413
// expected-error@-3 {{generic parameter 'U' could not be inferred}}
414414
// expected-warning@-4 {{expression of type '(A, (B, B), A)' is unused}}
415-
_ = (§)(foo)
415+
(§)(foo)
416416
_ = (a, (b, b), a)§
417417
_ = (§)(a, (b, b), a) // expected-error {{operator function '§' expects a single parameter of type '(T, (U, U), T)'}}
418-
_ = (§)((a, (b, b), a))
418+
(§)((a, (b, b), a))
419419
_ = (a, ((), (b, (a, a), b)§), a)§
420420
}

test/decl/func/special_func_name.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ struct S3 {
3535

3636
_ = S11(0) // expected-error {{argument passed to call that takes no arguments}}
3737
_ = S11.init(0) // expected-error {{argument passed to call that takes no arguments}}
38-
_ = S11.`init`(0)
38+
S11.`init`(0)
3939

4040
_ = S12(0)
4141
_ = S12.init(0)
4242
_ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}}
4343

4444
_ = S21(0) // expected-error {{argument passed to call that takes no arguments}}
4545
_ = S21.init(0) // expected-error {{argument passed to call that takes no arguments}}
46-
_ = S21.`init`(0)
46+
S21.`init`(0)
4747

4848
_ = S22(0)
4949
_ = S22.init(0)

test/decl/protocol/req/optional_visibility.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class Conforms : Opt {
1111
}
1212

1313
func g(x: Conforms) {
14-
_ = x.f(callback: {})
14+
x.f(callback: {})
1515
}

test/expr/unary/keypath/keypath.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ func test_keypath_with_method_refs() {
814814
// SR-10467 - Argument type 'KeyPath<String, Int>' does not conform to expected type 'Any'
815815
func test_keypath_in_any_context() {
816816
func foo(_: Any) {}
817-
_ = foo(\String.count) // Ok
817+
foo(\String.count) // Ok
818818
}
819819

820820
protocol PWithTypeAlias {

test/stdlib/UnsafePointerDiagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ struct SR9800 {
131131
func foo(_: UnsafePointer<UInt8>) {}
132132

133133
func ambiguityTest(buf: UnsafeMutablePointer<CChar>) {
134-
_ = foo(UnsafePointer(buf)) // this call should be unambiguoius
134+
foo(UnsafePointer(buf)) // this call should be unambiguoius
135135
}
136136
}
137137

0 commit comments

Comments
 (0)