Skip to content

Commit 58459ab

Browse files
committed
Allow pass by reference if we return a reference
Currently this code will trigger `trivally_copy_pass_by_ref`: ``` struct OuterStruct { field: [u8; 8], } fn return_inner(outer: &OuterStruct) -> &[u8] { &outer.field } ``` If we change the `outer` to be pass-by-value it will not live long enough for us to return the reference. The above example is trivial but I've hit this in real code that either returns a reference to either the argument or in to `self`. This suppresses the `trivally_copy_pass_by_ref` lint if we return a reference and it has the same lifetime as the argument. This will likely miss complex cases with multiple lifetimes bounded by each other but it should cover the majority of cases with little effort.
1 parent 1f65617 commit 58459ab

File tree

3 files changed

+47
-27
lines changed

3 files changed

+47
-27
lines changed

clippy_lints/src/trivially_copy_pass_by_ref.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
115115
let fn_sig = cx.tcx.fn_sig(fn_def_id);
116116
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
117117

118+
// Use lifetimes to determine if we're returning a reference to the argument. In that case
119+
// we can't switch to pass-by-value as the argument will not live long enough.
120+
let output_lt = if let TypeVariants::TyRef(output_lt, _, _) = fn_sig.output().sty {
121+
Some(output_lt)
122+
} else {
123+
None
124+
};
125+
118126
for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {
119127
// All spans generated from a proc-macro invocation are the same...
120128
if span == input.span {
121129
return;
122130
}
123131

124132
if_chain! {
125-
if let TypeVariants::TyRef(_, ty, Mutability::MutImmutable) = ty.sty;
133+
if let TypeVariants::TyRef(input_lt, ty, Mutability::MutImmutable) = ty.sty;
134+
if Some(input_lt) != output_lt;
126135
if is_copy(cx, ty);
127136
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
128137
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)