Skip to content

Commit 7225264

Browse files
committed
Auto merge of #64498 - estebank:point-at-arg, r=Centril
When possible point at argument causing item obligation failure Fix #41781, fix #42855, fix #46658, fix #48099, fix #63143.
2 parents ea3ba36 + c34d9e6 commit 7225264

File tree

67 files changed

+499
-371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+499
-371
lines changed

src/librustc/traits/chalk_fulfill.rs

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
8181
.map(|obligation| FulfillmentError {
8282
obligation: obligation.goal.clone(),
8383
code: FulfillmentErrorCode::CodeAmbiguity,
84+
points_at_arg_span: false,
8485
})
8586
.collect();
8687
Err(errors)
@@ -129,6 +130,7 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
129130
code: FulfillmentErrorCode::CodeSelectionError(
130131
SelectionError::Unimplemented
131132
),
133+
points_at_arg_span: false,
132134
}),
133135
}
134136
} else {
@@ -142,6 +144,7 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
142144
code: FulfillmentErrorCode::CodeSelectionError(
143145
SelectionError::Unimplemented
144146
),
147+
points_at_arg_span: false,
145148
})
146149
}
147150
}

src/librustc/traits/error_reporting.rs

+51-23
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
119119

120120
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
121121
// `error` occurring implies that `cond` occurs.
122-
fn error_implies(&self,
123-
cond: &ty::Predicate<'tcx>,
124-
error: &ty::Predicate<'tcx>)
125-
-> bool
126-
{
122+
fn error_implies(
123+
&self,
124+
cond: &ty::Predicate<'tcx>,
125+
error: &ty::Predicate<'tcx>,
126+
) -> bool {
127127
if cond == error {
128128
return true
129129
}
@@ -155,13 +155,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
155155
false
156156
}
157157

158-
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>,
159-
body_id: Option<hir::BodyId>,
160-
fallback_has_occurred: bool) {
158+
fn report_fulfillment_error(
159+
&self,
160+
error: &FulfillmentError<'tcx>,
161+
body_id: Option<hir::BodyId>,
162+
fallback_has_occurred: bool,
163+
) {
161164
debug!("report_fulfillment_errors({:?})", error);
162165
match error.code {
163-
FulfillmentErrorCode::CodeSelectionError(ref e) => {
164-
self.report_selection_error(&error.obligation, e, fallback_has_occurred);
166+
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
167+
self.report_selection_error(
168+
&error.obligation,
169+
selection_error,
170+
fallback_has_occurred,
171+
error.points_at_arg_span,
172+
);
165173
}
166174
FulfillmentErrorCode::CodeProjectionError(ref e) => {
167175
self.report_projection_error(&error.obligation, e);
@@ -170,19 +178,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
170178
self.maybe_report_ambiguity(&error.obligation, body_id);
171179
}
172180
FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => {
173-
self.report_mismatched_types(&error.obligation.cause,
174-
expected_found.expected,
175-
expected_found.found,
176-
err.clone())
177-
.emit();
181+
self.report_mismatched_types(
182+
&error.obligation.cause,
183+
expected_found.expected,
184+
expected_found.found,
185+
err.clone(),
186+
).emit();
178187
}
179188
}
180189
}
181190

182-
fn report_projection_error(&self,
183-
obligation: &PredicateObligation<'tcx>,
184-
error: &MismatchedProjectionTypes<'tcx>)
185-
{
191+
fn report_projection_error(
192+
&self,
193+
obligation: &PredicateObligation<'tcx>,
194+
error: &MismatchedProjectionTypes<'tcx>,
195+
) {
186196
let predicate =
187197
self.resolve_vars_if_possible(&obligation.predicate);
188198

@@ -603,6 +613,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
603613
obligation: &PredicateObligation<'tcx>,
604614
error: &SelectionError<'tcx>,
605615
fallback_has_occurred: bool,
616+
points_at_arg: bool,
606617
) {
607618
let span = obligation.cause.span;
608619

@@ -690,7 +701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
690701
}
691702

692703
self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
693-
self.suggest_fn_call(&obligation, &mut err, &trait_ref);
704+
self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg);
694705
self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
695706
self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
696707

@@ -963,6 +974,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
963974
obligation: &PredicateObligation<'tcx>,
964975
err: &mut DiagnosticBuilder<'tcx>,
965976
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
977+
points_at_arg: bool,
966978
) {
967979
let self_ty = trait_ref.self_ty();
968980
match self_ty.sty {
@@ -991,15 +1003,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
9911003
..
9921004
})) = self.tcx.hir().get_if_local(def_id) {
9931005
let body = self.tcx.hir().body(*body_id);
994-
err.help(&format!(
995-
"use parentheses to call the function: `{}({})`",
1006+
let msg = "use parentheses to call the function";
1007+
let snippet = format!(
1008+
"{}({})",
9961009
ident,
9971010
body.params.iter()
9981011
.map(|arg| match &arg.pat.node {
9991012
hir::PatKind::Binding(_, _, ident, None)
10001013
if ident.name != kw::SelfLower => ident.to_string(),
10011014
_ => "_".to_string(),
1002-
}).collect::<Vec<_>>().join(", ")));
1015+
}).collect::<Vec<_>>().join(", "),
1016+
);
1017+
// When the obligation error has been ensured to have been caused by
1018+
// an argument, the `obligation.cause.span` points at the expression
1019+
// of the argument, so we can provide a suggestion. This is signaled
1020+
// by `points_at_arg`. Otherwise, we give a more general note.
1021+
if points_at_arg {
1022+
err.span_suggestion(
1023+
obligation.cause.span,
1024+
msg,
1025+
snippet,
1026+
Applicability::HasPlaceholders,
1027+
);
1028+
} else {
1029+
err.help(&format!("{}: `{}`", msg, snippet));
1030+
}
10031031
}
10041032
}
10051033
_ => {}

src/librustc/traits/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,11 @@ EnumTypeFoldableImpl! {
484484

485485
pub struct FulfillmentError<'tcx> {
486486
pub obligation: PredicateObligation<'tcx>,
487-
pub code: FulfillmentErrorCode<'tcx>
487+
pub code: FulfillmentErrorCode<'tcx>,
488+
/// Diagnostics only: we opportunistically change the `code.span` when we encounter an
489+
/// obligation error caused by a call argument. When this is the case, we also signal that in
490+
/// this field to ensure accuracy of suggestions.
491+
pub points_at_arg_span: bool,
488492
}
489493

490494
#[derive(Clone)]
@@ -1183,7 +1187,7 @@ impl<'tcx> FulfillmentError<'tcx> {
11831187
code: FulfillmentErrorCode<'tcx>)
11841188
-> FulfillmentError<'tcx>
11851189
{
1186-
FulfillmentError { obligation: obligation, code: code }
1190+
FulfillmentError { obligation: obligation, code: code, points_at_arg_span: false }
11871191
}
11881192
}
11891193

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
19991999
),
20002000
&traits::SelectionError::Unimplemented,
20012001
false,
2002+
false,
20022003
);
20032004
}
20042005
}

src/librustc_typeck/check/coercion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
617617

618618
// Object safety violations or miscellaneous.
619619
Err(err) => {
620-
self.report_selection_error(&obligation, &err, false);
620+
self.report_selection_error(&obligation, &err, false, false);
621621
// Treat this like an obligation and follow through
622622
// with the unsizing - the lack of a coercion should
623623
// be silent, as it causes a type mismatch later.

src/librustc_typeck/check/mod.rs

+67-10
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,12 @@ fn typeck_tables_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TypeckTables<'_> {
912912
};
913913

914914
// All type checking constraints were added, try to fallback unsolved variables.
915-
fcx.select_obligations_where_possible(false);
915+
fcx.select_obligations_where_possible(false, |_| {});
916916
let mut fallback_has_occurred = false;
917917
for ty in &fcx.unsolved_variables() {
918918
fallback_has_occurred |= fcx.fallback_if_possible(ty);
919919
}
920-
fcx.select_obligations_where_possible(fallback_has_occurred);
920+
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
921921

