diff --git a/compiler/rustc_error_codes/src/error_codes/E0390.md b/compiler/rustc_error_codes/src/error_codes/E0390.md index ecc5b5568ada1..7a13160d09858 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0390.md +++ b/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: @@ -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: ``` diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 483ab2f58f222..0c1578498b846 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -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, }; @@ -70,6 +70,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "bool", "bool", item.span, + assoc_items, ); } ty::Char => { @@ -80,6 +81,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "char", "char", item.span, + assoc_items, ); } ty::Str => { @@ -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 => { @@ -100,6 +103,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "slice_u8", "[u8]", item.span, + assoc_items, ); } ty::Slice(_) => { @@ -110,6 +114,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "slice", "[T]", item.span, + assoc_items, ); } ty::Array(_, _) => { @@ -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 }) @@ -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 }) @@ -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 }) => { @@ -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 }) => { @@ -164,6 +173,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "mut_ptr", "*mut T", item.span, + assoc_items, ); } ty::Int(ast::IntTy::I8) => { @@ -174,6 +184,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "i8", "i8", item.span, + assoc_items, ); } ty::Int(ast::IntTy::I16) => { @@ -184,6 +195,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "i16", "i16", item.span, + assoc_items, ); } ty::Int(ast::IntTy::I32) => { @@ -194,6 +206,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "i32", "i32", item.span, + assoc_items, ); } ty::Int(ast::IntTy::I64) => { @@ -204,6 +217,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "i64", "i64", item.span, + assoc_items, ); } ty::Int(ast::IntTy::I128) => { @@ -214,6 +228,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "i128", "i128", item.span, + assoc_items, ); } ty::Int(ast::IntTy::Isize) => { @@ -224,6 +239,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "isize", "isize", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::U8) => { @@ -234,6 +250,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "u8", "u8", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::U16) => { @@ -244,6 +261,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "u16", "u16", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::U32) => { @@ -254,6 +272,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "u32", "u32", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::U64) => { @@ -264,6 +283,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "u64", "u64", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::U128) => { @@ -274,6 +294,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "u128", "u128", item.span, + assoc_items, ); } ty::Uint(ast::UintTy::Usize) => { @@ -284,6 +305,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "usize", "usize", item.span, + assoc_items, ); } ty::Float(ast::FloatTy::F32) => { @@ -294,6 +316,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "f32", "f32", item.span, + assoc_items, ); } ty::Float(ast::FloatTy::F64) => { @@ -304,6 +327,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { "f64", "f64", item.span, + assoc_items, ); } ty::Error(_) => {} @@ -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() => { @@ -378,6 +403,32 @@ impl InherentCollect<'tcx> { // OK } _ => { + let to_implement = if assoc_items.len() == 0 { + String::new() + } else { + let plural = assoc_items.len() > 1; + let assoc_items_kind = { + 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" + } + }; + + format!( + " to implement {} {}{}", + if plural { "these" } else { "this" }, + assoc_items_kind, + if plural { "s" } else { "" } + ) + }; + struct_span_err!( self.tcx.sess, span, @@ -387,7 +438,7 @@ impl InherentCollect<'tcx> { lang, ty ) - .span_help(span, "consider using a trait to implement these methods") + .help(&format!("consider using a trait{}", to_implement)) .emit(); } } diff --git a/src/test/ui/error-codes/E0390.stderr b/src/test/ui/error-codes/E0390.stderr index 3ca3a77c74fb7..be47e93d19a9b 100644 --- a/src/test/ui/error-codes/E0390.stderr +++ b/src/test/ui/error-codes/E0390.stderr @@ -4,11 +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 - --> $DIR/E0390.rs:5:1 - | -LL | impl *mut Foo {} - | ^^^^^^^^^^^^^^^^ + = help: consider using a trait error: aborting due to previous error diff --git a/src/test/ui/kinds-of-primitive-impl.rs b/src/test/ui/kinds-of-primitive-impl.rs new file mode 100644 index 0000000000000..cbd4d7ae904fd --- /dev/null +++ b/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() {} diff --git a/src/test/ui/kinds-of-primitive-impl.stderr b/src/test/ui/kinds-of-primitive-impl.stderr new file mode 100644 index 0000000000000..d19c85b17f9e7 --- /dev/null +++ b/src/test/ui/kinds-of-primitive-impl.stderr @@ -0,0 +1,40 @@ +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 + +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 + +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 + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0390`. diff --git a/src/test/ui/single-primitive-inherent-impl.stderr b/src/test/ui/single-primitive-inherent-impl.stderr index d357afa3b3841..50a0d5bef86d3 100644 --- a/src/test/ui/single-primitive-inherent-impl.stderr +++ b/src/test/ui/single-primitive-inherent-impl.stderr @@ -6,13 +6,7 @@ LL | | LL | | } | |_^ | -help: consider using a trait to implement these methods - --> $DIR/single-primitive-inherent-impl.rs:11:1 - | -LL | / impl str { -LL | | -LL | | } - | |_^ + = help: consider using a trait error: aborting due to previous error