X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Ftraits%2Ferror_reporting.rs;h=65d08ab03aaaf699bd60872ff6d53a5ec4198609;hb=48b0fd2060614fc917616e5023a2b1af3158308a;hp=ea29cc0d93f538a51c8f38375e24fde6a6bb4783;hpb=361791bb5fdd714bdc39f8af835f6468dd18331d;p=rust.git diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index ea29cc0d93f..65d08ab03aa 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -38,6 +38,7 @@ use syntax::ast; use syntax::symbol::{sym, kw}; use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan}; +use rustc::hir::def_id::LOCAL_CRATE; use rustc_error_codes::*; @@ -165,7 +166,7 @@ fn report_fulfillment_error( body_id: Option, fallback_has_occurred: bool, ) { - debug!("report_fulfillment_errors({:?})", error); + debug!("report_fulfillment_error({:?})", error); match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -799,6 +800,7 @@ pub fn report_selection_error( self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); + self.note_version_mismatch(&mut err, &trait_ref); // Try to report a help message if !trait_ref.has_infer_types() && @@ -1050,6 +1052,43 @@ pub fn report_selection_error( err.emit(); } + /// 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 DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let get_trait_impl = |trait_def_id| { + let mut trait_impl = None; + self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }); + trait_impl + }; + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let all_traits = self.tcx.all_traits(LOCAL_CRATE); + let traits_with_same_path: std::collections::BTreeSet<_> = all_traits + .iter() + .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(); + 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); + err.span_help(impl_span, "trait impl with same name found"); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = format!( + "Perhaps two different versions of crate `{}` are being used?", + trait_crate + ); + err.note(&crate_msg); + } + } + } fn suggest_restricting_param_bound( &self, err: &mut DiagnosticBuilder<'_>, @@ -1245,6 +1284,60 @@ fn suggest_borrow_on_unsized_slice( } } + fn mk_obligation_for_def_id( + &self, + def_id: DefId, + output_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> PredicateObligation<'tcx> { + let new_trait_ref = ty::TraitRef { + def_id, + substs: self.tcx.mk_substs_trait(output_ty, &[]), + }; + Obligation::new(cause, param_env, new_trait_ref.to_predicate()) + } + + /// Given a closure's `DefId`, return the given name of the closure. + /// + /// This doesn't account for reassignments, but it's only used for suggestions. + fn get_closure_name( + &self, + def_id: DefId, + err: &mut DiagnosticBuilder<'_>, + msg: &str, + ) -> Option { + let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { + Some(format!("{}", name)) + } + _ => { + err.note(&msg); + None + } + } + }; + + let hir = self.tcx.hir(); + let hir_id = hir.as_local_hir_id(def_id)?; + let parent_node = hir.get_parent_node(hir_id); + match hir.find(parent_node) { + Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Local(local), .. + })) => get_name(err, &local.pat.kind), + // Different to previous arm because one is `&hir::Local` and the other + // is `P`. + Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), + _ => return None, + } + } + + /// We tried to apply the bound to an `fn` or closure. Check whether calling it would + /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, @@ -1253,63 +1346,82 @@ fn suggest_fn_call( points_at_arg: bool, ) { let self_ty = trait_ref.self_ty(); - match self_ty.kind { + let (def_id, output_ty, callable) = match self_ty.kind { + ty::Closure(def_id, substs) => { + (def_id, self.closure_sig(def_id, substs).output(), "closure") + } ty::FnDef(def_id, _) => { - // We tried to apply the bound to an `fn`. Check whether calling it would evaluate - // to a type that *would* satisfy the trait binding. If it would, suggest calling - // it: `bar(foo)` -> `bar(foo)`. This case is *very* likely to be hit if `foo` is - // `async`. - let output_ty = self_ty.fn_sig(self.tcx).output(); - let new_trait_ref = ty::TraitRef { - def_id: trait_ref.def_id(), - substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]), + (def_id, self_ty.fn_sig(self.tcx).output(), "function") + } + _ => return, + }; + let msg = format!("use parentheses to call the {}", callable); + + let obligation = self.mk_obligation_for_def_id( + trait_ref.def_id(), + output_ty.skip_binder(), + obligation.cause.clone(), + obligation.param_env, + ); + + match self.evaluate_obligation(&obligation) { + Ok(EvaluationResult::EvaluatedToOk) | + Ok(EvaluationResult::EvaluatedToOkModuloRegions) | + Ok(EvaluationResult::EvaluatedToAmbig) => {} + _ => return, + } + let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. + let snippet = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, decl, _, span, ..), + .. + })) => { + err.span_label(*span, "consider calling this closure"); + let name = match self.get_closure_name(def_id, err, &msg) { + Some(name) => name, + None => return, }; - let obligation = Obligation::new( - obligation.cause.clone(), - obligation.param_env, - new_trait_ref.to_predicate(), - ); - match self.evaluate_obligation(&obligation) { - Ok(EvaluationResult::EvaluatedToOk) | - Ok(EvaluationResult::EvaluatedToOkModuloRegions) | - Ok(EvaluationResult::EvaluatedToAmbig) => { - if let Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) = self.tcx.hir().get_if_local(def_id) { - let body = self.tcx.hir().body(*body_id); - let msg = "use parentheses to call the function"; - let snippet = format!( - "{}({})", - ident, - body.params.iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }).collect::>().join(", "), - ); - // When the obligation error has been ensured to have been caused by - // an argument, the `obligation.cause.span` points at the expression - // of the argument, so we can provide a suggestion. This is signaled - // by `points_at_arg`. Otherwise, we give a more general note. - if points_at_arg { - err.span_suggestion( - obligation.cause.span, - msg, - snippet, - Applicability::HasPlaceholders, - ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); - } - } - } - _ => {} - } + let args = decl.inputs.iter() + .map(|_| "_") + .collect::>() + .join(", "); + format!("{}({})", name, args) + } + Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Fn(.., body_id), + .. + })) => { + err.span_label(ident.span, "consider calling this function"); + let body = hir.body(*body_id); + let args = body.params.iter() + .map(|arg| match &arg.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + // FIXME: provide a better suggestion when encountering `SelfLower`, it + // should suggest a method call. + if ident.name != kw::SelfLower => ident.to_string(), + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + format!("{}({})", ident, args) } - _ => {} + _ => return, + }; + if points_at_arg { + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. This is signaled + // by `points_at_arg`. Otherwise, we give a more general note. + err.span_suggestion( + obligation.cause.span, + &msg, + snippet, + Applicability::HasPlaceholders, + ); + } else { + err.help(&format!("{}: `{}`", msg, snippet)); } } @@ -1410,12 +1522,11 @@ fn suggest_remove_reference( if let ty::Ref(_, t_type, _) = trait_type.kind { trait_type = t_type; - let substs = self.tcx.mk_substs_trait(trait_type, &[]); - let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs); - let new_obligation = Obligation::new( + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.def_id, + trait_type, ObligationCause::dummy(), obligation.param_env, - new_trait_ref.to_predicate(), ); if self.predicate_may_hold(&new_obligation) { @@ -1473,12 +1584,11 @@ fn suggest_change_mut( hir::Mutability::Immutable => self.tcx.mk_mut_ref(region, t_type), }; - let substs = self.tcx.mk_substs_trait(&trait_type, &[]); - let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs); - let new_obligation = Obligation::new( + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.skip_binder().def_id, + trait_type, ObligationCause::dummy(), obligation.param_env, - new_trait_ref.to_predicate(), ); if self.evaluate_obligation_no_overflow(