Skip to content

Commit 2fe87a8

Browse files
committed
Auto merge of #6165 - dvermd:ref_option_ref, r=flip1995
Add lint 'ref_option_ref' #1377 This lint checks for usage of `&Option<&T>` which can be simplified as `Option<&T>` as suggested in #1377. This WIP PR is here to get feedback on the lint as there's more cases to be handled: * statics/consts, * associated types, * type alias, * function/method parameter/return, * ADT definitions (struct/tuple struct fields, enum variants) changelog: Add 'ref_option_ref' lint
2 parents a2bf404 + 7b065db commit 2fe87a8

9 files changed

+209
-12
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1920,6 +1920,7 @@ Released 2018-09-13
19201920
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
19211921
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
19221922
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
1923+
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
19231924
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
19241925
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
19251926
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ mod redundant_closure_call;
294294
mod redundant_field_names;
295295
mod redundant_pub_crate;
296296
mod redundant_static_lifetimes;
297+
mod ref_option_ref;
297298
mod reference;
298299
mod regex;
299300
mod repeat_once;
@@ -810,6 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
810811
&redundant_field_names::REDUNDANT_FIELD_NAMES,
811812
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
812813
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
814+
&ref_option_ref::REF_OPTION_REF,
813815
&reference::DEREF_ADDROF,
814816
&reference::REF_IN_DEREF,
815817
&regex::INVALID_REGEX,
@@ -1033,6 +1035,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10331035
&sess.target,
10341036
);
10351037
store.register_late_pass(move || box pass_by_ref_or_value);
1038+
store.register_late_pass(|| box ref_option_ref::RefOptionRef);
10361039
store.register_late_pass(|| box try_err::TryErr);
10371040
store.register_late_pass(|| box use_self::UseSelf);
10381041
store.register_late_pass(|| box bytecount::ByteCount);
@@ -1261,6 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12611264
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
12621265
LintId::of(&ranges::RANGE_MINUS_ONE),
12631266
LintId::of(&ranges::RANGE_PLUS_ONE),
1267+
LintId::of(&ref_option_ref::REF_OPTION_REF),
12641268
LintId::of(&shadow::SHADOW_UNRELATED),
12651269
LintId::of(&strings::STRING_ADD_ASSIGN),
12661270
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),

