Skip to content

Commit 74c9a6c

Browse files
committed
fix #103381, Detect incorrect chaining of if and if let conditions
1 parent 101e182 commit 74c9a6c

File tree

6 files changed

+192
-2
lines changed

6 files changed

+192
-2
lines changed

compiler/rustc_error_messages/locales/en-US/parser.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ parser_invalid_logical_operator = `{$incorrect}` is not a logical operator
5454
parser_tilde_is_not_unary_operator = `~` cannot be used as a unary operator
5555
.suggestion = use `!` to perform bitwise not
5656
57+
parser_unexpected_if_with_if = unexpected `if` in the condition expression
58+
.suggestion = remove the `if`
59+
5760
parser_unexpected_token_after_not = unexpected {$negated_desc} after identifier
5861
parser_unexpected_token_after_not_bitwise = use `!` to perform bitwise not
5962
parser_unexpected_token_after_not_logical = use `!` to perform logical negation

compiler/rustc_parse/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1301,3 +1301,11 @@ pub(crate) struct FnPtrWithGenericsSugg {
13011301
pub arity: usize,
13021302
pub for_param_list_exists: bool,
13031303
}
1304+
1305+
#[derive(Diagnostic)]
1306+
#[diag(parser_unexpected_if_with_if)]
1307+
pub(crate) struct UnexpectedIfWithIf(
1308+
#[primary_span]
1309+
#[suggestion(applicability = "machine-applicable", code = " ", style = "verbose")]
1310+
pub Span,
1311+
);

compiler/rustc_parse/src/parser/expr.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::errors::{
2424
NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
2525
OctalFloatLiteralNotSupported, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
2626
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
27-
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
28-
UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
27+
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
28+
UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
2929
};
3030
use crate::maybe_recover_from_interpolated_ty_qpath;
3131

