diff options
| author | Esteban Kuber <esteban@kuber.com.ar> | 2021-12-13 20:56:40 +0000 |
|---|---|---|
| committer | Esteban Kuber <esteban@kuber.com.ar> | 2022-04-04 21:01:42 +0000 |
| commit | 3aac307ca62f6d6c5b3612d3dfe80b6eeae8ba15 (patch) | |
| tree | 2c9610a6faa515517410ea51d6d48e52558b51da /compiler | |
| parent | 6a9080b25e73d26aae94c3f6a13b31de58e66b5a (diff) | |
| download | rust-3aac307ca62f6d6c5b3612d3dfe80b6eeae8ba15.tar.gz rust-3aac307ca62f6d6c5b3612d3dfe80b6eeae8ba15.zip | |
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 following other types implement trait `Foo`:
Option<T>
i32
str
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`
```
Mention implementers of traits in `ImplObligation`s.
Do not mention other `impl`s for closures, ranges and `?`.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/mod.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/check_packed_ref.rs | 25 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs | 126 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 44 |
6 files changed, 156 insertions, 58 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 00ecbbbb93b..32c52a6a8a6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -378,6 +378,12 @@ impl Diagnostic { self } + /// Add a help message attached to this diagnostic with a customizable highlighted message. + pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self { + self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None); + self + } + /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 37425c91157..45a215354d0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -41,7 +41,7 @@ use rustc_hir::Node; use rustc_macros::HashStable; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::CrateStoreDyn; -use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::Align; @@ -2206,7 +2206,7 @@ impl<'tcx> TyCtxt<'tcx> { self.impl_trait_ref(def_id).map(|tr| tr.def_id) } - /// If the given defid describes a method belonging to an impl, returns the + /// If the given `DefId` describes a method belonging to an impl, returns the /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> { self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container { @@ -2215,6 +2215,11 @@ impl<'tcx> TyCtxt<'tcx> { }) } + /// If the given `DefId` belongs to a trait that was automatically derived, returns `true`. + pub fn is_builtin_derive(self, def_id: DefId) -> bool { + self.has_attr(def_id, sym::automatically_derived) + } + /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` /// with the name of the crate containing the impl. pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> { diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index f0367958ef8..4bf66cd4c9f 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -1,10 +1,9 @@ -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::UNALIGNED_REFERENCES; -use rustc_span::symbol::sym; use crate::util; use crate::MirLint; @@ -50,22 +49,6 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { }); } -fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { - debug!("builtin_derive_def_id({:?})", def_id); - if let Some(impl_def_id) = tcx.impl_of_method(def_id) { - if tcx.has_attr(impl_def_id, sym::automatically_derived) { - debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id); - Some(impl_def_id) - } else { - debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id); - None - } - } else { - debug!("builtin_derive_def_id({:?}) - not a method", def_id); - None - } -} - impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { // Make sure we know where in the MIR we are. @@ -83,7 +66,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { if context.is_borrow() { if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) { + if let Some(impl_def_id) = self + .tcx + .impl_of_method(def_id) + .filter(|&def_id| self.tcx.is_builtin_derive(def_id)) + { // If a method is defined in the local crate, // the impl containing that method should also be. self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2737c26708b..f5803aaa078 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -180,6 +180,9 @@ symbols! { Error, File, FileType, + Fn, + FnMut, + FnOnce, FormatSpec, Formatter, From, @@ -248,6 +251,7 @@ symbols! { RustcEncodable, Send, SeqCst, + SliceIndex, Some, String, StructuralEq, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 216aa89dd1f..2da4c86a58e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -14,6 +14,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; @@ -354,7 +355,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 +364,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), @@ -519,10 +520,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, @@ -648,10 +651,14 @@ 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); + self.report_similar_impl_candidates( + impl_candidates, + trait_ref, + &mut err, + ); } // Changing mutability doesn't make a difference to whether we have @@ -676,7 +683,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 \ @@ -1301,8 +1307,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( @@ -1313,7 +1320,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`. @@ -1675,10 +1686,63 @@ 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, - ) { + ) -> bool { + let def_id = trait_ref.def_id(); if impl_candidates.is_empty() { - return; + 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 mut 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(_))) + .map(|trait_ref| format!("\n {}", trait_ref.self_ty())) + .collect(); + normalized_impl_candidates.sort(); + normalized_impl_candidates.dedup(); + let len = normalized_impl_candidates.len(); + if len == 0 { + return false; + } + if len == 1 { + err.highlighted_help(vec![ + ( + format!( + "the trait `{}` is implemented for `", + trait_ref.print_only_trait_path() + ), + Style::NoStyle, + ), + (normalized_impl_candidates[0].trim().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + ]); + return true; + } + let end = if normalized_impl_candidates.len() <= 9 { + normalized_impl_candidates.len() + } else { + 8 + }; + err.help(&format!( + "the following other types implement trait `{}`:{}{}", + trait_ref.print_only_trait_path(), + normalized_impl_candidates[..end].join(""), + if len > 9 { format!("\nand {} others", len - 8) } else { String::new() } + )); + return true; } let len = impl_candidates.len(); @@ -1703,6 +1767,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // // Prefer more similar candidates first, then sort lexicographically // by their normalized string representation. + let first_candidate = impl_candidates.get(0).map(|candidate| candidate.trait_ref); let mut normalized_impl_candidates_and_similarities = impl_candidates .into_iter() .map(|ImplCandidate { trait_ref, similarity }| { @@ -1711,17 +1776,33 @@ 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() } - )); + if normalized_impl_candidates.len() == 1 { + err.highlighted_help(vec![ + ( + format!( + "the trait `{}` is implemented for `", + first_candidate.unwrap().print_only_trait_path() + ), + Style::NoStyle, + ), + (first_candidate.unwrap().self_ty().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + ]); + } else { + err.help(&format!( + "the following implementations were found:{}{}", + normalized_impl_candidates[..end].join(""), + if len > 9 { format!("\nand {} others", len - 8) } else { String::new() } + )); + } + true } /// Gets the parent trait chain start @@ -1752,7 +1833,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) }; @@ -1763,6 +1848,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); @@ -1773,8 +1859,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( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b49a5f6578f..5bf9be87b57 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -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, @@ -82,7 +82,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); @@ -99,7 +99,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>; @@ -494,14 +494,14 @@ 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; + return false; }; let param_env = obligation.param_env; let body_id = obligation.cause.body_id; @@ -513,7 +513,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => trait_pred, }; let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else { - return; + return false; }; if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { @@ -537,11 +537,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { format!("&{}{}", derefs, &src[1..]), Applicability::MachineApplicable, ); + return true; } } } } } + false } /// Given a closure's `DefId`, return the given name of the closure. @@ -584,22 +586,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 = @@ -611,7 +613,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. @@ -622,7 +624,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); @@ -650,7 +652,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 { .. }) { @@ -667,6 +669,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } else { err.help(&format!("{}: `{}`", msg, snippet)); } + true } fn suggest_add_reference_to_arg( @@ -808,19 +811,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 { @@ -856,10 +860,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) { @@ -996,7 +1002,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); @@ -1015,7 +1021,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> { |
