Take into account negative impls in "trait item not found" suggestions
This commit is contained in:
parent
a68864b688
commit
cfc38d2d08
@ -12,6 +12,7 @@
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
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::{
|
||||
self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
|
||||
@ -1074,19 +1075,19 @@ fn suggest_traits_to_import<'b>(
|
||||
} else {
|
||||
"items from traits can only be used if the trait is implemented and in scope"
|
||||
});
|
||||
let candidates_len = candidates.len();
|
||||
let message = |action| {
|
||||
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" },
|
||||
if candidates_len == 1 { "trait defines" } else { "traits define" },
|
||||
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,
|
||||
)
|
||||
};
|
||||
// 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_typeck_results)
|
||||
{
|
||||
@ -1147,7 +1148,7 @@ fn suggest_traits_to_import<'b>(
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
suggested = true;
|
||||
return;
|
||||
}
|
||||
Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Trait(.., bounds, _),
|
||||
@ -1167,45 +1168,96 @@ fn suggest_traits_to_import<'b>(
|
||||
}),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
suggested = true;
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !suggested {
|
||||
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()
|
||||
};
|
||||
let mut use_note = true;
|
||||
if let [trait_info] = &candidates[..] {
|
||||
if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) {
|
||||
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
|
||||
),
|
||||
);
|
||||
use_note = false
|
||||
let (potential_candidates, explicitly_negative) = if param_type.is_some() {
|
||||
// FIXME: Even though negative bounds are not implemented, we could maybe handle
|
||||
// cases where a positive bound implies a negative impl.
|
||||
(candidates, Vec::new())
|
||||
} else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) {
|
||||
let mut potential_candidates = Vec::new();
|
||||
let mut explicitly_negative = Vec::new();
|
||||
for candidate in candidates {
|
||||
// Check if there's a negative impl of `candidate` for `rcvr_ty`
|
||||
if self
|
||||
.tcx
|
||||
.all_impls(candidate.def_id)
|
||||
.filter(|imp_did| {
|
||||
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
|
||||
})
|
||||
.any(|imp_did| {
|
||||
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)
|
||||
})
|
||||
{
|
||||
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);
|
||||
for (i, trait_info) in candidates.iter().enumerate() {
|
||||
for (i, trait_info) in trait_infos.iter().enumerate() {
|
||||
msg.push_str(&format!(
|
||||
"\ncandidate #{}: `{}`",
|
||||
i + 1,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user