diff options
| author | Mark Rousskov <mark.simulacrum@gmail.com> | 2019-07-23 12:51:07 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-07-23 12:51:07 -0400 |
| commit | ab7149bdc5da8b982f99acf619c6336b45cde552 (patch) | |
| tree | f36748a48aff41c834566c4875927d7597a78b08 /src/libsyntax/parse | |
| parent | 4264f8376a8187fa3404391bac6436b1123af42a (diff) | |
| parent | 9dbe2e77b34f5321976ee3b26ca008ad8d574faf (diff) | |
| download | rust-ab7149bdc5da8b982f99acf619c6336b45cde552.tar.gz rust-ab7149bdc5da8b982f99acf619c6336b45cde552.zip | |
Rollup merge of #62791 - estebank:type-ascription, r=petrochenkov
Handle more cases of typos misinterpreted as type ascription Fix #60933, #54516. CC #47666, #34255, #48016.
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/diagnostics.rs | 99 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 39 |
2 files changed, 73 insertions, 65 deletions
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 0e88a0ee289..f4fc87506f3 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -2,6 +2,7 @@ use crate::ast::{ self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData, }; +use crate::feature_gate::{feature_err, UnstableFeatures}; use crate::parse::{SeqSep, PResult, Parser, ParseSess}; use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType}; use crate::parse::token::{self, TokenKind}; @@ -326,8 +327,8 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw::Return) || self.token.is_keyword(kw::While) ); - let cm = self.sess.source_map(); - match (cm.lookup_line(self.token.span.lo()), cm.lookup_line(sp.lo())) { + let sm = self.sess.source_map(); + match (sm.lookup_line(self.token.span.lo()), sm.lookup_line(sp.lo())) { (Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => { // The spans are in different lines, expected `;` and found `let` or `return`. // High likelihood that it is only a missing `;`. @@ -365,9 +366,53 @@ impl<'a> Parser<'a> { err.span_label(self.token.span, "unexpected token"); } } + self.maybe_annotate_with_ascription(&mut err, false); Err(err) } + pub fn maybe_annotate_with_ascription( + &self, + err: &mut DiagnosticBuilder<'_>, + maybe_expected_semicolon: bool, + ) { + if let Some((sp, likely_path)) = self.last_type_ascription { + let sm = self.sess.source_map(); + let next_pos = sm.lookup_char_pos(self.token.span.lo()); + let op_pos = sm.lookup_char_pos(sp.hi()); + + if likely_path { + err.span_suggestion( + sp, + "maybe write a path separator here", + "::".to_string(), + match self.sess.unstable_features { + UnstableFeatures::Disallow => Applicability::MachineApplicable, + _ => Applicability::MaybeIncorrect, + }, + ); + } else if op_pos.line != next_pos.line && maybe_expected_semicolon { + err.span_suggestion( + sp, + "try using a semicolon", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + } else if let UnstableFeatures::Disallow = self.sess.unstable_features { + err.span_label(sp, "tried to parse a type due to this"); + } else { + err.span_label(sp, "tried to parse a type due to this type ascription"); + } + if let UnstableFeatures::Disallow = self.sess.unstable_features { + // Give extra information about type ascription only if it's a nightly compiler. + } else { + err.note("`#![feature(type_ascription)]` lets you annotate an expression with a \ + type: `<expr>: <type>`"); + err.note("for more information, see \ + https://github.com/rust-lang/rust/issues/23416"); + } + } + } + /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. crate fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { @@ -556,7 +601,7 @@ impl<'a> Parser<'a> { .collect::<Vec<_>>(); if !discriminant_spans.is_empty() && has_fields { - let mut err = crate::feature_gate::feature_err( + let mut err = feature_err( sess, sym::arbitrary_enum_discriminant, discriminant_spans.clone(), @@ -769,8 +814,8 @@ impl<'a> Parser<'a> { return Ok(recovered); } } - let cm = self.sess.source_map(); - match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) { + let sm = self.sess.source_map(); + match (sm.lookup_line(prev_sp.lo()), sm.lookup_line(sp.lo())) { (Ok(ref a), Ok(ref b)) if a.line == b.line => { // When the spans are in the same line, it means that the only content // between them is whitespace, point only at the found token. @@ -887,47 +932,9 @@ impl<'a> Parser<'a> { self.look_ahead(2, |t| t.is_ident()) || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz` self.look_ahead(2, |t| t.is_ident()) || - self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz` - self.look_ahead(2, |t| t.is_ident()) - } - - crate fn bad_type_ascription( - &self, - err: &mut DiagnosticBuilder<'a>, - lhs_span: Span, - cur_op_span: Span, - next_sp: Span, - maybe_path: bool, - ) { - err.span_label(self.token.span, "expecting a type here because of type ascription"); - let cm = self.sess.source_map(); - let next_pos = cm.lookup_char_pos(next_sp.lo()); - let op_pos = cm.lookup_char_pos(cur_op_span.hi()); - if op_pos.line != next_pos.line { - err.span_suggestion( - cur_op_span, - "try using a semicolon", - ";".to_string(), - Applicability::MaybeIncorrect, - ); - } else { - if maybe_path { - err.span_suggestion( - cur_op_span, - "maybe you meant to write a path separator here", - "::".to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.note("`#![feature(type_ascription)]` lets you annotate an \ - expression with a type: `<expr>: <type>`") - .span_note( - lhs_span, - "this expression expects an ascribed type after the colon", - ) - .help("this might be indicative of a syntax error elsewhere"); - } - } + self.look_ahead(1, |t| t == &token::ModSep) && + (self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz` + self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::<baz>` } crate fn recover_seq_parse_error( diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1d4d02c7325..da388694637 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -239,6 +239,7 @@ pub struct Parser<'a> { /// error. crate unclosed_delims: Vec<UnmatchedBrace>, crate last_unexpected_token_span: Option<Span>, + crate last_type_ascription: Option<(Span, bool /* likely path typo */)>, /// If present, this `Parser` is not parsing Rust code but rather a macro call. crate subparser_name: Option<&'static str>, } @@ -502,6 +503,7 @@ impl<'a> Parser<'a> { max_angle_bracket_count: 0, unclosed_delims: Vec::new(), last_unexpected_token_span: None, + last_type_ascription: None, subparser_name, }; @@ -1422,7 +1424,10 @@ impl<'a> Parser<'a> { } } else { let msg = format!("expected type, found {}", self.this_token_descr()); - return Err(self.fatal(&msg)); + let mut err = self.fatal(&msg); + err.span_label(self.token.span, "expected type"); + self.maybe_annotate_with_ascription(&mut err, true); + return Err(err); }; let span = lo.to(self.prev_span); @@ -2823,10 +2828,11 @@ impl<'a> Parser<'a> { } /// Parses an associative expression with operators of at least `min_prec` precedence. - fn parse_assoc_expr_with(&mut self, - min_prec: usize, - lhs: LhsExpr) - -> PResult<'a, P<Expr>> { + fn parse_assoc_expr_with( + &mut self, + min_prec: usize, + lhs: LhsExpr, + ) -> PResult<'a, P<Expr>> { let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { expr } else { @@ -2840,9 +2846,11 @@ impl<'a> Parser<'a> { self.parse_prefix_expr(attrs)? } }; + let last_type_ascription_set = self.last_type_ascription.is_some(); match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) { (true, None) => { + self.last_type_ascription = None; // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 return Ok(lhs); } @@ -2857,12 +2865,14 @@ impl<'a> Parser<'a> { // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: // `if x { a } else { b } && if y { c } else { d }` if !self.look_ahead(1, |t| t.is_reserved_ident()) => { + self.last_type_ascription = None; // These cases are ambiguous and can't be identified in the parser alone let sp = self.sess.source_map().start_point(self.token.span); self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); return Ok(lhs); } (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => { + self.last_type_ascription = None; return Ok(lhs); } (true, Some(_)) => { @@ -2921,21 +2931,9 @@ impl<'a> Parser<'a> { continue } else if op == AssocOp::Colon { let maybe_path = self.could_ascription_be_path(&lhs.node); - let next_sp = self.token.span; + self.last_type_ascription = Some((self.prev_span, maybe_path)); - lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) { - Ok(lhs) => lhs, - Err(mut err) => { - self.bad_type_ascription( - &mut err, - lhs_span, - cur_op_span, - next_sp, - maybe_path, - ); - return Err(err); - } - }; + lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; continue } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to @@ -3020,6 +3018,9 @@ impl<'a> Parser<'a> { if let Fixity::None = fixity { break } } + if last_type_ascription_set { + self.last_type_ascription = None; + } Ok(lhs) } |
