Detect blocks that could be struct expr bodies
This approach lives exclusively in the parser, so struct expr bodies that are syntactically correct on their own but are otherwise incorrect will still emit confusing errors, like in the following case: ```rust fn foo() -> Foo { bar: Vec::new() } ``` ``` error[E0425]: cannot find value `bar` in this scope --> src/file.rs:5:5 | 5 | bar: Vec::new() | ^^^ expecting a type here because of type ascription error[E0214]: parenthesized type parameters may only be used with a `Fn` trait --> src/file.rs:5:15 | 5 | bar: Vec::new() | ^^^^^ only `Fn` traits may use parentheses error[E0107]: wrong number of type arguments: expected 1, found 0 --> src/file.rs:5:10 | 5 | bar: Vec::new() | ^^^^^^^^^^ expected 1 type argument ``` If that field had a trailing comma, that would be a parse error and it would trigger the new, more targetted, error: ``` error: struct literal body without path --> file.rs:4:17 | 4 | fn foo() -> Foo { | _________________^ 5 | | bar: Vec::new(), 6 | | } | |_^ | help: you might have forgotten to add the struct literal inside the block | 4 | fn foo() -> Foo { Path { 5 | bar: Vec::new(), 6 | } } | ``` Partially address last part of #34255.
This commit is contained in:
parent
deec530523
commit
e5f83bcd04
7 changed files with 183 additions and 17 deletions
|
@ -5,8 +5,9 @@ use rustc_ast::ptr::P;
|
|||
use rustc_ast::token::{self, Lit, LitKind, TokenKind};
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_ast::{
|
||||
self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr,
|
||||
ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
|
||||
self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr,
|
||||
ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty,
|
||||
TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
@ -119,6 +120,28 @@ crate enum ConsumeClosingDelim {
|
|||
No,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AttemptLocalParseRecovery {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl AttemptLocalParseRecovery {
|
||||
pub fn yes(&self) -> bool {
|
||||
match self {
|
||||
AttemptLocalParseRecovery::Yes => true,
|
||||
AttemptLocalParseRecovery::No => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no(&self) -> bool {
|
||||
match self {
|
||||
AttemptLocalParseRecovery::Yes => false,
|
||||
AttemptLocalParseRecovery::No => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub(super) fn span_fatal_err<S: Into<MultiSpan>>(
|
||||
&self,
|
||||
|
@ -321,6 +344,66 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn maybe_suggest_struct_literal(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
s: BlockCheckMode,
|
||||
) -> Option<PResult<'a, P<Block>>> {
|
||||
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
|
||||
// We might be having a struct literal where people forgot to include the path:
|
||||
// fn foo() -> Foo {
|
||||
// field: value,
|
||||
// }
|
||||
let mut snapshot = self.clone();
|
||||
let path =
|
||||
Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None };
|
||||
let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false);
|
||||
let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
|
||||
return Some(match (struct_expr, block_tail) {
|
||||
(Ok(expr), Err(mut err)) => {
|
||||
// We have encountered the following:
|
||||
// fn foo() -> Foo {
|
||||
// field: value,
|
||||
// }
|
||||
// Suggest:
|
||||
// fn foo() -> Foo { Path {
|
||||
// field: value,
|
||||
// } }
|
||||
err.delay_as_bug();
|
||||
self.struct_span_err(expr.span, "struct literal body without path")
|
||||
.multipart_suggestion(
|
||||
"you might have forgotten to add the struct literal inside the block",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
|
||||
(expr.span.shrink_to_hi(), " }".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
*self = snapshot;
|
||||
Ok(self.mk_block(
|
||||
vec![self.mk_stmt_err(expr.span)],
|
||||
s,
|
||||
lo.to(self.prev_token.span),
|
||||
))
|
||||
}
|
||||
(Err(mut err), Ok(tail)) => {
|
||||
// We have a block tail that contains a somehow valid type ascription expr.
|
||||
err.cancel();
|
||||
Ok(tail)
|
||||
}
|
||||
(Err(mut snapshot_err), Err(err)) => {
|
||||
// We don't know what went wrong, emit the normal error.
|
||||
snapshot_err.cancel();
|
||||
self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
|
||||
Err(err)
|
||||
}
|
||||
(Ok(_), Ok(tail)) => Ok(tail),
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn maybe_annotate_with_ascription(
|
||||
&mut self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue