Skip to content

Commit 55404f9

Browse files
committed
Suggest Option<&T> instead of &Option<T>
1 parent a81f1c8 commit 55404f9

File tree

7 files changed

+209
-0
lines changed

7 files changed

+209
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5814,6 +5814,7 @@ Released 2018-09-13
58145814
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
58155815
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
58165816
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
5817+
[`ref_option_sig`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_sig
58175818
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
58185819
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
58195820
[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
202202
crate::functions::MUST_USE_CANDIDATE_INFO,
203203
crate::functions::MUST_USE_UNIT_INFO,
204204
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
205+
crate::functions::REF_OPTION_SIG_INFO,
205206
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
206207
crate::functions::RESULT_LARGE_ERR_INFO,
207208
crate::functions::RESULT_UNIT_ERR_INFO,

clippy_lints/src/functions/mod.rs

+25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod impl_trait_in_params;
22
mod misnamed_getters;
33
mod must_use;
44
mod not_unsafe_ptr_arg_deref;
5+
mod ref_option_sig;
56
mod renamed_function_params;
67
mod result;
78
mod too_many_arguments;
@@ -399,6 +400,26 @@ declare_clippy_lint! {
399400
"renamed function parameters in trait implementation"
400401
}
401402

403+
declare_clippy_lint! {
404+
/// ### What it does
405+
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
406+
/// ### Why is this bad?
407+
/// More flexibility, better memory optimization, and more idiomatic Rust code.
408+
/// See [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE)
409+
/// ### Example
410+
/// ```no_run
411+
/// fn foo(a: &Option<String>) {}
412+
/// ```
413+
/// Use instead:
414+
/// ```no_run
415+
/// fn foo(a: Option<&String>) {}
416+
/// ```
417+
#[clippy::version = "1.82.0"]
418+
pub REF_OPTION_SIG,
419+
nursery,
420+
"function signature uses `&Option<T>` instead of `Option<&T>`"
421+
}
422+
402423
pub struct Functions {
403424
too_many_arguments_threshold: u64,
404425
too_many_lines_threshold: u64,
@@ -437,6 +458,7 @@ impl_lint_pass!(Functions => [
437458
MISNAMED_GETTERS,
438459
IMPL_TRAIT_IN_PARAMS,
439460
RENAMED_FUNCTION_PARAMS,
461+
REF_OPTION_SIG,
440462
]);
441463

442464
impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -460,13 +482,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
460482
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
461483
must_use::check_item(cx, item);
462484
result::check_item(cx, item, self.large_error_threshold);
485+
ref_option_sig::check_item(cx, item);
463486
}
464487

465488
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
466489
must_use::check_impl_item(cx, item);
467490
result::check_impl_item(cx, item, self.large_error_threshold);
468491
impl_trait_in_params::check_impl_item(cx, item);
469492
renamed_function_params::check_impl_item(cx, item, &self.trait_ids);
493+
ref_option_sig::check_impl_item(cx, item);
470494
}
471495

472496
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
@@ -475,5 +499,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
475499
must_use::check_trait_item(cx, item);
476500
result::check_trait_item(cx, item, self.large_error_threshold);
477501
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
502+
ref_option_sig::check_trait_item(cx, item);
478503
}
479504
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::functions::hir::GenericArg;
2+
use crate::functions::REF_OPTION_SIG;
3+
use clippy_utils::diagnostics::span_lint_and_then;
4+
use clippy_utils::source::snippet;
5+
use rustc_errors::Applicability;
6+
use rustc_hir as hir;
7+
use rustc_hir::{GenericArgs, ImplItem, MutTy, QPath, TraitItem, TyKind};
8+
use rustc_lint::LateContext;
9+
use rustc_span::{sym, Span};
10+
11+
fn check_fn_sig(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, sig_span: Span) {
12+
let mut fixes = Vec::new();
13+
for param in decl.inputs {
14+
if let TyKind::Ref(_, MutTy { ty, .. }) = param.kind
15+
// TODO: is this the right way to check if ty is an Option?
16+
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
17+
&& path.segments.len() == 1
18+
&& let seg = &path.segments[0]
19+
&& seg.ident.name == sym::Option
20+
// check if option contains a regular type, not a reference
21+
// TODO: Should this instead just check that opt_ty is a TyKind::Path?
22+
&& let Some(GenericArgs { args: [GenericArg::Type(opt_ty)], .. }) = seg.args
23+
&& !matches!(opt_ty.kind, TyKind::Ref(..))
24+
{
25+
// FIXME: Should this use the Option path from the original type?
26+
// FIXME: Should reference be added in some other way to the snippet?
27+
fixes.push((param.span, format!("Option<&{}>", snippet(cx, opt_ty.span, ".."))));
28+
}
29+
}
30+
31+
if !fixes.is_empty() {
32+
span_lint_and_then(
33+
cx,
34+
REF_OPTION_SIG,
35+
sig_span,
36+
"it is more idiomatic to use `Option<&T>` instead of `&Option<T>`",
37+
|diag| {
38+
diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified);
39+
},
40+
);
41+
}
42+
}
43+
44+
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
45+
if let hir::ItemKind::Fn(ref sig, _, _) = item.kind {
46+
check_fn_sig(cx, sig.decl, sig.span);
47+
}
48+
}
49+
50+
pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
51+
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
52+
check_fn_sig(cx, sig.decl, sig.span);
53+
}
54+
}
55+
56+
pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>) {
57+
if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind {
58+
check_fn_sig(cx, sig.decl, sig.span);
59+
}
60+
}

