Skip to content

Commit 2d4d39d

Browse files
committed
Add the let_underscore_untyped lint
1 parent 5b6795f commit 2d4d39d

10 files changed

+172
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4494,6 +4494,7 @@ Released 2018-09-13
44944494
[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future
44954495
[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
44964496
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
4497+
[`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
44974498
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
44984499
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
44994500
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
224224
crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
225225
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
226226
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
227+
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
227228
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
228229
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
229230
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,

clippy_lints/src/let_underscore.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,45 @@ declare_clippy_lint! {
9090
"non-binding `let` on a future"
9191
}
9292

93-
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);
93+
declare_clippy_lint! {
94+
/// ### What it does
95+
/// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
96+
/// or remove the `let` keyword altogether.
97+
///
98+
/// ### Why is this bad?
99+
/// The `let _ = <expr>` expression ignores the value of `<expr>` but will remain doing so even
100+
/// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
101+
/// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
102+
///
103+
/// ### Known problems
104+
/// The `_ = <expr>` is not properly supported by some tools (e.g. IntelliJ) and may seem odd
105+
/// to many developers. This lint also partially overlaps with the other `let_underscore_*`
106+
/// lints.
107+
///
108+
/// ### Example
109+
/// ```rust
110+
/// fn foo() -> Result<u32, ()> {
111+
/// Ok(123)
112+
/// }
113+
/// let _ = foo();
114+
/// ```
115+
/// Use instead:
116+
/// ```rust
117+
/// fn foo() -> Result<u32, ()> {
118+
/// Ok(123)
119+
/// }
120+
/// // Either provide a type annotation:
121+
/// let _: Result<u32, ()> = foo();
122+
/// // …or drop the let keyword:
123+
/// _ = foo();
124+
/// ```
125+
#[clippy::version = "1.69.0"]
126+
pub LET_UNDERSCORE_UNTYPED,
127+
pedantic,
128+
"non-binding `let` without a type annotation"
129+
}
130+
131+
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
94132

95133
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
96134
&paths::PARKING_LOT_MUTEX_GUARD,
@@ -148,6 +186,18 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
148186
"consider explicitly using function result",
149187
);
150188
}
189+
190+
if local.pat.default_binding_modes && local.ty.is_none() {
191+
// When `default_binding_modes` is true, the `let` keyword is present.
192+
span_lint_and_help(
193+
cx,
194+
LET_UNDERSCORE_UNTYPED,
195+
local.span,
196+
"non-binding `let` without a type annotation",
197+
None,
198+
"consider adding a type annotation or removing the `let` keyword",
199+
);
200+
}
151201
}
152202
}
153203
}

tests/ui/let_underscore_untyped.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#![allow(unused)]
2+
#![warn(clippy::let_underscore_untyped)]
3+
4+
use std::future::Future;
5+
use std::{boxed::Box, fmt::Display};
6+
7+
fn a() -> u32 {
8+
1
9+
}
10+
11+
fn b<T>(x: T) -> T {
12+
x
13+
}
14+
15+
fn c() -> impl Display {
16+
1
17+
}
18+
19+
fn d(x: &u32) -> &u32 {
20+
x
21+
}
22+
23+
fn e() -> Result<u32, ()> {
24+
Ok(1)
25+
}
26+
27+
fn f() -> Box<dyn Display> {
28+
Box::new(1)
29+
}
30+
31+
fn main() {
32+
let _ = a();
33+
let _ = b(1);
34+
let _ = c();
35+
let _ = d(&1);
36+
let _ = e();
37+
let _ = f();
38+
39+
_ = a();
40+
_ = b(1);
41+
_ = c();
42+
_ = d(&1);
43+
_ = e();
44+
_ = f();
45+
46+
let _: u32 = a();
47+
let _: u32 = b(1);
48+
let _: &u32 = d(&1);
49+
let _: Result<_, _> = e();
50+
let _: Box<_> = f();
51+
52+
#[allow(clippy::let_underscore_untyped)]
53+
let _ = a();
54+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error: non-binding `let` without a type annotation
2+
--> $DIR/let_underscore_untyped.rs:32:5
3+
|
4+
LL | let _ = a();
5+
| ^^^^^^^^^^^^
6+
|
7+
= help: consider adding a type annotation or removing the `let` keyword
8+
= note: `-D clippy::let-underscore-untyped` implied by `-D warnings`
9+
10+
error: non-binding `let` without a type annotation
11+
--> $DIR/let_underscore_untyped.rs:33:5
12+
|
13+
LL | let _ = b(1);
14+
| ^^^^^^^^^^^^^
15+
|
16+
= help: consider adding a type annotation or removing the `let` keyword
17+
18+
error: non-binding `let` without a type annotation
19+
--> $DIR/let_underscore_untyped.rs:34:5
20+
|
21+
LL | let _ = c();
22+
| ^^^^^^^^^^^^
23+
|
24+
= help: consider adding a type annotation or removing the `let` keyword
25+
26+
error: non-binding `let` without a type annotation
27+
--> $DIR/let_underscore_untyped.rs:35:5
28+
|
29+
LL | let _ = d(&1);
30+
| ^^^^^^^^^^^^^^
31+
|
32+
= help: consider adding a type annotation or removing the `let` keyword
33+
34+
error: non-binding `let` without a type annotation
35+
--> $DIR/let_underscore_untyped.rs:36:5
36+
|
37+
LL | let _ = e();
38+
| ^^^^^^^^^^^^
39+
|
40+
= help: consider adding a type annotation or removing the `let` keyword
41+
42+
error: non-binding `let` without a type annotation
43+
--> $DIR/let_underscore_untyped.rs:37:5
44+
|
45+
LL | let _ = f();
46+
| ^^^^^^^^^^^^
47+
|
48+
= help: consider adding a type annotation or removing the `let` keyword
49+
50+
error: aborting due to 6 previous errors
51+

tests/ui/map_flatten_fixable.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// run-rustfix
22

33
#![warn(clippy::all, clippy::pedantic)]
4+
#![allow(clippy::let_underscore_untyped)]
45
#![allow(clippy::missing_docs_in_private_items)]
56
#![allow(clippy::map_identity)]
67
#![allow(clippy::redundant_closure)]

tests/ui/map_flatten_fixable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// run-rustfix
22

33
#![warn(clippy::all, clippy::pedantic)]
4+
#![allow(clippy::let_underscore_untyped)]
45
#![allow(clippy::missing_docs_in_private_items)]
56
#![allow(clippy::map_identity)]
67
#![allow(clippy::redundant_closure)]

tests/ui/map_flatten_fixable.stderr

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,49 @@
11
error: called `map(..).flatten()` on `Iterator`
2-
--> $DIR/map_flatten_fixable.rs:17:47
2+
--> $DIR/map_flatten_fixable.rs:18:47
33
|
44
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)`
66
|
77
= note: `-D clippy::map-flatten` implied by `-D warnings`
88

99
error: called `map(..).flatten()` on `Iterator`
10-
--> $DIR/map_flatten_fixable.rs:18:47
10+
--> $DIR/map_flatten_fixable.rs:19:47
1111
|
1212
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)`
1414

