@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
3
3
use std:: ops:: { Deref , DerefMut } ;
4
4
use std:: sync:: LazyLock ;
5
5
6
+ use itertools:: Itertools ;
6
7
use private:: Sealed ;
7
8
use rustc_ast:: { self as ast, LitKind , MetaItemLit , NodeId } ;
8
9
use rustc_errors:: { DiagCtxtHandle , Diagnostic } ;
@@ -63,8 +64,11 @@ use crate::attributes::traits::{
63
64
} ;
64
65
use crate :: attributes:: transparency:: TransparencyParser ;
65
66
use crate :: attributes:: { AttributeParser as _, Combine , Single , WithoutArgs } ;
67
+ use crate :: context:: MaybeWarn :: { Allow , Error , Warn } ;
66
68
use crate :: parser:: { ArgParser , MetaItemParser , PathParser } ;
67
- use crate :: session_diagnostics:: { AttributeParseError , AttributeParseErrorReason , UnknownMetaItem } ;
69
+ use crate :: session_diagnostics:: {
70
+ AttributeParseError , AttributeParseErrorReason , InvalidTarget , UnknownMetaItem ,
71
+ } ;
68
72
69
73
type GroupType < S > = LazyLock < GroupTypeInner < S > > ;
70
74
@@ -76,6 +80,7 @@ struct GroupTypeInner<S: Stage> {
76
80
struct GroupTypeInnerAccept < S : Stage > {
77
81
template : AttributeTemplate ,
78
82
accept_fn : AcceptFn < S > ,
83
+ allowed_targets : AllowedTargets ,
79
84
}
80
85
81
86
type AcceptFn < S > =
@@ -123,7 +128,8 @@ macro_rules! attribute_parsers {
123
128
STATE_OBJECT . with_borrow_mut( |s| {
124
129
accept_fn( s, cx, args)
125
130
} )
126
- } )
131
+ } ) ,
132
+ allowed_targets: <$names as crate :: attributes:: AttributeParser <$stage>>:: ALLOWED_TARGETS ,
127
133
} ) ;
128
134
}
129
135
@@ -645,6 +651,64 @@ impl ShouldEmit {
645
651
}
646
652
}
647
653
654
+ #[ derive( Debug ) ]
655
+ pub ( crate ) enum AllowedTargets {
656
+ AllowList ( & ' static [ MaybeWarn ] ) ,
657
+ AllowListWarnRest ( & ' static [ MaybeWarn ] ) ,
658
+ }
659
+
660
+ pub ( crate ) enum AllowedResult {
661
+ Allowed ,
662
+ Warn ,
663
+ Error ,
664
+ }
665
+
666
+ impl AllowedTargets {
667
+ pub ( crate ) fn is_allowed ( & self , target : Target ) -> AllowedResult {
668
+ match self {
669
+ AllowedTargets :: AllowList ( list) => {
670
+ if list. contains ( & Allow ( target) ) {
671
+ AllowedResult :: Allowed
672
+ } else if list. contains ( & Warn ( target) ) {
673
+ AllowedResult :: Warn
674
+ } else {
675
+ AllowedResult :: Error
676
+ }
677
+ }
678
+ AllowedTargets :: AllowListWarnRest ( list) => {
679
+ if list. contains ( & Allow ( target) ) {
680
+ AllowedResult :: Allowed
681
+ } else if list. contains ( & Error ( target) ) {
682
+ AllowedResult :: Error
683
+ } else {
684
+ AllowedResult :: Warn
685
+ }
686
+ }
687
+ }
688
+ }
689
+
690
+ pub ( crate ) fn allowed_targets ( & self ) -> Vec < Target > {
691
+ match self {
692
+ AllowedTargets :: AllowList ( list) => list,
693
+ AllowedTargets :: AllowListWarnRest ( list) => list,
694
+ }
695
+ . iter ( )
696
+ . filter_map ( |target| match target {
697
+ Allow ( target) => Some ( * target) ,
698
+ Warn ( _) => None ,
699
+ Error ( _) => None ,
700
+ } )
701
+ . collect ( )
702
+ }
703
+ }
704
+
705
+ #[ derive( Debug , Eq , PartialEq ) ]
706
+ pub ( crate ) enum MaybeWarn {
707
+ Allow ( Target ) ,
708
+ Warn ( Target ) ,
709
+ Error ( Target ) ,
710
+ }
711
+
648
712
/// Context created once, for example as part of the ast lowering
649
713
/// context, through which all attributes can be lowered.
650
714
pub struct AttributeParser < ' sess , S : Stage = Late > {
@@ -852,7 +916,48 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
852
916
attr_path : path. get_attribute_path ( ) ,
853
917
} ;
854
918
855
- ( accept. accept_fn ) ( & mut cx, args)
919
+ ( accept. accept_fn ) ( & mut cx, args) ;
920
+
921
+ if self . stage . should_emit ( ) . should_emit ( ) {
922
+ match accept. allowed_targets . is_allowed ( target) {
923
+ AllowedResult :: Allowed => { }
924
+ AllowedResult :: Warn => {
925
+ let allowed_targets =
926
+ accept. allowed_targets . allowed_targets ( ) ;
927
+ let ( applied, only) = allowed_targets_applied (
928
+ allowed_targets,
929
+ target,
930
+ self . features ,
931
+ ) ;
932
+ emit_lint ( AttributeLint {
933
+ id : target_id,
934
+ span : attr. span ,
935
+ kind : AttributeLintKind :: InvalidTarget {
936
+ name : parts[ 0 ] ,
937
+ target,
938
+ only : if only { "only " } else { "" } ,
939
+ applied,
940
+ } ,
941
+ } ) ;
942
+ }
943
+ AllowedResult :: Error => {
944
+ let allowed_targets =
945
+ accept. allowed_targets . allowed_targets ( ) ;
946
+ let ( applied, only) = allowed_targets_applied (
947
+ allowed_targets,
948
+ target,
949
+ self . features ,
950
+ ) ;
951
+ self . dcx ( ) . emit_err ( InvalidTarget {
952
+ span : attr. span ,
953
+ name : parts[ 0 ] ,
954
+ target : target. plural_name ( ) ,
955
+ only : if only { "only " } else { "" } ,
956
+ applied,
957
+ } ) ;
958
+ }
959
+ }
960
+ }
856
961
}
857
962
} else {
858
963
// If we're here, we must be compiling a tool attribute... Or someone
@@ -940,6 +1045,132 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
940
1045
}
941
1046
}
942
1047
1048
+ /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
1049
+ /// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
1050
+ pub ( crate ) fn allowed_targets_applied (
1051
+ mut allowed_targets : Vec < Target > ,
1052
+ target : Target ,
1053
+ features : Option < & Features > ,
1054
+ ) -> ( String , bool ) {
1055
+ // Remove unstable targets from `allowed_targets` if their features are not enabled
1056
+ if let Some ( features) = features {
1057
+ if !features. fn_delegation ( ) {
1058
+ allowed_targets. retain ( |t| !matches ! ( t, Target :: Delegation { .. } ) ) ;
1059
+ }
1060
+ if !features. stmt_expr_attributes ( ) {
1061
+ allowed_targets. retain ( |t| !matches ! ( t, Target :: Expression | Target :: Statement ) ) ;
1062
+ }
1063
+ }
1064
+
1065
+ // We define groups of "similar" targets.
1066
+ // If at least two of the targets are allowed, and the `target` is not in the group,
1067
+ // we collapse the entire group to a single entry to simplify the target list
1068
+ const FUNCTION_LIKE : & [ Target ] = & [
1069
+ Target :: Fn ,
1070
+ Target :: Closure ,
1071
+ Target :: ForeignFn ,
1072
+ Target :: Method ( MethodKind :: Inherent ) ,
1073
+ Target :: Method ( MethodKind :: Trait { body : false } ) ,
1074
+ Target :: Method ( MethodKind :: Trait { body : true } ) ,
1075
+ Target :: Method ( MethodKind :: TraitImpl ) ,
1076
+ ] ;
1077
+ const METHOD_LIKE : & [ Target ] = & [
1078
+ Target :: Method ( MethodKind :: Inherent ) ,
1079
+ Target :: Method ( MethodKind :: Trait { body : false } ) ,
1080
+ Target :: Method ( MethodKind :: Trait { body : true } ) ,
1081
+ Target :: Method ( MethodKind :: TraitImpl ) ,
1082
+ ] ;
1083
+ const IMPL_LIKE : & [ Target ] =
1084
+ & [ Target :: Impl { of_trait : false } , Target :: Impl { of_trait : true } ] ;
1085
+ const ADT_LIKE : & [ Target ] = & [ Target :: Struct , Target :: Enum ] ;
1086
+
1087
+ let mut added_fake_targets = Vec :: new ( ) ;
1088
+ filter_targets (
1089
+ & mut allowed_targets,
1090
+ FUNCTION_LIKE ,
1091
+ "functions" ,
1092
+ target,
1093
+ & mut added_fake_targets,
1094
+ ) ;
1095
+ filter_targets ( & mut allowed_targets, METHOD_LIKE , "methods" , target, & mut added_fake_targets) ;
1096
+ filter_targets ( & mut allowed_targets, IMPL_LIKE , "impl blocks" , target, & mut added_fake_targets) ;
1097
+ filter_targets ( & mut allowed_targets, ADT_LIKE , "data types" , target, & mut added_fake_targets) ;
1098
+
1099
+ // If there is now only 1 target left, show that as the only possible target
1100
+ (
1101
+ added_fake_targets
1102
+ . iter ( )
1103
+ . copied ( )
1104
+ . chain ( allowed_targets. iter ( ) . map ( |t| t. plural_name ( ) ) )
1105
+ . join ( ", " ) ,
1106
+ allowed_targets. len ( ) + added_fake_targets. len ( ) == 1 ,
1107
+ )
1108
+ }
1109
+
1110
+ fn filter_targets (
1111
+ allowed_targets : & mut Vec < Target > ,
1112
+ target_group : & ' static [ Target ] ,
1113
+ target_group_name : & ' static str ,
1114
+ target : Target ,
1115
+ added_fake_targets : & mut Vec < & ' static str > ,
1116
+ ) {
1117
+ if target_group. contains ( & target) {
1118
+ return ;
1119
+ }
1120
+ if allowed_targets. iter ( ) . filter ( |at| target_group. contains ( at) ) . count ( ) < 2 {
1121
+ return ;
1122
+ }
1123
+ allowed_targets. retain ( |t| !target_group. contains ( t) ) ;
1124
+ added_fake_targets. push ( target_group_name) ;
1125
+ }
1126
+
1127
+ /// This is the list of all targets to which a attribute can be applied
1128
+ /// This is used for:
1129
+ /// - `rustc_dummy`, which can be applied to all targets
1130
+ /// - Attributes that are not parted to the new target system yet can use this list as a placeholder
1131
+ pub ( crate ) const ALL_TARGETS : & ' static [ MaybeWarn ] = & [
1132
+ Allow ( Target :: ExternCrate ) ,
1133
+ Allow ( Target :: Use ) ,
1134
+ Allow ( Target :: Static ) ,
1135
+ Allow ( Target :: Const ) ,
1136
+ Allow ( Target :: Fn ) ,
1137
+ Allow ( Target :: Closure ) ,
1138
+ Allow ( Target :: Mod ) ,
1139
+ Allow ( Target :: ForeignMod ) ,
1140
+ Allow ( Target :: GlobalAsm ) ,
1141
+ Allow ( Target :: TyAlias ) ,
1142
+ Allow ( Target :: Enum ) ,
1143
+ Allow ( Target :: Variant ) ,
1144
+ Allow ( Target :: Struct ) ,
1145
+ Allow ( Target :: Field ) ,
1146
+ Allow ( Target :: Union ) ,
1147
+ Allow ( Target :: Trait ) ,
1148
+ Allow ( Target :: TraitAlias ) ,
1149
+ Allow ( Target :: Impl { of_trait : false } ) ,
1150
+ Allow ( Target :: Impl { of_trait : true } ) ,
1151
+ Allow ( Target :: Expression ) ,
1152
+ Allow ( Target :: Statement ) ,
1153
+ Allow ( Target :: Arm ) ,
1154
+ Allow ( Target :: AssocConst ) ,
1155
+ Allow ( Target :: Method ( MethodKind :: Inherent ) ) ,
1156
+ Allow ( Target :: Method ( MethodKind :: Trait { body : false } ) ) ,
1157
+ Allow ( Target :: Method ( MethodKind :: Trait { body : true } ) ) ,
1158
+ Allow ( Target :: Method ( MethodKind :: TraitImpl ) ) ,
1159
+ Allow ( Target :: AssocTy ) ,
1160
+ Allow ( Target :: ForeignFn ) ,
1161
+ Allow ( Target :: ForeignStatic ) ,
1162
+ Allow ( Target :: ForeignTy ) ,
1163
+ Allow ( Target :: MacroDef ) ,
1164
+ Allow ( Target :: Param ) ,
1165
+ Allow ( Target :: PatField ) ,
1166
+ Allow ( Target :: ExprField ) ,
1167
+ Allow ( Target :: WherePredicate ) ,
1168
+ Allow ( Target :: MacroCall ) ,
1169
+ Allow ( Target :: Crate ) ,
1170
+ Allow ( Target :: Delegation { mac : false } ) ,
1171
+ Allow ( Target :: Delegation { mac : true } ) ,
1172
+ ] ;
1173
+
943
1174
/// Parse a single integer.
944
1175
///
945
1176
/// Used by attributes that take a single integer as argument, such as
0 commit comments