@@ -2279,6 +2279,7 @@ impl<'a> Parser<'a> {
22792279
if let Some(block) = recover_block_from_condition(self) {
22802280
block
22812281
} else {
2282+
self.error_on_extra_if(&cond)?;
22822283
// Parse block, which will always fail, but we can add a nice note to the error
22832284
self.parse_block().map_err(|mut err| {
22842285
err.span_note(
@@ -2415,6 +2416,16 @@ impl<'a> Parser<'a> {
24152416
});
24162417
}
24172418

2419+
fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> {
2420+
if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind &&
2421+
let BinOpKind::And = binop &&
2422+
let ExprKind::If(cond, ..) = &right.kind {
2423+
Err(self.sess.create_err(UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
2424+
} else {
2425+
Ok(())
2426+
}
2427+
}
2428+
24182429
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
24192430
fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
24202431
// Record whether we are about to parse `for (`.

src/test/ui/parser/issue-103381.fixed

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// run-rustfix
2+
3+
#![feature(let_chains)]
4+
#![allow(unused_variables)]
5+
#![allow(dead_code)]
6+
#![allow(irrefutable_let_patterns)]
7+
8+
fn err_some(b: bool, x: Option<u32>) {
9+
if b && let Some(x) = x {}
10+
//~^ ERROR unexpected `if` in the condition expression
11+
}
12+
13+
fn err_none(b: bool, x: Option<u32>) {
14+
if b && let None = x {}
15+
//~^ ERROR unexpected `if` in the condition expression
16+
}
17+
18+
fn err_bool_1() {
19+
if true && true { true } else { false };
20+
//~^ ERROR unexpected `if` in the condition expression
21+
}
22+
23+
fn err_bool_2() {
24+
if true && false { true } else { false };
25+
//~^ ERROR unexpected `if` in the condition expression
26+
}
27+
28+
fn should_ok_1() {
29+
if true && if let x = 1 { true } else { true } {}
30+
}
31+
32+
fn should_ok_2() {
33+
if true && if let 1 = 1 { true } else { true } {}
34+
}
35+
36+
fn should_ok_3() {
37+
if true && if true { true } else { false } {}
38+
}
39+
40+
fn shoule_match_ok() {
41+
#[cfg(feature = "full")]
42+
{
43+
let a = 1;
44+
let b = 2;
45+
if match a {
46+
1 if b == 1 => true,
47+
_ => false,
48+
} && if a > 1 { true } else { false }
49+
{
50+
true
51+
}
52+
}
53+
}
54+
55+
fn should_ok_in_nested() {
56+
if true && if true { true } else { false } { true } else { false };
57+
}
58+
59+
fn main() {}

src/test/ui/parser/issue-103381.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// run-rustfix
2+
3+
#![feature(let_chains)]
4+
#![allow(unused_variables)]
5+
#![allow(dead_code)]
6+
#![allow(irrefutable_let_patterns)]
7+
8+
fn err_some(b: bool, x: Option<u32>) {
9+
if b && if let Some(x) = x {}
10+
//~^ ERROR unexpected `if` in the condition expression
11+
}
12+
13+
fn err_none(b: bool, x: Option<u32>) {
14+
if b && if let None = x {}
15+
//~^ ERROR unexpected `if` in the condition expression
16+
}
17+
18+
fn err_bool_1() {
19+
if true && if true { true } else { false };
20+
//~^ ERROR unexpected `if` in the condition expression
21+
}
22+
23+
fn err_bool_2() {
24+
if true && if false { true } else { false };
25+
//~^ ERROR unexpected `if` in the condition expression
26+
}
27+
28+
fn should_ok_1() {
29+
if true && if let x = 1 { true } else { true } {}
30+
}
31+
32+
fn should_ok_2() {
33+
if true && if let 1 = 1 { true } else { true } {}
34+
}
35+
36+
fn should_ok_3() {
37+
if true && if true { true } else { false } {}
38+
}
39+
40+
fn shoule_match_ok() {
41+
#[cfg(feature = "full")]
42+
{
43+
let a = 1;
44+
let b = 2;
45+
if match a {
46+
1 if b == 1 => true,
47+
_ => false,
48+
} && if a > 1 { true } else { false }
49+
{
50+
true
51+
}
52+
}
53+
}
54+
55+
fn should_ok_in_nested() {
56+
if true && if true { true } else { false } { true } else { false };
57+
}
58+
59+
fn main() {}
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error: unexpected `if` in the condition expression
2+
--> $DIR/issue-103381.rs:9:12
3+
|
4+
LL | if b && if let Some(x) = x {}
5+
| ^^^^
6+
|
7+
help: remove the `if`
8+
|
9+
LL - if b && if let Some(x) = x {}
10+
LL + if b && let Some(x) = x {}
11+
|
12+
13+
error: unexpected `if` in the condition expression
14+
--> $DIR/issue-103381.rs:14:12
15+
|
16+
LL | if b && if let None = x {}
17+
| ^^^^
18+
|
19+
help: remove the `if`
20+
|
21+
LL - if b && if let None = x {}
22+
LL + if b && let None = x {}
23+
|
24+
25+
error: unexpected `if` in the condition expression
26+
--> $DIR/issue-103381.rs:19:15
27+
|
28+
LL | if true && if true { true } else { false };
29+
| ^^^^
30+
|
31+
help: remove the `if`
32+
|
33+
LL - if true && if true { true } else { false };
34+
LL + if true && true { true } else { false };
35+
|
36+
37+
error: unexpected `if` in the condition expression
38+
--> $DIR/issue-103381.rs:24:15
39+
|
40+
LL | if true && if false { true } else { false };
41+
| ^^^^
42+
|
43+
help: remove the `if`
44+
|
45+
LL - if true && if false { true } else { false };
46+
LL + if true && false { true } else { false };
47+
|
48+
49+
error: aborting due to 4 previous errors
50+

0 commit comments

Comments
 (0)