]>
git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/map_clone.rs
5 use utils
::{is_adjusted
, match_qpath
, match_trait_method
, match_type
, remove_blocks
, paths
, snippet
,
6 span_help_and_lint
, walk_ptrs_ty
, walk_ptrs_ty_depth
, iter_input_pats
};
8 /// **What it does:** Checks for mapping `clone()` over an iterator.
10 /// **Why is this bad?** It makes the code less readable than using the
11 /// `.cloned()` adapter.
13 /// **Known problems:** None.
17 /// x.map(|e| e.clone());
22 "using `.map(|x| x.clone())` to clone an iterator or option's contents"
25 #[derive(Copy, Clone)]
28 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for Pass
{
29 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, expr
: &'tcx Expr
) {
31 if let ExprMethodCall(ref method
, _
, ref args
) = expr
.node
{
32 if method
.name
== "map" && args
.len() == 2 {
34 ExprClosure(_
, ref decl
, closure_eid
, _
, _
) => {
35 let body
= cx
.tcx
.hir
.body(closure_eid
);
36 let closure_expr
= remove_blocks(&body
.value
);
37 let ty
= cx
.tables
.pat_ty(&body
.arguments
[0].pat
);
39 // nothing special in the argument, besides reference bindings
40 // (e.g. .map(|&x| x) )
41 let Some(first_arg
) = iter_input_pats(decl
, body
).next(),
42 let Some(arg_ident
) = get_arg_name(&first_arg
.pat
),
43 // the method is being called on a known type (option or iterator)
44 let Some(type_name
) = get_type_name(cx
, expr
, &args
[0])
46 // look for derefs, for .map(|x| *x)
47 if only_derefs(cx
, &*closure_expr
, arg_ident
) &&
48 // .cloned() only removes one level of indirection, don't lint on more
49 walk_ptrs_ty_depth(cx
.tables
.pat_ty(&first_arg
.pat
)).1 == 1
51 // the argument is not an &mut T
52 if let ty
::TyRef(_
, tam
) = ty
.sty
{
53 if tam
.mutbl
== MutImmutable
{
54 span_help_and_lint(cx
, MAP_CLONE
, expr
.span
, &format
!(
55 "you seem to be using .map() to clone the contents of an {}, consider \
56 using `.cloned()`", type_name
),
57 &format
!("try\n{}.cloned()", snippet(cx
, args
[0].span
, "..")));
61 // explicit clone() calls ( .map(|x| x.clone()) )
62 else if let ExprMethodCall(ref clone_call
, _
, ref clone_args
) = closure_expr
.node
{
63 if clone_call
.name
== "clone" &&
64 clone_args
.len() == 1 &&
65 match_trait_method(cx
, closure_expr
, &paths
::CLONE_TRAIT
) &&
66 expr_eq_name(&clone_args
[0], arg_ident
)
68 span_help_and_lint(cx
, MAP_CLONE
, expr
.span
, &format
!(
69 "you seem to be using .map() to clone the contents of an {}, consider \
70 using `.cloned()`", type_name
),
71 &format
!("try\n{}.cloned()", snippet(cx
, args
[0].span
, "..")));
76 ExprPath(ref path
) => {
77 if match_qpath(path
, &paths
::CLONE
) {
78 let type_name
= get_type_name(cx
, expr
, &args
[0]).unwrap_or("_");
84 "you seem to be using .map() to clone the contents of an \
85 {}, consider using `.cloned()`",
88 &format
!("try\n{}.cloned()", snippet(cx
, args
[0].span
, "..")),
99 fn expr_eq_name(expr
: &Expr
, id
: ast
::Name
) -> bool
{
101 ExprPath(QPath
::Resolved(None
, ref path
)) => {
105 parameters
: PathParameters
::none(),
108 !path
.is_global() && path
.segments
[..] == arg_segment
114 fn get_type_name(cx
: &LateContext
, expr
: &Expr
, arg
: &Expr
) -> Option
<&'
static str> {
115 if match_trait_method(cx
, expr
, &paths
::ITERATOR
) {
117 } else if match_type(cx
, walk_ptrs_ty(cx
.tables
.expr_ty(arg
)), &paths
::OPTION
) {
124 fn get_arg_name(pat
: &Pat
) -> Option
<ast
::Name
> {
126 PatKind
::Binding(_
, _
, name
, None
) => Some(name
.node
),
127 PatKind
::Ref(ref subpat
, _
) => get_arg_name(subpat
),
132 fn only_derefs(cx
: &LateContext
, expr
: &Expr
, id
: ast
::Name
) -> bool
{
134 ExprUnary(UnDeref
, ref subexpr
) if !is_adjusted(cx
, subexpr
) => only_derefs(cx
, subexpr
, id
),
135 _
=> expr_eq_name(expr
, id
),
139 impl LintPass
for Pass
{
140 fn get_lints(&self) -> LintArray
{
141 lint_array
!(MAP_CLONE
)