self.parse_yield_expr(attrs)
} else if self.is_do_yeet() {
self.parse_yeet_expr(attrs)
- } else if self.eat_keyword(kw::Let) {
+ } else if self.check_keyword(kw::Let) {
+ self.manage_let_chains_context();
+ self.bump();
self.parse_let_expr(attrs)
} else if self.eat_keyword(kw::Underscore) {
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
Ok(cond)
}
+ // Checks if `let` is in an invalid position like `let x = let y = 1;` or
+ // if the current `let` is in a let_chains context but nested in another
+ // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`.
+ //
+ // This method expects that the current token is `let`.
+ fn manage_let_chains_context(&mut self) {
+ debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _)));
+ let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed
+ && !matches!(
+ self.prev_token.kind,
+ TokenKind::AndAnd
+ | TokenKind::CloseDelim(Delimiter::Brace)
+ | TokenKind::Ident(kw::If, _)
+ | TokenKind::Ident(kw::While, _)
+ );
+ if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr {
+ self.struct_span_err(self.token.span, "expected expression, found `let` statement")
+ .emit();
+ }
+ }
+
/// Parses a `let $pat = $expr` pseudo-expression.
/// The `let` token has already been eaten.
fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
- if !self.let_expr_allowed {
- self.struct_span_err(
- self.prev_token.span,
- "expected expression, found `let` statement",
- )
- .emit();
- }
let lo = self.prev_token.span;
let pat = self.parse_pat_allow_top_alt(
None,
}
};
+ let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand);
+ // A shorthand field can be turned into a full field with `:`.
+ // We should point this out.
+ self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon));
+
match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) {
Ok(_) => {
if let Some(f) = parsed_field.or(recovery_field) {
",",
Applicability::MachineApplicable,
);
+ } else if is_shorthand
+ && (AssocOp::from_token(&self.token).is_some()
+ || matches!(&self.token.kind, token::OpenDelim(_))
+ || self.token.kind == token::Dot)
+ {
+ // Looks like they tried to write a shorthand, complex expression.
+ let ident = parsed_field.expect("is_shorthand implies Some").ident;
+ e.span_suggestion(
+ ident.span.shrink_to_lo(),
+ "try naming a field",
+ &format!("{ident}: "),
+ Applicability::HasPlaceholders,
+ );
}
}
if !recover {