Skip to content

Commit 4cad705

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

File tree

6 files changed

+123
-39
lines changed

6 files changed

+123
-39
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/diagnostics.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ impl<'a> Parser<'a> {
430430
&mut self,
431431
edible: &[TokenKind],
432432
inedible: &[TokenKind],
433-
) -> PResult<'a, Recovered> {
433+
) -> PResult<'a, ErrorGuaranteed> {
434434
debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
435435
fn tokens_to_string(tokens: &[TokenType]) -> String {
436436
let mut i = tokens.iter();
@@ -533,7 +533,7 @@ impl<'a> Parser<'a> {
533533
sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
534534
});
535535
self.bump();
536-
return Ok(Recovered::Yes(guar));
536+
return Ok(guar);
537537
} else if self.look_ahead(0, |t| {
538538
t == &token::CloseDelim(Delimiter::Brace)
539539
|| ((t.can_begin_expr() || t.can_begin_item())
@@ -557,7 +557,7 @@ impl<'a> Parser<'a> {
557557
unexpected_token_label: Some(self.token.span),
558558
sugg: ExpectedSemiSugg::AddSemi(span),
559559
});
560-
return Ok(Recovered::Yes(guar));
560+
return Ok(guar);
561561
}
562562
}
563563

@@ -712,7 +712,7 @@ impl<'a> Parser<'a> {
712712
if self.check_too_many_raw_str_terminators(&mut err) {
713713
if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
714714
let guar = err.emit();
715-
return Ok(Recovered::Yes(guar));
715+
return Ok(guar);
716716
} else {
717717
return Err(err);
718718
}

compiler/rustc_parse/src/parser/item.rs

+96-28
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_span::edit_distance::edit_distance;
1919
use rustc_span::edition::Edition;
2020
use rustc_span::source_map;
2121
use rustc_span::symbol::{kw, sym, Ident, Symbol};
22+
use rustc_span::ErrorGuaranteed;
2223
use rustc_span::{Span, DUMMY_SP};
2324
use std::fmt::Write;
2425
use std::mem;
@@ -2332,14 +2333,106 @@ impl<'a> Parser<'a> {
23322333
}
23332334
}
23342335
};
2336+
2337+
// Store the end of function parameters to give better diagnostics
2338+
// inside `parse_fn_body()`.
2339+
let fn_params_end = self.prev_token.span.shrink_to_hi();
2340+
23352341
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
23362342

2343+
// `fn_params_end` is needed only when it's followed by a where clause.
2344+
let fn_params_end =
2345+
if generics.where_clause.has_where_token { Some(fn_params_end) } else { None };
2346+
23372347
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 `{ ... }`.
2348+
// Either `;` or `{ ... }`.
2349+
let body =
2350+
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
23392351
let fn_sig_span = sig_lo.to(sig_hi);
23402352
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
23412353
}
23422354

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

compiler/rustc_parse/src/parser/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ impl<'a> Parser<'a> {
506506
FatalError.raise();
507507
} else {
508508
self.expected_one_of_not_found(edible, inedible)
509+
.map(|error_guaranteed| Recovered::Yes(error_guaranteed))
509510
}
510511
}
511512

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)