From ffcbfc1cf49990f7f98abe2b052ba1885242d015 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Mon, 20 Apr 2026 22:21:51 +0800 Subject: [PATCH] Use singular wording for single _ placeholders in type suggestions --- .../error_reporting/infer/need_type_info.rs | 28 ++++++++++++++----- compiler/rustc_trait_selection/src/errors.rs | 3 +- ...eref-ambiguity-becomes-nonambiguous.stderr | 2 +- .../edition-raw-pointer-method-2018.stderr | 2 +- .../recursive-in-exhaustiveness.next.stderr | 4 +-- .../ambiguity-after-deref-step.stderr | 2 +- .../underscore-placeholder-wording.rs | 16 +++++++++++ .../underscore-placeholder-wording.stderr | 25 +++++++++++++++++ .../call_method_unknown_pointee.stderr | 4 +-- ...closing-angle-bracket-eq-constraint.stderr | 4 +-- .../pattern/slice-patterns-irrefutable.stderr | 2 +- ...opy-inference-side-effects-are-lazy.stderr | 2 +- .../cannot_infer_local_or_array.stderr | 2 +- .../cannot_infer_local_or_vec.stderr | 2 +- ...cannot_infer_local_or_vec_in_tuples.stderr | 2 +- tests/ui/typeck/issue-7813.stderr | 2 +- ...oxed-closures-failed-recursive-fn-2.stderr | 2 +- 17 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 tests/ui/inference/need_type_info/underscore-placeholder-wording.rs create mode 100644 tests/ui/inference/need_type_info/underscore-placeholder-wording.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 86b6a3c7b766..cdda3dc84305 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -27,7 +27,7 @@ AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, SourceKindMultiSuggestion, SourceKindSubdiag, }; -use crate::infer::InferCtxt; +use crate::infer::{InferCtxt, TyOrConstInferVar}; pub enum TypeAnnotationNeeded { /// ```compile_fail,E0282 @@ -81,13 +81,27 @@ fn can_add_more_info(&self) -> bool { !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. })) } - fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str { + fn where_x_is_kind<'tcx>(&self, infcx: &InferCtxt<'tcx>, in_type: Ty<'tcx>) -> &'static str { if in_type.is_ty_or_numeric_infer() { "" } else if self.name == "_" { - // FIXME: Consider specializing this message if there is a single `_` - // in the type. - "underscore" + let displayed_ty = infcx + .resolve_vars_if_possible(in_type) + .fold_with(&mut ClosureEraser { infcx, depth: 0 }); + if displayed_ty.is_ty_or_numeric_infer() { + "" + } else { + match displayed_ty + .walk() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) + .take(2) + .count() + { + 0 => "", + 1 => "underscore_single", + _ => "underscore_multiple", + } + } } else { "has_name" } @@ -554,7 +568,7 @@ pub fn emit_inference_failure_err_with_type_hint( infer_subdiags.push(SourceKindSubdiag::LetLike { span: insert_span, name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new), - x_kind: arg_data.where_x_is_kind(ty), + x_kind: arg_data.where_x_is_kind(self.infcx, ty), prefix_kind: arg_data.kind.clone(), prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), arg_name: arg_data.name, @@ -566,7 +580,7 @@ pub fn emit_inference_failure_err_with_type_hint( infer_subdiags.push(SourceKindSubdiag::LetLike { span: insert_span, name: String::new(), - x_kind: arg_data.where_x_is_kind(ty), + x_kind: arg_data.where_x_is_kind(self.infcx, ty), prefix_kind: arg_data.kind.clone(), prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), arg_name: arg_data.name, diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 1edb3f172149..8db2720edb0f 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -277,7 +277,8 @@ pub enum SourceKindSubdiag<'a> { [const_with_param] value of const parameter [const] value of the constant } `{$arg_name}` is specified - [underscore] , where the placeholders `_` are specified + [underscore_single] , where the placeholder `_` is specified + [underscore_multiple] , where the placeholders `_` are specified *[empty] {\"\"} }", style = "verbose", diff --git a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr index bad799b2550b..e5d9485e1797 100644 --- a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr +++ b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr @@ -7,7 +7,7 @@ LL | let var_fn = Value::wrap(); LL | let _ = var_fn.clone(); | ------ type must be known at this point | -help: consider giving `var_fn` an explicit type, where the placeholders `_` are specified +help: consider giving `var_fn` an explicit type, where the placeholder `_` is specified | LL | let var_fn: Value> = Value::wrap(); | ++++++++++++++ diff --git a/tests/ui/editions/edition-raw-pointer-method-2018.stderr b/tests/ui/editions/edition-raw-pointer-method-2018.stderr index 2792d1e74005..a95d7dff9caa 100644 --- a/tests/ui/editions/edition-raw-pointer-method-2018.stderr +++ b/tests/ui/editions/edition-raw-pointer-method-2018.stderr @@ -7,7 +7,7 @@ LL | LL | let _ = y.is_null(); | ------- cannot call a method on a raw pointer with an unknown pointee type | -help: consider giving `y` an explicit type, where the placeholders `_` are specified +help: consider giving `y` an explicit type, where the placeholder `_` is specified | LL | let y: *const _ = &x as *const _; | ++++++++++ diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr index 10e8dbf41ccb..4b7e312644d2 100644 --- a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `(_,)` LL | let (x,) = (build(x),); | ^^^^ | -help: consider giving this pattern a type, where the placeholders `_` are specified +help: consider giving this pattern a type, where the placeholder `_` is specified | LL | let (x,): (_,) = (build(x),); | ++++++ @@ -15,7 +15,7 @@ error[E0282]: type annotations needed for `((_,),)` LL | let (x,) = (build2(x),); | ^^^^ | -help: consider giving this pattern a type, where the placeholders `_` are specified +help: consider giving this pattern a type, where the placeholder `_` is specified | LL | let (x,): ((_,),) = (build2(x),); | +++++++++ diff --git a/tests/ui/indexing/ambiguity-after-deref-step.stderr b/tests/ui/indexing/ambiguity-after-deref-step.stderr index c7ddd4731c7c..458f83d72121 100644 --- a/tests/ui/indexing/ambiguity-after-deref-step.stderr +++ b/tests/ui/indexing/ambiguity-after-deref-step.stderr @@ -7,7 +7,7 @@ LL | LL | x[1]; | - type must be known at this point | -help: consider giving `x` an explicit type, where the placeholders `_` are specified +help: consider giving `x` an explicit type, where the placeholder `_` is specified | LL | let x: &_ = &Default::default(); | ++++ diff --git a/tests/ui/inference/need_type_info/underscore-placeholder-wording.rs b/tests/ui/inference/need_type_info/underscore-placeholder-wording.rs new file mode 100644 index 000000000000..db1d909f85b1 --- /dev/null +++ b/tests/ui/inference/need_type_info/underscore-placeholder-wording.rs @@ -0,0 +1,16 @@ +// Check that `need_type_info` distinguishes between a single placeholder and +// multiple placeholders when suggesting an explicit type. + +fn singular() { + let v = &[]; + //~^ ERROR type annotations needed + let _ = v.iter(); +} + +fn plural() { + let x = (vec![], vec![]); + //~^ ERROR type annotations needed + let _ = x; +} + +fn main() {} diff --git a/tests/ui/inference/need_type_info/underscore-placeholder-wording.stderr b/tests/ui/inference/need_type_info/underscore-placeholder-wording.stderr new file mode 100644 index 000000000000..322fcddc8a48 --- /dev/null +++ b/tests/ui/inference/need_type_info/underscore-placeholder-wording.stderr @@ -0,0 +1,25 @@ +error[E0282]: type annotations needed for `&[_; 0]` + --> $DIR/underscore-placeholder-wording.rs:5:9 + | +LL | let v = &[]; + | ^ --- type must be known at this point + | +help: consider giving `v` an explicit type, where the placeholder `_` is specified + | +LL | let v: &[_; 0] = &[]; + | +++++++++ + +error[E0282]: type annotations needed for `(Vec<_>, Vec<_>)` + --> $DIR/underscore-placeholder-wording.rs:11:9 + | +LL | let x = (vec![], vec![]); + | ^ ---------------- type must be known at this point + | +help: consider giving `x` an explicit type, where the placeholders `_` are specified + | +LL | let x: (Vec<_>, Vec<_>) = (vec![], vec![]); + | ++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index c123533b51bc..e21d516e79fb 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -15,7 +15,7 @@ LL | LL | let _b: u8 = b.read(); | ---- cannot call a method on a raw pointer with an unknown pointee type | -help: consider giving `b` an explicit type, where the placeholders `_` are specified +help: consider giving `b` an explicit type, where the placeholder `_` is specified | LL | let b: *const _ = ptr as *const _; | ++++++++++ @@ -37,7 +37,7 @@ LL | LL | let _d: u8 = d.read(); | ---- cannot call a method on a raw pointer with an unknown pointee type | -help: consider giving `d` an explicit type, where the placeholders `_` are specified +help: consider giving `d` an explicit type, where the placeholder `_` is specified | LL | let d: *mut _ = ptr as *mut _; | ++++++++ diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr index f01da7a38f3e..487a6abc8c43 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr @@ -43,7 +43,7 @@ error[E0282]: type annotations needed for `Vec<_>` LL | let v : Vec<(u32,_) = vec![]; | ^ ------ type must be known at this point | -help: consider giving `v` an explicit type, where the placeholders `_` are specified +help: consider giving `v` an explicit type, where the placeholder `_` is specified | LL | let v: Vec<_> : Vec<(u32,_) = vec![]; | ++++++++ @@ -54,7 +54,7 @@ error[E0282]: type annotations needed for `Vec<_>` LL | let v : Vec<'a = vec![]; | ^ ------ type must be known at this point | -help: consider giving `v` an explicit type, where the placeholders `_` are specified +help: consider giving `v` an explicit type, where the placeholder `_` is specified | LL | let v: Vec<_> : Vec<'a = vec![]; | ++++++++ diff --git a/tests/ui/pattern/slice-patterns-irrefutable.stderr b/tests/ui/pattern/slice-patterns-irrefutable.stderr index 9b46f8a88546..4619a0d798d4 100644 --- a/tests/ui/pattern/slice-patterns-irrefutable.stderr +++ b/tests/ui/pattern/slice-patterns-irrefutable.stderr @@ -7,7 +7,7 @@ LL | LL | [a, b] = Default::default(); | - type must be known at this point | -help: consider giving `b` an explicit type, where the placeholders `_` are specified +help: consider giving `b` an explicit type, where the placeholder `_` is specified | LL | let b: [_; 3]; | ++++++++ diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr index c98b9bb38fdb..79da0319749a 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -7,7 +7,7 @@ LL | LL | extract(x).max(2); | ---------- type must be known at this point | -help: consider giving `x` an explicit type, where the placeholders `_` are specified +help: consider giving `x` an explicit type, where the placeholder `_` is specified | LL | let x: [Foo; 2] = [Foo(PhantomData); 2]; | +++++++++++++ diff --git a/tests/ui/type/type-check/cannot_infer_local_or_array.stderr b/tests/ui/type/type-check/cannot_infer_local_or_array.stderr index dafbab8278d1..c58900a66a2b 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_array.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_array.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[_; 0]` LL | let x = []; | ^ -- type must be known at this point | -help: consider giving `x` an explicit type, where the placeholders `_` are specified +help: consider giving `x` an explicit type, where the placeholder `_` is specified | LL | let x: [_; 0] = []; | ++++++++ diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr index fa90240d34e4..c4133bf4b56c 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Vec<_>` LL | let x = vec![]; | ^ ------ type must be known at this point | -help: consider giving `x` an explicit type, where the placeholders `_` are specified +help: consider giving `x` an explicit type, where the placeholder `_` is specified | LL | let x: Vec<_> = vec![]; | ++++++++ diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr index 5f389bee7107..2aa66799f4f6 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `(Vec<_>,)` LL | let (x, ) = (vec![], ); | ^^^^^ ---------- type must be known at this point | -help: consider giving this pattern a type, where the placeholders `_` are specified +help: consider giving this pattern a type, where the placeholder `_` is specified | LL | let (x, ): (Vec<_>,) = (vec![], ); | +++++++++++ diff --git a/tests/ui/typeck/issue-7813.stderr b/tests/ui/typeck/issue-7813.stderr index 953cbd206dc7..613a0c2fe483 100644 --- a/tests/ui/typeck/issue-7813.stderr +++ b/tests/ui/typeck/issue-7813.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `&[_; 0]` LL | let v = &[]; | ^ --- type must be known at this point | -help: consider giving `v` an explicit type, where the placeholders `_` are specified +help: consider giving `v` an explicit type, where the placeholder `_` is specified | LL | let v: &[_; 0] = &[]; | +++++++++ diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 739182e120b4..29b426d42d22 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -7,7 +7,7 @@ LL | let mut closure0 = None; LL | return c(); | - type must be known at this point | -help: consider giving `closure0` an explicit type, where the placeholders `_` are specified +help: consider giving `closure0` an explicit type, where the placeholder `_` is specified | LL | let mut closure0: Option = None; | +++++++++++