Skip to content

Commit 2be6245

Browse files
committed
Duplicate ptr_arg's suggestion logic
1 parent bf97cd0 commit 2be6245

File tree

6 files changed

+183
-72
lines changed

6 files changed

+183
-72
lines changed

clippy_lints/src/needless_pass_by_value.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ use syntax::ast::NodeId;
99
use syntax_pos::Span;
1010
use syntax::errors::DiagnosticBuilder;
1111
use utils::{get_trait_def_id, implements_trait, in_macro, is_copy, is_self, match_type, multispan_sugg, paths,
12-
snippet, span_lint_and_then};
12+
snippet, snippet_opt, span_lint_and_then};
13+
use utils::ptr::get_spans;
1314
use std::collections::{HashMap, HashSet};
15+
use std::borrow::Cow;
1416

1517
/// **What it does:** Checks for functions taking arguments by value, but not
1618
/// consuming them in its
@@ -109,7 +111,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
109111
let fn_sig = cx.tcx.fn_sig(fn_def_id);
110112
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
111113

112-
for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {
114+
for (idx, ((input, &ty), arg)) in decl.inputs
115+
.iter()
116+
.zip(fn_sig.inputs())
117+
.zip(&body.arguments)
118+
.enumerate()
119+
{
113120
// * Exclude a type that is specifically bounded by `Borrow`.
114121
// * Exclude a type whose reference also fulfills its bound.
115122
// (e.g. `std::borrow::Borrow`, `serde::Serialize`)
@@ -152,6 +159,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
152159
let deref_span = spans_need_deref.get(&canonical_id);
153160
if_let_chain! {[
154161
match_type(cx, ty, &paths::VEC),
162+
let Some(clone_spans) =
163+
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]),
155164
let TyPath(QPath::Resolved(_, ref path)) = input.node,
156165
let Some(elem_ty) = path.segments.iter()
157166
.find(|seg| seg.name == "Vec")
@@ -162,14 +171,44 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
162171
db.span_suggestion(input.span,
163172
"consider changing the type to",
164173
slice_ty);
174+
175+
for (span, suggestion) in clone_spans {
176+
db.span_suggestion(
177+
span,
178+
&snippet_opt(cx, span)
179+
.map_or(
180+
"change the call to".into(),
181+
|x| Cow::from(format!("change `{}` to", x)),
182+
),
183+
suggestion.into()
184+
);
185+
}
186+
187+
// cannot be destructured, no need for `*` suggestion
165188
assert!(deref_span.is_none());
166-
return; // `Vec` cannot be destructured - no need for `*` suggestion
189+
return;
167190
}}
168191

169192
if match_type(cx, ty, &paths::STRING) {
170-
db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());
171-
assert!(deref_span.is_none());
172-
return; // ditto
193+
if let Some(clone_spans) =
194+
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
195+
db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());
196+
197+
for (span, suggestion) in clone_spans {
198+
db.span_suggestion(
199+
span,
200+
&snippet_opt(cx, span)
201+
.map_or(
202+
"change the call to".into(),
203+
|x| Cow::from(format!("change `{}` to", x))
204+
),
205+
suggestion.into(),
206+
);
207+
}
208+
209+
assert!(deref_span.is_none());
210+
return;
211+
}
173212
}
174213

175214
let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

clippy_lints/src/ptr.rs