clippy_lints/src/ref_option_ref.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
2+
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
3+
use rustc_lint::{LateContext, LateLintPass};
4+
use rustc_session::{declare_lint_pass, declare_tool_lint};
5+
6+
use if_chain::if_chain;
7+
use rustc_errors::Applicability;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for usage of `&Option<&T>`.
11+
///
12+
/// **Why is this bad?** Since `&` is Copy, it's useless to have a
13+
/// reference on `Option<&T>`.
14+
///
15+
/// **Known problems:** It may be irrevelent to use this lint on
16+
/// public API code as it will make a breaking change to apply it.
17+
///
18+
/// **Example:**
19+
///
20+
/// ```rust,ignore
21+
/// let x: &Option<&u32> = &Some(&0u32);
22+
/// ```
23+
/// Use instead:
24+
/// ```rust,ignore
25+
/// let x: Option<&u32> = Some(&0u32);
26+
/// ```
27+
pub REF_OPTION_REF,
28+
pedantic,
29+
"use `Option<&T>` instead of `&Option<&T>`"
30+
}
31+
32+
declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);
33+
34+
impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
35+
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
36+
if_chain! {
37+
if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
38+
if mut_ty.mutbl == Mutability::Not;
39+
if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
40+
let last = last_path_segment(qpath);
41+
if let Some(res) = last.res;
42+
if let Some(def_id) = res.opt_def_id();
43+
44+
if cx.tcx.is_diagnostic_item(sym!(option_type), def_id);
45+
if let Some(ref params) = last_path_segment(qpath).args ;
46+
if !params.parenthesized;
47+
if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
48+
GenericArg::Type(inner_ty) => Some(inner_ty),
49+
_ => None,
50+
});
51+
if let TyKind::Rptr(_, _) = inner_ty.kind;
52+
53+
then {
54+
span_lint_and_sugg(
55+
cx,
56+
REF_OPTION_REF,
57+
ty.span,
58+
"since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`",
59+
"try",
60+
format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
61+
Applicability::MaybeIncorrect,
62+
);
63+
}
64+
}
65+
}
66+
}

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,13 @@ vec![
20342034
deprecation: None,
20352035
module: "reference",
20362036
},
2037+
Lint {
2038+
name: "ref_option_ref",
2039+
group: "pedantic",
2040+
desc: "use `Option<&T>` instead of `&Option<&T>`",
2041+
deprecation: None,
2042+
module: "ref_option_ref",
2043+
},
20372044
Lint {
20382045
name: "repeat_once",
20392046
group: "complexity",

tests/ui/option_if_let_else.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// run-rustfix
22
#![warn(clippy::option_if_let_else)]
33
#![allow(clippy::redundant_closure)]
4+
#![allow(clippy::ref_option_ref)]
45

56
fn bad1(string: Option<&str>) -> (bool, &str) {
67
string.map_or((false, "hello"), |x| (true, x))

tests/ui/option_if_let_else.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// run-rustfix
22
#![warn(clippy::option_if_let_else)]
33
#![allow(clippy::redundant_closure)]
4+
#![allow(clippy::ref_option_ref)]
45

56
fn bad1(string: Option<&str>) -> (bool, &str) {
67
if let Some(x) = string {

tests/ui/option_if_let_else.stderr

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: use Option::map_or instead of an if let/else
2-
--> $DIR/option_if_let_else.rs:6:5
2+
--> $DIR/option_if_let_else.rs:7:5
33
|
44
LL | / if let Some(x) = string {
55
LL | | (true, x)
@@ -11,7 +11,7 @@ LL | | }
1111
= note: `-D clippy::option-if-let-else` implied by `-D warnings`
1212

1313
error: use Option::map_or instead of an if let/else
14-
--> $DIR/option_if_let_else.rs:16:12
14+
--> $DIR/option_if_let_else.rs:17:12
1515
|
1616
LL | } else if let Some(x) = string {
1717
| ____________^
@@ -22,19 +22,19 @@ LL | | }
2222
| |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }`
2323

2424
error: use Option::map_or instead of an if let/else
25-
--> $DIR/option_if_let_else.rs:24:13
25+
--> $DIR/option_if_let_else.rs:25:13
2626
|
2727
LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
2828
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
2929

3030
error: use Option::map_or instead of an if let/else
31-
--> $DIR/option_if_let_else.rs:25:13
31+
--> $DIR/option_if_let_else.rs:26:13
3232
|
3333
LL | let _ = if let Some(s) = &num { s } else { &0 };
3434
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
3535

3636
error: use Option::map_or instead of an if let/else
37-
--> $DIR/option_if_let_else.rs:26:13
37+
--> $DIR/option_if_let_else.rs:27:13
3838
|
3939
LL | let _ = if let Some(s) = &mut num {
4040
| _____________^
@@ -54,13 +54,13 @@ LL | });
5454
|
5555

5656
error: use Option::map_or instead of an if let/else
57-
--> $DIR/option_if_let_else.rs:32:13
57+
--> $DIR/option_if_let_else.rs:33:13
5858
|
5959
LL | let _ = if let Some(ref s) = num { s } else { &0 };
6060
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
6161

6262
error: use Option::map_or instead of an if let/else
63-
--> $DIR/option_if_let_else.rs:33:13
63+
--> $DIR/option_if_let_else.rs:34:13
6464
|
6565
LL | let _ = if let Some(mut s) = num {
6666
| _____________^
@@ -80,7 +80,7 @@ LL | });
8080
|
8181

8282
error: use Option::map_or instead of an if let/else
83-
--> $DIR/option_if_let_else.rs:39:13
83+
--> $DIR/option_if_let_else.rs:40:13
8484
|
8585
LL | let _ = if let Some(ref mut s) = num {
8686
| _____________^
@@ -100,7 +100,7 @@ LL | });
100100
|
101101

102102
error: use Option::map_or instead of an if let/else
103-
--> $DIR/option_if_let_else.rs:48:5
103+
--> $DIR/option_if_let_else.rs:49:5
104104
|
105105
LL | / if let Some(x) = arg {
106106
LL | | let y = x * x;
@@ -119,7 +119,7 @@ LL | })
119119
|
120120

121121
error: use Option::map_or_else instead of an if let/else
122-
--> $DIR/option_if_let_else.rs:61:13
122+
--> $DIR/option_if_let_else.rs:62:13
123123
|
124124
LL | let _ = if let Some(x) = arg {
125125
| _____________^
@@ -131,7 +131,7 @@ LL | | };
131131
| |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
132132

133133
error: use Option::map_or_else instead of an if let/else
134-
--> $DIR/option_if_let_else.rs:70:13
134+
--> $DIR/option_if_let_else.rs:71:13
135135
|
136136
LL | let _ = if let Some(x) = arg {
137137
| _____________^
@@ -154,7 +154,7 @@ LL | }, |x| x * x * x * x);
154154
|
155155

156156
error: use Option::map_or instead of an if let/else
157-
--> $DIR/option_if_let_else.rs:99:13
157+
--> $DIR/option_if_let_else.rs:100:13
158158
|
159159
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
160160
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`

tests/ui/ref_option_ref.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#![allow(unused)]
2+
#![warn(clippy::ref_option_ref)]
3+
4+
// This lint is not tagged as run-rustfix because automatically
5+
// changing the type of a variable would also means changing
6+
// all usages of this variable to match and This is not handled
7+
// by this lint.
8+
9+
static THRESHOLD: i32 = 10;
10+
static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
11+
const CONST_THRESHOLD: &i32 = &10;
12+
const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD);
13+
14+
type RefOptRefU32<'a> = &'a Option<&'a u32>;
15+
type RefOptRef<'a, T> = &'a Option<&'a T>;
16+
17+
fn foo(data: &Option<&u32>) {}
18+
19+
fn bar(data: &u32) -> &Option<&u32> {
20+
&None
21+
}
22+
23+
struct StructRef<'a> {
24+
data: &'a Option<&'a u32>,
25+
}
26+
27+
struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
28+
29+
enum EnumRef<'a> {
30+
Variant1(u32),
31+
Variant2(&'a Option<&'a u32>),
32+
}
33+
34+
trait RefOptTrait {
35+
type A;
36+
fn foo(&self, _: Self::A);
37+
}
38+
39+
impl RefOptTrait for u32 {
40+
type A = &'static Option<&'static Self>;
41+
42+
fn foo(&self, _: Self::A) {}
43+
}
44+
45+
fn main() {
46+
let x: &Option<&u32> = &None;
47+
}

