Skip to content

Commit 0bc93b6

Browse files
committed
Auto merge of #8563 - Jarcho:let_unit_1502, r=Jarcho
Don't lint `let_unit_value` when needed for type inferenece fixes: #1502 Pinging `@dtolnay.` I think this is enough to fix the issue. Do you have a good list crates to test this on? changelog: Don't lint `let_unit_value` when needed for type inference
2 parents 80bcd9b + 70f7c62 commit 0bc93b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+592
-269
lines changed

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
308308
LintId::of(uninit_vec::UNINIT_VEC),
309309
LintId::of(unit_hash::UNIT_HASH),
310310
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
311+
LintId::of(unit_types::LET_UNIT_VALUE),
311312
LintId::of(unit_types::UNIT_ARG),
312313
LintId::of(unit_types::UNIT_CMP),
313314
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),

clippy_lints/src/lib.register_pedantic.rs

-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
8989
LintId::of(types::LINKEDLIST),
9090
LintId::of(types::OPTION_OPTION),
9191
LintId::of(unicode::UNICODE_NOT_NFC),
92-
LintId::of(unit_types::LET_UNIT_VALUE),
9392
LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
9493
LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
9594
LintId::of(unused_async::UNUSED_ASYNC),

clippy_lints/src/lib.register_style.rs

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
107107
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
108108
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
109109
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
110+
LintId::of(unit_types::LET_UNIT_VALUE),
110111
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
111112
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
112113
LintId::of(unused_unit::UNUSED_UNIT),

clippy_lints/src/unit_types/let_unit_value.rs

+75-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
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;
4+
use core::ops::ControlFlow;
35
use rustc_errors::Applicability;
4-
use rustc_hir::{Stmt, StmtKind};
6+
use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
57
use rustc_lint::{LateContext, LintContext};
68
use rustc_middle::lint::in_external_macro;
9+
use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor};
710

811
use super::LET_UNIT_VALUE;
912

1013
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
11-
if let StmtKind::Local(local) = stmt.kind {
12-
if cx.typeck_results().pat_ty(local.pat).is_unit() {
13-
if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
14-
return;
14+
if let StmtKind::Local(local) = stmt.kind
15+
&& let Some(init) = local.init
16+
&& !local.pat.span.from_expansion()
17+
&& !in_external_macro(cx.sess(), stmt.span)
18+
&& cx.typeck_results().pat_ty(local.pat).is_unit()
19+
{
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 {
27+
if !matches!(local.pat.kind, PatKind::Wild) {
28+
span_lint_and_then(
29+
cx,
30+
LET_UNIT_VALUE,
31+
stmt.span,
32+
"this let-binding has unit value",
33+
|diag| {
34+
diag.span_suggestion(
35+
local.pat.span,
36+
"use a wild (`_`) binding",
37+
"_".into(),
38+
Applicability::MaybeIncorrect, // snippet
39+
);
40+
},
41+
);
1542
}
43+
} else {
1644
span_lint_and_then(
1745
cx,
1846
LET_UNIT_VALUE,
@@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
3361
}
3462
}
3563
}
64+
65+
fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
66+
let id = match e.kind {
67+
ExprKind::Call(
68+
Expr {
69+
kind: ExprKind::Path(ref path),
70+
hir_id,
71+
..
72+
},
73+
_,
74+
) => cx.qpath_res(path, *hir_id).opt_def_id(),
75+
ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id),
76+
_ => return false,
77+
};
78+
if let Some(id) = id
79+
&& let sig = cx.tcx.fn_sig(id).skip_binder()
80+
&& let ty::Param(output_ty) = *sig.output().kind()
81+
{
82+
sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
83+
} else {
84+
false
85+
}
86+
}
87+
88+
fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
89+
struct Visitor(u32);
90+
impl<'tcx> TypeVisitor<'tcx> for Visitor {
91+
type BreakTy = ();
92+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
93+
if let ty::Param(ty) = *ty.kind() {
94+
if ty.index == self.0 {
95+
ControlFlow::BREAK
96+
} else {
97+
ControlFlow::CONTINUE
98+
}
99+
} else {
100+
ty.super_visit_with(self)
101+
}
102+
}
103+
}
104+
ty.visit_with(&mut Visitor(index)).is_break()
105+
}

