Skip to content

Commit bc3295c

Browse files
authored
Rollup merge of rust-lang#59150 - estebank:type-ascription, r=varkor
Expand suggestions for type ascription parse errors Fix rust-lang#51222. CC rust-lang#48016, rust-lang#47666, rust-lang#54516, rust-lang#34255.
2 parents 54479c6 + d72ef21 commit bc3295c

21 files changed

+302
-22
lines changed

src/librustc_resolve/lib.rs

+57-7
Original file line numberDiff line numberDiff line change
@@ -3262,32 +3262,82 @@ impl<'a> Resolver<'a> {
32623262
resolution
32633263
}
32643264

3265-
fn type_ascription_suggestion(&self,
3266-
err: &mut DiagnosticBuilder<'_>,
3267-
base_span: Span) {
3265+
/// Only used in a specific case of type ascription suggestions
3266+
#[doc(hidden)]
3267+
fn get_colon_suggestion_span(&self, start: Span) -> Span {
3268+
let cm = self.session.source_map();
3269+
start.to(cm.next_point(start))
3270+
}
3271+
3272+
fn type_ascription_suggestion(
3273+
&self,
3274+
err: &mut DiagnosticBuilder<'_>,
3275+
base_span: Span,
3276+
) {
32683277
debug!("type_ascription_suggetion {:?}", base_span);
32693278
let cm = self.session.source_map();
3279+
let base_snippet = cm.span_to_snippet(base_span);
32703280
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
32713281
if let Some(sp) = self.current_type_ascription.last() {
32723282
let mut sp = *sp;
32733283
loop {
32743284
// Try to find the `:`; bail on first non-':' / non-whitespace.
32753285
sp = cm.next_point(sp);
32763286
if let Ok(snippet) = cm.span_to_snippet(sp.to(cm.next_point(sp))) {
3277-
debug!("snippet {:?}", snippet);
32783287
let line_sp = cm.lookup_char_pos(sp.hi()).line;
32793288
let line_base_sp = cm.lookup_char_pos(base_span.lo()).line;
3280-
debug!("{:?} {:?}", line_sp, line_base_sp);
32813289
if snippet == ":" {
3282-
err.span_label(base_span,
3283-
"expecting a type here because of type ascription");
3290+
let mut show_label = true;
32843291
if line_sp != line_base_sp {
32853292
err.span_suggestion_short(
32863293
sp,
32873294
"did you mean to use `;` here instead?",
32883295
";".to_string(),
32893296
Applicability::MaybeIncorrect,
32903297
);
3298+
} else {
3299+
let colon_sp = self.get_colon_suggestion_span(sp);
3300+
let after_colon_sp = self.get_colon_suggestion_span(
3301+
colon_sp.shrink_to_hi(),
3302+
);
3303+
if !cm.span_to_snippet(after_colon_sp).map(|s| s == " ")
3304+
.unwrap_or(false)
3305+
{
3306+
err.span_suggestion(
3307+
colon_sp,
3308+
"maybe you meant to write a path separator here",
3309+
"::".to_string(),
3310+
Applicability::MaybeIncorrect,
3311+
);
3312+
show_label = false;
3313+
}
3314+
if let Ok(base_snippet) = base_snippet {
3315+
let mut sp = after_colon_sp;
3316+
for _ in 0..100 {
3317+
// Try to find an assignment
3318+
sp = cm.next_point(sp);
3319+
let snippet = cm.span_to_snippet(sp.to(cm.next_point(sp)));
3320+
match snippet {
3321+
Ok(ref x) if x.as_str() == "=" => {
3322+
err.span_suggestion(
3323+
base_span,
3324+
"maybe you meant to write an assignment here",
3325+
format!("let {}", base_snippet),
3326+
Applicability::MaybeIncorrect,
3327+
);
3328+
show_label = false;
3329+
break;
3330+
}
3331+
Ok(ref x) if x.as_str() == "\n" => break,
3332+
Err(_) => break,
3333+
Ok(_) => {}
3334+
}
3335+
}
3336+
}
3337+
}
3338+
if show_label {
3339+
err.span_label(base_span,
3340+
"expecting a type here because of type ascription");
32913341
}
32923342
break;
32933343
} else if !snippet.trim().is_empty() {

src/libsyntax/feature_gate.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1836,8 +1836,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
18361836
gate_feature_post!(&self, box_syntax, e.span, EXPLAIN_BOX_SYNTAX);
18371837
}
18381838
ast::ExprKind::Type(..) => {
1839-
gate_feature_post!(&self, type_ascription, e.span,
1840-
"type ascription is experimental");
1839+
// To avoid noise about type ascription in common syntax errors, only emit if it
1840+
// is the *only* error.
1841+
if self.context.parse_sess.span_diagnostic.err_count() == 0 {
1842+
gate_feature_post!(&self, type_ascription, e.span,
1843+
"type ascription is experimental");
1844+
}
18411845
}
18421846
ast::ExprKind::ObsoleteInPlace(..) => {
18431847
// these get a hard error in ast-validation

src/libsyntax/parse/parser.rs

+62-13
Original file line numberDiff line numberDiff line change
@@ -3538,22 +3538,19 @@ impl<'a> Parser<'a> {
35383538
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
35393539
continue
35403540
} else if op == AssocOp::Colon {
3541+
let maybe_path = self.could_ascription_be_path(&lhs.node);
3542+
let next_sp = self.span;
3543+
35413544
lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
35423545
Ok(lhs) => lhs,
35433546
Err(mut err) => {
3544-
err.span_label(self.span,
3545-
"expecting a type here because of type ascription");
3546-
let cm = self.sess.source_map();
3547-
let cur_pos = cm.lookup_char_pos(self.span.lo());
3548-
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
3549-
if cur_pos.line != op_pos.line {
3550-
err.span_suggestion(
3551-
cur_op_span,
3552-
"try using a semicolon",
3553-
";".to_string(),
3554-
Applicability::MaybeIncorrect // speculative
3555-
);
3556-
}
3547+
self.bad_type_ascription(
3548+
&mut err,
3549+
lhs_span,
3550+
cur_op_span,
3551+
next_sp,
3552+
maybe_path,
3553+
);
35573554
return Err(err);
35583555
}
35593556
};
@@ -3658,6 +3655,58 @@ impl<'a> Parser<'a> {
36583655
Ok(lhs)
36593656
}
36603657

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

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

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
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+
= help: this might be indicative of a syntax error elsewhere
614

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

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

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

+8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ 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 an expression with a type: `<expr>: <type>`
93+
note: this expression expects an ascribed type after the colon
94+
--> $DIR/issue-22644.rs:34:20
95+
|
96+
LL | println!("{}", a: &mut 4);
97+
| ^
98+
= help: this might be indicative of a syntax error elsewhere
9199

92100
error: aborting due to 9 previous errors
93101

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

+10
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+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
9+
--> $DIR/issue-34255-1.rs:8:17
10+
|
11+
LL | Test::Drill(field: 42);
12+
| ^^^^^
13+
= help: this might be indicative of a syntax error elsewhere
14+
15+
error: aborting due to previous error
16+

src/test/ui/lifetime_starts_expressions.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ 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 an expression with a type: `<expr>: <type>`
18+
note: this expression expects an ascribed type after the colon
19+
--> $DIR/lifetime_starts_expressions.rs:6:12
20+
|
21+
LL | loop { break 'label: loop { break 'label 42; }; }
22+
| ^^^^^^^^^^^^
23+
= help: this might be indicative of a syntax error elsewhere
1624

1725
error: aborting due to 2 previous errors
1826

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

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
9+
--> $DIR/struct-literal-in-for.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
= help: this might be indicative of a syntax error elsewhere
614

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

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

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
9+
--> $DIR/struct-literal-in-if.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
= help: this might be indicative of a syntax error elsewhere
614

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

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

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
9+
--> $DIR/struct-literal-in-while.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
= help: this might be indicative of a syntax error elsewhere
614

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

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

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ 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 an expression with a type: `<expr>: <type>`
8+
note: this expression expects an ascribed type after the colon
9+
--> $DIR/struct-literal-restrictions-in-lamda.rs:13:9
10+
|
11+
LL | x: 3
12+
| ^
13+
= help: this might be indicative of a syntax error elsewhere
614

715
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
816
--> $DIR/struct-literal-restrictions-in-lamda.rs:14:12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
temp + value + 1
8+
//~^ ERROR cannot find value `temp` in this scope
9+
};
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
| help: maybe you meant to write an assignment here: `let temp`
9+
10+
error[E0425]: cannot find value `temp` in this scope
11+
--> $DIR/type-ascription-instead-of-let.rs:7:9
12+
|
13+
LL | temp + value + 1
14+
| ^^^^ not found in this scope
15+
16+
error: aborting due to 2 previous errors
17+
18+
For more information about this error, try `rustc --explain E0425`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
Box:new("foo".to_string())
3+
//~^ ERROR expected type, found
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: expected type, found `"foo"`
2+
--> $DIR/type-ascription-instead-of-method.rs:2:13
3+
|
4+
LL | Box:new("foo".to_string())
5+
| - ^^^^^ expecting a type here because of type ascription
6+
| |
7+
| help: maybe you meant to write a path separator here: `::`
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
std:io::stdin();
3+
//~^ ERROR failed to resolve: use of undeclared type or module `io`
4+
//~| ERROR expected value, found module
5+
}

0 commit comments

Comments
 (0)