Skip to content

Commit 80a1536

Browse files
committed
recover more unbraced const args
1 parent 705a96d commit 80a1536

File tree

4 files changed

+188
-22
lines changed

4 files changed

+188
-22
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+22
Original file line numberDiff line numberDiff line change
@@ -2353,6 +2353,28 @@ impl<'a> Parser<'a> {
23532353
Err(err)
23542354
}
23552355

2356+
/// Try to recover from an unbraced const argument whose first token [could begin a type][ty].
2357+
///
2358+
/// [ty]: token::Token::can_begin_type
2359+
pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
2360+
&mut self,
2361+
mut snapshot: SnapshotParser<'a>,
2362+
) -> Option<P<ast::Expr>> {
2363+
match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) {
2364+
// Since we don't know the exact reason why we failed to parse the type or the
2365+
// expression, employ a simple heuristic to weed out some pathological cases.
2366+
Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
2367+
self.restore_snapshot(snapshot);
2368+
Some(expr)
2369+
}
2370+
Ok(_) => None,
2371+
Err(err) => {
2372+
err.cancel();
2373+
None
2374+
}
2375+
}
2376+
}
2377+
23562378
/// Creates a dummy const argument, and reports that the expression must be enclosed in braces
23572379
pub fn dummy_const_arg_needs_braces(
23582380
&self,

compiler/rustc_parse/src/parser/path.rs

+34-14
Original file line numberDiff line numberDiff line change
@@ -675,22 +675,42 @@ impl<'a> Parser<'a> {
675675
GenericArg::Const(self.parse_const_arg()?)
676676
} else if self.check_type() {
677677
// Parse type argument.
678-
let is_const_fn =
679-
self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis));
680-
let mut snapshot = self.create_snapshot_for_diagnostic();
678+
679+
// Proactively create a parser snapshot enabling us to rewind and try to reparse the
680+
// input as a const expression in case we fail to parse a type. If we successfully
681+
// do so, we will report an error that it needs to be wrapped in braces.
682+
let mut snapshot = None;
683+
if self.may_recover() && self.token.can_begin_expr() {
684+
snapshot = Some(self.create_snapshot_for_diagnostic());
685+
}
686+
681687
match self.parse_ty() {
682-
Ok(ty) => GenericArg::Type(ty),
688+
Ok(ty) => {
689+
// Since the type parser recovers from some malformed slice and array types and
690+
// successfully returns a type, we need to look for `TyKind::Err`s in the
691+
// type to determine if error recovery has occurred and if the input is not a
692+
// syntactically valid type after all.
693+
if let ast::TyKind::Slice(inner_ty) | ast::TyKind::Array(inner_ty, _) = &ty.kind
694+
&& let ast::TyKind::Err = inner_ty.kind
695+
&& let Some(snapshot) = snapshot
696+
&& let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot)
697+
{
698+
return Ok(Some(self.dummy_const_arg_needs_braces(
699+
self.struct_span_err(expr.span, "invalid const generic expression"),
700+
expr.span,
701+
)));
702+
}
703+
704+
GenericArg::Type(ty)
705+
}
683706
Err(err) => {
684-
if is_const_fn {
685-
match (*snapshot).parse_expr_res(Restrictions::CONST_EXPR, None) {
686-
Ok(expr) => {
687-
self.restore_snapshot(snapshot);
688-
return Ok(Some(self.dummy_const_arg_needs_braces(err, expr.span)));
689-
}
690-
Err(err) => {
691-
err.cancel();
692-
}
693-
}
707+
if let Some(snapshot) = snapshot
708+
&& let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot)
709+
{
710+
return Ok(Some(self.dummy_const_arg_needs_braces(
711+
err,
712+
expr.span,
713+
)));
694714
}
695715
// Try to recover from possible `const` arg without braces.
696716
return self.recover_const_arg(start, err).map(Some);

tests/ui/const-generics/bad-const-generic-exprs.rs

+29-5
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,34 @@ fn main() {
1313
let _: Wow<A.0>;
1414
//~^ ERROR expected one of
1515
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
16-
17-
// FIXME(compiler-errors): This one is still unsatisfying,
18-
// and probably a case I could see someone typing by accident..
16+
let _: Wow<[]>;
17+
//~^ ERROR expected type
18+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
1919
let _: Wow<[12]>;
20-
//~^ ERROR expected type, found
21-
//~| ERROR type provided when a constant was expected
20+
//~^ ERROR expected type
21+
//~| ERROR invalid const generic expression
22+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
23+
let _: Wow<[0, 1, 3]>;
24+
//~^ ERROR expected type
25+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
26+
let _: Wow<[0xff; 8]>;
27+
//~^ ERROR expected type
28+
//~| ERROR invalid const generic expression
29+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
30+
let _: Wow<[1, 2]>; // Regression test for issue #81698.
31+
//~^ ERROR expected type
32+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
33+
let _: Wow<&0>;
34+
//~^ ERROR expected type
35+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
36+
let _: Wow<("", 0)>;
37+
//~^ ERROR expected type
38+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
39+
let _: Wow<(1 + 2) * 3>;
40+
//~^ ERROR expected type
41+
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
42+
// FIXME(fmease): This one is pretty bad.
43+
let _: Wow<!0>;
44+
//~^ ERROR expected one of
45+
//~| HELP you might have meant to end the type parameters here
2246
}

tests/ui/const-generics/bad-const-generic-exprs.stderr

+103-3
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,118 @@ help: expressions must be enclosed in braces to be used as const generic argumen
4242
LL | let _: Wow<{ A.0 }>;
4343
| + +
4444

45+
error: expected type, found `]`
46+
--> $DIR/bad-const-generic-exprs.rs:16:17
47+
|
48+
LL | let _: Wow<[]>;
49+
| ^ expected type
50+
|
51+
help: expressions must be enclosed in braces to be used as const generic arguments
52+
|
53+
LL | let _: Wow<{ [] }>;
54+
| + +
55+
4556
error: expected type, found `12`
4657
--> $DIR/bad-const-generic-exprs.rs:19:17
4758
|
4859
LL | let _: Wow<[12]>;
4960
| ^^ expected type
5061

51-
error[E0747]: type provided when a constant was expected
62+
error: invalid const generic expression
5263
--> $DIR/bad-const-generic-exprs.rs:19:16
5364
|
5465
LL | let _: Wow<[12]>;
5566
| ^^^^
67+
|
68+
help: expressions must be enclosed in braces to be used as const generic arguments
69+
|
70+
LL | let _: Wow<{ [12] }>;
71+
| + +
72+
73+
error: expected type, found `0`
74+
--> $DIR/bad-const-generic-exprs.rs:23:17
75+
|
76+
LL | let _: Wow<[0, 1, 3]>;
77+
| ^ expected type
78+
|
79+
help: expressions must be enclosed in braces to be used as const generic arguments
80+
|
81+
LL | let _: Wow<{ [0, 1, 3] }>;
82+
| + +
83+
84+
error: expected type, found `0xff`
85+
--> $DIR/bad-const-generic-exprs.rs:26:17
86+
|
87+
LL | let _: Wow<[0xff; 8]>;
88+
| ^^^^ expected type
89+
90+
error: invalid const generic expression
91+
--> $DIR/bad-const-generic-exprs.rs:26:16
92+
|
93+
LL | let _: Wow<[0xff; 8]>;
94+
| ^^^^^^^^^
95+
|
96+
help: expressions must be enclosed in braces to be used as const generic arguments
97+
|
98+
LL | let _: Wow<{ [0xff; 8] }>;
99+
| + +
100+
101+
error: expected type, found `1`
102+
--> $DIR/bad-const-generic-exprs.rs:30:17
103+
|
104+
LL | let _: Wow<[1, 2]>; // Regression test for issue #81698.
105+
| ^ expected type
106+
|
107+
help: expressions must be enclosed in braces to be used as const generic arguments
108+
|
109+
LL | let _: Wow<{ [1, 2] }>; // Regression test for issue #81698.
110+
| + +
111+
112+
error: expected type, found `0`
113+
--> $DIR/bad-const-generic-exprs.rs:33:17
114+
|
115+
LL | let _: Wow<&0>;
116+
| ^ expected type
117+
|
118+
help: expressions must be enclosed in braces to be used as const generic arguments
119+
|
120+
LL | let _: Wow<{ &0 }>;
121+
| + +
122+
123+
error: expected type, found `""`
124+
--> $DIR/bad-const-generic-exprs.rs:36:17
125+
|
126+
LL | let _: Wow<("", 0)>;
127+
| ^^ expected type
128+
|
129+
help: expressions must be enclosed in braces to be used as const generic arguments
130+
|
131+
LL | let _: Wow<{ ("", 0) }>;
132+
| + +
133+
134+
error: expected type, found `1`
135+
--> $DIR/bad-const-generic-exprs.rs:39:17
136+
|
137+
LL | let _: Wow<(1 + 2) * 3>;
138+
| ^ expected type
139+
|
140+
help: expressions must be enclosed in braces to be used as const generic arguments
141+
|
142+
LL | let _: Wow<{ (1 + 2) * 3 }>;
143+
| + +
144+
145+
error: expected one of `,` or `>`, found `0`
146+
--> $DIR/bad-const-generic-exprs.rs:43:17
147+
|
148+
LL | let _: Wow<!0>;
149+
| - ^ expected one of `,` or `>`
150+
| |
151+
| while parsing the type for `_`
152+
|
153+
help: you might have meant to end the type parameters here
154+
|
155+
LL | let _: Wow<!>0>;
156+
| +
56157

57-
error: aborting due to 6 previous errors
158+
error: aborting due to 15 previous errors
58159

59-
For more information about this error, try `rustc --explain E0747`.

0 commit comments

Comments
 (0)