use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
-use rustc_hir::intravisit;
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::fast_reject::simplify_type;
use rustc_middle::ty::print::with_crate_prefix;
-use rustc_middle::ty::{
- self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
-};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_span::lev_distance;
use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{source_map, FileName, Span};
+use rustc_span::{source_map, FileName, MultiSpan, Span};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_trait_selection::traits::Obligation;
+use rustc_trait_selection::traits::{FulfillmentError, Obligation};
use std::cmp::Ordering;
use std::iter;
.into()],
);
let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs);
- let poly_trait_ref = trait_ref.to_poly_trait_ref();
+ let poly_trait_ref = ty::Binder::dummy(trait_ref);
let obligation = Obligation::misc(
span,
self.body_id,
sugg_span,
idx,
self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
);
}
}
sugg_span,
idx,
self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
);
}
}
Applicability::MaybeIncorrect,
);
}
- ExprKind::Path(ref qpath) => {
+ ExprKind::Path(QPath::Resolved(_, path)) => {
// local binding
- if let QPath::Resolved(_, path) = qpath {
- if let hir::def::Res::Local(hir_id) = path.res {
- let span = tcx.hir().span(hir_id);
- let snippet = tcx.sess.source_map().span_to_snippet(span);
- let filename = tcx.sess.source_map().span_to_filename(span);
-
- let parent_node = self
- .tcx
- .hir()
- .get(self.tcx.hir().get_parent_node(hir_id));
- let msg = format!(
- "you must specify a type for this binding, like `{}`",
- concrete_type,
- );
-
- match (filename, parent_node, snippet) {
- (
- FileName::Real(_),
- Node::Local(hir::Local {
- source: hir::LocalSource::Normal,
- ty,
- ..
- }),
- Ok(ref snippet),
- ) => {
- err.span_suggestion(
- // account for `let x: _ = 42;`
- // ^^^^
- span.to(ty
- .as_ref()
- .map(|ty| ty.span)
- .unwrap_or(span)),
- &msg,
- format!("{}: {}", snippet, concrete_type),
- Applicability::MaybeIncorrect,
- );
- }
- _ => {
- err.span_label(span, msg);
- }
+ if let hir::def::Res::Local(hir_id) = path.res {
+ let span = tcx.hir().span(hir_id);
+ let snippet = tcx.sess.source_map().span_to_snippet(span);
+ let filename = tcx.sess.source_map().span_to_filename(span);
+
+ let parent_node =
+ self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+ let msg = format!(
+ "you must specify a type for this binding, like `{}`",
+ concrete_type,
+ );
+
+ match (filename, parent_node, snippet) {
+ (
+ FileName::Real(_),
+ Node::Local(hir::Local {
+ source: hir::LocalSource::Normal,
+ ty,
+ ..
+ }),
+ Ok(ref snippet),
+ ) => {
+ err.span_suggestion(
+ // account for `let x: _ = 42;`
+ // ^^^^
+ span.to(ty
+ .as_ref()
+ .map(|ty| ty.span)
+ .unwrap_or(span)),
+ &msg,
+ format!("{}: {}", snippet, concrete_type),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ err.span_label(span, msg);
}
}
}
// Don't show generic arguments when the method can't be found in any implementation (#81576).
let mut ty_str_reported = ty_str.clone();
- if let ty::Adt(_, ref generics) = actual.kind() {
+ if let ty::Adt(_, generics) = actual.kind() {
if generics.len() > 0 {
let mut autoderef = self.autoderef(span, actual);
let candidate_found = autoderef.any(|(ty, _)| {
- if let ty::Adt(ref adt_deref, _) = ty.kind() {
+ if let ty::Adt(adt_deref, _) = ty.kind() {
self.tcx
.inherent_impls(adt_deref.did)
.iter()
}
}
if let Some(span) =
- tcx.sess.confused_type_with_std_module.borrow().get(&span)
+ tcx.resolutions(()).confused_type_with_std_module.get(&span)
{
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
err.span_suggestion(
let mut label_span_not_found = || {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
- if let ty::Adt(ref adt, _) = rcvr_ty.kind() {
+ if let ty::Adt(adt, _) = rcvr_ty.kind() {
let mut inherent_impls_candidate = self
.tcx
.inherent_impls(adt.did)
}
})
.collect::<Vec<_>>();
- if inherent_impls_candidate.len() > 0 {
+ if !inherent_impls_candidate.is_empty() {
inherent_impls_candidate.sort();
inherent_impls_candidate.dedup();
let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
if is_accessible {
- if self.is_fn_ty(&field_ty, span) {
+ if self.is_fn_ty(field_ty, span) {
let expr_span = expr.span.to(item_name.span);
err.multipart_suggestion(
&format!(
label_span_not_found();
}
- if self.is_fn_ty(&rcvr_ty, span) {
+ if self.is_fn_ty(rcvr_ty, span) {
fn report_function<T: std::fmt::Display>(
err: &mut DiagnosticBuilder<'_>,
name: T,
if let SelfSource::MethodCall(expr) = source {
if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
report_function(&mut err, expr_string);
- } else if let ExprKind::Path(QPath::Resolved(_, ref path)) = expr.kind {
+ } else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
if let Some(segment) = path.segments.last() {
report_function(&mut err, segment.ident);
}
let projection_ty = pred.skip_binder().projection_ty;
let substs_with_infer_self = tcx.mk_substs(
- iter::once(tcx.mk_ty_var(ty::TyVid { index: 0 }).into())
+ iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
.chain(projection_ty.substs.iter().skip(1)),
);
);
}
- bound_list.sort_by(|(_, a), (_, b)| a.cmp(&b)); // Sort alphabetically.
+ bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677
bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order.
bound_spans.sort();
err.note(&format!(
"the following trait bounds were not satisfied:\n{bound_list}"
));
+ self.suggest_derive(&mut err, &unsatisfied_predicates);
+
unsatisfied_bounds = true;
}
}
None
}
+ crate fn note_unmet_impls_on_type(
+ &self,
+ err: &mut rustc_errors::DiagnosticBuilder<'_>,
+ errors: Vec<FulfillmentError<'tcx>>,
+ ) {
+ let all_local_types_needing_impls =
+ errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() {
+ ty::Adt(def, _) => def.did.is_local(),
+ _ => false,
+ },
+ _ => false,
+ });
+ let mut preds: Vec<_> = errors
+ .iter()
+ .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => Some(pred),
+ _ => None,
+ })
+ .collect();
+ preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty()));
+ let def_ids = preds
+ .iter()
+ .filter_map(|pred| match pred.self_ty().kind() {
+ ty::Adt(def, _) => Some(def.did),
+ _ => None,
+ })
+ .collect::<FxHashSet<_>>();
+ let sm = self.tcx.sess.source_map();
+ let mut spans: MultiSpan = def_ids
+ .iter()
+ .filter_map(|def_id| {
+ let span = self.tcx.def_span(*def_id);
+ if span.is_dummy() { None } else { Some(sm.guess_head_span(span)) }
+ })
+ .collect::<Vec<_>>()
+ .into();
+
+ for pred in &preds {
+ match pred.self_ty().kind() {
+ ty::Adt(def, _) => {
+ spans.push_span_label(
+ sm.guess_head_span(self.tcx.def_span(def.did)),
+ format!("must implement `{}`", pred.trait_ref.print_only_trait_path()),
+ );
+ }
+ _ => {}
+ }
+ }
+
+ if all_local_types_needing_impls && spans.primary_span().is_some() {
+ let msg = if preds.len() == 1 {
+ format!(
+ "an implementation of `{}` might be missing for `{}`",
+ preds[0].trait_ref.print_only_trait_path(),
+ preds[0].self_ty()
+ )
+ } else {
+ format!(
+ "the following type{} would have to `impl` {} required trait{} for this \
+ operation to be valid",
+ pluralize!(def_ids.len()),
+ if def_ids.len() == 1 { "its" } else { "their" },
+ pluralize!(preds.len()),
+ )
+ };
+ err.span_note(spans, &msg);
+ }
+
+ let preds: Vec<_> = errors.iter().map(|e| (e.obligation.predicate, None)).collect();
+ self.suggest_derive(err, &preds);
+ }
+
+ fn suggest_derive(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ unsatisfied_predicates: &Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
+ ) {
+ let mut derives = Vec::<(String, Span, String)>::new();
+ let mut traits = Vec::<Span>::new();
+ for (pred, _) in unsatisfied_predicates {
+ let trait_pred = match pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(trait_pred) => trait_pred,
+ _ => continue,
+ };
+ let adt = match trait_pred.self_ty().ty_adt_def() {
+ Some(adt) if adt.did.is_local() => adt,
+ _ => continue,
+ };
+ let can_derive = match self.tcx.get_diagnostic_name(trait_pred.def_id()) {
+ Some(sym::Default) => !adt.is_enum(),
+ Some(
+ sym::Eq
+ | sym::PartialEq
+ | sym::Ord
+ | sym::PartialOrd
+ | sym::Clone
+ | sym::Copy
+ | sym::Hash
+ | sym::Debug,
+ ) => true,
+ _ => false,
+ };
+ if can_derive {
+ derives.push((
+ format!("{}", trait_pred.self_ty()),
+ self.tcx.def_span(adt.did),
+ format!("{}", trait_pred.trait_ref.print_only_trait_name()),
+ ));
+ } else {
+ traits.push(self.tcx.def_span(trait_pred.def_id()));
+ }
+ }
+ derives.sort();
+ let derives_grouped = derives.into_iter().fold(
+ Vec::<(String, Span, String)>::new(),
+ |mut acc, (self_name, self_span, trait_name)| {
+ if let Some((acc_self_name, _, ref mut traits)) = acc.last_mut() {
+ if acc_self_name == &self_name {
+ traits.push_str(format!(", {}", trait_name).as_str());
+ return acc;
+ }
+ }
+ acc.push((self_name, self_span, trait_name));
+ acc
+ },
+ );
+ traits.sort();
+ traits.dedup();
+
+ let len = traits.len();
+ if len > 0 {
+ let span: MultiSpan = traits.into();
+ err.span_note(
+ span,
+ &format!("the following trait{} must be implemented", pluralize!(len),),
+ );
+ }
+
+ for (self_name, self_span, traits) in &derives_grouped {
+ err.span_suggestion_verbose(
+ self_span.shrink_to_lo(),
+ &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits),
+ format!("#[derive({})]\n", traits),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
candidates: Vec<DefId>,
) {
let module_did = self.tcx.parent_module(self.body_id);
- let module_id = self.tcx.hir().local_def_id_to_hir_id(module_did);
- let krate = self.tcx.hir().krate();
- let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id);
+ let (span, found_use) = find_use_placement(self.tcx, module_did);
if let Some(span) = span {
let path_strings = candidates.iter().map(|did| {
// Produce an additional newline to separate the new use statement
)
};
// Obtain the span for `param` and use it for a structured suggestion.
- if let (Some(ref param), Some(ref table)) =
- (param_type, self.in_progress_typeck_results)
- {
+ if let (Some(param), Some(table)) = (param_type, self.in_progress_typeck_results) {
let table_owner = table.borrow().hir_owner;
let generics = self.tcx.generics_of(table_owner.to_def_id());
let type_param = generics.type_param(param, self.tcx);
// We do this to avoid suggesting code that ends up as `T: FooBar`,
// instead we suggest `T: Foo + Bar` in that case.
match hir.get(id) {
- Node::GenericParam(ref param) => {
+ Node::GenericParam(param) => {
let mut impl_trait = false;
let has_bounds =
if let hir::GenericParamKind::Type { synthetic: Some(_), .. } =
match ty.kind() {
ty::Adt(def, _) => def.did.is_local(),
ty::Foreign(did) => did.is_local(),
- ty::Dynamic(ref tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()),
+ ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()),
ty::Param(_) => true,
// Everything else (primitive types, etc.) is effectively
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
}
- tcx.hir().krate().visit_all_item_likes(&mut Visitor { traits: &mut traits });
+ tcx.hir().visit_all_item_likes(&mut Visitor { traits: &mut traits });
// Cross-crate:
tcx: TyCtxt<'_>,
traits: &mut Vec<DefId>,
external_mods: &mut FxHashSet<DefId>,
- res: Res,
+ res: Res<!>,
) {
match res {
Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) => {
providers.all_traits = compute_all_traits;
}
-struct UsePlacementFinder<'tcx> {
- target_module: hir::HirId,
- span: Option<Span>,
- found_use: bool,
- tcx: TyCtxt<'tcx>,
-}
-
-impl UsePlacementFinder<'tcx> {
- fn check(
- tcx: TyCtxt<'tcx>,
- krate: &'tcx hir::Crate<'tcx>,
- target_module: hir::HirId,
- ) -> (Option<Span>, bool) {
- let mut finder = UsePlacementFinder { target_module, span: None, found_use: false, tcx };
- intravisit::walk_crate(&mut finder, krate);
- (finder.span, finder.found_use)
- }
-}
-
-impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> {
- fn visit_mod(&mut self, module: &'tcx hir::Mod<'tcx>, _: Span, hir_id: hir::HirId) {
- if self.span.is_some() {
- return;
- }
- if hir_id != self.target_module {
- intravisit::walk_mod(self, module, hir_id);
- return;
- }
- // Find a `use` statement.
- for &item_id in module.item_ids {
- let item = self.tcx.hir().item(item_id);
- match item.kind {
- hir::ItemKind::Use(..) => {
- // Don't suggest placing a `use` before the prelude
- // import or other generated ones.
- if !item.span.from_expansion() {
- self.span = Some(item.span.shrink_to_lo());
- self.found_use = true;
- return;
- }
+fn find_use_placement<'tcx>(tcx: TyCtxt<'tcx>, target_module: LocalDefId) -> (Option<Span>, bool) {
+ let mut span = None;
+ let mut found_use = false;
+ let (module, _, _) = tcx.hir().get_module(target_module);
+
+ // Find a `use` statement.
+ for &item_id in module.item_ids {
+ let item = tcx.hir().item(item_id);
+ match item.kind {
+ hir::ItemKind::Use(..) => {
+ // Don't suggest placing a `use` before the prelude
+ // import or other generated ones.
+ if !item.span.from_expansion() {
+ span = Some(item.span.shrink_to_lo());
+ found_use = true;
+ break;
}
- // Don't place `use` before `extern crate`...
- hir::ItemKind::ExternCrate(_) => {}
- // ...but do place them before the first other item.
- _ => {
- if self.span.map_or(true, |span| item.span < span) {
- if !item.span.from_expansion() {
- self.span = Some(item.span.shrink_to_lo());
- // Don't insert between attributes and an item.
- let attrs = self.tcx.hir().attrs(item.hir_id());
- // Find the first attribute on the item.
- // FIXME: This is broken for active attributes.
- for attr in attrs {
- if !attr.span.is_dummy()
- && self.span.map_or(true, |span| attr.span < span)
- {
- self.span = Some(attr.span.shrink_to_lo());
- }
+ }
+ // Don't place `use` before `extern crate`...
+ hir::ItemKind::ExternCrate(_) => {}
+ // ...but do place them before the first other item.
+ _ => {
+ if span.map_or(true, |span| item.span < span) {
+ if !item.span.from_expansion() {
+ span = Some(item.span.shrink_to_lo());
+ // Don't insert between attributes and an item.
+ let attrs = tcx.hir().attrs(item.hir_id());
+ // Find the first attribute on the item.
+ // FIXME: This is broken for active attributes.
+ for attr in attrs {
+ if !attr.span.is_dummy() && span.map_or(true, |span| attr.span < span) {
+ span = Some(attr.span.shrink_to_lo());
}
}
}
}
}
- type Map = intravisit::ErasedMap<'tcx>;
-
- fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
- intravisit::NestedVisitorMap::None
- }
+ (span, found_use)
}
fn print_disambiguation_help(
span: Span,
candidate: Option<usize>,
source_map: &source_map::SourceMap,
+ fn_has_self_parameter: bool,
) {
let mut applicability = Applicability::MachineApplicable;
let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) {
.collect::<Vec<_>>()
.join(", "),
);
+ let trait_name = if !fn_has_self_parameter {
+ format!("<{} as {}>", rcvr_ty, trait_name)
+ } else {
+ trait_name
+ };
(span, format!("{}::{}{}", trait_name, item_name, args))
} else {
- (span.with_hi(item_name.span.lo()), format!("{}::", trait_name))
+ (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name))
};
err.span_suggestion_verbose(
span,