Skip to content

Commit 90b1f5a

Browse files
committed
Auto merge of #76171 - estebank:turbofish-the-revenge, r=davidtwco
Detect turbofish with multiple type params missing leading `::` Fix #76072.
2 parents c1589cc + 62effcb commit 90b1f5a

File tree

4 files changed

+167
-9
lines changed

4 files changed

+167
-9
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+46
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,52 @@ impl<'a> Parser<'a> {
553553
}
554554
}
555555

556+
/// When writing a turbofish with multiple type parameters missing the leading `::`, we will
557+
/// encounter a parse error when encountering the first `,`.
558+
pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
559+
&mut self,
560+
mut e: DiagnosticBuilder<'a>,
561+
expr: &mut P<Expr>,
562+
) -> PResult<'a, ()> {
563+
if let ExprKind::Binary(binop, _, _) = &expr.kind {
564+
if let ast::BinOpKind::Lt = binop.node {
565+
if self.eat(&token::Comma) {
566+
let x = self.parse_seq_to_before_end(
567+
&token::Gt,
568+
SeqSep::trailing_allowed(token::Comma),
569+
|p| p.parse_ty(),
570+
);
571+
match x {
572+
Ok((_, _, false)) => {
573+
self.bump(); // `>`
574+
match self.parse_expr() {
575+
Ok(_) => {
576+
e.span_suggestion_verbose(
577+
binop.span.shrink_to_lo(),
578+
"use `::<...>` instead of `<...>` to specify type arguments",
579+
"::".to_string(),
580+
Applicability::MaybeIncorrect,
581+
);
582+
e.emit();
583+
*expr = self.mk_expr_err(expr.span.to(self.prev_token.span));
584+
return Ok(());
585+
}
586+
Err(mut err) => {
587+
err.cancel();
588+
}
589+
}
590+
}
591+
Err(mut err) => {
592+
err.cancel();
593+
}
594+
_ => {}
595+
}
596+
}
597+
}
598+
}
599+
Err(e)
600+
}
601+
556602
/// Check to see if a pair of chained operators looks like an attempt at chained comparison,
557603
/// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
558604
/// parenthesising the leftmost comparison.

