Skip to content

Commit 10368c6

Browse files
committed
Point at method chains on E0271 errors
1 parent ba64ba8 commit 10368c6

File tree

3 files changed

+139
-89
lines changed

3 files changed

+139
-89
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+120-89
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,14 @@ pub trait TypeErrCtxtExt<'tcx> {
352352
param_env: ty::ParamEnv<'tcx>,
353353
err: &mut Diagnostic,
354354
);
355+
fn probe_assoc_types_at_expr(
356+
&self,
357+
type_diffs: &[TypeError<'tcx>],
358+
span: Span,
359+
prev_ty: Ty<'tcx>,
360+
body_id: hir::HirId,
361+
param_env: ty::ParamEnv<'tcx>,
362+
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
355363
}
356364

357365
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -3152,23 +3160,37 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
31523160
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
31533161
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
31543162
&& let Some(pred) = predicates.predicates.get(*idx)
3155-
&& let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred {
3156-
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
3157-
_ => Err(()),
3158-
})
31593163
{
3160-
let mut c = CollectAllMismatches {
3161-
infcx: self.infcx,
3162-
param_env,
3163-
errors: vec![],
3164-
};
3165-
if let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred {
3164+
if let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred {
31663165
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
31673166
_ => Err(()),
3168-
}) {
3167+
})
3168+
&& let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred {
3169+
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
3170+
_ => Err(()),
3171+
})
3172+
{
3173+
let mut c = CollectAllMismatches {
3174+
infcx: self.infcx,
3175+
param_env,
3176+
errors: vec![],
3177+
};
31693178
if let Ok(_) = c.relate(trait_pred, trait_predicate) {
31703179
type_diffs = c.errors;
31713180
}
3181+
} else if let ty::PredicateKind::Clause(
3182+
ty::Clause::Projection(proj)
3183+
) = pred.kind().skip_binder()
3184+
&& let ty::PredicateKind::Clause(
3185+
ty::Clause::Projection(projection)
3186+
) = predicate.kind().skip_binder()
3187+
{
3188+
type_diffs = vec![
3189+
Sorts(ty::error::ExpectedFound {
3190+
expected: self.tcx.mk_ty(ty::Alias(ty::Projection, proj.projection_ty)),
3191+
found: projection.term.ty().unwrap(),
3192+
}),
3193+
];
31723194
}
31733195
}
31743196
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
@@ -3221,10 +3243,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
32213243

32223244
let tcx = self.tcx;
32233245

