Skip to content

Commit e366210

Browse files
committed
Auto merge of rust-lang#88719 - estebank:point-at-arg-for-obligation, r=nagisa
Point at argument instead of call for their obligations When an obligation is introduced by a specific `fn` argument, point at the argument instead of the `fn` call if the obligation fails to be fulfilled. Move the information about pointing at the call argument expression in an unmet obligation span from the `FulfillmentError` to a new `ObligationCauseCode`. When giving an error about an obligation introduced by a function call that an argument doesn't fulfill, and that argument is a block, add a span_label pointing at the innermost tail expression. Current output: ``` error[E0425]: cannot find value `x` in this scope --> f10.rs:4:14 | 4 | Some(x * 2) | ^ not found in this scope error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<_>` --> f10.rs:2:31 | 2 | let p = Some(45).and_then({ | ______________________--------_^ | | | | | required by a bound introduced by this call 3 | | |x| println!("doubling {}", x); 4 | | Some(x * 2) | | ----------- 5 | | }); | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option<_>` | = help: the trait `FnOnce<({integer},)>` is not implemented for `Option<_>` ``` Previous output: ``` error[E0425]: cannot find value `x` in this scope --> f10.rs:4:14 | 4 | Some(x * 2) | ^ not found in this scope error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<_>` --> f10.rs:2:22 | 2 | let p = Some(45).and_then({ | ^^^^^^^^ expected an `FnOnce<({integer},)>` closure, found `Option<_>` | = help: the trait `FnOnce<({integer},)>` is not implemented for `Option<_>` ``` Partially address rust-lang#27300. Will require rebasing on top of rust-lang#88546.
2 parents e4828d5 + 0a4540b commit e366210

File tree

108 files changed

+884
-400
lines changed

Some content is hidden

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

108 files changed

+884
-400
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1998,7 +1998,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
19981998
&obligation,
19991999
&traits::SelectionError::Unimplemented,
20002000
false,
2001-
false,
20022001
);
20032002
}
20042003
}

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
2929
SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause,
3030
_ => return None,
3131
};
32-
let (parent, impl_def_id) = match &cause.code {
32+
// If we added a "points at argument expression" obligation, we remove it here, we care
33+
// about the original obligation only.
34+
let code = match &cause.code {
35+
ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => &*parent_code,
36+
_ => &cause.code,
37+
};
38+
let (parent, impl_def_id) = match code {
3339
ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id),
3440
_ => return None,
3541
};

compiler/rustc_infer/src/traits/mod.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
6666
pub struct FulfillmentError<'tcx> {
6767
pub obligation: PredicateObligation<'tcx>,
6868
pub code: FulfillmentErrorCode<'tcx>,
69-
/// Diagnostics only: we opportunistically change the `code.span` when we encounter an
70-
/// obligation error caused by a call argument. When this is the case, we also signal that in
71-
/// this field to ensure accuracy of suggestions.
72-
pub points_at_arg_span: bool,
7369
/// Diagnostics only: the 'root' obligation which resulted in
7470
/// the failure to process `obligation`. This is the obligation
7571
/// that was initially passed to `register_predicate_obligation`
@@ -128,7 +124,7 @@ impl<'tcx> FulfillmentError<'tcx> {
128124
code: FulfillmentErrorCode<'tcx>,
129125
root_obligation: PredicateObligation<'tcx>,
130126
) -> FulfillmentError<'tcx> {
131-
FulfillmentError { obligation, code, points_at_arg_span: false, root_obligation }
127+
FulfillmentError { obligation, code, root_obligation }
132128
}
133129
}
134130

compiler/rustc_middle/src/traits/mod.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ pub enum ObligationCauseCode<'tcx> {
253253