Lines changed: 5 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
33
use std::borrow::Cow;
44
use rustc::hir::*;
5-
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
65
use rustc::hir::map::NodeItem;
76
use rustc::lint::*;
87
use rustc::ty;
9-
use syntax::ast::{Name, NodeId};
8+
use syntax::ast::NodeId;
109
use syntax::codemap::Span;
1110
use syntax_pos::MultiSpan;
12-
use utils::{get_pat_name, match_qpath, match_type, match_var, paths,
13-
snippet, snippet_opt, span_lint, span_lint_and_then,
11+
use utils::{match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_then,
1412
walk_ptrs_hir_ty};
13+
use utils::ptr::get_spans;
1514

1615
/// **What it does:** This lint checks for function arguments of type `&String`
1716
/// or `&Vec` unless the references are mutable. It will also suggest you
@@ -164,7 +163,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
164163
], {
165164
ty_snippet = snippet_opt(cx, parameters.types[0].span);
166165
});
167-
if let Ok(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
166+
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
168167
span_lint_and_then(
169168
cx,
170169
PTR_ARG,
@@ -187,7 +186,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
187186
);
188187
}
189188
} else if match_type(cx, ty, &paths::STRING) {
190-
if let Ok(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
189+
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
191190
span_lint_and_then(
192191
cx,
193192
PTR_ARG,
@@ -234,66 +233,6 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
234233
}
235234
}
236235

237-
fn get_spans(cx: &LateContext, opt_body_id: Option<BodyId>, idx: usize, replacements: &'static [(&'static str, &'static str)]) -> Result<Vec<(Span, Cow<'static, str>)>, ()> {
238-
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir.body(id)) {
239-
get_binding_name(&body.arguments[idx]).map_or_else(|| Ok(vec![]),
240-
|name| extract_clone_suggestions(cx, name, replacements, body))
241-
} else {
242-
Ok(vec![])
243-
}
244-
}
245-
246-
fn extract_clone_suggestions<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, name: Name, replace: &'static [(&'static str, &'static str)], body: &'tcx Body) -> Result<Vec<(Span, Cow<'static, str>)>, ()> {
247-
let mut visitor = PtrCloneVisitor {
248-
cx,
249-
name,
250-
replace,
251-
spans: vec![],
252-
abort: false,
253-
};
254-
visitor.visit_body(body);
255-
if visitor.abort { Err(()) } else { Ok(visitor.spans) }
256-
}
257-
258-
struct PtrCloneVisitor<'a, 'tcx: 'a> {
259-
cx: &'a LateContext<'a, 'tcx>,
260-
name: Name,
261-
replace: &'static [(&'static str, &'static str)],
262-
spans: Vec<(Span, Cow<'static, str>)>,
263-
abort: bool,
264-
}
265-
266-
impl<'a, 'tcx: 'a> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
267-
fn visit_expr(&mut self, expr: &'tcx Expr) {
268-
if self.abort { return; }
269-
if let ExprMethodCall(ref seg, _, ref args) = expr.node {
270-
if args.len() == 1 && match_var(&args[0], self.name) {
271-
if seg.name == "capacity" {
272-
self.abort = true;
273-
return;
274-
}
275-
for &(fn_name, suffix) in self.replace {
276-
if seg.name == fn_name {
277-
self.spans.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
278-
return;
279-
}
280-
}
281-
}
282-
return;
283-
}
284-
walk_expr(self, expr);
285-
}
286-
287-
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
288-
NestedVisitorMap::None
289-
}
290-
291-
}
292-
293-
fn get_binding_name(arg: &Arg) -> Option<Name> {
294-
get_pat_name(&arg.pat)
295-
}
296-
297236
fn get_rptr_lm(ty: &Ty) -> Option<(&Lifetime, Mutability, Span)> {
298237
if let Ty_::TyRptr(ref lt, ref m) = ty.node {
299238
Some((lt, m.mutbl, ty.span))

clippy_lints/src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod sugg;
3232
pub mod inspector;
3333
pub mod internal_lints;
3434
pub mod author;
35+
pub mod ptr;
3536
pub use self::hir_utils::{SpanlessEq, SpanlessHash};
3637

3738
pub type MethodArgs = HirVec<P<Expr>>;

clippy_lints/src/utils/ptr.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::borrow::Cow;
2+
use rustc::hir::*;
3+
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
4+
use rustc::lint::LateContext;
5+
use syntax::ast::Name;
6+
use syntax::codemap::Span;
7+
use utils::{get_pat_name, match_var, snippet};
8+
9+
pub fn get_spans(
10+
cx: &LateContext,
11+
opt_body_id: Option<BodyId>,
12+
idx: usize,
13+
replacements: &'static [(&'static str, &'static str)],
14+
) -> Option<Vec<(Span, Cow<'static, str>)>> {
15+
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir.body(id)) {
16+
get_binding_name(&body.arguments[idx])
17+
.map_or_else(|| Some(vec![]), |name| extract_clone_suggestions(cx, name, replacements, body))
18+
} else {
19+
Some(vec![])
20+
}
21+
}
22+
23+
fn extract_clone_suggestions<'a, 'tcx: 'a>(
24+
cx: &LateContext<'a, 'tcx>,
25+
name: Name,
26+
replace: &'static [(&'static str, &'static str)],
27+
body: &'tcx Body,
28+
) -> Option<Vec<(Span, Cow<'static, str>)>> {
29+
let mut visitor = PtrCloneVisitor {
30+
cx,
31+
name,
32+
replace,
33+
spans: vec![],
34+
abort: false,
35+
};
36+
visitor.visit_body(body);
37+
if visitor.abort {
38+
None
39+
} else {
40+
Some(visitor.spans)
41+
}
42+
}
43+
44+
struct PtrCloneVisitor<'a, 'tcx: 'a> {
45+
cx: &'a LateContext<'a, 'tcx>,
46+
name: Name,
47+
replace: &'static [(&'static str, &'static str)],
48+
spans: Vec<(Span, Cow<'static, str>)>,
49+
abort: bool,
50+
}
51+
52+
impl<'a, 'tcx: 'a> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
53+
fn visit_expr(&mut self, expr: &'tcx Expr) {
54+
if self.abort {
55+
return;
56+
}
57+
if let ExprMethodCall(ref seg, _, ref args) = expr.node {
58+
if args.len() == 1 && match_var(&args[0], self.name) {
59+
if seg.name == "capacity" {
60+
self.abort = true;
61+
return;
62+
}
63+
for &(fn_name, suffix) in self.replace {
64+
if seg.name == fn_name {
65+
self.spans
66+
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
67+
return;
68+
}
69+
}
70+
}
71+
return;
72+
}
73+
walk_expr(self, expr);
74+
}
75+
76+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
77+
NestedVisitorMap::None
78+
}
79+
}
80+
81+
fn get_binding_name(arg: &Arg) -> Option<Name> {
82+
get_pat_name(&arg.pat)
83+
}

tests/ui/needless_pass_by_value.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,11 @@ impl Serialize for i32 {}
7272

7373
fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
7474

75+
fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
76+
s.capacity();
77+
let _ = t.clone();
78+
u.capacity();
79+
let _ = v.clone();
80+
}
81+
7582
fn main() {}

tests/ui/needless_pass_by_value.stderr

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,45 @@ error: this argument is passed by value, but not consumed in the function body
6262
73 | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
6363
| ^ help: consider taking a reference instead: `&T`
6464

65+
error: this argument is passed by value, but not consumed in the function body
66+
--> $DIR/needless_pass_by_value.rs:75:18
67+
|
68+
75 | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
69+
| ^^^^^^ help: consider taking a reference instead: `&String`
70+
71+
error: this argument is passed by value, but not consumed in the function body
72+
--> $DIR/needless_pass_by_value.rs:75:29
73+
|
74+
75 | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
75+
| ^^^^^^
76+
|
77+
help: consider changing the type to
78+
|
79+
75 | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
80+
| ^^^^
81+
help: change `t.clone()` to
82+
|
83+
77 | let _ = t.to_string();
84+
| ^^^^^^^^^^^^^
85+
86+
error: this argument is passed by value, but not consumed in the function body
87+
--> $DIR/needless_pass_by_value.rs:75:40
88+
|
89+
75 | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
90+
| ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
91+
92+
error: this argument is passed by value, but not consumed in the function body
93+
--> $DIR/needless_pass_by_value.rs:75:53
94+
|
95+
75 | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
96+
| ^^^^^^^^
97+
|
98+
help: consider changing the type to
99+
|
100+
75 | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
101+
| ^^^^^^
102+
help: change `v.clone()` to
103+
|
104+
79 | let _ = v.to_owned();
105+
| ^^^^^^^^^^^^
106+

0 commit comments

Comments
 (0)