Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc_errors/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ impl Diagnostic {
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
}

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
}
Expand Down
126 changes: 118 additions & 8 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,13 @@ pub struct Parser<'a> {
}


#[derive(Clone)]
struct TokenCursor {
frame: TokenCursorFrame,
stack: Vec<TokenCursorFrame>,
}

#[derive(Clone)]
struct TokenCursorFrame {
delim: token::DelimToken,
span: Span,
Expand Down Expand Up @@ -397,6 +399,7 @@ impl Error {
}
}

#[derive(Debug)]
pub enum LhsExpr {
NotYetParsed,
AttributesParsed(ThinVec<Attribute>),
Expand Down Expand Up @@ -438,6 +441,15 @@ fn dummy_arg(span: Span) -> Arg {
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID }
}

struct RewindPoint {
Copy link
Contributor

@petrochenkov petrochenkov Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, could you just clone Parser instead? Is it very heavy? (as TYPE < shouldn't happen often so it may be ok.)
(Some comment about the purpose of RewindPoint would be useful regardless.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Using Parser.clone() and mem::replace() instead of RewindPoint.

token: token::Token,
span: Span,
meta_var_span: Option<Span>,
prev_span: Span,
token_cursor: TokenCursor,
expected_tokens: Vec<TokenType>,
}

impl<'a> Parser<'a> {
pub fn new(sess: &'a ParseSess,
tokens: TokenStream,
Expand Down Expand Up @@ -786,6 +798,13 @@ impl<'a> Parser<'a> {
}
}

fn is_lt(&mut self) -> bool {
match self.token {
token::Lt | token::BinOp(token::Shl) => true,
_ => false,
}
}

/// Attempt to consume a `<`. If `<<` is seen, replace it with a single
/// `<` and continue. If a `<` is not seen, return false.
///
Expand Down Expand Up @@ -1724,7 +1743,7 @@ impl<'a> Parser<'a> {

let segments = match mode {
PathStyle::Type => {
self.parse_path_segments_without_colons()?
self.parse_path_segments_without_colons(false)?
}
PathStyle::Expr => {
self.parse_path_segments_with_colons()?
Expand All @@ -1745,6 +1764,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, false)
}

pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
self.parse_path_common(mode, true)
}

fn parse_path_common(&mut self, mode: PathStyle, dont_parse_generics: bool)
-> PResult<'a, ast::Path>
{
maybe_whole!(self, NtPath, |x| x);

let lo = self.meta_var_span.unwrap_or(self.span);
Expand All @@ -1755,7 +1784,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(dont_parse_generics)?
}
PathStyle::Expr => {
self.parse_path_segments_with_colons()?
Expand Down Expand Up @@ -1800,7 +1829,9 @@ impl<'a> Parser<'a> {
/// - `a::b<T,U>::c<V,W>`
/// - `a::b<T,U>::c(V) -> W`
/// - `a::b<T,U>::c(V)`
pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<PathSegment>> {
pub fn parse_path_segments_without_colons(&mut self, dont_parse_generics: bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: dont_parse_generics: bool -> parse_generics: bool

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

-> PResult<'a, Vec<PathSegment>>
{
let mut segments = Vec::new();
loop {
// First, parse an identifier.
Expand All @@ -1819,7 +1850,8 @@ impl<'a> Parser<'a> {
}

// Parse types, optionally.
let parameters = if self.eat_lt() {
let parameters = if self.is_lt() && !dont_parse_generics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if !dont_parse_generics && self.eat_lt() {

&& doesnt run second arg when first arg is false.

Copy link
Contributor

@petrochenkov petrochenkov Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
This also makes fn is_lt unnecessary
EDIT: ... and keeps < in the expected set.

let _ = self.eat_lt();
let (lifetimes, types, bindings) = self.parse_generic_args()?;
self.expect_gt()?;
ast::AngleBracketedParameterData {
Expand Down Expand Up @@ -2798,8 +2830,62 @@ 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.
let rp = self.get_rewind_point();
match self.parse_ty_no_plus() {
Ok(rhs) => {
lhs = self.mk_expr(lhs_span.to(rhs.span),
ExprKind::Cast(lhs, rhs), ThinVec::new());
}
Err(mut err) => {
// Rewind to before attempting to parse the type with generics, to get
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the error handling logic into a separate function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// arround #22644.
let rp_err = self.get_rewind_point();
let sp = rp_err.span.clone();
self.rewind(rp);
let lo = self.span;
let path = 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!("(<expression>)")
};
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 warn = self.sess.span_diagnostic.struct_span_warn(sp, &msg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct_span_err please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

warn.span_label(sp, "interpreted as generic argument");
warn.span_label(self.span, "not interpreted as comparison");
warn.span_suggestion(suggestion_span,
"if you want to compare the casted value \
then write:",
suggestion);
warn.emit();
path
}
Err(mut path_err) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a test for this case as well?

// Still couldn't parse, return original error and parser state
path_err.cancel();
self.rewind(rp_err);
return Err(err);
}
};
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 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use mk _ty here? Or is the DUMMY_NODE_ID intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mk_ty is part of TyCtxt which isn't available yet. This was extracted from parse_ty_common in order to see if this would work at all. I could modify parse_ty_common for this case, but I felt it would muddy the logic of that method.

lhs = self.mk_expr(lhs_span.to(rhs.span),
ExprKind::Cast(lhs, rhs), ThinVec::new());
}
};
continue
} else if op == AssocOp::Colon {
let rhs = self.parse_ty_no_plus()?;
Expand Down Expand Up @@ -2901,7 +2987,9 @@ impl<'a> Parser<'a> {
/// 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
Expand All @@ -2925,7 +3013,9 @@ impl<'a> Parser<'a> {
fn parse_prefix_range_expr(&mut self,
already_parsed_attrs: Option<ThinVec<Attribute>>)
-> PResult<'a, P<Expr>> {
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;
Expand Down Expand Up @@ -6174,4 +6264,24 @@ impl<'a> Parser<'a> {
_ => Err(self.fatal("expected string literal"))
}
}

fn get_rewind_point(&mut self) -> RewindPoint {
RewindPoint {
token: self.token.clone(),
span: self.span,
meta_var_span: self.meta_var_span,
prev_span: self.prev_span,
token_cursor: self.token_cursor.clone(),
expected_tokens: self.expected_tokens.clone(),
}
}

fn rewind(&mut self, rp: RewindPoint) {
self.token = rp.token;
self.span = rp.span;
self.meta_var_span = rp.meta_var_span;
self.prev_span = rp.prev_span;
self.token_cursor = rp.token_cursor;
self.expected_tokens = rp.expected_tokens;
}
}
3 changes: 3 additions & 0 deletions src/libsyntax/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenStream>,
index: usize,
Expand Down
2 changes: 1 addition & 1 deletion src/test/parse-fail/better-expected.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
// compile-flags: -Z parse-only

fn main() {
let x: [isize 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
let x: [isize 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, or `]`, found `3`
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/bounds-type-where.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ type A where T: Trait + Trait = u8; // OK
type A where = u8; // OK
type A where T: Trait + = u8; // OK
type A where T, = u8;
//~^ ERROR expected one of `!`, `(`, `+`, `::`, `:`, `<`, `==`, or `=`, found `,`
//~^ ERROR expected one of `!`, `(`, `+`, `::`, `:`, `==`, or `=`, found `,`

fn main() {}
2 changes: 1 addition & 1 deletion src/test/parse-fail/closure-return-syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@

fn main() {
let x = || -> i32 22;
//~^ ERROR expected one of `!`, `(`, `::`, `<`, or `{`, found `22`
//~^ ERROR expected one of `!`, `(`, `::`, or `{`, found `22`
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/empty-impl-semicolon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

impl Foo; //~ ERROR expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `;`
impl Foo; //~ ERROR expected one of `!`, `(`, `+`, `::`, `for`, `where`, or `{`, found `;`
2 changes: 1 addition & 1 deletion src/test/parse-fail/multitrait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct S {
}

impl Cmp, ToString for S {
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `,`
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `for`, `where`, or `{`, found `,`
fn eq(&&other: S) { false }
fn to_string(&self) -> String { "hi".to_string() }
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-closure-lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
// compile-flags: -Z parse-only

type closure = Box<lt/fn()>;
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `/`
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, or `>`, found `/`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-fixed-vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

type v = [isize * 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `*`
type v = [isize * 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, or `]`, found `*`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-ptr-lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `::`, `;`, or `<`, found `/`
type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `::`, or `;`, found `/`
18 changes: 18 additions & 0 deletions src/test/ui/issue-22644.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}
22 changes: 22 additions & 0 deletions src/test/ui/issue-22644.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `<` 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);

warning: `<` 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);