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