Auto merge of #12051 - y21:option_as_ref_cloned, r=dswij

new lint: `option_as_ref_cloned`

Closes #12009

Adds a new lint that looks for `.as_ref().cloned()` on `Option`s. That's the same as just `.clone()`-ing the option directly.

changelog: new lint: [`option_as_ref_cloned`]
This commit is contained in:
bors 2024-01-05 06:39:46 +00:00
commit ee8bfb7f7a
9 changed files with 138 additions and 3 deletions

View file

@ -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,

View file

@ -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) {

View file

@ -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,
);
}
}