Skip to content

Commit 731cd3f

Browse files
authored
Rollup merge of #94344 - notriddle:notriddle/suggest-parens-more, r=oli-obk
diagnostic: suggest parens when users want logical ops, but get closures Fixes #93536
2 parents cf3bb09 + fd35770 commit 731cd3f

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed

compiler/rustc_parse/src/parser/expr.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,17 @@ impl<'a> Parser<'a> {
372372
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
373373
false
374374
}
375-
(true, Some(AssocOp::LAnd)) => {
375+
(true, Some(AssocOp::LAnd)) |
376+
(true, Some(AssocOp::LOr)) |
377+
(true, Some(AssocOp::BitOr)) => {
376378
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`. Separated from the
377379
// above due to #74233.
378380
// These cases are ambiguous and can't be identified in the parser alone.
381+
//
382+
// Bitwise AND is left out because guessing intent is hard. We can make
383+
// suggestions based on the assumption that double-refs are rarely intentional,
384+
// and closures are distinct enough that they don't get mixed up with their
385+
// return value.
379386
let sp = self.sess.source_map().start_point(self.token.span);
380387
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
381388
false
@@ -1247,7 +1254,14 @@ impl<'a> Parser<'a> {
12471254
} else if self.check(&token::OpenDelim(token::Brace)) {
12481255
self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs)
12491256
} else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
1250-
self.parse_closure_expr(attrs)
1257+
self.parse_closure_expr(attrs).map_err(|mut err| {
1258+
// If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
1259+
// then suggest parens around the lhs.
1260+
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
1261+
self.sess.expr_parentheses_needed(&mut err, *sp);
1262+
}
1263+
err
1264+
})
12511265
} else if self.check(&token::OpenDelim(token::Bracket)) {
12521266
self.parse_array_or_repeat_expr(attrs, token::Bracket)
12531267
} else if self.check_path() {

src/test/ui/parser/expr-as-stmt.fixed

+27
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,31 @@ fn qux() -> u32 {
3737
//~^ ERROR mismatched types
3838
}
3939

40+
fn space_cadet() -> bool {
41+
({ true }) | { true } //~ ERROR E0308
42+
//~^ ERROR expected parameter name
43+
}
44+
45+
fn revenge_from_mars() -> bool {
46+
({ true }) && { true } //~ ERROR E0308
47+
//~^ ERROR mismatched types
48+
}
49+
50+
fn attack_from_mars() -> bool {
51+
({ true }) || { true } //~ ERROR E0308
52+
//~^ ERROR mismatched types
53+
}
54+
55+
// This gets corrected by adding a semicolon, instead of parens.
56+
// It's placed here to help keep track of the way this diagnostic
57+
// needs to interact with type checking to avoid MachineApplicable
58+
// suggestions that actually break stuff.
59+
//
60+
// If you're wondering what happens if that `foo()` is a `true` like
61+
// all the ones above use? Nothing. It makes neither suggestion in
62+
// that case.
63+
fn asteroids() -> impl FnOnce() -> bool {
64+
{ foo(); } || { true } //~ ERROR E0308
65+
}
66+
4067
fn main() {}

src/test/ui/parser/expr-as-stmt.rs

+27
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,31 @@ fn qux() -> u32 {
3737
//~^ ERROR mismatched types
3838
}
3939

40+
fn space_cadet() -> bool {
41+
{ true } | { true } //~ ERROR E0308
42+
//~^ ERROR expected parameter name
43+
}
44+
45+
fn revenge_from_mars() -> bool {
46+
{ true } && { true } //~ ERROR E0308
47+
//~^ ERROR mismatched types
48+
}
49+
50+
fn attack_from_mars() -> bool {
51+
{ true } || { true } //~ ERROR E0308
52+
//~^ ERROR mismatched types
53+
}
54+
55+
// This gets corrected by adding a semicolon, instead of parens.
56+
// It's placed here to help keep track of the way this diagnostic
57+
// needs to interact with type checking to avoid MachineApplicable
58+
// suggestions that actually break stuff.
59+
//
60+
// If you're wondering what happens if that `foo()` is a `true` like
61+
// all the ones above use? Nothing. It makes neither suggestion in
62+
// that case.
63+
fn asteroids() -> impl FnOnce() -> bool {
64+
{ foo() } || { true } //~ ERROR E0308
65+
}
66+
4067
fn main() {}

src/test/ui/parser/expr-as-stmt.stderr

+81-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ LL | _ => 1,
4444
LL ~ }) > 0
4545
|
4646

47+
error: expected parameter name, found `{`
48+
--> $DIR/expr-as-stmt.rs:41:16
49+
|
50+
LL | { true } | { true }
51+
| ^ expected parameter name
52+
|
53+
help: parentheses are required to parse this as an expression
54+
|
55+
LL | ({ true }) | { true }
56+
| + +
57+
58+
error[E0308]: mismatched types
59+
--> $DIR/expr-as-stmt.rs:64:7
60+
|
61+
LL | { foo() } || { true }
62+
| ^^^^^- help: consider using a semicolon here: `;`
63+
| |
64+
| expected `()`, found `i32`
65+
4766
error[E0308]: mismatched types
4867
--> $DIR/expr-as-stmt.rs:8:6
4968
|
@@ -121,7 +140,68 @@ help: parentheses are required to parse this as an expression
121140
LL | ({2}) - 2
122141
| + +
123142

124-
error: aborting due to 11 previous errors
143+
error[E0308]: mismatched types
144+
--> $DIR/expr-as-stmt.rs:41:7
145+
|
146+
LL | { true } | { true }
147+
| ^^^^ expected `()`, found `bool`
148+
|
149+
help: you might have meant to return this value
150+
|
151+
LL | { return true; } | { true }
152+
| ++++++ +
153+
154+
error[E0308]: mismatched types
155+
--> $DIR/expr-as-stmt.rs:46:7
156+
|
157+
LL | { true } && { true }
158+
| ^^^^ expected `()`, found `bool`
159+
|
160+
help: you might have meant to return this value
161+
|
162+
LL | { return true; } && { true }
163+
| ++++++ +
164+
165+
error[E0308]: mismatched types
166+
--> $DIR/expr-as-stmt.rs:46:14
167+
|
168+
LL | fn revenge_from_mars() -> bool {
169+
| ---- expected `bool` because of return type
170+
LL | { true } && { true }
171+
| ^^^^^^^^^^^ expected `bool`, found `&&bool`
172+
|
173+
help: parentheses are required to parse this as an expression
174+
|
175+
LL | ({ true }) && { true }
176+
| + +
177+
178+
error[E0308]: mismatched types
179+
--> $DIR/expr-as-stmt.rs:51:7
180+
|
181+
LL | { true } || { true }
182+
| ^^^^ expected `()`, found `bool`
183+
|
184+
help: you might have meant to return this value
185+
|
186+
LL | { return true; } || { true }
187+
| ++++++ +
188+
189+
error[E0308]: mismatched types
190+
--> $DIR/expr-as-stmt.rs:51:14
191+
|
192+
LL | fn attack_from_mars() -> bool {
193+
| ---- expected `bool` because of return type
194+
LL | { true } || { true }
195+
| ^^^^^^^^^^^ expected `bool`, found closure
196+
|
197+
= note: expected type `bool`
198+
found closure `[closure@$DIR/expr-as-stmt.rs:51:14: 51:25]`
199+
help: parentheses are required to parse this as an expression
200+
|
201+
LL | ({ true }) || { true }
202+
| + +
203+
204+
error: aborting due to 18 previous errors
125205

126206
Some errors have detailed explanations: E0308, E0600, E0614.
127207
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)