Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,11 +1375,20 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
);

let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
if let Some((kind, _)) = desugaring {
err.subdiagnostic(CaptureReasonNote::DesugaringFuncTakeSelf {
func,
desugar_name: kind.name(),
place_name: place_name.clone(),
span: self_arg.span,
});
} else {
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
}
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
Expand Down Expand Up @@ -1451,20 +1460,31 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
}
} else {
if let Some((CallDesugaringKind::Await, _)) = desugaring {
err.subdiagnostic(CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
} else {
err.subdiagnostic(CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
match desugaring {
Some((CallDesugaringKind::Await, _)) => {
err.subdiagnostic(CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
Some((CallDesugaringKind::QuestionBranch, _)) => {
err.subdiagnostic(CaptureReasonLabel::QuestionMark {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
_ => {
err.subdiagnostic(CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
}
// Erase and shadow everything that could be passed to the new infcx.
let ty = moved_place.ty(self.body, tcx).ty;
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_borrowck/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,22 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to usage in the question mark {$is_loop_message ->
[true] operator, in previous iteration of loop
*[false] operator
}"
)]
QuestionMark {
#[primary_span]
fn_call_span: Span,
place_name: &'a str,
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
Expand Down Expand Up @@ -498,6 +514,17 @@ pub(crate) enum CaptureReasonNote {
#[primary_span]
span: Span,
},
#[note(
"the {$desugar_name} is expanded into a call to `{$func}`, which takes ownership of the \
receiver `self`, which moves {$place_name}"
)]
DesugaringFuncTakeSelf {
desugar_name: &'static str,
func: String,
place_name: String,
#[primary_span]
span: Span,
},
}

#[derive(Subdiagnostic)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ pub enum CallDesugaringKind {
}

impl CallDesugaringKind {
pub fn name(&self) -> &'static str {
match self {
CallDesugaringKind::ForLoopIntoIter => "`for` loop",
CallDesugaringKind::ForLoopNext => "`for` loop",
CallDesugaringKind::QuestionBranch => "question mark operator",
CallDesugaringKind::QuestionFromResidual => "question mark operator",
CallDesugaringKind::TryBlockFromOutput => "try block",
CallDesugaringKind::Await => "`await`",
}
}

pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
match self {
Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/async-await/clone-suggestion.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ LL | f.await;
LL | f.await;
| ^ value used here after move
|
note: `into_future` takes ownership of the receiver `self`, which moves `f`
note: the `await` is expanded into a call to `into_future`, which takes ownership of the receiver `self`, which moves `f`
--> $SRC_DIR/core/src/future/into_future.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | for l in bad_letters {
LL | bad_letters.push('s');
| ^^^^^^^^^^^ value borrowed here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `bad_letters`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `bad_letters`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<char>`'s content to avoid moving into the `for` loop
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume
|
LL | pub fn in_fn<F: Fn() -> Result<(), ()>>(f: F) -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^
note: `into_iter` takes ownership of the receiver `self`, which moves `foos`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `foos`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL

error[E0507]: cannot move out of `foos`, a captured variable in an `FnMut` closure
Expand All @@ -41,7 +41,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume
|
LL | pub fn in_fn_mut<F: FnMut() -> Result<(), ()>>(mut f: F) -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: `into_iter` takes ownership of the receiver `self`, which moves `foos`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `foos`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider creating a fresh reborrow of `foos` here
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/borrowck/issue-83924.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | for n in v {
LL | for n in v {
| ^ value used here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `v`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `v`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider creating a fresh reborrow of `v` here
|
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/borrowck/moved-into-question-mark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://github.com/rust-lang/rust/issues/89567
use std::fs;
use std::io;

fn main() -> io::Result<()> {
for entry in fs::read_dir(".")? {
//~^ NOTE move occurs because `entry` has type `Result
let file_type = entry?.file_type()?;
//~^ NOTE `entry` moved due to usage in the question mark operator
if file_type.is_dir() {
dbg!(entry?.file_name()); //~ ERROR use of moved value
//~^ NOTE value used here after move
//~| NOTE the question mark operator is expanded into a call to `branch`, which
}
}
Ok(())
}
22 changes: 22 additions & 0 deletions tests/ui/borrowck/moved-into-question-mark.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0382]: use of moved value: `entry`
--> $DIR/moved-into-question-mark.rs:11:18
|
LL | for entry in fs::read_dir(".")? {
| ----- move occurs because `entry` has type `Result<DirEntry, std::io::Error>`, which does not implement the `Copy` trait
LL |
LL | let file_type = entry?.file_type()?;
| ------ `entry` moved due to usage in the question mark operator
...
LL | dbg!(entry?.file_name());
| ^^^^^ value used here after move
|
note: the question mark operator is expanded into a call to `branch`, which takes ownership of the receiver `self`, which moves `entry`

@chenyukang chenyukang Jul 28, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it better to use desugarded than expanded, not sure.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed, as well as a few other tweaks in the second commit. I think that using the full path to the item makes more sense in this case (it is unlikely to be too long, and the clarity gain is high). LMKWYT.

--> $SRC_DIR/core/src/ops/try_trait.rs:LL:COL
help: you could `clone` the value and consume it, if the following trait bounds could be satisfied: `DirEntry: Clone` and `std::io::Error: Clone`
|
LL | let file_type = entry.clone()?.file_type()?;
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0382`.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | let _closure = || orig;
| |
| value used here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `orig`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `orig`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<bool>`'s content to avoid moving into the `for` loop
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/borrowck/reborrow-sugg-move-then-borrow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL |
LL | fill_segment(state);
| ^^^^^ value borrowed here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `state`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `state`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider creating a fresh reborrow of `state` here
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/loops/issue-82916.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | for y in x {
LL | let z = x;
| ^ value used here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `x`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<S>`'s content to avoid moving into the `for` loop
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/suggestions/borrow-for-loop-head.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LL | for i in &a {
LL | for j in a {
| ^ `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
|
note: `into_iter` takes ownership of the receiver `self`, which moves `a`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `a`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<i32>`'s content to avoid moving into the `for` loop
|
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/suggestions/for-i-in-vec.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | for _ in self.v {
| `self.v` moved due to this implicit call to `.into_iter()`
| move occurs because `self.v` has type `Vec<u32>`, which does not implement the `Copy` trait
|
note: `into_iter` takes ownership of the receiver `self`, which moves `self.v`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `self.v`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<u32>`'s content to avoid moving into the `for` loop
|
Expand Down Expand Up @@ -45,7 +45,7 @@ LL | for loader in *LOADERS {
| value moved due to this implicit call to `.into_iter()`
| move occurs because value has type `Vec<&u8>`, which does not implement the `Copy` trait
|
note: `into_iter` takes ownership of the receiver `self`, which moves value
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves value
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<&u8>`'s content to avoid moving into the `for` loop
|
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/suggestions/issue-102972.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ LL | iter.next();
| ^^^^ value borrowed here after move
|
= note: a for loop advances the iterator for you, the result is stored in `_i`
note: `into_iter` takes ownership of the receiver `self`, which moves `iter`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `iter`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: if you want to call `next` on a iterator within the loop, consider using `while let`
|
Expand Down Expand Up @@ -66,7 +66,7 @@ LL | iter.next();
| ^^^^ value borrowed here after move
|
= note: a for loop advances the iterator for you, the result is stored in its pattern
note: `into_iter` takes ownership of the receiver `self`, which moves `iter`
note: the `for` loop is expanded into a call to `into_iter`, which takes ownership of the receiver `self`, which moves `iter`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: if you want to call `next` on a iterator within the loop, consider using `while let`
|
Expand Down
Loading