tests/ui/ref_option_ref.stderr

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
2+
--> $DIR/ref_option_ref.rs:10:23
3+
|
4+
LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
5+
| ^^^^^^^^^^^^^ help: try: `Option<&i32>`
6+
|
7+
= note: `-D clippy::ref-option-ref` implied by `-D warnings`
8+
9+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
10+
--> $DIR/ref_option_ref.rs:12:18
11+
|
12+
LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD);
13+
| ^^^^^^^^^^^^^ help: try: `Option<&i32>`
14+
15+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
16+
--> $DIR/ref_option_ref.rs:14:25
17+
|
18+
LL | type RefOptRefU32<'a> = &'a Option<&'a u32>;
19+
| ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
20+
21+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
22+
--> $DIR/ref_option_ref.rs:15:25
23+
|
24+
LL | type RefOptRef<'a, T> = &'a Option<&'a T>;
25+
| ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>`
26+
27+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
28+
--> $DIR/ref_option_ref.rs:17:14
29+
|
30+
LL | fn foo(data: &Option<&u32>) {}
31+
| ^^^^^^^^^^^^^ help: try: `Option<&u32>`
32+
33+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
34+
--> $DIR/ref_option_ref.rs:19:23
35+
|
36+
LL | fn bar(data: &u32) -> &Option<&u32> {
37+
| ^^^^^^^^^^^^^ help: try: `Option<&u32>`
38+
39+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
40+
--> $DIR/ref_option_ref.rs:24:11
41+
|
42+
LL | data: &'a Option<&'a u32>,
43+
| ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
44+
45+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
46+
--> $DIR/ref_option_ref.rs:27:32
47+
|
48+
LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
49+
| ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
50+
51+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
52+
--> $DIR/ref_option_ref.rs:31:14
53+
|
54+
LL | Variant2(&'a Option<&'a u32>),
55+
| ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
56+
57+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
58+
--> $DIR/ref_option_ref.rs:40:14
59+
|
60+
LL | type A = &'static Option<&'static Self>;
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>`
62+
63+
error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
64+
--> $DIR/ref_option_ref.rs:46:12
65+
|
66+
LL | let x: &Option<&u32> = &None;
67+
| ^^^^^^^^^^^^^ help: try: `Option<&u32>`
68+
69+
error: aborting due to 11 previous errors
70+

0 commit comments

Comments
 (0)