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