diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 7a64cdeee65c7..d7c21127474a4 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -248,6 +248,10 @@ impl Diagnostic { self.message.iter().map(|i| i.0.to_owned()).collect::() } + pub fn set_message(&mut self, message: &str) { + self.message = vec![(message.to_owned(), Style::NoStyle)]; + } + pub fn styled_message(&self) -> &Vec<(String, Style)> { &self.message } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d9cb2b4ab7db0..3e62f6e33e09a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -150,7 +150,7 @@ fn maybe_append(mut lhs: Vec, rhs: Option>) lhs } -#[derive(PartialEq)] +#[derive(Clone, PartialEq)] enum PrevTokenKind { DocComment, Comma, @@ -162,6 +162,7 @@ enum PrevTokenKind { /* ident is handled by common.rs */ +#[derive(Clone)] pub struct Parser<'a> { pub sess: &'a ParseSess, /// the current token: @@ -193,11 +194,13 @@ pub struct Parser<'a> { } +#[derive(Clone)] struct TokenCursor { frame: TokenCursorFrame, stack: Vec, } +#[derive(Clone)] struct TokenCursorFrame { delim: token::DelimToken, span: Span, @@ -397,6 +400,7 @@ impl Error { } } +#[derive(Debug)] pub enum LhsExpr { NotYetParsed, AttributesParsed(ThinVec), @@ -1724,7 +1728,7 @@ impl<'a> Parser<'a> { let segments = match mode { PathStyle::Type => { - self.parse_path_segments_without_colons()? + self.parse_path_segments_without_colons(true)? } PathStyle::Expr => { self.parse_path_segments_with_colons()? @@ -1745,6 +1749,16 @@ impl<'a> Parser<'a> { /// bounds are permitted and whether `::` must precede type parameter /// groups. pub fn parse_path(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> { + self.parse_path_common(mode, true) + } + + pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> { + self.parse_path_common(mode, false) + } + + fn parse_path_common(&mut self, mode: PathStyle, parse_generics: bool) + -> PResult<'a, ast::Path> + { maybe_whole!(self, NtPath, |x| x); let lo = self.meta_var_span.unwrap_or(self.span); @@ -1755,7 +1769,7 @@ impl<'a> Parser<'a> { // A bound set is a set of type parameter bounds. let mut segments = match mode { PathStyle::Type => { - self.parse_path_segments_without_colons()? + self.parse_path_segments_without_colons(parse_generics)? } PathStyle::Expr => { self.parse_path_segments_with_colons()? @@ -1800,7 +1814,9 @@ impl<'a> Parser<'a> { /// - `a::b::c` /// - `a::b::c(V) -> W` /// - `a::b::c(V)` - pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec> { + pub fn parse_path_segments_without_colons(&mut self, parse_generics: bool) + -> PResult<'a, Vec> + { let mut segments = Vec::new(); loop { // First, parse an identifier. @@ -1819,7 +1835,7 @@ impl<'a> Parser<'a> { } // Parse types, optionally. - let parameters = if self.eat_lt() { + let parameters = if parse_generics && self.eat_lt() { let (lifetimes, types, bindings) = self.parse_generic_args()?; self.expect_gt()?; ast::AngleBracketedParameterData { @@ -2798,8 +2814,9 @@ impl<'a> Parser<'a> { } // Special cases: if op == AssocOp::As { - let rhs = self.parse_ty_no_plus()?; - lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Cast(lhs, rhs), ThinVec::new()); + // 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)?; continue } else if op == AssocOp::Colon { let rhs = self.parse_ty_no_plus()?; @@ -2897,11 +2914,74 @@ 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(); + match self.parse_ty_no_plus() { + Ok(rhs) => { + Ok(self.mk_expr(lhs_span.to(rhs.span), + ExprKind::Cast(lhs, rhs), + ThinVec::new())) + } + Err(mut err) => { + let rp_err = self.clone(); + let sp = rp_err.span.clone(); + + // 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 suggestion = match codemap.span_to_snippet(suggestion_span) { + Ok(lstring) => format!("({})", lstring), + _ => format!("( as )") + }; + let warn_message = match codemap.span_to_snippet(self.prev_span) { + Ok(lstring) => format!("`{}`", lstring), + _ => "a type".to_string(), + }; + 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"); + err.span_label(self.span, "not interpreted as comparison"); + err.span_suggestion(suggestion_span, + "if you want to compare the casted value then write:", + suggestion); + 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())) + } + Err(mut path_err) => { + // Still couldn't parse, return original error and parser state + path_err.cancel(); + mem::replace(self, rp_err); + Err(err) + } + } + } + } + } + /// Produce an error if comparison operators are chained (RFC #558). /// We only need to check lhs, not rhs, because all comparison ops /// have same precedence and are left-associative fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) { - debug_assert!(outer_op.is_comparison()); + debug_assert!(outer_op.is_comparison(), + "check_no_chained_comparison: {:?} is not comparison", + outer_op); match lhs.node { ExprKind::Binary(op, _, _) if op.node.is_comparison() => { // respan to include both operators @@ -2925,7 +3005,9 @@ impl<'a> Parser<'a> { fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option>) -> PResult<'a, P> { - debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot); + debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot, + "parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot", + self.token); let tok = self.token.clone(); let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let lo = self.span; diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 339e7c0b628ad..963482fc223f1 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -227,14 +227,17 @@ impl TokenStream { } } +#[derive(Clone)] pub struct Cursor(CursorKind); +#[derive(Clone)] enum CursorKind { Empty, Tree(TokenTree, bool /* consumed? */), Stream(StreamCursor), } +#[derive(Clone)] struct StreamCursor { stream: RcSlice, index: usize, diff --git a/src/test/ui/issue-22644.rs b/src/test/ui/issue-22644.rs new file mode 100644 index 0000000000000..9269180396c54 --- /dev/null +++ b/src/test/ui/issue-22644.rs @@ -0,0 +1,18 @@ +// 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. + +fn main() { + let a : u32 = 0; + let b : usize = 0; + + println!("{}", a as usize > b); + println!("{}", a as usize < b); + println!("{}", a as usize < 4); +} diff --git a/src/test/ui/issue-22644.stderr b/src/test/ui/issue-22644.stderr new file mode 100644 index 0000000000000..a22496357d991 --- /dev/null +++ b/src/test/ui/issue-22644.stderr @@ -0,0 +1,24 @@ +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:16:33 + | +16 | println!("{}", a as usize < b); + | - ^ interpreted as generic argument + | | + | not interpreted as comparison + | +help: if you want to compare the casted value then write: + | println!("{}", (a as usize) < b); + +error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison + --> $DIR/issue-22644.rs:17:33 + | +17 | println!("{}", a as usize < 4); + | - ^ interpreted as generic argument + | | + | not interpreted as comparison + | +help: if you want to compare the casted value then write: + | println!("{}", (a as usize) < 4); + +error: aborting due to previous error(s) +