1use std::borrow::Cow;
2
3use rustc_ast::{AttrStyle, Safety};
4use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target};
5use rustc_attr_ir::{AttrItem, Attribute, AttributeKind};
6use rustc_errors::{DiagArgValue, MultiSpan, StashKey};
7use rustc_feature::Features;
8use rustc_lint_defs::builtin::{
9 INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, USELESS_DEPRECATED,
10};
11use rustc_span::{BytePos, FileName, RemapPathScopeComponents, Span, Symbol, sym};
12
13use crate::context::AcceptContext;
14use crate::diagnostics::{
15 InvalidAttrAtCrateLevel, InvalidTarget, InvalidTargetHelp, ItemFollowingInnerAttr,
16 UnsupportedAttributesInWhere,
17};
18use crate::target_checking::Policy::Allow;
19use crate::{AttributeParser, ShouldEmit};
20
21#[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 {
Self::AllowList(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"AllowList", &__self_0),
Self::AllowListWarnRest(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"AllowListWarnRest", &__self_0),
Self::ManuallyChecked =>
::core::fmt::Formatter::write_str(f, "ManuallyChecked"),
}
}
}Debug)]
22pub(crate) enum AllowedTargets<'a> {
23 AllowList(&'a [Policy]),
24 AllowListWarnRest(&'a [Policy]),
25 ManuallyChecked,
29}
30
31pub(crate) enum AllowedResult {
32 Allowed,
33 Warn,
34 Error,
35}
36
37impl AllowedTargets<'_> {
38 pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
39 match self {
40 AllowedTargets::AllowList(list) => {
41 if list.contains(&Policy::Allow(target))
42 || list.contains(&Policy::AllowSilent(target))
43 {
44 AllowedResult::Allowed
45 } else if list.contains(&Policy::Warn(target)) {
46 AllowedResult::Warn
47 } else {
48 AllowedResult::Error
49 }
50 }
51 AllowedTargets::AllowListWarnRest(list) => {
52 if list.contains(&Policy::Allow(target))
53 || list.contains(&Policy::AllowSilent(target))
54 {
55 AllowedResult::Allowed
56 } else if list.contains(&Policy::Error(target)) {
57 AllowedResult::Error
58 } else {
59 AllowedResult::Warn
60 }
61 }
62 AllowedTargets::ManuallyChecked => ::core::panicking::unreachable_default_msg()unreachable!(),
63 }
64 }
65
66 pub(crate) fn allowed_targets(&self) -> Vec<Target> {
67 match self {
68 AllowedTargets::AllowList(list) | AllowedTargets::AllowListWarnRest(list) => list,
69 AllowedTargets::ManuallyChecked => ::core::panicking::unreachable_default_msg()unreachable!(),
70 }
71 .iter()
72 .filter_map(|target| match target {
73 Policy::Allow(target) => Some(*target),
74 Policy::AllowSilent(_) | Policy::Warn(_) | 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 {
Self::Allow(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f, "Allow",
&__self_0),
Self::AllowSilent(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"AllowSilent", &__self_0),
Self::Warn(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f, "Warn",
&__self_0),
Self::Error(__self_0) =>
::core::fmt::Formatter::debug_struct_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::marker::StructuralPartialEq for Policy {
}
#[automatically_derived]impl ::core::cmp::PartialEq for Policy {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Allow(__self_0), Self::Allow(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::AllowSilent(__self_0), Self::AllowSilent(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Warn(__self_0), Self::Warn(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Error(__self_0), Self::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: &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(policies) | AllowedTargets::AllowListWarnRest(policies) = allowed_targets
117 && policies.iter().filter(|p| #[allow(non_exhaustive_omitted_patterns)] match p {
Allow(_) => true,
_ => false,
}matches!(p, Allow(_))).all(|p| *p == Allow(Target::Crate))
119 {
120 Self::check_crate_level(cx, allowed_targets);
121 return;
122 }
123
124 let result = allowed_targets.is_allowed(cx.target);
125 if #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Allowed => true,
_ => false,
}matches!(result, AllowedResult::Allowed) {
126 return;
127 }
128
129 let allowed_targets = allowed_targets.allowed_targets();
130 let (applied, only) = allowed_targets_applied(allowed_targets, cx.target, cx.features);
131 let is_diagnostic_attr = cx.attr_path.segments[0] == sym::diagnostic;
132 let is_lint_check_attr = [sym::allow, sym::expect, sym::deny, sym::warn, sym::forbid]
133 .contains(&cx.attr_path.segments[0]);
134
135 let span = if attribute_args.is_empty() {
136 cx.attr_path.span
139 } else {
140 cx.inner_span
147 };
148 let target_span = if span.ctxt() == cx.target_span.ctxt() && cx.target != Target::Crate {
149 cx.target_span.shrink_to_lo()
150 } else {
151 span
152 };
153
154 let diag = InvalidTarget {
155 span,
156 attr_span: cx.attr_span,
157 name: cx.attr_path.clone(),
158 target: cx.target.plural_name(),
159 target_span,
160 only: if only { "only " } else { "" },
161 applied: DiagArgValue::StrListSepByAnd(applied.into_iter().map(Cow::Owned).collect()),
162 attribute_args: attribute_args.to_string(),
163 help: Self::target_checking_help(attribute_args, cx),
164 previously_accepted: #[allow(non_exhaustive_omitted_patterns)] match result {
AllowedResult::Warn => true,
_ => false,
}matches!(result, AllowedResult::Warn)
165 && !is_diagnostic_attr
166 && !is_lint_check_attr,
167 on_macro_call: #[allow(non_exhaustive_omitted_patterns)] match cx.target {
Target::MacroCall => true,
_ => false,
}matches!(cx.target, Target::MacroCall),
168 };
169
170 match result {
171 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"),
172 AllowedResult::Warn => {
173 let lint = if cx.attr_path.segments[0] == sym::deprecated
174 && ![
175 Target::Closure,
176 Target::Expression,
177 Target::Statement,
178 Target::Arm,
179 Target::MacroCall,
180 ]
181 .contains(&cx.target)
182 {
183 USELESS_DEPRECATED
184 } else if is_diagnostic_attr {
185 MISPLACED_DIAGNOSTIC_ATTRIBUTES
186 } else {
187 UNUSED_ATTRIBUTES
188 };
189
190 let attr_span = cx.attr_span;
191 cx.emit_lint(lint, diag, attr_span);
192 }
193 AllowedResult::Error => {
194 cx.dcx().emit_err(diag);
195 }
196 }
197 }
198
199 fn target_checking_help(
200 attribute_args: &str,
201 cx: &AcceptContext<'_, '_>,
202 ) -> Option<InvalidTargetHelp> {
203 match &*cx.attr_path.segments {
204 [sym::link_name] if cx.target == Target::Static => {
205 let needs_unsafe_wrapper = #[allow(non_exhaustive_omitted_patterns)] match cx.attr_safety {
Safety::Default => true,
_ => false,
}matches!(cx.attr_safety, Safety::Default);
206
207 Some(InvalidTargetHelp::UseExportName {
208 unsafe_open: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_lo()),
209 name: cx.attr_path.span,
210 unsafe_close: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_hi()),
211 })
212 }
213 [sym::repr] if attribute_args == "(align(...))" => match cx.target {
214 Target::Fn | Target::Method(..) if cx.features().fn_align() => {
215 Some(InvalidTargetHelp::UseRustcAlign)
216 }
217 Target::Static if cx.features().static_align() => {
218 Some(InvalidTargetHelp::UseRustcAlignStatic)
219 }
220 _ => None,
221 },
222 _ => None,
223 }
224 }
225
226 pub(crate) fn check_crate_level(
227 cx: &mut AcceptContext<'_, 'sess>,
228 allowed_targets: &AllowedTargets<'_>,
229 ) {
230 let warn = match allowed_targets.is_allowed(cx.target) {
231 AllowedResult::Allowed => return,
232 AllowedResult::Warn => true,
233 AllowedResult::Error => false,
234 };
235
236 let name = cx.attr_path.to_string();
237 let is_used_as_inner = cx.attr_style == AttrStyle::Inner;
238 let target_span = cx.target_span;
239 let attr_span = cx.attr_span;
240
241 let (show_crate_root_help, crate_root_path) = is_used_as_inner
242 .then(|| cx.cx.sess.local_crate_source_file())
243 .flatten()
244 .filter(|src| {
245 !#[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!(
246 cx.cx.sess.source_map().span_to_filename(attr_span),
247 FileName::Real(ref name) if name == src
248 )
249 })
250 .map(|src| {
251 (true, src.path(RemapPathScopeComponents::DIAGNOSTICS).display().to_string())
252 })
253 .unwrap_or_default();
254
255 if name == "doc" {
256 let diag = crate::diagnostics::AttrCrateLevelOnly;
257 if warn {
258 cx.emit_lint(INVALID_DOC_ATTRIBUTES, diag, attr_span);
259 } else {
260 cx.emit_err(diag);
261 }
262 return;
263 }
264
265 let diag = crate::diagnostics::InvalidAttrStyle {
266 name,
267 is_used_as_inner,
268 target_span: (!is_used_as_inner).then_some(target_span),
269 target: cx.target.name(),
270 crate_root_path,
271 show_crate_root_help,
272 span: attr_span,
273 };
274 if warn {
275 cx.emit_lint(UNUSED_ATTRIBUTES, diag, attr_span);
276 } else {
277 cx.emit_err(diag);
278 }
279 }
280
281 pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
284 const ATTRS_TO_CHECK: &[Symbol] =
288 &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
289
290 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)) {
292 let span = attr.span;
293 let name = *name;
294
295 let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
296
297 let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
298 span,
299 pound_to_opening_bracket: span.until(inner_span),
300 name,
301 item,
302 });
303
304 self.dcx().try_steal_replace_and_emit_err(
305 attr.path.span,
306 StashKey::UndeterminedMacroResolution,
307 err,
308 );
309 }
310 }
311
312 fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
313 self.sess()
318 .source_map()
319 .span_to_source(span, |content, _, span_end| {
320 let mut source = &content[span_end..];
321 let initial_source_len = source.len();
322 let span = try {
323 loop {
324 let first = source.chars().next()?;
325
326 if first.is_whitespace() {
327 let split_idx = source.find(|c: char| !c.is_whitespace())?;
328 source = &source[split_idx..];
329 } else if source.starts_with("//") {
330 let line_idx = source.find('\n')?;
331 source = &source[line_idx + '\n'.len_utf8()..];
332 } else if source.starts_with("/*") {
333 let close_idx = source.find("*/")?;
335 source = &source[close_idx + "*/".len()..];
336 } else if first == '#' {
337 let close_idx = source.find(']')?;
341 source = &source[close_idx + ']'.len_utf8()..];
342 } else {
343 let lo = span_end + initial_source_len - source.len();
344 let last_line = source.split('\n').next().map(|s| s.trim_end())?;
345
346 let hi = lo + last_line.len();
347 let lo = BytePos(lo as u32);
348 let hi = BytePos(hi as u32);
349 let next_item_span = Span::new(lo, hi, span.ctxt(), None);
350
351 break next_item_span;
352 }
353 }
354 };
355
356 Ok(span)
357 })
358 .ok()
359 .flatten()
360 }
361
362 pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
363 &self,
364 attrs: impl IntoIterator<Item = &'attr Attribute>,
365 ) {
366 let spans = attrs
371 .into_iter()
372 .filter_map(|attr| {
373 match attr {
374 Attribute::Parsed(AttributeKind::DocComment { span, .. }) => Some(*span),
375 Attribute::Parsed(AttributeKind::Doc(attr)) => Some(attr.first_span),
377 Attribute::Parsed(_) => None,
379 Attribute::Tool(attr) | Attribute::DeriveHelper(attr) => Some(attr.span),
380 }
381 })
382 .collect::<Vec<_>>();
383 if !spans.is_empty() {
384 self.dcx()
385 .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
386 }
387 }
388}
389
390pub(crate) fn allowed_targets_applied(
393 mut allowed_targets: Vec<Target>,
394 target: Target,
395 features: Option<&Features>,
396) -> (Vec<String>, bool) {
397 if let Some(features) = features {
399 if !features.fn_delegation() {
400 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Delegation { .. } => true,
_ => false,
}matches!(t, Target::Delegation { .. }));
401 }
402 if !features.stmt_expr_attributes() {
403 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Expression | Target::Statement => true,
_ => false,
}matches!(t, Target::Expression | Target::Statement));
404 }
405 if !features.extern_types() {
406 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::ForeignTy => true,
_ => false,
}matches!(t, Target::ForeignTy));
407 }
408 }
409
410 const FUNCTION_LIKE: &[Target] = &[
414 Target::Fn,
415 Target::Closure,
416 Target::ForeignFn,
417 Target::Method(MethodKind::Inherent),
418 Target::Method(MethodKind::Trait { body: false }),
419 Target::Method(MethodKind::Trait { body: true }),
420 Target::Method(MethodKind::TraitImpl),
421 ];
422 const FUNCTION_WITH_BODY_LIKE: &[Target] = &[
423 Target::Fn,
424 Target::Closure,
425 Target::Method(MethodKind::Inherent),
426 Target::Method(MethodKind::Trait { body: true }),
427 Target::Method(MethodKind::TraitImpl),
428 ];
429 const METHOD_LIKE: &[Target] = &[
430 Target::Method(MethodKind::Inherent),
431 Target::Method(MethodKind::Trait { body: false }),
432 Target::Method(MethodKind::Trait { body: true }),
433 Target::Method(MethodKind::TraitImpl),
434 ];
435 const IMPL_LIKE: &[Target] =
436 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
437 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
438
439 let mut added_fake_targets = Vec::new();
440 filter_targets(
441 &mut allowed_targets,
442 FUNCTION_LIKE,
443 "functions",
444 target,
445 &mut added_fake_targets,
446 );
447 filter_targets(
448 &mut allowed_targets,
449 FUNCTION_WITH_BODY_LIKE,
450 "functions with a body",
451 target,
452 &mut added_fake_targets,
453 );
454 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
455 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
456 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
457
458 let mut target_strings: Vec<_> = added_fake_targets
459 .iter()
460 .copied()
461 .chain(allowed_targets.iter().map(|t| t.plural_name()))
462 .map(|i| i.to_string())
463 .collect();
464
465 target_strings.sort();
467 target_strings.dedup();
468
469 let only_target = target_strings.len() == 1;
471
472 (target_strings, only_target)
473}
474
475fn filter_targets(
476 allowed_targets: &mut Vec<Target>,
477 target_group: &'static [Target],
478 target_group_name: &'static str,
479 target: Target,
480 added_fake_targets: &mut Vec<&'static str>,
481) {
482 if target_group.contains(&target) {
483 return;
484 }
485 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
486 return;
487 }
488 allowed_targets.retain(|t| !target_group.contains(t));
489 added_fake_targets.push(target_group_name);
490}
491
492impl<'f, 'sess> AcceptContext<'f, 'sess> {
493 pub(crate) fn check_target(
494 &mut self,
495 attribute_args: &str,
496 allowed_targets: &AllowedTargets<'_>,
497 ) {
498 self.ignore_target_checks();
499 AttributeParser::check_target(allowed_targets, attribute_args, self);
500 }
501
502 pub(crate) fn ignore_target_checks(&mut self) {
503 #[cfg(debug_assertions)]
504 {
505 self.has_target_been_checked = true;
506 }
507 }
508}
509
510pub(crate) const ALL_TARGETS: &[Policy] = {
515 use Policy::Allow;
516 &[
517 Allow(Target::ExternCrate),
518 Allow(Target::Use),
519 Allow(Target::Static),
520 Allow(Target::Const),
521 Allow(Target::Fn),
522 Allow(Target::Closure),
523 Allow(Target::Mod),
524 Allow(Target::ForeignMod),
525 Allow(Target::GlobalAsm),
526 Allow(Target::TyAlias),
527 Allow(Target::Enum),
528 Allow(Target::Variant),
529 Allow(Target::Struct),
530 Allow(Target::Field),
531 Allow(Target::Union),
532 Allow(Target::Trait),
533 Allow(Target::TraitAlias),
534 Allow(Target::Impl { of_trait: false }),
535 Allow(Target::Impl { of_trait: true }),
536 Allow(Target::Expression),
537 Allow(Target::Statement),
538 Allow(Target::Arm),
539 Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: false })),
540 Allow(Target::AssocConst(AssocCtxt::Trait)),
541 Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: true })),
542 Allow(Target::Method(MethodKind::Inherent)),
543 Allow(Target::Method(MethodKind::Trait { body: false })),
544 Allow(Target::Method(MethodKind::Trait { body: true })),
545 Allow(Target::Method(MethodKind::TraitImpl)),
546 Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: false })),
547 Allow(Target::AssocTy(AssocCtxt::Trait)),
548 Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: true })),
549 Allow(Target::ForeignFn),
550 Allow(Target::ForeignStatic),
551 Allow(Target::ForeignTy),
552 Allow(Target::MacroDef),
553 Allow(Target::Param),
554 Allow(Target::PatField),
555 Allow(Target::ExprField),
556 Allow(Target::WherePredicate),
557 Allow(Target::MacroCall),
558 Allow(Target::Crate),
559 Allow(Target::Delegation { mac: false }),
560 Allow(Target::Delegation { mac: true }),
561 Allow(Target::ConstParam),
562 Allow(Target::LifetimeParam),
563 Allow(Target::TypeParam),
564 Allow(Target::Loop),
565 Allow(Target::ForLoop),
566 Allow(Target::While),
567 Allow(Target::Break),
568 ]
569};