Skip to content

Commit a81ac09

Browse files
committed
Allow more complex expressions in let_unit_value
1 parent bb03a78 commit a81ac09

File tree

6 files changed

+124
-3
lines changed

6 files changed

+124
-3
lines changed

clippy_lints/src/unit_types/let_unit_value.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet_with_macro_callsite;
3+
use clippy_utils::visitors::for_each_value_source;
34
use core::ops::ControlFlow;
45
use rustc_errors::Applicability;
56
use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
@@ -11,11 +12,18 @@ use super::LET_UNIT_VALUE;
1112

1213
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
1314
if let StmtKind::Local(local) = stmt.kind
15+
&& let Some(init) = local.init
1416
&& !local.pat.span.from_expansion()
1517
&& !in_external_macro(cx.sess(), stmt.span)
1618
&& cx.typeck_results().pat_ty(local.pat).is_unit()
1719
{
18-
if local.init.map_or(false, |e| needs_inferred_result_ty(cx, e)) {
20+
let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
21+
ControlFlow::Continue(())
22+
} else {
23+
ControlFlow::Break(())
24+
}).is_continue();
25+
26+
if needs_inferred {
1927
if !matches!(local.pat.kind, PatKind::Wild) {
2028
span_lint_and_then(
2129
cx,

clippy_utils/src/visitors.rs

+34
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::path_to_local_id;
2+
use core::ops::ControlFlow;
23
use rustc_hir as hir;
34
use rustc_hir::def::{DefKind, Res};
45
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
@@ -370,3 +371,36 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
370371
v.visit_expr(e);
371372
v.is_unsafe
372373
}
374+
375+
/// Runs the given function for each sub expression producing the final value consumed the parent of
376+
/// the give expression.
377+
///
378+
/// e.g. for the following expression
379+
/// ```rust,ignore
380+
/// if foo {
381+
/// f(0)
382+
/// } else {
383+
/// 1 + 1
384+
/// }
385+
/// ```
386+
/// this will pass both `f(0)` and `1+1` to the given function.
387+
pub fn for_each_value_source<'tcx, B>(
388+
e: &'tcx Expr<'tcx>,
389+
f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
390+
) -> ControlFlow<B> {
391+
match e.kind {
392+
ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
393+
ExprKind::Match(_, arms, _) => {
394+
for arm in arms {
395+
for_each_value_source(arm.body, f)?;
396+
}
397+
ControlFlow::Continue(())
398+
},
399+
ExprKind::If(_, if_expr, Some(else_expr)) => {
400+
for_each_value_source(if_expr, f)?;
401+
for_each_value_source(else_expr, f)
402+
},
403+
ExprKind::DropTemps(e) => for_each_value_source(e, f),
404+
_ => f(e),
405+
}
406+
}

tests/ui/let_unit.fixed

+25
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,29 @@ fn _returns_generic() {
8787

8888
f4(vec![()]); // Lint
8989
f4(vec![()]); // Lint
90+
91+
// Ok
92+
let _: () = {
93+
let x = 5;
94+
f2(x)
95+
};
96+
97+
let _: () = if true { f() } else { f2(0) }; // Ok
98+
let _: () = if true { f() } else { f2(0) }; // Lint
99+
100+
// Ok
101+
let _: () = match Some(0) {
102+
None => f2(1),
103+
Some(0) => f(),
104+
Some(1) => f2(3),
105+
Some(_) => f2('x'),
106+
};
107+
108+
// Lint
109+
match Some(0) {
110+
None => f2(1),
111+
Some(0) => f(),
112+
Some(1) => f2(3),
113+
Some(_) => (),
114+
};
90115
}

tests/ui/let_unit.rs

+25
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,29 @@ fn _returns_generic() {
8787

8888
let _: () = f4(vec![()]); // Lint
8989
let x: () = f4(vec![()]); // Lint
90+
91+
// Ok
92+
let _: () = {
93+
let x = 5;
94+
f2(x)
95+
};
96+
97+
let _: () = if true { f() } else { f2(0) }; // Ok
98+
let x: () = if true { f() } else { f2(0) }; // Lint
99+
100+
// Ok
101+
let _: () = match Some(0) {
102+
None => f2(1),
103+
Some(0) => f(),
104+
Some(1) => f2(3),
105+
Some(_) => f2('x'),
106+
};
107+
108+
// Lint
109+
let _: () = match Some(0) {
110+
None => f2(1),
111+
Some(0) => f(),
112+
Some(1) => f2(3),
113+
Some(_) => (),
114+
};
90115
}

tests/ui/let_unit.stderr

+30-1
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,34 @@ error: this let-binding has unit value
7474
LL | let x: () = f4(vec![()]); // Lint
7575
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
7676

77-
error: aborting due to 9 previous errors
77+
error: this let-binding has unit value
78+
--> $DIR/let_unit.rs:98:5
79+
|
80+
LL | let x: () = if true { f() } else { f2(0) }; // Lint
81+
| ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
82+
| |
83+
| help: use a wild (`_`) binding: `_`
84+
85+
error: this let-binding has unit value
86+
--> $DIR/let_unit.rs:109:5
87+
|
88+
LL | / let _: () = match Some(0) {
89+
LL | | None => f2(1),
90+
LL | | Some(0) => f(),
91+
LL | | Some(1) => f2(3),
92+
LL | | Some(_) => (),
93+
LL | | };
94+
| |______^
95+
|
96+
help: omit the `let` binding
97+
|
98+
LL ~ match Some(0) {
99+
LL + None => f2(1),
100+
LL + Some(0) => f(),
101+
LL + Some(1) => f2(3),
102+
LL + Some(_) => (),
103+
LL + };
104+
|
105+
106+
error: aborting due to 11 previous errors
78107

tests/ui/panicking_macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(clippy::assertions_on_constants, clippy::eq_op)]
1+
#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
22
#![feature(inline_const)]
33
#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
44

0 commit comments

Comments
 (0)