Skip to content

Commit 488d0c9

Browse files
committed
Type-directed probing for inherent associated types
1 parent eebdfb5 commit 488d0c9

11 files changed

+581
-54
lines changed

compiler/rustc_hir_analysis/src/astconv/errors.rs

Lines changed: 164 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::astconv::AstConv;
22
use crate::errors::{ManualImplementation, MissingTypeParams};
33
use rustc_data_structures::fx::FxHashMap;
4-
use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
4+
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
55
use rustc_hir as hir;
66
use rustc_hir::def_id::DefId;
7-
use rustc_middle::ty;
7+
use rustc_middle::ty::{self, Ty};
88
use rustc_session::parse::feature_err;
99
use rustc_span::lev_distance::find_best_match_for_name;
1010
use rustc_span::symbol::{sym, Ident};
@@ -221,6 +221,168 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
221221
err.emit()
222222
}
223223

224+
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
225+
pub(crate) fn complain_about_inherent_assoc_type_not_found(
226+
&self,
227+
name: Ident,
228+
self_ty: Ty<'tcx>,
229+
candidates: &[DefId],
230+
unsatisfied_predicates: Vec<ty::Predicate<'tcx>>,
231+
span: Span,
232+
) -> ErrorGuaranteed {
233+
let tcx = self.tcx();
234+
235+
let adt_did = self_ty.ty_adt_def().map(|def| def.did());
236+
let add_def_label = |err: &mut Diagnostic| {
237+
if let Some(did) = adt_did {
238+
err.span_label(
239+
tcx.def_span(did),
240+
format!(
241+
"associated item `{name}` not found for this {}",
242+
tcx.def_kind(did).descr(did)
243+
),
244+
);
245+
}
246+
};
247+
248+
if unsatisfied_predicates.is_empty() {
249+
// FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
250+
251+
let limit = if candidates.len() == 5 { 5 } else { 4 };
252+
let type_candidates = candidates
253+
.iter()
254+
.take(limit)
255+
.map(|candidate| {
256+
format!("- `{}`", tcx.at(span).type_of(candidate).subst_identity())
257+
})
258+
.collect::<Vec<_>>()
259+
.join("\n");
260+
let additional_types = if candidates.len() > limit {
261+
format!("\nand {} more types", candidates.len() - limit)
262+
} else {
263+
String::new()
264+
};
265+
266+
let mut err = struct_span_err!(
267+
tcx.sess,
268+
name.span,
269+
E0220,
270+
"associated type `{name}` not found for `{self_ty}` in the current scope"
271+
);
272+
err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
273+
err.note(&format!(
274+
"the associated type was found for\n{type_candidates}{additional_types}",
275+
));
276+
add_def_label(&mut err);
277+
return err.emit();
278+
}
279+
280+
let mut bound_spans = Vec::new();
281+
282+
// FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
283+
let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
284+
let msg = format!(
285+
"doesn't satisfy `{}`",
286+
if obligation.len() > 50 { quiet } else { obligation }
287+
);
288+
match &self_ty.kind() {
289+
// Point at the type that couldn't satisfy the bound.
290+
ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)),
291+
// Point at the trait object that couldn't satisfy the bound.
292+
ty::Dynamic(preds, _, _) => {
293+
for pred in preds.iter() {
294+
match pred.skip_binder() {
295+
ty::ExistentialPredicate::Trait(tr) => {
296+
bound_spans.push((tcx.def_span(tr.def_id), msg.clone()))
297+
}
298+
ty::ExistentialPredicate::Projection(_)
299+
| ty::ExistentialPredicate::AutoTrait(_) => {}
300+
}
301+
}
302+
}
303+
// Point at the closure that couldn't satisfy the bound.
304+
ty::Closure(def_id, _) => {
305+
bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`")))
306+
}
307+
_ => {}
308+
}
309+
};
310+
311+
// FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
312+
let format_pred = |pred: ty::Predicate<'tcx>| {
313+
let bound_predicate = pred.kind();
314+
match bound_predicate.skip_binder() {
315+
ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
316+
let pred = bound_predicate.rebind(pred);
317+
// `<Foo as Iterator>::Item = String`.
318+
let projection_ty = pred.skip_binder().projection_ty;
319+
320+
let substs_with_infer_self = tcx.mk_substs(
321+
std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
322+
.chain(projection_ty.substs.iter().skip(1)),
323+
);
324+
325+
let quiet_projection_ty =
326+
tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self);
327+
328+
let term = pred.skip_binder().term;
329+
330+
let obligation = format!("{projection_ty} = {term}");
331+
let quiet = format!("{quiet_projection_ty} = {term}");
332+
333+
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
334+
Some((obligation, projection_ty.self_ty()))
335+
}
336+
ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
337+
let p = poly_trait_ref.trait_ref;
338+
let self_ty = p.self_ty();
339+
let path = p.print_only_trait_path();
340+
let obligation = format!("{self_ty}: {path}");
341+
let quiet = format!("_: {path}");
342+
bound_span_label(self_ty, &obligation, &quiet);
343+
Some((obligation, self_ty))
344+
}
345+
_ => None,
346+
}
347+
};
348+
349+
// FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds.
350+
// I would do the same here if it didn't mean more code duplication.
351+
let mut bounds: Vec<_> = unsatisfied_predicates
352+
.into_iter()
353+
.filter_map(format_pred)
354+
.map(|(p, _)| format!("`{}`", p))
355+
.collect();
356+
bounds.sort();
357+
bounds.dedup();
358+
359+
let mut err = tcx.sess.struct_span_err(
360+
name.span,
361+
&format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
362+
);
363+
if !bounds.is_empty() {
364+
err.note(&format!(
365+
"the following trait bounds were not satisfied:\n{}",
366+
bounds.join("\n")
367+
));
368+
}
369+
err.span_label(
370+
name.span,
371+
format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
372+
);
373+
374+
bound_spans.sort();
375+
bound_spans.dedup();
376+
for (span, msg) in bound_spans {
377+
if !tcx.sess.source_map().is_span_accessible(span) {
378+
continue;
379+
}
380+
err.span_label(span, &msg);
381+
}
382+
add_def_label(&mut err);
383+
err.emit()
384+
}
385+
224386
/// When there are any missing associated types, emit an E0191 error and attempt to supply a
225387
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
226388
/// same trait bound have the same name (as they come from different supertraits), we instead

0 commit comments

Comments
 (0)