Skip to content

Commit 2113940

Browse files
authored
[CSDiagnostics] Offer a fix-it to insert a return type placeholder when returning from a void function (#29747)
* [CSDiagnostics] Offer a fix-it to insert a return type when returning from a void function * [CSDiagnostics] Make sure the function name is not empty The function name will be empty in some cases, for example for property setters. In cases where the function name is empty, skip the note and fix-it. * [Test] Update existing diagnostics
1 parent b3590c5 commit 2113940

File tree

5 files changed

+112
-14
lines changed

5 files changed

+112
-14
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,8 @@ NOTE(selector_construction_suppress_warning,none,
703703

704704
ERROR(cannot_return_value_from_void_func,none,
705705
"unexpected non-void return value in void function", ())
706+
NOTE(add_return_type_note,none,
707+
"did you mean to add a return type?", ())
706708

707709
//------------------------------------------------------------------------------
708710
// MARK: Name Binding

lib/Sema/CSDiagnostics.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4792,6 +4792,23 @@ bool InvalidUseOfAddressOf::diagnoseAsError() {
47924792
bool ExtraneousReturnFailure::diagnoseAsError() {
47934793
auto *anchor = getAnchor();
47944794
emitDiagnostic(anchor->getLoc(), diag::cannot_return_value_from_void_func);
4795+
if (auto FD = dyn_cast<FuncDecl>(getDC())) {
4796+
// We only want to emit the note + fix-it if the function does not
4797+
// have an explicit return type. The reason we also need to check
4798+
// whether the parameter list has a valid loc is to guard against
4799+
// cases like like 'var foo: () { return 1 }' as here that loc will
4800+
// be invalid. We also need to check that the name is not empty,
4801+
// because certain decls will have empty name (like setters).
4802+
if (FD->getBodyResultTypeLoc().getLoc().isInvalid() &&
4803+
FD->getParameters()->getStartLoc().isValid() &&
4804+
!FD->getName().empty()) {
4805+
auto fixItLoc = Lexer::getLocForEndOfToken(
4806+
getASTContext().SourceMgr, FD->getParameters()->getEndLoc());
4807+
emitDiagnostic(anchor->getLoc(), diag::add_return_type_note)
4808+
.fixItInsert(fixItLoc, " -> <#Return Type#>");
4809+
}
4810+
}
4811+
47954812
return true;
47964813
}
47974814

test/Constraints/diagnostics.swift

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ protocol Shoes {
127127

128128
// Here the opaque value has type (metatype_type (archetype_type ... ))
129129
func f(_ x: Shoes, asType t: Shoes.Type) {
130-
return t.select(x) // expected-error{{unexpected non-void return value in void function}}
130+
return t.select(x)
131+
// expected-error@-1 {{unexpected non-void return value in void function}}
132+
// expected-note@-2 {{did you mean to add a return type?}}
131133
}
132134

133135
precedencegroup Starry {
@@ -184,7 +186,9 @@ func perform<T>() {} // expected-error {{generic parameter 'T' is not used in f
184186

185187
// <rdar://problem/17080659> Error Message QOI - wrong return type in an overload
186188
func recArea(_ h: Int, w : Int) {
187-
return h * w // expected-error {{unexpected non-void return value in void function}}
189+
return h * w
190+
// expected-error@-1 {{unexpected non-void return value in void function}}
191+
// expected-note@-2 {{did you mean to add a return type?}}
188192
}
189193

190194
// <rdar://problem/17224804> QoI: Error In Ternary Condition is Wrong
@@ -752,7 +756,9 @@ func segfault23433271(_ a : UnsafeMutableRawPointer) {
752756
// <rdar://problem/23272739> Poor diagnostic due to contextual constraint
753757
func r23272739(_ contentType: String) {
754758
let actualAcceptableContentTypes: Set<String> = []
755-
return actualAcceptableContentTypes.contains(contentType) // expected-error {{unexpected non-void return value in void function}}
759+
return actualAcceptableContentTypes.contains(contentType)
760+
// expected-error@-1 {{unexpected non-void return value in void function}}
761+
// expected-note@-2 {{did you mean to add a return type?}}
756762
}
757763

758764
// <rdar://problem/23641896> QoI: Strings in Swift cannot be indexed directly with integer offsets
@@ -863,8 +869,8 @@ class Foo23752537 {
863869
extension Foo23752537 {
864870
func isEquivalent(other: Foo23752537) {
865871
// TODO: <rdar://problem/27391581> QoI: Nonsensical "binary operator '&&' cannot be applied to two 'Bool' operands"
866-
// expected-error @+1 {{unexpected non-void return value in void function}}
867-
return (self.title != other.title && self.message != other.message)
872+
// expected-error@+1 {{unexpected non-void return value in void function}}
873+
return (self.title != other.title && self.message != other.message) // expected-note {{did you mean to add a return type?}}
868874
}
869875
}
870876

@@ -890,7 +896,9 @@ func f23213302() {
890896

891897
// <rdar://problem/24202058> QoI: Return of call to overloaded function in void-return context
892898
func rdar24202058(a : Int) {
893-
return a <= 480 // expected-error {{unexpected non-void return value in void function}}
899+
return a <= 480
900+
// expected-error@-1 {{unexpected non-void return value in void function}}
901+
// expected-note@-2 {{did you mean to add a return type?}}
894902
}
895903

896904
// SR-1752: Warning about unused result with ternary operator
@@ -953,7 +961,9 @@ r27212391(a: 1, 3, x: 5) // expected-error {{argument 'x' must precede unname
953961

954962
// SR-1255
955963
func foo1255_1() {
956-
return true || false // expected-error {{unexpected non-void return value in void function}}
964+
return true || false
965+
// expected-error@-1 {{unexpected non-void return value in void function}}
966+
// expected-note@-2 {{did you mean to add a return type?}}
957967
}
958968
func foo1255_2() -> Int {
959969
return true || false // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
@@ -1150,6 +1160,7 @@ func sr5045() {
11501160
let doubles: [Double] = [1, 2, 3]
11511161
return doubles.reduce(0, +)
11521162
// expected-error@-1 {{unexpected non-void return value in void function}}
1163+
// expected-note@-2 {{did you mean to add a return type?}}
11531164
}
11541165

11551166
// rdar://problem/32934129 - QoI: misleading diagnostic
@@ -1165,7 +1176,9 @@ class L_32934129<T : Comparable> {
11651176

11661177
func length() -> Int {
11671178
func inner(_ list: L_32934129<T>?, _ count: Int) {
1168-
guard let list = list else { return count } // expected-error {{unexpected non-void return value in void function}}
1179+
guard let list = list else { return count }
1180+
// expected-error@-1 {{unexpected non-void return value in void function}}
1181+
// expected-note@-2 {{did you mean to add a return type?}}
11691182
return inner(list.next, count + 1)
11701183
}
11711184

@@ -1315,3 +1328,60 @@ takesGenericFunction(true) // expected-error {{cannot convert value of type 'Boo
13151328
func takesTuple<T>(_ x: ([T], [T])) {} // expected-note {{in call to function 'takesTuple'}}
13161329
takesTuple(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([T], [T])'}}
13171330
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
1331+
1332+
// Void function returns non-void result fix-it
1333+
1334+
func voidFunc() {
1335+
return 1
1336+
// expected-error@-1 {{unexpected non-void return value in void function}}
1337+
// expected-note@-2 {{did you mean to add a return type?}}{{16-16= -> <#Return Type#>}}
1338+
}
1339+
1340+
func voidFuncWithArgs(arg1: Int) {
1341+
return 1
1342+
// expected-error@-1 {{unexpected non-void return value in void function}}
1343+
// expected-note@-2 {{did you mean to add a return type?}}{{33-33= -> <#Return Type#>}}
1344+
}
1345+
1346+
func voidFuncWithCondFlow() {
1347+
if Bool.random() {
1348+
return 1
1349+
// expected-error@-1 {{unexpected non-void return value in void function}}
1350+
// expected-note@-2 {{did you mean to add a return type?}}{{28-28= -> <#Return Type#>}}
1351+
} else {
1352+
return 2
1353+
// expected-error@-1 {{unexpected non-void return value in void function}}
1354+
// expected-note@-2 {{did you mean to add a return type?}}{{28-28= -> <#Return Type#>}}
1355+
}
1356+
}
1357+
1358+
func voidFuncWithNestedVoidFunc() {
1359+
func nestedVoidFunc() {
1360+
return 1
1361+
// expected-error@-1 {{unexpected non-void return value in void function}}
1362+
// expected-note@-2 {{did you mean to add a return type?}}{{24-24= -> <#Return Type#>}}
1363+
}
1364+
}
1365+
1366+
// Special cases: These should not offer a note + fix-it
1367+
1368+
func voidFuncExplicitType() -> Void {
1369+
return 1 // expected-error {{unexpected non-void return value in void function}}
1370+
}
1371+
1372+
class ClassWithDeinit {
1373+
deinit {
1374+
return 0 // expected-error {{unexpected non-void return value in void function}}
1375+
}
1376+
}
1377+
1378+
class ClassWithVoidProp {
1379+
var propertyWithVoidType: () { return 5 } // expected-error {{unexpected non-void return value in void function}}
1380+
}
1381+
1382+
class ClassWithPropContainingSetter {
1383+
var propWithSetter: Int {
1384+
get { return 0 }
1385+
set { return 1 } // expected-error {{unexpected non-void return value in void function}}
1386+
}
1387+
}

test/Constraints/overload.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ func test_contextual_result_1() {
135135
}
136136

137137
func test_contextual_result_2() {
138-
return overloaded_identity(1) // expected-error {{unexpected non-void return value in void function}}
138+
return overloaded_identity(1)
139+
// expected-error@-1 {{unexpected non-void return value in void function}}
140+
// expected-note@-2 {{did you mean to add a return type?}}
139141
}
140142

141143
// rdar://problem/24128153

test/Misc/misc_diagnostics.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class A {
5656
}
5757
}
5858

59-
func retV() { return true } // expected-error {{unexpected non-void return value in void function}}
59+
func retV() { return true }
60+
// expected-error@-1 {{unexpected non-void return value in void function}}
61+
// expected-note@-2 {{did you mean to add a return type?}}
6062

6163
func retAI() -> Int {
6264
let a = [""]
@@ -65,7 +67,9 @@ func retAI() -> Int {
6567
}
6668

6769
func bad_return1() {
68-
return 42 // expected-error {{unexpected non-void return value in void function}}
70+
return 42
71+
// expected-error@-1 {{unexpected non-void return value in void function}}
72+
// expected-note@-2 {{did you mean to add a return type?}}
6973
}
7074

7175
func bad_return2() -> (Int, Int) {
@@ -74,15 +78,19 @@ func bad_return2() -> (Int, Int) {
7478

7579
// <rdar://problem/14096697> QoI: Diagnostics for trying to return values from void functions
7680
func bad_return3(lhs: Int, rhs: Int) {
77-
return lhs != 0 // expected-error {{unexpected non-void return value in void function}}
81+
return lhs != 0
82+
// expected-error@-1 {{unexpected non-void return value in void function}}
83+
// expected-note@-2 {{did you mean to add a return type?}}
7884
}
7985

8086
class MyBadReturnClass {
8187
static var intProperty = 42
8288
}
8389

8490
func ==(lhs:MyBadReturnClass, rhs:MyBadReturnClass) {
85-
return MyBadReturnClass.intProperty == MyBadReturnClass.intProperty // expected-error{{unexpected non-void return value in void function}}
91+
return MyBadReturnClass.intProperty == MyBadReturnClass.intProperty
92+
// expected-error@-1 {{unexpected non-void return value in void function}}
93+
// expected-note@-2 {{did you mean to add a return type?}}
8694
}
8795

8896

@@ -156,4 +164,3 @@ func tuple_splat2(_ q : (a : Int, b : Int)) {
156164
func is_foreign(a: AnyObject) -> Bool {
157165
return a is CGColor // expected-warning {{'is' test is always true because 'CGColor' is a Core Foundation type}}
158166
}
159-

0 commit comments

Comments
 (0)