Skip to content

Commit 294944d

Browse files
committed
Point at method chains on E0271 errors
1 parent 984eab5 commit 294944d

File tree

3 files changed

+138
-89
lines changed

3 files changed

+138
-89
lines changed

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

Lines changed: 119 additions & 89 deletions
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,63 +3254,8 @@ 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 projection = ty::Binder::dummy(ty::PredicateKind::Clause(
3269-
ty::Clause::Projection(ty::ProjectionPredicate {
3270-
projection_ty: tcx.mk_alias_ty(proj.def_id, substs),
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-
projection,
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);
32953260
prev_ty = self.resolve_vars_if_possible(
32963261
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()),
@@ -3300,17 +3265,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33003265
&& let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path
33013266
&& let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id)
33023267
&& 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
3268+
&& let Some(parent) = self.tcx.hir().find(parent_hir_id)
33053269
{
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;
3270+
// We've reached the root of the method call chain...
3271+
if let hir::Node::Local(local) = parent
3272+
&& let Some(binding_expr) = local.init
3273+
{
3274+
// ...and it is a binding. Get the binding creation and continue the chain.
3275+
expr = binding_expr;
3276+
}
3277+
if let hir::Node::Param(param) = parent {
3278+
// ...and it is a an fn argument.
3279+
let prev_ty = self.resolve_vars_if_possible(
3280+
typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error()),
3281+
);
3282+
let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
3283+
if assocs_in_this_method.iter().any(|a| a.is_some()) {
3284+
assocs.push(assocs_in_this_method);
3285+
print_root_expr = false;
3286+
}
3287+
break;
3288+
}
33093289
}
33103290
}
33113291
// We want the type before deref coercions, otherwise we talk about `&[_]`
33123292
// instead of `Vec<_>`.
3313-
if let Some(ty) = typeck_results.expr_ty_opt(expr) {
3293+
if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr {
33143294
let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
33153295
// Point at the root expression
33163296
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
@@ -3324,7 +3304,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33243304
let Some(prev_assoc_in_method) = assocs.peek() else {
33253305
for entry in assocs_in_method {
33263306
let Some((span, (assoc, ty))) = entry else { continue; };
3327-
if type_diffs.iter().any(|diff| {
3307+
if primary_spans.is_empty() || type_diffs.iter().any(|diff| {
33283308
let Sorts(expected_found) = diff else { return false; };
33293309
self.can_eq(param_env, expected_found.found, ty).is_ok()
33303310
}) {
@@ -3380,27 +3360,77 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33803360
}
33813361
}
33823362
}
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-
}
33903363
if !primary_spans.is_empty() {
33913364
let mut multi_span: MultiSpan = primary_spans.into();
33923365
for (span, label) in span_labels {
33933366
multi_span.push_span_label(span, label);
33943367
}
33953368
err.span_note(
33963369
multi_span,
3397-
format!(
3398-
"the method call chain might not have had the expected \
3399-
associated types",
3400-
),
3370+
format!("the method call chain might not have had the expected associated types"),
34013371
);
34023372
}
34033373
}
3374+
3375+
fn probe_assoc_types_at_expr(
3376+
&self,
3377+
type_diffs: &[TypeError<'tcx>],
3378+
span: Span,
3379+
prev_ty: Ty<'tcx>,
3380+
body_id: hir::HirId,
3381+
param_env: ty::ParamEnv<'tcx>,
3382+
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
3383+
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
3384+
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
3385+
for diff in type_diffs {
3386+
let Sorts(expected_found) = diff else { continue; };
3387+
let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; };
3388+
3389+
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
3390+
let trait_def_id = proj.trait_def_id(self.tcx);
3391+
// Make `Self` be equivalent to the type of the call chain
3392+
// expression we're looking at now, so that we can tell what
3393+
// for example `Iterator::Item` is at this point in the chain.
3394+
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
3395+
match param.kind {
3396+
ty::GenericParamDefKind::Type { .. } => {
3397+
if param.index == 0 {
3398+
return prev_ty.into();
3399+
}
3400+
}
3401+
ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {}
3402+
}
3403+
self.var_for_def(span, param)
3404+
});
3405+
// This will hold the resolved type of the associated type, if the
3406+
// current expression implements the trait that associated type is
3407+
// in. For example, this would be what `Iterator::Item` is here.
3408+
let ty_var = self.infcx.next_ty_var(origin);
3409+
// This corresponds to `<ExprTy as Iterator>::Item = _`.
3410+
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
3411+
ty::ProjectionPredicate {
3412+
projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs),
3413+
term: ty_var.into(),
3414+
},
3415+
)));
3416+
// Add `<ExprTy as Iterator>::Item = _` obligation.
3417+
ocx.register_obligation(Obligation::misc(
3418+
self.tcx, span, body_id, param_env, projection,
3419+
));
3420+
if ocx.select_where_possible().is_empty() {
3421+
// `ty_var` now holds the type that `Item` is for `ExprTy`.
3422+
let ty_var = self.resolve_vars_if_possible(ty_var);
3423+
assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
3424+
} else {
3425+
// `<ExprTy as Iterator>` didn't select, so likely we've
3426+
// reached the end of the iterator chain, like the originating
3427+
// `Vec<_>`.
3428+
// Keep the space consistent for later zipping.
3429+
assocs_in_this_method.push(None);
3430+
}
3431+
}
3432+
assocs_in_this_method
3433+
}
34043434
}
34053435

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

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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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)