1use std::borrow::Cow;
2
3use rustc_ast::AttrStyle;
4use rustc_errors::DiagArgValue;
5use rustc_feature::{AttributeType, 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}
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<'sess, S: Stage> AttributeParser<'sess, S> {
72 pub(crate) fn check_target(
73 allowed_targets: &AllowedTargets,
74 target: Target,
75 cx: &mut AcceptContext<'_, 'sess, S>,
76 ) {
77 match allowed_targets.is_allowed(target) {
78 AllowedResult::Allowed => {}
79 AllowedResult::Warn => {
80 let allowed_targets = allowed_targets.allowed_targets();
81 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
82 let name = cx.attr_path.clone();
83 let attr_span = cx.attr_span;
84 cx.emit_lint(
85 AttributeLintKind::InvalidTarget {
86 name,
87 target,
88 only: if only { "only " } else { "" },
89 applied,
90 },
91 attr_span,
92 );
93 }
94 AllowedResult::Error => {
95 let allowed_targets = allowed_targets.allowed_targets();
96 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
97 let name = cx.attr_path.clone();
98 cx.dcx().emit_err(InvalidTarget {
99 span: cx.attr_span.clone(),
100 name,
101 target: target.plural_name(),
102 only: if only { "only " } else { "" },
103 applied: DiagArgValue::StrListSepByAnd(
104 applied.into_iter().map(Cow::Owned).collect(),
105 ),
106 });
107 }
108 }
109 }
110
111 pub(crate) fn check_type(
112 attribute_type: AttributeType,
113 target: Target,
114 cx: &mut AcceptContext<'_, 'sess, S>,
115 ) {
116 let is_crate_root = S::id_is_crate_root(cx.target_id);
117
118 if is_crate_root {
119 return;
120 }
121
122 if attribute_type != AttributeType::CrateLevel {
123 return;
124 }
125
126 let lint = AttributeLintKind::InvalidStyle {
127 name: cx.attr_path.clone(),
128 is_used_as_inner: cx.attr_style == AttrStyle::Inner,
129 target,
130 target_span: cx.target_span,
131 };
132 let attr_span = cx.attr_span;
133
134 cx.emit_lint(lint, attr_span);
135 }
136}
137
138pub(crate) fn allowed_targets_applied(
141 mut allowed_targets: Vec<Target>,
142 target: Target,
143 features: Option<&Features>,
144) -> (Vec<String>, bool) {
145 if let Some(features) = features {
147 if !features.fn_delegation() {
148 allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
149 }
150 if !features.stmt_expr_attributes() {
151 allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
152 }
153 if !features.extern_types() {
154 allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
155 }
156 }
157
158 const FUNCTION_LIKE: &[Target] = &[
162 Target::Fn,
163 Target::Closure,
164 Target::ForeignFn,
165 Target::Method(MethodKind::Inherent),
166 Target::Method(MethodKind::Trait { body: false }),
167 Target::Method(MethodKind::Trait { body: true }),
168 Target::Method(MethodKind::TraitImpl),
169 ];
170 const METHOD_LIKE: &[Target] = &[
171 Target::Method(MethodKind::Inherent),
172 Target::Method(MethodKind::Trait { body: false }),
173 Target::Method(MethodKind::Trait { body: true }),
174 Target::Method(MethodKind::TraitImpl),
175 ];
176 const IMPL_LIKE: &[Target] =
177 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
178 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
179
180 let mut added_fake_targets = Vec::new();
181 filter_targets(
182 &mut allowed_targets,
183 FUNCTION_LIKE,
184 "functions",
185 target,
186 &mut added_fake_targets,
187 );
188 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
189 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
190 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
191
192 (
194 added_fake_targets
195 .iter()
196 .copied()
197 .chain(allowed_targets.iter().map(|t| t.plural_name()))
198 .map(|i| i.to_string())
199 .collect(),
200 allowed_targets.len() + added_fake_targets.len() == 1,
201 )
202}
203
204fn filter_targets(
205 allowed_targets: &mut Vec<Target>,
206 target_group: &'static [Target],
207 target_group_name: &'static str,
208 target: Target,
209 added_fake_targets: &mut Vec<&'static str>,
210) {
211 if target_group.contains(&target) {
212 return;
213 }
214 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
215 return;
216 }
217 allowed_targets.retain(|t| !target_group.contains(t));
218 added_fake_targets.push(target_group_name);
219}
220
221pub(crate) const ALL_TARGETS: &'static [Policy] = {
226 use Policy::Allow;
227 &[
228 Allow(Target::ExternCrate),
229 Allow(Target::Use),
230 Allow(Target::Static),
231 Allow(Target::Const),
232 Allow(Target::Fn),
233 Allow(Target::Closure),
234 Allow(Target::Mod),
235 Allow(Target::ForeignMod),
236 Allow(Target::GlobalAsm),
237 Allow(Target::TyAlias),
238 Allow(Target::Enum),
239 Allow(Target::Variant),
240 Allow(Target::Struct),
241 Allow(Target::Field),
242 Allow(Target::Union),
243 Allow(Target::Trait),
244 Allow(Target::TraitAlias),
245 Allow(Target::Impl { of_trait: false }),
246 Allow(Target::Impl { of_trait: true }),
247 Allow(Target::Expression),
248 Allow(Target::Statement),
249 Allow(Target::Arm),
250 Allow(Target::AssocConst),
251 Allow(Target::Method(MethodKind::Inherent)),
252 Allow(Target::Method(MethodKind::Trait { body: false })),
253 Allow(Target::Method(MethodKind::Trait { body: true })),
254 Allow(Target::Method(MethodKind::TraitImpl)),
255 Allow(Target::AssocTy),
256 Allow(Target::ForeignFn),
257 Allow(Target::ForeignStatic),
258 Allow(Target::ForeignTy),
259 Allow(Target::MacroDef),
260 Allow(Target::Param),
261 Allow(Target::PatField),
262 Allow(Target::ExprField),
263 Allow(Target::WherePredicate),
264 Allow(Target::MacroCall),
265 Allow(Target::Crate),
266 Allow(Target::Delegation { mac: false }),
267 Allow(Target::Delegation { mac: true }),
268 ]
269};