diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2021-12-11 17:35:23 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-12-11 17:35:23 +0100 |
| commit | 433a13b47347849dbc8c5d5300b98b95be7fb2c9 (patch) | |
| tree | 1c330cf7465ec6adcdb2b607594b65f80284eb73 /src | |
| parent | b9a37ad0d995c71518629b032f8e816e1efa8bca (diff) | |
| parent | e27315268b10c9ef2f4c3d815dfc79f513327def (diff) | |
| download | rust-433a13b47347849dbc8c5d5300b98b95be7fb2c9.tar.gz rust-433a13b47347849dbc8c5d5300b98b95be7fb2c9.zip | |
Rollup merge of #83174 - camelid:borrow-help, r=oli-obk
Suggest using a temporary variable to fix borrowck errors
Fixes #77834.
In Rust, nesting method calls with both require `&mut` access to `self`
produces a borrow-check error:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:7:14
|
7 | self.foo(self.bar());
| ---------^^^^^^^^^^-
| | | |
| | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
That's because Rust has a left-to-right evaluation order, and the method
receiver is passed first. Thus, the argument to the method cannot then
mutate `self`.
There's an easy solution to this error: just extract a local variable
for the inner argument:
let tmp = self.bar();
self.foo(tmp);
However, the error doesn't give any suggestion of how to solve the
problem. As a result, new users may assume that it's impossible to
express their code correctly and get stuck.
This commit adds a (non-structured) suggestion to extract a local
variable for the inner argument to solve the error. The suggestion uses
heuristics that eliminate most false positives, though there are a few
false negatives (cases where the suggestion should be emitted but is
not). Those other cases can be implemented in a future change.
Diffstat (limited to 'src')
7 files changed, 181 insertions, 0 deletions
diff --git a/src/test/ui/borrowck/suggest-local-var-double-mut.rs b/src/test/ui/borrowck/suggest-local-var-double-mut.rs new file mode 100644 index 00000000000..d5996ba68be --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-double-mut.rs @@ -0,0 +1,27 @@ +// See issue #77834. + +#![crate_type = "lib"] + +mod method_syntax { + struct Foo; + + impl Foo { + fn foo(&mut self, _: f32) -> i32 { todo!() } + fn bar(&mut self) -> f32 { todo!() } + fn baz(&mut self) { + self.foo(self.bar()); //~ ERROR + } + } +} + +mod fully_qualified_syntax { + struct Foo; + + impl Foo { + fn foo(&mut self, _: f32) -> i32 { todo!() } + fn bar(&mut self) -> f32 { todo!() } + fn baz(&mut self) { + Self::foo(self, Self::bar(self)); //~ ERROR + } + } +} diff --git a/src/test/ui/borrowck/suggest-local-var-double-mut.stderr b/src/test/ui/borrowck/suggest-local-var-double-mut.stderr new file mode 100644 index 00000000000..3a43c18a7ed --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-double-mut.stderr @@ -0,0 +1,44 @@ +error[E0499]: cannot borrow `*self` as mutable more than once at a time + --> $DIR/suggest-local-var-double-mut.rs:12:22 + | +LL | self.foo(self.bar()); + | ---------^^^^^^^^^^- + | | | | + | | | second mutable borrow occurs here + | | first borrow later used by call + | first mutable borrow occurs here + | +help: try adding a local storing this argument... + --> $DIR/suggest-local-var-double-mut.rs:12:22 + | +LL | self.foo(self.bar()); + | ^^^^^^^^^^ +help: ...and then using that local as the argument to this call + --> $DIR/suggest-local-var-double-mut.rs:12:13 + | +LL | self.foo(self.bar()); + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0499]: cannot borrow `*self` as mutable more than once at a time + --> $DIR/suggest-local-var-double-mut.rs:24:39 + | +LL | Self::foo(self, Self::bar(self)); + | --------- ---- ^^^^ second mutable borrow occurs here + | | | + | | first mutable borrow occurs here + | first borrow later used by call + | +help: try adding a local storing this argument... + --> $DIR/suggest-local-var-double-mut.rs:24:29 + | +LL | Self::foo(self, Self::bar(self)); + | ^^^^^^^^^^^^^^^ +help: ...and then using that local as the argument to this call + --> $DIR/suggest-local-var-double-mut.rs:24:13 + | +LL | Self::foo(self, Self::bar(self)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr new file mode 100644 index 00000000000..2ba0b6b28aa --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr @@ -0,0 +1,33 @@ +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/suggest-local-var-imm-and-mut.rs:12:22 + | +LL | self.foo(self.bar()); + | ---------^^^^^^^^^^- + | | | | + | | | mutable borrow occurs here + | | immutable borrow later used by call + | immutable borrow occurs here + | +help: try adding a local storing this argument... + --> $DIR/suggest-local-var-imm-and-mut.rs:12:22 + | +LL | self.foo(self.bar()); + | ^^^^^^^^^^ +help: ...and then using that local as the argument to this call + --> $DIR/suggest-local-var-imm-and-mut.rs:12:13 + | +LL | self.foo(self.bar()); + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/suggest-local-var-imm-and-mut.rs:24:39 + | +LL | Self::foo(self, Self::bar(self)); + | --------- ---- ^^^^ mutable borrow occurs here + | | | + | | immutable borrow occurs here + | immutable borrow later used by call + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/suggest-local-var-imm-and-mut.rs b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.rs new file mode 100644 index 00000000000..bf167ba79f3 --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.rs @@ -0,0 +1,27 @@ +// See issue #77834. + +#![crate_type = "lib"] + +mod method_syntax { + struct Foo; + + impl Foo { + fn foo(&self, _: f32) -> i32 { todo!() } + fn bar(&mut self) -> f32 { todo!() } + fn baz(&mut self) { + self.foo(self.bar()); //~ ERROR + } + } +} + +mod fully_qualified_syntax { + struct Foo; + + impl Foo { + fn foo(&self, _: f32) -> i32 { todo!() } + fn bar(&mut self) -> f32 { todo!() } + fn baz(&mut self) { + Self::foo(self, Self::bar(self)); //~ ERROR + } + } +} diff --git a/src/test/ui/borrowck/suggest-local-var-imm-and-mut.stderr b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.stderr new file mode 100644 index 00000000000..eb934e7b72b --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.stderr @@ -0,0 +1,22 @@ +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/suggest-local-var-imm-and-mut.rs:12:22 + | +LL | self.foo(self.bar()); + | ---------^^^^^^^^^^- + | | | | + | | | mutable borrow occurs here + | | immutable borrow later used by call + | immutable borrow occurs here + +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/suggest-local-var-imm-and-mut.rs:24:29 + | +LL | Self::foo(self, Self::bar(self)); + | --------- ---- ^^^^^^^^^^^^^^^ mutable borrow occurs here + | | | + | | immutable borrow occurs here + | immutable borrow later used by call + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr b/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr index a89bb941532..85c7159952f 100644 --- a/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr +++ b/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr @@ -13,6 +13,23 @@ LL | | LL | | 0 LL | | }); | |______- immutable borrow occurs here + | +help: try adding a local storing this argument... + --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9 + | +LL | vec.push(2); + | ^^^^^^^^^^^ +help: ...and then using that local as the argument to this call + --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:14:5 + | +LL | / vec.get({ +LL | | +LL | | vec.push(2); +LL | | +LL | | +LL | | 0 +LL | | }); + | |______^ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/one_line.stderr b/src/test/ui/codemap_tests/one_line.stderr index 1ee612184de..6fe6e26135b 100644 --- a/src/test/ui/codemap_tests/one_line.stderr +++ b/src/test/ui/codemap_tests/one_line.stderr @@ -7,6 +7,17 @@ LL | v.push(v.pop().unwrap()); | | | second mutable borrow occurs here | | first borrow later used by call | first mutable borrow occurs here + | +help: try adding a local storing this argument... + --> $DIR/one_line.rs:3:12 + | +LL | v.push(v.pop().unwrap()); + | ^^^^^^^ +help: ...and then using that local as the argument to this call + --> $DIR/one_line.rs:3:5 + | +LL | v.push(v.pop().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error |
