diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2017-07-04 02:17:01 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2017-07-07 11:53:12 +0300 |
| commit | 4323877e9202aa7a7fc2742d4863300e9abed17b (patch) | |
| tree | 60a495eaef643cd183a2a9a55ea09d88f4fa4d32 /src/libsyntax/parse | |
| parent | 5fa1c1b5f3e03a8f2049c6a36f58fae1fe05852d (diff) | |
| download | rust-4323877e9202aa7a7fc2742d4863300e9abed17b.tar.gz rust-4323877e9202aa7a7fc2742d4863300e9abed17b.zip | |
syntax: Apply recovery for casts to type ascription
Fix spans, add some comments
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 99 |
1 files changed, 53 insertions, 46 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f35ecbe20e0..f605c464310 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1081,6 +1081,16 @@ impl<'a> Parser<'a> { None => token::CloseDelim(self.token_cursor.frame.delim), }) } + fn look_ahead_span(&self, dist: usize) -> Span { + if dist == 0 { + return self.span + } + + match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) { + Some(TokenTree::Token(span, _)) | Some(TokenTree::Delimited(span, _)) => span, + None => self.look_ahead_span(dist - 1), + } + } pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> { self.sess.span_diagnostic.struct_span_fatal(self.span, m) } @@ -2805,13 +2815,10 @@ impl<'a> Parser<'a> { } // Special cases: if op == AssocOp::As { - // Save the state of the parser before parsing type normally, in case there is a - // LessThan comparison after this cast. - lhs = self.parse_assoc_op_as(lhs, lhs_span)?; + lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; continue } else if op == AssocOp::Colon { - let rhs = self.parse_ty_no_plus()?; - lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Type(lhs, rhs), ThinVec::new()); + lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; continue } else if op == AssocOp::DotDot || op == AssocOp::DotDotDot { // If we didn’t have to handle `x..`/`x...`, it would be pretty easy to @@ -2905,61 +2912,61 @@ impl<'a> Parser<'a> { Ok(lhs) } - fn parse_assoc_op_as(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> { - let rp = self.clone(); + fn parse_assoc_op_cast(&mut self, lhs: P<Expr>, lhs_span: Span, + expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind) + -> PResult<'a, P<Expr>> { + let mk_expr = |this: &mut Self, rhs: P<Ty>| { + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), ThinVec::new()) + }; + + // 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(); match self.parse_ty_no_plus() { Ok(rhs) => { - Ok(self.mk_expr(lhs_span.to(rhs.span), - ExprKind::Cast(lhs, rhs), - ThinVec::new())) + Ok(mk_expr(self, rhs)) } - Err(mut err) => { - let rp_err = self.clone(); - let sp = rp_err.span.clone(); + Err(mut type_err) => { + // Rewind to before attempting to parse the type with generics, to recover + // from situations like `x as usize < y` in which we first tried to parse + // `usize < y` as a type with generic arguments. + let parser_snapshot_after_type = self.clone(); + mem::replace(self, parser_snapshot_before_type); - // Rewind to before attempting to parse the type with generics, to get - // arround #22644. - mem::replace(self, rp); - let lo = self.span; match self.parse_path_without_generics(PathStyle::Type) { Ok(path) => { - // Successfully parsed the type leaving a `<` yet to parse - err.cancel(); - let codemap = self.sess.codemap(); - let suggestion_span = lhs_span.to(self.prev_span); - let warn_message = match codemap.span_to_snippet(self.prev_span) { - Ok(lstring) => format!("`{}`", lstring), - _ => "a type".to_string(), - }; + // Successfully parsed the type path leaving a `<` yet to parse. + type_err.cancel(); + + // Report non-fatal diagnostics, keep `x as usize` as an expression + // in AST and continue parsing. let msg = format!("`<` is interpreted as a start of generic \ - arguments for {}, not a comparison", - warn_message); - let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg); - err.span_label(sp, "interpreted as generic argument"); + arguments for `{}`, not a comparison", path); + let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg); + err.span_label(self.look_ahead_span(1).to(parser_snapshot_after_type.span), + "interpreted as generic arguments"); err.span_label(self.span, "not interpreted as comparison"); - let suggestion = match codemap.span_to_snippet(suggestion_span) { - Ok(lstring) => format!("({})", lstring), - _ => format!("(<expression> as <type>)") - }; - err.span_suggestion(suggestion_span, + + let expr = mk_expr(self, P(Ty { + span: path.span, + node: TyKind::Path(None, path), + id: ast::DUMMY_NODE_ID + })); + + let expr_str = self.sess.codemap().span_to_snippet(expr.span) + .unwrap_or(pprust::expr_to_string(&expr)); + err.span_suggestion(expr.span, "if you want to compare the casted value then write:", - suggestion); + format!("({})", expr_str)); err.emit(); - let path = TyKind::Path(None, path); - let span = lo.to(self.prev_span); - let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID }); - // Letting the parser accept the recovered type to avoid further errors, - // but the code will still not compile due to the error emitted above. - Ok(self.mk_expr(lhs_span.to(rhs.span), - ExprKind::Cast(lhs, rhs), - ThinVec::new())) + Ok(expr) } Err(mut path_err) => { - // Still couldn't parse, return original error and parser state + // Couldn't parse as a path, return original error and parser state. path_err.cancel(); - mem::replace(self, rp_err); - Err(err) + mem::replace(self, parser_snapshot_after_type); + Err(type_err) } } } |