clippy_lints/src/unit_types/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ declare_clippy_lint! {
2323
/// ```
2424
#[clippy::version = "pre 1.29.0"]
2525
pub LET_UNIT_VALUE,
26-
pedantic,
26+
style,
2727
"creating a `let` binding to a value of unit type, which usually can't be used afterwards"
2828
}
2929

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};
@@ -402,3 +403,36 @@ pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
402403
v.visit_expr(e);
403404
v.found_unsafe
404405
}
406+
407+
/// Runs the given function for each sub-expression producing the final value consumed by the parent
408+
/// of the give expression.
409+
///
410+
/// e.g. for the following expression
411+
/// ```rust,ignore
412+
/// if foo {
413+
/// f(0)
414+
/// } else {
415+
/// 1 + 1
416+
/// }
417+
/// ```
418+
/// this will pass both `f(0)` and `1+1` to the given function.
419+
pub fn for_each_value_source<'tcx, B>(
420+
e: &'tcx Expr<'tcx>,
421+
f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
422+
) -> ControlFlow<B> {
423+
match e.kind {
424+
ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
425+
ExprKind::Match(_, arms, _) => {
426+
for arm in arms {
427+
for_each_value_source(arm.body, f)?;
428+
}
429+
ControlFlow::Continue(())
430+
},
431+
ExprKind::If(_, if_expr, Some(else_expr)) => {
432+
for_each_value_source(if_expr, f)?;
433+
for_each_value_source(else_expr, f)
434+
},
435+
ExprKind::DropTemps(e) => for_each_value_source(e, f),
436+
_ => f(e),
437+
}
438+
}

tests/ui-internal/interning_defined_symbol.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// run-rustfix
22
#![deny(clippy::internal)]
3-
#![allow(clippy::missing_clippy_version_attribute)]
3+
#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
44
#![feature(rustc_private)]
55

66
extern crate rustc_span;

tests/ui-internal/interning_defined_symbol.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// run-rustfix
22
#![deny(clippy::internal)]
3-
#![allow(clippy::missing_clippy_version_attribute)]
3+
#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
44
#![feature(rustc_private)]
55

66
extern crate rustc_span;

tests/ui-toml/functions_maxlines/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![warn(clippy::too_many_lines)]
2+
#![allow(clippy::let_unit_value)]
23

