Skip to content

Commit 7ca42a2

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

File tree

4 files changed

+116
-35
lines changed

4 files changed

+116
-35
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

+94-28
Original file line numberDiff line numberDiff line change
@@ -2332,14 +2332,105 @@ 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
}
23422353

2354+
/// Provide diagnostics when function body is not found
2355+
fn error_fn_body_not_found(
2356+
&mut self,
2357+
ident_span: Span,
2358+
req_body: bool,
2359+
fn_params_end: Option<Span>,
2360+
) -> PResult<'a, ()> {
2361+
let expected = if req_body {
2362+
&[token::OpenDelim(Delimiter::Brace)][..]
2363+
} else {
2364+
&[token::Semi, token::OpenDelim(Delimiter::Brace)]
2365+
};
2366+
if let Err(mut err) = self.expected_one_of_not_found(&[], expected) {
2367+
if self.token.kind == token::CloseDelim(Delimiter::Brace) {
2368+
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
2369+
// the AST for typechecking.
2370+
err.span_label(ident_span, "while parsing this `fn`");
2371+
err.emit();
2372+
Ok(())
2373+
} else if self.token.kind == token::RArrow
2374+
&& let Some(fn_params_end) = fn_params_end
2375+
{
2376+
// Instead of a function body, the parser has encountered a right arrow
2377+
// preceded by a where clause.
2378+
2379+
// Find whether token behind the right arrow is a function trait and
2380+
// store its span.
2381+
let fn_trait_span =
2382+
[sym::FnOnce, sym::FnMut, sym::Fn].into_iter().find_map(|symbol| {
2383+
if self.prev_token.is_ident_named(symbol) {
2384+
Some(self.prev_token.span)
2385+
} else {
2386+
None
2387+
}
2388+
});
2389+
2390+
// Parse the return type (along with the right arrow) and store its span.
2391+
// If there's a parse error, cancel it and return the existing error
2392+
// as we are primarily concerned with the
2393+
// expected-function-body-but-found-something-else error here.
2394+
let arrow_span = self.token.span;
2395+
let ty_span = match self.parse_ret_ty(
2396+
AllowPlus::Yes,
2397+
RecoverQPath::Yes,
2398+
RecoverReturnSign::Yes,
2399+
) {
2400+
Ok(ty_span) => ty_span.span().shrink_to_hi(),
2401+
Err(parse_error) => {
2402+
parse_error.cancel();
2403+
return Err(err);
2404+
}
2405+
};
2406+
let ret_ty_span = arrow_span.to(ty_span);
2407+
2408+
if let Some(fn_trait_span) = fn_trait_span {
2409+
// Typo'd Fn* trait bounds such as
2410+
// fn foo<F>() where F: FnOnce -> () {}
2411+
err.subdiagnostic(errors::FnTraitMissingParen { span: fn_trait_span });
2412+
} else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span) {
2413+
// If token behind right arrow is not a Fn* trait, the programmer
2414+
// probably misplaced the return type after the where clause like
2415+
// `fn foo<T>() where T: Default -> u8 {}`
2416+
err.primary_message(
2417+
"return type should be specified after the function parameters",
2418+
);
2419+
err.subdiagnostic(errors::MisplacedReturnType {
2420+
fn_params_end,
2421+
snippet,
2422+
ret_ty_span,
2423+
});
2424+
}
2425+
Err(err)
2426+
} else {
2427+
Err(err)
2428+
}
2429+
} else {
2430+
Ok(())
2431+
}
2432+
}
2433+
23432434
/// Parse the "body" of a function.
23442435
/// This can either be `;` when there's no body,
23452436
/// or e.g. a block when the function is a provided one.
@@ -2349,6 +2440,7 @@ impl<'a> Parser<'a> {
23492440
ident: &Ident,
23502441
sig_hi: &mut Span,
23512442
req_body: bool,
2443+
fn_params_end: Option<Span>,
23522444
) -> PResult<'a, Option<P<Block>>> {
23532445
let has_semi = if req_body {
23542446
self.token.kind == TokenKind::Semi
@@ -2377,33 +2469,7 @@ impl<'a> Parser<'a> {
23772469
});
23782470
(AttrVec::new(), Some(self.mk_block_err(span, guar)))
23792471
} else {
2380-
let expected = if req_body {
2381-
&[token::OpenDelim(Delimiter::Brace)][..]
2382-
} else {
2383-
&[token::Semi, token::OpenDelim(Delimiter::Brace)]
2384-
};
2385-
if let Err(mut err) = self.expected_one_of_not_found(&[], expected) {
2386-
if self.token.kind == token::CloseDelim(Delimiter::Brace) {
2387-
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
2388-
// the AST for typechecking.
2389-
err.span_label(ident.span, "while parsing this `fn`");
2390-
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,
2402-
});
2403-
}
2404-
return Err(err);
2405-
}
2406-
}
2472+
self.error_fn_body_not_found(ident.span, req_body, fn_params_end)?;
24072473
(AttrVec::new(), None)
24082474
};
24092475
attrs.extend(inner_attrs);

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)