diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 182 | 
1 files changed, 137 insertions, 45 deletions
| diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3d29d305021..693dd0051da 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -213,11 +213,11 @@ impl<'a> Parser<'a> { } } + // Look for JS' `===` and `!==` and recover if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual) && self.token.kind == token::Eq && self.prev_token.span.hi() == self.token.span.lo() { - // Look for JS' `===` and `!==` and recover 😇 let sp = op.span.to(self.token.span); let sugg = match op.node { AssocOp::Equal => "==", @@ -235,6 +235,38 @@ impl<'a> Parser<'a> { self.bump(); } + // Look for PHP's `<>` and recover + if op.node == AssocOp::Less + && self.token.kind == token::Gt + && self.prev_token.span.hi() == self.token.span.lo() + { + let sp = op.span.to(self.token.span); + self.struct_span_err(sp, "invalid comparison operator `<>`") + .span_suggestion_short( + sp, + "`<>` is not a valid comparison operator, use `!=`", + "!=".to_string(), + Applicability::MachineApplicable, + ) + .emit(); + self.bump(); + } + + // Look for C++'s `<=>` and recover + if op.node == AssocOp::LessEqual + && self.token.kind == token::Gt + && self.prev_token.span.hi() == self.token.span.lo() + { + let sp = op.span.to(self.token.span); + self.struct_span_err(sp, "invalid comparison operator `<=>`") + .span_label( + sp, + "`<=>` is not a valid comparison operator, use `std::cmp::Ordering`", + ) + .emit(); + self.bump(); + } + let op = op.node; // Special cases: if op == AssocOp::As { @@ -650,7 +682,7 @@ impl<'a> Parser<'a> { // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); - let cast_expr = match self.parse_ty_no_plus() { + let cast_expr = match self.parse_as_cast_ty() { Ok(rhs) => mk_expr(self, lhs, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover @@ -776,7 +808,7 @@ impl<'a> Parser<'a> { "casts cannot be followed by {}", match with_postfix.kind { ExprKind::Index(_, _) => "indexing", - ExprKind::Try(_) => "?", + ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", ExprKind::MethodCall(_, _, _) => "a method call", ExprKind::Call(_, _) => "a function call", @@ -1032,6 +1064,8 @@ impl<'a> Parser<'a> { [IdentLike(_), Punct('+' | '-')] | // 1e+2 | 1e-2 [IdentLike(_), Punct('+' | '-'), IdentLike(_)] | + // 1.2e+ | 1.2e- + [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] | // 1.2e+3 | 1.2e-3 [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. @@ -1098,30 +1132,37 @@ impl<'a> Parser<'a> { snapshot.bump(); // `(` match snapshot.parse_struct_fields(path, false, token::Paren) { Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(token::Paren)) => { - // We have are certain we have `Enum::Foo(a: 3, b: 4)`, suggest + // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`. *self = snapshot; let close_paren = self.prev_token.span; let span = lo.to(self.prev_token.span); - err.cancel(); - self.struct_span_err( - span, - "invalid `struct` delimiters or `fn` call arguments", - ) - .multipart_suggestion( - &format!("if `{}` is a struct, use braces as delimiters", name), - vec![(open_paren, " { ".to_string()), (close_paren, " }".to_string())], - Applicability::MaybeIncorrect, - ) - .multipart_suggestion( - &format!("if `{}` is a function, use the arguments directly", name), - fields - .into_iter() - .map(|field| (field.span.until(field.expr.span), String::new())) - .collect(), - Applicability::MaybeIncorrect, - ) - .emit(); + if !fields.is_empty() { + err.cancel(); + let mut err = self.struct_span_err( + span, + "invalid `struct` delimiters or `fn` call arguments", + ); + err.multipart_suggestion( + &format!("if `{}` is a struct, use braces as delimiters", name), + vec![ + (open_paren, " { ".to_string()), + (close_paren, " }".to_string()), + ], + Applicability::MaybeIncorrect, + ); + err.multipart_suggestion( + &format!("if `{}` is a function, use the arguments directly", name), + fields + .into_iter() + .map(|field| (field.span.until(field.expr.span), String::new())) + .collect(), + Applicability::MaybeIncorrect, + ); + err.emit(); + } else { + err.emit(); + } return Some(self.mk_expr_err(span)); } Ok(_) => {} @@ -1148,7 +1189,7 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let mut segment = self.parse_path_segment(PathStyle::Expr)?; + let mut segment = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); self.check_turbofish_missing_angle_brackets(&mut segment); @@ -1241,7 +1282,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Unsafe) { self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) } else if self.check_inline_const(0) { - self.parse_const_block(lo.to(self.token.span)) + self.parse_const_block(lo.to(self.token.span), false) } else if self.is_do_catch_block() { self.recover_do_catch(attrs) } else if self.is_try_block() { @@ -1256,7 +1297,6 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Let) { self.parse_let_expr(attrs) } else if self.eat_keyword(kw::Underscore) { - self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { // Don't complain about bare semicolons after unclosed braces @@ -1403,7 +1443,7 @@ impl<'a> Parser<'a> { &mut self, label: Label, attrs: AttrVec, - consume_colon: bool, + mut consume_colon: bool, ) -> PResult<'a, P<Expr>> { let lo = label.ident.span; let label = Some(label); @@ -1416,6 +1456,12 @@ impl<'a> Parser<'a> { self.parse_loop_expr(label, lo, attrs) } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) { + // We're probably inside of a `Path<'a>` that needs a turbofish, so suppress the + // "must be followed by a colon" error, and the "expected one of" error. + self.diagnostic().delay_span_bug(lo, "this label wasn't parsed correctly"); + consume_colon = false; + Ok(self.mk_expr_err(lo)) } else { let msg = "expected `while`, `for`, `loop` or `{` after a label"; self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); @@ -1599,7 +1645,7 @@ impl<'a> Parser<'a> { next_token.kind { if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &symbol.as_str(); + let s = String::from("0.") + symbol.as_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); } @@ -1670,7 +1716,8 @@ impl<'a> Parser<'a> { ); } LitError::InvalidIntSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); + let suf = suffix.expect("suffix error with no suffix"); + let suf = suf.as_str(); if looks_like_width_suffix(&['i', 'u'], &suf) { // If it looks like a width, try to be helpful. let msg = format!("invalid width `{}` for integer literal", &suf[1..]); @@ -1686,8 +1733,9 @@ impl<'a> Parser<'a> { } } LitError::InvalidFloatSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); - if looks_like_width_suffix(&['f'], &suf) { + let suf = suffix.expect("suffix error with no suffix"); + let suf = suf.as_str(); + if looks_like_width_suffix(&['f'], suf) { // If it looks like a width, try to be helpful. let msg = format!("invalid width `{}` for float literal", &suf[1..]); self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit(); @@ -1986,25 +2034,34 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let cond = self.parse_cond_expr()?; + let missing_then_block_binop_span = || { + match cond.kind { + ExprKind::Binary(Spanned { span: binop_span, .. }, _, ref right) + if let ExprKind::Block(..) = right.kind => Some(binop_span), + _ => None + } + }; + // Verify that the parsed `if` condition makes sense as a condition. If it is a block, then // verify that the last statement is either an implicit return (no `;`) or an explicit // return. This won't catch blocks with an explicit `return`, but that would be caught by // the dead code lint. - let thn = if self.eat_keyword(kw::Else) || !cond.returns() { - self.error_missing_if_cond(lo, cond.span) + let thn = if self.token.is_keyword(kw::Else) || !cond.returns() { + if let Some(binop_span) = missing_then_block_binop_span() { + self.error_missing_if_then_block(lo, None, Some(binop_span)).emit(); + self.mk_block_err(cond.span) + } else { + self.error_missing_if_cond(lo, cond.span) + } } else { let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. let not_block = self.token != token::OpenDelim(token::Brace); - let block = self.parse_block().map_err(|mut err| { + let block = self.parse_block().map_err(|err| { if not_block { - err.span_label(lo, "this `if` expression has a condition, but no block"); - if let ExprKind::Binary(_, _, ref right) = cond.kind { - if let ExprKind::Block(_, _) = right.kind { - err.help("maybe you forgot the right operand of the condition?"); - } - } + self.error_missing_if_then_block(lo, Some(err), missing_then_block_binop_span()) + } else { + err } - err })?; self.error_on_if_block_attrs(lo, false, block.span, &attrs); block @@ -2013,6 +2070,28 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs)) } + fn error_missing_if_then_block( + &self, + if_span: Span, + err: Option<DiagnosticBuilder<'a>>, + binop_span: Option<Span>, + ) -> DiagnosticBuilder<'a> { + let msg = "this `if` expression has a condition, but no block"; + + let mut err = if let Some(mut err) = err { + err.span_label(if_span, msg); + err + } else { + self.struct_span_err(if_span, msg) + }; + + if let Some(binop_span) = binop_span { + err.span_help(binop_span, "maybe you forgot the right operand of the condition?"); + } + + err + } + fn error_missing_if_cond(&self, lo: Span, span: Span) -> P<ast::Block> { let sp = self.sess.source_map().next_point(lo); self.struct_span_err(sp, "missing condition for `if` expression") @@ -2304,6 +2383,17 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { + fn check_let_expr(expr: &Expr) -> (bool, bool) { + match expr.kind { + ExprKind::Binary(_, ref lhs, ref rhs) => { + let lhs_rslt = check_let_expr(lhs); + let rhs_rslt = check_let_expr(rhs); + (lhs_rslt.0 || rhs_rslt.0, false) + } + ExprKind::Let(..) => (true, true), + _ => (false, true), + } + } let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -2311,9 +2401,12 @@ impl<'a> Parser<'a> { let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; let cond = this.parse_expr()?; - if let ExprKind::Let(..) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + if has_let_expr { + if does_not_have_bin_op { + // Remove the last feature gating of a `let` expression since it's stable. + this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + } let span = if_span.to(cond.span); this.sess.gated_spans.gate(sym::if_let_guard, span); } @@ -2548,7 +2641,6 @@ impl<'a> Parser<'a> { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. if self.check(&token::CloseDelim(close_delim)) { - self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); break; } | 
