Skip to content

Commit 8ba1a97

Browse files
committed
Expand suggestions for type ascription parse errors
1 parent 9f91bee commit 8ba1a97

20 files changed

+358
-18
lines changed

src/librustc_resolve/lib.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,22 +3264,30 @@ impl<'a> Resolver<'a> {
32643264
resolution
32653265
}
32663266

3267-
fn type_ascription_suggestion(&self,
3268-
err: &mut DiagnosticBuilder<'_>,
3269-
base_span: Span) {
3267+
/// Only used in a specific case of type ascription suggestions
3268+
#[doc(hidden)]
3269+
fn get_colon_suggestion_span(&self, start: Span) -> Span {
3270+
let cm = self.session.source_map();
3271+
start.to(cm.next_point(start))
3272+
}
3273+
3274+
fn type_ascription_suggestion(
3275+
&self,
3276+
err: &mut DiagnosticBuilder<'_>,
3277+
base_span: Span,
3278+
) {
32703279
debug!("type_ascription_suggetion {:?}", base_span);
32713280
let cm = self.session.source_map();
3281+
let base_snippet = cm.span_to_snippet(base_span);
32723282
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
32733283
if let Some(sp) = self.current_type_ascription.last() {
32743284
let mut sp = *sp;
32753285
loop {
32763286
// Try to find the `:`; bail on first non-':' / non-whitespace.
32773287
sp = cm.next_point(sp);
32783288
if let Ok(snippet) = cm.span_to_snippet(sp.to(cm.next_point(sp))) {
3279-
debug!("snippet {:?}", snippet);
32803289
let line_sp = cm.lookup_char_pos(sp.hi()).line;
32813290
let line_base_sp = cm.lookup_char_pos(base_span.lo()).line;
3282-
debug!("{:?} {:?}", line_sp, line_base_sp);
32833291
if snippet == ":" {
32843292
err.span_label(base_span,
32853293
"expecting a type here because of type ascription");
@@ -3290,6 +3298,29 @@ impl<'a> Resolver<'a> {
32903298
";".to_string(),
32913299
Applicability::MaybeIncorrect,
32923300
);
3301+
} else {
3302+
let colon_sp = self.get_colon_suggestion_span(sp);
3303+
let after_colon_sp = self.get_colon_suggestion_span(
3304+
colon_sp.shrink_to_hi(),
3305+
);
3306+
if !cm.span_to_snippet(after_colon_sp).map(|s| s == " ")
3307+
.unwrap_or(false)
3308+
{
3309+
err.span_suggestion(
3310+
colon_sp,
3311+
"maybe you meant to write a path separator here",
3312+
"::".to_string(),
3313+
Applicability::MaybeIncorrect,
3314+
);
3315+
}
3316+
if let Ok(base_snippet) = base_snippet {
3317+
err.span_suggestion(
3318+
base_span,
3319+
"maybe you meant to write an assignment here",
3320+
format!("let {}", base_snippet),
3321+
Applicability::MaybeIncorrect,
3322+
);
3323+
}
32933324
}
32943325
break;
32953326
} else if !snippet.trim().is_empty() {

src/libsyntax/parse/parser.rs

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,22 +3546,19 @@ impl<'a> Parser<'a> {
35463546
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
35473547
continue
35483548
} else if op == AssocOp::Colon {
3549+
let maybe_path = self.could_ascription_be_path(&lhs.node);
3550+
let next_sp = self.span;
3551+
35493552
lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
35503553
Ok(lhs) => lhs,
35513554
Err(mut err) => {
3552-
err.span_label(self.span,
3553-
"expecting a type here because of type ascription");
3554-
let cm = self.sess.source_map();
3555-
let cur_pos = cm.lookup_char_pos(self.span.lo());
3556-
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
3557-
if cur_pos.line != op_pos.line {
3558-
err.span_suggestion(
3559-
cur_op_span,
3560-
"try using a semicolon",
3561-
";".to_string(),
3562-
Applicability::MaybeIncorrect // speculative
3563-
);
3564-
}
3555+
self.bad_type_ascription(
3556+
&mut err,
3557+
lhs_span,
3558+
cur_op_span,
3559+
next_sp,
3560+
maybe_path,
3561+
);
35653562
return Err(err);
35663563
}
35673564
};
@@ -3666,6 +3663,62 @@ impl<'a> Parser<'a> {
36663663
Ok(lhs)
36673664
}
36683665

3666+
fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
3667+
self.token.is_ident() &&
3668+
if let ast::ExprKind::Path(..) = node { true } else { false } &&
3669+
!self.token.is_reserved_ident() && // v `foo:bar(baz)`
3670+
self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
3671+
self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz`
3672+
self.look_ahead(2, |t| t.is_ident()) ||
3673+
self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
3674+
self.look_ahead(2, |t| t.is_ident()) ||
3675+
self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
3676+
self.look_ahead(2, |t| t.is_ident())
3677+
}
3678+
3679+
fn bad_type_ascription(
3680+
&self,
3681+
err: &mut DiagnosticBuilder<'a>,
3682+
lhs_span: Span,
3683+
cur_op_span: Span,
3684+
next_sp: Span,
3685+
maybe_path: bool,
3686+
) {
3687+
err.span_label(self.span, "expecting a type here because of type ascription");
3688+
let cm = self.sess.source_map();
3689+
let next_pos = cm.lookup_char_pos(next_sp.lo());
3690+
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
3691+
if op_pos.line != next_pos.line {
3692+
err.span_suggestion(
3693+
cur_op_span,
3694+
"try using a semicolon",
3695+
";".to_string(),
3696+
Applicability::MaybeIncorrect,
3697+
);
3698+
} else {
3699+
if maybe_path {
3700+
err.span_suggestion(
3701+
cur_op_span,
3702+
"maybe you meant to write a path separator here",
3703+
"::".to_string(),
3704+
Applicability::MaybeIncorrect,
3705+
);
3706+
} else {
3707+
err.note("type ascription is a nightly only feature that lets \
3708+
you annotate expressions with a type: `<expr>: <type>`");
3709+
err.span_note(
3710+
lhs_span,
3711+
"this expression is annotated with type ascription...",
3712+
);
3713+
err.span_note(
3714+
cur_op_span,
3715+
"...due to this, which is why a type is expected after",
3716+
);
3717+
err.help("this might be indicative of a syntax error elsewhere");
3718+
}
3719+
}
3720+
}
3721+
36693722
fn parse_assoc_op_cast(&mut self, lhs: P<Expr>, lhs_span: Span,
36703723
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind)
36713724
-> PResult<'a, P<Expr>> {

src/test/ui/error-codes/E0423.stderr

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ error: expected type, found `1`
33
|
44
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
55
| ^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/E0423.rs:12:36
10+
|
11+
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
12+
| ^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/E0423.rs:12:37
15+
|
16+
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
619

720
error: expected expression, found `==`
821
--> $DIR/E0423.rs:15:13
@@ -15,6 +28,19 @@ error: expected type, found `0`
1528
|
1629
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
1730
| ^ expecting a type here because of type ascription
31+
|
32+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
33+
note: this expression is annotated with type ascription...
34+
--> $DIR/E0423.rs:21:32
35+
|
36+
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
37+
| ^^^^^
38+
note: ...due to this, which is why a type is expected after
39+
--> $DIR/E0423.rs:21:37
40+
|
41+
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
42+
| ^
43+
= help: this might be indicative of a syntax error elsewhere
1844

1945
error[E0423]: expected function, found struct `Foo`
2046
--> $DIR/E0423.rs:4:13

src/test/ui/issues/issue-22644.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ error: expected type, found `4`
8888
|
8989
LL | println!("{}", a: &mut 4);
9090
| ^ expecting a type here because of type ascription
91+
|
92+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
93+
note: this expression is annotated with type ascription...
94+
--> $DIR/issue-22644.rs:34:20
95+
|
96+
LL | println!("{}", a: &mut 4);
97+
| ^
98+
note: ...due to this, which is why a type is expected after
99+
--> $DIR/issue-22644.rs:34:21
100+
|
101+
LL | println!("{}", a: &mut 4);
102+
| ^
103+
= help: this might be indicative of a syntax error elsewhere
91104

92105
error: aborting due to 9 previous errors
93106

src/test/ui/issues/issue-34255-1.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum Test {
2+
Drill {
3+
field: i32,
4+
}
5+
}
6+
7+
fn main() {
8+
Test::Drill(field: 42);
9+
//~^ ERROR expected type, found
10+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error: expected type, found `42`
2+
--> $DIR/issue-34255-1.rs:8:24
3+
|
4+
LL | Test::Drill(field: 42);
5+
| ^^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/issue-34255-1.rs:8:17
10+
|
11+
LL | Test::Drill(field: 42);
12+
| ^^^^^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/issue-34255-1.rs:8:22
15+
|
16+
LL | Test::Drill(field: 42);
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
19+
20+
error: aborting due to previous error
21+

src/test/ui/lifetime_starts_expressions.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ error: expected type, found keyword `loop`
1313
|
1414
LL | loop { break 'label: loop { break 'label 42; }; }
1515
| ^^^^ expecting a type here because of type ascription
16+
|
17+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
18+
note: this expression is annotated with type ascription...
19+
--> $DIR/lifetime_starts_expressions.rs:6:12
20+
|
21+
LL | loop { break 'label: loop { break 'label 42; }; }
22+
| ^^^^^^^^^^^^
23+
note: ...due to this, which is why a type is expected after
24+
--> $DIR/lifetime_starts_expressions.rs:6:24
25+
|
26+
LL | loop { break 'label: loop { break 'label 42; }; }
27+
| ^
28+
= help: this might be indicative of a syntax error elsewhere
1629

1730
error: aborting due to 2 previous errors
1831

src/test/ui/parser/struct-literal-in-for.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ error: expected type, found `3`
33
|
44
LL | x: 3
55
| ^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/struct-literal-in-for.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/struct-literal-in-for.rs:13:10
15+
|
16+
LL | x: 3
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
619

720
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
821
--> $DIR/struct-literal-in-for.rs:14:12

src/test/ui/parser/struct-literal-in-if.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ error: expected type, found `3`
33
|
44
LL | x: 3
55
| ^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/struct-literal-in-if.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/struct-literal-in-if.rs:13:10
15+
|
16+
LL | x: 3
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
619

720
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
821
--> $DIR/struct-literal-in-if.rs:14:12

src/test/ui/parser/struct-literal-in-while.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ error: expected type, found `3`
33
|
44
LL | x: 3
55
| ^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/struct-literal-in-while.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/struct-literal-in-while.rs:13:10
15+
|
16+
LL | x: 3
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
619

720
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
821
--> $DIR/struct-literal-in-while.rs:14:12

src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ error: expected type, found `3`
33
|
44
LL | x: 3
55
| ^ expecting a type here because of type ascription
6+
|
7+
= note: type ascription is a nightly only feature that lets you annotate expressions with a type: `<expr>: <type>`
8+
note: this expression is annotated with type ascription...
9+
--> $DIR/struct-literal-restrictions-in-lamda.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
note: ...due to this, which is why a type is expected after
14+
--> $DIR/struct-literal-restrictions-in-lamda.rs:13:10
15+
|
16+
LL | x: 3
17+
| ^
18+
= help: this might be indicative of a syntax error elsewhere
619

720
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
821
--> $DIR/struct-literal-restrictions-in-lamda.rs:14:12
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn fun(x: i32) -> i32 { x }
2+
3+
fn main() {
4+
let closure_annotated = |value: i32| -> i32 {
5+
temp: i32 = fun(5i32);
6+
//~^ ERROR cannot find value `temp` in this scope
7+
//~| ERROR type ascription is experimental
8+
temp + value + 1
9+
//~^ ERROR cannot find value `temp` in this scope
10+
};
11+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error[E0425]: cannot find value `temp` in this scope
2+
--> $DIR/type-ascription-instead-of-let.rs:5:9
3+
|
4+
LL | temp: i32 = fun(5i32);
5+
| ^^^^
6+
| |
7+
| not found in this scope
8+
| expecting a type here because of type ascription
9+
| help: maybe you meant to write an assignment here: `let temp`
10+
11+
error[E0425]: cannot find value `temp` in this scope
12+
--> $DIR/type-ascription-instead-of-let.rs:8:9
13+
|
14+
LL | temp + value + 1
15+
| ^^^^ not found in this scope
16+
17+
error[E0658]: type ascription is experimental (see issue #23416)
18+
--> $DIR/type-ascription-instead-of-let.rs:5:9
19+
|
20+
LL | temp: i32 = fun(5i32);
21+
| ^^^^^^^^^
22+
|
23+
= help: add #![feature(type_ascription)] to the crate attributes to enable
24+
25+
error: aborting due to 3 previous errors
26+
27+
Some errors occurred: E0425, E0658.
28+
For more information about an error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)