Skip to content

Commit d72bcdb

Browse files
committed
When object unsafe trait uses itself in associated item suggest using Self
1 parent 1c9242f commit d72bcdb

File tree

3 files changed

+130
-2
lines changed

3 files changed

+130
-2
lines changed

src/librustc_typeck/check/wfcheck.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use rustc::ty::{
1010
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
1111
};
1212
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
13-
use rustc_errors::{struct_span_err, DiagnosticBuilder};
13+
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
1414
use rustc_hir::def_id::DefId;
1515
use rustc_hir::ItemKind;
16-
use rustc_span::symbol::sym;
16+
use rustc_span::symbol::{sym, Ident};
1717
use rustc_span::Span;
1818
use syntax::ast;
1919

@@ -176,9 +176,74 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
176176
hir::TraitItemKind::Method(ref sig, _) => Some(sig),
177177
_ => None,
178178
};
179+
check_bare_self_trait_by_name(tcx, &trait_item);
179180
check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig);
180181
}
181182

183+
fn could_be_self(trait_name: Ident, ty: &hir::Ty<'_>) -> bool {
184+
match ty.kind {
185+
hir::TyKind::TraitObject([trait_ref], ..) => {
186+
let mut p = trait_ref.trait_ref.path.segments.iter().map(|s| s.ident);
187+
match (p.next(), p.next()) {
188+
(Some(ident), None) => ident == trait_name,
189+
_ => false,
190+
}
191+
}
192+
_ => false,
193+
}
194+
}
195+
196+
/// Detect when an object unsafe trait is referring to itself in one of its associated items.
197+
/// When this is done, suggest using `Self` instead.
198+
fn check_bare_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
199+
let (trait_name, trait_def_id) = match tcx.hir().get(tcx.hir().get_parent_item(item.hir_id)) {
200+
hir::Node::Item(item) => match item.kind {
201+
hir::ItemKind::Trait(..) => (item.ident, tcx.hir().local_def_id(item.hir_id)),
202+
_ => return,
203+
},
204+
_ => return,
205+
};
206+
let mut trait_should_be_self = vec![];
207+
match &item.kind {
208+
hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
209+
if could_be_self(trait_name, ty) =>
210+
{
211+
trait_should_be_self.push(ty.span)
212+
}
213+
hir::TraitItemKind::Method(sig, _) => {
214+
for ty in sig.decl.inputs {
215+
if could_be_self(trait_name, ty) {
216+
trait_should_be_self.push(ty.span);
217+
}
218+
}
219+
match sig.decl.output {
220+
hir::FunctionRetTy::Return(ty) if could_be_self(trait_name, ty) => {
221+
trait_should_be_self.push(ty.span);
222+
}
223+
_ => {}
224+
}
225+
}
226+
_ => {}
227+
}
228+
if !trait_should_be_self.is_empty() {
229+
if rustc::traits::object_safety_violations(tcx, trait_def_id).is_empty() {
230+
return;
231+
}
232+
let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();
233+
let mut err = tcx.sess.struct_span_err(
234+
trait_should_be_self,
235+
"associated item referring to unboxed trait object for its own trait",
236+
);
237+
err.span_label(trait_name.span, "in this trait");
238+
err.multipart_suggestion(
239+
"you might have meant to use `Self` to refer to the materialized type",
240+
sugg,
241+
Applicability::MachineApplicable,
242+
);
243+
err.emit();
244+
}
245+
}
246+
182247
pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) {
183248
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
184249
let impl_item = tcx.hir().expect_impl_item(hir_id);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![allow(bare_trait_objects)]
2+
trait A: Sized {
3+
fn f(a: A) -> A;
4+
//~^ ERROR associated item referring to unboxed trait object for its own trait
5+
//~| ERROR the trait `A` cannot be made into an object
6+
}
7+
trait B {
8+
fn f(a: B) -> B;
9+
//~^ ERROR associated item referring to unboxed trait object for its own trait
10+
//~| ERROR the trait `B` cannot be made into an object
11+
}
12+
trait C {
13+
fn f(&self, a: C) -> C;
14+
}
15+
16+
fn main() {}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error: associated item referring to unboxed trait object for its own trait
2+
--> $DIR/object-unsafe-trait-should-use-self.rs:3:13
3+
|
4+
LL | trait A: Sized {
5+
| - in this trait
6+
LL | fn f(a: A) -> A;
7+
| ^ ^
8+
|
9+
help: you might have meant to use `Self` to refer to the materialized type
10+
|
11+
LL | fn f(a: Self) -> Self;
12+
| ^^^^ ^^^^
13+
14+
error[E0038]: the trait `A` cannot be made into an object
15+
--> $DIR/object-unsafe-trait-should-use-self.rs:3:13
16+
|
17+
LL | trait A: Sized {
18+
| ----- the trait cannot require that `Self : Sized`
19+
LL | fn f(a: A) -> A;
20+
| ^ the trait `A` cannot be made into an object
21+
|
22+
= note: the trait cannot require that `Self : Sized`
23+
24+
error: associated item referring to unboxed trait object for its own trait
25+
--> $DIR/object-unsafe-trait-should-use-self.rs:8:13
26+
|
27+
LL | trait B {
28+
| - in this trait
29+
LL | fn f(a: B) -> B;
30+
| ^ ^
31+
|
32+
help: you might have meant to use `Self` to refer to the materialized type
33+
|
34+
LL | fn f(a: Self) -> Self;
35+
| ^^^^ ^^^^
36+
37+
error[E0038]: the trait `B` cannot be made into an object
38+
--> $DIR/object-unsafe-trait-should-use-self.rs:8:13
39+
|
40+
LL | fn f(a: B) -> B;
41+
| - ^ the trait `B` cannot be made into an object
42+
| |
43+
| associated function `f` has no `self` parameter
44+
45+
error: aborting due to 4 previous errors
46+
47+
For more information about this error, try `rustc --explain E0038`.

0 commit comments

Comments
 (0)