922922
// Even though coercion casts provide type hints, we check casts after fallback for
923923
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
@@ -2391,7 +2391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23912391
// possible. This can help substantially when there are
23922392
// indirect dependencies that don't seem worth tracking
23932393
// precisely.
2394-
self.select_obligations_where_possible(false);
2394+
self.select_obligations_where_possible(false, |_| {});
23952395
ty = self.resolve_vars_if_possible(&ty);
23962396

23972397
debug!("resolve_type_vars_with_obligations: ty={:?}", ty);
@@ -2842,7 +2842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28422842
fn resolve_generator_interiors(&self, def_id: DefId) {
28432843
let mut generators = self.deferred_generator_interiors.borrow_mut();
28442844
for (body_id, interior, kind) in generators.drain(..) {
2845-
self.select_obligations_where_possible(false);
2845+
self.select_obligations_where_possible(false, |_| {});
28462846
generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
28472847
}
28482848
}
@@ -2879,8 +2879,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28792879
}
28802880

28812881
/// Select as many obligations as we can at present.
2882-
fn select_obligations_where_possible(&self, fallback_has_occurred: bool) {
2883-
if let Err(errors) = self.fulfillment_cx.borrow_mut().select_where_possible(self) {
2882+
fn select_obligations_where_possible(
2883+
&self,
2884+
fallback_has_occurred: bool,
2885+
mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
2886+
) {
2887+
if let Err(mut errors) = self.fulfillment_cx.borrow_mut().select_where_possible(self) {
2888+
mutate_fullfillment_errors(&mut errors);
28842889
self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
28852890
}
28862891
}
@@ -3288,6 +3293,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32883293
formal_tys.clone()
32893294
};
32903295

3296+
let mut final_arg_types: Vec<(usize, Ty<'_>)> = vec![];
3297+
32913298
// Check the arguments.
32923299
// We do this in a pretty awful way: first we type-check any arguments
32933300
// that are not closures, then we type-check the closures. This is so
@@ -3300,7 +3307,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33003307
// an "opportunistic" vtable resolution of any trait bounds on
33013308
// the call. This helps coercions.
33023309
if check_closures {
3303-
self.select_obligations_where_possible(false);
3310+
self.select_obligations_where_possible(false, |errors| {
3311+
self.point_at_arg_instead_of_call_if_possible(
3312+
errors,
3313+
&final_arg_types[..],
3314+
sp,
3315+
&args,
3316+
);
3317+
})
33043318
}
33053319

33063320
// For C-variadic functions, we don't have a declared type for all of
@@ -3346,6 +3360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33463360
// We're processing function arguments so we definitely want to use
33473361
// two-phase borrows.
33483362
self.demand_coerce(&arg, checked_ty, coerce_ty, AllowTwoPhase::Yes);
3363+
final_arg_types.push((i, coerce_ty));
33493364

33503365
// 3. Relate the expected type and the formal one,
33513366
// if the expected type was used for the coercion.
@@ -3392,6 +3407,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33923407
vec![self.tcx.types.err; len]
33933408
}
33943409

3410+
/// Given a vec of evaluated `FullfillmentError`s and an `fn` call argument expressions, we
3411+
/// walk the resolved types for each argument to see if any of the `FullfillmentError`s
3412+
/// reference a type argument. If they do, and there's only *one* argument that does, we point
3413+
/// at the corresponding argument's expression span instead of the `fn` call path span.
3414+
fn point_at_arg_instead_of_call_if_possible(
3415+
&self,
3416+
errors: &mut Vec<traits::FulfillmentError<'_>>,
3417+
final_arg_types: &[(usize, Ty<'tcx>)],
3418+
call_sp: Span,
3419+
args: &'tcx [hir::Expr],
3420+
) {
3421+
if !call_sp.desugaring_kind().is_some() {
3422+
// We *do not* do this for desugared call spans to keep good diagnostics when involving
3423+
// the `?` operator.
3424+
for error in errors {
3425+
if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
3426+
// Collect the argument position for all arguments that could have caused this
3427+
// `FullfillmentError`.
3428+
let mut referenced_in = final_arg_types.iter()
3429+
.flat_map(|(i, ty)| {
3430+
let ty = self.resolve_vars_if_possible(ty);
3431+
// We walk the argument type because the argument's type could have
3432+
// been `Option<T>`, but the `FullfillmentError` references `T`.
3433+
ty.walk()
3434+
.filter(|&ty| ty == predicate.skip_binder().self_ty())
3435+
.map(move |_| *i)
3436+
});
3437+
if let (Some(ref_in), None) = (referenced_in.next(), referenced_in.next()) {
3438+
// We make sure that only *one* argument matches the obligation failure
3439+
// and thet the obligation's span to its expression's.
3440+
error.obligation.cause.span = args[ref_in].span;
3441+
error.points_at_arg_span = true;
3442+
}
3443+
}
3444+
}
3445+
}
3446+
}
3447+
33953448
// AST fragment checking
33963449
fn check_lit(&self,
33973450
lit: &hir::Lit,
@@ -3549,8 +3602,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
35493602

35503603
// Check bounds on type arguments used in the path.
35513604
let bounds = self.instantiate_bounds(path_span, did, substs);
3552-
let cause = traits::ObligationCause::new(path_span, self.body_id,
3553-
traits::ItemObligation(did));
3605+
let cause = traits::ObligationCause::new(
3606+
path_span,
3607+
self.body_id,
3608+
traits::ItemObligation(did),
3609+
);
35543610
self.add_obligations_for_parameters(cause, &bounds);
35553611

35563612
Some((variant, ty))
@@ -4674,7 +4730,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
46744730
let bounds = self.instantiate_bounds(span, def_id, &substs);
46754731
self.add_obligations_for_parameters(
46764732
traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
4677-
&bounds);
4733+
&bounds,
4734+
);
46784735

46794736
// Substitute the values for the type parameters into the type of
46804737
// the referenced item.

src/librustc_typeck/check/op.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
724724
match method {
725725
Some(ok) => {
726726
let method = self.register_infer_ok_obligations(ok);
727-
self.select_obligations_where_possible(false);
727+
self.select_obligations_where_possible(false, |_| {});
728728

729729
Ok(method)
730730
}

src/librustc_typeck/check/wfcheck.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ fn check_where_clauses<'tcx, 'fcx>(
506506
});
507507

508508
// Now we build the substituted predicates.
509-
let default_obligations = predicates.predicates.iter().flat_map(|&(pred, _)| {
509+
let default_obligations = predicates.predicates.iter().flat_map(|&(pred, sp)| {
510510
#[derive(Default)]
511511
struct CountParams { params: FxHashSet<u32> }
512512
impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams {
@@ -539,9 +539,9 @@ fn check_where_clauses<'tcx, 'fcx>(
539539
// Avoid duplication of predicates that contain no parameters, for example.
540540
None
541541
} else {
542-
Some(substituted_pred)
542+
Some((substituted_pred, sp))
543543
}
544-
}).map(|pred| {
544+
}).map(|(pred, sp)| {
545545
// Convert each of those into an obligation. So if you have
546546
// something like `struct Foo<T: Copy = String>`, we would
547547
// take that predicate `T: Copy`, substitute to `String: Copy`
@@ -551,8 +551,8 @@ fn check_where_clauses<'tcx, 'fcx>(
551551
// Note the subtle difference from how we handle `predicates`
552552
// below: there, we are not trying to prove those predicates
553553
// to be *true* but merely *well-formed*.
554-
let pred = fcx.normalize_associated_types_in(span, &pred);
555-
let cause = traits::ObligationCause::new(span, fcx.body_id, traits::ItemObligation(def_id));
554+
let pred = fcx.normalize_associated_types_in(sp, &pred);
555+
let cause = traits::ObligationCause::new(sp, fcx.body_id, traits::ItemObligation(def_id));
556556
traits::Obligation::new(cause, fcx.param_env, pred)
557557
});
558558

0 commit comments

Comments
 (0)