Skip to content

Commit b02e53d

Browse files
authored
Merge pull request #2951 from etaoins/allow-pass-by-ref-on-ref-return
Allow pass by reference if we return a reference
2 parents 3f9b837 + 0afa5e1 commit b02e53d

File tree

3 files changed

+54
-27
lines changed

3 files changed

+54
-27
lines changed

clippy_lints/src/trivially_copy_pass_by_ref.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ use crate::utils::{in_macro, is_copy, is_self, span_lint_and_sugg, snippet};
3131
/// The configuration option `trivial_copy_size_limit` can be set to override
3232
/// this limit for a project.
3333
///
34+
/// This lint attempts to allow passing arguments by reference if a reference
35+
/// to that argument is returned. This is implemented by comparing the lifetime
36+
/// of the argument and return value for equality. However, this can cause
37+
/// false positives in cases involving multiple lifetimes that are bounded by
38+
/// each other.
39+
///
3440
/// **Example:**
3541
/// ```rust
3642
/// fn foo(v: &u32) {
@@ -115,14 +121,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
115121
let fn_sig = cx.tcx.fn_sig(fn_def_id);
116122
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
117123

124+
// Use lifetimes to determine if we're returning a reference to the
125+
// argument. In that case we can't switch to pass-by-value as the
126+
// argument will not live long enough.
127+
let output_lt = if let TypeVariants::TyRef(output_lt, _, _) = fn_sig.output().sty {
128+
Some(output_lt)
129+
} else {
130+
None
131+
};
132+
118133
for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {
119134
// All spans generated from a proc-macro invocation are the same...
120135
if span == input.span {
121136
return;
122137
}
123138

124139
if_chain! {
125-
if let TypeVariants::TyRef(_, ty, Mutability::MutImmutable) = ty.sty;
140+
if let TypeVariants::TyRef(input_lt, ty, Mutability::MutImmutable) = ty.sty;
141+
if Some(input_lt) != output_lt;
126142
if is_copy(cx, ty);
127143
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
128144
if size <= self.limit;

tests/ui/trivially_copy_pass_by_ref.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ type Baz = u32;
1111
fn good(a: &mut u32, b: u32, c: &Bar) {
1212
}
1313

14+
fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
15+
&foo.0
16+
}
17+
18+
#[allow(needless_lifetimes)]
19+
fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
20+
&foo.0
21+
}
22+
1423
fn bad(x: &u32, y: &Foo, z: &Baz) {
1524
}
1625

