Rollup merge of #103405 - chenyukang:yukang/fix-103381-and-if, r=compiler-errors

Detect incorrect chaining of if and if let conditions and recover

Fixes #103381
This commit is contained in:
Matthias Krüger 2022-11-18 14:13:36 +01:00 committed by GitHub
commit 3efbf30220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 192 additions and 2 deletions

View File

@ -54,6 +54,9 @@ parser_invalid_logical_operator = `{$incorrect}` is not a logical operator
parser_tilde_is_not_unary_operator = `~` cannot be used as a unary operator
.suggestion = use `!` to perform bitwise not
parser_unexpected_if_with_if = unexpected `if` in the condition expression
.suggestion = remove the `if`
parser_unexpected_token_after_not = unexpected {$negated_desc} after identifier
parser_unexpected_token_after_not_bitwise = use `!` to perform bitwise not
parser_unexpected_token_after_not_logical = use `!` to perform logical negation

View File

@ -1219,3 +1219,11 @@ pub(crate) struct FnPtrWithGenericsSugg {
pub arity: usize,
pub for_param_list_exists: bool,
}
#[derive(Diagnostic)]
#[diag(parser_unexpected_if_with_if)]
pub(crate) struct UnexpectedIfWithIf(
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = " ", style = "verbose")]
pub Span,
);

View File

@ -21,8 +21,8 @@
NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
};
use crate::maybe_recover_from_interpolated_ty_qpath;
use core::mem;
@ -2231,6 +2231,7 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
if let Some(block) = recover_block_from_condition(self) {
block
} else {
self.error_on_extra_if(&cond)?;
// Parse block, which will always fail, but we can add a nice note to the error
self.parse_block().map_err(|mut err| {
err.span_note(
@ -2367,6 +2368,16 @@ fn error_on_if_block_attrs(
});
}
fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> {
if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind &&
let BinOpKind::And = binop &&
let ExprKind::If(cond, ..) = &right.kind {
Err(self.sess.create_err(UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
} else {
Ok(())
}
}
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
// Record whether we are about to parse `for (`.

View File

@ -0,0 +1,59 @@
// run-rustfix
#![feature(let_chains)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(irrefutable_let_patterns)]
fn err_some(b: bool, x: Option<u32>) {
if b && let Some(x) = x {}
//~^ ERROR unexpected `if` in the condition expression
}
fn err_none(b: bool, x: Option<u32>) {
if b && let None = x {}
//~^ ERROR unexpected `if` in the condition expression
}
fn err_bool_1() {
if true && true { true } else { false };
//~^ ERROR unexpected `if` in the condition expression
}
fn err_bool_2() {
if true && false { true } else { false };
//~^ ERROR unexpected `if` in the condition expression
}
fn should_ok_1() {
if true && if let x = 1 { true } else { true } {}
}
fn should_ok_2() {
if true && if let 1 = 1 { true } else { true } {}
}
fn should_ok_3() {
if true && if true { true } else { false } {}
}
fn shoule_match_ok() {
#[cfg(feature = "full")]
{
let a = 1;
let b = 2;
if match a {
1 if b == 1 => true,
_ => false,
} && if a > 1 { true } else { false }
{
true
}
}
}
fn should_ok_in_nested() {
if true && if true { true } else { false } { true } else { false };
}
fn main() {}

View File

@ -0,0 +1,59 @@
// run-rustfix
#![feature(let_chains)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(irrefutable_let_patterns)]
fn err_some(b: bool, x: Option<u32>) {
if b && if let Some(x) = x {}
//~^ ERROR unexpected `if` in the condition expression
}
fn err_none(b: bool, x: Option<u32>) {
if b && if let None = x {}
//~^ ERROR unexpected `if` in the condition expression
}
fn err_bool_1() {
if true && if true { true } else { false };
//~^ ERROR unexpected `if` in the condition expression
}
fn err_bool_2() {
if true && if false { true } else { false };
//~^ ERROR unexpected `if` in the condition expression
}
fn should_ok_1() {
if true && if let x = 1 { true } else { true } {}
}
fn should_ok_2() {
if true && if let 1 = 1 { true } else { true } {}
}
fn should_ok_3() {
if true && if true { true } else { false } {}
}
fn shoule_match_ok() {
#[cfg(feature = "full")]
{
let a = 1;
let b = 2;
if match a {
1 if b == 1 => true,
_ => false,
} && if a > 1 { true } else { false }
{
true
}
}
}
fn should_ok_in_nested() {
if true && if true { true } else { false } { true } else { false };
}
fn main() {}

View File

@ -0,0 +1,50 @@
error: unexpected `if` in the condition expression
--> $DIR/issue-103381.rs:9:12
|
LL | if b && if let Some(x) = x {}
| ^^^^
|
help: remove the `if`
|
LL - if b && if let Some(x) = x {}
LL + if b && let Some(x) = x {}
|
error: unexpected `if` in the condition expression
--> $DIR/issue-103381.rs:14:12
|
LL | if b && if let None = x {}
| ^^^^
|
help: remove the `if`
|
LL - if b && if let None = x {}
LL + if b && let None = x {}
|
error: unexpected `if` in the condition expression
--> $DIR/issue-103381.rs:19:15
|
LL | if true && if true { true } else { false };
| ^^^^
|
help: remove the `if`
|
LL - if true && if true { true } else { false };
LL + if true && true { true } else { false };
|
error: unexpected `if` in the condition expression
--> $DIR/issue-103381.rs:24:15
|
LL | if true && if false { true } else { false };
| ^^^^
|
help: remove the `if`
|
LL - if true && if false { true } else { false };
LL + if true && false { true } else { false };
|
error: aborting due to 4 previous errors