Skip to content

Commit 519ad87

Browse files
committed
Auto merge of rust-lang#143213 - dianne:lower-cond-tweaks, r=cjgillot
de-duplicate condition scoping logic between AST→HIR lowering and `ScopeTree` construction There was some overlap between `rustc_ast_lowering::LoweringContext::lower_cond` and `rustc_hir_analysis::check::region::resolve_expr`, so I've removed the former and migrated its logic to the latter, with some simplifications. Consequences: - For `while` and `if` expressions' `let`-chains, this changes the `HirId`s for the `&&`s to properly correspond to their AST nodes. This is how guards were handled already. - This makes match guards share previously-duplicated logic with `if`/`while` expressions. This will also be used by guard pattern[^1] guards. - Aside from legacy syntax extensions (e.g. some builtin macros) that directly feed AST to the compiler, it's currently impossible to put attributes directly on `&&` operators in `let` chains[^2]. Nonetheless, attributes on `&&` operators in `let` chains in `if`/`while` expression conditions are no longer silently ignored and will be lowered. - This no longer wraps conditions in `DropTemps`, so the HIR and THIR will be slightly smaller. - `DesugaringKind::CondTemporary` is now gone. It's no longer applied to any spans, and all uses of it were dead since they were made to account for `if` and `while` being desugared to `match` on a boolean scrutinee. - Should be a marginal perf improvement beyond that due to leveraging [`ScopeTree` construction](https://github.com/rust-lang/rust/blob/5e749eb66f93ee998145399fbdde337e57cd72ef/compiler/rustc_hir_analysis/src/check/region.rs#L312-L355)'s clever handling of `&&` and `||`: - This removes some unnecessary terminating scopes that were placed around top-level `&&` and `||` operators in conditions. When lowered to MIR, logical operator chains don't create intermediate boolean temporaries, so there's no temporary to drop. The linked snippet handles wrapping the operands in terminating scopes as necessary, in case they create temporaries. - The linked snippet takes care of letting `let` temporaries live and terminating other operands, so we don't need separate traversals of `&&` chains for that. [^1]: rust-lang#129967 [^2]: Case-by-case, here's my justification: `#[attr] e1 && e2` applies the attribute to `e1`. In `#[attr] (e1 && e2)` , the attribute is on the parentheses in the AST, plus it'd fail to parse if `e1` or `e2` contains a `let`. In `#[attr] expands_to_let_chain!()`, the attribute would already be ignored (rust-lang#63221) and it'd fail to parse anyway; even if the expansion site is a condition, the expansion wouldn't be parsed with `Restrictions::ALLOW_LET`. If it *was* allowed, the notion of a "reparse context" from rust-lang#61733 (comment) would be necessary in order to make `let`-chains left-associative; multiple places in the compiler assume they are.
2 parents a1844ec + 9d7637b commit 519ad87

File tree

7 files changed

+29
-48
lines changed

7 files changed

+29
-48
lines changed

clippy_lints/src/booleans.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
3+
use clippy_utils::higher::has_let_expr;
34
use clippy_utils::msrvs::{self, Msrv};
45
use clippy_utils::source::SpanRangeExt;
56
use clippy_utils::sugg::Sugg;
@@ -646,7 +647,9 @@ impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> {
646647
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
647648
if !e.span.from_expansion() {
648649
match &e.kind {
649-
ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
650+
ExprKind::Binary(binop, _, _)
651+
if binop.node == BinOpKind::Or || binop.node == BinOpKind::And && !has_let_expr(e) =>
652+
{
650653
self.bool_expr(e);
651654
},
652655
ExprKind::Unary(UnOp::Not, inner) => {

clippy_lints/src/if_not_else.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
5151
impl LateLintPass<'_> for IfNotElse {
5252
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
5353
if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind
54-
&& let ExprKind::DropTemps(cond) = cond.kind
5554
&& let ExprKind::Block(..) = els.kind
5655
{
5756
let (msg, help) = match cond.kind {

clippy_lints/src/implicit_saturating_add.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
4141
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
4242
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
4343
if let ExprKind::If(cond, then, None) = expr.kind
44-
&& let ExprKind::DropTemps(expr1) = cond.kind
45-
&& let Some((c, op_node, l)) = get_const(cx, expr1)
44+
&& let Some((c, op_node, l)) = get_const(cx, cond)
4645
&& let BinOpKind::Ne | BinOpKind::Lt = op_node
4746
&& let ExprKind::Block(block, None) = then.kind
4847
&& let Block {
@@ -66,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
6665
&& Some(c) == get_int_max(ty)
6766
&& let ctxt = expr.span.ctxt()
6867
&& ex.span.ctxt() == ctxt
69-
&& expr1.span.ctxt() == ctxt
68+
&& cond.span.ctxt() == ctxt
7069
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
7170
&& AssignOpKind::AddAssign == op1.node
7271
&& let ExprKind::Lit(lit) = value.kind

clippy_lints/src/let_if_seq.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
6262
if let hir::StmtKind::Let(local) = stmt.kind
6363
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
6464
&& let hir::StmtKind::Expr(if_) = next.kind
65-
&& let hir::ExprKind::If(
66-
hir::Expr {
67-
kind: hir::ExprKind::DropTemps(cond),
68-
..
69-
},
70-
then,
71-
else_,
72-
) = if_.kind
73-
&& !is_local_used(cx, *cond, canonical_id)
65+
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
66+
&& !is_local_used(cx, cond, canonical_id)
7467
&& let hir::ExprKind::Block(then, _) = then.kind
7568
&& let Some(value) = check_assign(cx, canonical_id, then)
7669
&& !is_local_used(cx, value, canonical_id)

clippy_utils/src/higher.rs

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl<'tcx> ForLoop<'tcx> {
5454
}
5555
}
5656

57-
/// An `if` expression without `DropTemps`
57+
/// An `if` expression without `let`
5858
pub struct If<'hir> {
5959
/// `if` condition
6060
pub cond: &'hir Expr<'hir>,
@@ -66,16 +66,10 @@ pub struct If<'hir> {
6666

6767
impl<'hir> If<'hir> {
6868
#[inline]
69-
/// Parses an `if` expression
69+
/// Parses an `if` expression without `let`
7070
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
71-
if let ExprKind::If(
72-
Expr {
73-
kind: ExprKind::DropTemps(cond),
74-
..
75-
},
76-
then,
77-
r#else,
78-
) = expr.kind
71+
if let ExprKind::If(cond, then, r#else) = expr.kind
72+
&& !has_let_expr(cond)
7973
{
8074
Some(Self { cond, then, r#else })
8175
} else {
@@ -198,18 +192,10 @@ impl<'hir> IfOrIfLet<'hir> {
198192
/// Parses an `if` or `if let` expression
199193
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
200194
if let ExprKind::If(cond, then, r#else) = expr.kind {
201-
if let ExprKind::DropTemps(new_cond) = cond.kind {
202-
return Some(Self {
203-
cond: new_cond,
204-
then,
205-
r#else,
206-
});
207-
}
208-
if let ExprKind::Let(..) = cond.kind {
209-
return Some(Self { cond, then, r#else });
210-
}
195+
Some(Self { cond, then, r#else })
196+
} else {
197+
None
211198
}
212-
None
213199
}
214200
}
215201

@@ -343,15 +329,7 @@ impl<'hir> While<'hir> {
343329
Block {
344330
expr:
345331
Some(Expr {
346-
kind:
347-
ExprKind::If(
348-
Expr {
349-
kind: ExprKind::DropTemps(condition),
350-
..
351-
},
352-
body,
353-
_,
354-
),
332+
kind: ExprKind::If(condition, body, _),
355333
..
356334
}),
357335
..
@@ -360,6 +338,7 @@ impl<'hir> While<'hir> {
360338
LoopSource::While,
361339
span,
362340
) = expr.kind
341+
&& !has_let_expr(condition)
363342
{
364343
return Some(Self { condition, body, span });
365344
}
@@ -493,3 +472,13 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
493472
}
494473
None
495474
}
475+
476+
/// Checks that a condition doesn't have a `let` expression, to keep `If` and `While` from accepting
477+
/// `if let` and `while let`.
478+
pub const fn has_let_expr<'tcx>(cond: &'tcx Expr<'tcx>) -> bool {
479+
match &cond.kind {
480+
ExprKind::Let(_) => true,
481+
ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
482+
_ => false,
483+
}
484+
}

tests/ui/author/if.stdout

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
if let StmtKind::Let(local) = stmt.kind
22
&& let Some(init) = local.init
33
&& let ExprKind::If(cond, then, Some(else_expr)) = init.kind
4-
&& let ExprKind::DropTemps(expr) = cond.kind
5-
&& let ExprKind::Lit(ref lit) = expr.kind
4+
&& let ExprKind::Lit(ref lit) = cond.kind
65
&& let LitKind::Bool(true) = lit.node
76
&& let ExprKind::Block(block, None) = then.kind
87
&& block.stmts.len() == 1

tests/ui/author/struct.stdout

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind
22
&& fields.len() == 1
33
&& fields[0].ident.as_str() == "field"
44
&& let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
5-
&& let ExprKind::DropTemps(expr1) = cond.kind
6-
&& let ExprKind::Lit(ref lit) = expr1.kind
5+
&& let ExprKind::Lit(ref lit) = cond.kind
76
&& let LitKind::Bool(true) = lit.node
87
&& let ExprKind::Block(block, None) = then.kind
98
&& block.stmts.is_empty()

0 commit comments

Comments
 (0)