When object unsafe trait uses itself in associated item suggest using Self
This commit is contained in:
parent
1c9242f83f
commit
d72bcdb42c
3 changed files with 130 additions and 2 deletions
|
@ -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);
|
||||
|
|
|
@ -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() {}
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue