1
Fork 0

Take into account negative impls in "trait item not found" suggestions

This commit is contained in:
LeSeulArtichaut 2020-12-07 12:34:27 +01:00
parent a68864b688
commit cfc38d2d08
3 changed files with 193 additions and 28 deletions

View file

@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath}; use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::hir::map as hir_map; use rustc_middle::hir::map as hir_map;
use rustc_middle::ty::fast_reject::simplify_type;
use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else { } else {
"items from traits can only be used if the trait is implemented and in scope" "items from traits can only be used if the trait is implemented and in scope"
}); });
let candidates_len = candidates.len();
let message = |action| { let message = |action| {
format!( format!(
"the following {traits_define} an item `{name}`, perhaps you need to {action} \ "the following {traits_define} an item `{name}`, perhaps you need to {action} \
{one_of_them}:", {one_of_them}:",
traits_define = traits_define =
if candidates.len() == 1 { "trait defines" } else { "traits define" }, if candidates_len == 1 { "trait defines" } else { "traits define" },
action = action, action = action,
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, one_of_them = if candidates_len == 1 { "it" } else { "one of them" },
name = item_name, name = item_name,
) )
}; };
// Obtain the span for `param` and use it for a structured suggestion. // Obtain the span for `param` and use it for a structured suggestion.
let mut suggested = false;
if let (Some(ref param), Some(ref table)) = if let (Some(ref param), Some(ref table)) =
(param_type, self.in_progress_typeck_results) (param_type, self.in_progress_typeck_results)
{ {
@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
suggested = true; return;
} }
Node::Item(hir::Item { Node::Item(hir::Item {
kind: hir::ItemKind::Trait(.., bounds, _), kind: hir::ItemKind::Trait(.., bounds, _),
@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}), }),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
suggested = true; return;
} }
_ => {} _ => {}
} }
} }
} }
if !suggested { let (potential_candidates, explicitly_negative) = if param_type.is_some() {
let action = if let Some(param) = param_type { // FIXME: Even though negative bounds are not implemented, we could maybe handle
format!("restrict type parameter `{}` with", param) // cases where a positive bound implies a negative impl.
} else { (candidates, Vec::new())
// FIXME: it might only need to be imported into scope, not implemented. } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) {
"implement".to_string() let mut potential_candidates = Vec::new();
}; let mut explicitly_negative = Vec::new();
let mut use_note = true; for candidate in candidates {
if let [trait_info] = &candidates[..] { // Check if there's a negative impl of `candidate` for `rcvr_ty`
if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) { if self
err.span_note( .tcx
self.tcx.sess.source_map().guess_head_span(span), .all_impls(candidate.def_id)
&format!( .filter(|imp_did| {
"`{}` defines an item `{}`, perhaps you need to {} it", self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
self.tcx.def_path_str(trait_info.def_id), })
item_name, .any(|imp_did| {
action let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
), let imp_simp = simplify_type(self.tcx, imp.self_ty(), true);
); imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false)
use_note = false })
{
explicitly_negative.push(candidate);
} else {
potential_candidates.push(candidate);
} }
} }
if use_note { (potential_candidates, explicitly_negative)
} else {
// We don't know enough about `recv_ty` to make proper suggestions.
(candidates, Vec::new())
};
let action = if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
// FIXME: it might only need to be imported into scope, not implemented.
"implement".to_string()
};
match &potential_candidates[..] {
[] => {}
[trait_info] if trait_info.def_id.is_local() => {
let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap();
err.span_note(
self.tcx.sess.source_map().guess_head_span(span),
&format!(
"`{}` defines an item `{}`, perhaps you need to {} it",
self.tcx.def_path_str(trait_info.def_id),
item_name,
action
),
);
}
trait_infos => {
let mut msg = message(action); let mut msg = message(action);
for (i, trait_info) in candidates.iter().enumerate() { for (i, trait_info) in trait_infos.iter().enumerate() {
msg.push_str(&format!( msg.push_str(&format!(
"\ncandidate #{}: `{}`", "\ncandidate #{}: `{}`",
i + 1, i + 1,
self.tcx.def_path_str(trait_info.def_id), self.tcx.def_path_str(trait_info.def_id),
)); ));
} }
err.note(&msg[..]); err.note(&msg);
}
}
match &explicitly_negative[..] {
[] => {}
[trait_info] => {
let msg = format!(
"the trait `{}` defines an item `{}`, but is explicitely unimplemented",
self.tcx.def_path_str(trait_info.def_id),
item_name
);
err.note(&msg);
}
trait_infos => {
let mut msg = format!(
"the following traits define an item `{}`, but are explicitely unimplemented:",
item_name
);
for trait_info in trait_infos {
msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id)));
}
err.note(&msg);
} }
} }
} }

View file

@ -0,0 +1,53 @@
// This tests issue #79683: note in the error message that the trait is
// explicitely unimplemented instead of suggesting to implement it.
#![feature(negative_impls)]
struct Qux;
//~^ NOTE method `clone` not found for this
//~^^ NOTE method `foo` not found for this
impl !Clone for Qux {}
trait Bar {
fn bar(&self);
}
impl !Bar for u32 {}
trait Foo {
fn foo(&self);
}
//~^^^ NOTE `Foo` defines an item `foo`, perhaps you need to implement it
trait FooBar {
fn foo(&self);
}
impl !Foo for Qux {}
impl !FooBar for Qux {}
impl !FooBar for u32 {}
fn main() {
Qux.clone();
//~^ ERROR no method named `clone` found for struct `Qux`
//~| NOTE method not found in `Qux`
//~| NOTE `Clone` defines an item `clone`, but is explicitely unimplemented
0_u32.bar();
//~^ ERROR no method named `bar` found for type `u32`
//~| NOTE method not found in `u32`
//~| NOTE `Bar` defines an item `bar`, but is explicitely unimplemented
Qux.foo();
//~^ ERROR no method named `foo` found for struct `Qux`
//~| NOTE method not found in `Qux`
//~| NOTE the following traits define an item `foo`, but are explicitely unimplemented
0_u32.foo();
//~^ ERROR no method named `foo` found for type `u32`
//~| NOTE method not found in `u32`
//~| NOTE `FooBar` defines an item `foo`, but is explicitely unimplemented
}

View file

@ -0,0 +1,60 @@
error[E0599]: no method named `clone` found for struct `Qux` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:34:9
|
LL | struct Qux;
| ----------- method `clone` not found for this
...
LL | Qux.clone();
| ^^^^^ method not found in `Qux`
|
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | fn clone(&self) -> Self;
| -----
| |
| the method is available for `Arc<Qux>` here
| the method is available for `Rc<Qux>` here
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented
error[E0599]: no method named `bar` found for type `u32` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:39:11
|
LL | 0_u32.bar();
| ^^^ method not found in `u32`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the trait `Bar` defines an item `bar`, but is explicitely unimplemented
error[E0599]: no method named `foo` found for struct `Qux` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:44:9
|
LL | struct Qux;
| ----------- method `foo` not found for this
...
LL | Qux.foo();
| ^^^ method not found in `Qux`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `foo`, but are explicitely unimplemented:
Foo
FooBar
error[E0599]: no method named `foo` found for type `u32` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:49:11
|
LL | 0_u32.foo();
| ^^^ method not found in `u32`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `Foo` defines an item `foo`, perhaps you need to implement it
--> $DIR/explicitly-unimplemented-error-message.rs:18:1
|
LL | trait Foo {
| ^^^^^^^^^
= note: the trait `FooBar` defines an item `foo`, but is explicitely unimplemented
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0599`.