Skip to content

Commit

Permalink
smarter E0390
Browse files Browse the repository at this point in the history
  • Loading branch information
eopb committed Dec 6, 2020
1 parent 5957f20 commit 0c13a9c
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 7 deletions.
5 changes: 3 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0390.md
@@ -1,4 +1,4 @@
A method was implemented on a primitive type.
A method or constant was implemented on a primitive type.

Erroneous code example:

Expand All @@ -12,7 +12,8 @@ impl *mut Foo {}
// `#[lang = "mut_ptr"]` is allowed for the `*mut T` primitive
```

This isn't allowed, but using a trait to implement a method is a good solution.
This isn't allowed, but using a trait to implement a method or constant
is a good solution.
Example:

```
Expand Down
58 changes: 55 additions & 3 deletions compiler/rustc_typeck/src/coherence/inherent_impls.rs
Expand Up @@ -44,8 +44,8 @@ struct InherentCollect<'tcx> {

impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
fn visit_item(&mut self, item: &hir::Item<'_>) {
let ty = match item.kind {
hir::ItemKind::Impl { of_trait: None, ref self_ty, .. } => self_ty,
let (ty, assoc_items) = match item.kind {
hir::ItemKind::Impl { of_trait: None, ref self_ty, items, .. } => (self_ty, items),
_ => return,
};

Expand All @@ -70,6 +70,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"bool",
"bool",
item.span,
assoc_items,
);
}
ty::Char => {
Expand All @@ -80,6 +81,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"char",
"char",
item.span,
assoc_items,
);
}
ty::Str => {
Expand All @@ -90,6 +92,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"str",
"str",
item.span,
assoc_items,
);
}
ty::Slice(slice_item) if slice_item == self.tcx.types.u8 => {
Expand All @@ -100,6 +103,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"slice_u8",
"[u8]",
item.span,
assoc_items,
);
}
ty::Slice(_) => {
Expand All @@ -110,6 +114,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"slice",
"[T]",
item.span,
assoc_items,
);
}
ty::Array(_, _) => {
Expand All @@ -120,6 +125,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"array",
"[T; N]",
item.span,
assoc_items,
);
}
ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not })
Expand All @@ -132,6 +138,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"const_slice_ptr",
"*const [T]",
item.span,
assoc_items,
);
}
ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Mut })
Expand All @@ -144,6 +151,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"mut_slice_ptr",
"*mut [T]",
item.span,
assoc_items,
);
}
ty::RawPtr(ty::TypeAndMut { ty: _, mutbl: hir::Mutability::Not }) => {
Expand All @@ -154,6 +162,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"const_ptr",
"*const T",
item.span,
assoc_items,
);
}
ty::RawPtr(ty::TypeAndMut { ty: _, mutbl: hir::Mutability::Mut }) => {
Expand All @@ -164,6 +173,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"mut_ptr",
"*mut T",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::I8) => {
Expand All @@ -174,6 +184,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"i8",
"i8",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::I16) => {
Expand All @@ -184,6 +195,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"i16",
"i16",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::I32) => {
Expand All @@ -194,6 +206,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"i32",
"i32",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::I64) => {
Expand All @@ -204,6 +217,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"i64",
"i64",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::I128) => {
Expand All @@ -214,6 +228,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"i128",
"i128",
item.span,
assoc_items,
);
}
ty::Int(ast::IntTy::Isize) => {
Expand All @@ -224,6 +239,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"isize",
"isize",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::U8) => {
Expand All @@ -234,6 +250,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"u8",
"u8",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::U16) => {
Expand All @@ -244,6 +261,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"u16",
"u16",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::U32) => {
Expand All @@ -254,6 +272,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"u32",
"u32",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::U64) => {
Expand All @@ -264,6 +283,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"u64",
"u64",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::U128) => {
Expand All @@ -274,6 +294,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"u128",
"u128",
item.span,
assoc_items,
);
}
ty::Uint(ast::UintTy::Usize) => {
Expand All @@ -284,6 +305,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"usize",
"usize",
item.span,
assoc_items,
);
}
ty::Float(ast::FloatTy::F32) => {
Expand All @@ -294,6 +316,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"f32",
"f32",
item.span,
assoc_items,
);
}
ty::Float(ast::FloatTy::F64) => {
Expand All @@ -304,6 +327,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
"f64",
"f64",
item.span,
assoc_items,
);
}
ty::Error(_) => {}
Expand Down Expand Up @@ -369,6 +393,7 @@ impl InherentCollect<'tcx> {
lang: &str,
ty: &str,
span: Span,
assoc_items: &[hir::ImplItemRef<'_>],
) {
match (lang_def_id, lang_def_id2) {
(Some(lang_def_id), _) if lang_def_id == impl_def_id.to_def_id() => {
Expand All @@ -387,7 +412,34 @@ impl InherentCollect<'tcx> {
lang,
ty
)
.span_help(span, "consider using a trait to implement these methods")
.span_help(
span,
&format!("consider using a trait{}", {
if assoc_items.len() == 0 {
String::new()
} else {
let plural = assoc_items.len() > 1;
format!(
" to implement {} {}{}",
if plural { "these" } else { "this" },
{
let item_types = assoc_items.iter().map(|x| x.kind);
if item_types.clone().all(|x| x == hir::AssocItemKind::Const) {
"constant"
} else if item_types
.clone()
.all(|x| matches! {x, hir::AssocItemKind::Fn{ .. } })
{
"method"
} else {
"associated item"
}
},
if plural { "s" } else { "" }
)
}
}),
)
.emit();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/error-codes/E0390.stderr
Expand Up @@ -4,7 +4,7 @@ error[E0390]: only a single inherent implementation marked with `#[lang = "mut_p
LL | impl *mut Foo {}
| ^^^^^^^^^^^^^^^^
|
help: consider using a trait to implement these methods
help: consider using a trait
--> $DIR/E0390.rs:5:1
|
LL | impl *mut Foo {}
Expand Down
23 changes: 23 additions & 0 deletions src/test/ui/kinds-of-primitive-impl.rs
@@ -0,0 +1,23 @@
// ignore-tidy-linelength


impl u8 {
//~^ error: only a single inherent implementation marked with `#[lang = "u8"]` is allowed for the `u8` primitive
pub const B: u8 = 0;
}

impl str {
//~^ error: only a single inherent implementation marked with `#[lang = "str"]` is allowed for the `str` primitive
fn foo() {}
fn bar(self) {}
}

impl char {
//~^ error: only a single inherent implementation marked with `#[lang = "char"]` is allowed for the `char` primitive
pub const B: u8 = 0;
pub const C: u8 = 0;
fn foo() {}
fn bar(self) {}
}

fn main() {}
65 changes: 65 additions & 0 deletions src/test/ui/kinds-of-primitive-impl.stderr
@@ -0,0 +1,65 @@
error[E0390]: only a single inherent implementation marked with `#[lang = "u8"]` is allowed for the `u8` primitive
--> $DIR/kinds-of-primitive-impl.rs:4:1
|
LL | / impl u8 {
LL | |
LL | | pub const B: u8 = 0;
LL | | }
| |_^
|
help: consider using a trait to implement this constant
--> $DIR/kinds-of-primitive-impl.rs:4:1
|
LL | / impl u8 {
LL | |
LL | | pub const B: u8 = 0;
LL | | }
| |_^

error[E0390]: only a single inherent implementation marked with `#[lang = "str"]` is allowed for the `str` primitive
--> $DIR/kinds-of-primitive-impl.rs:9:1
|
LL | / impl str {
LL | |
LL | | fn foo() {}
LL | | fn bar(self) {}
LL | | }
| |_^
|
help: consider using a trait to implement these methods
--> $DIR/kinds-of-primitive-impl.rs:9:1
|
LL | / impl str {
LL | |
LL | | fn foo() {}
LL | | fn bar(self) {}
LL | | }
| |_^

error[E0390]: only a single inherent implementation marked with `#[lang = "char"]` is allowed for the `char` primitive
--> $DIR/kinds-of-primitive-impl.rs:15:1
|
LL | / impl char {
LL | |
LL | | pub const B: u8 = 0;
LL | | pub const C: u8 = 0;
LL | | fn foo() {}
LL | | fn bar(self) {}
LL | | }
| |_^
|
help: consider using a trait to implement these associated items
--> $DIR/kinds-of-primitive-impl.rs:15:1
|
LL | / impl char {
LL | |
LL | | pub const B: u8 = 0;
LL | | pub const C: u8 = 0;
LL | | fn foo() {}
LL | | fn bar(self) {}
LL | | }
| |_^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0390`.
2 changes: 1 addition & 1 deletion src/test/ui/single-primitive-inherent-impl.stderr
Expand Up @@ -6,7 +6,7 @@ LL | |
LL | | }
| |_^
|
help: consider using a trait to implement these methods
help: consider using a trait
--> $DIR/single-primitive-inherent-impl.rs:11:1
|
LL | / impl str {
Expand Down

0 comments on commit 0c13a9c

Please sign in to comment.