diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 23d8523236977..f605c4643107d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -150,13 +150,14 @@ fn maybe_append(mut lhs: Vec, rhs: Option>) lhs } -#[derive(Clone, PartialEq)] +#[derive(Clone, Copy, PartialEq)] enum PrevTokenKind { DocComment, Comma, Plus, Interpolated, Eof, + Ident, Other, } @@ -1040,6 +1041,7 @@ impl<'a> Parser<'a> { token::BinOp(token::Plus) => PrevTokenKind::Plus, token::Interpolated(..) => PrevTokenKind::Interpolated, token::Eof => PrevTokenKind::Eof, + token::Ident(..) => PrevTokenKind::Ident, _ => PrevTokenKind::Other, }; @@ -1079,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) } @@ -2777,10 +2789,15 @@ impl<'a> Parser<'a> { self.expected_tokens.push(TokenType::Operator); while let Some(op) = AssocOp::from_token(&self.token) { - let lhs_span = if self.prev_token_kind == PrevTokenKind::Interpolated { - self.prev_span - } else { - lhs.span + // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what + // it refers to. Interpolated identifiers are unwrapped early and never show up here + // as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process + // it as "interpolated", it doesn't change the answer for non-interpolated idents. + let lhs_span = match (self.prev_token_kind, &lhs.node) { + (PrevTokenKind::Interpolated, _) => self.prev_span, + (PrevTokenKind::Ident, &ExprKind::Path(None, ref path)) + if path.segments.len() == 1 => self.prev_span, + _ => lhs.span, }; let cur_op_span = self.span; @@ -2798,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 @@ -2898,61 +2912,61 @@ impl<'a> Parser<'a> { Ok(lhs) } - fn parse_assoc_op_as(&mut self, lhs: P, lhs_span: Span) -> PResult<'a, P> { - let rp = self.clone(); + fn parse_assoc_op_cast(&mut self, lhs: P, lhs_span: Span, + expr_kind: fn(P, P) -> ExprKind) + -> PResult<'a, P> { + let mk_expr = |this: &mut Self, rhs: P| { + 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!("( as )") - }; - 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) } } } diff --git a/src/test/ui/issue-22644.rs b/src/test/ui/issue-22644.rs index e8a2db4048c4d..b482d0595f7b6 100644 --- a/src/test/ui/issue-22644.rs +++ b/src/test/ui/issue-22644.rs @@ -10,10 +10,16 @@ fn main() { let a : u32 = 0; - let b : usize = 0; + let long_name : usize = 0; + + println!("{}", a as usize > long_name); + println!("{}", a as usize < long_name); + println!("{}{}", a as usize < long_name, long_name); + println!("{}", a as usize < 4); + println!("{}", a: usize > long_name); + println!("{}{}", a: usize < long_name, long_name); + println!("{}", a: usize < 4); - println!("{}", a as usize > b); - println!("{}", a as usize < b); println!("{}", a as usize @@ -28,4 +34,6 @@ fn main() { usize < 5); + + println!("{}", a: &mut 4); } diff --git a/src/test/ui/issue-22644.stderr b/src/test/ui/issue-22644.stderr index af61571625b71..144c5ef1ed6a2 100644 --- a/src/test/ui/issue-22644.stderr +++ b/src/test/ui/issue-22644.stderr @@ -1,48 +1,104 @@ error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:16:33 + --> $DIR/issue-22644.rs:16:31 | -16 | println!("{}", a as usize < b); - | - ^ interpreted as generic argument +16 | println!("{}", a as usize < long_name); + | ^ --------- interpreted as generic arguments | | | not interpreted as comparison | help: if you want to compare the casted value then write: | -16 | println!("{}", (a as usize) < b); +16 | println!("{}", (a as usize) < long_name); | ^^^^^^^^^^^^ error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:21:20 + --> $DIR/issue-22644.rs:17:33 | -20 | < - | - not interpreted as comparison -21 | 4); - | ^ interpreted as generic argument +17 | println!("{}{}", a as usize < long_name, long_name); + | ^ -------------------- interpreted as generic arguments + | | + | not interpreted as comparison | help: if you want to compare the casted value then write: | -17 | println!("{}", (a -18 | as -19 | usize) +17 | println!("{}{}", (a as usize) < long_name, long_name); + | ^^^^^^^^^^^^ + +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:18:31 + | +18 | println!("{}", a as usize < 4); + | ^ - interpreted as generic arguments + | | + | not interpreted as comparison + | +help: if you want to compare the casted value then write: + | +18 | println!("{}", (a as usize) < 4); + | ^^^^^^^^^^^^ + +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:20:31 + | +20 | println!("{}{}", a: usize < long_name, long_name); + | ^ -------------------- interpreted as generic arguments + | | + | not interpreted as comparison + | +help: if you want to compare the casted value then write: + | +20 | println!("{}{}", (a: usize) < long_name, long_name); + | ^^^^^^^^^^ + +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:21:29 | +21 | println!("{}", a: usize < 4); + | ^ - interpreted as generic arguments + | | + | not interpreted as comparison + | +help: if you want to compare the casted value then write: + | +21 | println!("{}", (a: usize) < 4); + | ^^^^^^^^^^ error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:30:20 + --> $DIR/issue-22644.rs:26:20 | -29 | < - | - not interpreted as comparison -30 | 5); - | ^ interpreted as generic argument +26 | < + | ^ not interpreted as comparison +27 | 4); + | - interpreted as generic arguments | help: if you want to compare the casted value then write: | -22 | println!("{}", (a -23 | -24 | -25 | as -26 | -27 | +23 | println!("{}", (a +24 | as +25 | usize) + | + +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:35:20 + | +35 | < + | ^ not interpreted as comparison +36 | 5); + | - interpreted as generic arguments + | +help: if you want to compare the casted value then write: + | +28 | println!("{}", (a +29 | +30 | +31 | as +32 | +33 | ... -error: aborting due to 3 previous errors +error: expected type, found `4` + --> $DIR/issue-22644.rs:38:28 + | +38 | println!("{}", a: &mut 4); + | ^ diff --git a/src/test/ui/issue-42954.rs b/src/test/ui/issue-42954.rs new file mode 100644 index 0000000000000..bdfdf44c0e28a --- /dev/null +++ b/src/test/ui/issue-42954.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! is_plainly_printable { + ($i: ident) => { + $i as u32 < 0 + }; +} + +fn main() { + let c = 'a'; + is_plainly_printable!(c); +} diff --git a/src/test/ui/issue-42954.stderr b/src/test/ui/issue-42954.stderr new file mode 100644 index 0000000000000..35995749d3bd0 --- /dev/null +++ b/src/test/ui/issue-42954.stderr @@ -0,0 +1,18 @@ +error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison + --> $DIR/issue-42954.rs:13:19 + | +13 | $i as u32 < 0 + | ^ - interpreted as generic arguments + | | + | not interpreted as comparison +... +19 | is_plainly_printable!(c); + | ------------------------- in this macro invocation + | +help: if you want to compare the casted value then write: + | +13 | ($i as u32) < 0 + | ^^^^^^^^^^^ + +error: aborting due to previous error +