1use std::borrow::Cow;
2
3use rustc_ast::AttrStyle;
4use rustc_errors::DiagArgValue;
5use rustc_feature::Features;
6use rustc_hir::lints::AttributeLintKind;
7use rustc_hir::{MethodKind, Target};
8
9use crate::AttributeParser;
10use crate::context::{AcceptContext, Stage};
11use crate::session_diagnostics::InvalidTarget;
12
13#[derive(Debug)]
14pub(crate) enum AllowedTargets {
15 AllowList(&'static [Policy]),
16 AllowListWarnRest(&'static [Policy]),
17 CrateLevel,
22}
23
24pub(crate) enum AllowedResult {
25 Allowed,
26 Warn,
27 Error,
28}
29
30impl AllowedTargets {
31 pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
32 match self {
33 AllowedTargets::AllowList(list) => {
34 if list.contains(&Policy::Allow(target))
35 || list.contains(&Policy::AllowSilent(target))
36 {
37 AllowedResult::Allowed
38 } else if list.contains(&Policy::Warn(target)) {
39 AllowedResult::Warn
40 } else {
41 AllowedResult::Error
42 }
43 }
44 AllowedTargets::AllowListWarnRest(list) => {
45 if list.contains(&Policy::Allow(target))
46 || list.contains(&Policy::AllowSilent(target))
47 {
48 AllowedResult::Allowed
49 } else if list.contains(&Policy::Error(target)) {
50 AllowedResult::Error
51 } else {
52 AllowedResult::Warn
53 }
54 }
55 AllowedTargets::CrateLevel => AllowedResult::Allowed,
56 }
57 }
58
59 pub(crate) fn allowed_targets(&self) -> Vec<Target> {
60 match self {
61 AllowedTargets::AllowList(list) => list,
62 AllowedTargets::AllowListWarnRest(list) => list,
63 AllowedTargets::CrateLevel => ALL_TARGETS,
64 }
65 .iter()
66 .filter_map(|target| match target {
67 Policy::Allow(target) => Some(*target),
68 Policy::AllowSilent(_) => None, Policy::Warn(_) => None,
70 Policy::Error(_) => None,
71 })
72 .collect()
73 }
74}
75
76#[derive(Debug, Eq, PartialEq)]
78pub(crate) enum Policy {
79 Allow(Target),
81 AllowSilent(Target),
84 Warn(Target),
87 Error(Target),
89}
90
91impl<'sess, S: Stage> AttributeParser<'sess, S> {
92 pub(crate) fn check_target(
93 allowed_targets: &AllowedTargets,
94 target: Target,
95 cx: &mut AcceptContext<'_, 'sess, S>,
96 ) {
97 Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx);
98
99 match allowed_targets.is_allowed(target) {
100 AllowedResult::Allowed => {}
101 AllowedResult::Warn => {
102 let allowed_targets = allowed_targets.allowed_targets();
103 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
104 let name = cx.attr_path.clone();
105 let attr_span = cx.attr_span;
106 cx.emit_lint(
107 AttributeLintKind::InvalidTarget {
108 name,
109 target,
110 only: if only { "only " } else { "" },
111 applied,
112 },
113 attr_span,
114 );
115 }
116 AllowedResult::Error => {
117 let allowed_targets = allowed_targets.allowed_targets();
118 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
119 let name = cx.attr_path.clone();
120 cx.dcx().emit_err(InvalidTarget {
121 span: cx.attr_span.clone(),
122 name,
123 target: target.plural_name(),
124 only: if only { "only " } else { "" },
125 applied: DiagArgValue::StrListSepByAnd(
126 applied.into_iter().map(Cow::Owned).collect(),
127 ),
128 });
129 }
130 }
131 }
132
133 pub(crate) fn check_type(
134 crate_level: bool,
135 target: Target,
136 cx: &mut AcceptContext<'_, 'sess, S>,
137 ) {
138 let is_crate_root = S::id_is_crate_root(cx.target_id);
139
140 if is_crate_root {
141 return;
142 }
143
144 if !crate_level {
145 return;
146 }
147
148 let lint = AttributeLintKind::InvalidStyle {
149 name: cx.attr_path.clone(),
150 is_used_as_inner: cx.attr_style == AttrStyle::Inner,
151 target,
152 target_span: cx.target_span,
153 };
154 let attr_span = cx.attr_span;
155
156 cx.emit_lint(lint, attr_span);
157 }
158}
159
160pub(crate) fn allowed_targets_applied(
163 mut allowed_targets: Vec<Target>,
164 target: Target,
165 features: Option<&Features>,
166) -> (Vec<String>, bool) {
167 if let Some(features) = features {
169 if !features.fn_delegation() {
170 allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
171 }
172 if !features.stmt_expr_attributes() {
173 allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
174 }
175 if !features.extern_types() {
176 allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
177 }
178 }
179
180 const FUNCTION_LIKE: &[Target] = &[
184 Target::Fn,
185 Target::Closure,
186 Target::ForeignFn,
187 Target::Method(MethodKind::Inherent),
188 Target::Method(MethodKind::Trait { body: false }),
189 Target::Method(MethodKind::Trait { body: true }),
190 Target::Method(MethodKind::TraitImpl),
191 ];
192 const METHOD_LIKE: &[Target] = &[
193 Target::Method(MethodKind::Inherent),
194 Target::Method(MethodKind::Trait { body: false }),
195 Target::Method(MethodKind::Trait { body: true }),
196 Target::Method(MethodKind::TraitImpl),
197 ];
198 const IMPL_LIKE: &[Target] =
199 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
200 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
201
202 let mut added_fake_targets = Vec::new();
203 filter_targets(
204 &mut allowed_targets,
205 FUNCTION_LIKE,
206 "functions",
207 target,
208 &mut added_fake_targets,
209 );
210 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
211 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
212 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
213
214 let mut target_strings: Vec<_> = added_fake_targets
215 .iter()
216 .copied()
217 .chain(allowed_targets.iter().map(|t| t.plural_name()))
218 .map(|i| i.to_string())
219 .collect();
220
221 target_strings.sort();
223
224 let only_target = target_strings.len() == 1;
226
227 (target_strings, only_target)
228}
229
230fn filter_targets(
231 allowed_targets: &mut Vec<Target>,
232 target_group: &'static [Target],
233 target_group_name: &'static str,
234 target: Target,
235 added_fake_targets: &mut Vec<&'static str>,
236) {
237 if target_group.contains(&target) {
238 return;
239 }
240 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
241 return;
242 }
243 allowed_targets.retain(|t| !target_group.contains(t));
244 added_fake_targets.push(target_group_name);
245}
246
247pub(crate) const ALL_TARGETS: &'static [Policy] = {
252 use Policy::Allow;
253 &[
254 Allow(Target::ExternCrate),
255 Allow(Target::Use),
256 Allow(Target::Static),
257 Allow(Target::Const),
258 Allow(Target::Fn),
259 Allow(Target::Closure),
260 Allow(Target::Mod),
261 Allow(Target::ForeignMod),
262 Allow(Target::GlobalAsm),
263 Allow(Target::TyAlias),
264 Allow(Target::Enum),
265 Allow(Target::Variant),
266 Allow(Target::Struct),
267 Allow(Target::Field),
268 Allow(Target::Union),
269 Allow(Target::Trait),
270 Allow(Target::TraitAlias),
271 Allow(Target::Impl { of_trait: false }),
272 Allow(Target::Impl { of_trait: true }),
273 Allow(Target::Expression),
274 Allow(Target::Statement),
275 Allow(Target::Arm),
276 Allow(Target::AssocConst),
277 Allow(Target::Method(MethodKind::Inherent)),
278 Allow(Target::Method(MethodKind::Trait { body: false })),
279 Allow(Target::Method(MethodKind::Trait { body: true })),
280 Allow(Target::Method(MethodKind::TraitImpl)),
281 Allow(Target::AssocTy),
282 Allow(Target::ForeignFn),
283 Allow(Target::ForeignStatic),
284 Allow(Target::ForeignTy),
285 Allow(Target::MacroDef),
286 Allow(Target::Param),
287 Allow(Target::PatField),
288 Allow(Target::ExprField),
289 Allow(Target::WherePredicate),
290 Allow(Target::MacroCall),
291 Allow(Target::Crate),
292 Allow(Target::Delegation { mac: false }),
293 Allow(Target::Delegation { mac: true }),
294 ]
295};