diff options
| author | y21 <30553356+y21@users.noreply.github.com> | 2023-12-30 03:15:38 +0100 |
|---|---|---|
| committer | y21 <30553356+y21@users.noreply.github.com> | 2024-01-03 19:40:47 +0100 |
| commit | 5960107415caf34a84fc8bb2f2fd303b7e358041 (patch) | |
| tree | f84b15dbc2271c8f2fe7ec56d2ac6c110644c070 /clippy_lints/src | |
| parent | 0153ca95ae588f0423f919e199370b6ff02b02c1 (diff) | |
| download | rust-5960107415caf34a84fc8bb2f2fd303b7e358041.tar.gz rust-5960107415caf34a84fc8bb2f2fd303b7e358041.zip | |
new lint: `option_as_ref_cloned`
Diffstat (limited to 'clippy_lints/src')
| -rw-r--r-- | clippy_lints/src/declared_lints.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/methods/mod.rs | 32 | ||||
| -rw-r--r-- | clippy_lints/src/methods/option_as_ref_cloned.rs | 24 |
3 files changed, 56 insertions, 1 deletions
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f6c9ffea9fc..20230106d53 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -410,6 +410,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::NO_EFFECT_REPLACE_INFO, crate::methods::OBFUSCATED_IF_ELSE_INFO, crate::methods::OK_EXPECT_INFO, + crate::methods::OPTION_AS_REF_CLONED_INFO, crate::methods::OPTION_AS_REF_DEREF_INFO, crate::methods::OPTION_FILTER_MAP_INFO, crate::methods::OPTION_MAP_OR_ERR_OK_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fbca641cfa3..cf6f6ad1a0f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -71,6 +71,7 @@ mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; mod open_options; +mod option_as_ref_cloned; mod option_as_ref_deref; mod option_map_or_err_ok; mod option_map_or_none; @@ -3887,6 +3888,31 @@ declare_clippy_lint! { "splitting a trimmed string at hard-coded newlines" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.as_ref().cloned()` and `.as_mut().cloned()` on `Option`s + /// + /// ### Why is this bad? + /// This can be written more concisely by cloning the `Option` directly. + /// + /// ### Example + /// ```no_run + /// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> { + /// bar.as_ref().cloned() + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> { + /// bar.clone() + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub OPTION_AS_REF_CLONED, + pedantic, + "cloning an `Option` via `as_ref().cloned()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4043,6 +4069,7 @@ impl_lint_pass!(Methods => [ ITER_FILTER_IS_OK, MANUAL_IS_VARIANT_AND, STR_SPLIT_AT_NEWLINE, + OPTION_AS_REF_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4290,7 +4317,10 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), + ("cloned", []) => { + cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv); + option_as_ref_cloned::check(cx, recv, span); + }, ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs new file mode 100644 index 00000000000..d7fec360fa2 --- /dev/null +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::{sym, Span}; + +use super::{method_call, OPTION_AS_REF_CLONED}; + +pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { + if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + { + span_lint_and_sugg( + cx, + OPTION_AS_REF_CLONED, + as_ref_ident_span.to(cloned_ident_span), + &format!("cloning an `Option<_>` using `.{method}().cloned()`"), + "this can be written more concisely by cloning the `Option<_>` directly", + "clone".into(), + Applicability::MachineApplicable, + ); + } +} |
