Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand suggestions for type ascription parse errors #59150

Merged
merged 6 commits into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 57 additions & 7 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3264,32 +3264,82 @@ impl<'a> Resolver<'a> {
resolution
}

fn type_ascription_suggestion(&self,
err: &mut DiagnosticBuilder<'_>,
base_span: Span) {
/// Only used in a specific case of type ascription suggestions
#[doc(hidden)]
fn get_colon_suggestion_span(&self, start: Span) -> Span {
let cm = self.session.source_map();
start.to(cm.next_point(start))
}

fn type_ascription_suggestion(
&self,
err: &mut DiagnosticBuilder<'_>,
base_span: Span,
) {
debug!("type_ascription_suggetion {:?}", base_span);
let cm = self.session.source_map();
let base_snippet = cm.span_to_snippet(base_span);
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
if let Some(sp) = self.current_type_ascription.last() {
let mut sp = *sp;
loop {
// Try to find the `:`; bail on first non-':' / non-whitespace.
sp = cm.next_point(sp);
if let Ok(snippet) = cm.span_to_snippet(sp.to(cm.next_point(sp))) {
debug!("snippet {:?}", snippet);
let line_sp = cm.lookup_char_pos(sp.hi()).line;
let line_base_sp = cm.lookup_char_pos(base_span.lo()).line;
debug!("{:?} {:?}", line_sp, line_base_sp);
if snippet == ":" {
err.span_label(base_span,
"expecting a type here because of type ascription");
let mut show_label = true;
if line_sp != line_base_sp {
err.span_suggestion_short(
sp,
"did you mean to use `;` here instead?",
";".to_string(),
Applicability::MaybeIncorrect,
);
} else {
let colon_sp = self.get_colon_suggestion_span(sp);
let after_colon_sp = self.get_colon_suggestion_span(
colon_sp.shrink_to_hi(),
);
if !cm.span_to_snippet(after_colon_sp).map(|s| s == " ")
.unwrap_or(false)
{
err.span_suggestion(
colon_sp,
"maybe you meant to write a path separator here",
"::".to_string(),
Applicability::MaybeIncorrect,
);
show_label = false;
}
if let Ok(base_snippet) = base_snippet {
let mut sp = after_colon_sp;
for _ in 0..100 {
// Try to find an assignment
sp = cm.next_point(sp);
let snippet = cm.span_to_snippet(sp.to(cm.next_point(sp)));
match snippet {
Ok(ref x) if x.as_str() == "=" => {
err.span_suggestion(
base_span,
"maybe you meant to write an assignment here",
format!("let {}", base_snippet),
Applicability::MaybeIncorrect,
);
show_label = false;
break;
}
Ok(ref x) if x.as_str() == "\n" => break,
Err(_) => break,
Ok(_) => {}
}
}
}
}
if show_label {
err.span_label(base_span,
"expecting a type here because of type ascription");
}
break;
} else if !snippet.trim().is_empty() {
Expand Down
8 changes: 6 additions & 2 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1836,8 +1836,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_feature_post!(&self, box_syntax, e.span, EXPLAIN_BOX_SYNTAX);
}
ast::ExprKind::Type(..) => {
gate_feature_post!(&self, type_ascription, e.span,
"type ascription is experimental");
// To avoid noise about type ascription in common syntax errors, only emit if it
// is the *only* error.
if self.context.parse_sess.span_diagnostic.err_count() == 0 {
gate_feature_post!(&self, type_ascription, e.span,
"type ascription is experimental");
}
}
ast::ExprKind::ObsoleteInPlace(..) => {
// these get a hard error in ast-validation
Expand Down
75 changes: 62 additions & 13 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3546,22 +3546,19 @@ impl<'a> Parser<'a> {
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
continue
} else if op == AssocOp::Colon {
let maybe_path = self.could_ascription_be_path(&lhs.node);
let next_sp = self.span;

lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
Ok(lhs) => lhs,
Err(mut err) => {
err.span_label(self.span,
"expecting a type here because of type ascription");
let cm = self.sess.source_map();
let cur_pos = cm.lookup_char_pos(self.span.lo());
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
if cur_pos.line != op_pos.line {
err.span_suggestion(
cur_op_span,
"try using a semicolon",
";".to_string(),
Applicability::MaybeIncorrect // speculative
);
}
self.bad_type_ascription(
&mut err,
lhs_span,
cur_op_span,
next_sp,
maybe_path,
);
return Err(err);
}
};
Expand Down Expand Up @@ -3666,6 +3663,58 @@ impl<'a> Parser<'a> {
Ok(lhs)
}

fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
self.token.is_ident() &&
if let ast::ExprKind::Path(..) = node { true } else { false } &&
!self.token.is_reserved_ident() && // v `foo:bar(baz)`
varkor marked this conversation as resolved.
Show resolved Hide resolved
self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz`
self.look_ahead(2, |t| t.is_ident()) ||
self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
self.look_ahead(2, |t| t.is_ident()) ||
self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
self.look_ahead(2, |t| t.is_ident())
}

fn bad_type_ascription(
&self,
err: &mut DiagnosticBuilder<'a>,
lhs_span: Span,
cur_op_span: Span,
next_sp: Span,
maybe_path: bool,
) {
err.span_label(self.span, "expecting a type here because of type ascription");
let cm = self.sess.source_map();
let next_pos = cm.lookup_char_pos(next_sp.lo());
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
if op_pos.line != next_pos.line {
err.span_suggestion(
cur_op_span,
"try using a semicolon",
";".to_string(),
Applicability::MaybeIncorrect,
);
} else {
if maybe_path {
err.span_suggestion(
cur_op_span,
"maybe you meant to write a path separator here",
"::".to_string(),
Applicability::MaybeIncorrect,
);
} else {
err.note("type ascription is a nightly-only feature that lets \
you annotate an expression with a type: `<expr>: <type>`");
err.span_note(
lhs_span,
"this expression expects an ascribed type after the colon",
);
err.help("this might be indicative of a syntax error elsewhere");
}
}
}

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>> {
Expand Down
16 changes: 16 additions & 0 deletions src/test/ui/error-codes/E0423.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ error: expected type, found `1`
|
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/E0423.rs:12:36
|
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
estebank marked this conversation as resolved.
Show resolved Hide resolved
| ^
= help: this might be indicative of a syntax error elsewhere

error: expected expression, found `==`
--> $DIR/E0423.rs:15:13
Expand All @@ -15,6 +23,14 @@ error: expected type, found `0`
|
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/E0423.rs:21:32
|
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
| ^^^^^
= help: this might be indicative of a syntax error elsewhere

error[E0423]: expected function, found struct `Foo`
--> $DIR/E0423.rs:4:13
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/issues/issue-22644.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ error: expected type, found `4`
|
LL | println!("{}", a: &mut 4);
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/issue-22644.rs:34:20
|
LL | println!("{}", a: &mut 4);
| ^
= help: this might be indicative of a syntax error elsewhere

error: aborting due to 9 previous errors

10 changes: 10 additions & 0 deletions src/test/ui/issues/issue-34255-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
enum Test {
Drill {
field: i32,
}
}

fn main() {
Test::Drill(field: 42);
//~^ ERROR expected type, found
}
16 changes: 16 additions & 0 deletions src/test/ui/issues/issue-34255-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: expected type, found `42`
--> $DIR/issue-34255-1.rs:8:24
|
LL | Test::Drill(field: 42);
| ^^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/issue-34255-1.rs:8:17
|
LL | Test::Drill(field: 42);
| ^^^^^
= help: this might be indicative of a syntax error elsewhere

error: aborting due to previous error

8 changes: 8 additions & 0 deletions src/test/ui/lifetime_starts_expressions.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ error: expected type, found keyword `loop`
|
LL | loop { break 'label: loop { break 'label 42; }; }
| ^^^^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/lifetime_starts_expressions.rs:6:12
|
LL | loop { break 'label: loop { break 'label 42; }; }
| ^^^^^^^^^^^^
= help: this might be indicative of a syntax error elsewhere

error: aborting due to 2 previous errors

8 changes: 8 additions & 0 deletions src/test/ui/parser/struct-literal-in-for.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ error: expected type, found `3`
|
LL | x: 3
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/struct-literal-in-for.rs:13:9
|
LL | x: 3
| ^
= help: this might be indicative of a syntax error elsewhere

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> $DIR/struct-literal-in-for.rs:14:12
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/parser/struct-literal-in-if.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ error: expected type, found `3`
|
LL | x: 3
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/struct-literal-in-if.rs:13:9
|
LL | x: 3
| ^
= help: this might be indicative of a syntax error elsewhere

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> $DIR/struct-literal-in-if.rs:14:12
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/parser/struct-literal-in-while.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ error: expected type, found `3`
|
LL | x: 3
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/struct-literal-in-while.rs:13:9
|
LL | x: 3
| ^
= help: this might be indicative of a syntax error elsewhere

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> $DIR/struct-literal-in-while.rs:14:12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ error: expected type, found `3`
|
LL | x: 3
| ^ expecting a type here because of type ascription
|
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/struct-literal-restrictions-in-lamda.rs:13:9
|
LL | x: 3
| ^
= help: this might be indicative of a syntax error elsewhere

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
--> $DIR/struct-literal-restrictions-in-lamda.rs:14:12
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-let.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn fun(x: i32) -> i32 { x }

fn main() {
let closure_annotated = |value: i32| -> i32 {
temp: i32 = fun(5i32);
//~^ ERROR cannot find value `temp` in this scope
temp + value + 1
//~^ ERROR cannot find value `temp` in this scope
};
}
18 changes: 18 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-let.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0425]: cannot find value `temp` in this scope
--> $DIR/type-ascription-instead-of-let.rs:5:9
|
LL | temp: i32 = fun(5i32);
| ^^^^
| |
| not found in this scope
| help: maybe you meant to write an assignment here: `let temp`

error[E0425]: cannot find value `temp` in this scope
--> $DIR/type-ascription-instead-of-let.rs:7:9
|
LL | temp + value + 1
| ^^^^ not found in this scope

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0425`.
4 changes: 4 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
Box:new("foo".to_string())
//~^ ERROR expected type, found
}
10 changes: 10 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: expected type, found `"foo"`
--> $DIR/type-ascription-instead-of-method.rs:2:13
|
LL | Box:new("foo".to_string())
| - ^^^^^ expecting a type here because of type ascription
| |
| help: maybe you meant to write a path separator here: `::`

error: aborting due to previous error

5 changes: 5 additions & 0 deletions src/test/ui/suggestions/type-ascription-instead-of-path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
std:io::stdin();
//~^ ERROR failed to resolve: use of undeclared type or module `io`
//~| ERROR expected value, found module
}