use rustc_middle::ty::{self, DefIdTree, InferConst};
use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
-use rustc_span::symbol::{kw, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span};
use std::borrow::Cow;
use std::iter;
fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
if in_type.is_ty_infer() {
- "empty"
+ ""
} else if self.name == "_" {
// FIXME: Consider specializing this message if there is a single `_`
// in the type.
printer
}
-fn ty_to_string<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
+fn ty_to_string<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ called_method_def_id: Option<DefId>,
+) -> String {
let printer = fmt_printer(infcx, Namespace::TypeNS);
let ty = infcx.resolve_vars_if_possible(ty);
- match ty.kind() {
+ match (ty.kind(), called_method_def_id) {
// We don't want the regular output for `fn`s because it includes its path in
// invalid pseudo-syntax, we want the `fn`-pointer output instead.
- ty::FnDef(..) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
+ (ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
+ (_, Some(def_id))
+ if ty.is_ty_infer()
+ && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
+ {
+ "Vec<_>".to_string()
+ }
+ _ if ty.is_ty_infer() => "/* Type */".to_string(),
// FIXME: The same thing for closures, but this only works when the closure
// does not capture anything.
//
.map(|args| {
args.tuple_fields()
.iter()
- .map(|arg| ty_to_string(infcx, arg))
+ .map(|arg| ty_to_string(infcx, arg, None))
.collect::<Vec<_>>()
.join(", ")
})
let ret = if fn_sig.output().skip_binder().is_unit() {
String::new()
} else {
- format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder()))
+ format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None))
};
format!("fn({}){}", args, ret)
}
}
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ #[instrument(level = "debug", skip(self, error_code))]
pub fn emit_inference_failure_err(
&self,
body_id: Option<hir::BodyId>,
let mut infer_subdiags = Vec::new();
let mut multi_suggestions = Vec::new();
match kind {
- InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
+ InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => {
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
- type_name: ty_to_string(self, ty),
+ type_name: ty_to_string(self, ty, def_id),
});
}
InferSourceKind::ClosureArg { insert_span, ty } => {
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
kind: "closure",
- type_name: ty_to_string(self, ty),
+ type_name: ty_to_string(self, ty, None),
});
}
InferSourceKind::GenericArg {
parent_name,
});
- let args = fmt_printer(self, Namespace::TypeNS)
- .comma_sep(generic_args.iter().copied().map(|arg| {
- if arg.is_suggestable(self.tcx, true) {
- return arg;
- }
+ let args = if self.infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn)
+ == Some(generics_def_id)
+ {
+ "Vec<_>".to_string()
+ } else {
+ fmt_printer(self, Namespace::TypeNS)
+ .comma_sep(generic_args.iter().copied().map(|arg| {
+ if arg.is_suggestable(self.tcx, true) {
+ return arg;
+ }
- match arg.unpack() {
- GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
- GenericArgKind::Type(_) => self
- .next_ty_var(TypeVariableOrigin {
- span: rustc_span::DUMMY_SP,
- kind: TypeVariableOriginKind::MiscVariable,
- })
- .into(),
- GenericArgKind::Const(arg) => self
- .next_const_var(
- arg.ty(),
- ConstVariableOrigin {
+ match arg.unpack() {
+ GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
+ GenericArgKind::Type(_) => self
+ .next_ty_var(TypeVariableOrigin {
span: rustc_span::DUMMY_SP,
- kind: ConstVariableOriginKind::MiscVariable,
- },
- )
- .into(),
- }
- }))
- .unwrap()
- .into_buffer();
+ kind: TypeVariableOriginKind::MiscVariable,
+ })
+ .into(),
+ GenericArgKind::Const(arg) => self
+ .next_const_var(
+ arg.ty(),
+ ConstVariableOrigin {
+ span: rustc_span::DUMMY_SP,
+ kind: ConstVariableOriginKind::MiscVariable,
+ },
+ )
+ .into(),
+ }
+ }))
+ .unwrap()
+ .into_buffer()
+ };
if !have_turbofish {
infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
));
}
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
- let ty_info = ty_to_string(self, ty);
+ let ty_info = ty_to_string(self, ty, None);
multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return(
ty_info,
data,
insert_span: Span,
pattern_name: Option<Ident>,
ty: Ty<'tcx>,
+ def_id: Option<DefId>,
},
ClosureArg {
insert_span: Span,
if ty.is_closure() {
("closure", closure_as_fn_str(infcx, ty))
} else if !ty.is_ty_infer() {
- ("normal", ty_to_string(infcx, ty))
+ ("normal", ty_to_string(infcx, ty, None))
} else {
("other", String::new())
}
/// Uses `fn source_cost` to determine whether this inference source is preferable to
/// previous sources. We generally prefer earlier sources.
#[instrument(level = "debug", skip(self))]
- fn update_infer_source(&mut self, new_source: InferSource<'tcx>) {
+ fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) {
let cost = self.source_cost(&new_source) + self.attempt;
debug!(?cost);
self.attempt += 1;
+ if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, ..}, .. }) = self.infer_source
+ && let InferSourceKind::LetBinding { ref ty, ref mut def_id, ..} = new_source.kind
+ && ty.is_ty_infer()
+ {
+ // Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of
+ // `let x: _ = iter.collect();`, as this is a very common case.
+ *def_id = Some(did);
+ }
if cost < self.infer_source_cost {
self.infer_source_cost = cost;
self.infer_source = Some(new_source);
insert_span: local.pat.span.shrink_to_hi(),
pattern_name: local.pat.simple_ident(),
ty,
+ def_id: None,
},
})
}