add new emit_inference_failure_err

This commit is contained in:
lcnr 2022-02-14 13:25:26 +01:00
parent 721fc73208
commit 3fe346e7a3
124 changed files with 2113 additions and 629 deletions

View file

@ -1,21 +1,22 @@
use crate::infer::type_variable::TypeVariableOriginKind;
use crate::infer::{InferCtxt, Symbol};
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
};
use crate::infer::InferCtxt;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def::{CtorOf, DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, MatchSource, Pat};
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
use rustc_middle::hir::nested_filter;
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
use rustc_middle::ty::print::Print;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, InferConst};
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_span::symbol::{kw, Ident};
use rustc_span::{BytePos, Span};
use std::borrow::Cow;
use std::iter;
pub enum TypeAnnotationNeeded {
/// ```compile_fail,E0282
@ -54,9 +55,8 @@ pub struct InferenceDiagnosticsData {
/// Data on the parent definition where a generic argument was declared.
pub struct InferenceDiagnosticsParentData {
pub prefix: &'static str,
pub name: String,
pub def_id: DefId,
prefix: &'static str,
name: String,
}
pub enum UnderspecifiedArgKind {
@ -81,6 +81,18 @@ impl InferenceDiagnosticsData {
// For example: "cannot infer type for type parameter `T`"
format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
}
fn where_x_is_specified(&self, in_type: Ty<'_>) -> String {
if in_type.is_ty_infer() {
String::new()
} else if self.name == "_" {
// FIXME: Consider specializing this message if there is a single `_`
// in the type.
", where the placeholders `_` are specified".to_string()
} else {
format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name)
}
}
}
impl InferenceDiagnosticsParentData {
@ -94,7 +106,6 @@ impl InferenceDiagnosticsParentData {
Some(InferenceDiagnosticsParentData {
prefix: tcx.def_kind(parent_def_id).descr(parent_def_id),
name: parent_name,
def_id: parent_def_id,
})
}
@ -117,6 +128,80 @@ impl UnderspecifiedArgKind {
}
}
fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
let mut printer = FmtPrinter::new(infcx.tcx, ns);
let ty_getter = move |ty_vid| {
if infcx.probe_ty_var(ty_vid).is_ok() {
warn!("resolved ty var in error message");
}
if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
{
Some(name.to_string())
} else {
None
}
};
printer.ty_infer_name_resolver = Some(Box::new(ty_getter));
let const_getter = move |ct_vid| {
if infcx.probe_const_var(ct_vid).is_ok() {
warn!("resolved const var in error message");
}
if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
{
return Some(name.to_string());
} else {
None
}
};
printer.const_infer_name_resolver = Some(Box::new(const_getter));
printer
}
fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
let printer = fmt_printer(infcx, Namespace::TypeNS);
let ty = infcx.resolve_vars_if_possible(ty);
match ty.kind() {
// 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(),
// FIXME: The same thing for closures, but this only works when the closure
// does not capture anything.
//
// We do have to hide the `extern "rust-call"` ABI in that case though,
// which is too much of a bother for now.
_ => ty.print(printer).unwrap().into_buffer(),
}
}
/// We don't want to directly use `ty_to_string` for closures as their type isn't really
/// something users are familar with. Directly printing the `fn_sig` of closures also
/// doesn't work as they actually use the "rust-call" API.
fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
let ty::Closure(_, substs) = ty.kind() else { unreachable!() };
let fn_sig = substs.as_closure().sig();
let args = fn_sig
.inputs()
.skip_binder()
.iter()
.next()
.map(|args| {
args.tuple_fields()
.iter()
.map(|arg| ty_to_string(infcx, arg))
.collect::<Vec<_>>()
.join(", ")
})
.unwrap_or_default();
let ret = if fn_sig.output().skip_binder().is_unit() {
String::new()
} else {
format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder()))
};
format!("fn({}){}", args, ret)
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// Extracts data used by diagnostic for either types or constants
/// which were stuck during inference.
@ -153,9 +238,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
if let Some(highlight) = highlight {
printer.region_highlight_mode = highlight;
}
let name = ty.print(printer).unwrap().into_buffer();
InferenceDiagnosticsData {
name,
name: ty.print(printer).unwrap().into_buffer(),
span: None,
kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
parent: None,
@ -177,8 +261,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
debug_assert!(!origin.span.is_dummy());
let mut printer =
ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
if let Some(highlight) = highlight {
printer.region_highlight_mode = highlight;
}
@ -189,7 +272,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
parent: None,
}
} else {
bug!("unexpect const: {:?}", ct);
// FIXME: This code seems a bit wrong, idk.
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
if let Some(highlight) = highlight {
printer.region_highlight_mode = highlight;
}
InferenceDiagnosticsData {
name: ct.print(printer).unwrap().into_buffer(),
span: None,
kind: UnderspecifiedArgKind::Const { is_parameter: false },
parent: None,
}
}
}
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
@ -201,17 +294,160 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
body_id: Option<hir::BodyId>,
span: Span,
arg: GenericArg<'tcx>,
impl_candidates: Vec<ty::TraitRef<'tcx>>,
// FIXME(#94483): Either use this or remove it.
_impl_candidates: Vec<ty::TraitRef<'tcx>>,
error_code: TypeAnnotationNeeded,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let arg = self.resolve_vars_if_possible(arg);
let arg_data = self.extract_inference_diagnostics_data(arg, None);
let mut local_visitor = FindInferSourceVisitor::new(&self, arg);
if let Some(body_id) = body_id {
let expr = self.tcx.hir().expect_expr(body_id.hir_id);
debug!(?expr);
local_visitor.visit_expr(expr);
}
let Some(InferSource { span, kind }) = local_visitor.infer_source else {
let error_code = error_code.into();
let mut err = self.tcx.sess.struct_span_err_with_code(
span,
&format!("type annotations needed"),
error_code,
);
err.span_label(
span,
arg_data.cannot_infer_msg(),
);
return err;
};
let error_code = error_code.into();
let err = self.tcx.sess.struct_span_err_with_code(
let mut err = self.tcx.sess.struct_span_err_with_code(
span,
&format!("type annotations needed"),
&format!("type annotations needed{}", kind.ty_msg(self)),
error_code,
);
match kind {
InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
let suggestion_msg = if let Some(name) = pattern_name {
format!(
"consider giving `{}` an explicit type{}",
name,
arg_data.where_x_is_specified(ty)
)
} else {
format!(
"consider giving this pattern a type{}",
arg_data.where_x_is_specified(ty)
)
};
err.span_suggestion_verbose(
insert_span,
&suggestion_msg,
format!(": {}", ty_to_string(self, ty)),
Applicability::HasPlaceholders,
);
}
InferSourceKind::ClosureArg { insert_span, ty } => {
err.span_suggestion_verbose(
insert_span,
&format!(
"consider giving this closure parameter an explicit type{}",
arg_data.where_x_is_specified(ty)
),
format!(": {}", ty_to_string(self, ty)),
Applicability::HasPlaceholders,
);
}
InferSourceKind::GenericArg {
insert_span,
argument_index,
generics_def_id,
def_id: _,
generic_args,
} => {
let generics = self.tcx.generics_of(generics_def_id);
let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
let cannot_infer_msg = format!(
"cannot infer {} of the {} parameter `{}`{}",
if is_type { "type" } else { "the value" },
if is_type { "type" } else { "const" },
generics.params[argument_index].name,
// We use the `generics_def_id` here, as even when suggesting `None::<T>`,
// the type parameter `T` was still declared on the enum, not on the
// variant.
InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
.map_or(String::new(), |parent| parent.suffix_string()),
);
err.span_label(span, cannot_infer_msg);
let printer = fmt_printer(self, Namespace::TypeNS);
let args = printer.comma_sep(generic_args.iter().copied()).unwrap().into_buffer();
err.span_suggestion_verbose(
insert_span,
&format!("consider specifying the generic argument{}", pluralize!(args.len()),),
format!("::<{}>", args),
Applicability::HasPlaceholders,
);
}
InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => {
let typeck_results = self.in_progress_typeck_results.unwrap();
let typeck_results = typeck_results.borrow();
let printer = fmt_printer(self, Namespace::ValueNS);
let def_path = printer.print_def_path(def_id, substs).unwrap().into_buffer();
// We only care about whether we have to add `&` or `&mut ` for now.
// This is the case if the last adjustment is a borrow and the
// first adjustment was not a builtin deref.
let adjustment = match typeck_results.expr_adjustments(receiver) {
[
Adjustment { kind: Adjust::Deref(None), target: _ },
..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ },
] => "",
[
..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ },
] => match mut_ {
AutoBorrowMutability::Mut { .. } => "&mut ",
AutoBorrowMutability::Not => "&",
},
_ => "",
};
let suggestion = vec![
(receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")),
(receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
];
err.multipart_suggestion(
"try using a fully qualified path to specify the expected types",
suggestion,
Applicability::HasPlaceholders,
);
}
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
let ret = ty_to_string(self, ty);
let (arrow, post) = match data {
FnRetTy::DefaultReturn(_) => ("-> ", " "),
_ => ("", ""),
};
let suggestion = match should_wrap_expr {
Some(end_span) => vec![
(data.span(), format!("{}{}{}{{ ", arrow, ret, post)),
(end_span, " }".to_string()),
],
None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))],
};
err.multipart_suggestion(
"try giving this closure an explicit return type",
suggestion,
Applicability::HasPlaceholders,
);
}
}
err
}
@ -235,3 +471,550 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
err
}
}
#[derive(Debug)]
struct InferSource<'tcx> {
span: Span,
kind: InferSourceKind<'tcx>,
}
#[derive(Debug)]
enum InferSourceKind<'tcx> {
LetBinding {
insert_span: Span,
pattern_name: Option<Ident>,
ty: Ty<'tcx>,
},
ClosureArg {
insert_span: Span,
ty: Ty<'tcx>,
},
GenericArg {
insert_span: Span,
argument_index: usize,
generics_def_id: DefId,
def_id: DefId,
generic_args: &'tcx [GenericArg<'tcx>],
},
FullyQualifiedMethodCall {
receiver: &'tcx Expr<'tcx>,
/// If the method has other arguments, this is ", " and the start of the first argument,
/// while for methods without arguments this is ")" and the end of the method call.
successor: (&'static str, BytePos),
substs: SubstsRef<'tcx>,
def_id: DefId,
},
ClosureReturn {
ty: Ty<'tcx>,
data: &'tcx FnRetTy<'tcx>,
should_wrap_expr: Option<Span>,
},
}
impl<'tcx> InferSourceKind<'tcx> {
fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
match *self {
InferSourceKind::LetBinding { ty, .. }
| InferSourceKind::ClosureArg { ty, .. }
| InferSourceKind::ClosureReturn { ty, .. } => {
if ty.is_closure() {
format!(" for the closure `{}`", closure_as_fn_str(infcx, ty))
} else if !ty.is_ty_infer() {
format!(" for `{}`", ty_to_string(infcx, ty))
} else {
String::new()
}
}
// FIXME: We should be able to add some additional info here.
InferSourceKind::GenericArg { .. }
| InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(),
}
}
}
struct InsertableGenericArgs<'tcx> {
insert_span: Span,
substs: SubstsRef<'tcx>,
generics_def_id: DefId,
def_id: DefId,
}
/// A visitor which searches for the "best" spot to use in the inference error.
///
/// For this it walks over the hir body and tries to check all places where
/// inference variables could be bound.
///
/// While doing so, the currently best spot is stored in `infer_source`.
/// For details on how we rank spots, see [Self::source_cost]
struct FindInferSourceVisitor<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
target: GenericArg<'tcx>,
attempt: usize,
infer_source_cost: usize,
infer_source: Option<InferSource<'tcx>>,
}
impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
fn new(infcx: &'a InferCtxt<'a, 'tcx>, target: GenericArg<'tcx>) -> Self {
FindInferSourceVisitor {
infcx,
target,
attempt: 0,
infer_source_cost: usize::MAX,
infer_source: None,
}
}
/// Computes cost for the given source.
///
/// Sources with a small cost are prefer and should result
/// in a clearer and idiomatic suggestion.
fn source_cost(&self, source: &InferSource<'tcx>) -> usize {
let tcx = self.infcx.tcx;
fn arg_cost<'tcx>(arg: GenericArg<'tcx>) -> usize {
match arg.unpack() {
GenericArgKind::Lifetime(_) => 0, // erased
GenericArgKind::Type(ty) => ty_cost(ty),
GenericArgKind::Const(_) => 3, // some non-zero value
}
}
fn ty_cost<'tcx>(ty: Ty<'tcx>) -> usize {
match ty.kind() {
ty::Closure(..) => 100,
ty::FnDef(..) => 20,
ty::FnPtr(..) => 10,
ty::Infer(..) => 0,
_ => 1,
}
}
// The sources are listed in order of preference here.
match source.kind {
InferSourceKind::LetBinding { ty, .. } => ty_cost(ty),
InferSourceKind::ClosureArg { ty, .. } => 5 + ty_cost(ty),
InferSourceKind::GenericArg { def_id, generic_args, .. } => {
let variant_cost = match tcx.def_kind(def_id) {
DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, // `None::<u32>` and friends are ugly.
_ => 12,
};
variant_cost + generic_args.iter().map(|&arg| arg_cost(arg)).sum::<usize>()
}
InferSourceKind::FullyQualifiedMethodCall { substs, .. } => {
// FIXME: We should also consider the cost of lifetimes and constants here.
20 + substs.iter().map(|arg| arg_cost(arg)).sum::<usize>()
}
InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => {
30 + ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
}
}
}
/// 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>) {
let cost = self.source_cost(&new_source) + self.attempt;
self.attempt += 1;
if cost < self.infer_source_cost {
self.infer_source_cost = cost;
self.infer_source = Some(new_source);
}
}
fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
let ty = self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id);
self.infcx.resolve_vars_if_possible(ty)
}
// Check whether this generic argument is the inference variable we
// are looking for.
fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool {
if arg == self.target {
return true;
}
match (arg.unpack(), self.target.unpack()) {
(GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
use ty::{Infer, TyVar};
match (inner_ty.kind(), target_ty.kind()) {
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid)
}
_ => false,
}
}
(GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
use ty::InferConst::*;
match (inner_ct.val(), target_ct.val()) {
(ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self
.infcx
.inner
.borrow_mut()
.const_unification_table()
.unioned(a_vid, b_vid),
_ => false,
}
}
_ => false,
}
}
/// Does this generic argument contain our target inference variable
/// in a way which can be written by the user.
fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool {
let mut walker = arg.walk();
while let Some(inner) = walker.next() {
if self.generic_arg_is_target(inner) {
return true;
}
match inner.unpack() {
GenericArgKind::Lifetime(_) => {}
GenericArgKind::Type(ty) => {
if matches!(ty.kind(), ty::Opaque(..)) {
// Opaque types can't be named by the user right now
// FIXME(type_alias_impl_trait): These opaque types
// can actually be named, so it would make sense to
// adjust this case and add a test for it.
walker.skip_current_subtree();
}
}
GenericArgKind::Const(ct) => {
if matches!(ct.val(), ty::ConstKind::Unevaluated(..)) {
// You can't write the generic arguments for
// unevaluated constants.
walker.skip_current_subtree();
}
}
}
}
false
}
fn expr_inferred_subst_iter(
&self,
expr: &'tcx hir::Expr<'tcx>,
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
let tcx = self.infcx.tcx;
let typeck_results = self.infcx.in_progress_typeck_results.unwrap().borrow();
match expr.kind {
hir::ExprKind::Path(ref path) => {
if let Some(substs) = typeck_results.node_substs_opt(expr.hir_id) {
return self.path_inferred_subst_iter(expr.hir_id, substs, path);
}
}
hir::ExprKind::Struct(path, _, _) => {
if let Some(ty) = self.opt_node_type(expr.hir_id) {
if let ty::Adt(_, substs) = ty.kind() {
return self.path_inferred_subst_iter(expr.hir_id, substs, path);
}
}
}
hir::ExprKind::MethodCall(segment, _, _) => {
if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
let generics = tcx.generics_of(def_id);
let insertable: Option<_> = try {
if generics.has_impl_trait() {
None?
}
let substs = typeck_results.node_substs_opt(expr.hir_id)?;
let span = tcx.hir().span(segment.hir_id?);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
InsertableGenericArgs {
insert_span,
substs,
generics_def_id: def_id,
def_id,
}
};
return Box::new(insertable.into_iter());
}
}
_ => {}
}
Box::new(iter::empty())
}
fn resolved_path_inferred_subst_iter(
&self,
path: &'tcx hir::Path<'tcx>,
substs: SubstsRef<'tcx>,
) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
let tcx = self.infcx.tcx;
// The last segment of a path often has `Res::Err` and the
// correct `Res` is the one of the whole path.
//
// FIXME: We deal with that one separately for now,
// would be good to remove this special case.
let last_segment_using_path_data: Option<_> = try {
let generics_def_id = tcx.res_generics_def_id(path.res)?;
let generics = tcx.generics_of(generics_def_id);
if generics.has_impl_trait() {
None?
}
let insert_span =
path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi());
InsertableGenericArgs {
insert_span,
substs,
generics_def_id,
def_id: path.res.def_id(),
}
};
path.segments
.iter()
.filter_map(move |segment| {
let res = segment.res?;
let generics_def_id = tcx.res_generics_def_id(res)?;
let generics = tcx.generics_of(generics_def_id);
if generics.has_impl_trait() {
return None;
}
let span = tcx.hir().span(segment.hir_id?);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
Some(InsertableGenericArgs {
insert_span,
substs,
generics_def_id,
def_id: res.def_id(),
})
})
.chain(last_segment_using_path_data)
}
fn path_inferred_subst_iter(
&self,
hir_id: HirId,
substs: SubstsRef<'tcx>,
qpath: &'tcx hir::QPath<'tcx>,
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
let tcx = self.infcx.tcx;
let typeck_results = self.infcx.in_progress_typeck_results.unwrap().borrow();
match qpath {
hir::QPath::Resolved(_self_ty, path) => {
Box::new(self.resolved_path_inferred_subst_iter(path, substs))
}
hir::QPath::TypeRelative(ty, segment) => {
let Some(def_id) = typeck_results.type_dependent_def_id(hir_id) else {
return Box::new(iter::empty());
};
let generics = tcx.generics_of(def_id);
let segment: Option<_> = try {
if !segment.infer_args || generics.has_impl_trait() {
None?;
}
let span = tcx.hir().span(segment.hir_id?);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id }
};
let parent_def_id = generics.parent.unwrap();
if tcx.def_kind(parent_def_id) == DefKind::Impl {
let parent_ty = tcx.bound_type_of(parent_def_id).subst(tcx, substs);
match (parent_ty.kind(), &ty.kind) {
(
ty::Adt(def, substs),
hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)),
) => {
if tcx.res_generics_def_id(path.res) != Some(def.did()) {
bug!(
"unexpected path: def={:?} substs={:?} path={:?}",
def,
substs,
path,
);
} else {
return Box::new(
self.resolved_path_inferred_subst_iter(path, substs)
.chain(segment),
);
}
}
_ => (),
}
}
Box::new(segment.into_iter())
}
hir::QPath::LangItem(_, _, _) => Box::new(iter::empty()),
}
}
}
impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.infcx.tcx.hir()
}
fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
intravisit::walk_local(self, local);
if let Some(ty) = self.opt_node_type(local.hir_id) {
if self.generic_arg_contains_target(ty.into()) {
match local.source {
LocalSource::Normal if local.ty.is_none() => {
self.update_infer_source(InferSource {
span: local.pat.span,
kind: InferSourceKind::LetBinding {
insert_span: local.pat.span.shrink_to_hi(),
pattern_name: local.pat.simple_ident(),
ty,
},
})
}
_ => {}
}
}
}
}
/// For closures, we first visit the parameters and then the content,
/// as we prefer those.
fn visit_body(&mut self, body: &'tcx Body<'tcx>) {
for param in body.params {
debug!(
"param: span {:?}, ty_span {:?}, pat.span {:?}",
param.span, param.ty_span, param.pat.span
);
if param.ty_span != param.pat.span {
debug!("skipping param: has explicit type");
continue;
}
let Some(param_ty) = self.opt_node_type(param.hir_id) else {
continue
};
if self.generic_arg_contains_target(param_ty.into()) {
self.update_infer_source(InferSource {
span: param.pat.span,
kind: InferSourceKind::ClosureArg {
insert_span: param.pat.span.shrink_to_hi(),
ty: param_ty,
},
})
}
}
intravisit::walk_body(self, body);
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
let tcx = self.infcx.tcx;
match expr.kind {
// When encountering `func(arg)` first look into `arg` and then `func`,
// as `arg` is "more specific".
ExprKind::Call(func, args) => {
for arg in args {
self.visit_expr(arg);
}
self.visit_expr(func);
}
_ => intravisit::walk_expr(self, expr),
}
for InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } in
self.expr_inferred_subst_iter(expr)
{
let generics = tcx.generics_of(generics_def_id);
if let Some(argument_index) =
generics.own_substs(substs).iter().position(|&arg| self.generic_arg_is_target(arg))
{
let substs = self.infcx.resolve_vars_if_possible(substs);
let num_args = generics
.params
.iter()
.rev()
.filter(|&p| !matches!(p.kind, GenericParamDefKind::Lifetime))
.skip_while(|&param| {
if let Some(default) = param.default_value(tcx) {
// FIXME: Using structural comparisions has a bunch of false negatives.
//
// We should instead try to replace inference variables with placeholders and
// then use `infcx.can_eq`. That probably should be a separate method
// generally used during error reporting.
default.subst(tcx, substs) == substs[param.index as usize]
} else {
false
}
})
.count();
let generic_args =
&generics.own_substs(substs)[generics.own_counts().lifetimes..][..num_args];
let span = match expr.kind {
ExprKind::MethodCall(path, _, _) => path.ident.span,
_ => expr.span,
};
self.update_infer_source(InferSource {
span,
kind: InferSourceKind::GenericArg {
insert_span,
argument_index,
generics_def_id,
def_id,
generic_args,
},
});
}
}
if let Some(node_ty) = self.opt_node_type(expr.hir_id) {
if let (&ExprKind::Closure(_, decl, body_id, span, _), ty::Closure(_, substs)) =
(&expr.kind, node_ty.kind())
{
let output = substs.as_closure().sig().output().skip_binder();
if self.generic_arg_contains_target(output.into()) {
let body = self.infcx.tcx.hir().body(body_id);
let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
None
} else {
Some(body.value.span.shrink_to_hi())
};
self.update_infer_source(InferSource {
span,
kind: InferSourceKind::ClosureReturn {
ty: output,
data: &decl.output,
should_wrap_expr,
},
})
}
}
}
let has_impl_trait = |def_id| {
iter::successors(Some(tcx.generics_of(def_id)), |generics| {
generics.parent.map(|def_id| tcx.generics_of(def_id))
})
.any(|generics| generics.has_impl_trait())
};
if let ExprKind::MethodCall(path, args, span) = expr.kind
&& let Some(typeck_results) = self.infcx.in_progress_typeck_results
&& let Some(substs) = typeck_results.borrow().node_substs_opt(expr.hir_id)
&& substs.iter().any(|arg| self.generic_arg_contains_target(arg))
&& let Some(def_id) = typeck_results.borrow().type_dependent_def_id(expr.hir_id)
&& self.infcx.tcx.trait_of_item(def_id).is_some()
&& !has_impl_trait(def_id)
{
let successor =
args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
let substs = self.infcx.resolve_vars_if_possible(substs);
self.update_infer_source(InferSource {
span: path.ident.span,
kind: InferSourceKind::FullyQualifiedMethodCall {
receiver: args.first().unwrap(),
successor,
substs,
def_id,
}
})
}
}
}

View file

@ -22,6 +22,7 @@
#![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(try_blocks)]
#![recursion_limit = "512"] // For rustdoc
#[macro_use]