1use std::borrow::Cow;
2
3use rustc_ast::AttrStyle;
4use rustc_errors::{DiagArgValue, MultiSpan, StashKey};
5use rustc_feature::Features;
6use rustc_hir::attrs::AttributeKind;
7use rustc_hir::lints::AttributeLintKind;
8use rustc_hir::{AttrItem, Attribute, MethodKind, Target};
9use rustc_span::{BytePos, Span, Symbol, sym};
10
11use crate::AttributeParser;
12use crate::context::{AcceptContext, Stage};
13use crate::errors::{
14 InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere,
15};
16use crate::session_diagnostics::InvalidTarget;
17use crate::target_checking::Policy::Allow;
18
19#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AllowedTargets {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
AllowedTargets::AllowList(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowList", &__self_0),
AllowedTargets::AllowListWarnRest(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowListWarnRest", &__self_0),
}
}
}Debug)]
20pub(crate) enum AllowedTargets {
21 AllowList(&'static [Policy]),
22 AllowListWarnRest(&'static [Policy]),
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 }
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 }
64 .iter()
65 .filter_map(|target| match target {
66 Policy::Allow(target) => Some(*target),
67 Policy::AllowSilent(_) => None, Policy::Warn(_) => None,
69 Policy::Error(_) => None,
70 })
71 .collect()
72 }
73}
74
75#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Policy {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Policy::Allow(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Allow",
&__self_0),
Policy::AllowSilent(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowSilent", &__self_0),
Policy::Warn(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Warn",
&__self_0),
Policy::Error(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Error",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for Policy {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Target>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for Policy {
#[inline]
fn eq(&self, other: &Policy) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Policy::Allow(__self_0), Policy::Allow(__arg1_0)) =>
__self_0 == __arg1_0,
(Policy::AllowSilent(__self_0), Policy::AllowSilent(__arg1_0))
=> __self_0 == __arg1_0,
(Policy::Warn(__self_0), Policy::Warn(__arg1_0)) =>
__self_0 == __arg1_0,
(Policy::Error(__self_0), Policy::Error(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq)]
77pub(crate) enum Policy {
78 Allow(Target),
80 AllowSilent(Target),
83 Warn(Target),
86 Error(Target),
88}
89
90impl<'sess, S: Stage> AttributeParser<'sess, S> {
91 pub(crate) fn check_target(
92 allowed_targets: &AllowedTargets,
93 target: Target,
94 cx: &mut AcceptContext<'_, 'sess, S>,
95 ) {
96 if let &AllowedTargets::AllowList(&[Allow(Target::Crate)]) = allowed_targets {
99 Self::check_crate_level(target, cx);
100 return;
101 }
102
103 if #[allow(non_exhaustive_omitted_patterns)] match cx.attr_path.segments.as_ref()
{
[sym::repr] => true,
_ => false,
}matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate {
104 let span = cx.attr_span;
107 let item =
108 cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
109
110 let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span);
111
112 cx.dcx()
113 .create_err(InvalidAttrAtCrateLevel {
114 span,
115 pound_to_opening_bracket,
116 name: sym::repr,
117 item,
118 })
119 .emit();
120 }
121
122 match allowed_targets.is_allowed(target) {
123 AllowedResult::Allowed => {}
124 AllowedResult::Warn => {
125 let allowed_targets = allowed_targets.allowed_targets();
126 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
127 let name = cx.attr_path.clone();
128
129 let lint = if name.segments[0] == sym::deprecated
130 && ![
131 Target::Closure,
132 Target::Expression,
133 Target::Statement,
134 Target::Arm,
135 Target::MacroCall,
136 ]
137 .contains(&target)
138 {
139 rustc_session::lint::builtin::USELESS_DEPRECATED
140 } else {
141 rustc_session::lint::builtin::UNUSED_ATTRIBUTES
142 };
143
144 let attr_span = cx.attr_span;
145 cx.emit_lint(
146 lint,
147 AttributeLintKind::InvalidTarget {
148 name: name.to_string(),
149 target: target.plural_name(),
150 only: if only { "only " } else { "" },
151 applied,
152 attr_span,
153 },
154 attr_span,
155 );
156 }
157 AllowedResult::Error => {
158 let allowed_targets = allowed_targets.allowed_targets();
159 let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
160 let name = cx.attr_path.clone();
161 cx.dcx().emit_err(InvalidTarget {
162 span: cx.attr_span.clone(),
163 name,
164 target: target.plural_name(),
165 only: if only { "only " } else { "" },
166 applied: DiagArgValue::StrListSepByAnd(
167 applied.into_iter().map(Cow::Owned).collect(),
168 ),
169 });
170 }
171 }
172 }
173
174 pub(crate) fn check_crate_level(target: Target, cx: &mut AcceptContext<'_, 'sess, S>) {
175 if target == Target::Crate {
176 return;
177 }
178
179 let kind = AttributeLintKind::InvalidStyle {
180 name: cx.attr_path.to_string(),
181 is_used_as_inner: cx.attr_style == AttrStyle::Inner,
182 target: target.name(),
183 target_span: cx.target_span,
184 };
185 let attr_span = cx.attr_span;
186
187 cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
188 }
189
190 pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
193 const ATTRS_TO_CHECK: &[Symbol] =
197 &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
198
199 if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| #[allow(non_exhaustive_omitted_patterns)] match attr.path.segments.as_ref() {
[segment] if segment == *attr_to_check => true,
_ => false,
}matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) {
201 let span = attr.span;
202 let name = *name;
203
204 let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
205
206 let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
207 span,
208 pound_to_opening_bracket: span.until(inner_span),
209 name,
210 item,
211 });
212
213 self.dcx().try_steal_replace_and_emit_err(
214 attr.path.span,
215 StashKey::UndeterminedMacroResolution,
216 err,
217 );
218 }
219 }
220
221 fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
222 self.sess()
227 .source_map()
228 .span_to_source(span, |content, _, span_end| {
229 let mut source = &content[span_end..];
230 let initial_source_len = source.len();
231 let span = try {
232 loop {
233 let first = source.chars().next()?;
234
235 if first.is_whitespace() {
236 let split_idx = source.find(|c: char| !c.is_whitespace())?;
237 source = &source[split_idx..];
238 } else if source.starts_with("//") {
239 let line_idx = source.find('\n')?;
240 source = &source[line_idx + '\n'.len_utf8()..];
241 } else if source.starts_with("/*") {
242 let close_idx = source.find("*/")?;
244 source = &source[close_idx + "*/".len()..];
245 } else if first == '#' {
246 let close_idx = source.find(']')?;
250 source = &source[close_idx + ']'.len_utf8()..];
251 } else {
252 let lo = span_end + initial_source_len - source.len();
253 let last_line = source.split('\n').next().map(|s| s.trim_end())?;
254
255 let hi = lo + last_line.len();
256 let lo = BytePos(lo as u32);
257 let hi = BytePos(hi as u32);
258 let next_item_span = Span::new(lo, hi, span.ctxt(), None);
259
260 break next_item_span;
261 }
262 }
263 };
264
265 Ok(span)
266 })
267 .ok()
268 .flatten()
269 }
270
271 pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
272 &self,
273 attrs: impl IntoIterator<Item = &'attr Attribute>,
274 ) {
275 let spans = attrs
280 .into_iter()
281 .filter(|attr| {
283 #[allow(non_exhaustive_omitted_patterns)] match attr {
Attribute::Parsed(AttributeKind::DocComment { .. } |
AttributeKind::Doc(_)) | Attribute::Unparsed(_) => true,
_ => false,
}matches!(
284 attr,
285 Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_))
286 | Attribute::Unparsed(_)
287 )
288 })
289 .map(|attr| attr.span())
290 .collect::<Vec<_>>();
291 if !spans.is_empty() {
292 self.dcx()
293 .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
294 }
295 }
296}
297
298pub(crate) fn allowed_targets_applied(
301 mut allowed_targets: Vec<Target>,
302 target: Target,
303 features: Option<&Features>,
304) -> (Vec<String>, bool) {
305 if let Some(features) = features {
307 if !features.fn_delegation() {
308 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Delegation { .. } => true,
_ => false,
}matches!(t, Target::Delegation { .. }));
309 }
310 if !features.stmt_expr_attributes() {
311 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Expression | Target::Statement => true,
_ => false,
}matches!(t, Target::Expression | Target::Statement));
312 }
313 if !features.extern_types() {
314 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::ForeignTy => true,
_ => false,
}matches!(t, Target::ForeignTy));
315 }
316 }
317
318 const FUNCTION_LIKE: &[Target] = &[
322 Target::Fn,
323 Target::Closure,
324 Target::ForeignFn,
325 Target::Method(MethodKind::Inherent),
326 Target::Method(MethodKind::Trait { body: false }),
327 Target::Method(MethodKind::Trait { body: true }),
328 Target::Method(MethodKind::TraitImpl),
329 ];
330 const METHOD_LIKE: &[Target] = &[
331 Target::Method(MethodKind::Inherent),
332 Target::Method(MethodKind::Trait { body: false }),
333 Target::Method(MethodKind::Trait { body: true }),
334 Target::Method(MethodKind::TraitImpl),
335 ];
336 const IMPL_LIKE: &[Target] =
337 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
338 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
339
340 let mut added_fake_targets = Vec::new();
341 filter_targets(
342 &mut allowed_targets,
343 FUNCTION_LIKE,
344 "functions",
345 target,
346 &mut added_fake_targets,
347 );
348 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
349 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
350 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
351
352 let mut target_strings: Vec<_> = added_fake_targets
353 .iter()
354 .copied()
355 .chain(allowed_targets.iter().map(|t| t.plural_name()))
356 .map(|i| i.to_string())
357 .collect();
358
359 target_strings.sort();
361
362 let only_target = target_strings.len() == 1;
364
365 (target_strings, only_target)
366}
367
368fn filter_targets(
369 allowed_targets: &mut Vec<Target>,
370 target_group: &'static [Target],
371 target_group_name: &'static str,
372 target: Target,
373 added_fake_targets: &mut Vec<&'static str>,
374) {
375 if target_group.contains(&target) {
376 return;
377 }
378 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
379 return;
380 }
381 allowed_targets.retain(|t| !target_group.contains(t));
382 added_fake_targets.push(target_group_name);
383}
384
385pub(crate) const ALL_TARGETS: &'static [Policy] = {
390 use Policy::Allow;
391 &[
392 Allow(Target::ExternCrate),
393 Allow(Target::Use),
394 Allow(Target::Static),
395 Allow(Target::Const),
396 Allow(Target::Fn),
397 Allow(Target::Closure),
398 Allow(Target::Mod),
399 Allow(Target::ForeignMod),
400 Allow(Target::GlobalAsm),
401 Allow(Target::TyAlias),
402 Allow(Target::Enum),
403 Allow(Target::Variant),
404 Allow(Target::Struct),
405 Allow(Target::Field),
406 Allow(Target::Union),
407 Allow(Target::Trait),
408 Allow(Target::TraitAlias),
409 Allow(Target::Impl { of_trait: false }),
410 Allow(Target::Impl { of_trait: true }),
411 Allow(Target::Expression),
412 Allow(Target::Statement),
413 Allow(Target::Arm),
414 Allow(Target::AssocConst),
415 Allow(Target::Method(MethodKind::Inherent)),
416 Allow(Target::Method(MethodKind::Trait { body: false })),
417 Allow(Target::Method(MethodKind::Trait { body: true })),
418 Allow(Target::Method(MethodKind::TraitImpl)),
419 Allow(Target::AssocTy),
420 Allow(Target::ForeignFn),
421 Allow(Target::ForeignStatic),
422 Allow(Target::ForeignTy),
423 Allow(Target::MacroDef),
424 Allow(Target::Param),
425 Allow(Target::PatField),
426 Allow(Target::ExprField),
427 Allow(Target::WherePredicate),
428 Allow(Target::MacroCall),
429 Allow(Target::Crate),
430 Allow(Target::Delegation { mac: false }),
431 Allow(Target::Delegation { mac: true }),
432 Allow(Target::GenericParam {
433 kind: rustc_hir::target::GenericParamKind::Const,
434 has_default: false,
435 }),
436 Allow(Target::GenericParam {
437 kind: rustc_hir::target::GenericParamKind::Const,
438 has_default: true,
439 }),
440 Allow(Target::GenericParam {
441 kind: rustc_hir::target::GenericParamKind::Lifetime,
442 has_default: false,
443 }),
444 Allow(Target::GenericParam {
445 kind: rustc_hir::target::GenericParamKind::Lifetime,
446 has_default: true,
447 }),
448 Allow(Target::GenericParam {
449 kind: rustc_hir::target::GenericParamKind::Type,
450 has_default: false,
451 }),
452 Allow(Target::GenericParam {
453 kind: rustc_hir::target::GenericParamKind::Type,
454 has_default: true,
455 }),
456 ]
457};