}
}
- fn suggest_traits_to_import<'b>(&self,
- err: &mut DiagnosticBuilder<'_>,
- span: Span,
- rcvr_ty: Ty<'tcx>,
- item_name: ast::Ident,
- source: SelfSource<'b>,
- valid_out_of_scope_traits: Vec<DefId>) {
+ fn suggest_traits_to_import<'b>(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ span: Span,
+ rcvr_ty: Ty<'tcx>,
+ item_name: ast::Ident,
+ source: SelfSource<'b>,
+ valid_out_of_scope_traits: Vec<DefId>,
+ ) {
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
return;
}
candidates.sort_by(|a, b| a.cmp(b).reverse());
candidates.dedup();
- // FIXME #21673: this help message could be tuned to the case
- // of a type parameter: suggest adding a trait bound rather
- // than implementing.
- err.help("items from traits can only be used if the trait is implemented and in scope");
- let mut msg = format!("the following {traits_define} an item `{name}`, \
- perhaps you need to implement {one_of_them}:",
- traits_define = if candidates.len() == 1 {
- "trait defines"
- } else {
- "traits define"
- },
- one_of_them = if candidates.len() == 1 {
- "it"
- } else {
- "one of them"
- },
- name = item_name);
-
- for (i, trait_info) in candidates.iter().enumerate() {
- msg.push_str(&format!("\ncandidate #{}: `{}`",
- i + 1,
- self.tcx.def_path_str(trait_info.def_id)));
+ let param_type = match rcvr_ty.sty {
+ ty::Param(param) => Some(param),
+ ty::Ref(_, ty, _) => match ty.sty {
+ ty::Param(param) => Some(param),
+ _ => None,
+ }
+ _ => None,
+ };
+ err.help(if param_type.is_some() {
+ "items from traits can only be used if the type parameter is bounded by the trait"
+ } else {
+ "items from traits can only be used if the trait is implemented and in scope"
+ });
+ let mut msg = format!(
+ "the following {traits_define} an item `{name}`, perhaps you need to {action} \
+ {one_of_them}:",
+ traits_define = if candidates.len() == 1 {
+ "trait defines"
+ } else {
+ "traits define"
+ },
+ action = if let Some(param) = param_type {
+ format!("restrict type parameter `{}` with", param)
+ } else {
+ "implement".to_string()
+ },
+ one_of_them = if candidates.len() == 1 {
+ "it"
+ } else {
+ "one of them"
+ },
+ name = item_name,
+ );
+ // Obtain the span for `param` and use it for a structured suggestion.
+ let mut suggested = false;
+ if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) {
+ let table = table.borrow();
+ if let Some(did) = table.local_id_root {
+ let generics = self.tcx.generics_of(did);
+ let type_param = generics.type_param(param, self.tcx);
+ let hir = &self.tcx.hir();
+ if let Some(id) = hir.as_local_hir_id(type_param.def_id) {
+ // Get the `hir::Param` to verify whether it already has any bounds.
+ // We do this to avoid suggesting code that ends up as `T: FooBar`,
+ // instead we suggest `T: Foo + Bar` in that case.
+ let mut has_bounds = false;
+ if let Node::GenericParam(ref param) = hir.get(id) {
+ has_bounds = !param.bounds.is_empty();
+ }
+ let sp = hir.span(id);
+ // `sp` only covers `T`, change it so that it covers
+ // `T:` when appropriate
+ let sp = if has_bounds {
+ sp.to(self.tcx
+ .sess
+ .source_map()
+ .next_point(self.tcx.sess.source_map().next_point(sp)))
+ } else {
+ sp
+ };
+
+ // FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
+ // already there. That can happen when the cause is that we're in a const
+ // scope or associated function used as a method.
+ err.span_suggestions(
+ sp,
+ &msg[..],
+ candidates.iter().map(|t| format!(
+ "{}: {}{}",
+ param,
+ self.tcx.def_path_str(t.def_id),
+ if has_bounds { " +"} else { "" },
+ )),
+ Applicability::MaybeIncorrect,
+ );
+ suggested = true;
+ }
+ };
+ }
+
+ if !suggested {
+ for (i, trait_info) in candidates.iter().enumerate() {
+ msg.push_str(&format!(
+ "\ncandidate #{}: `{}`",
+ i + 1,
+ self.tcx.def_path_str(trait_info.def_id),
+ ));
+ }
+ err.note(&msg[..]);
}
- err.note(&msg[..]);
}
}
--- /dev/null
+error[E0599]: no method named `method` found for type `&T` in the current scope
+ --> $DIR/issue-21673.rs:6:7
+ |
+LL | x.method()
+ | ^^^^^^
+ |
+ = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
+ |
+LL | fn call_method<T: Foo + std::fmt::Debug>(x: &T) {
+ | ^^^^^^^^
+
+error[E0599]: no method named `method` found for type `T` in the current scope
+ --> $DIR/issue-21673.rs:10:7
+ |
+LL | x.method()
+ | ^^^^^^
+ |
+ = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
+ |
+LL | fn call_method_2<T: Foo>(x: T) {
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.