1
Fork 0

Rollup merge of #91873 - estebank:mention-impls-for-unsatisfied-trait, r=davidtwco

Mention implementers of unsatisfied trait

When encountering an unsatisfied trait bound, if there are no other
suggestions, mention all the types that *do* implement that trait:

```
error[E0277]: the trait bound `f32: Foo` is not satisfied
  --> $DIR/impl_wf.rs:22:6
   |
LL | impl Baz<f32> for f32 { }
   |      ^^^^^^^^ the trait `Foo` is not implemented for `f32`
   |
   = help: the trait `Foo` is implemented for `i32`
note: required by a bound in `Baz`
  --> $DIR/impl_wf.rs:18:31
   |
LL | trait Baz<U: ?Sized> where U: Foo { }
   |                               ^^^ required by this bound in `Baz`
```
```
error[E0277]: the trait bound `u32: Foo` is not satisfied
  --> $DIR/associated-types-path-2.rs:29:5
   |
LL |     f1(2u32, 4u32);
   |     ^^ the trait `Foo` is not implemented for `u32`
   |
   = help: the trait `Foo` is implemented for `i32`
note: required by a bound in `f1`
  --> $DIR/associated-types-path-2.rs:13:14
   |
LL | pub fn f1<T: Foo>(a: T, x: T::A) {}
   |              ^^^ required by this bound in `f1`
```

Suggest dereferencing in more cases.

Fix #87437, fix #90970.
This commit is contained in:
Dylan DPC 2022-04-05 01:53:31 +02:00 committed by GitHub
commit a5c81695a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
136 changed files with 1242 additions and 292 deletions

View file

