Skip to content

Commit 7596503

Browse files
authored
Merge pull request #3251 from rust-lang-nursery/relicense-rewrite
[do not merge] Relicensing rewrite
2 parents 87f6d9e + 8407957 commit 7596503

9 files changed

+370
-463
lines changed

clippy_lints/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
328328
reg.register_late_lint_pass(box strings::StringAdd);
329329
reg.register_early_lint_pass(box returns::ReturnPass);
330330
reg.register_late_lint_pass(box methods::Pass);
331+
reg.register_late_lint_pass(box map_clone::Pass);
331332
reg.register_late_lint_pass(box shadow::Pass);
332333
reg.register_late_lint_pass(box types::LetPass);
333334
reg.register_late_lint_pass(box types::UnitCmp);
@@ -346,7 +347,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
346347
reg.register_late_lint_pass(box needless_borrow::NeedlessBorrow);
347348
reg.register_late_lint_pass(box needless_borrowed_ref::NeedlessBorrowedRef);
348349
reg.register_late_lint_pass(box no_effect::Pass);
349-
reg.register_late_lint_pass(box map_clone::Pass);
350350
reg.register_late_lint_pass(box temporary_assignment::Pass);
351351
reg.register_late_lint_pass(box transmute::Transmute);
352352
reg.register_late_lint_pass(
@@ -792,6 +792,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
792792
returns::NEEDLESS_RETURN,
793793
strings::STRING_LIT_AS_BYTES,
794794
types::FN_TO_NUMERIC_CAST,
795+
types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
795796
types::IMPLICIT_HASHER,
796797
types::LET_UNIT_VALUE,
797798
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
@@ -921,7 +922,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
921922
transmute::WRONG_TRANSMUTE,
922923
types::ABSURD_EXTREME_COMPARISONS,
923924
types::CAST_PTR_ALIGNMENT,
924-
types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
925925
types::UNIT_CMP,
926926
unicode::ZERO_WIDTH_SPACE,
927927
unused_io_amount::UNUSED_IO_AMOUNT,

clippy_lints/src/map_clone.rs

+71-113
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,98 @@
1+
use crate::rustc::hir;
12
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
23
use crate::rustc::{declare_tool_lint, lint_array};
4+
use crate::syntax::source_map::Span;
5+
use crate::utils::paths;
6+
use crate::utils::{
7+
in_macro, match_trait_method, match_type,
8+
remove_blocks, snippet,
9+
span_lint_and_sugg,
10+
};
311
use if_chain::if_chain;
4-
use crate::rustc::hir::*;
5-
use crate::rustc::ty;
6-
use crate::syntax::ast;
7-
use crate::utils::{get_arg_ident, is_adjusted, iter_input_pats, match_qpath, match_trait_method, match_type,
8-
paths, remove_blocks, snippet, span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq};
12+
use crate::syntax::ast::Ident;
913

10-
/// **What it does:** Checks for mapping `clone()` over an iterator.
14+
#[derive(Clone)]
15+
pub struct Pass;
16+
17+
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
18+
/// `iterator.cloned()` instead
1119
///
12-
/// **Why is this bad?** It makes the code less readable than using the
13-
/// `.cloned()` adapter.
20+
/// **Why is this bad?** Readability, this can be written more concisely
1421
///
15-
/// **Known problems:** Sometimes `.cloned()` requires stricter trait
16-
/// bound than `.map(|e| e.clone())` (which works because of the coercion).
17-
/// See [#498](https://github.com/rust-lang-nursery/rust-clippy/issues/498).
22+
/// **Known problems:** None.
1823
///
1924
/// **Example:**
25+
///
26+
/// ```rust
27+
/// let x = vec![42, 43];
28+
/// let y = x.iter();
29+
/// let z = y.map(|i| *i);
30+
/// ```
31+
///
32+
/// The correct use would be:
33+
///
2034
/// ```rust
21-
/// x.map(|e| e.clone());
35+
/// let x = vec![42, 43];
36+
/// let y = x.iter();
37+
/// let z = y.cloned();
2238
/// ```
2339
declare_clippy_lint! {
2440
pub MAP_CLONE,
2541
style,
26-
"using `.map(|x| x.clone())` to clone an iterator or option's contents"
42+
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
2743
}
2844

29-
#[derive(Copy, Clone)]
30-
pub struct Pass;
45+
impl LintPass for Pass {
46+
fn get_lints(&self) -> LintArray {
47+
lint_array!(MAP_CLONE)
48+
}
49+
}
3150

3251
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
33-
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
34-
// call to .map()
35-
if let ExprKind::MethodCall(ref method, _, ref args) = expr.node {
36-
if method.ident.name == "map" && args.len() == 2 {
37-
match args[1].node {
38-
ExprKind::Closure(_, ref decl, closure_eid, _, _) => {
39-
let body = cx.tcx.hir.body(closure_eid);
40-
let closure_expr = remove_blocks(&body.value);
41-
if_chain! {
42-
// nothing special in the argument, besides reference bindings
43-
// (e.g. .map(|&x| x) )
44-
if let Some(first_arg) = iter_input_pats(decl, body).next();
45-
if let Some(arg_ident) = get_arg_ident(&first_arg.pat);
46-
// the method is being called on a known type (option or iterator)
47-
if let Some(type_name) = get_type_name(cx, expr, &args[0]);
48-
then {
49-
// We know that body.arguments is not empty at this point
50-
let ty = cx.tables.pat_ty(&body.arguments[0].pat);
51-
// look for derefs, for .map(|x| *x)
52-
if only_derefs(cx, &*closure_expr, arg_ident) &&
53-
// .cloned() only removes one level of indirection, don't lint on more
54-
walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
55-
{
56-
// the argument is not an &mut T
57-
if let ty::Ref(_, _, mutbl) = ty.sty {
58-
if mutbl == MutImmutable {
59-
span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
60-
"you seem to be using .map() to clone the contents of an {}, consider \
61-
using `.cloned()`", type_name),
62-
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
63-
}
64-
}
65-
}
66-
// explicit clone() calls ( .map(|x| x.clone()) )
67-
else if let ExprKind::MethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
68-
if clone_call.ident.name == "clone" &&
69-
clone_args.len() == 1 &&
70-
match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
71-
expr_eq_name(cx, &clone_args[0], arg_ident)
72-
{
73-
span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
74-
"you seem to be using .map() to clone the contents of an {}, consider \
75-
using `.cloned()`", type_name),
76-
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
77-
}
78-
}
79-
}
80-
}
52+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
53+
if in_macro(e.span) {
54+
return;
55+
}
56+
57+
if_chain! {
58+
if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node;
59+
if args.len() == 2;
60+
if method.ident.as_str() == "map";
61+
let ty = cx.tables.expr_ty(&args[0]);
62+
if match_type(cx, ty, &paths::OPTION) || match_trait_method(cx, e, &paths::ITERATOR);
63+
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].node;
64+
let closure_body = cx.tcx.hir.body(body_id);
65+
let closure_expr = remove_blocks(&closure_body.value);
66+
then {
67+
match closure_body.arguments[0].pat.node {
68+
hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) = inner.node {
69+
lint(cx, e.span, args[0].span, name, closure_expr);
8170
},
82-
ExprKind::Path(ref path) => if match_qpath(path, &paths::CLONE) {
83-
let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
84-
span_help_and_lint(
85-
cx,
86-
MAP_CLONE,
87-
expr.span,
88-
&format!(
89-
"you seem to be using .map() to clone the contents of an \
90-
{}, consider using `.cloned()`",
91-
type_name
92-
),
93-
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
94-
);
71+
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => match closure_expr.node {
72+
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => lint(cx, e.span, args[0].span, name, inner),
73+
hir::ExprKind::MethodCall(ref method, _, ref obj) => if method.ident.as_str() == "clone" && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
74+
lint(cx, e.span, args[0].span, name, &obj[0]);
75+
}
76+
_ => {},
9577
},
96-
_ => (),
78+
_ => {},
9779
}
9880
}
9981
}
10082
}
10183
}
10284

103-
fn expr_eq_name(cx: &LateContext<'_, '_>, expr: &Expr, id: ast::Ident) -> bool {
104-
match expr.node {
105-
ExprKind::Path(QPath::Resolved(None, ref path)) => {
106-
let arg_segment = [
107-
PathSegment {
108-
ident: id,
109-
args: None,
110-
infer_types: true,
111-
},
112-
];
113-
!path.is_global() && SpanlessEq::new(cx).eq_path_segments(&path.segments[..], &arg_segment)
114-
},
115-
_ => false,
116-
}
117-
}
118-
119-
fn get_type_name(cx: &LateContext<'_, '_>, expr: &Expr, arg: &Expr) -> Option<&'static str> {
120-
if match_trait_method(cx, expr, &paths::ITERATOR) {
121-
Some("iterator")
122-
} else if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(arg)), &paths::OPTION) {
123-
Some("Option")
124-
} else {
125-
None
126-
}
127-
}
128-
129-
fn only_derefs(cx: &LateContext<'_, '_>, expr: &Expr, id: ast::Ident) -> bool {
130-
match expr.node {
131-
ExprKind::Unary(UnDeref, ref subexpr) if !is_adjusted(cx, subexpr) => only_derefs(cx, subexpr, id),
132-
_ => expr_eq_name(cx, expr, id),
133-
}
134-
}
135-
136-
impl LintPass for Pass {
137-
fn get_lints(&self) -> LintArray {
138-
lint_array!(MAP_CLONE)
85+
fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, name: Ident, path: &hir::Expr) {
86+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.node {
87+
if path.segments.len() == 1 && path.segments[0].ident == name {
88+
span_lint_and_sugg(
89+
cx,
90+
MAP_CLONE,
91+
replace,
92+
"You are using an explicit closure for cloning elements",
93+
"Consider calling the dedicated `cloned` method",
94+
format!("{}.cloned()", snippet(cx, root, "..")),
95+
)
96+
}
13997
}
14098
}

0 commit comments

Comments
 (0)