Skip to content

Commit d14e281

Browse files
Parser: Suggest Placing the Return Type After Function Parameters
1 parent 5834772 commit d14e281

File tree

4 files changed

+84
-19
lines changed

4 files changed

+84
-19
lines changed

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,8 @@ parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}
526526
.label_opening_candidate = closing delimiter possibly meant for this
527527
.label_unclosed = unclosed delimiter
528528
529+
parse_misplaced_return_type = place the return type after the function parameters
530+
529531
parse_missing_comma_after_match_arm = expected `,` following `match` arm
530532
.suggestion = missing a comma here to end this `match` arm
531533

compiler/rustc_parse/src/errors.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,20 @@ pub(crate) struct FnPtrWithGenerics {
14341434
pub sugg: Option<FnPtrWithGenericsSugg>,
14351435
}
14361436

1437+
#[derive(Subdiagnostic)]
1438+
#[multipart_suggestion(
1439+
parse_misplaced_return_type,
1440+
style = "verbose",
1441+
applicability = "maybe-incorrect"
1442+
)]
1443+
pub(crate) struct MisplacedReturnType {
1444+
#[suggestion_part(code = " {snippet}")]
1445+
pub fn_params_end: Span,
1446+
pub snippet: String,
1447+
#[suggestion_part(code = "")]
1448+
pub ret_ty_span: Span,
1449+
}
1450+
14371451
#[derive(Subdiagnostic)]
14381452
#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
14391453
pub(crate) struct FnPtrWithGenericsSugg {
@@ -1448,7 +1462,6 @@ pub(crate) struct FnPtrWithGenericsSugg {
14481462

14491463
pub(crate) struct FnTraitMissingParen {
14501464
pub span: Span,
1451-
pub machine_applicable: bool,
14521465
}
14531466

14541467
impl Subdiagnostic for FnTraitMissingParen {
@@ -1458,16 +1471,11 @@ impl Subdiagnostic for FnTraitMissingParen {
14581471
_: &F,
14591472
) {
14601473
diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
1461-
let applicability = if self.machine_applicable {
1462-
Applicability::MachineApplicable
1463-
} else {
1464-
Applicability::MaybeIncorrect
1465-
};
14661474
diag.span_suggestion_short(
14671475
self.span.shrink_to_hi(),
14681476
crate::fluent_generated::parse_add_paren,
14691477
"()",
1470-
applicability,
1478+
Applicability::MachineApplicable,
14711479
);
14721480
}
14731481
}

compiler/rustc_parse/src/parser/item.rs

+62-12
Original file line numberDiff line numberDiff line change
@@ -2332,10 +2332,21 @@ impl<'a> Parser<'a> {
23322332
}
23332333
}
23342334
};
2335+
2336+
// Store the end of function parameters to give better diagnostics
2337+
// inside `parse_fn_body()`.
2338+
let fn_params_end = self.prev_token.span.shrink_to_hi();
2339+
23352340
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
23362341

2342+
// `fn_params_end` is needed only when it's followed by a where clause.
2343+
let fn_params_end =
2344+
if generics.where_clause.has_where_token { Some(fn_params_end) } else { None };
2345+
23372346
let mut sig_hi = self.prev_token.span;
2338-
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
2347+
// Either `;` or `{ ... }`.
2348+
let body =
2349+
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
23392350
let fn_sig_span = sig_lo.to(sig_hi);
23402351
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
23412352
}
@@ -2349,6 +2360,7 @@ impl<'a> Parser<'a> {
23492360
ident: &Ident,
23502361
sig_hi: &mut Span,
23512362
req_body: bool,
2363+
fn_params_end: Option<Span>,
23522364
) -> PResult<'a, Option<P<Block>>> {
23532365
let has_semi = if req_body {
23542366
self.token.kind == TokenKind::Semi
@@ -2388,20 +2400,58 @@ impl<'a> Parser<'a> {
23882400
// the AST for typechecking.
23892401
err.span_label(ident.span, "while parsing this `fn`");
23902402
err.emit();
2391-
} else {
2392-
// check for typo'd Fn* trait bounds such as
2393-
// fn foo<F>() where F: FnOnce -> () {}
2394-
if self.token.kind == token::RArrow {
2395-
let machine_applicable = [sym::FnOnce, sym::FnMut, sym::Fn]
2396-
.into_iter()
2397-
.any(|s| self.prev_token.is_ident_named(s));
2398-
2399-
err.subdiagnostic(errors::FnTraitMissingParen {
2400-
span: self.prev_token.span,
2401-
machine_applicable,
2403+
} else if self.token.kind == token::RArrow
2404+
&& let Some(fn_params_end) = fn_params_end
2405+
{
2406+
// Instead of a function body, the parser has encountered a right arrow
2407+
// preceded by a where clause.
2408+
2409+
// Find whether token behind the right arrow is a function trait and
2410+
// store its span.
2411+
let prev_token_is_fn_trait = [sym::FnOnce, sym::FnMut, sym::Fn]
2412+
.into_iter()
2413+
.any(|s| self.prev_token.is_ident_named(s));
2414+
let fn_trait_span = self.prev_token.span;
2415+
2416+
// Parse the return type (along with the right arrow) and store its span.
2417+
// If there's a parse error, cancel it and return the existing error
2418+
// as we are primarily concerned with the
2419+
// expected-function-body-but-found-something-else error here.
2420+
let arrow_span = self.token.span;
2421+
let ty_span = match self.parse_ret_ty(
2422+
AllowPlus::Yes,
2423+
RecoverQPath::Yes,
2424+
RecoverReturnSign::Yes,
2425+
) {
2426+
Ok(ty_span) => ty_span.span().shrink_to_hi(),
2427+
Err(parse_error) => {
2428+
parse_error.cancel();
2429+
return Err(err);
2430+
}
2431+
};
2432+
let ret_ty_span = arrow_span.to(ty_span);
2433+
2434+
if prev_token_is_fn_trait {
2435+
// Typo'd Fn* trait bounds such as
2436+
// fn foo<F>() where F: FnOnce -> () {}
2437+
err.subdiagnostic(errors::FnTraitMissingParen { span: fn_trait_span });
2438+
} else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span)
2439+
{
2440+
// If token behind right arrow is not a Fn* trait, the programmer
2441+
// probably misplaced the return type after the where clause like
2442+
// `fn foo<T>() where T: Default -> u8 {}`
2443+
err.primary_message(
2444+
"return type should be specified after the function parameters",
2445+
);
2446+
err.subdiagnostic(errors::MisplacedReturnType {
2447+
fn_params_end,
2448+
snippet,
2449+
ret_ty_span,
24022450
});
24032451
}
24042452
return Err(err);
2453+
} else {
2454+
return Err(err);
24052455
}
24062456
}
24072457
(AttrVec::new(), None)

compiler/rustc_parse/src/parser/ty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ use rustc_span::symbol::{kw, sym, Ident};
2121
use rustc_span::{ErrorGuaranteed, Span, Symbol};
2222
use thin_vec::{thin_vec, ThinVec};
2323

24+
/// Signals whether parsing a type should allow `+`.
25+
///
26+
/// For example, let T be the type `impl Default + 'static`
27+
/// With `AllowPlus::Yes`, T will be parsed successfully
28+
/// With `AllowPlus::No`, parsing T will return a parse error
2429
#[derive(Copy, Clone, PartialEq)]
2530
pub(super) enum AllowPlus {
2631
Yes,

0 commit comments

Comments
 (0)