@@ -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,63 @@ 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.
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