@@ -13,6 +13,8 @@ use rustc_span::symbol::{Ident, sym};
13
13
use rustc_span:: { Span , Symbol } ;
14
14
use thin_vec:: { ThinVec , thin_vec} ;
15
15
16
+ use crate :: errors;
17
+
16
18
macro_rules! path {
17
19
( $span: expr, $( $part: ident) ::* ) => { vec![ $( Ident :: new( sym:: $part, $span) , ) * ] }
18
20
}
@@ -25,6 +27,8 @@ pub(crate) fn expand_deriving_smart_ptr(
25
27
push : & mut dyn FnMut ( Annotatable ) ,
26
28
_is_const : bool ,
27
29
) {
30
+ item. visit_with ( & mut DetectNonGenericPointeeAttr { cx } ) ;
31
+
28
32
let ( name_ident, generics) = if let Annotatable :: Item ( aitem) = item
29
33
&& let ItemKind :: Struct ( struct_data, g) = & aitem. kind
30
34
{
@@ -396,3 +400,61 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
396
400
}
397
401
}
398
402
}
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. The problem is block expressions
421
+ // may define structs with generics. A user may attach a #[pointee] attribute to one of these
422
+ // generics and we want to catch that. The simple solution is to just always raise a `NonGenericPointee`
423
+ // error in these cases.
424
+ //
425
+ // We do actually end up rejecting valid rust programs while handling this edge case.
426
+ // Note that such a program would have to, in order:
427
+ // - Define a smart pointer struct.
428
+ // - Somewhere in this struct definition reference a type with a const generic argument.
429
+ // - Calculate this const generic in a expression block.
430
+ // - Define a new smart pointer type in this block.
431
+ // - Have this smart pointer type have more than 1 generic type.
432
+ // In this case, the inner smart pointer derive would be complaining that it needs a pointer attribute.
433
+ // Meanwhile, the outer macro would be complaining about a #[pointee] attribute attached to a generic type argument,
434
+ // while also complaining that a #[pointee] attribute can only be attached to generic type arguments.
435
+ rustc_ast:: visit:: visit_opt!( error_on_pointee, visit_ty, default ) ;
436
+ }
437
+
438
+ GenericParamKind :: Const { .. } | GenericParamKind :: Lifetime => {
439
+ rustc_ast:: visit:: walk_generic_param ( & mut error_on_pointee, param) ;
440
+ }
441
+ }
442
+ }
443
+
444
+ fn visit_ty ( & mut self , t : & ' a rustc_ast:: Ty ) -> Self :: Result {
445
+ let mut error_on_pointee = AlwaysErrorOnGenericParam { cx : self . cx } ;
446
+ error_on_pointee. visit_ty ( t)
447
+ }
448
+ }
449
+
450
+ struct AlwaysErrorOnGenericParam < ' a , ' b > {
451
+ cx : & ' a ExtCtxt < ' b > ,
452
+ }
453
+
454
+ impl < ' a , ' b > rustc_ast:: visit:: Visitor < ' a > for AlwaysErrorOnGenericParam < ' a , ' b > {
455
+ fn visit_attribute ( & mut self , attr : & ' a rustc_ast:: Attribute ) -> Self :: Result {
456
+ if attr. has_name ( sym:: pointee) {
457
+ self . cx . dcx ( ) . emit_err ( errors:: NonGenericPointee { span : attr. span } ) ;
458
+ }
459
+ }
460
+ }
0 commit comments