Skip to content

Commit 0c43b60

Browse files
authored
Merge pull request #2199 from sinkuu/needless_pass_by_value_method
Extend needless_pass_by_value to methods
2 parents 4127c23 + d88cc53 commit 0c43b60

File tree

7 files changed

+95
-20
lines changed

7 files changed

+95
-20
lines changed

clippy_lints/src/collapsible_if.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext, expr: &ast::Expr, check: &ast:
134134
db.span_suggestion(expr.span,
135135
"try",
136136
format!("if {} {}",
137-
lhs.and(rhs),
137+
lhs.and(&rhs),
138138
snippet_block(cx, content.span, "..")));
139139
});
140140
}

clippy_lints/src/int_plus_one.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl LintPass for IntPlusOne {
4545
// x + 1 <= y
4646
// x <= y - 1
4747

48+
#[derive(Copy, Clone)]
4849
enum Side {
4950
LHS,
5051
RHS,

clippy_lints/src/needless_pass_by_value.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use rustc::hir::*;
2+
use rustc::hir::map::*;
23
use rustc::hir::intravisit::FnKind;
34
use rustc::lint::*;
45
use rustc::ty::{self, RegionKind, TypeFoldable};
@@ -22,13 +23,20 @@ use std::borrow::Cow;
2223
/// sometimes avoid
2324
/// unnecessary allocations.
2425
///
25-
/// **Known problems:** Hopefully none.
26+
/// **Known problems:**
27+
/// * This lint suggests taking an argument by reference,
28+
/// however sometimes it is better to let users decide the argument type
29+
/// (by using `Borrow` trait, for example), depending on how the function is used.
2630
///
2731
/// **Example:**
2832
/// ```rust
2933
/// fn foo(v: Vec<i32>) {
3034
/// assert_eq!(v.len(), 42);
3135
/// }
36+
/// // should be
37+
/// fn foo(v: &[i32]) {
38+
/// assert_eq!(v.len(), 42);
39+
/// }
3240
/// ```
3341
declare_lint! {
3442
pub NEEDLESS_PASS_BY_VALUE,
@@ -73,9 +81,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
7381
}
7482
}
7583
},
84+
FnKind::Method(..) => (),
7685
_ => return,
7786
}
7887

88+
// Exclude non-inherent impls
89+
if let Some(NodeItem(item)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(node_id)) {
90+
if matches!(item.node, ItemImpl(_, _, _, _, Some(_), _, _) | ItemDefaultImpl(..)) {
91+
return;
92+
}
93+
}
94+
7995
// Allow `Borrow` or functions to be taken by value
8096
let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
8197
let fn_traits = [
@@ -109,7 +125,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
109125
} = {
110126
let mut ctx = MovedVariablesCtxt::new(cx);
111127
let region_scope_tree = &cx.tcx.region_scope_tree(fn_def_id);
112-
euv::ExprUseVisitor::new(&mut ctx, cx.tcx, cx.param_env, region_scope_tree, cx.tables, None).consume_body(body);
128+
euv::ExprUseVisitor::new(&mut ctx, cx.tcx, cx.param_env, region_scope_tree, cx.tables, None)
129+
.consume_body(body);
113130
ctx
114131
};
115132

@@ -127,6 +144,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
127144
return;
128145
}
129146