1515
error: called `map(..).flatten()` on `Iterator`
16-
--> $DIR/map_flatten_fixable.rs:19:47
16+
--> $DIR/map_flatten_fixable.rs:20:47
1717
|
1818
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)`
2020

2121
error: called `map(..).flatten()` on `Iterator`
22-
--> $DIR/map_flatten_fixable.rs:20:47
22+
--> $DIR/map_flatten_fixable.rs:21:47
2323
|
2424
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))`
2626

2727
error: called `map(..).flatten()` on `Iterator`
28-
--> $DIR/map_flatten_fixable.rs:23:47
28+
--> $DIR/map_flatten_fixable.rs:24:47
2929
|
3030
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
3131
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)`
3232

3333
error: called `map(..).flatten()` on `Option`
34-
--> $DIR/map_flatten_fixable.rs:26:40
34+
--> $DIR/map_flatten_fixable.rs:27:40
3535
|
3636
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
3737
| ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
3838

3939
error: called `map(..).flatten()` on `Result`
40-
--> $DIR/map_flatten_fixable.rs:29:42
40+
--> $DIR/map_flatten_fixable.rs:30:42
4141
|
4242
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
4343
| ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
4444

4545
error: called `map(..).flatten()` on `Iterator`
46-
--> $DIR/map_flatten_fixable.rs:38:10
46+
--> $DIR/map_flatten_fixable.rs:39:10
4747
|
4848
LL | .map(|n| match n {
4949
| __________^
@@ -72,7 +72,7 @@ LL ~ });
7272
|
7373

7474
error: called `map(..).flatten()` on `Option`
75-
--> $DIR/map_flatten_fixable.rs:58:10
75+
--> $DIR/map_flatten_fixable.rs:59:10
7676
|
7777
LL | .map(|_| {
7878
| __________^

tests/ui/methods.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![allow(
55
clippy::disallowed_names,
66
clippy::default_trait_access,
7+
clippy::let_underscore_untyped,
78
clippy::missing_docs_in_private_items,
89
clippy::missing_safety_doc,
910
clippy::non_ascii_literal,

tests/ui/methods.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: methods called `new` usually return `Self`
2-
--> $DIR/methods.rs:104:5
2+
--> $DIR/methods.rs:105:5
33
|
44
LL | / fn new() -> i32 {
55
LL | | 0
@@ -9,7 +9,7 @@ LL | | }
99
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
1010

1111
error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
12-
--> $DIR/methods.rs:125:13
12+
--> $DIR/methods.rs:126:13
1313
|
1414
LL | let _ = v.iter().filter(|&x| {
1515
| _____________^

0 commit comments

Comments
 (0)