Skip to content

[CSDiagnostics] Offer a fix-it to insert a return type placeholder when returning from a void function #29747

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,8 @@ NOTE(selector_construction_suppress_warning,none,

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

//------------------------------------------------------------------------------
// MARK: Name Binding
Expand Down
17 changes: 17 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4792,6 +4792,23 @@ bool InvalidUseOfAddressOf::diagnoseAsError() {
bool ExtraneousReturnFailure::diagnoseAsError() {
auto *anchor = getAnchor();
emitDiagnostic(anchor->getLoc(), diag::cannot_return_value_from_void_func);
if (auto FD = dyn_cast<FuncDecl>(getDC())) {
// We only want to emit the note + fix-it if the function does not
// have an explicit return type. The reason we also need to check
// whether the parameter list has a valid loc is to guard against
// cases like like 'var foo: () { return 1 }' as here that loc will
// be invalid. We also need to check that the name is not empty,
// because certain decls will have empty name (like setters).
if (FD->getBodyResultTypeLoc().getLoc().isInvalid() &&
FD->getParameters()->getStartLoc().isValid() &&
!FD->getName().empty()) {
auto fixItLoc = Lexer::getLocForEndOfToken(
getASTContext().SourceMgr, FD->getParameters()->getEndLoc());
emitDiagnostic(anchor->getLoc(), diag::add_return_type_note)
.fixItInsert(fixItLoc, " -> <#Return Type#>");
}
}

return true;
}

Expand Down
86 changes: 78 additions & 8 deletions test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ protocol Shoes {

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

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

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

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

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

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

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

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

// SR-1255
func foo1255_1() {
return true || false // expected-error {{unexpected non-void return value in void function}}
return true || false
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}
}
func foo1255_2() -> Int {
return true || false // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
Expand Down Expand Up @@ -1150,6 +1160,7 @@ func sr5045() {
let doubles: [Double] = [1, 2, 3]
return doubles.reduce(0, +)
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}
}

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

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

Expand Down Expand Up @@ -1315,3 +1328,60 @@ takesGenericFunction(true) // expected-error {{cannot convert value of type 'Boo
func takesTuple<T>(_ x: ([T], [T])) {} // expected-note {{in call to function 'takesTuple'}}
takesTuple(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([T], [T])'}}
// expected-error@-1 {{generic parameter 'T' could not be inferred}}

// Void function returns non-void result fix-it

func voidFunc() {
return 1
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}{{16-16= -> <#Return Type#>}}
}

func voidFuncWithArgs(arg1: Int) {
return 1
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}{{33-33= -> <#Return Type#>}}
}

func voidFuncWithCondFlow() {
if Bool.random() {
return 1
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}{{28-28= -> <#Return Type#>}}
} else {
return 2
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}{{28-28= -> <#Return Type#>}}
}
}

func voidFuncWithNestedVoidFunc() {
func nestedVoidFunc() {
return 1
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}{{24-24= -> <#Return Type#>}}
}
}

// Special cases: These should not offer a note + fix-it

func voidFuncExplicitType() -> Void {
return 1 // expected-error {{unexpected non-void return value in void function}}
}

class ClassWithDeinit {
deinit {
return 0 // expected-error {{unexpected non-void return value in void function}}
}
}

class ClassWithVoidProp {
var propertyWithVoidType: () { return 5 } // expected-error {{unexpected non-void return value in void function}}
}

class ClassWithPropContainingSetter {
var propWithSetter: Int {
get { return 0 }
set { return 1 } // expected-error {{unexpected non-void return value in void function}}
}
}
4 changes: 3 additions & 1 deletion test/Constraints/overload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ func test_contextual_result_1() {
}

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

// rdar://problem/24128153
Expand Down
17 changes: 12 additions & 5 deletions test/Misc/misc_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class A {
}
}

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

func retAI() -> Int {
let a = [""]
Expand All @@ -65,7 +67,9 @@ func retAI() -> Int {
}

func bad_return1() {
return 42 // expected-error {{unexpected non-void return value in void function}}
return 42
// expected-error@-1 {{unexpected non-void return value in void function}}
// expected-note@-2 {{did you mean to add a return type?}}
}

func bad_return2() -> (Int, Int) {
Expand All @@ -74,15 +78,19 @@ func bad_return2() -> (Int, Int) {

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

class MyBadReturnClass {
static var intProperty = 42
}

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


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