Skip to content

Commit 5ead90f

Browse files
committed
Auto merge of #12150 - ithinuel:add_misleading_use_of_ok, r=y21
Add lint for `unused_result_ok` This PR adds a lint to capture the use of `expr.ok();` when the result is not _really_ used. This could be interpreted as the result being checked (like it is with `unwrap()` or `expect`) but it actually only ignores the result. `let _ = expr;` expresses that intent better. This was also mentionned in #8994 (although not being the main topic of that issue). changelog: [`misleading_use_of_ok`]: Add new lint to capture `.ok();` when the result is not _really_ used.
2 parents 9d9a0dc + 182c268 commit 5ead90f

8 files changed

+200
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5999,6 +5999,7 @@ Released 2018-09-13
59995999
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
60006000
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
60016001
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
6002+
[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
60026003
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
60036004
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
60046005
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit

clippy_dev/src/update_lints.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -372,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
372372

373373
// Some lints have their own directories, delete them
374374
if path.is_dir() {
375-
fs::remove_dir_all(path).ok();
375+
let _ = fs::remove_dir_all(path);
376376
return;
377377
}
378378

379379
// Remove all related test files
380-
fs::remove_file(path.with_extension("rs")).ok();
381-
fs::remove_file(path.with_extension("stderr")).ok();
382-
fs::remove_file(path.with_extension("fixed")).ok();
380+
let _ = fs::remove_file(path.with_extension("rs"));
381+
let _ = fs::remove_file(path.with_extension("stderr"));
382+
let _ = fs::remove_file(path.with_extension("fixed"));
383383
}
384384

385385
fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
@@ -422,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
422422
lint_mod_path.set_file_name(name);
423423
lint_mod_path.set_extension("rs");
424424

425-
fs::remove_file(lint_mod_path).ok();
425+
let _ = fs::remove_file(lint_mod_path);
426426
}
427427

428428
let mut content =

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
739739
crate::unused_async::UNUSED_ASYNC_INFO,
740740
crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO,
741741
crate::unused_peekable::UNUSED_PEEKABLE_INFO,
742+
crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
742743
crate::unused_rounding::UNUSED_ROUNDING_INFO,
743744
crate::unused_self::UNUSED_SELF_INFO,
744745
crate::unused_unit::UNUSED_UNIT_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ mod unsafe_removed_from_name;
370370
mod unused_async;
371371
mod unused_io_amount;
372372
mod unused_peekable;
373+
mod unused_result_ok;
373374
mod unused_rounding;
374375
mod unused_self;
375376
mod unused_unit;
@@ -671,6 +672,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
671672
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf)));
672673
store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
673674
store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
675+
store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk));
674676
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
675677
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
676678
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));

clippy_lints/src/unused_result_ok.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_context;
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{ExprKind, Stmt, StmtKind};
6+
use rustc_lint::{LateContext, LateLintPass, LintContext};
7+
use rustc_middle::lint::in_external_macro;
8+
use rustc_session::declare_lint_pass;
9+
use rustc_span::sym;
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for calls to `Result::ok()` without using the returned `Option`.
14+
///
15+
/// ### Why is this bad?
16+
/// Using `Result::ok()` may look like the result is checked like `unwrap` or `expect` would do
17+
/// but it only silences the warning caused by `#[must_use]` on the `Result`.
18+
///
19+
/// ### Example
20+
/// ```no_run
21+
/// # fn some_function() -> Result<(), ()> { Ok(()) }
22+
/// some_function().ok();
23+
/// ```
24+
/// Use instead:
25+
/// ```no_run
26+
/// # fn some_function() -> Result<(), ()> { Ok(()) }
27+
/// let _ = some_function();
28+
/// ```
29+
#[clippy::version = "1.70.0"]
30+
pub UNUSED_RESULT_OK,
31+
restriction,
32+
"Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead."
33+
}
34+
declare_lint_pass!(UnusedResultOk => [UNUSED_RESULT_OK]);
35+
36+
impl LateLintPass<'_> for UnusedResultOk {
37+
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
38+
if let StmtKind::Semi(expr) = stmt.kind
39+
&& let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
40+
&& ok_path.ident.as_str() == "ok"
41+
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
42+
&& !in_external_macro(cx.sess(), stmt.span)
43+
{
44+
let ctxt = expr.span.ctxt();
45+
let mut applicability = Applicability::MaybeIncorrect;
46+
let snippet = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
47+
let sugg = format!("let _ = {snippet}");
48+
span_lint_and_sugg(
49+
cx,
50+
UNUSED_RESULT_OK,
51+
expr.span,
52+
"ignoring a result with `.ok()` is misleading",
53+
"consider using `let _ =` and removing the call to `.ok()` instead",
54+
sugg,
55+
applicability,
56+
);
57+
}
58+
}
59+
}

