about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros')
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs64
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs7
3 files changed, 73 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index b25892242b5..77cb8dc63c4 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 78028df2aa0..fab1906eecd 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 &param.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 f13ca224a45..639c2aa231c 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -940,3 +940,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,
+}