Skip to content

Commit ab7149b

Browse files
Rollup merge of rust-lang#62791 - estebank:type-ascription, r=petrochenkov
Handle more cases of typos misinterpreted as type ascription Fix rust-lang#60933, rust-lang#54516. CC rust-lang#47666, rust-lang#34255, rust-lang#48016.
2 parents 4264f83 + 9dbe2e7 commit ab7149b

29 files changed

+225
-118
lines changed

src/libsyntax/parse/diagnostics.rs

+53-46
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::ast::{
22
self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
33
Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData,
44
};
5+
use crate::feature_gate::{feature_err, UnstableFeatures};
56
use crate::parse::{SeqSep, PResult, Parser, ParseSess};
67
use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType};
78
use crate::parse::token::{self, TokenKind};
@@ -326,8 +327,8 @@ impl<'a> Parser<'a> {
326327
self.token.is_keyword(kw::Return) ||
327328
self.token.is_keyword(kw::While)
328329
);
329-
let cm = self.sess.source_map();
330-
match (cm.lookup_line(self.token.span.lo()), cm.lookup_line(sp.lo())) {
330+
let sm = self.sess.source_map();
331+
match (sm.lookup_line(self.token.span.lo()), sm.lookup_line(sp.lo())) {
331332
(Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => {
332333
// The spans are in different lines, expected `;` and found `let` or `return`.
333334
// High likelihood that it is only a missing `;`.
@@ -365,9 +366,53 @@ impl<'a> Parser<'a> {
365366
err.span_label(self.token.span, "unexpected token");
366367
}
367368
}
369+
self.maybe_annotate_with_ascription(&mut err, false);
368370
Err(err)
369371
}
370372

373+
pub fn maybe_annotate_with_ascription(
374+
&self,
375+
err: &mut DiagnosticBuilder<'_>,
376+
maybe_expected_semicolon: bool,
377+
) {
378+
if let Some((sp, likely_path)) = self.last_type_ascription {
379+
let sm = self.sess.source_map();
380+
let next_pos = sm.lookup_char_pos(self.token.span.lo());
381+
let op_pos = sm.lookup_char_pos(sp.hi());
382+
383+
if likely_path {
384+
err.span_suggestion(
385+
sp,
386+
"maybe write a path separator here",
387+
"::".to_string(),
388+
match self.sess.unstable_features {
389+
UnstableFeatures::Disallow => Applicability::MachineApplicable,
390+
_ => Applicability::MaybeIncorrect,
391+
},
392+
);
393+
} else if op_pos.line != next_pos.line && maybe_expected_semicolon {
394+
err.span_suggestion(
395+
sp,
396+
"try using a semicolon",
397+
";".to_string(),
398+
Applicability::MaybeIncorrect,
399+
);
400+
} else if let UnstableFeatures::Disallow = self.sess.unstable_features {
401+
err.span_label(sp, "tried to parse a type due to this");
402+
} else {
403+
err.span_label(sp, "tried to parse a type due to this type ascription");
404+
}
405+
if let UnstableFeatures::Disallow = self.sess.unstable_features {
406+
// Give extra information about type ascription only if it's a nightly compiler.
407+
} else {
408+
err.note("`#![feature(type_ascription)]` lets you annotate an expression with a \
409+
type: `<expr>: <type>`");
410+
err.note("for more information, see \
411+
https://github.com/rust-lang/rust/issues/23416");
412+
}
413+
}
414+
}
415+
371416
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
372417
/// passes through any errors encountered. Used for error recovery.
373418
crate fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
@@ -556,7 +601,7 @@ impl<'a> Parser<'a> {
556601
.collect::<Vec<_>>();
557602

558603
if !discriminant_spans.is_empty() && has_fields {
559-
let mut err = crate::feature_gate::feature_err(
604+
let mut err = feature_err(
560605
sess,
561606
sym::arbitrary_enum_discriminant,
562607
discriminant_spans.clone(),
@@ -769,8 +814,8 @@ impl<'a> Parser<'a> {
769814
return Ok(recovered);
770815
}
771816
}
772-
let cm = self.sess.source_map();
773-
match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
817+
let sm = self.sess.source_map();
818+
match (sm.lookup_line(prev_sp.lo()), sm.lookup_line(sp.lo())) {
774819
(Ok(ref a), Ok(ref b)) if a.line == b.line => {
775820
// When the spans are in the same line, it means that the only content
776821
// between them is whitespace, point only at the found token.
@@ -887,47 +932,9 @@ impl<'a> Parser<'a> {
887932
self.look_ahead(2, |t| t.is_ident()) ||
888933
self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
889934
self.look_ahead(2, |t| t.is_ident()) ||
890-
self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
891-
self.look_ahead(2, |t| t.is_ident())
892-
}
893-
894-
crate fn bad_type_ascription(
895-
&self,
896-
err: &mut DiagnosticBuilder<'a>,
897-
lhs_span: Span,
898-
cur_op_span: Span,
899-
next_sp: Span,
900-
maybe_path: bool,
901-
) {
902-
err.span_label(self.token.span, "expecting a type here because of type ascription");
903-
let cm = self.sess.source_map();
904-
let next_pos = cm.lookup_char_pos(next_sp.lo());
905-
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
906-
if op_pos.line != next_pos.line {
907-
err.span_suggestion(
908-
cur_op_span,
909-
"try using a semicolon",
910-
";".to_string(),
911-
Applicability::MaybeIncorrect,
912-
);
913-
} else {
914-
if maybe_path {
915-
err.span_suggestion(
916-
cur_op_span,
917-
"maybe you meant to write a path separator here",
918-
"::".to_string(),
919-
Applicability::MaybeIncorrect,
920-
);
921-
} else {
922-
err.note("`#![feature(type_ascription)]` lets you annotate an \
923-
expression with a type: `<expr>: <type>`")
924-
.span_note(
925-
lhs_span,
926-
"this expression expects an ascribed type after the colon",
927-
)
928-
.help("this might be indicative of a syntax error elsewhere");
929-
}
930-
}
935+
self.look_ahead(1, |t| t == &token::ModSep) &&
936+
(self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz`
937+
self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::<baz>`
931938
}
932939

933940
crate fn recover_seq_parse_error(

src/libsyntax/parse/parser.rs

+20-19
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ pub struct Parser<'a> {
239239
/// error.
240240
crate unclosed_delims: Vec<UnmatchedBrace>,
241241
crate last_unexpected_token_span: Option<Span>,
242+
crate last_type_ascription: Option<(Span, bool /* likely path typo */)>,
242243
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
243244
crate subparser_name: Option<&'static str>,
244245
}
@@ -502,6 +503,7 @@ impl<'a> Parser<'a> {
502503
max_angle_bracket_count: 0,
503504
unclosed_delims: Vec::new(),
504505
last_unexpected_token_span: None,
506+
last_type_ascription: None,
505507
subparser_name,
506508
};
507509

@@ -1422,7 +1424,10 @@ impl<'a> Parser<'a> {
14221424
}
14231425
} else {
14241426
let msg = format!("expected type, found {}", self.this_token_descr());
1425-
return Err(self.fatal(&msg));
1427+
let mut err = self.fatal(&msg);
1428+
err.span_label(self.token.span, "expected type");
1429+
self.maybe_annotate_with_ascription(&mut err, true);
1430+
return Err(err);
14261431
};
14271432

14281433
let span = lo.to(self.prev_span);
@@ -2823,10 +2828,11 @@ impl<'a> Parser<'a> {
28232828
}
28242829

28252830
/// Parses an associative expression with operators of at least `min_prec` precedence.
2826-
fn parse_assoc_expr_with(&mut self,
2827-
min_prec: usize,
2828-
lhs: LhsExpr)
2829-
-> PResult<'a, P<Expr>> {
2831+
fn parse_assoc_expr_with(
2832+
&mut self,
2833+
min_prec: usize,
2834+
lhs: LhsExpr,
2835+
) -> PResult<'a, P<Expr>> {
28302836
let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
28312837
expr
28322838
} else {
@@ -2840,9 +2846,11 @@ impl<'a> Parser<'a> {
28402846
self.parse_prefix_expr(attrs)?
28412847
}
28422848
};
2849+
let last_type_ascription_set = self.last_type_ascription.is_some();
28432850

28442851
match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) {
28452852
(true, None) => {
2853+
self.last_type_ascription = None;
28462854
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
28472855
return Ok(lhs);
28482856
}
@@ -2857,12 +2865,14 @@ impl<'a> Parser<'a> {
28572865
// If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
28582866
// `if x { a } else { b } && if y { c } else { d }`
28592867
if !self.look_ahead(1, |t| t.is_reserved_ident()) => {
2868+
self.last_type_ascription = None;
28602869
// These cases are ambiguous and can't be identified in the parser alone
28612870
let sp = self.sess.source_map().start_point(self.token.span);
28622871
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
28632872
return Ok(lhs);
28642873
}
28652874
(true, Some(ref op)) if !op.can_continue_expr_unambiguously() => {
2875+
self.last_type_ascription = None;
28662876
return Ok(lhs);
28672877
}
28682878
(true, Some(_)) => {
@@ -2921,21 +2931,9 @@ impl<'a> Parser<'a> {
29212931
continue
29222932
} else if op == AssocOp::Colon {
29232933
let maybe_path = self.could_ascription_be_path(&lhs.node);
2924-
let next_sp = self.token.span;
2934+
self.last_type_ascription = Some((self.prev_span, maybe_path));
29252935

2926-
lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
2927-
Ok(lhs) => lhs,
2928-
Err(mut err) => {
2929-
self.bad_type_ascription(
2930-
&mut err,
2931-
lhs_span,
2932-
cur_op_span,
2933-
next_sp,
2934-
maybe_path,
2935-
);
2936-
return Err(err);
2937-
}
2938-
};
2936+
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
29392937
continue
29402938
} else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
29412939
// If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to
@@ -3020,6 +3018,9 @@ impl<'a> Parser<'a> {
30203018

30213019
if let Fixity::None = fixity { break }
30223020
}
3021+
if last_type_ascription_set {
3022+
self.last_type_ascription = None;
3023+
}
30233024
Ok(lhs)
30243025
}
30253026

src/libsyntax_ext/format.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ fn parse_args<'a>(
141141

142142
while p.token != token::Eof {
143143
if !p.eat(&token::Comma) {
144-
return Err(ecx.struct_span_err(p.token.span, "expected token: `,`"));
144+
let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
145+
err.span_label(p.token.span, "expected `,`");
146+
p.maybe_annotate_with_ascription(&mut err, false);
147+
return Err(err);
145148
}
146149
if p.token == token::Eof {
147150
break;

src/test/ui/codemap_tests/bad-format-args.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ error: expected token: `,`
1010
--> $DIR/bad-format-args.rs:3:16
1111
|
1212
LL | format!("" 1);
13-
| ^
13+
| ^ expected `,`
1414

1515
error: expected token: `,`
1616
--> $DIR/bad-format-args.rs:4:19
1717
|
1818
LL | format!("", 1 1);
19-
| ^
19+
| ^ expected `,`
2020

2121
error: aborting due to 3 previous errors
2222

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

+4-7
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,12 @@ error: expected type, found `4`
8787
--> $DIR/issue-22644.rs:34:28
8888
|
8989
LL | println!("{}", a: &mut 4);
90-
| ^ expecting a type here because of type ascription
90+
| - ^ expected type
91+
| |
92+
| tried to parse a type due to this type ascription
9193
|
9294
= note: `#![feature(type_ascription)]` 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
95+
= note: for more information, see https://github.com/rust-lang/rust/issues/23416
9996

10097
error: aborting due to 9 previous errors
10198

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

+4-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ error: expected type, found `42`
22
--> $DIR/issue-34255-1.rs:8:24
33
|
44
LL | Test::Drill(field: 42);
5-
| ^^ expecting a type here because of type ascription
5+
| - ^^ expected type
6+
| |
7+
| tried to parse a type due to this type ascription
68
|
79
= note: `#![feature(type_ascription)]` 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
10+
= note: for more information, see https://github.com/rust-lang/rust/issues/23416
1411

1512
error: aborting due to previous error
1613

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: expected type, found `0`
22
--> $DIR/issue-39616.rs:1:12
33
|
44
LL | fn foo(a: [0; 1]) {}
5-
| ^
5+
| ^ expected type
66

77
error: expected one of `)`, `,`, `->`, `where`, or `{`, found `]`
88
--> $DIR/issue-39616.rs:1:16

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ LL | bar(baz: $rest)
1515
| - help: try using a semicolon: `;`
1616
...
1717
LL | foo!(true);
18-
| ^^^^ expecting a type here because of type ascription
18+
| ^^^^ expected type
19+
|
20+
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
21+
= note: for more information, see https://github.com/rust-lang/rust/issues/23416
1922

2023
error: aborting due to 2 previous errors
2124

src/test/ui/lifetime_starts_expressions.stderr

+4-7
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,12 @@ error: expected type, found keyword `loop`
1212
--> $DIR/lifetime_starts_expressions.rs:6:26
1313
|
1414
LL | loop { break 'label: loop { break 'label 42; }; }
15-
| ^^^^ expecting a type here because of type ascription
15+
| - ^^^^ expected type
16+
| |
17+
| tried to parse a type due to this type ascription
1618
|
1719
= note: `#![feature(type_ascription)]` 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
20+
= note: for more information, see https://github.com/rust-lang/rust/issues/23416
2421

2522
error: aborting due to 2 previous errors
2623

src/test/ui/macros/missing-comma.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: expected token: `,`
22
--> $DIR/missing-comma.rs:19:19
33
|
44
LL | println!("{}" a);
5-
| ^
5+
| ^ expected `,`
66

77
error: no rules expected the token `b`
88
--> $DIR/missing-comma.rs:21:12

src/test/ui/parser/issue-33262.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: expected type, found `{`
22
--> $DIR/issue-33262.rs:4:22
33
|
44
LL | for i in 0..a as { }
5-
| ^
5+
| ^ expected type
66

77
error: aborting due to previous error
88

src/test/ui/parser/macro/trait-object-macro-matcher.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: expected type, found `'static`
22
--> $DIR/trait-object-macro-matcher.rs:9:8
33
|
44
LL | m!('static);
5-
| ^^^^^^^
5+
| ^^^^^^^ expected type
66

77
error: aborting due to previous error
88

src/test/ui/parser/recover-enum2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: expected type, found `{`
22
--> $DIR/recover-enum2.rs:6:18
33
|
44
LL | abc: {},
5-
| ^
5+
| ^ expected type
66

77
error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
88
--> $DIR/recover-enum2.rs:25:22

0 commit comments

Comments
 (0)