Skip to content

Commit 62effcb

Browse files
committed
Detect turbofish with multiple type params missing leading ::
Fix #76072.
1 parent 85fbf49 commit 62effcb

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
@@ -548,6 +548,52 @@ impl<'a> Parser<'a> {
548548
}
549549
}
550550

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

compiler/rustc_parse/src/parser/stmt.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ impl<'a> Parser<'a> {
363363
let mut eat_semi = true;
364364
match stmt.kind {
365365
// Expression without semicolon.
366-
StmtKind::Expr(ref expr)
366+
StmtKind::Expr(ref mut expr)
367367
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
368368
{
369369
// Just check for errors and recover; do not eat semicolon yet.
@@ -387,15 +387,29 @@ impl<'a> Parser<'a> {
387387
);
388388
}
389389
}
390-
e.emit();
391-
self.recover_stmt();
390+
if let Err(mut e) =
391+
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
392+
{
393+
e.emit();
394+
self.recover_stmt();
395+
}
392396
// Don't complain about type errors in body tail after parse error (#57383).
393397
let sp = expr.span.to(self.prev_token.span);
394-
stmt.kind = StmtKind::Expr(self.mk_expr_err(sp));
398+
*expr = self.mk_expr_err(sp);
395399
}
396400
}
397-
StmtKind::Local(..) => {
398-
self.expect_semi()?;
401+
StmtKind::Local(ref mut local) => {
402+
if let Err(e) = self.expect_semi() {
403+
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
404+
match &mut local.init {
405+
Some(ref mut expr) => {
406+
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
407+
// We found `foo<bar, baz>`, have we fully recovered?
408+
self.expect_semi()?;
409+
}
410+
None => return Err(e),
411+
}
412+
}
399413
eat_semi = false;
400414
}
401415
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)