1use std::borrow::Cow;
2
3use rustc_errors::DiagArgValue;
4use rustc_feature::Features;
5use rustc_hir::lints::{AttributeLint, AttributeLintKind};
6use rustc_hir::{AttrPath, MethodKind, Target};
7use rustc_span::Span;
8
9use crate::AttributeParser;
10use crate::context::Stage;
11use crate::session_diagnostics::InvalidTarget;
12
13#[derive(Debug)]
14pub(crate) enum AllowedTargets {
15 AllowList(&'static [Policy]),
16 AllowListWarnRest(&'static [Policy]),
17}
18
19pub(crate) enum AllowedResult {
20 Allowed,
21 Warn,
22 Error,
23}
24
25impl AllowedTargets {
26 pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
27 match self {
28 AllowedTargets::AllowList(list) => {
29 if list.contains(&Policy::Allow(target)) {
30 AllowedResult::Allowed
31 } else if list.contains(&Policy::Warn(target)) {
32 AllowedResult::Warn
33 } else {
34 AllowedResult::Error
35 }
36 }
37 AllowedTargets::AllowListWarnRest(list) => {
38 if list.contains(&Policy::Allow(target)) {
39 AllowedResult::Allowed
40 } else if list.contains(&Policy::Error(target)) {
41 AllowedResult::Error
42 } else {
43 AllowedResult::Warn
44 }
45 }
46 }
47 }
48
49 pub(crate) fn allowed_targets(&self) -> Vec<Target> {
50 match self {
51 AllowedTargets::AllowList(list) => list,
52 AllowedTargets::AllowListWarnRest(list) => list,
53 }
54 .iter()
55 .filter_map(|target| match target {
56 Policy::Allow(target) => Some(*target),
57 Policy::Warn(_) => None,
58 Policy::Error(_) => None,
59 })
60 .collect()
61 }
62}
63
64#[derive(Debug, Eq, PartialEq)]
65pub(crate) enum Policy {
66 Allow(Target),
67 Warn(Target),
68 Error(Target),
69}
70
71impl<S: Stage> AttributeParser<'_, S> {
72 pub(crate) fn check_target(
73 &self,
74 attr_name: AttrPath,
75 attr_span: Span,
76 allowed_targets: &AllowedTargets,
77 target: Target,
78 target_id: S::Id,
79 mut emit_lint: impl FnMut(AttributeLint<S::Id>),
80 ) {
81 match allowed_targets.is_allowed(target) {
82 AllowedResult::Allowed => {}
83 AllowedResult::Warn => {
84 let allowed_targets = allowed_targets.allowed_targets();
85 let (applied, only) =
86 allowed_targets_applied(allowed_targets, target, self.features);
87 emit_lint(AttributeLint {
88 id: target_id,
89 span: attr_span,
90 kind: AttributeLintKind::InvalidTarget {
91 name: attr_name,
92 target,
93 only: if only { "only " } else { "" },
94 applied,
95 },
96 });
97 }
98 AllowedResult::Error => {
99 let allowed_targets = allowed_targets.allowed_targets();
100 let (applied, only) =
101 allowed_targets_applied(allowed_targets, target, self.features);
102 self.dcx().emit_err(InvalidTarget {
103 span: attr_span,
104 name: attr_name,
105 target: target.plural_name(),
106 only: if only { "only " } else { "" },
107 applied: DiagArgValue::StrListSepByAnd(
108 applied.into_iter().map(Cow::Owned).collect(),
109 ),
110 });
111 }
112 }
113 }
114}
115
116pub(crate) fn allowed_targets_applied(
119 mut allowed_targets: Vec<Target>,
120 target: Target,
121 features: Option<&Features>,
122) -> (Vec<String>, bool) {
123 if let Some(features) = features {
125 if !features.fn_delegation() {
126 allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
127 }
128 if !features.stmt_expr_attributes() {
129 allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
130 }
131 if !features.extern_types() {
132 allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
133 }
134 }
135
136 const FUNCTION_LIKE: &[Target] = &[
140 Target::Fn,
141 Target::Closure,
142 Target::ForeignFn,
143 Target::Method(MethodKind::Inherent),
144 Target::Method(MethodKind::Trait { body: false }),
145 Target::Method(MethodKind::Trait { body: true }),
146 Target::Method(MethodKind::TraitImpl),
147 ];
148 const METHOD_LIKE: &[Target] = &[
149 Target::Method(MethodKind::Inherent),
150 Target::Method(MethodKind::Trait { body: false }),
151 Target::Method(MethodKind::Trait { body: true }),
152 Target::Method(MethodKind::TraitImpl),
153 ];
154 const IMPL_LIKE: &[Target] =
155 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
156 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
157
158 let mut added_fake_targets = Vec::new();
159 filter_targets(
160 &mut allowed_targets,
161 FUNCTION_LIKE,
162 "functions",
163 target,
164 &mut added_fake_targets,
165 );
166 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
167 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
168 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
169
170 (
172 added_fake_targets
173 .iter()
174 .copied()
175 .chain(allowed_targets.iter().map(|t| t.plural_name()))
176 .map(|i| i.to_string())
177 .collect(),
178 allowed_targets.len() + added_fake_targets.len() == 1,
179 )
180}
181
182fn filter_targets(
183 allowed_targets: &mut Vec<Target>,
184 target_group: &'static [Target],
185 target_group_name: &'static str,
186 target: Target,
187 added_fake_targets: &mut Vec<&'static str>,
188) {
189 if target_group.contains(&target) {
190 return;
191 }
192 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
193 return;
194 }
195 allowed_targets.retain(|t| !target_group.contains(t));
196 added_fake_targets.push(target_group_name);
197}
198
199pub(crate) const ALL_TARGETS: &'static [Policy] = {
204 use Policy::Allow;
205 &[
206 Allow(Target::ExternCrate),
207 Allow(Target::Use),
208 Allow(Target::Static),
209 Allow(Target::Const),
210 Allow(Target::Fn),
211 Allow(Target::Closure),
212 Allow(Target::Mod),
213 Allow(Target::ForeignMod),
214 Allow(Target::GlobalAsm),
215 Allow(Target::TyAlias),
216 Allow(Target::Enum),
217 Allow(Target::Variant),
218 Allow(Target::Struct),
219 Allow(Target::Field),
220 Allow(Target::Union),
221 Allow(Target::Trait),
222 Allow(Target::TraitAlias),
223 Allow(Target::Impl { of_trait: false }),
224 Allow(Target::Impl { of_trait: true }),
225 Allow(Target::Expression),
226 Allow(Target::Statement),
227 Allow(Target::Arm),
228 Allow(Target::AssocConst),
229 Allow(Target::Method(MethodKind::Inherent)),
230 Allow(Target::Method(MethodKind::Trait { body: false })),
231 Allow(Target::Method(MethodKind::Trait { body: true })),
232 Allow(Target::Method(MethodKind::TraitImpl)),
233 Allow(Target::AssocTy),
234 Allow(Target::ForeignFn),
235 Allow(Target::ForeignStatic),
236 Allow(Target::ForeignTy),
237 Allow(Target::MacroDef),
238 Allow(Target::Param),
239 Allow(Target::PatField),
240 Allow(Target::ExprField),
241 Allow(Target::WherePredicate),
242 Allow(Target::MacroCall),
243 Allow(Target::Crate),
244 Allow(Target::Delegation { mac: false }),
245 Allow(Target::Delegation { mac: true }),
246 ]
247};