Skip to content

Commit 1985acd

Browse files
Add clippy::result_as_ref_deref lint
1 parent f58088b commit 1985acd

File tree

8 files changed

+297
-19
lines changed

8 files changed

+297
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5922,6 +5922,7 @@ Released 2018-09-13
59225922
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
59235923
[`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization
59245924
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
5925+
[`result_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_as_ref_deref
59255926
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
59265927
[`result_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_filter_map
59275928
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err

clippy_config/src/msrvs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ msrv_aliases! {
2121
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
2222
1,82,0 { IS_NONE_OR, REPEAT_N }
2323
1,81,0 { LINT_REASONS_STABILIZATION }
24-
1,80,0 { BOX_INTO_ITER}
24+
1,80,0 { BOX_INTO_ITER }
2525
1,77,0 { C_STR_LITERALS }
2626
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
2727
1,73,0 { MANUAL_DIV_CEIL }
@@ -40,7 +40,7 @@ msrv_aliases! {
4040
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
4141
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
4242
1,50,0 { BOOL_THEN, CLAMP }
43-
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
43+
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST, RESULT_AS_DEREF }
4444
1,46,0 { CONST_IF_MATCH }
4545
1,45,0 { STR_STRIP_PREFIX }
4646
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
452452
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
453453
crate::methods::REDUNDANT_AS_STR_INFO,
454454
crate::methods::REPEAT_ONCE_INFO,
455+
crate::methods::RESULT_AS_REF_DEREF_INFO,
455456
crate::methods::RESULT_FILTER_MAP_INFO,
456457
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
457458
crate::methods::SEARCH_IS_SOME_INFO,

clippy_lints/src/methods/option_as_ref_deref.rs renamed to clippy_lints/src/methods/manual_as_ref_deref.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
use clippy_config::msrvs::{self, Msrv};
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::snippet;
4-
use clippy_utils::ty::is_type_diagnostic_item;
4+
use clippy_utils::ty::get_type_diagnostic_name;
55
use clippy_utils::{path_to_local_id, peel_blocks};
66
use rustc_errors::Applicability;
77
use rustc_hir as hir;
88
use rustc_lint::LateContext;
99
use rustc_middle::ty;
1010
use rustc_span::{Symbol, sym};
1111

12-
use super::OPTION_AS_REF_DEREF;
12+
use super::{OPTION_AS_REF_DEREF, RESULT_AS_REF_DEREF};
1313

14-
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
14+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15+
enum Target {
16+
Option,
17+
Result,
18+
}
19+
20+
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s and `Result`s
1521
pub(super) fn check(
1622
cx: &LateContext<'_>,
1723
expr: &hir::Expr<'_>,
@@ -20,16 +26,16 @@ pub(super) fn check(
2026
is_mut: bool,
2127
msrv: &Msrv,
2228
) {
23-
if !msrv.meets(msrvs::OPTION_AS_DEREF) {
24-
return;
25-
}
26-
2729
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
2830

29-
let option_ty = cx.typeck_results().expr_ty(as_ref_recv);
30-
if !is_type_diagnostic_item(cx, option_ty, sym::Option) {
31-
return;
32-
}
31+
let target = {
32+
let target_ty = cx.typeck_results().expr_ty(as_ref_recv);
33+
match get_type_diagnostic_name(cx, target_ty) {
34+
Some(sym::Option) if msrv.meets(msrvs::OPTION_AS_DEREF) => Target::Option,
35+
Some(sym::Result) if msrv.meets(msrvs::RESULT_AS_DEREF) => Target::Result,
36+
_ => return,
37+
}
38+
};
3339

3440
let deref_aliases: [Symbol; 7] = [
3541
sym::cstring_as_c_str,
@@ -103,10 +109,20 @@ pub(super) fn check(
103109
let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, ".."));
104110
let suggestion = format!("consider using {method_hint}");
105111

106-
let msg = format!("called `{current_method}` on an `Option` value");
112+
let target_name_with_article = match target {
113+
Target::Option => "an `Option`",
114+
Target::Result => "a `Result`",
115+
};
116+
let msg = format!("called `{current_method}` on {target_name_with_article} value");
117+
118+
let lint = match target {
119+
Target::Option => OPTION_AS_REF_DEREF,
120+
Target::Result => RESULT_AS_REF_DEREF,
121+
};
122+
107123
span_lint_and_sugg(
108124
cx,
109-
OPTION_AS_REF_DEREF,
125+
lint,
110126
expr.span,
111127
msg,
112128
suggestion,

clippy_lints/src/methods/mod.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod iter_skip_zero;
5252
mod iter_with_drain;
5353
mod iterator_step_by_zero;
5454
mod join_absolute_paths;
55+
mod manual_as_ref_deref;
5556
mod manual_c_str_literals;
5657
mod manual_inspect;
5758
mod manual_is_variant_and;
@@ -79,7 +80,6 @@ mod obfuscated_if_else;
7980
mod ok_expect;
8081
mod open_options;
8182
mod option_as_ref_cloned;
82-
mod option_as_ref_deref;
8383
mod option_map_or_err_ok;
8484
mod option_map_or_none;
8585
mod option_map_unwrap_or;
@@ -1762,7 +1762,8 @@ declare_clippy_lint! {
17621762

17631763
declare_clippy_lint! {
17641764
/// ### What it does
1765-
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str).
1765+
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases
1766+
/// (such as `String::as_str`) on `Option`.
17661767
///
17671768
/// ### Why is this bad?
17681769
/// Readability, this can be written more concisely as
@@ -1786,6 +1787,33 @@ declare_clippy_lint! {
17861787
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
17871788
}
17881789

1790+
declare_clippy_lint! {
1791+
/// ### What it does
1792+
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases
1793+
/// (such as `String::as_str`) on `Result`.
1794+
///
1795+
/// ### Why is this bad?
1796+
/// Readability, this can be written more concisely as
1797+
/// `_.as_deref()`.
1798+
///
1799+
/// ### Example
1800+
/// ```no_run
1801+
/// # let res = Ok::<_, ()>("".to_string());
1802+
/// res.as_ref().map(String::as_str)
1803+
/// # ;
1804+
/// ```
1805+
/// Use instead:
1806+
/// ```no_run
1807+
/// # let res = Ok::<_, ()>("".to_string());
1808+
/// res.as_deref()
1809+
/// # ;
1810+
/// ```
1811+
#[clippy::version = "1.84.0"]
1812+
pub RESULT_AS_REF_DEREF,
1813+
complexity,
1814+
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
1815+
}
1816+
17891817
declare_clippy_lint! {
17901818
/// ### What it does
17911819
/// Checks for usage of `iter().next()` on a Slice or an Array
@@ -4339,6 +4367,7 @@ impl_lint_pass!(Methods => [
43394367
ZST_OFFSET,
43404368
FILETYPE_IS_FILE,
43414369
OPTION_AS_REF_DEREF,
4370+
RESULT_AS_REF_DEREF,
43424371
UNNECESSARY_LAZY_EVALUATIONS,
43434372
MAP_COLLECT_RESULT_UNIT,
43444373
FROM_ITER_INSTEAD_OF_COLLECT,
@@ -4932,8 +4961,8 @@ impl Methods {
49324961
}
49334962
if let Some((name, recv2, args, span2, _)) = method_call(recv) {
49344963
match (name, args) {
4935-
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
4936-
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
4964+
("as_mut", []) => manual_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
4965+
("as_ref", []) => manual_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
49374966
("filter", [f_arg]) => {
49384967
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
49394968
},

tests/ui/result_as_ref_deref.fixed

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#![allow(unused, clippy::redundant_clone, clippy::useless_vec)]
2+
#![warn(clippy::result_as_ref_deref)]
3+
4+
use std::ffi::{CString, OsString};
5+
use std::ops::{Deref, DerefMut};
6+
use std::path::PathBuf;
7+
8+
fn main() {
9+
let mut res = Ok::<_, ()>(String::from("123"));
10+
11+
let _ = res.clone().as_deref().map(str::len);
12+
13+
#[rustfmt::skip]
14+
let _ = res.clone().as_deref()
15+
.map(str::len);
16+
17+
let _ = res.as_deref_mut();
18+
19+
let _ = res.as_deref();
20+
let _ = res.as_deref();
21+
let _ = res.as_deref_mut();
22+
let _ = res.as_deref_mut();
23+
let _ = Ok::<_, ()>(CString::new(vec![]).unwrap()).as_deref();
24+
let _ = Ok::<_, ()>(OsString::new()).as_deref();
25+
let _ = Ok::<_, ()>(PathBuf::new()).as_deref();
26+
let _ = Ok::<_, ()>(Vec::<()>::new()).as_deref();
27+
let _ = Ok::<_, ()>(Vec::<()>::new()).as_deref_mut();
28+
29+
let _ = res.as_deref();
30+
let _ = res.clone().as_deref_mut().map(|x| x.len());
31+
32+
let vc = vec![String::new()];
33+
let _ = Ok::<_, ()>(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
34+
35+
let _: Result<&str, &()> = Ok(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
36+
37+
let _ = res.as_deref();
38+
let _ = res.as_deref_mut();
39+
40+
let _ = res.as_deref();
41+
}
42+
43+
#[clippy::msrv = "1.46"]
44+
fn msrv_1_46() {
45+
let res = Ok::<_, ()>(String::from("123"));
46+
let _ = res.as_ref().map(String::as_str);
47+
}
48+
49+
#[clippy::msrv = "1.47"]
50+
fn msrv_1_47() {
51+
let res = Ok::<_, ()>(String::from("123"));
52+
let _ = res.as_deref();
53+
}

tests/ui/result_as_ref_deref.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![allow(unused, clippy::redundant_clone, clippy::useless_vec)]
2+
#![warn(clippy::result_as_ref_deref)]
3+
4+
use std::ffi::{CString, OsString};
5+
use std::ops::{Deref, DerefMut};
6+
use std::path::PathBuf;
7+
8+
fn main() {
9+
let mut res = Ok::<_, ()>(String::from("123"));
10+
11+
let _ = res.clone().as_ref().map(Deref::deref).map(str::len);
12+
13+
#[rustfmt::skip]
14+
let _ = res.clone()
15+
.as_ref().map(
16+
Deref::deref
17+
)
18+
.map(str::len);
19+
20+
let _ = res.as_mut().map(DerefMut::deref_mut);
21+
22+
let _ = res.as_ref().map(String::as_str);
23+
let _ = res.as_ref().map(|x| x.as_str());
24+
let _ = res.as_mut().map(String::as_mut_str);
25+
let _ = res.as_mut().map(|x| x.as_mut_str());
26+
let _ = Ok::<_, ()>(CString::new(vec![]).unwrap())
27+
.as_ref()
28+
.map(CString::as_c_str);
29+
let _ = Ok::<_, ()>(OsString::new()).as_ref().map(OsString::as_os_str);
30+
let _ = Ok::<_, ()>(PathBuf::new()).as_ref().map(PathBuf::as_path);
31+
let _ = Ok::<_, ()>(Vec::<()>::new()).as_ref().map(Vec::as_slice);
32+
let _ = Ok::<_, ()>(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
33+
34+
let _ = res.as_ref().map(|x| x.deref());
35+
let _ = res.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
36+
37+
let vc = vec![String::new()];
38+
let _ = Ok::<_, ()>(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
39+
40+
let _: Result<&str, &()> = Ok(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
41+
42+
let _ = res.as_ref().map(|x| &**x);
43+
let _ = res.as_mut().map(|x| &mut **x);
44+
45+
let _ = res.as_ref().map(std::ops::Deref::deref);
46+
}
47+
48+
#[clippy::msrv = "1.46"]
49+
fn msrv_1_46() {
50+
let res = Ok::<_, ()>(String::from("123"));
51+
let _ = res.as_ref().map(String::as_str);
52+
}
53+
54+
#[clippy::msrv = "1.47"]
55+
fn msrv_1_47() {
56+
let res = Ok::<_, ()>(String::from("123"));
57+
let _ = res.as_ref().map(String::as_str);
58+
}

0 commit comments

Comments
 (0)