3246+
let mut print_root_expr = true;
32243247
let mut assocs = vec![];
3225-
// We still want to point at the different methods even if there hasn't
3226-
// been a change of assoc type.
3227-
let mut call_spans = vec![];
32283248
let mut expr = expr;
32293249
let mut prev_ty = self.resolve_vars_if_possible(
32303250
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()),
@@ -3234,64 +3254,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
32343254
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
32353255
// ^^^^^^ ^^^^^^^^^^^
32363256
expr = rcvr_expr;
3237-
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
3238-
call_spans.push(span);
3239-
3240-
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
3241-
for diff in &type_diffs {
3242-
let Sorts(expected_found) = diff else { continue; };
3243-
let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; };
3244-
3245-
let origin =
3246-
TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
3247-
let trait_def_id = proj.trait_def_id(self.tcx);
3248-
// Make `Self` be equivalent to the type of the call chain
3249-
// expression we're looking at now, so that we can tell what
3250-
// for example `Iterator::Item` is at this point in the chain.
3251-
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
3252-
match param.kind {
3253-
ty::GenericParamDefKind::Type { .. } => {
3254-
if param.index == 0 {
3255-
return prev_ty.into();
3256-
}
3257-
}
3258-
ty::GenericParamDefKind::Lifetime
3259-
| ty::GenericParamDefKind::Const { .. } => {}
3260-
}
3261-
self.var_for_def(span, param)
3262-
});
3263-
// This will hold the resolved type of the associated type, if the
3264-
// current expression implements the trait that associated type is
3265-
// in. For example, this would be what `Iterator::Item` is here.
3266-
let ty_var = self.infcx.next_ty_var(origin);
3267-
// This corresponds to `<ExprTy as Iterator>::Item = _`.
3268-
let trait_ref = ty::Binder::dummy(ty::PredicateKind::Clause(
3269-
ty::Clause::Projection(ty::ProjectionPredicate {
3270-
projection_ty: ty::AliasTy { substs, def_id: proj.def_id },
3271-
term: ty_var.into(),
3272-
}),
3273-
));
3274-
// Add `<ExprTy as Iterator>::Item = _` obligation.
3275-
ocx.register_obligation(Obligation::misc(
3276-
self.tcx,
3277-
span,
3278-
expr.hir_id,
3279-
param_env,
3280-
trait_ref,
3281-
));
3282-
if ocx.select_where_possible().is_empty() {
3283-
// `ty_var` now holds the type that `Item` is for `ExprTy`.
3284-
let ty_var = self.resolve_vars_if_possible(ty_var);
3285-
assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
3286-
} else {
3287-
// `<ExprTy as Iterator>` didn't select, so likely we've
3288-
// reached the end of the iterator chain, like the originating
3289-
// `Vec<_>`.
3290-
// Keep the space consistent for later zipping.
3291-
assocs_in_this_method.push(None);
3292-
}
3293-
}
3257+
let assocs_in_this_method =
3258+
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
32943259
assocs.push(assocs_in_this_method);
3260+
32953261
prev_ty = self.resolve_vars_if_possible(
32963262
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()),
32973263
);
@@ -3300,17 +3266,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33003266
&& let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path
33013267
&& let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id)
33023268
&& let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id)
3303-
&& let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id)
3304-
&& let Some(binding_expr) = local.init
3269+
&& let Some(parent) = self.tcx.hir().find(parent_hir_id)
33053270
{
3306-
// We've reached the root of the method call chain and it is a
3307-
// binding. Get the binding creation and try to continue the chain.
3308-
expr = binding_expr;
3271+
// We've reached the root of the method call chain...
3272+
if let hir::Node::Local(local) = parent
3273+
&& let Some(binding_expr) = local.init
3274+
{
3275+
// ...and it is a binding. Get the binding creation and continue the chain.
3276+
expr = binding_expr;
3277+
}
3278+
if let hir::Node::Param(param) = parent {
3279+
// ...and it is a an fn argument.
3280+
let prev_ty = self.resolve_vars_if_possible(
3281+
typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error()),
3282+
);
3283+
let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
3284+
if assocs_in_this_method.iter().any(|a| a.is_some()) {
3285+
assocs.push(assocs_in_this_method);
3286+
print_root_expr = false;
3287+
}
3288+
break;
3289+
}
33093290
}
33103291
}
33113292
// We want the type before deref coercions, otherwise we talk about `&[_]`
33123293
// instead of `Vec<_>`.
3313-
if let Some(ty) = typeck_results.expr_ty_opt(expr) {
3294+
if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr {
33143295
let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
33153296
// Point at the root expression
33163297
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
@@ -3324,7 +3305,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33243305
let Some(prev_assoc_in_method) = assocs.peek() else {
33253306
for entry in assocs_in_method {
33263307
let Some((span, (assoc, ty))) = entry else { continue; };
3327-
if type_diffs.iter().any(|diff| {
3308+
if primary_spans.is_empty() || type_diffs.iter().any(|diff| {
33283309
let Sorts(expected_found) = diff else { return false; };
33293310
self.can_eq(param_env, expected_found.found, ty).is_ok()
33303311
}) {
@@ -3380,27 +3361,77 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33803361
}
33813362
}
33823363
}
3383-
for span in call_spans {
3384-
if span_labels.iter().find(|(s, _)| *s == span).is_none() {
3385-
// Ensure we are showing the entire chain, even if the assoc types
3386-
// haven't changed.
3387-
span_labels.push((span, String::new()));
3388-
}
3389-
}
33903364
if !primary_spans.is_empty() {
33913365
let mut multi_span: MultiSpan = primary_spans.into();
33923366
for (span, label) in span_labels {
33933367
multi_span.push_span_label(span, label);
33943368
}
33953369
err.span_note(
33963370
multi_span,
3397-
format!(
3398-
"the method call chain might not have had the expected \
3399-
associated types",
3400-
),
3371+
format!("the method call chain might not have had the expected associated types"),
34013372
);
34023373
}
34033374
}
3375+
3376+
fn probe_assoc_types_at_expr(
3377+
&self,
3378+
type_diffs: &[TypeError<'tcx>],
3379+
span: Span,
3380+
prev_ty: Ty<'tcx>,
3381+
body_id: hir::HirId,
3382+
param_env: ty::ParamEnv<'tcx>,
3383+
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
3384+
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
3385+
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
3386+
for diff in type_diffs {
3387+
let Sorts(expected_found) = diff else { continue; };
3388+
let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; };
3389+
3390+
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
3391+
let trait_def_id = proj.trait_def_id(self.tcx);
3392+
// Make `Self` be equivalent to the type of the call chain
3393+
// expression we're looking at now, so that we can tell what
3394+
// for example `Iterator::Item` is at this point in the chain.
3395+
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
3396+
match param.kind {
3397+
ty::GenericParamDefKind::Type { .. } => {
3398+
if param.index == 0 {
3399+
return prev_ty.into();
3400+
}
3401+
}
3402+
ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {}
3403+
}
3404+
self.var_for_def(span, param)
3405+
});
3406+
// This will hold the resolved type of the associated type, if the
3407+
// current expression implements the trait that associated type is
3408+
// in. For example, this would be what `Iterator::Item` is here.
3409+
let ty_var = self.infcx.next_ty_var(origin);
3410+
// This corresponds to `<ExprTy as Iterator>::Item = _`.
3411+
let trait_ref = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
3412+
ty::ProjectionPredicate {
3413+
projection_ty: ty::AliasTy { substs, def_id: proj.def_id },
3414+
term: ty_var.into(),
3415+
},
3416+
)));
3417+
// Add `<ExprTy as Iterator>::Item = _` obligation.
3418+
ocx.register_obligation(Obligation::misc(
3419+
self.tcx, span, body_id, param_env, trait_ref,
3420+
));
3421+
if ocx.select_where_possible().is_empty() {
3422+
// `ty_var` now holds the type that `Item` is for `ExprTy`.
3423+
let ty_var = self.resolve_vars_if_possible(ty_var);
3424+
assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
3425+
} else {
3426+
// `<ExprTy as Iterator>` didn't select, so likely we've
3427+
// reached the end of the iterator chain, like the originating
3428+
// `Vec<_>`.
3429+
// Keep the space consistent for later zipping.
3430+
assocs_in_this_method.push(None);
3431+
}
3432+
}
3433+
assocs_in_this_method
3434+
}
34043435
}
34053436

