1use std::borrow::Cow;
2
3use rustc_ast::{AttrStyle, Safety};
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::diagnostics::{
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<'a> ::core::fmt::Debug for AllowedTargets<'a> {
#[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<'a> {
20 AllowList(&'a [Policy]),
21 AllowListWarnRest(&'a [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) | AllowedTargets::AllowListWarnRest(list) => list,
66 AllowedTargets::ManuallyChecked => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
67 }
68 .iter()
69 .filter_map(|target| match target {
70 Policy::Allow(target) => Some(*target),
71 Policy::AllowSilent(_) | Policy::Warn(_) | Policy::Error(_) => None,
72 })
73 .collect()
74 }
75}
76
77#[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)]
79pub(crate) enum Policy {
80 Allow(Target),
82 AllowSilent(Target),
85 Warn(Target),
88 Error(Target),
90}
91
92impl<'sess> AttributeParser<'sess> {
93 pub(crate) fn check_target(
94 allowed_targets: &AllowedTargets<'_>,
95 attribute_args: &str,
96 cx: &mut AcceptContext<'_, 'sess>,
97 ) {
98 if #[allow(non_exhaustive_omitted_patterns)] match cx.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.should_emit, ShouldEmit::Nothing) {
99 return;
100 }
101
102 if let AllowedTargets::ManuallyChecked = allowed_targets {
103 #[cfg(debug_assertions)]
104 if !cx.has_target_been_checked {
105 cx.dcx().delayed_bug("Attribute target has not been checked");
106 }
107
108 return;
109 }
110
111 if let &AllowedTargets::AllowList(&[Allow(Target::Crate)]) = allowed_targets {
114 Self::check_crate_level(cx, false);
115 return;
116 }
117 if let &AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]) = allowed_targets {
118 Self::check_crate_level(cx, true);
119 return;
120 }
121
122 let result = allowed_targets.is_allowed(cx.target);
123 if #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Allowed => true,
_ => false,
}matches!(result, AllowedResult::Allowed) {
124 return;
125 }
126
127 let allowed_targets = allowed_targets.allowed_targets();
128 let (applied, only) = allowed_targets_applied(allowed_targets, cx.target, cx.features);
129 let is_diagnostic_attr = cx.attr_path.segments[0] == sym::diagnostic;
130
131 let diag = InvalidTarget {
132 span: if attribute_args.is_empty() {
133 cx.attr_path.span
136 } else {
137 cx.inner_span
144 },
145 attr_span: cx.attr_span,
146 name: cx.attr_path.clone(),
147 target: cx.target.plural_name(),
148 only: if only { "only " } else { "" },
149 applied: DiagArgValue::StrListSepByAnd(applied.into_iter().map(Cow::Owned).collect()),
150 attribute_args: attribute_args.to_string(),
151 help: Self::target_checking_help(attribute_args, cx),
152 previously_accepted: #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Warn => true,
_ => false,
}matches!(result, AllowedResult::Warn) && !is_diagnostic_attr,
153 on_macro_call: #[allow(non_exhaustive_omitted_patterns)] match cx.target {
Target::MacroCall => true,
_ => false,
}matches!(cx.target, Target::MacroCall),
154 };
155
156 match result {
157 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"),
158 AllowedResult::Warn => {
159 let lint = if cx.attr_path.segments[0] == sym::deprecated
160 && ![
161 Target::Closure,
162 Target::Expression,
163 Target::Statement,
164 Target::Arm,
165 Target::MacroCall,
166 ]
167 .contains(&cx.target)
168 {
169 rustc_session::lint::builtin::USELESS_DEPRECATED
170 } else if is_diagnostic_attr {
171 rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES
172 } else {
173 rustc_session::lint::builtin::UNUSED_ATTRIBUTES
174 };
175
176 let attr_span = cx.attr_span;
177 cx.emit_lint(lint, diag, attr_span);
178 }
179 AllowedResult::Error => {
180 cx.dcx().emit_err(diag);
181 }
182 }
183 }
184
185 fn target_checking_help(
186 attribute_args: &str,
187 cx: &AcceptContext<'_, '_>,
188 ) -> Option<InvalidTargetHelp> {
189 match &*cx.attr_path.segments {
190 [sym::link_name] if cx.target == Target::Static => {
191 let needs_unsafe_wrapper = #[allow(non_exhaustive_omitted_patterns)] match cx.attr_safety {
Safety::Default => true,
_ => false,
}matches!(cx.attr_safety, Safety::Default);
192
193 Some(InvalidTargetHelp::UseExportName {
194 unsafe_open: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_lo()),
195 name: cx.attr_path.span,
196 unsafe_close: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_hi()),
197 })
198 }
199 [sym::repr] if attribute_args == "(align(...))" => match cx.target {
200 Target::Fn | Target::Method(..) if cx.features().fn_align() => {
201 Some(InvalidTargetHelp::UseRustcAlign)
202 }
203 Target::Static if cx.features().static_align() => {
204 Some(InvalidTargetHelp::UseRustcAlignStatic)
205 }
206 _ => None,
207 },
208 _ => None,
209 }
210 }
211
212 pub(crate) fn check_crate_level(cx: &mut AcceptContext<'_, 'sess>, warn: bool) {
213 if cx.target == Target::Crate {
214 return;
215 }
216
217 let name = cx.attr_path.to_string();
218 let is_used_as_inner = cx.attr_style == AttrStyle::Inner;
219 let target_span = cx.target_span;
220 let attr_span = cx.attr_span;
221
222 let (show_crate_root_help, crate_root_path) = is_used_as_inner
223 .then(|| cx.cx.sess.local_crate_source_file())
224 .flatten()
225 .filter(|src| {
226 !#[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!(
227 cx.cx.sess.source_map().span_to_filename(attr_span),
228 FileName::Real(ref name) if name == src
229 )
230 })
231 .map(|src| {
232 (true, src.path(RemapPathScopeComponents::DIAGNOSTICS).display().to_string())
233 })
234 .unwrap_or_default();
235
236 let diag = crate::diagnostics::InvalidAttrStyle {
237 name,
238 is_used_as_inner,
239 target_span: (!is_used_as_inner).then_some(target_span),
240 target: cx.target.name(),
241 crate_root_path,
242 show_crate_root_help,
243 span: attr_span,
244 };
245 if warn {
246 cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, diag, attr_span);
247 } else {
248 cx.emit_err(diag);
249 }
250 }
251
252 pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
255 const ATTRS_TO_CHECK: &[Symbol] =
259 &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
260
261 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)) {
263 let span = attr.span;
264 let name = *name;
265
266 let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
267
268 let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
269 span,
270 pound_to_opening_bracket: span.until(inner_span),
271 name,
272 item,
273 });
274
275 self.dcx().try_steal_replace_and_emit_err(
276 attr.path.span,
277 StashKey::UndeterminedMacroResolution,
278 err,
279 );
280 }
281 }
282
283 fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
284 self.sess()
289 .source_map()
290 .span_to_source(span, |content, _, span_end| {
291 let mut source = &content[span_end..];
292 let initial_source_len = source.len();
293 let span = try {
294 loop {
295 let first = source.chars().next()?;
296
297 if first.is_whitespace() {
298 let split_idx = source.find(|c: char| !c.is_whitespace())?;
299 source = &source[split_idx..];
300 } else if source.starts_with("//") {
301 let line_idx = source.find('\n')?;
302 source = &source[line_idx + '\n'.len_utf8()..];
303 } else if source.starts_with("/*") {
304 let close_idx = source.find("*/")?;
306 source = &source[close_idx + "*/".len()..];
307 } else if first == '#' {
308 let close_idx = source.find(']')?;
312 source = &source[close_idx + ']'.len_utf8()..];
313 } else {
314 let lo = span_end + initial_source_len - source.len();
315 let last_line = source.split('\n').next().map(|s| s.trim_end())?;
316
317 let hi = lo + last_line.len();
318 let lo = BytePos(lo as u32);
319 let hi = BytePos(hi as u32);
320 let next_item_span = Span::new(lo, hi, span.ctxt(), None);
321
322 break next_item_span;
323 }
324 }
325 };
326
327 Ok(span)
328 })
329 .ok()
330 .flatten()
331 }
332
333 pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
334 &self,
335 attrs: impl IntoIterator<Item = &'attr Attribute>,
336 ) {
337 let spans = attrs
342 .into_iter()
343 .filter_map(|attr| {
344 match attr {
345 Attribute::Parsed(AttributeKind::DocComment { span, .. }) => Some(*span),
346 Attribute::Parsed(AttributeKind::Doc(attr)) => Some(attr.first_span),
348 Attribute::Parsed(_) => None,
350 Attribute::Unparsed(attr) => Some(attr.span),
351 }
352 })
353 .collect::<Vec<_>>();
354 if !spans.is_empty() {
355 self.dcx()
356 .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
357 }
358 }
359}
360
361pub(crate) fn allowed_targets_applied(
364 mut allowed_targets: Vec<Target>,
365 target: Target,
366 features: Option<&Features>,
367) -> (Vec<String>, bool) {
368 if let Some(features) = features {
370 if !features.fn_delegation() {
371 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Delegation { .. } => true,
_ => false,
}matches!(t, Target::Delegation { .. }));
372 }
373 if !features.stmt_expr_attributes() {
374 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Expression | Target::Statement => true,
_ => false,
}matches!(t, Target::Expression | Target::Statement));
375 }
376 if !features.extern_types() {
377 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::ForeignTy => true,
_ => false,
}matches!(t, Target::ForeignTy));
378 }
379 }
380
381 const FUNCTION_LIKE: &[Target] = &[
385 Target::Fn,
386 Target::Closure,
387 Target::ForeignFn,
388 Target::Method(MethodKind::Inherent),
389 Target::Method(MethodKind::Trait { body: false }),
390 Target::Method(MethodKind::Trait { body: true }),
391 Target::Method(MethodKind::TraitImpl),
392 ];
393 const FUNCTION_WITH_BODY_LIKE: &[Target] = &[
394 Target::Fn,
395 Target::Closure,
396 Target::Method(MethodKind::Inherent),
397 Target::Method(MethodKind::Trait { body: true }),
398 Target::Method(MethodKind::TraitImpl),
399 ];
400 const METHOD_LIKE: &[Target] = &[
401 Target::Method(MethodKind::Inherent),
402 Target::Method(MethodKind::Trait { body: false }),
403 Target::Method(MethodKind::Trait { body: true }),
404 Target::Method(MethodKind::TraitImpl),
405 ];
406 const IMPL_LIKE: &[Target] =
407 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
408 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
409
410 let mut added_fake_targets = Vec::new();
411 filter_targets(
412 &mut allowed_targets,
413 FUNCTION_LIKE,
414 "functions",
415 target,
416 &mut added_fake_targets,
417 );
418 filter_targets(
419 &mut allowed_targets,
420 FUNCTION_WITH_BODY_LIKE,
421 "functions with a body",
422 target,
423 &mut added_fake_targets,
424 );
425 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
426 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
427 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
428
429 let mut target_strings: Vec<_> = added_fake_targets
430 .iter()
431 .copied()
432 .chain(allowed_targets.iter().map(|t| t.plural_name()))
433 .map(|i| i.to_string())
434 .collect();
435
436 target_strings.sort();
438 target_strings.dedup();
439
440 let only_target = target_strings.len() == 1;
442
443 (target_strings, only_target)
444}
445
446fn filter_targets(
447 allowed_targets: &mut Vec<Target>,
448 target_group: &'static [Target],
449 target_group_name: &'static str,
450 target: Target,
451 added_fake_targets: &mut Vec<&'static str>,
452) {
453 if target_group.contains(&target) {
454 return;
455 }
456 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
457 return;
458 }
459 allowed_targets.retain(|t| !target_group.contains(t));
460 added_fake_targets.push(target_group_name);
461}
462
463impl<'f, 'sess> AcceptContext<'f, 'sess> {
464 pub(crate) fn check_target(
465 &mut self,
466 attribute_args: &str,
467 allowed_targets: &AllowedTargets<'_>,
468 ) {
469 self.ignore_target_checks();
470 AttributeParser::check_target(allowed_targets, attribute_args, self);
471 }
472
473 pub(crate) fn ignore_target_checks(&mut self) {
474 #[cfg(debug_assertions)]
475 {
476 self.has_target_been_checked = true;
477 }
478 }
479}
480
481pub(crate) const ALL_TARGETS: &[Policy] = {
486 use Policy::Allow;
487 &[
488 Allow(Target::ExternCrate),
489 Allow(Target::Use),
490 Allow(Target::Static),
491 Allow(Target::Const),
492 Allow(Target::Fn),
493 Allow(Target::Closure),
494 Allow(Target::Mod),
495 Allow(Target::ForeignMod),
496 Allow(Target::GlobalAsm),
497 Allow(Target::TyAlias),
498 Allow(Target::Enum),
499 Allow(Target::Variant),
500 Allow(Target::Struct),
501 Allow(Target::Field),
502 Allow(Target::Union),
503 Allow(Target::Trait),
504 Allow(Target::TraitAlias),
505 Allow(Target::Impl { of_trait: false }),
506 Allow(Target::Impl { of_trait: true }),
507 Allow(Target::Expression),
508 Allow(Target::Statement),
509 Allow(Target::Arm),
510 Allow(Target::AssocConst),
511 Allow(Target::Method(MethodKind::Inherent)),
512 Allow(Target::Method(MethodKind::Trait { body: false })),
513 Allow(Target::Method(MethodKind::Trait { body: true })),
514 Allow(Target::Method(MethodKind::TraitImpl)),
515 Allow(Target::AssocTy),
516 Allow(Target::ForeignFn),
517 Allow(Target::ForeignStatic),
518 Allow(Target::ForeignTy),
519 Allow(Target::MacroDef),
520 Allow(Target::Param),
521 Allow(Target::PatField),
522 Allow(Target::ExprField),
523 Allow(Target::WherePredicate),
524 Allow(Target::MacroCall),
525 Allow(Target::Crate),
526 Allow(Target::Delegation { mac: false }),
527 Allow(Target::Delegation { mac: true }),
528 Allow(Target::GenericParam {
529 kind: rustc_hir::target::GenericParamKind::Const,
530 has_default: false,
531 }),
532 Allow(Target::GenericParam {
533 kind: rustc_hir::target::GenericParamKind::Const,
534 has_default: true,
535 }),
536 Allow(Target::GenericParam {
537 kind: rustc_hir::target::GenericParamKind::Lifetime,
538 has_default: false,
539 }),
540 Allow(Target::GenericParam {
541 kind: rustc_hir::target::GenericParamKind::Lifetime,
542 has_default: true,
543 }),
544 Allow(Target::GenericParam {
545 kind: rustc_hir::target::GenericParamKind::Type,
546 has_default: false,
547 }),
548 Allow(Target::GenericParam {
549 kind: rustc_hir::target::GenericParamKind::Type,
550 has_default: true,
551 }),
552 Allow(Target::Loop),
553 Allow(Target::ForLoop),
554 Allow(Target::While),
555 Allow(Target::Break),
556 ]
557};