compiler/rustc_parse/src/parser/stmt.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ impl<'a> Parser<'a> {
364364
let mut eat_semi = true;
365365
match stmt.kind {
366366
// Expression without semicolon.
367-
StmtKind::Expr(ref expr)
367+
StmtKind::Expr(ref mut expr)
368368
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
369369
{
370370
// Just check for errors and recover; do not eat semicolon yet.
@@ -388,15 +388,29 @@ impl<'a> Parser<'a> {
388388
);
389389
}
390390
}
391-
e.emit();
392-
self.recover_stmt();
391+
if let Err(mut e) =
392+
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
393+
{
394+
e.emit();
395+
self.recover_stmt();
396+
}
393397
// Don't complain about type errors in body tail after parse error (#57383).
394398
let sp = expr.span.to(self.prev_token.span);
395-
stmt.kind = StmtKind::Expr(self.mk_expr_err(sp));
399+
*expr = self.mk_expr_err(sp);
396400
}
397401
}
398-
StmtKind::Local(..) => {
399-
self.expect_semi()?;
402+
StmtKind::Local(ref mut local) => {
403+
if let Err(e) = self.expect_semi() {
404+
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
405+
match &mut local.init {
406+
Some(ref mut expr) => {
407+
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
408+
// We found `foo<bar, baz>`, have we fully recovered?
409+
self.expect_semi()?;
410+
}
411+
None => return Err(e),
412+
}
413+
}
400414
eat_semi = false;
401415
}
402416
StmtKind::Empty => eat_semi = false,
+21
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
11
fn main() {
22
(0..13).collect<Vec<i32>>();
33
//~^ ERROR comparison operators cannot be chained
4+
//~| HELP use `::<...>` instead
45
Vec<i32>::new();
56
//~^ ERROR comparison operators cannot be chained
7+
//~| HELP use `::<...>` instead
68
(0..13).collect<Vec<i32>();
79
//~^ ERROR comparison operators cannot be chained
10+
//~| HELP use `::<...>` instead
11+
let x = std::collections::HashMap<i128, i128>::new(); //~ ERROR expected one of
12+
//~^ HELP use `::<...>` instead
13+
let x: () = 42; //~ ERROR mismatched types
14+
let x = {
15+
std::collections::HashMap<i128, i128>::new() //~ ERROR expected one of
16+
//~^ HELP use `::<...>` instead
17+
};
18+
let x: () = 42; //~ ERROR mismatched types
19+
let x = {
20+
std::collections::HashMap<i128, i128>::new(); //~ ERROR expected one of
21+
//~^ HELP use `::<...>` instead
22+
let x: () = 42; //~ ERROR mismatched types
23+
};
24+
{
25+
std::collections::HashMap<i128, i128>::new(1, 2); //~ ERROR expected one of
26+
//~^ HELP use `::<...>` instead
27+
let x: () = 32; //~ ERROR mismatched types
28+
};
829
}

src/test/ui/did_you_mean/issue-40396.stderr

+80-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ LL | (0..13).collect::<Vec<i32>>();
1010
| ^^
1111

1212
error: comparison operators cannot be chained
13-
--> $DIR/issue-40396.rs:4:8
13+
--> $DIR/issue-40396.rs:5:8
1414
|
1515
LL | Vec<i32>::new();
1616
| ^ ^
@@ -21,7 +21,7 @@ LL | Vec::<i32>::new();
2121
| ^^
2222

2323
error: comparison operators cannot be chained
24-
--> $DIR/issue-40396.rs:6:20
24+
--> $DIR/issue-40396.rs:8:20
2525
|
2626
LL | (0..13).collect<Vec<i32>();
2727
| ^ ^
@@ -31,5 +31,82 @@ help: use `::<...>` instead of `<...>` to specify type arguments
3131
LL | (0..13).collect::<Vec<i32>();
3232
| ^^
3333

34-
error: aborting due to 3 previous errors
34+
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
35+
--> $DIR/issue-40396.rs:11:43
36+
|
37+
LL | let x = std::collections::HashMap<i128, i128>::new();
38+
| ^ expected one of 7 possible tokens
39+
|
40+
help: use `::<...>` instead of `<...>` to specify type arguments
41+
|
42+
LL | let x = std::collections::HashMap::<i128, i128>::new();
43+
| ^^
44+
45+
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
46+
--> $DIR/issue-40396.rs:15:39
47+
|
48+
LL | std::collections::HashMap<i128, i128>::new()
49+
| ^ expected one of 8 possible tokens
50+
|
51+
help: use `::<...>` instead of `<...>` to specify type arguments
52+
|
53+
LL | std::collections::HashMap::<i128, i128>::new()
54+
| ^^
55+
56+
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
57+
--> $DIR/issue-40396.rs:20:39
58+
|
59+
LL | std::collections::HashMap<i128, i128>::new();
60+
| ^ expected one of 8 possible tokens
61+
|
62+
help: use `::<...>` instead of `<...>` to specify type arguments
63+
|
64+
LL | std::collections::HashMap::<i128, i128>::new();
65+
| ^^
66+
67+
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
68+
--> $DIR/issue-40396.rs:25:39
69+
|
70+
LL | std::collections::HashMap<i128, i128>::new(1, 2);
71+
| ^ expected one of 8 possible tokens
72+
|
73+
help: use `::<...>` instead of `<...>` to specify type arguments
74+
|
75+
LL | std::collections::HashMap::<i128, i128>::new(1, 2);
76+
| ^^
77+
78+
error[E0308]: mismatched types
79+
--> $DIR/issue-40396.rs:13:17
80+
|
81+
LL | let x: () = 42;
82+
| -- ^^ expected `()`, found integer
83+
| |
84+
| expected due to this
85+
86+
error[E0308]: mismatched types
87+
--> $DIR/issue-40396.rs:18:17
88+
|
89+
LL | let x: () = 42;
90+
| -- ^^ expected `()`, found integer
91+
| |
92+
| expected due to this
93+
94+
error[E0308]: mismatched types
95+
--> $DIR/issue-40396.rs:22:21
96+
|
97+
LL | let x: () = 42;
98+
| -- ^^ expected `()`, found integer
99+
| |
100+
| expected due to this
101+
102+
error[E0308]: mismatched types
103+
--> $DIR/issue-40396.rs:27:21
104+
|
105+
LL | let x: () = 32;
106+
| -- ^^ expected `()`, found integer
107+
| |
108+
| expected due to this
109+
110+
error: aborting due to 11 previous errors
35111

112+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)