34063437
/// Add a hint to add a missing borrow or remove an unnecessary one.

src/test/ui/issues/issue-31173.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ LL | .cloned()
66
|
77
= note: expected reference `&_`
88
found type `u8`
9+
note: the method call chain might not have had the expected associated types
10+
--> $DIR/issue-31173.rs:3:20
11+
|
12+
LL | pub fn get_tok(it: &mut IntoIter<u8>) {
13+
| ^^^^^^^^^^^^^^^^^ `Iterator::Item` is `u8` here
14+
...
15+
LL | .take_while(|&x| {
16+
| __________-
17+
LL | | found_e = true;
18+
LL | | false
19+
LL | | })
20+
| |__________- `Iterator::Item` remains `u8` here
921
note: required by a bound in `cloned`
1022
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
1123

src/test/ui/issues/issue-33941.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ LL | for _ in HashMap::new().iter().cloned() {}
66
|
77
= note: expected reference `&_`
88
found tuple `(&_, &_)`
9+
note: the method call chain might not have had the expected associated types
10+
--> $DIR/issue-33941.rs:6:29
11+
|
12+
LL | for _ in HashMap::new().iter().cloned() {}
13+
| -------------- ^^^^^^ `Iterator::Item` is `(&_, &_)` here
14+
| |
15+
| this expression has type `HashMap<_, _>`
916
note: required by a bound in `cloned`
1017
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
1118

0 commit comments

Comments
 (0)