Skip to content

Commit 8ad950f

Browse files
authored
Rollup merge of rust-lang#79790 - LeSeulArtichaut:issue-79683, r=lcnr
Take into account negative impls in "trait item not found" suggestions This removes the suggestion to implement a trait for a type when that type already has a negative implementation for the trait, and replaces it with a note to point out that the trait is explicitely unimplemented, as suggested by `@scottmcm.` Helps with rust-lang#79683. r? `@scottmcm` do you want to review this?
2 parents 268cbfe + cfc38d2 commit 8ad950f

File tree

3 files changed

+193
-28
lines changed

3 files changed

+193
-28
lines changed

compiler/rustc_typeck/src/check/method/suggest.rs

+80-28
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
1212
use rustc_hir::{ExprKind, Node, QPath};
1313
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1414
use rustc_middle::hir::map as hir_map;
15+
use rustc_middle::ty::fast_reject::simplify_type;
1516
use rustc_middle::ty::print::with_crate_prefix;
1617
use rustc_middle::ty::{
1718
self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
@@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10741075
} else {
10751076
"items from traits can only be used if the trait is implemented and in scope"
10761077
});
1078+
let candidates_len = candidates.len();
10771079
let message = |action| {
10781080
format!(
10791081
"the following {traits_define} an item `{name}`, perhaps you need to {action} \
10801082
{one_of_them}:",
10811083
traits_define =
1082-
if candidates.len() == 1 { "trait defines" } else { "traits define" },
1084+
if candidates_len == 1 { "trait defines" } else { "traits define" },
10831085
action = action,
1084-
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
1086+
one_of_them = if candidates_len == 1 { "it" } else { "one of them" },
10851087
name = item_name,
10861088
)
10871089
};
10881090
// Obtain the span for `param` and use it for a structured suggestion.
1089-
let mut suggested = false;
10901091
if let (Some(ref param), Some(ref table)) =
10911092
(param_type, self.in_progress_typeck_results)
10921093
{
@@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11471148
Applicability::MaybeIncorrect,
11481149
);
11491150
}
1150-
suggested = true;
1151+
return;
11511152
}
11521153
Node::Item(hir::Item {
11531154
kind: hir::ItemKind::Trait(.., bounds, _),
@@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11671168
}),
11681169
Applicability::MaybeIncorrect,
11691170
);
1170-
suggested = true;
1171+
return;
11711172
}
11721173
_ => {}
11731174
}
11741175
}
11751176
}
11761177