@ -2,10 +2,11 @@ pub mod on_unimplemented;
pub mod suggestions;
use super::{
EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode,
MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow,
PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError,
FulfillmentErrorCode, ImplDerivedObligationCause, MismatchedProjectionTypes, Obligation,
ObligationCause, ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote,
OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError,
TraitNotObjectSafe,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
@ -14,6 +15,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
Style,
};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@ -28,7 +30,7 @@ use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
};
use rustc_span::symbol::{kw, sym};
use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP};
@ -354,7 +356,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let have_alt_message = message.is_some() || label.is_some();
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
let is_unsize =
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
let (message, note, append_const_msg) = if is_try_conversion {
(
Some(format!(
@ -363,7 +365,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
)),
Some(
"the question mark operation (`?`) implicitly performs a \
conversion on the error value using the `From` trait"
conversion on the error value using the `From` trait"
.to_owned(),
),
Some(None),
@ -525,10 +527,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
self.suggest_semicolon_removal(
let mut suggested =
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
suggested |=
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_semicolon_removal(
&obligation,
&mut err,
span,
@ -654,10 +658,80 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
trait_predicate,
obligation.cause.body_id,
);
} else if !have_alt_message {
} else if !suggested {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(impl_candidates, &mut err);
if !self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
&mut err,
) {
// This is *almost* equivalent to
// `obligation.cause.code().peel_derives()`, but it gives us the
// trait predicate for that corresponding root obligation. This
// lets us get a derived obligation from a type parameter, like
// when calling `string.strip_suffix(p)` where `p` is *not* an
// implementer of `Pattern<'_>`.
let mut code = obligation.cause.code();
let mut trait_pred = trait_predicate;
let mut peeled = false;
loop {
match &*code {
ObligationCauseCode::FunctionArgumentObligation {
parent_code,
..
} => {
code = &parent_code;
}
ObligationCauseCode::ImplDerivedObligation(
box ImplDerivedObligationCause {
derived:
DerivedObligationCause {
parent_code,
parent_trait_pred,
},
..
},
)
| ObligationCauseCode::BuiltinDerivedObligation(
DerivedObligationCause {
parent_code,
parent_trait_pred,
},
)
| ObligationCauseCode::DerivedObligation(
DerivedObligationCause {
parent_code,
parent_trait_pred,
},
) => {
peeled = true;
code = &parent_code;
trait_pred = *parent_trait_pred;
}
_ => break,
};
}
let def_id = trait_pred.def_id();
// Mention *all* the `impl`s for the *top most* obligation, the
// user might have meant to use one of them, if any found. We skip
// auto-traits or fundamental traits that might not be exactly what
// the user might expect to be presented with. Instead this is
// useful for less general traits.
if peeled
&& !self.tcx.trait_is_auto(def_id)
&& !self.tcx.lang_items().items().contains(&Some(def_id))
{
let trait_ref = trait_pred.to_poly_trait_ref();
let impl_candidates =
self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
&mut err,
);
}
}
}
// Changing mutability doesn't make a difference to whether we have
@ -682,7 +756,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
});
let unit_obligation = obligation.with(predicate.to_predicate(tcx));
if self.predicate_may_hold(&unit_obligation) {
err.note("this trait is implemented for `()`");
err.note(
"this error might have been caused by changes to \
Rust's type-inference algorithm (see issue #48950 \
@ -1307,8 +1380,9 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
fn report_similar_impl_candidates(
&self,
impl_candidates: Vec<ImplCandidate<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
);
) -> bool;
/// Gets the parent trait chain start
fn get_parent_trait_ref(
@ -1319,7 +1393,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
fn note_version_mismatch(
&self,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool;
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
/// `trait_ref`.
@ -1681,14 +1759,82 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
fn report_similar_impl_candidates(
&self,
impl_candidates: Vec<ImplCandidate<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
) {
if impl_candidates.is_empty() {
return;
}
) -> bool {
let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
candidates.sort();
candidates.dedup();
let len = candidates.len();
if candidates.len() == 0 {
return false;
}
if candidates.len() == 1 {
err.highlighted_help(vec![
(
format!("the trait `{}` ", candidates[0].print_only_trait_path()),
Style::NoStyle,
),
("is".to_string(), Style::Highlight),
(" implemented for `".to_string(), Style::NoStyle),
(candidates[0].self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
]);
return true;
}
let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id);
// Check if the trait is the same in all cases. If so, we'll only show the type.
let mut traits: Vec<_> =
candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
traits.sort();
traits.dedup();
let len = impl_candidates.len();
let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 };
let mut candidates: Vec<String> = candidates
.into_iter()
.map(|c| {
if traits.len() == 1 {
format!("\n {}", c.self_ty())
} else {
format!("\n {}", c)
}
})
.collect();
candidates.sort();
candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
err.help(&format!(
"the following other types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
));
true
};
let def_id = trait_ref.def_id();
if impl_candidates.is_empty() {
if self.tcx.trait_is_auto(def_id)
|| self.tcx.lang_items().items().contains(&Some(def_id))
|| self.tcx.get_diagnostic_name(def_id).is_some()
{
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
return false;
}
let normalized_impl_candidates: Vec<_> = self
.tcx
.all_impls(def_id)
// Ignore automatically derived impls and `!Trait` impls.
.filter(|&def_id| {
self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
|| self.tcx.is_builtin_derive(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
// Avoid mentioning type parameters.
.filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
.collect();
return report(normalized_impl_candidates, err);
}
let normalize = |candidate| {
self.tcx.infer_ctxt().enter(|ref infcx| {
@ -1697,8 +1843,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.normalize(candidate)
.ok();
match normalized {
Some(normalized) => format!("\n {}", normalized.value),
None => format!("\n {}", candidate),
Some(normalized) => normalized.value,
None => candidate,
}
})
};
@ -1717,17 +1863,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
})
.collect::<Vec<_>>();
normalized_impl_candidates_and_similarities.sort();
normalized_impl_candidates_and_similarities.dedup();
let normalized_impl_candidates = normalized_impl_candidates_and_similarities
.into_iter()
.map(|(_, normalized)| normalized)
.collect::<Vec<_>>();
err.help(&format!(
"the following implementations were found:{}{}",
normalized_impl_candidates[..end].join(""),
if len > 5 { format!("\nand {} others", len - 4) } else { String::new() }
));
report(normalized_impl_candidates, err)
}
/// Gets the parent trait chain start
@ -1758,7 +1901,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
fn note_version_mismatch(
&self,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool {
let get_trait_impl = |trait_def_id| {
self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
};
@ -1769,6 +1916,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
.filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
.collect();
let mut suggested = false;
for trait_with_same_path in traits_with_same_path {
if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
let impl_span = self.tcx.def_span(impl_def_id);
@ -1779,8 +1927,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
trait_crate
);
err.note(&crate_msg);
suggested = true;
}
}
suggested
}
fn mk_trait_obligation_with_new_self_ty(

View file

@ -1,6 +1,6 @@
use super::{
EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
SelectionContext,
DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
};
use crate::autoderef::Autoderef;
@ -58,7 +58,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
@ -67,7 +67,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn suggest_add_reference_to_arg(
&self,
@ -90,7 +90,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
@ -107,7 +107,7 @@ pub trait InferCtxtExt<'tcx> {
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
@ -502,54 +502,87 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
// It only make sense when suggesting dereferences for arguments
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
obligation.cause.code()
{
parent_code.clone()
} else {
return;
let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
return false;
};
let param_env = obligation.param_env;
let body_id = obligation.cause.body_id;
let span = obligation.cause.span;
let real_trait_pred = match &*code {
ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred,
ObligationCauseCode::DerivedObligation(cause)
| ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred,
_ => trait_pred,
};
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
return;
};
let mut real_trait_pred = trait_pred;
let mut code = obligation.cause.code();
loop {
match &code {
ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
code = &parent_code;
}
ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
derived: DerivedObligationCause { parent_code, parent_trait_pred },
..
})
| ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
parent_code,
parent_trait_pred,
})
| ObligationCauseCode::DerivedObligation(DerivedObligationCause {
parent_code,
parent_trait_pred,
}) => {
code = &parent_code;
real_trait_pred = *parent_trait_pred;
}
_ => break,
};
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
continue;
};
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
// Re-add the `&`
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
}) {
if steps > 0 {
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
// Don't care about `&mut` because `DerefMut` is used less
// often and user will not expect autoderef happens.
if src.starts_with('&') && !src.starts_with("&mut ") {
let derefs = "*".repeat(steps);
err.span_suggestion(
span,
"consider adding dereference here",
format!("&{}{}", derefs, &src[1..]),
Applicability::MachineApplicable,
);
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
// Re-add the `&`
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
}) {
if steps > 0 {
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
// Don't care about `&mut` because `DerefMut` is used less
// often and user will not expect autoderef happens.
if src.starts_with('&') && !src.starts_with("&mut ") {
let derefs = "*".repeat(steps);
err.span_suggestion(
span,
"consider dereferencing here",
format!("&{}{}", derefs, &src[1..]),
Applicability::MachineApplicable,
);
return true;
}
}
}
} else if real_trait_pred != trait_pred {
// This branch addresses #87437.
let obligation = self.mk_trait_obligation_with_new_self_ty(
param_env,
real_trait_pred,
base_ty,
);
if self.predicate_may_hold(&obligation) {
err.span_suggestion_verbose(
span.shrink_to_lo(),
"consider dereferencing here",
"*".to_string(),
Applicability::MachineApplicable,
);
return true;
}
}
}
}
false
}
/// Given a closure's `DefId`, return the given name of the closure.
@ -592,22 +625,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
return;
return false;
};
let (def_id, output_ty, callable) = match *self_ty.kind() {
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
_ => return,
_ => return false,
};
let msg = format!("use parentheses to call the {}", callable);
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
// variables, so bail out if we have any.
let Some(output_ty) = output_ty.no_bound_vars() else {
return;
return false;
};
let new_obligation =
@ -619,7 +652,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| EvaluationResult::EvaluatedToOkModuloRegions
| EvaluationResult::EvaluatedToAmbig,
) => {}
_ => return,
_ => return false,
}
let hir = self.tcx.hir();
// Get the name of the callable and the arguments to be used in the suggestion.
@ -630,7 +663,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
})) => {
err.span_label(*span, "consider calling this closure");
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
return;
return false;
};
let args = decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
let sugg = format!("({})", args);
@ -658,7 +691,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let sugg = format!("({})", args);
(format!("{}{}", ident, sugg), sugg)
}
_ => return,
_ => return false,
};
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
{
@ -675,6 +708,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
} else {
err.help(&format!("{}: `{}`", msg, snippet));
}
true
}
fn suggest_add_reference_to_arg(
@ -845,19 +879,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let span = obligation.cause.span;
let mut suggested = false;
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let refs_number =
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
// Do not suggest removal of borrow from type arguments.
return;
return false;
}
let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
return;
return false;
};
for refs_remaining in 0..refs_number {
@ -893,10 +928,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
String::new(),
Applicability::MachineApplicable,
);
suggested = true;
break;
}
}
}
suggested
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
@ -1033,7 +1070,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let hir = self.tcx.hir();
let parent_node = hir.get_parent_node(obligation.cause.body_id);
let node = hir.find(parent_node);
@ -1052,7 +1089,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
{
let sp = self.tcx.sess.source_map().end_point(stmt.span);
err.span_label(sp, "consider removing this semicolon");
return true;
}
false
}
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {