1
Fork 0

When object unsafe trait uses itself in associated item suggest using Self

This commit is contained in:
Esteban Küber 2020-01-19 17:27:37 -08:00
parent 1c9242f83f
commit d72bcdb42c
3 changed files with 130 additions and 2 deletions

View file

@ -10,10 +10,10 @@ use rustc::ty::{
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir::def_id::DefId;
use rustc_hir::ItemKind;
use rustc_span::symbol::sym;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use syntax::ast;
@ -176,9 +176,74 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
hir::TraitItemKind::Method(ref sig, _) => Some(sig),
_ => None,
};
check_bare_self_trait_by_name(tcx, &trait_item);
check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig);
}
fn could_be_self(trait_name: Ident, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => {
let mut p = trait_ref.trait_ref.path.segments.iter().map(|s| s.ident);
match (p.next(), p.next()) {
(Some(ident), None) => ident == trait_name,
_ => false,
}
}
_ => false,
}
}
/// Detect when an object unsafe trait is referring to itself in one of its associated items.
/// When this is done, suggest using `Self` instead.
fn check_bare_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
let (trait_name, trait_def_id) = match tcx.hir().get(tcx.hir().get_parent_item(item.hir_id)) {
hir::Node::Item(item) => match item.kind {
hir::ItemKind::Trait(..) => (item.ident, tcx.hir().local_def_id(item.hir_id)),
_ => return,
},
_ => return,
};
let mut trait_should_be_self = vec![];
match &item.kind {
hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
if could_be_self(trait_name, ty) =>
{
trait_should_be_self.push(ty.span)
}
hir::TraitItemKind::Method(sig, _) => {
for ty in sig.decl.inputs {
if could_be_self(trait_name, ty) {
trait_should_be_self.push(ty.span);
}
}
match sig.decl.output {
hir::FunctionRetTy::Return(ty) if could_be_self(trait_name, ty) => {
trait_should_be_self.push(ty.span);
}
_ => {}
}
}
_ => {}
}
if !trait_should_be_self.is_empty() {
if rustc::traits::object_safety_violations(tcx, trait_def_id).is_empty() {
return;
}
let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();
let mut err = tcx.sess.struct_span_err(
trait_should_be_self,
"associated item referring to unboxed trait object for its own trait",
);
err.span_label(trait_name.span, "in this trait");
err.multipart_suggestion(
"you might have meant to use `Self` to refer to the materialized type",
sugg,
Applicability::MachineApplicable,
);
err.emit();
}
}
pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
let impl_item = tcx.hir().expect_impl_item(hir_id);

View file

@ -0,0 +1,16 @@
#![allow(bare_trait_objects)]
trait A: Sized {
fn f(a: A) -> A;
//~^ ERROR associated item referring to unboxed trait object for its own trait
//~| ERROR the trait `A` cannot be made into an object
}
trait B {
fn f(a: B) -> B;
//~^ ERROR associated item referring to unboxed trait object for its own trait
//~| ERROR the trait `B` cannot be made into an object
}
trait C {
fn f(&self, a: C) -> C;
}
fn main() {}

View file

@ -0,0 +1,47 @@
error: associated item referring to unboxed trait object for its own trait
--> $DIR/object-unsafe-trait-should-use-self.rs:3:13
|
LL | trait A: Sized {
| - in this trait
LL | fn f(a: A) -> A;
| ^ ^
|
help: you might have meant to use `Self` to refer to the materialized type
|
LL | fn f(a: Self) -> Self;
| ^^^^ ^^^^
error[E0038]: the trait `A` cannot be made into an object
--> $DIR/object-unsafe-trait-should-use-self.rs:3:13
|
LL | trait A: Sized {
| ----- the trait cannot require that `Self : Sized`
LL | fn f(a: A) -> A;
| ^ the trait `A` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
error: associated item referring to unboxed trait object for its own trait
--> $DIR/object-unsafe-trait-should-use-self.rs:8:13
|
LL | trait B {
| - in this trait
LL | fn f(a: B) -> B;
| ^ ^
|
help: you might have meant to use `Self` to refer to the materialized type
|
LL | fn f(a: Self) -> Self;
| ^^^^ ^^^^
error[E0038]: the trait `B` cannot be made into an object
--> $DIR/object-unsafe-trait-should-use-self.rs:8:13
|
LL | fn f(a: B) -> B;
| - ^ the trait `B` cannot be made into an object
| |
| associated function `f` has no `self` parameter
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0038`.