1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::is_trait_method
;
3 use clippy_utils
::remove_blocks
;
4 use clippy_utils
::source
::snippet_with_applicability
;
5 use clippy_utils
::ty
::{is_copy, is_type_diagnostic_item}
;
6 use if_chain
::if_chain
;
7 use rustc_errors
::Applicability
;
9 use rustc_lint
::{LateContext, LateLintPass}
;
10 use rustc_middle
::mir
::Mutability
;
12 use rustc_middle
::ty
::adjustment
::Adjust
;
13 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
14 use rustc_span
::symbol
::Ident
;
15 use rustc_span
::{sym, Span}
;
17 declare_clippy_lint
! {
19 /// Checks for usage of `map(|x| x.clone())` or
20 /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
21 /// and suggests `cloned()` or `copied()` instead
23 /// ### Why is this bad?
24 /// Readability, this can be written more concisely
28 /// let x = vec![42, 43];
30 /// let z = y.map(|i| *i);
33 /// The correct use would be:
36 /// let x = vec![42, 43];
38 /// let z = y.cloned();
42 "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
45 declare_lint_pass
!(MapClone
=> [MAP_CLONE
]);
47 impl<'tcx
> LateLintPass
<'tcx
> for MapClone
{
48 fn check_expr(&mut self, cx
: &LateContext
<'_
>, e
: &hir
::Expr
<'_
>) {
49 if e
.span
.from_expansion() {
54 if let hir
::ExprKind
::MethodCall(method
, _
, args
, _
) = e
.kind
;
56 if method
.ident
.name
== sym
::map
;
57 let ty
= cx
.typeck_results().expr_ty(&args
[0]);
58 if is_type_diagnostic_item(cx
, ty
, sym
::option_type
) || is_trait_method(cx
, e
, sym
::Iterator
);
59 if let hir
::ExprKind
::Closure(_
, _
, body_id
, _
, _
) = args
[1].kind
;
61 let closure_body
= cx
.tcx
.hir().body(body_id
);
62 let closure_expr
= remove_blocks(&closure_body
.value
);
63 match closure_body
.params
[0].pat
.kind
{
64 hir
::PatKind
::Ref(inner
, hir
::Mutability
::Not
) => if let hir
::PatKind
::Binding(
65 hir
::BindingAnnotation
::Unannotated
, .., name
, None
67 if ident_eq(name
, closure_expr
) {
68 lint(cx
, e
.span
, args
[0].span
, true);
71 hir
::PatKind
::Binding(hir
::BindingAnnotation
::Unannotated
, .., name
, None
) => {
72 match closure_expr
.kind
{
73 hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, inner
) => {
74 if ident_eq(name
, inner
) {
75 if let ty
::Ref(.., Mutability
::Not
) = cx
.typeck_results().expr_ty(inner
).kind() {
76 lint(cx
, e
.span
, args
[0].span
, true);
80 hir
::ExprKind
::MethodCall(method
, _
, [obj
], _
) => if_chain
! {
81 if ident_eq(name
, obj
) && method
.ident
.name
== sym
::clone
;
82 if let Some(fn_id
) = cx
.typeck_results().type_dependent_def_id(closure_expr
.hir_id
);
83 if let Some(trait_id
) = cx
.tcx
.trait_of_item(fn_id
);
84 if cx
.tcx
.lang_items().clone_trait().map_or(false, |id
| id
== trait_id
);
86 if !cx
.typeck_results().expr_adjustments(obj
).iter()
87 .any(|a
| matches
!(a
.kind
, Adjust
::Deref(Some(..))));
89 let obj_ty
= cx
.typeck_results().expr_ty(obj
);
90 if let ty
::Ref(_
, ty
, mutability
) = obj_ty
.kind() {
91 if matches
!(mutability
, Mutability
::Not
) {
92 let copy
= is_copy(cx
, ty
);
93 lint(cx
, e
.span
, args
[0].span
, copy
);
96 lint_needless_cloning(cx
, e
.span
, args
[0].span
);
110 fn ident_eq(name
: Ident
, path
: &hir
::Expr
<'_
>) -> bool
{
111 if let hir
::ExprKind
::Path(hir
::QPath
::Resolved(None
, path
)) = path
.kind
{
112 path
.segments
.len() == 1 && path
.segments
[0].ident
== name
118 fn lint_needless_cloning(cx
: &LateContext
<'_
>, root
: Span
, receiver
: Span
) {
122 root
.trim_start(receiver
).unwrap(),
123 "you are needlessly cloning iterator elements",
124 "remove the `map` call",
126 Applicability
::MachineApplicable
,
130 fn lint(cx
: &LateContext
<'_
>, replace
: Span
, root
: Span
, copied
: bool
) {
131 let mut applicability
= Applicability
::MachineApplicable
;
137 "you are using an explicit closure for copying elements",
138 "consider calling the dedicated `copied` method",
141 snippet_with_applicability(cx
, root
, "..", &mut applicability
)
150 "you are using an explicit closure for cloning elements",
151 "consider calling the dedicated `cloned` method",
154 snippet_with_applicability(cx
, root
, "..", &mut applicability
)