254254
DerivedObligation(DerivedObligationCause<'tcx>),
255255

256+
FunctionArgumentObligation {
257+
/// The node of the relevant argument in the function call.
258+
arg_hir_id: hir::HirId,
259+
/// The node of the function call.
260+
call_hir_id: hir::HirId,
261+
/// The obligation introduced by this argument.
262+
parent_code: Lrc<ObligationCauseCode<'tcx>>,
263+
},
264+
256265
/// Error derived when matching traits/impls; see ObligationCause for more details
257266
CompareImplConstObligation,
258267

@@ -368,11 +377,12 @@ impl ObligationCauseCode<'_> {
368377
// Return the base obligation, ignoring derived obligations.
369378
pub fn peel_derives(&self) -> &Self {
370379
let mut base_cause = self;
371-
while let BuiltinDerivedObligation(cause)
372-
| ImplDerivedObligation(cause)
373-
| DerivedObligation(cause) = base_cause
380+
while let BuiltinDerivedObligation(DerivedObligationCause { parent_code, .. })
381+
| ImplDerivedObligation(DerivedObligationCause { parent_code, .. })
382+
| DerivedObligation(DerivedObligationCause { parent_code, .. })
383+
| FunctionArgumentObligation { parent_code, .. } = base_cause
374384
{
375-
base_cause = &cause.parent_code;
385+
base_cause = &parent_code;
376386
}
377387
base_cause
378388
}

compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs

-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
5757
.map(|obligation| FulfillmentError {
5858
obligation: obligation.clone(),
5959
code: FulfillmentErrorCode::CodeAmbiguity,
60-
points_at_arg_span: false,
6160
// FIXME - does Chalk have a notation of 'root obligation'?
6261
// This is just for diagnostics, so it's okay if this is wrong
6362
root_obligation: obligation.clone(),
@@ -112,7 +111,6 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
112111
code: FulfillmentErrorCode::CodeSelectionError(
113112
SelectionError::Unimplemented,
114113
),
115-
points_at_arg_span: false,
116114
// FIXME - does Chalk have a notation of 'root obligation'?
117115
// This is just for diagnostics, so it's okay if this is wrong
118116
root_obligation: obligation,
@@ -129,7 +127,6 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
129127
code: FulfillmentErrorCode::CodeSelectionError(
130128
SelectionError::Unimplemented,
131129
),
132-
points_at_arg_span: false,
133130
// FIXME - does Chalk have a notation of 'root obligation'?
134131
// This is just for diagnostics, so it's okay if this is wrong
135132
root_obligation: obligation,

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

+3-12
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ pub trait InferCtxtExt<'tcx> {
6666
root_obligation: &PredicateObligation<'tcx>,
6767
error: &SelectionError<'tcx>,
6868
fallback_has_occurred: bool,
69-
points_at_arg: bool,
7069
);
7170

7271
/// Given some node representing a fn-like thing in the HIR map,
@@ -237,7 +236,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
237236
root_obligation: &PredicateObligation<'tcx>,
238237
error: &SelectionError<'tcx>,
239238
fallback_has_occurred: bool,
240-
points_at_arg: bool,
241239
) {
242240
let tcx = self.tcx;
243241
let mut span = obligation.cause.span;
@@ -387,7 +385,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
387385
&obligation,
388386
&mut err,
389387
&trait_ref,
390-
points_at_arg,
391388
have_alt_message,
392389
) {
393390
self.note_obligation_cause(&mut err, &obligation);
@@ -430,8 +427,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
430427
err.span_label(enclosing_scope_span, s.as_str());
431428
}
432429

433-
self.suggest_dereferences(&obligation, &mut err, trait_ref, points_at_arg);
434-
self.suggest_fn_call(&obligation, &mut err, trait_ref, points_at_arg);
430+
self.suggest_dereferences(&obligation, &mut err, trait_ref);
431+
self.suggest_fn_call(&obligation, &mut err, trait_ref);
435432
self.suggest_remove_reference(&obligation, &mut err, trait_ref);
436433
self.suggest_semicolon_removal(&obligation, &mut err, span, trait_ref);
437434
self.note_version_mismatch(&mut err, &trait_ref);
@@ -500,12 +497,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
500497
// Changing mutability doesn't make a difference to whether we have
501498
// an `Unsize` impl (Fixes ICE in #71036)
502499
if !is_unsize {
503-
self.suggest_change_mut(
504-
&obligation,
505-
&mut err,
506-
trait_ref,
507-
points_at_arg,
508-
);
500+
self.suggest_change_mut(&obligation, &mut err, trait_ref);
509501
}
510502

511503
// If this error is due to `!: Trait` not implemented but `(): Trait` is
@@ -1214,7 +1206,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
12141206
&error.root_obligation,
12151207
selection_error,
12161208
fallback_has_occurred,
1217-
error.points_at_arg_span,
12181209
);
12191210
}
12201211
FulfillmentErrorCode::CodeProjectionError(ref e) => {

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

+78-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::traits::normalize_projection_type;
99

1010
use rustc_data_structures::fx::FxHashSet;
1111
use rustc_data_structures::stack::ensure_sufficient_stack;
12+
use rustc_data_structures::sync::Lrc;
1213
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
1314
use rustc_hir as hir;
1415
use rustc_hir::def::DefKind;
@@ -54,7 +55,6 @@ pub trait InferCtxtExt<'tcx> {
5455
obligation: &PredicateObligation<'tcx>,
5556
err: &mut DiagnosticBuilder<'tcx>,
5657
trait_ref: ty::PolyTraitRef<'tcx>,
57-
points_at_arg: bool,
5858
);
5959

