From 8ba1a97e375451f51d0657e2135d4e6e657fd72e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Esteban=20K=C3=BCber?= Date: Tue, 12 Mar 2019 19:27:10 -0700 Subject: [PATCH] Expand suggestions for type ascription parse errors --- src/librustc_resolve/lib.rs | 41 ++++++++-- src/libsyntax/parse/parser.rs | 79 ++++++++++++++++--- src/test/ui/error-codes/E0423.stderr | 26 ++++++ src/test/ui/issues/issue-22644.stderr | 13 +++ src/test/ui/issues/issue-34255-1.rs | 10 +++ src/test/ui/issues/issue-34255-1.stderr | 21 +++++ .../ui/lifetime_starts_expressions.stderr | 13 +++ .../ui/parser/struct-literal-in-for.stderr | 13 +++ .../ui/parser/struct-literal-in-if.stderr | 13 +++ .../ui/parser/struct-literal-in-while.stderr | 13 +++ ...truct-literal-restrictions-in-lamda.stderr | 13 +++ .../type-ascription-instead-of-let.rs | 11 +++ .../type-ascription-instead-of-let.stderr | 28 +++++++ .../type-ascription-instead-of-method.rs | 4 + .../type-ascription-instead-of-method.stderr | 10 +++ .../type-ascription-instead-of-path.rs | 6 ++ .../type-ascription-instead-of-path.stderr | 35 ++++++++ .../type-ascription-instead-of-variant.rs | 4 + .../type-ascription-instead-of-variant.stderr | 10 +++ ...ascription-instead-of-statement-end.stderr | 13 +++ 20 files changed, 358 insertions(+), 18 deletions(-) create mode 100644 src/test/ui/issues/issue-34255-1.rs create mode 100644 src/test/ui/issues/issue-34255-1.stderr create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-let.rs create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-let.stderr create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-method.rs create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-method.stderr create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-path.rs create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-path.stderr create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-variant.rs create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-variant.stderr diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index ac149be4b2a..1a032922902 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3264,11 +3264,21 @@ fn smart_resolve_path_fragment(&mut self, 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; @@ -3276,10 +3286,8 @@ fn type_ascription_suggestion(&self, // 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"); @@ -3290,6 +3298,29 @@ fn type_ascription_suggestion(&self, ";".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, + ); + } + if let Ok(base_snippet) = base_snippet { + err.span_suggestion( + base_span, + "maybe you meant to write an assignment here", + format!("let {}", base_snippet), + Applicability::MaybeIncorrect, + ); + } } break; } else if !snippet.trim().is_empty() { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5627ac3fcf2..d052abf96d7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3546,22 +3546,19 @@ fn parse_assoc_expr_with(&mut self, 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); } }; @@ -3666,6 +3663,62 @@ fn parse_assoc_expr_with(&mut self, 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)` + self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) || + self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar, + 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 expressions with a type: `: `"); + err.span_note( + lhs_span, + "this expression is annotated with type ascription...", + ); + err.span_note( + cur_op_span, + "...due to this, which is why a type is expected after", + ); + err.help("this might be indicative of a syntax error elsewhere"); + } + } + } + fn parse_assoc_op_cast(&mut self, lhs: P, lhs_span: Span, expr_kind: fn(P, P) -> ExprKind) -> PResult<'a, P> { diff --git a/src/test/ui/error-codes/E0423.stderr b/src/test/ui/error-codes/E0423.stderr index bdcfaae60a0..af5f88f4ce5 100644 --- a/src/test/ui/error-codes/E0423.stderr +++ b/src/test/ui/error-codes/E0423.stderr @@ -3,6 +3,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/E0423.rs:12:36 + | +LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); } + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/E0423.rs:12:37 + | +LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); } + | ^ + = help: this might be indicative of a syntax error elsewhere error: expected expression, found `==` --> $DIR/E0423.rs:15:13 @@ -15,6 +28,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/E0423.rs:21:32 + | +LL | for _ in std::ops::Range { start: 0, end: 10 } {} + | ^^^^^ +note: ...due to this, which is why a type is expected after + --> $DIR/E0423.rs:21:37 + | +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 diff --git a/src/test/ui/issues/issue-22644.stderr b/src/test/ui/issues/issue-22644.stderr index cbff5575ed2..08758ce9c94 100644 --- a/src/test/ui/issues/issue-22644.stderr +++ b/src/test/ui/issues/issue-22644.stderr @@ -88,6 +88,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/issue-22644.rs:34:20 + | +LL | println!("{}", a: &mut 4); + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/issue-22644.rs:34:21 + | +LL | println!("{}", a: &mut 4); + | ^ + = help: this might be indicative of a syntax error elsewhere error: aborting due to 9 previous errors diff --git a/src/test/ui/issues/issue-34255-1.rs b/src/test/ui/issues/issue-34255-1.rs new file mode 100644 index 00000000000..b1071934bb2 --- /dev/null +++ b/src/test/ui/issues/issue-34255-1.rs @@ -0,0 +1,10 @@ +enum Test { + Drill { + field: i32, + } +} + +fn main() { + Test::Drill(field: 42); + //~^ ERROR expected type, found +} diff --git a/src/test/ui/issues/issue-34255-1.stderr b/src/test/ui/issues/issue-34255-1.stderr new file mode 100644 index 00000000000..ea324302d40 --- /dev/null +++ b/src/test/ui/issues/issue-34255-1.stderr @@ -0,0 +1,21 @@ +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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/issue-34255-1.rs:8:17 + | +LL | Test::Drill(field: 42); + | ^^^^^ +note: ...due to this, which is why a type is expected after + --> $DIR/issue-34255-1.rs:8:22 + | +LL | Test::Drill(field: 42); + | ^ + = help: this might be indicative of a syntax error elsewhere + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime_starts_expressions.stderr b/src/test/ui/lifetime_starts_expressions.stderr index fa0a7ac002b..3de3298e3b5 100644 --- a/src/test/ui/lifetime_starts_expressions.stderr +++ b/src/test/ui/lifetime_starts_expressions.stderr @@ -13,6 +13,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/lifetime_starts_expressions.rs:6:12 + | +LL | loop { break 'label: loop { break 'label 42; }; } + | ^^^^^^^^^^^^ +note: ...due to this, which is why a type is expected after + --> $DIR/lifetime_starts_expressions.rs:6:24 + | +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 diff --git a/src/test/ui/parser/struct-literal-in-for.stderr b/src/test/ui/parser/struct-literal-in-for.stderr index b319c64f406..2940f465826 100644 --- a/src/test/ui/parser/struct-literal-in-for.stderr +++ b/src/test/ui/parser/struct-literal-in-for.stderr @@ -3,6 +3,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/struct-literal-in-for.rs:13:9 + | +LL | x: 3 + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/struct-literal-in-for.rs:13:10 + | +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 diff --git a/src/test/ui/parser/struct-literal-in-if.stderr b/src/test/ui/parser/struct-literal-in-if.stderr index 27672eeda83..e7d22ae0292 100644 --- a/src/test/ui/parser/struct-literal-in-if.stderr +++ b/src/test/ui/parser/struct-literal-in-if.stderr @@ -3,6 +3,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/struct-literal-in-if.rs:13:9 + | +LL | x: 3 + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/struct-literal-in-if.rs:13:10 + | +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 diff --git a/src/test/ui/parser/struct-literal-in-while.stderr b/src/test/ui/parser/struct-literal-in-while.stderr index 8a130f441a3..038e30956ff 100644 --- a/src/test/ui/parser/struct-literal-in-while.stderr +++ b/src/test/ui/parser/struct-literal-in-while.stderr @@ -3,6 +3,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/struct-literal-in-while.rs:13:9 + | +LL | x: 3 + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/struct-literal-in-while.rs:13:10 + | +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 diff --git a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr index 3505d00b64b..b3a6f6ac734 100644 --- a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr +++ b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr @@ -3,6 +3,19 @@ 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 expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/struct-literal-restrictions-in-lamda.rs:13:9 + | +LL | x: 3 + | ^ +note: ...due to this, which is why a type is expected after + --> $DIR/struct-literal-restrictions-in-lamda.rs:13:10 + | +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 diff --git a/src/test/ui/suggestions/type-ascription-instead-of-let.rs b/src/test/ui/suggestions/type-ascription-instead-of-let.rs new file mode 100644 index 00000000000..51d3d32565f --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-let.rs @@ -0,0 +1,11 @@ +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 + //~| ERROR type ascription is experimental + temp + value + 1 + //~^ ERROR cannot find value `temp` in this scope + }; +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-let.stderr b/src/test/ui/suggestions/type-ascription-instead-of-let.stderr new file mode 100644 index 00000000000..1efa94a5532 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-let.stderr @@ -0,0 +1,28 @@ +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 + | expecting a type here because of type ascription + | 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:8:9 + | +LL | temp + value + 1 + | ^^^^ not found in this scope + +error[E0658]: type ascription is experimental (see issue #23416) + --> $DIR/type-ascription-instead-of-let.rs:5:9 + | +LL | temp: i32 = fun(5i32); + | ^^^^^^^^^ + | + = help: add #![feature(type_ascription)] to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors occurred: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/suggestions/type-ascription-instead-of-method.rs b/src/test/ui/suggestions/type-ascription-instead-of-method.rs new file mode 100644 index 00000000000..361729d50c2 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-method.rs @@ -0,0 +1,4 @@ +fn main() { + Box:new("foo".to_string()) + //~^ ERROR expected type, found +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-method.stderr b/src/test/ui/suggestions/type-ascription-instead-of-method.stderr new file mode 100644 index 00000000000..15ec087b1cc --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-method.stderr @@ -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 + diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path.rs b/src/test/ui/suggestions/type-ascription-instead-of-path.rs new file mode 100644 index 00000000000..a81996ed7bb --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-path.rs @@ -0,0 +1,6 @@ +fn main() { + std:io::stdin(); + //~^ ERROR failed to resolve: use of undeclared type or module `io` + //~| ERROR expected value, found module + //~| ERROR type ascription is experimental +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path.stderr b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr new file mode 100644 index 00000000000..e371611ccff --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr @@ -0,0 +1,35 @@ +error[E0433]: failed to resolve: use of undeclared type or module `io` + --> $DIR/type-ascription-instead-of-path.rs:2:9 + | +LL | std:io::stdin(); + | ^^ use of undeclared type or module `io` + +error[E0423]: expected value, found module `std` + --> $DIR/type-ascription-instead-of-path.rs:2:5 + | +LL | std:io::stdin(); + | ^^^ + | | + | not a value + | expecting a type here because of type ascription +help: maybe you meant to write a path separator here + | +LL | std::io::stdin(); + | ^^ +help: maybe you meant to write an assignment here + | +LL | let std:io::stdin(); + | ^^^^^^^ + +error[E0658]: type ascription is experimental (see issue #23416) + --> $DIR/type-ascription-instead-of-path.rs:2:5 + | +LL | std:io::stdin(); + | ^^^^^^^^^^^^^^^ + | + = help: add #![feature(type_ascription)] to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors occurred: E0423, E0433, E0658. +For more information about an error, try `rustc --explain E0423`. diff --git a/src/test/ui/suggestions/type-ascription-instead-of-variant.rs b/src/test/ui/suggestions/type-ascription-instead-of-variant.rs new file mode 100644 index 00000000000..b90867fef6b --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-variant.rs @@ -0,0 +1,4 @@ +fn main() { + let _ = Option:Some(""); + //~^ ERROR expected type, found +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr b/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr new file mode 100644 index 00000000000..5719a667a84 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr @@ -0,0 +1,10 @@ +error: expected type, found `""` + --> $DIR/type-ascription-instead-of-variant.rs:2:25 + | +LL | let _ = Option:Some(""); + | - ^^ expecting a type here because of type ascription + | | + | help: maybe you meant to write a path separator here: `::` + +error: aborting due to previous error + diff --git a/src/test/ui/type/type-ascription-instead-of-statement-end.stderr b/src/test/ui/type/type-ascription-instead-of-statement-end.stderr index bc5a923a3f3..4077be9d082 100644 --- a/src/test/ui/type/type-ascription-instead-of-statement-end.stderr +++ b/src/test/ui/type/type-ascription-instead-of-statement-end.stderr @@ -11,6 +11,19 @@ error: expected type, found `0` | LL | println!("test"): 0; | ^ expecting a type here because of type ascription + | + = note: type ascription is a nightly only feature that lets you annotate expressions with a type: `: ` +note: this expression is annotated with type ascription... + --> $DIR/type-ascription-instead-of-statement-end.rs:9:5 + | +LL | println!("test"): 0; + | ^^^^^^^^^^^^^^^^ +note: ...due to this, which is why a type is expected after + --> $DIR/type-ascription-instead-of-statement-end.rs:9:21 + | +LL | println!("test"): 0; + | ^ + = help: this might be indicative of a syntax error elsewhere error: aborting due to 2 previous errors -- 2.44.0