@@ -13,6 +13,8 @@ use rustc_span::symbol::{Ident, sym};
1313use rustc_span:: { Span , Symbol } ;
1414use thin_vec:: { ThinVec , thin_vec} ;
1515
16+ use crate :: errors;
17+
1618macro_rules! path {
1719 ( $span: expr, $( $part: ident) ::* ) => { vec![ $( Ident :: new( sym:: $part, $span) , ) * ] }
1820}
@@ -25,6 +27,8 @@ pub(crate) fn expand_deriving_smart_ptr(
2527 push : & mut dyn FnMut ( Annotatable ) ,
2628 _is_const : bool ,
2729) {
30+ item. visit_with ( & mut DetectNonGenericPointeeAttr { cx } ) ;
31+
2832 let ( name_ident, generics) = if let Annotatable :: Item ( aitem) = item
2933 && let ItemKind :: Struct ( struct_data, g) = & aitem. kind
3034 {
@@ -396,3 +400,63 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
396400 }
397401 }
398402}
403+
404+ struct DetectNonGenericPointeeAttr < ' a , ' b > {
405+ cx : & ' a ExtCtxt < ' b > ,
406+ }
407+
408+ impl < ' a , ' b > rustc_ast:: visit:: Visitor < ' a > for DetectNonGenericPointeeAttr < ' a , ' b > {
409+ fn visit_attribute ( & mut self , attr : & ' a rustc_ast:: Attribute ) -> Self :: Result {
410+ if attr. has_name ( sym:: pointee) {
411+ self . cx . dcx ( ) . emit_err ( errors:: NonGenericPointee { span : attr. span } ) ;
412+ }
413+ }
414+
415+ fn visit_generic_param ( & mut self , param : & ' a rustc_ast:: GenericParam ) -> Self :: Result {
416+ let mut error_on_pointee = AlwaysErrorOnGenericParam { cx : self . cx } ;
417+
418+ match & param. kind {
419+ GenericParamKind :: Type { default } => {
420+ // The `default` may end up containing a block expression.
421+ // The problem is block expressions may define structs with generics.
422+ // A user may attach a #[pointee] attribute to one of these generics
423+ // We want to catch that. The simple solution is to just
424+ // always raise a `NonGenericPointee` error when this happens.
425+ //
426+ // This solution does reject valid rust programs but,
427+ // such a code would have to, in order:
428+ // - Define a smart pointer struct.
429+ // - Somewhere in this struct definition use a type with a const generic argument.
430+ // - Calculate this const generic in a expression block.
431+ // - Define a new smart pointer type in this block.
432+ // - Have this smart pointer type have more than 1 generic type.
433+ // In this case, the inner smart pointer derive would be complaining that it
434+ // needs a pointer attribute. Meanwhile, the outer macro would be complaining
435+ // that we attached a #[pointee] to a generic type argument while helpfully
436+ // informing the user that #[pointee] can only be attached to generic pointer arguments
437+ rustc_ast:: visit:: visit_opt!( error_on_pointee, visit_ty, default ) ;
438+ }
439+
440+ GenericParamKind :: Const { .. } | GenericParamKind :: Lifetime => {
441+ rustc_ast:: visit:: walk_generic_param ( & mut error_on_pointee, param) ;
442+ }
443+ }
444+ }
445+
446+ fn visit_ty ( & mut self , t : & ' a rustc_ast:: Ty ) -> Self :: Result {
447+ let mut error_on_pointee = AlwaysErrorOnGenericParam { cx : self . cx } ;
448+ error_on_pointee. visit_ty ( t)
449+ }
450+ }
451+
452+ struct AlwaysErrorOnGenericParam < ' a , ' b > {
453+ cx : & ' a ExtCtxt < ' b > ,
454+ }
455+
456+ impl < ' a , ' b > rustc_ast:: visit:: Visitor < ' a > for AlwaysErrorOnGenericParam < ' a , ' b > {
457+ fn visit_attribute ( & mut self , attr : & ' a rustc_ast:: Attribute ) -> Self :: Result {
458+ if attr. has_name ( sym:: pointee) {
459+ self . cx . dcx ( ) . emit_err ( errors:: NonGenericPointee { span : attr. span } ) ;
460+ }
461+ }
462+ }
0 commit comments