34
// This function should be considered one line.
45
fn many_comments_but_one_line_of_code() {

tests/ui-toml/functions_maxlines/test.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this function has too many lines (2/1)
2-
--> $DIR/test.rs:18:1
2+
--> $DIR/test.rs:19:1
33
|
44
LL | / fn too_many_lines() {
55
LL | | println!("This is bad.");
@@ -10,7 +10,7 @@ LL | | }
1010
= note: `-D clippy::too-many-lines` implied by `-D warnings`
1111

1212
error: this function has too many lines (4/1)
13-
--> $DIR/test.rs:24:1
13+
--> $DIR/test.rs:25:1
1414
|
1515
LL | / async fn async_too_many_lines() {
1616
LL | | println!("This is bad.");
@@ -19,7 +19,7 @@ LL | | }
1919
| |_^
2020

2121
error: this function has too many lines (4/1)
22-
--> $DIR/test.rs:30:1
22+
--> $DIR/test.rs:31:1
2323
|
2424
LL | / fn closure_too_many_lines() {
2525
LL | | let _ = {
@@ -30,7 +30,7 @@ LL | | }
3030
| |_^
3131

3232
error: this function has too many lines (2/1)
33-
--> $DIR/test.rs:52:1
33+
--> $DIR/test.rs:53:1
3434
|
3535
LL | / fn comment_before_code() {
3636
LL | | let _ = "test";

tests/ui/cast_alignment.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ fn main() {
4444
let _ = core::ptr::read_unaligned(ptr as *const u16);
4545
let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
4646
let ptr = &mut data as *mut [u8; 2] as *mut u8;
47-
let _ = (ptr as *mut u16).write_unaligned(0);
48-
let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
49-
let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
47+
(ptr as *mut u16).write_unaligned(0);
48+
core::ptr::write_unaligned(ptr as *mut u16, 0);
49+
core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
5050
}
5151
}

tests/ui/cast_slice_different_sizes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::let_unit_value)]
2+
13
fn main() {
24
let x: [i32; 3] = [1_i32, 2, 3];
35
let r_x = &x;

tests/ui/cast_slice_different_sizes.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
2-
--> $DIR/cast_slice_different_sizes.rs:7:13
2+
--> $DIR/cast_slice_different_sizes.rs:9:13
33
|
44
LL | let b = a as *const [u8];
55
| ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
66
|
77
= note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
88

99
error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
10-
--> $DIR/cast_slice_different_sizes.rs:8:13
10+
--> $DIR/cast_slice_different_sizes.rs:10:13
1111
|
1212
LL | let c = b as *const [u32];
1313
| ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
1414

1515
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
16-
--> $DIR/cast_slice_different_sizes.rs:11:16
16+
--> $DIR/cast_slice_different_sizes.rs:13:16
1717
|
1818
LL | let loss = r_x as *const [i32] as *const [u8];
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
2020

2121
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
22-
--> $DIR/cast_slice_different_sizes.rs:18:24
22+
--> $DIR/cast_slice_different_sizes.rs:20:24
2323
|
2424
LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8];
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
2626

2727
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
28-
--> $DIR/cast_slice_different_sizes.rs:19:24
28+
--> $DIR/cast_slice_different_sizes.rs:21:24
2929
|
3030
LL | let loss_block_2 = {
3131
| ________________________^
@@ -43,7 +43,7 @@ LL ~ } as *const u8, ..);
4343
|
4444

4545
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
46-
--> $DIR/cast_slice_different_sizes.rs:36:27
46+
--> $DIR/cast_slice_different_sizes.rs:38:27
4747
|
4848
LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)`

tests/ui/crashes/ice-5944.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![warn(clippy::repeat_once)]
2+
#![allow(clippy::let_unit_value)]
23

34
trait Repeat {
45
fn repeat(&self) {}

tests/ui/default_numeric_fallback_f64.fixed

+9-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// aux-build:macro_rules.rs
33

44
#![warn(clippy::default_numeric_fallback)]
5-
#![allow(unused)]
6-
#![allow(clippy::never_loop)]
7-
#![allow(clippy::no_effect)]
8-
#![allow(clippy::unnecessary_operation)]
9-
#![allow(clippy::branches_sharing_code)]
10-
#![allow(clippy::match_single_binding)]
5+
#![allow(
6+
unused,
7+
clippy::never_loop,
8+
clippy::no_effect,
9+
clippy::unnecessary_operation,
10+
clippy::branches_sharing_code,
11+
clippy::match_single_binding,
12+
clippy::let_unit_value
13+
)]
1114

1215
#[macro_use]
1316
extern crate macro_rules;

tests/ui/default_numeric_fallback_f64.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// aux-build:macro_rules.rs
33

44
#![warn(clippy::default_numeric_fallback)]
5-
#![allow(unused)]
6-
#![allow(clippy::never_loop)]
7-
#![allow(clippy::no_effect)]
8-
#![allow(clippy::unnecessary_operation)]
9-
#![allow(clippy::branches_sharing_code)]
10-
#![allow(clippy::match_single_binding)]
5+
#![allow(
6+
unused,
7+
clippy::never_loop,
8+
clippy::no_effect,
9+
clippy::unnecessary_operation,
10+
clippy::branches_sharing_code,
11+
clippy::match_single_binding,
12+
clippy::let_unit_value
13+
)]
1114

1215
#[macro_use]
1316
extern crate macro_rules;

0 commit comments

Comments
 (0)