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