From aa4f16a6e71fa63a366ffa0981a422eec73f66ca Mon Sep 17 00:00:00 2001 From: Brezak Date: Tue, 20 Aug 2024 20:53:49 +0200 Subject: [PATCH] Check that `#[pointee]` is applied only to generic arguments --- compiler/rustc_builtin_macros/messages.ftl | 2 + .../src/deriving/smart_ptr.rs | 64 +++++++++++++++++++ compiler/rustc_builtin_macros/src/errors.rs | 7 ++ .../ui/deriving/deriving-smart-pointer-neg.rs | 33 ++++++++++ .../deriving-smart-pointer-neg.stderr | 26 +++++++- 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 9695df9c87e8a..8bd07027da46f 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -235,6 +235,8 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive .label = declared `#[non_exhaustive]` here .help = consider a manual implementation of `Default` +builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters + builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants .help = consider a manual implementation of `Default` diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs index 78028df2aa0ee..fab1906eecd50 100644 --- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs +++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs @@ -13,6 +13,8 @@ use rustc_span::symbol::{Ident, sym}; use rustc_span::{Span, Symbol}; use thin_vec::{ThinVec, thin_vec}; +use crate::errors; + macro_rules! path { ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } } @@ -25,6 +27,8 @@ pub(crate) fn expand_deriving_smart_ptr( push: &mut dyn FnMut(Annotatable), _is_const: bool, ) { + item.visit_with(&mut DetectNonGenericPointeeAttr { cx }); + let (name_ident, generics) = if let Annotatable::Item(aitem) = item && let ItemKind::Struct(struct_data, g) = &aitem.kind { @@ -396,3 +400,63 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { } } } + +struct DetectNonGenericPointeeAttr<'a, 'b> { + cx: &'a ExtCtxt<'b>, +} + +impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> { + fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { + if attr.has_name(sym::pointee) { + self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + } + } + + fn visit_generic_param(&mut self, param: &'a rustc_ast::GenericParam) -> Self::Result { + let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx }; + + match ¶m.kind { + GenericParamKind::Type { default } => { + // The `default` may end up containing a block expression. + // The problem is block expressions may define structs with generics. + // A user may attach a #[pointee] attribute to one of these generics + // We want to catch that. The simple solution is to just + // always raise a `NonGenericPointee` error when this happens. + // + // This solution does reject valid rust programs but, + // such a code would have to, in order: + // - Define a smart pointer struct. + // - Somewhere in this struct definition use a type with a const generic argument. + // - Calculate this const generic in a expression block. + // - Define a new smart pointer type in this block. + // - Have this smart pointer type have more than 1 generic type. + // In this case, the inner smart pointer derive would be complaining that it + // needs a pointer attribute. Meanwhile, the outer macro would be complaining + // that we attached a #[pointee] to a generic type argument while helpfully + // informing the user that #[pointee] can only be attached to generic pointer arguments + rustc_ast::visit::visit_opt!(error_on_pointee, visit_ty, default); + } + + GenericParamKind::Const { .. } | GenericParamKind::Lifetime => { + rustc_ast::visit::walk_generic_param(&mut error_on_pointee, param); + } + } + } + + fn visit_ty(&mut self, t: &'a rustc_ast::Ty) -> Self::Result { + let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx }; + error_on_pointee.visit_ty(t) + } +} + +struct AlwaysErrorOnGenericParam<'a, 'b> { + cx: &'a ExtCtxt<'b>, +} + +impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> { + fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result { + if attr.has_name(sym::pointee) { + self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span }); + } + } +} diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 4fffffb91c8b4..625aa4f2b75de 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -937,3 +937,10 @@ pub(crate) struct NakedFunctionTestingAttribute { #[label] pub testing_span: Span, } + +#[derive(Diagnostic)] +#[diag(builtin_macros_non_generic_pointee)] +pub(crate) struct NonGenericPointee { + #[primary_span] + pub span: Span, +} diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.rs b/tests/ui/deriving/deriving-smart-pointer-neg.rs index f02fb56130fa7..41d3039236f71 100644 --- a/tests/ui/deriving/deriving-smart-pointer-neg.rs +++ b/tests/ui/deriving/deriving-smart-pointer-neg.rs @@ -53,6 +53,39 @@ struct NoMaybeSized<'a, #[pointee] T> { ptr: &'a T, } +#[derive(SmartPointer)] +#[repr(transparent)] +struct PointeeOnField<'a, #[pointee] T: ?Sized> { + #[pointee] + //~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters + ptr: &'a T +} + +#[derive(SmartPointer)] +#[repr(transparent)] +struct PointeeInTypeConstBlock<'a, T: ?Sized = [u32; const { struct UhOh<#[pointee] T>(T); 10 }]> { + //~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters + ptr: &'a T, +} + +#[derive(SmartPointer)] +#[repr(transparent)] +struct PointeeInConstConstBlock< + 'a, + T: ?Sized, + const V: u32 = { struct UhOh<#[pointee] T>(T); 10 }> + //~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters +{ + ptr: &'a T, +} + +#[derive(SmartPointer)] +#[repr(transparent)] +struct PointeeInAnotherTypeConstBlock<'a, #[pointee] T: ?Sized> { + ptr: PointeeInConstConstBlock<'a, T, { struct UhOh<#[pointee] T>(T); 0 }> + //~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters +} + // However, reordering attributes should work nevertheless. #[repr(transparent)] #[derive(SmartPointer)] diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.stderr b/tests/ui/deriving/deriving-smart-pointer-neg.stderr index e7c2afc8b00c7..9ab117698c7a0 100644 --- a/tests/ui/deriving/deriving-smart-pointer-neg.stderr +++ b/tests/ui/deriving/deriving-smart-pointer-neg.stderr @@ -58,6 +58,30 @@ error: `derive(SmartPointer)` requires T to be marked `?Sized` LL | struct NoMaybeSized<'a, #[pointee] T> { | ^ +error: the `#[pointee]` attribute may only be used on generic parameters + --> $DIR/deriving-smart-pointer-neg.rs:59:5 + | +LL | #[pointee] + | ^^^^^^^^^^ + +error: the `#[pointee]` attribute may only be used on generic parameters + --> $DIR/deriving-smart-pointer-neg.rs:66:74 + | +LL | struct PointeeInTypeConstBlock<'a, T: ?Sized = [u32; const { struct UhOh<#[pointee] T>(T); 10 }]> { + | ^^^^^^^^^^ + +error: the `#[pointee]` attribute may only be used on generic parameters + --> $DIR/deriving-smart-pointer-neg.rs:76:34 + | +LL | const V: u32 = { struct UhOh<#[pointee] T>(T); 10 }> + | ^^^^^^^^^^ + +error: the `#[pointee]` attribute may only be used on generic parameters + --> $DIR/deriving-smart-pointer-neg.rs:85:56 + | +LL | ptr: PointeeInConstConstBlock<'a, T, { struct UhOh<#[pointee] T>(T); 0 }> + | ^^^^^^^^^^ + error[E0392]: lifetime parameter `'a` is never used --> $DIR/deriving-smart-pointer-neg.rs:15:16 | @@ -90,6 +114,6 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 12 previous errors +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0392`.