147+
// Ignore `self`s.
148+
if idx == 0 {
149+
if let PatKind::Binding(_, _, name, ..) = arg.pat.node {
150+
if name.node.as_str() == "self" {
151+
continue;
152+
}
153+
}
154+
}
155+
130156
// * Exclude a type that is specifically bounded by `Borrow`.
131157
// * Exclude a type whose reference also fulfills its bound.
132158
// (e.g. `std::convert::AsRef`, `serde::Serialize`)
@@ -163,7 +189,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
163189
if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
164190
continue;
165191
}
166-
192+
167193
// Dereference suggestion
168194
let sugg = |db: &mut DiagnosticBuilder| {
169195
let deref_span = spans_need_deref.get(&canonical_id);
@@ -181,7 +207,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
181207
db.span_suggestion(input.span,
182208
"consider changing the type to",
183209
slice_ty);
184-
210+
185211
for (span, suggestion) in clone_spans {
186212
db.span_suggestion(
187213
span,
@@ -193,18 +219,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
193219
suggestion.into()
194220
);
195221
}
196-
222+
197223
// cannot be destructured, no need for `*` suggestion
198224
assert!(deref_span.is_none());
199225
return;
200226
}
201227
}
202-
228+
203229
if match_type(cx, ty, &paths::STRING) {
204230
if let Some(clone_spans) =
205231
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
206232
db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());
207-
233+
208234
for (span, suggestion) in clone_spans {
209235
db.span_suggestion(
210236
span,
@@ -216,14 +242,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
216242
suggestion.into(),
217243
);
218244
}
219-
245+
220246
assert!(deref_span.is_none());
221247
return;
222248
}
223249
}
224-
250+
225251
let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
226-
252+
227253
// Suggests adding `*` to dereference the added reference.
228254
if let Some(deref_span) = deref_span {
229255
spans.extend(
@@ -236,7 +262,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
236262
}
237263
multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
238264
};
239-
265+
240266
span_lint_and_then(
241267
cx,
242268
NEEDLESS_PASS_BY_VALUE,

clippy_lints/src/utils/sugg.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ impl<'a> Sugg<'a> {
136136
}
137137

138138
/// Convenience method to create the `<lhs> && <rhs>` suggestion.
139-
pub fn and(self, rhs: Self) -> Sugg<'static> {
140-
make_binop(ast::BinOpKind::And, &self, &rhs)
139+
pub fn and(self, rhs: &Self) -> Sugg<'static> {
140+
make_binop(ast::BinOpKind::And, &self, rhs)
141141
}
142142

143143
/// Convenience method to create the `<lhs> as <rhs>` suggestion.
@@ -162,10 +162,10 @@ impl<'a> Sugg<'a> {
162162

163163
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
164164
/// suggestion.
165-
pub fn range(self, end: Self, limit: ast::RangeLimits) -> Sugg<'static> {
165+
pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
166166
match limit {
167-
ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, &end),
168-
ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, &end),
167+
ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
168+
ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
169169
}
170170
}
171171

tests/ui/methods.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

22
#![feature(const_fn)]
33

4-
54
#![warn(clippy, clippy_pedantic)]
6-
#![allow(blacklisted_name, unused, print_stdout, non_ascii_literal, new_without_default, new_without_default_derive, missing_docs_in_private_items)]
5+
#![allow(blacklisted_name, unused, print_stdout, non_ascii_literal, new_without_default,
6+
new_without_default_derive, missing_docs_in_private_items, needless_pass_by_value)]
77

88
use std::collections::BTreeMap;
99
use std::collections::HashMap;

tests/ui/needless_pass_by_value.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
6565

6666
trait Foo {}
6767

68-
// `S: Serialize` can be passed by value
68+
// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead
6969
trait Serialize {}
7070
impl<'a, T> Serialize for &'a T where T: Serialize {}
7171
impl Serialize for i32 {}
@@ -79,4 +79,28 @@ fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
7979
let _ = v.clone();
8080
}
8181

82+
struct S<T, U>(T, U);
83+
84+
impl<T: Serialize, U> S<T, U> {
85+
fn foo(
86+
self, // taking `self` by value is always allowed
87+
s: String,
88+
t: String,
89+
) -> usize {
90+
s.len() + t.capacity()
91+
}
92+
93+
fn bar(
94+
_t: T, // Ok, since `&T: Serialize` too
95+
) {
96+
}
97+
98+
fn baz(
99+
&self,
100+
_u: U,
101+
_s: Self,
102+
) {
103+
}
104+
}
105+
82106
fn main() {}

tests/ui/needless_pass_by_value.stderr

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,27 @@ help: change `v.clone()` to
104104
79 | let _ = v.to_owned();
105105
| ^^^^^^^^^^^^
106106

107+
error: this argument is passed by value, but not consumed in the function body
108+
--> $DIR/needless_pass_by_value.rs:87:12
109+
|
110+
87 | s: String,
111+
| ^^^^^^ help: consider changing the type to: `&str`
112+
113+
error: this argument is passed by value, but not consumed in the function body
114+
--> $DIR/needless_pass_by_value.rs:88:12
115+
|
116+
88 | t: String,
117+
| ^^^^^^ help: consider taking a reference instead: `&String`
118+
119+
error: this argument is passed by value, but not consumed in the function body
120+
--> $DIR/needless_pass_by_value.rs:100:13
121+
|
122+
100 | _u: U,
123+
| ^ help: consider taking a reference instead: `&U`
124+
125+
error: this argument is passed by value, but not consumed in the function body
126+
--> $DIR/needless_pass_by_value.rs:101:13
127+
|
128+
101 | _s: Self,
129+
| ^^^^ help: consider taking a reference instead: `&Self`
130+

0 commit comments

Comments
 (0)