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