tests/ui/unused_result_ok.fixed

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@aux-build:proc_macros.rs
2+
#![warn(clippy::unused_result_ok)]
3+
#![allow(dead_code)]
4+
5+
#[macro_use]
6+
extern crate proc_macros;
7+
8+
fn bad_style(x: &str) {
9+
let _ = x.parse::<u32>();
10+
}
11+
12+
fn good_style(x: &str) -> Option<u32> {
13+
x.parse::<u32>().ok()
14+
}
15+
16+
#[rustfmt::skip]
17+
fn strange_parse(x: &str) {
18+
let _ = x . parse::<i32>();
19+
}
20+
21+
macro_rules! v {
22+
() => {
23+
Ok::<(), ()>(())
24+
};
25+
}
26+
27+
macro_rules! w {
28+
() => {
29+
let _ = Ok::<(), ()>(());
30+
};
31+
}
32+
33+
fn main() {
34+
let _ = v!();
35+
w!();
36+
37+
external! {
38+
Ok::<(),()>(()).ok();
39+
};
40+
}

tests/ui/unused_result_ok.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@aux-build:proc_macros.rs
2+
#![warn(clippy::unused_result_ok)]
3+
#![allow(dead_code)]
4+
5+
#[macro_use]
6+
extern crate proc_macros;
7+
8+
fn bad_style(x: &str) {
9+
x.parse::<u32>().ok();
10+
}
11+
12+
fn good_style(x: &str) -> Option<u32> {
13+
x.parse::<u32>().ok()
14+
}
15+
16+
#[rustfmt::skip]
17+
fn strange_parse(x: &str) {
18+
x . parse::<i32>() . ok ();
19+
}
20+
21+
macro_rules! v {
22+
() => {
23+
Ok::<(), ()>(())
24+
};
25+
}
26+
27+
macro_rules! w {
28+
() => {
29+
Ok::<(), ()>(()).ok();
30+
};
31+
}
32+
33+
fn main() {
34+
v!().ok();
35+
w!();
36+
37+
external! {
38+
Ok::<(),()>(()).ok();
39+
};
40+
}

tests/ui/unused_result_ok.stderr

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: ignoring a result with `.ok()` is misleading
2+
--> tests/ui/unused_result_ok.rs:9:5
3+
|
4+
LL | x.parse::<u32>().ok();
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::unused-result-ok` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::unused_result_ok)]`
9+
help: consider using `let _ =` and removing the call to `.ok()` instead
10+
|
11+
LL | let _ = x.parse::<u32>();
12+
| ~~~~~~~~~~~~~~~~~~~~~~~~
13+
14+
error: ignoring a result with `.ok()` is misleading
15+
--> tests/ui/unused_result_ok.rs:18:5
16+
|
17+
LL | x . parse::<i32>() . ok ();
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
help: consider using `let _ =` and removing the call to `.ok()` instead
21+
|
22+
LL | let _ = x . parse::<i32>();
23+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24+
25+
error: ignoring a result with `.ok()` is misleading
26+
--> tests/ui/unused_result_ok.rs:34:5
27+
|
28+
LL | v!().ok();
29+
| ^^^^^^^^^
30+
|
31+
help: consider using `let _ =` and removing the call to `.ok()` instead
32+
|
33+
LL | let _ = v!();
34+
| ~~~~~~~~~~~~
35+
36+
error: ignoring a result with `.ok()` is misleading
37+
--> tests/ui/unused_result_ok.rs:29:9
38+
|
39+
LL | Ok::<(), ()>(()).ok();
40+
| ^^^^^^^^^^^^^^^^^^^^^
41+
...
42+
LL | w!();
43+
| ---- in this macro invocation
44+
|
45+
= note: this error originates in the macro `w` (in Nightly builds, run with -Z macro-backtrace for more info)
46+
help: consider using `let _ =` and removing the call to `.ok()` instead
47+
|
48+
LL | let _ = Ok::<(), ()>(());
49+
| ~~~~~~~~~~~~~~~~~~~~~~~~
50+
51+
error: aborting due to 4 previous errors
52+

0 commit comments

Comments
 (0)