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::{AttrItem, Attribute, MethodKind, Target};
8use rustc_span::{BytePos, FileName, RemapPathScopeComponents, Span, Symbol, sym};
9
10use crate::context::AcceptContext;
11use crate::errors::{
12 InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere,
13};
14use crate::session_diagnostics::{InvalidTarget, InvalidTargetHelp};
15use crate::target_checking::Policy::Allow;
16use crate::{AttributeParser, ShouldEmit};
17
18#[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),
AllowedTargets::ManuallyChecked =>
::core::fmt::Formatter::write_str(f, "ManuallyChecked"),
}
}
}Debug)]
19pub(crate) enum AllowedTargets {
20 AllowList(&'static [Policy]),
21 AllowListWarnRest(&'static [Policy]),
22 ManuallyChecked,
26}
27
28pub(crate) enum AllowedResult {
29 Allowed,
30 Warn,
31 Error,
32}
33
34impl AllowedTargets {
35 pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
36 match self {
37 AllowedTargets::AllowList(list) => {
38 if list.contains(&Policy::Allow(target))
39 || list.contains(&Policy::AllowSilent(target))
40 {
41 AllowedResult::Allowed
42 } else if list.contains(&Policy::Warn(target)) {
43 AllowedResult::Warn
44 } else {
45 AllowedResult::Error
46 }
47 }
48 AllowedTargets::AllowListWarnRest(list) => {
49 if list.contains(&Policy::Allow(target))
50 || list.contains(&Policy::AllowSilent(target))
51 {
52 AllowedResult::Allowed
53 } else if list.contains(&Policy::Error(target)) {
54 AllowedResult::Error
55 } else {
56 AllowedResult::Warn
57 }
58 }
59 AllowedTargets::ManuallyChecked => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
60 }
61 }
62
63 pub(crate) fn allowed_targets(&self) -> Vec<Target> {
64 match self {
65 AllowedTargets::AllowList(list) => list,
66 AllowedTargets::AllowListWarnRest(list) => list,
67 AllowedTargets::ManuallyChecked => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
68 }
69 .iter()
70 .filter_map(|target| match target {
71 Policy::Allow(target) => Some(*target),
72 Policy::AllowSilent(_) => None, Policy::Warn(_) => None,
74 Policy::Error(_) => None,
75 })
76 .collect()
77 }
78}
79
80#[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)]
82pub(crate) enum Policy {
83 Allow(Target),
85 AllowSilent(Target),
88 Warn(Target),
91 Error(Target),
93}
94
95impl<'sess> AttributeParser<'sess> {
96 pub(crate) fn check_target(
97 allowed_targets: &AllowedTargets,
98 attribute_args: &'static str,
99 cx: &mut AcceptContext<'_, 'sess>,
100 ) {
101 if #[allow(non_exhaustive_omitted_patterns)] match cx.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.should_emit, ShouldEmit::Nothing) {
102 return;
103 }
104
105 if let AllowedTargets::ManuallyChecked = allowed_targets {
106 #[cfg(debug_assertions)]
107 if !cx.has_target_been_checked {
108 cx.dcx().delayed_bug("Attribute target has not been checked");
109 }
110
111 return;
112 }
113
114 if let &AllowedTargets::AllowList(&[Allow(Target::Crate)]) = allowed_targets {
117 Self::check_crate_level(cx);
118 return;
119 }
120
121 let result = allowed_targets.is_allowed(cx.target);
122 if #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Allowed => true,
_ => false,
}matches!(result, AllowedResult::Allowed) {
123 return;
124 }
125
126 let allowed_targets = allowed_targets.allowed_targets();
127 let (applied, only) = allowed_targets_applied(allowed_targets, cx.target, cx.features);
128 let diag = InvalidTarget {
129 span: cx.attr_span.clone(),
130 name: cx.attr_path.clone(),
131 target: cx.target.plural_name(),
132 only: if only { "only " } else { "" },
133 applied: DiagArgValue::StrListSepByAnd(applied.into_iter().map(Cow::Owned).collect()),
134 attribute_args,
135 help: Self::target_checking_help(attribute_args, cx),
136 previously_accepted: #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Warn => true,
_ => false,
}matches!(result, AllowedResult::Warn),
137 };
138
139 match result {
140 AllowedResult::Allowed => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Should have early returned above")));
}unreachable!("Should have early returned above"),
141 AllowedResult::Warn => {
142 let lint = if cx.attr_path.segments[0] == sym::deprecated
143 && ![
144 Target::Closure,
145 Target::Expression,
146 Target::Statement,
147 Target::Arm,
148 Target::MacroCall,
149 ]
150 .contains(&cx.target)
151 {
152 rustc_session::lint::builtin::USELESS_DEPRECATED
153 } else {
154 rustc_session::lint::builtin::UNUSED_ATTRIBUTES
155 };
156
157 let attr_span = cx.attr_span.clone();
158 cx.emit_lint(lint, diag, attr_span);
159 }
160 AllowedResult::Error => {
161 cx.dcx().emit_err(diag);
162 }
163 }
164 }
165
166 fn target_checking_help(
167 attribute_args: &'static str,
168 cx: &AcceptContext<'_, '_>,
169 ) -> Option<InvalidTargetHelp> {
170 match &*cx.attr_path.segments {
171 [sym::repr] if attribute_args == "(align(...))" => match cx.target {
172 Target::Fn | Target::Method(..) if cx.features().fn_align() => {
173 Some(InvalidTargetHelp::UseRustcAlign)
174 }
175 Target::Static if cx.features().static_align() => {
176 Some(InvalidTargetHelp::UseRustcAlignStatic)
177 }
178 _ => None,
179 },
180 _ => None,
181 }
182 }
183
184 pub(crate) fn check_crate_level(cx: &mut AcceptContext<'_, 'sess>) {
185 if cx.target == Target::Crate {
186 return;
187 }
188
189 let name = cx.attr_path.to_string();
190 let is_used_as_inner = cx.attr_style == AttrStyle::Inner;
191 let target_span = cx.target_span;
192 let attr_span = cx.attr_span;
193
194 let (show_crate_root_help, crate_root_path) = is_used_as_inner
195 .then(|| cx.cx.sess.local_crate_source_file())
196 .flatten()
197 .filter(|src| {
198 !#[allow(non_exhaustive_omitted_patterns)] match cx.cx.sess.source_map().span_to_filename(attr_span)
{
FileName::Real(ref name) if name == src => true,
_ => false,
}matches!(
199 cx.cx.sess.source_map().span_to_filename(attr_span),
200 FileName::Real(ref name) if name == src
201 )
202 })
203 .map(|src| {
204 (true, src.path(RemapPathScopeComponents::DIAGNOSTICS).display().to_string())
205 })
206 .unwrap_or_default();
207
208 let target = cx.target;
209 cx.emit_lint(
210 rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
211 crate::errors::InvalidAttrStyle {
212 name,
213 is_used_as_inner,
214 target_span: (!is_used_as_inner).then_some(target_span),
215 target: target.name(),
216 crate_root_path,
217 show_crate_root_help,
218 },
219 attr_span,
220 );
221 }
222
223 pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
226 const ATTRS_TO_CHECK: &[Symbol] =
230 &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
231
232 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)) {
234 let span = attr.span;
235 let name = *name;
236
237 let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
238
239 let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
240 span,
241 pound_to_opening_bracket: span.until(inner_span),
242 name,
243 item,
244 });
245
246 self.dcx().try_steal_replace_and_emit_err(
247 attr.path.span,
248 StashKey::UndeterminedMacroResolution,
249 err,
250 );
251 }
252 }
253
254 fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
255 self.sess()
260 .source_map()
261 .span_to_source(span, |content, _, span_end| {
262 let mut source = &content[span_end..];
263 let initial_source_len = source.len();
264 let span = try {
265 loop {
266 let first = source.chars().next()?;
267
268 if first.is_whitespace() {
269 let split_idx = source.find(|c: char| !c.is_whitespace())?;
270 source = &source[split_idx..];
271 } else if source.starts_with("//") {
272 let line_idx = source.find('\n')?;
273 source = &source[line_idx + '\n'.len_utf8()..];
274 } else if source.starts_with("/*") {
275 let close_idx = source.find("*/")?;
277 source = &source[close_idx + "*/".len()..];
278 } else if first == '#' {
279 let close_idx = source.find(']')?;
283 source = &source[close_idx + ']'.len_utf8()..];
284 } else {
285 let lo = span_end + initial_source_len - source.len();
286 let last_line = source.split('\n').next().map(|s| s.trim_end())?;
287
288 let hi = lo + last_line.len();
289 let lo = BytePos(lo as u32);
290 let hi = BytePos(hi as u32);
291 let next_item_span = Span::new(lo, hi, span.ctxt(), None);
292
293 break next_item_span;
294 }
295 }
296 };
297
298 Ok(span)
299 })
300 .ok()
301 .flatten()
302 }
303
304 pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
305 &self,
306 attrs: impl IntoIterator<Item = &'attr Attribute>,
307 ) {
308 let spans = attrs
313 .into_iter()
314 .filter_map(|attr| {
315 match attr {
316 Attribute::Parsed(AttributeKind::DocComment { span, .. }) => Some(*span),
317 Attribute::Parsed(AttributeKind::Doc(attr)) => Some(attr.first_span),
319 Attribute::Parsed(_) => None,
321 Attribute::Unparsed(attr) => Some(attr.span),
322 }
323 })
324 .collect::<Vec<_>>();
325 if !spans.is_empty() {
326 self.dcx()
327 .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
328 }
329 }
330}
331
332pub(crate) fn allowed_targets_applied(
335 mut allowed_targets: Vec<Target>,
336 target: Target,
337 features: Option<&Features>,
338) -> (Vec<String>, bool) {
339 if let Some(features) = features {
341 if !features.fn_delegation() {
342 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Delegation { .. } => true,
_ => false,
}matches!(t, Target::Delegation { .. }));
343 }
344 if !features.stmt_expr_attributes() {
345 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Expression | Target::Statement => true,
_ => false,
}matches!(t, Target::Expression | Target::Statement));
346 }
347 if !features.extern_types() {
348 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::ForeignTy => true,
_ => false,
}matches!(t, Target::ForeignTy));
349 }
350 }
351
352 const FUNCTION_LIKE: &[Target] = &[
356 Target::Fn,
357 Target::Closure,
358 Target::ForeignFn,
359 Target::Method(MethodKind::Inherent),
360 Target::Method(MethodKind::Trait { body: false }),
361 Target::Method(MethodKind::Trait { body: true }),
362 Target::Method(MethodKind::TraitImpl),
363 ];
364 const METHOD_LIKE: &[Target] = &[
365 Target::Method(MethodKind::Inherent),
366 Target::Method(MethodKind::Trait { body: false }),
367 Target::Method(MethodKind::Trait { body: true }),
368 Target::Method(MethodKind::TraitImpl),
369 ];
370 const IMPL_LIKE: &[Target] =
371 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
372 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
373
374 let mut added_fake_targets = Vec::new();
375 filter_targets(
376 &mut allowed_targets,
377 FUNCTION_LIKE,
378 "functions",
379 target,
380 &mut added_fake_targets,
381 );
382 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
383 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
384 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
385
386 let mut target_strings: Vec<_> = added_fake_targets
387 .iter()
388 .copied()
389 .chain(allowed_targets.iter().map(|t| t.plural_name()))
390 .map(|i| i.to_string())
391 .collect();
392
393 target_strings.sort();
395
396 let only_target = target_strings.len() == 1;
398
399 (target_strings, only_target)
400}
401
402fn filter_targets(
403 allowed_targets: &mut Vec<Target>,
404 target_group: &'static [Target],
405 target_group_name: &'static str,
406 target: Target,
407 added_fake_targets: &mut Vec<&'static str>,
408) {
409 if target_group.contains(&target) {
410 return;
411 }
412 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
413 return;
414 }
415 allowed_targets.retain(|t| !target_group.contains(t));
416 added_fake_targets.push(target_group_name);
417}
418
419impl<'f, 'sess> AcceptContext<'f, 'sess> {
420 pub(crate) fn check_target(
421 &mut self,
422 attribute_args: &'static str,
423 allowed_targets: &AllowedTargets,
424 ) {
425 #[cfg(debug_assertions)]
426 {
427 self.has_target_been_checked = true;
428 }
429 AttributeParser::check_target(allowed_targets, attribute_args, self);
430 }
431}
432
433pub(crate) const ALL_TARGETS: &'static [Policy] = {
438 use Policy::Allow;
439 &[
440 Allow(Target::ExternCrate),
441 Allow(Target::Use),
442 Allow(Target::Static),
443 Allow(Target::Const),
444 Allow(Target::Fn),
445 Allow(Target::Closure),
446 Allow(Target::Mod),
447 Allow(Target::ForeignMod),
448 Allow(Target::GlobalAsm),
449 Allow(Target::TyAlias),
450 Allow(Target::Enum),
451 Allow(Target::Variant),
452 Allow(Target::Struct),
453 Allow(Target::Field),
454 Allow(Target::Union),
455 Allow(Target::Trait),
456 Allow(Target::TraitAlias),
457 Allow(Target::Impl { of_trait: false }),
458 Allow(Target::Impl { of_trait: true }),
459 Allow(Target::Expression),
460 Allow(Target::Statement),
461 Allow(Target::Arm),
462 Allow(Target::AssocConst),
463 Allow(Target::Method(MethodKind::Inherent)),
464 Allow(Target::Method(MethodKind::Trait { body: false })),
465 Allow(Target::Method(MethodKind::Trait { body: true })),
466 Allow(Target::Method(MethodKind::TraitImpl)),
467 Allow(Target::AssocTy),
468 Allow(Target::ForeignFn),
469 Allow(Target::ForeignStatic),
470 Allow(Target::ForeignTy),
471 Allow(Target::MacroDef),
472 Allow(Target::Param),
473 Allow(Target::PatField),
474 Allow(Target::ExprField),
475 Allow(Target::WherePredicate),
476 Allow(Target::MacroCall),
477 Allow(Target::Crate),
478 Allow(Target::Delegation { mac: false }),
479 Allow(Target::Delegation { mac: true }),
480 Allow(Target::GenericParam {
481 kind: rustc_hir::target::GenericParamKind::Const,
482 has_default: false,
483 }),
484 Allow(Target::GenericParam {
485 kind: rustc_hir::target::GenericParamKind::Const,
486 has_default: true,
487 }),
488 Allow(Target::GenericParam {
489 kind: rustc_hir::target::GenericParamKind::Lifetime,
490 has_default: false,
491 }),
492 Allow(Target::GenericParam {
493 kind: rustc_hir::target::GenericParamKind::Lifetime,
494 has_default: true,
495 }),
496 Allow(Target::GenericParam {
497 kind: rustc_hir::target::GenericParamKind::Type,
498 has_default: false,
499 }),
500 Allow(Target::GenericParam {
501 kind: rustc_hir::target::GenericParamKind::Type,
502 has_default: true,
503 }),
504 ]
505};