tests/ui/ref_option_sig.fixed

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![allow(unused, clippy::all)]
2+
#![warn(clippy::ref_option_sig)]
3+
4+
fn main() {}
5+
6+
fn opt_u8(a: Option<&u8>) {}
7+
fn opt_gen<T>(a: Option<&T>) {}
8+
fn opt_string(a: Option<&String>) {}
9+
fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
10+
11+
pub fn pub_opt_string(a: Option<&String>) {}
12+
13+
pub trait HasOpt {
14+
fn trait_opt(&self, a: Option<&Vec<u8>>);
15+
}
16+
17+
trait PrivateHasOpt {
18+
fn private_trait_opt(&self, a: Option<&Option<String>>);
19+
}
20+
21+
pub struct UnitOpt;
22+
23+
impl UnitOpt {
24+
pub fn opt_params(&self, a: Option<&()>) {}
25+
}

tests/ui/ref_option_sig.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![allow(unused, clippy::all)]
2+
#![warn(clippy::ref_option_sig)]
3+
4+
fn main() {}
5+
6+
fn opt_u8(a: &Option<u8>) {}
7+
fn opt_gen<T>(a: &Option<T>) {}
8+
fn opt_string(a: &Option<String>) {}
9+
fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
10+
11+
pub fn pub_opt_string(a: &Option<String>) {}
12+
13+
pub trait HasOpt {
14+
fn trait_opt(&self, a: &Option<Vec<u8>>);
15+
}
16+
17+
trait PrivateHasOpt {
18+
fn private_trait_opt(&self, a: &Option<Option<String>>);
19+
}
20+
21+
pub struct UnitOpt;
22+
23+
impl UnitOpt {
24+
pub fn opt_params(&self, a: &Option<()>) {}
25+
}

tests/ui/ref_option_sig.stderr

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
2+
--> tests/ui/ref_option_sig.rs:6:1
3+
|
4+
LL | fn opt_u8(a: &Option<u8>) {}
5+
| ^^^^^^^^^^^^^-----------^
6+
| |
7+
| help: change this to: `Option<&u8>`
8+
|
9+
= note: `-D clippy::ref-option-sig` implied by `-D warnings`
10+
= help: to override `-D warnings` add `#[allow(clippy::ref_option_sig)]`
11+
12+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
13+
--> tests/ui/ref_option_sig.rs:7:1
14+
|
15+
LL | fn opt_gen<T>(a: &Option<T>) {}
16+
| ^^^^^^^^^^^^^^^^^----------^
17+
| |
18+
| help: change this to: `Option<&T>`
19+
20+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
21+
--> tests/ui/ref_option_sig.rs:8:1
22+
|
23+
LL | fn opt_string(a: &Option<String>) {}
24+
| ^^^^^^^^^^^^^^^^^---------------^
25+
| |
26+
| help: change this to: `Option<&String>`
27+
28+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
29+
--> tests/ui/ref_option_sig.rs:9:1
30+
|
31+
LL | fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33+
|
34+
help: change this to
35+
|
36+
LL | fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
37+
| ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
38+
39+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
40+
--> tests/ui/ref_option_sig.rs:11:1
41+
|
42+
LL | pub fn pub_opt_string(a: &Option<String>) {}
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^
44+
| |
45+
| help: change this to: `Option<&String>`
46+
47+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
48+
--> tests/ui/ref_option_sig.rs:14:5
49+
|
50+
LL | fn trait_opt(&self, a: &Option<Vec<u8>>);
51+
| ^^^^^^^^^^^^^^^^^^^^^^^----------------^^
52+
| |
53+
| help: change this to: `Option<&Vec<u8>>`
54+
55+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
56+
--> tests/ui/ref_option_sig.rs:18:5
57+
|
58+
LL | fn private_trait_opt(&self, a: &Option<Option<String>>);
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------------^^
60+
| |
61+
| help: change this to: `Option<&Option<String>>`
62+
63+
error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
64+
--> tests/ui/ref_option_sig.rs:24:5
65+
|
66+
LL | pub fn opt_params(&self, a: &Option<()>) {}
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^
68+
| |
69+
| help: change this to: `Option<&()>`
70+
71+
error: aborting due to 8 previous errors
72+

0 commit comments

Comments
 (0)