6060
fn get_closure_name(
@@ -69,15 +69,13 @@ pub trait InferCtxtExt<'tcx> {
6969
obligation: &PredicateObligation<'tcx>,
7070
err: &mut DiagnosticBuilder<'_>,
7171
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
72-
points_at_arg: bool,
7372
);
7473

7574
fn suggest_add_reference_to_arg(
7675
&self,
7776
obligation: &PredicateObligation<'tcx>,
7877
err: &mut DiagnosticBuilder<'_>,
7978
trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>,
80-
points_at_arg: bool,
8179
has_custom_message: bool,
8280
) -> bool;
8381

@@ -93,7 +91,6 @@ pub trait InferCtxtExt<'tcx> {
9391
obligation: &PredicateObligation<'tcx>,
9492
err: &mut DiagnosticBuilder<'_>,
9593
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
96-
points_at_arg: bool,
9794
);
9895

9996
fn suggest_semicolon_removal(
@@ -490,16 +487,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
490487
obligation: &PredicateObligation<'tcx>,
491488
err: &mut DiagnosticBuilder<'tcx>,
492489
trait_ref: ty::PolyTraitRef<'tcx>,
493-
points_at_arg: bool,
494490
) {
495491
// It only make sense when suggesting dereferences for arguments
496-
if !points_at_arg {
492+
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
493+
&obligation.cause.code
494+
{
495+
parent_code.clone()
496+
} else {
497497
return;
498-
}
498+
};
499499
let param_env = obligation.param_env;
500500
let body_id = obligation.cause.body_id;
501501
let span = obligation.cause.span;
502-
let real_trait_ref = match &obligation.cause.code {
502+
let real_trait_ref = match &*code {
503503
ObligationCauseCode::ImplDerivedObligation(cause)
504504
| ObligationCauseCode::DerivedObligation(cause)
505505
| ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_ref,
@@ -584,7 +584,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
584584
obligation: &PredicateObligation<'tcx>,
585585
err: &mut DiagnosticBuilder<'_>,
586586
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
587-
points_at_arg: bool,
588587
) {
589588
let self_ty = match trait_ref.self_ty().no_bound_vars() {
590589
None => return,
@@ -656,11 +655,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
656655
}
657656
_ => return,
658657
};
659-
if points_at_arg {
658+
if matches!(obligation.cause.code, ObligationCauseCode::FunctionArgumentObligation { .. }) {
660659
// When the obligation error has been ensured to have been caused by
661660
// an argument, the `obligation.cause.span` points at the expression
662-
// of the argument, so we can provide a suggestion. This is signaled
663-
// by `points_at_arg`. Otherwise, we give a more general note.
661+
// of the argument, so we can provide a suggestion. Otherwise, we give
662+
// a more general note.
664663
err.span_suggestion_verbose(
665664
obligation.cause.span.shrink_to_hi(),
666665
&msg,
@@ -677,18 +676,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
677676
obligation: &PredicateObligation<'tcx>,
678677
err: &mut DiagnosticBuilder<'_>,
679678
trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>,
680-
points_at_arg: bool,
681679
has_custom_message: bool,
682680
) -> bool {
683681
let span = obligation.cause.span;
684-
let points_at_for_iter = matches!(
685-
span.ctxt().outer_expn_data().kind,
686-
ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
687-
);
688682

689-
if !points_at_arg && !points_at_for_iter {
683+
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
684+
&obligation.cause.code
685+
{
686+
parent_code.clone()
687+
} else if let ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) =
688+
span.ctxt().outer_expn_data().kind
689+
{
690+
Lrc::new(obligation.cause.code.clone())
691+
} else {
690692
return false;
691-
}
693+
};
692694

693695
// List of traits for which it would be nonsensical to suggest borrowing.
694696
// For instance, immutable references are always Copy, so suggesting to
@@ -787,7 +789,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
787789
return false;
788790
};
789791

790-
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
792+
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &*code {
791793
let expected_trait_ref = obligation.parent_trait_ref.skip_binder();
792794
let new_imm_trait_ref =
793795
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs);
@@ -799,7 +801,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
799801
return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]);
800802
}
801803
} else if let ObligationCauseCode::BindingObligation(_, _)
802-
| ObligationCauseCode::ItemObligation(_) = &obligation.cause.code
804+
| ObligationCauseCode::ItemObligation(_) = &*code
803805
{
804806
if try_borrowing(
805807
ty::TraitRef::new(trait_ref.def_id, imm_substs),
@@ -891,8 +893,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
891893
obligation: &PredicateObligation<'tcx>,
892894
err: &mut DiagnosticBuilder<'_>,
893895
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
894-
points_at_arg: bool,
895896
) {
897+
let points_at_arg = matches!(
898+
obligation.cause.code,
899+
ObligationCauseCode::FunctionArgumentObligation { .. },
900+
);
901+
896902
let span = obligation.cause.span;
897903
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
898904
let refs_number =
@@ -2289,6 +2295,56 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
22892295
)
22902296
});
22912297
}
2298+
ObligationCauseCode::FunctionArgumentObligation {
2299+
arg_hir_id,
2300+
call_hir_id,
2301+
ref parent_code,
2302+
} => {
2303+
let hir = self.tcx.hir();
2304+
if let Some(Node::Expr(expr @ hir::Expr { kind: hir::ExprKind::Block(..), .. })) =
2305+
hir.find(arg_hir_id)
2306+
{
2307+
let in_progress_typeck_results =
2308+
self.in_progress_typeck_results.map(|t| t.borrow());
2309+
let parent_id = hir.local_def_id(hir.get_parent_item(arg_hir_id));
2310+
let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results {
2311+
Some(t) if t.hir_owner == parent_id => t,
2312+
_ => self.tcx.typeck(parent_id),
2313+
};
2314+
let ty = typeck_results.expr_ty_adjusted(expr);
2315+
let span = expr.peel_blocks().span;
2316+
if Some(span) != err.span.primary_span() {
2317+
err.span_label(
2318+
span,
2319+
&if ty.references_error() {
2320+
String::new()
2321+
} else {
2322+
format!("this tail expression is of type `{:?}`", ty)
2323+
},
2324+
);
2325+
}
2326+
}
2327+
if let Some(Node::Expr(hir::Expr {
2328+
kind:
2329+
hir::ExprKind::Call(hir::Expr { span, .. }, _)
2330+
| hir::ExprKind::MethodCall(_, span, ..),
2331+
..
2332+
})) = hir.find(call_hir_id)
2333+
{
2334+
if Some(*span) != err.span.primary_span() {
2335+
err.span_label(*span, "required by a bound introduced by this call");
2336+
}
2337+
}
2338+
ensure_sufficient_stack(|| {
2339+
self.note_obligation_cause_code(
2340+
err,
2341+
predicate,
2342+
&parent_code,
2343+
obligated_types,
2344+
seen_requirements,
2345+
)
2346+
});
2347+
}
22922348
ObligationCauseCode::CompareImplMethodObligation {
22932349
item_name,
22942350
trait_item_def_id,

compiler/rustc_typeck/src/check/callee.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7272
arg_exprs: &'tcx [hir::Expr<'tcx>],
7373
expected: Expectation<'tcx>,
7474
) -> Ty<'tcx> {
75-
let original_callee_ty = self.check_expr(callee_expr);
75+
let original_callee_ty = match &callee_expr.kind {
76+
hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
77+
.check_expr_with_expectation_and_args(
78+
callee_expr,
79+
Expectation::NoExpectation,
80+
arg_exprs,
81+
),
82+
_ => self.check_expr(callee_expr),
83+
};
84+
7685
let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
7786

7887
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);

compiler/rustc_typeck/src/check/coercion.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -707,13 +707,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
707707

708708
// Object safety violations or miscellaneous.
709709
Err(err) => {
710-
self.report_selection_error(
711-
obligation.clone(),
712-
&obligation,
713-
&err,
714-
false,
715-
false,
716-
);
710+
self.report_selection_error(obligation.clone(), &obligation, &err, false);
717711
// Treat this like an obligation and follow through
718712
// with the unsizing - the lack of a coercion should
719713
// be silent, as it causes a type mismatch later.

0 commit comments

Comments
 (0)