diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 0a7bbbdae92ba..b072665a9d8c3 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -619,8 +619,9 @@ ERROR(expected_type_function_result,PointsToFirstBadToken, "expected type for function result", ()) ERROR(generic_non_function,PointsToFirstBadToken, "only syntactic function types can be generic", ()) -ERROR(rethrowing_function_type,PointsToFirstBadToken, - "only function declarations may be marked 'rethrows'", ()) +ERROR(rethrowing_function_type,none, + "only function declarations may be marked 'rethrows'; " + "did you mean 'throws'?", ()) ERROR(throws_in_wrong_position,none, "'throws' may only occur before '->'", ()) ERROR(rethrows_in_wrong_position,none, diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 2f0e577c89639..fc38af720dafc 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2217,7 +2217,8 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, throwsLoc = consumeToken(); } else if (Tok.is(tok::kw_rethrows)) { throwsLoc = consumeToken(); - diagnose(throwsLoc, diag::rethrowing_function_type); + diagnose(throwsLoc, diag::rethrowing_function_type) + .fixItReplace(throwsLoc, "throws"); } // Parse the optional explicit return type. diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index f5c68ad400f89..1ae1aecbea8ee 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -184,6 +184,7 @@ ParserResult Parser::parseType() { /// /// type-function: /// type-composition '->' type +/// type-composition 'throws' '->' type /// ParserResult Parser::parseType(Diag<> MessageID, bool HandleCodeCompletion) { @@ -203,62 +204,45 @@ ParserResult Parser::parseType(Diag<> MessageID, parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion); if (ty.hasCodeCompletion()) return makeParserCodeCompletionResult(); - if (ty.isNull()) return nullptr; + auto tyR = ty.get(); - // Parse a throws specifier. 'throw' is probably a typo for 'throws', - // but in local contexts we could just be at the end of a statement, - // so we need to check for the arrow. - ParserPosition beforeThrowsPos; + // Parse a throws specifier. + // Don't consume 'throws', if the next token is not '->', so we can emit a + // more useful diagnostic when parsing a function decl. SourceLoc throwsLoc; - bool rethrows = false; - if (Tok.isAny(tok::kw_throws, tok::kw_rethrows) || - (Tok.is(tok::kw_throw) && peekToken().is(tok::arrow))) { - if (Tok.is(tok::kw_throw)) { - diagnose(Tok.getLoc(), diag::throw_in_function_type) + if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) && + peekToken().is(tok::arrow)) { + if (Tok.isNot(tok::kw_throws)) { + // 'rethrows' is only allowed on function declarations for now. + // 'throw' is probably a typo for 'throws'. + Diag<> DiagID = Tok.is(tok::kw_rethrows) ? + diag::rethrowing_function_type : diag::throw_in_function_type; + diagnose(Tok.getLoc(), DiagID) .fixItReplace(Tok.getLoc(), "throws"); } - - beforeThrowsPos = getParserPosition(); - rethrows = Tok.is(tok::kw_rethrows); throwsLoc = consumeToken(); } - // Handle type-function if we have an arrow. - SourceLoc arrowLoc; - if (consumeIf(tok::arrow, arrowLoc)) { + if (Tok.is(tok::arrow)) { + // Handle type-function if we have an arrow. + SourceLoc arrowLoc = consumeToken(); ParserResult SecondHalf = parseType(diag::expected_type_function_result); if (SecondHalf.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (SecondHalf.isNull()) return nullptr; - if (rethrows) { - // 'rethrows' is only allowed on function declarations for now. - diagnose(throwsLoc, diag::rethrowing_function_type); - } - auto fnTy = new (Context) FunctionTypeRepr(generics, ty.get(), - throwsLoc, - arrowLoc, - SecondHalf.get()); - return makeParserResult(applyAttributeToType(fnTy, inoutLoc, attrs)); - } else if (throwsLoc.isValid()) { - // Don't consume 'throws', so we can emit a more useful diagnostic when - // parsing a function decl. - restoreParserPosition(beforeThrowsPos); - } - - // Only function types may be generic. - if (generics) { + tyR = new (Context) FunctionTypeRepr(generics, tyR, throwsLoc, arrowLoc, + SecondHalf.get()); + } else if (generics) { + // Only function types may be generic. auto brackets = generics->getSourceRange(); diagnose(brackets.Start, diag::generic_non_function); } - if (ty.isNonNull() && !ty.hasCodeCompletion()) { - ty = makeParserResult(applyAttributeToType(ty.get(), inoutLoc, attrs)); - } - return ty; + return makeParserResult(applyAttributeToType(tyR, inoutLoc, attrs)); } ParserResult Parser::parseTypeForInheritance( diff --git a/test/Parse/errors.swift b/test/Parse/errors.swift index ab0de0b993dc0..75ad002f9ddbf 100644 --- a/test/Parse/errors.swift +++ b/test/Parse/errors.swift @@ -122,6 +122,13 @@ func postRethrows2(_ f: () throws -> Int) -> rethrows Int { // expected-error{{' return try f() } +func incompleteThrowType() { + // FIXME: Bad recovery for incomplete function type. + let _: () throws + // expected-error @-1 {{consecutive statements on a line must be separated by ';'}} + // expected-error @-2 {{expected expression}} +} + // rdar://21328447 func fixitThrow0() throw {} // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{20-25=throws}} func fixitThrow1() throw -> Int {} // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{20-25=throws}} diff --git a/test/decl/func/rethrows.swift b/test/decl/func/rethrows.swift index 5bced2f1e88fa..6d4c78a18e8f9 100644 --- a/test/decl/func/rethrows.swift +++ b/test/decl/func/rethrows.swift @@ -3,9 +3,9 @@ /** Basics *******************************************************************/ // Function types can't be rethrows right now. -let r1 = {() rethrows -> Int in 0} // expected-error {{only function declarations may be marked 'rethrows'}} -let r2 : () rethrows -> Int = { 0 } // expected-error {{only function declarations may be marked 'rethrows'}} -let r3 : Optional<() rethrows -> ()> = nil // expected-error {{only function declarations may be marked 'rethrows'}} +let r1 = {() rethrows -> Int in 0} // expected-error {{only function declarations may be marked 'rethrows'; did you mean 'throws'?}} {{14-22=throws}} +let r2 : () rethrows -> Int = { 0 } // expected-error {{only function declarations may be marked 'rethrows'; did you mean 'throws'?}} {{13-21=throws}} +let r3 : Optional<() rethrows -> ()> = nil // expected-error {{only function declarations may be marked 'rethrows'; did you mean 'throws'?}} {{22-30=throws}} func f1(_ f: () throws -> ()) rethrows { try f() } func f2(_ f: () -> ()) rethrows { f() } // expected-error {{'rethrows' function must take a throwing function argument}}