Skip to content

Commit 781b3cd

Browse files
Add clippy::result_as_ref_deref lint
1 parent 2536745 commit 781b3cd

File tree

8 files changed

+304
-17
lines changed

8 files changed

+304
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5880,6 +5880,7 @@ Released 2018-09-13
58805880
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
58815881
[`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization
58825882
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
5883+
[`result_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_as_ref_deref
58835884
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
58845885
[`result_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_filter_map
58855886
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err

clippy_config/src/msrvs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ macro_rules! msrv_aliases {
1717

1818
// names may refer to stabilized feature flags or library items
1919
msrv_aliases! {
20-
1,81,0 { LINT_REASONS_STABILIZATION }
21-
1,80,0 { BOX_INTO_ITER}
20+
1,81,0 { LINT_REASONS_STABILIZATION }
21+
1,80,0 { BOX_INTO_ITER }
2222
1,77,0 { C_STR_LITERALS }
2323
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
2424
1,73,0 { MANUAL_DIV_CEIL }
@@ -37,7 +37,7 @@ msrv_aliases! {
3737
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
3838
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
3939
1,50,0 { BOOL_THEN, CLAMP }
40-
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
40+
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST, RESULT_AS_DEREF }
4141
1,46,0 { CONST_IF_MATCH }
4242
1,45,0 { STR_STRIP_PREFIX }
4343
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
@@ -444,6 +444,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
444444
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
445445
crate::methods::REDUNDANT_AS_STR_INFO,
446446
crate::methods::REPEAT_ONCE_INFO,
447+
crate::methods::RESULT_AS_REF_DEREF_INFO,
447448
crate::methods::RESULT_FILTER_MAP_INFO,
448449
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
449450
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: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ use rustc_lint::LateContext;
99
use rustc_middle::ty;
1010
use rustc_span::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,14 +26,23 @@ 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+
let target = 'target: {
32+
let target_ty = cx.typeck_results().expr_ty(as_ref_recv);
33+
if is_type_diagnostic_item(cx, target_ty, sym::Option) {
34+
break 'target Target::Option;
35+
}
36+
if is_type_diagnostic_item(cx, target_ty, sym::Result) {
37+
break 'target Target::Result;
38+
}
39+
return;
40+
};
41+
42+
if target == Target::Option && !msrv.meets(msrvs::OPTION_AS_DEREF) {
43+
return;
44+
}
45+
if target == Target::Result && !msrv.meets(msrvs::RESULT_AS_DEREF) {
3146
return;
3247
}
3348

@@ -99,10 +114,20 @@ pub(super) fn check(
99114
let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, ".."));
100115
let suggestion = format!("consider using {method_hint}");
101116

102-
let msg = format!("called `{current_method}` on an `Option` value");
117+
let target_name_with_article = match target {
118+
Target::Option => "an `Option`",
119+
Target::Result => "a `Result`",
120+
};
121+
let msg = format!("called `{current_method}` on {target_name_with_article} value");
122+
123+
let lint = match target {
124+
Target::Option => OPTION_AS_REF_DEREF,
125+
Target::Result => RESULT_AS_REF_DEREF,
126+
};
127+
103128
span_lint_and_sugg(
104129
cx,
105-
OPTION_AS_REF_DEREF,
130+
lint,
106131
expr.span,
107132
msg,
108133
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;
@@ -76,7 +77,6 @@ mod obfuscated_if_else;
7677
mod ok_expect;
7778
mod open_options;
7879
mod option_as_ref_cloned;
79-
mod option_as_ref_deref;
8080
mod option_map_or_err_ok;
8181
mod option_map_or_none;
8282
mod option_map_unwrap_or;
@@ -1758,7 +1758,8 @@ declare_clippy_lint! {
17581758

17591759
declare_clippy_lint! {
17601760
/// ### What it does
1761-
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str).
1761+
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases
1762+
/// (such as `String::as_str`) on `Option`.
17621763
///
17631764
/// ### Why is this bad?
17641765
/// Readability, this can be written more concisely as
@@ -1782,6 +1783,33 @@ declare_clippy_lint! {
17821783
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
17831784
}
17841785

1786+
declare_clippy_lint! {
1787+
/// ### What it does
1788+
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases
1789+
/// (such as `String::as_str`) on `Result`.
1790+
///
1791+
/// ### Why is this bad?
1792+
/// Readability, this can be written more concisely as
1793+
/// `_.as_deref()`.
1794+
///
1795+
/// ### Example
1796+
/// ```no_run
1797+
/// # let res = Ok::<_, ()>("".to_string());
1798+
/// res.as_ref().map(String::as_str)
1799+
/// # ;
1800+
/// ```
1801+
/// Use instead:
1802+
/// ```no_run
1803+
/// # let res = OK::<_, ()>("".to_string());
1804+
/// res.as_deref()
1805+
/// # ;
1806+
/// ```
1807+
#[clippy::version = "1.83.0"]
1808+
pub RESULT_AS_REF_DEREF,
1809+
complexity,
1810+
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
1811+
}
1812+
17851813
declare_clippy_lint! {
17861814
/// ### What it does
17871815
/// Checks for usage of `iter().next()` on a Slice or an Array
@@ -4223,6 +4251,7 @@ impl_lint_pass!(Methods => [
42234251
ZST_OFFSET,
42244252
FILETYPE_IS_FILE,
42254253
OPTION_AS_REF_DEREF,
4254+
RESULT_AS_REF_DEREF,
42264255
UNNECESSARY_LAZY_EVALUATIONS,
42274256
MAP_COLLECT_RESULT_UNIT,
42284257
FROM_ITER_INSTEAD_OF_COLLECT,
@@ -4791,8 +4820,8 @@ impl Methods {
47914820
}
47924821
if let Some((name, recv2, args, span2, _)) = method_call(recv) {
47934822
match (name, args) {
4794-
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
4795-
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
4823+
("as_mut", []) => manual_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
4824+
("as_ref", []) => manual_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
47964825
("filter", [f_arg]) => {
47974826
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
47984827
},

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)