Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
40 changes: 40 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2547,6 +2547,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"you must specify a type for this binding, like `{concrete_type}`",
);

// FIXME: Maybe FileName::Anon should also be handled,
// otherwise there would be no suggestion if the source is STDIN for example.
match (filename, parent_node) {
(
FileName::Real(_),
Expand All @@ -2568,6 +2570,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
// For closure parameters with reference patterns (e.g., |&v|), suggest the type annotation
// on the pattern itself, e.g., |&v: &i32|
(FileName::Real(_), Node::Pat(pat))
Copy link
Member

@fmease fmease Oct 11, 2025

Choose a reason for hiding this comment

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

(preexisting) Matching on the file name presumably to suppress this suggestion if this comes from a macro expansion (FileName::MacroExpansion) is wild and simply incorrect, lol. E.g., it means it never suggests anything if the source is STDIN (FileName::Anon) like for printf '/* ... */' | rustc -.

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense, but addressing it in this PR feels like out-of-scope, so added FIXME: 632e759 (#147577)

Or do you want me to resolve it here?

Copy link
Member

Choose a reason for hiding this comment

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

Or do you want me to resolve it here?

No, a FIXME is fine 👍

if let Node::Pat(binding_pat) = self.tcx.hir_node(hir_id)
&& let hir::PatKind::Binding(..) = binding_pat.kind
&& let Node::Pat(parent_pat) = parent_node
&& matches!(parent_pat.kind, hir::PatKind::Ref(..)) =>
Comment on lines +2576 to +2579
Copy link
Member

@fmease fmease Oct 11, 2025

Choose a reason for hiding this comment

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

Food for thought (I don't have time to investigate this): If the unsolved inference variable is not an integer/float one ({integer} / {float}) but a normal one, then need_type_info / InferSourceKind is able to figure out the type annotation for various kinds of patterns.

E.g.,

[].into_iter().filter(|&x| x.pow(0)); // suggests `: &T` on master
[].into_iter().filter(|(x,)| x.pow(0)); // suggests `: &(T,)` on master

So I guess ideally we would be modifying needs_type_info to properly deal with integer/float ty vars since that seems to have all the infrastructure in place already (unless they really follow a different code path in which case my suggestion would be nonsensical).

{
err.span_label(span, "you must specify a type for this binding");

let mut ref_muts = Vec::new();
let mut current_node = parent_node;

while let Node::Pat(parent_pat) = current_node {
if let hir::PatKind::Ref(_, mutability) = parent_pat.kind {
ref_muts.push(mutability);
current_node = self.tcx.parent_hir_node(parent_pat.hir_id);
} else {
break;
}
}

let mut type_annotation = String::new();
for mutability in ref_muts.iter().rev() {
match mutability {
hir::Mutability::Mut => type_annotation.push_str("&mut "),
hir::Mutability::Not => type_annotation.push('&'),
}
}
type_annotation.push_str(&concrete_type);

err.span_suggestion_verbose(
pat.span.shrink_to_hi(),
"specify the type in the closure argument list",
format!(": {type_annotation}"),
Applicability::MaybeIncorrect,
);
}
_ => {
err.span_label(span, msg);
}
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Test for better error message when numeric type is ambiguous in closure parameter with reference

//@ run-rustfix

fn main() {
let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0);
//~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
//~| SUGGESTION &i32

let v = vec![0, 1, 2];
let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0);
//~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
//~| SUGGESTION &&i32
}
14 changes: 14 additions & 0 deletions tests/ui/inference/ambiguous-numeric-in-closure-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Test for better error message when numeric type is ambiguous in closure parameter with reference

//@ run-rustfix

fn main() {
let _ = (0..10).filter(|&v| v.pow(2) > 0);
//~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
//~| SUGGESTION &i32

let v = vec![0, 1, 2];
let _ = v.iter().filter(|&&v| v.pow(2) > 0);
//~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
//~| SUGGESTION &&i32
}
29 changes: 29 additions & 0 deletions tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
--> $DIR/ambiguous-numeric-in-closure-ref.rs:6:35
|
LL | let _ = (0..10).filter(|&v| v.pow(2) > 0);
| - ^^^
| |
| you must specify a type for this binding
|
help: specify the type in the closure argument list
|
LL | let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0);
| ++++++

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
--> $DIR/ambiguous-numeric-in-closure-ref.rs:11:37
|
LL | let _ = v.iter().filter(|&&v| v.pow(2) > 0);
| - ^^^
| |
| you must specify a type for this binding
|
help: specify the type in the closure argument list
|
LL | let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0);
| +++++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0689`.
Loading