Skip to content

Commit 78e88f4

Browse files
committed
Only shown relevant type params in E0283 label
When we point at a binding to suggest giving it a type, erase all the type for ADTs that have been resolved, leaving only the ones that could not be inferred. For small shallow types this is not a problem, but for big nested types with lots of params, this can otherwise cause a lot of unnecessary visual output.
1 parent 0fb1c37 commit 78e88f4

File tree

5 files changed

+136
-3
lines changed

5 files changed

+136
-3
lines changed

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::hir::map::Map;
1111
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
1212
use rustc_middle::ty::print::Print;
1313
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
14-
use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
14+
use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
1515
use rustc_span::symbol::kw;
1616
use rustc_span::Span;
1717
use std::borrow::Cow;
@@ -629,6 +629,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
629629
format!("the explicit type `{}`, with the type parameters specified", ty)
630630
}
631631
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
632+
let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty);
633+
let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty);
632634
let ty = ty_to_string(ty);
633635
format!(
634636
"the explicit type `{}`, where the type parameter `{}` is specified",
@@ -908,3 +910,99 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
908910
err
909911
}
910912
}
913+
914+
/// Turn resolved type params into `[type error]` to signal we don't want to display them.
915+
struct ResolvedTypeParamEraser<'tcx> {
916+
tcx: TyCtxt<'tcx>,
917+
level: usize,
918+
}
919+
920+
impl<'tcx> ResolvedTypeParamEraser<'tcx> {
921+
fn new(tcx: TyCtxt<'tcx>) -> Self {
922+
ResolvedTypeParamEraser { tcx, level: 0 }
923+
}
924+
}
925+
impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> {
926+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
927+
self.tcx
928+
}
929+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
930+
self.level += 1;
931+
let t = match t.kind() {
932+
// We'll hide this type only if all its type params are hidden as well.
933+
ty::Adt(def, substs) => {
934+
let generics = self.tcx().generics_of(def.did);
935+
// Account for params with default values, like `Vec`, where we
936+
// want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that
937+
// subst, then we'd get the incorrect output, so we passthrough.
938+
let substs: Vec<_> = substs
939+
.iter()
940+
.zip(generics.params.iter())
941+
.map(|(subst, param)| match &param.kind {
942+
ty::GenericParamDefKind::Type { has_default: true, .. } => subst,
943+
_ => subst.super_fold_with(self),
944+
})
945+
.collect();
946+
if self.level == 1
947+
|| substs.iter().any(|subst| match subst.unpack() {
948+
ty::subst::GenericArgKind::Type(t) => match t.kind() {
949+
ty::Error(_) => false,
950+
_ => true,
951+
},
952+
// Account for `const` params here, otherwise `doesnt_infer.rs`
953+
// shows `_` instead of `Foo<{ _: u32 }>`
954+
ty::subst::GenericArgKind::Const(_) => true,
955+
_ => false,
956+
})
957+
{
958+
let substs = self.tcx().intern_substs(&substs[..]);
959+
self.tcx().mk_ty(ty::Adt(def, substs))
960+
} else {
961+
self.tcx().ty_error()
962+
}
963+
}
964+
ty::Ref(_, ty, _) => {
965+
let ty = self.fold_ty(ty);
966+
match ty.kind() {
967+
// Avoid `&_`, these can be safely presented as `_`.
968+
ty::Error(_) => self.tcx().ty_error(),
969+
_ => t.super_fold_with(self),
970+
}
971+
}
972+
// We could account for `()` if we wanted to replace it, but it's assured to be short.
973+
ty::Tuple(_)
974+
| ty::Slice(_)
975+
| ty::RawPtr(_)
976+
| ty::FnDef(..)
977+
| ty::FnPtr(_)
978+
| ty::Opaque(..)
979+
| ty::Projection(_)
980+
| ty::Never
981+
| ty::Array(..) => t.super_fold_with(self),
982+
// We don't want to hide type params that haven't been resolved yet.
983+
// This would be the type that will be written out with the type param
984+
// name in the output.
985+
ty::Infer(_) => t,
986+
// We don't want to hide the outermost type, only its type params.
987+
_ if self.level == 1 => t.super_fold_with(self),
988+
// Hide this type
989+
_ => self.tcx().ty_error(),
990+
};
991+
self.level -= 1;
992+
t
993+
}
994+
}
995+
996+
/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`.
997+
struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>);
998+
impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
999+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
1000+
self.0
1001+
}
1002+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
1003+
match t.kind() {
1004+
ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
1005+
_ => t.super_fold_with(self),
1006+
}
1007+
}
1008+
}

src/test/ui/const-generics/defaults/doesnt_infer.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Foo<{_: u32}>`
44
LL | let foo = Foo::foo();
55
| --- ^^^^^^^^ cannot infer the value of const parameter `N`
66
| |
7-
| consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified
7+
| consider giving `foo` the explicit type `Foo<{_: _}>`, where the type parameter `N` is specified
88

99
error: aborting due to previous error
1010

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn main() {
2+
let foo = new(1, ""); //~ ERROR E0283
3+
}
4+
5+
struct Bar<T, K, N: Default> {
6+
t: T,
7+
k: K,
8+
n: N,
9+
}
10+
11+
fn new<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
12+
Bar { t, k, n: Default::default() }
13+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0283]: type annotations needed for `Bar<i32, &str, Z>`
2+
--> $DIR/erase-type-params-in-label.rs:2:15
3+
|
4+
LL | let foo = new(1, "");
5+
| --- ^^^ cannot infer type for type parameter `Z` declared on the function `new`
6+
| |
7+
| consider giving `foo` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified
8+
|
9+
= note: cannot satisfy `_: Default`
10+
note: required by a bound in `new`
11+
--> $DIR/erase-type-params-in-label.rs:11:17
12+
|
13+
LL | fn new<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
14+
| ^^^^^^^ required by this bound in `new`
15+
help: consider specifying the type arguments in the function call
16+
|
17+
LL | let foo = new::<T, K, Z>(1, "");
18+
| +++++++++++
19+
20+
error: aborting due to previous error
21+
22+
For more information about this error, try `rustc --explain E0283`.

src/test/ui/inference/issue-83606.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[usize; _]`
44
LL | let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`!
55
| - ^^^ cannot infer the value of const parameter `N` declared on the function `foo`
66
| |
7-
| consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified
7+
| consider giving this pattern the explicit type `[_; _]`, where the type parameter `N` is specified
88

99
error: aborting due to previous error
1010

0 commit comments

Comments
 (0)