Skip to content

Commit 6951e46

Browse files
committed
If 'throws' is written after the result type in a function decl, suggest
moving it to before the arrow. <rdar://problem/20857518> QoI for "throws" in the wrong place Swift SVN r28492
1 parent 07251be commit 6951e46

File tree

5 files changed

+54
-15
lines changed

5 files changed

+54
-15
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ namespace swift {
381381
return fixItReplace(R, {});
382382
}
383383

384-
/// \brief Add a token-based removal fix-it to the currently-active
384+
/// \brief Add a character-based removal fix-it to the currently-active
385385
/// diagnostic.
386386
InFlightDiagnostic &fixItRemoveChars(SourceLoc Start, SourceLoc End) {
387387
return fixItReplaceChars(Start, End, {});

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@ ERROR(throwing_non_function,type_parsing,PointsToFirstBadToken,
577577
"only function types may throw", ())
578578
ERROR(rethrowing_function_type,type_parsing,PointsToFirstBadToken,
579579
"only function declarations may be marked 'rethrows'", ())
580+
ERROR(throws_after_function_result,type_parsing,none,
581+
"'throws' may only occur before '->'", ())
582+
ERROR(rethrows_after_function_result,type_parsing,none,
583+
"'rethrows' may only occur before '->'", ())
580584

581585
// Enum Types
582586
ERROR(expected_expr_enum_case_raw_value,type_parsing,PointsToFirstBadToken,

lib/Parse/ParsePattern.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,13 +629,14 @@ Parser::parseFunctionSignature(Identifier SimpleName,
629629
rethrows = true;
630630
}
631631

632+
SourceLoc arrowLoc;
632633
// If there's a trailing arrow, parse the rest as the result type.
633634
if (Tok.isAny(tok::arrow, tok::colon)) {
634-
if (!consumeIf(tok::arrow)) {
635+
if (!consumeIf(tok::arrow, arrowLoc)) {
635636
// FixIt ':' to '->'.
636637
diagnose(Tok, diag::func_decl_expected_arrow)
637638
.fixItReplace(SourceRange(Tok.getLoc()), "->");
638-
consumeToken(tok::colon);
639+
arrowLoc = consumeToken(tok::colon);
639640
}
640641

641642
ParserResult<TypeRepr> ResultType =
@@ -652,6 +653,29 @@ Parser::parseFunctionSignature(Identifier SimpleName,
652653
retType = nullptr;
653654
}
654655

656+
// Check for 'throws' and 'rethrows' after the type and correct it.
657+
if (!throwsLoc.isValid()) {
658+
if (Tok.is(tok::kw_throws)) {
659+
throwsLoc = consumeToken();
660+
} else if (Tok.is(tok::kw_rethrows)) {
661+
throwsLoc = consumeToken();
662+
rethrows = true;
663+
}
664+
665+
if (throwsLoc.isValid()) {
666+
assert(arrowLoc.isValid());
667+
assert(retType);
668+
auto diag = rethrows ? diag::rethrows_after_function_result
669+
: diag::throws_after_function_result;
670+
SourceLoc typeEndLoc = Lexer::getLocForEndOfToken(SourceMgr,
671+
retType->getEndLoc());
672+
SourceLoc throwsEndLoc = Lexer::getLocForEndOfToken(SourceMgr, throwsLoc);
673+
diagnose(Tok, diag)
674+
.fixItInsert(arrowLoc, rethrows ? "rethrows " : "throws ")
675+
.fixItRemoveChars(typeEndLoc, throwsEndLoc);
676+
}
677+
}
678+
655679
return Status;
656680
}
657681

lib/Parse/ParseType.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,13 @@ ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID) {
163163
if (ty.isNull())
164164
return nullptr;
165165

166+
ParserPosition beforeThrowsPos;
166167
SourceLoc throwsLoc;
167-
if (consumeIf(tok::kw_throws, throwsLoc)) {
168-
assert(throwsLoc.isValid());
169-
} else if (consumeIf(tok::kw_rethrows, throwsLoc)) {
170-
// 'rethrows' is only allowed on function declarations for now.
171-
diagnose(throwsLoc, diag::rethrowing_function_type);
168+
bool rethrows = false;
169+
if (Tok.isAny(tok::kw_throws, tok::kw_rethrows)) {
170+
beforeThrowsPos = getParserPosition();
171+
rethrows = Tok.is(tok::kw_rethrows);
172+
throwsLoc = consumeToken();
172173
}
173174

174175
// Handle type-function if we have an arrow.
@@ -180,20 +181,22 @@ ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID) {
180181
return makeParserCodeCompletionResult<TypeRepr>();
181182
if (SecondHalf.isNull())
182183
return nullptr;
184+
if (rethrows) {
185+
// 'rethrows' is only allowed on function declarations for now.
186+
diagnose(throwsLoc, diag::rethrowing_function_type);
187+
}
183188
auto fnTy = new (Context) FunctionTypeRepr(generics, ty.get(),
184189
throwsLoc,
185190
arrowLoc,
186191
SecondHalf.get());
187192
return makeParserResult(applyAttributeToType(fnTy, attrs));
193+
} else if (throwsLoc.isValid()) {
194+
// Don't consume 'throws', so we can emit a more useful diagnostic when
195+
// parsing a function decl.
196+
restoreParserPosition(beforeThrowsPos);
197+
return ty;
188198
}
189199

190-
// Only function types may throw.
191-
if (throwsLoc.isValid()) {
192-
diagnose(throwsLoc, diag::throwing_non_function)
193-
.highlight(ty.get()->getSourceRange());
194-
return nullptr;
195-
}
196-
197200
// Only function types may be generic.
198201
if (generics) {
199202
auto brackets = generics->getSourceRange();

test/Parse/errors.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ func illformed() throws {
7777
} catch MSV.CarriesInt(let i) where i == genError()) { // expected-error {{call can throw, but errors cannot be thrown out of a catch guard expression}} expected-error {{expected '{'}} expected-error {{braced block of statements is an unused closure}} expected-error {{type of expression is ambiguous without more context}}
7878
}
7979
}
80+
81+
func postThrows() -> Int throws { // expected-error{{'throws' may only occur before '->'}}{{19-19=throws }}{{25-32=}}
82+
return 5
83+
}
84+
85+
func postRethrows(f: () throws -> Int) -> Int rethrows { // expected-error{{'rethrows' may only occur before '->'}}{{40-40=rethrows }}{{46-55=}}
86+
return try f()
87+
}

0 commit comments

Comments
 (0)