@@ -46,6 +55,8 @@ fn main() {
4655
let (mut foo, bar) = (Foo(0), Bar([0; 24]));
4756
let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
4857
good(&mut a, b, &c);
58+
good_return_implicit_lt_ref(&y);
59+
good_return_explicit_lt_ref(&y);
4960
bad(&x, &y, &z);
5061
foo.good(&mut a, b, &c);
5162
foo.good2();

tests/ui/trivially_copy_pass_by_ref.stderr

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,81 @@
11
error: this argument is passed by reference, but would be more efficient if passed by value
2-
--> $DIR/trivially_copy_pass_by_ref.rs:14:11
2+
--> $DIR/trivially_copy_pass_by_ref.rs:23:11
33
|
4-
14 | fn bad(x: &u32, y: &Foo, z: &Baz) {
4+
23 | fn bad(x: &u32, y: &Foo, z: &Baz) {
55
| ^^^^ help: consider passing by value instead: `u32`
66
|
77
= note: `-D trivially-copy-pass-by-ref` implied by `-D warnings`
88

99
error: this argument is passed by reference, but would be more efficient if passed by value
10-
--> $DIR/trivially_copy_pass_by_ref.rs:14:20
10+
--> $DIR/trivially_copy_pass_by_ref.rs:23:20
1111
|
12-
14 | fn bad(x: &u32, y: &Foo, z: &Baz) {
12+
23 | fn bad(x: &u32, y: &Foo, z: &Baz) {
1313
| ^^^^ help: consider passing by value instead: `Foo`
1414

1515
error: this argument is passed by reference, but would be more efficient if passed by value
16-
--> $DIR/trivially_copy_pass_by_ref.rs:14:29
16+
--> $DIR/trivially_copy_pass_by_ref.rs:23:29
1717
|
18-
14 | fn bad(x: &u32, y: &Foo, z: &Baz) {
18+
23 | fn bad(x: &u32, y: &Foo, z: &Baz) {
1919
| ^^^^ help: consider passing by value instead: `Baz`
2020

2121
error: this argument is passed by reference, but would be more efficient if passed by value
22-
--> $DIR/trivially_copy_pass_by_ref.rs:24:12
22+
--> $DIR/trivially_copy_pass_by_ref.rs:33:12
2323
|
24-
24 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
24+
33 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
2525
| ^^^^^ help: consider passing by value instead: `self`
2626

2727
error: this argument is passed by reference, but would be more efficient if passed by value
28-
--> $DIR/trivially_copy_pass_by_ref.rs:24:22
28+
--> $DIR/trivially_copy_pass_by_ref.rs:33:22
2929
|
30-
24 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
30+
33 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
3131
| ^^^^ help: consider passing by value instead: `u32`
3232

3333
error: this argument is passed by reference, but would be more efficient if passed by value
34-
--> $DIR/trivially_copy_pass_by_ref.rs:24:31
34+
--> $DIR/trivially_copy_pass_by_ref.rs:33:31
3535
|
36-
24 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
36+
33 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
3737
| ^^^^ help: consider passing by value instead: `Foo`
3838

3939
error: this argument is passed by reference, but would be more efficient if passed by value
40-
--> $DIR/trivially_copy_pass_by_ref.rs:24:40
40+
--> $DIR/trivially_copy_pass_by_ref.rs:33:40
4141
|
42-
24 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
42+
33 | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {
4343
| ^^^^ help: consider passing by value instead: `Baz`
4444

4545
error: this argument is passed by reference, but would be more efficient if passed by value
46-
--> $DIR/trivially_copy_pass_by_ref.rs:27:16
46+
--> $DIR/trivially_copy_pass_by_ref.rs:36:16
4747
|
48-
27 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
48+
36 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
4949
| ^^^^ help: consider passing by value instead: `u32`
5050

5151
error: this argument is passed by reference, but would be more efficient if passed by value
52-
--> $DIR/trivially_copy_pass_by_ref.rs:27:25
52+
--> $DIR/trivially_copy_pass_by_ref.rs:36:25
5353
|
54-
27 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
54+
36 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
5555
| ^^^^ help: consider passing by value instead: `Foo`
5656

5757
error: this argument is passed by reference, but would be more efficient if passed by value
58-
--> $DIR/trivially_copy_pass_by_ref.rs:27:34
58+
--> $DIR/trivially_copy_pass_by_ref.rs:36:34
5959
|
60-
27 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
60+
36 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
6161
| ^^^^ help: consider passing by value instead: `Baz`
6262

6363
error: this argument is passed by reference, but would be more efficient if passed by value
64-
--> $DIR/trivially_copy_pass_by_ref.rs:41:16
64+
--> $DIR/trivially_copy_pass_by_ref.rs:50:16
6565
|
66-
41 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
66+
50 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
6767
| ^^^^ help: consider passing by value instead: `u32`
6868

6969
error: this argument is passed by reference, but would be more efficient if passed by value
70-
--> $DIR/trivially_copy_pass_by_ref.rs:41:25
70+
--> $DIR/trivially_copy_pass_by_ref.rs:50:25
7171
|
72-
41 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
72+
50 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
7373
| ^^^^ help: consider passing by value instead: `Foo`
7474

7575
error: this argument is passed by reference, but would be more efficient if passed by value
76-
--> $DIR/trivially_copy_pass_by_ref.rs:41:34
76+
--> $DIR/trivially_copy_pass_by_ref.rs:50:34
7777
|
78-
41 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
78+
50 | fn bad2(x: &u32, y: &Foo, z: &Baz) {
7979
| ^^^^ help: consider passing by value instead: `Baz`
8080

8181
error: aborting due to 13 previous errors

0 commit comments

Comments
 (0)