syntax: Apply recovery for casts to type ascription

Fix spans, add some comments
This commit is contained in:
Vadim Petrochenkov
2017-07-04 02:17:01 +03:00
parent 5fa1c1b5f3
commit 4323877e92
5 changed files with 155 additions and 79 deletions

View File

@@ -1081,6 +1081,16 @@ impl<'a> Parser<'a> {
None => token::CloseDelim(self.token_cursor.frame.delim), 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> { pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
self.sess.span_diagnostic.struct_span_fatal(self.span, m) self.sess.span_diagnostic.struct_span_fatal(self.span, m)
} }
@@ -2805,13 +2815,10 @@ impl<'a> Parser<'a> {
} }
// Special cases: // Special cases:
if op == AssocOp::As { if op == AssocOp::As {
// Save the state of the parser before parsing type normally, in case there is a lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
// LessThan comparison after this cast.
lhs = self.parse_assoc_op_as(lhs, lhs_span)?;
continue continue
} else if op == AssocOp::Colon { } else if op == AssocOp::Colon {
let rhs = self.parse_ty_no_plus()?; lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Type(lhs, rhs), ThinVec::new());
continue continue
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot { } else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
// If we didnt have to handle `x..`/`x...`, it would be pretty easy to // If we didnt have to handle `x..`/`x...`, it would be pretty easy to
@@ -2905,61 +2912,61 @@ impl<'a> Parser<'a> {
Ok(lhs) Ok(lhs)
} }
fn parse_assoc_op_as(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> { fn parse_assoc_op_cast(&mut self, lhs: P<Expr>, lhs_span: Span,
let rp = self.clone(); 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() { match self.parse_ty_no_plus() {
Ok(rhs) => { Ok(rhs) => {
Ok(self.mk_expr(lhs_span.to(rhs.span), Ok(mk_expr(self, rhs))
ExprKind::Cast(lhs, rhs),
ThinVec::new()))
} }
Err(mut err) => { Err(mut type_err) => {
let rp_err = self.clone(); // Rewind to before attempting to parse the type with generics, to recover
let sp = rp_err.span.clone(); // 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) { match self.parse_path_without_generics(PathStyle::Type) {
Ok(path) => { Ok(path) => {
// Successfully parsed the type leaving a `<` yet to parse // Successfully parsed the type path leaving a `<` yet to parse.
err.cancel(); type_err.cancel();
let codemap = self.sess.codemap();
let suggestion_span = lhs_span.to(self.prev_span); // Report non-fatal diagnostics, keep `x as usize` as an expression
let warn_message = match codemap.span_to_snippet(self.prev_span) { // in AST and continue parsing.
Ok(lstring) => format!("`{}`", lstring),
_ => "a type".to_string(),
};
let msg = format!("`<` is interpreted as a start of generic \ let msg = format!("`<` is interpreted as a start of generic \
arguments for {}, not a comparison", arguments for `{}`, not a comparison", path);
warn_message); let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg);
let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg); err.span_label(self.look_ahead_span(1).to(parser_snapshot_after_type.span),
err.span_label(sp, "interpreted as generic argument"); "interpreted as generic arguments");
err.span_label(self.span, "not interpreted as comparison"); err.span_label(self.span, "not interpreted as comparison");
let suggestion = match codemap.span_to_snippet(suggestion_span) {
Ok(lstring) => format!("({})", lstring), let expr = mk_expr(self, P(Ty {
_ => format!("(<expression> as <type>)") span: path.span,
}; node: TyKind::Path(None, path),
err.span_suggestion(suggestion_span, 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:", "if you want to compare the casted value then write:",
suggestion); format!("({})", expr_str));
err.emit(); err.emit();
let path = TyKind::Path(None, path); Ok(expr)
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) => { 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(); path_err.cancel();
mem::replace(self, rp_err); mem::replace(self, parser_snapshot_after_type);
Err(err) Err(type_err)
} }
} }
} }

View File

@@ -10,10 +10,16 @@
fn main() { fn main() {
let a : u32 = 0; 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 println!("{}", a
as as
usize usize
@@ -28,4 +34,6 @@ fn main() {
usize usize
< <
5); 5);
println!("{}", a: &mut 4);
} }

View File

@@ -1,48 +1,104 @@
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison 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); 16 | println!("{}", a as usize < long_name);
| - ^ interpreted as generic argument | ^ --------- interpreted as generic arguments
| | | |
| not interpreted as comparison | not interpreted as comparison
| |
help: if you want to compare the casted value then write: 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 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 | < 17 | println!("{}{}", a as usize < long_name, long_name);
| - not interpreted as comparison | ^ -------------------- interpreted as generic arguments
21 | 4); | |
| ^ interpreted as generic argument | not interpreted as comparison
| |
help: if you want to compare the casted value then write: help: if you want to compare the casted value then write:
| |
17 | println!("{}", (a 17 | println!("{}{}", (a as usize) < long_name, long_name);
18 | as | ^^^^^^^^^^^^
19 | usize)
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:26:20
|
26 | <
| ^ not interpreted as comparison
27 | 4);
| - interpreted as generic arguments
|
help: if you want to compare the casted value then write:
|
23 | println!("{}", (a
24 | as
25 | usize)
| |
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
--> $DIR/issue-22644.rs:30:20 --> $DIR/issue-22644.rs:35:20
| |
29 | < 35 | <
| - not interpreted as comparison | ^ not interpreted as comparison
30 | 5); 36 | 5);
| ^ interpreted as generic argument | - interpreted as generic arguments
| |
help: if you want to compare the casted value then write: help: if you want to compare the casted value then write:
| |
22 | println!("{}", (a 28 | println!("{}", (a
23 | 29 |
24 | 30 |
25 | as 31 | as
26 | 32 |
27 | 33 |
... ...
error: aborting due to 3 previous errors error: expected type, found `4`
--> $DIR/issue-22644.rs:38:28
|
38 | println!("{}", a: &mut 4);
| ^

View File

@@ -2,12 +2,17 @@ error: `<` is interpreted as a start of generic arguments for `u32`, not a compa
--> $DIR/issue-42954.rs:13:19 --> $DIR/issue-42954.rs:13:19
| |
13 | $i as u32 < 0 13 | $i as u32 < 0
| - ^ interpreted as generic argument | ^ - interpreted as generic arguments
| | | |
| not interpreted as comparison | not interpreted as comparison
...
19 | is_plainly_printable!(c);
| ------------------------- in this macro invocation
| |
help: if you want to compare the casted value then write: help: if you want to compare the casted value then write:
| ($i as u32) < 0 |
13 | ($i as u32) < 0
| ^^^^^^^^^^^
error: aborting due to previous error error: aborting due to previous error