use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::visitors::for_each_value_source;
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
if let StmtKind::Local(local) = stmt.kind
+ && let Some(init) = local.init
&& !local.pat.span.from_expansion()
&& !in_external_macro(cx.sess(), stmt.span)
&& cx.typeck_results().pat_ty(local.pat).is_unit()
{
- if local.init.map_or(false, |e| needs_inferred_result_ty(cx, e)) {
+ let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::Break(())
+ }).is_continue();
+
+ if needs_inferred {
if !matches!(local.pat.kind, PatKind::Wild) {
span_lint_and_then(
cx,
use crate::path_to_local_id;
+use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
v.visit_expr(e);
v.found_unsafe
}
+
+/// Runs the given function for each sub-expression producing the final value consumed by the parent
+/// of the give expression.
+///
+/// e.g. for the following expression
+/// ```rust,ignore
+/// if foo {
+/// f(0)
+/// } else {
+/// 1 + 1
+/// }
+/// ```
+/// this will pass both `f(0)` and `1+1` to the given function.
+pub fn for_each_value_source<'tcx, B>(
+ e: &'tcx Expr<'tcx>,
+ f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+ match e.kind {
+ ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
+ ExprKind::Match(_, arms, _) => {
+ for arm in arms {
+ for_each_value_source(arm.body, f)?;
+ }
+ ControlFlow::Continue(())
+ },
+ ExprKind::If(_, if_expr, Some(else_expr)) => {
+ for_each_value_source(if_expr, f)?;
+ for_each_value_source(else_expr, f)
+ },
+ ExprKind::DropTemps(e) => for_each_value_source(e, f),
+ _ => f(e),
+ }
+}
let _ = core::ptr::read_unaligned(ptr as *const u16);
let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
let ptr = &mut data as *mut [u8; 2] as *mut u8;
- let _ = (ptr as *mut u16).write_unaligned(0);
- let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
- let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+ (ptr as *mut u16).write_unaligned(0);
+ core::ptr::write_unaligned(ptr as *mut u16, 0);
+ core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
}
}
f4(vec![()]); // Lint
f4(vec![()]); // Lint
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let _: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
}
let _: () = f4(vec![()]); // Lint
let x: () = f4(vec![()]); // Lint
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let x: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
}
LL | let x: () = f4(vec![()]); // Lint
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
-error: aborting due to 9 previous errors
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:98:5
+ |
+LL | let x: () = if true { f() } else { f2(0) }; // Lint
+ | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:109:5
+ |
+LL | / let _: () = match Some(0) {
+LL | | None => f2(1),
+LL | | Some(0) => f(),
+LL | | Some(1) => f2(3),
+LL | | Some(_) => (),
+LL | | };
+ | |______^
+ |
+help: omit the `let` binding
+ |
+LL ~ match Some(0) {
+LL + None => f2(1),
+LL + Some(0) => f(),
+LL + Some(1) => f2(3),
+LL + Some(_) => (),
+LL + };
+ |
+
+error: aborting due to 11 previous errors
// run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
struct SomeStruct;
impl SomeStruct {
// run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
struct SomeStruct;
impl SomeStruct {
-#![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
#![feature(inline_const)]
#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]