1177-
if !suggested {
1178-
let action = if let Some(param) = param_type {
1179-
format!("restrict type parameter `{}` with", param)
1180-
} else {
1181-
// FIXME: it might only need to be imported into scope, not implemented.
1182-
"implement".to_string()
1183-
};
1184-
let mut use_note = true;
1185-
if let [trait_info] = &candidates[..] {
1186-
if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) {
1187-
err.span_note(
1188-
self.tcx.sess.source_map().guess_head_span(span),
1189-
&format!(
1190-
"`{}` defines an item `{}`, perhaps you need to {} it",
1191-
self.tcx.def_path_str(trait_info.def_id),
1192-
item_name,
1193-
action
1194-
),
1195-
);
1196-
use_note = false
1178+
let (potential_candidates, explicitly_negative) = if param_type.is_some() {
1179+
// FIXME: Even though negative bounds are not implemented, we could maybe handle
1180+
// cases where a positive bound implies a negative impl.
1181+
(candidates, Vec::new())
1182+
} else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) {
1183+
let mut potential_candidates = Vec::new();
1184+
let mut explicitly_negative = Vec::new();
1185+
for candidate in candidates {
1186+
// Check if there's a negative impl of `candidate` for `rcvr_ty`
1187+
if self
1188+
.tcx
1189+
.all_impls(candidate.def_id)
1190+
.filter(|imp_did| {
1191+
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
1192+
})
1193+
.any(|imp_did| {
1194+
let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
1195+
let imp_simp = simplify_type(self.tcx, imp.self_ty(), true);
1196+
imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false)
1197+
})
1198+
{
1199+
explicitly_negative.push(candidate);
1200+
} else {
1201+
potential_candidates.push(candidate);
11971202
}
11981203
}
1199-
if use_note {
1204+
(potential_candidates, explicitly_negative)
1205+
} else {
1206+
// We don't know enough about `recv_ty` to make proper suggestions.
1207+
(candidates, Vec::new())
1208+
};
1209+
1210+
let action = if let Some(param) = param_type {
1211+
format!("restrict type parameter `{}` with", param)
1212+
} else {
1213+
// FIXME: it might only need to be imported into scope, not implemented.
1214+
"implement".to_string()
1215+
};
1216+
match &potential_candidates[..] {
1217+
[] => {}
1218+
[trait_info] if trait_info.def_id.is_local() => {
1219+
let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap();
1220+
err.span_note(
1221+
self.tcx.sess.source_map().guess_head_span(span),
1222+
&format!(
1223+
"`{}` defines an item `{}`, perhaps you need to {} it",
1224+
self.tcx.def_path_str(trait_info.def_id),
1225+
item_name,
1226+
action
1227+
),
1228+
);
1229+
}
1230+
trait_infos => {
12001231
let mut msg = message(action);
1201-
for (i, trait_info) in candidates.iter().enumerate() {
1232+
for (i, trait_info) in trait_infos.iter().enumerate() {
12021233
msg.push_str(&format!(
12031234
"\ncandidate #{}: `{}`",
12041235
i + 1,
12051236
self.tcx.def_path_str(trait_info.def_id),
12061237
));
12071238
}
1208-
err.note(&msg[..]);
1239+
err.note(&msg);
1240+
}
1241+
}
1242+
match &explicitly_negative[..] {
1243+
[] => {}
1244+
[trait_info] => {
1245+
let msg = format!(
1246+
"the trait `{}` defines an item `{}`, but is explicitely unimplemented",
1247+
self.tcx.def_path_str(trait_info.def_id),
1248+
item_name
1249+
);
1250+
err.note(&msg);
1251+
}
1252+
trait_infos => {
1253+
let mut msg = format!(
1254+
"the following traits define an item `{}`, but are explicitely unimplemented:",
1255+
item_name
1256+
);
1257+
for trait_info in trait_infos {
1258+
msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id)));
1259+
}
1260+
err.note(&msg);
12091261
}
12101262
}
12111263
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This tests issue #79683: note in the error message that the trait is
2+
// explicitely unimplemented instead of suggesting to implement it.
3+
4+
#![feature(negative_impls)]
5+
6+
struct Qux;
7+
//~^ NOTE method `clone` not found for this
8+
//~^^ NOTE method `foo` not found for this
9+
10+
impl !Clone for Qux {}
11+
12+
trait Bar {
13+
fn bar(&self);
14+
}
15+
16+
impl !Bar for u32 {}
17+
18+
trait Foo {
19+
fn foo(&self);
20+
}
21+
//~^^^ NOTE `Foo` defines an item `foo`, perhaps you need to implement it
22+
23+
trait FooBar {
24+
fn foo(&self);
25+
}
26+
27+
impl !Foo for Qux {}
28+
29+
impl !FooBar for Qux {}
30+
31+
impl !FooBar for u32 {}
32+
33+
fn main() {
34+
Qux.clone();
35+
//~^ ERROR no method named `clone` found for struct `Qux`
36+
//~| NOTE method not found in `Qux`
37+
//~| NOTE `Clone` defines an item `clone`, but is explicitely unimplemented
38+
39+
0_u32.bar();
40+
//~^ ERROR no method named `bar` found for type `u32`
41+
//~| NOTE method not found in `u32`
42+
//~| NOTE `Bar` defines an item `bar`, but is explicitely unimplemented
43+
44+
Qux.foo();
45+
//~^ ERROR no method named `foo` found for struct `Qux`
46+
//~| NOTE method not found in `Qux`
47+
//~| NOTE the following traits define an item `foo`, but are explicitely unimplemented
48+
49+
0_u32.foo();
50+
//~^ ERROR no method named `foo` found for type `u32`
51+
//~| NOTE method not found in `u32`
52+
//~| NOTE `FooBar` defines an item `foo`, but is explicitely unimplemented
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
error[E0599]: no method named `clone` found for struct `Qux` in the current scope
2+
--> $DIR/explicitly-unimplemented-error-message.rs:34:9
3+
|
4+
LL | struct Qux;
5+
| ----------- method `clone` not found for this
6+
...
7+
LL | Qux.clone();
8+
| ^^^^^ method not found in `Qux`
9+
|
10+
::: $SRC_DIR/core/src/clone.rs:LL:COL
11+
|
12+
LL | fn clone(&self) -> Self;
13+
| -----
14+
| |
15+
| the method is available for `Arc<Qux>` here
16+
| the method is available for `Rc<Qux>` here
17+
|
18+
= help: items from traits can only be used if the trait is implemented and in scope
19+
= note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented
20+
21+
error[E0599]: no method named `bar` found for type `u32` in the current scope
22+
--> $DIR/explicitly-unimplemented-error-message.rs:39:11
23+
|
24+
LL | 0_u32.bar();
25+
| ^^^ method not found in `u32`
26+
|
27+
= help: items from traits can only be used if the trait is implemented and in scope
28+
= note: the trait `Bar` defines an item `bar`, but is explicitely unimplemented
29+
30+
error[E0599]: no method named `foo` found for struct `Qux` in the current scope
31+
--> $DIR/explicitly-unimplemented-error-message.rs:44:9
32+
|
33+
LL | struct Qux;
34+
| ----------- method `foo` not found for this
35+
...
36+
LL | Qux.foo();
37+
| ^^^ method not found in `Qux`
38+
|
39+
= help: items from traits can only be used if the trait is implemented and in scope
40+
= note: the following traits define an item `foo`, but are explicitely unimplemented:
41+
Foo
42+
FooBar
43+
44+
error[E0599]: no method named `foo` found for type `u32` in the current scope
45+
--> $DIR/explicitly-unimplemented-error-message.rs:49:11
46+
|
47+
LL | 0_u32.foo();
48+
| ^^^ method not found in `u32`
49+
|
50+
= help: items from traits can only be used if the trait is implemented and in scope
51+
note: `Foo` defines an item `foo`, perhaps you need to implement it
52+
--> $DIR/explicitly-unimplemented-error-message.rs:18:1
53+
|
54+
LL | trait Foo {
55+
| ^^^^^^^^^
56+
= note: the trait `FooBar` defines an item `foo`, but is explicitely unimplemented
57+
58+
error: aborting due to 4 previous errors
59+
60+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)