1use std::cell::Cell;
9use std::collections::hash_map::Entry;
10
11use rustc_abi::{Align, ExternAbi, Size};
12use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
13use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
14use rustc_data_structures::fx::FxHashMap;
15use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
16use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
17use rustc_hir::def::DefKind;
18use rustc_hir::def_id::LocalModDefId;
19use rustc_hir::intravisit::{self, Visitor};
20use rustc_hir::{
21 self as hir, self, AssocItemKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem,
22 HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem,
23};
24use rustc_macros::LintDiagnostic;
25use rustc_middle::hir::nested_filter;
26use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
27use rustc_middle::query::Providers;
28use rustc_middle::traits::ObligationCause;
29use rustc_middle::ty::error::{ExpectedFound, TypeError};
30use rustc_middle::ty::{self, TyCtxt, TypingMode};
31use rustc_middle::{bug, span_bug};
32use rustc_session::config::CrateType;
33use rustc_session::lint::builtin::{
34 CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
35 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
36};
37use rustc_session::parse::feature_err;
38use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
39use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
40use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
41use rustc_trait_selection::traits::ObligationCtxt;
42use tracing::debug;
43
44use crate::{errors, fluent_generated as fluent};
45
46#[derive(LintDiagnostic)]
47#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)]
48struct DiagnosticOnUnimplementedOnlyForTraits;
49
50fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
51 match impl_item.kind {
52 hir::ImplItemKind::Const(..) => Target::AssocConst,
53 hir::ImplItemKind::Fn(..) => {
54 let parent_def_id = tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
55 let containing_item = tcx.hir_expect_item(parent_def_id);
56 let containing_impl_is_for_trait = match &containing_item.kind {
57 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
58 _ => bug!("parent of an ImplItem must be an Impl"),
59 };
60 if containing_impl_is_for_trait {
61 Target::Method(MethodKind::Trait { body: true })
62 } else {
63 Target::Method(MethodKind::Inherent)
64 }
65 }
66 hir::ImplItemKind::Type(..) => Target::AssocTy,
67 }
68}
69
70#[derive(Clone, Copy)]
71enum ItemLike<'tcx> {
72 Item(&'tcx Item<'tcx>),
73 ForeignItem,
74}
75
76#[derive(Copy, Clone)]
77pub(crate) enum ProcMacroKind {
78 FunctionLike,
79 Derive,
80 Attribute,
81}
82
83impl IntoDiagArg for ProcMacroKind {
84 fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
85 match self {
86 ProcMacroKind::Attribute => "attribute proc macro",
87 ProcMacroKind::Derive => "derive proc macro",
88 ProcMacroKind::FunctionLike => "function-like proc macro",
89 }
90 .into_diag_arg(&mut None)
91 }
92}
93
94struct CheckAttrVisitor<'tcx> {
95 tcx: TyCtxt<'tcx>,
96
97 abort: Cell<bool>,
99}
100
101impl<'tcx> CheckAttrVisitor<'tcx> {
102 fn dcx(&self) -> DiagCtxtHandle<'tcx> {
103 self.tcx.dcx()
104 }
105
106 fn check_attributes(
108 &self,
109 hir_id: HirId,
110 span: Span,
111 target: Target,
112 item: Option<ItemLike<'_>>,
113 ) {
114 let mut doc_aliases = FxHashMap::default();
115 let mut specified_inline = None;
116 let mut seen = FxHashMap::default();
117 let attrs = self.tcx.hir_attrs(hir_id);
118 for attr in attrs {
119 match attr {
120 Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
121 self.check_confusables(*first_span, target);
122 }
123 Attribute::Parsed(
124 AttributeKind::Stability { span, .. }
125 | AttributeKind::ConstStability { span, .. },
126 ) => self.check_stability_promotable(*span, target),
127 Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
128 .check_allow_internal_unstable(
129 hir_id,
130 syms.first().unwrap().1,
131 span,
132 target,
133 attrs,
134 ),
135 _ => {
136 match attr.path().as_slice() {
137 [sym::diagnostic, sym::do_not_recommend, ..] => {
138 self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
139 }
140 [sym::diagnostic, sym::on_unimplemented, ..] => {
141 self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
142 }
143 [sym::inline, ..] => self.check_inline(hir_id, attr, span, target),
144 [sym::coverage, ..] => self.check_coverage(attr, span, target),
145 [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
146 [sym::no_sanitize, ..] => {
147 self.check_no_sanitize(attr, span, target)
148 }
149 [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
150 [sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
151 [sym::target_feature, ..] => {
152 self.check_target_feature(hir_id, attr, span, target, attrs)
153 }
154 [sym::thread_local, ..] => self.check_thread_local(attr, span, target),
155 [sym::track_caller, ..] => {
156 self.check_track_caller(hir_id, attr.span(), attrs, span, target)
157 }
158 [sym::doc, ..] => self.check_doc_attrs(
159 attr,
160 hir_id,
161 target,
162 &mut specified_inline,
163 &mut doc_aliases,
164 ),
165 [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target),
166 [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target),
167 [sym::rustc_layout_scalar_valid_range_start, ..]
168 | [sym::rustc_layout_scalar_valid_range_end, ..] => {
169 self.check_rustc_layout_scalar_valid_range(attr, span, target)
170 }
171 [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target),
172 [sym::rustc_allow_const_fn_unstable, ..] => {
173 self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
174 }
175 [sym::rustc_std_internal_symbol, ..] => {
176 self.check_rustc_std_internal_symbol(attr, span, target)
177 }
178 [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs),
179 [sym::rustc_as_ptr, ..] => {
180 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
181 }
182 [sym::rustc_no_implicit_autorefs, ..] => {
183 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
184 }
185 [sym::rustc_never_returns_null_ptr, ..] => {
186 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
187 }
188 [sym::rustc_legacy_const_generics, ..] => {
189 self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item)
190 }
191 [sym::rustc_lint_query_instability, ..] => {
192 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
193 }
194 [sym::rustc_lint_untracked_query_information, ..] => {
195 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
196 }
197 [sym::rustc_lint_diagnostics, ..] => {
198 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
199 }
200 [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target),
201 [sym::rustc_lint_opt_deny_field_access, ..] => {
202 self.check_rustc_lint_opt_deny_field_access(attr, span, target)
203 }
204 [sym::rustc_clean, ..]
205 | [sym::rustc_dirty, ..]
206 | [sym::rustc_if_this_changed, ..]
207 | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr),
208 [sym::rustc_coinductive, ..]
209 | [sym::rustc_must_implement_one_of, ..]
210 | [sym::rustc_deny_explicit_impl, ..]
211 | [sym::rustc_do_not_implement_via_object, ..]
212 | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target),
213 [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
214 [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
215 [sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
216 [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr),
217 [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
218 [sym::rustc_allow_incoherent_impl, ..] => {
219 self.check_allow_incoherent_impl(attr, span, target)
220 }
221 [sym::rustc_has_incoherent_inherent_impls, ..] => {
222 self.check_has_incoherent_inherent_impls(attr, span, target)
223 }
224 [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
225 [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
226 [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
227 [sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
228 [sym::link, ..] => self.check_link(hir_id, attr, span, target),
229 [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
230 [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
231 [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target),
232 [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target),
233 [sym::macro_use, ..] | [sym::macro_escape, ..] => {
234 self.check_macro_use(hir_id, attr, target)
235 }
236 [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod),
237 [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
238 [sym::ignore, ..] | [sym::should_panic, ..] => {
239 self.check_generic_attr(hir_id, attr, target, Target::Fn)
240 }
241 [sym::automatically_derived, ..] => {
242 self.check_generic_attr(hir_id, attr, target, Target::Impl)
243 }
244 [sym::no_implicit_prelude, ..] => {
245 self.check_generic_attr(hir_id, attr, target, Target::Mod)
246 }
247 [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id),
248 [sym::proc_macro, ..] => {
249 self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
250 }
251 [sym::proc_macro_attribute, ..] => {
252 self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
253 }
254 [sym::proc_macro_derive, ..] => {
255 self.check_generic_attr(hir_id, attr, target, Target::Fn);
256 self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
257 }
258 [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
259 self.check_autodiff(hir_id, attr, span, target)
260 }
261 [sym::coroutine, ..] => {
262 self.check_coroutine(attr, target);
263 }
264 [sym::type_const, ..] => {
265 self.check_type_const(hir_id,attr, target);
266 }
267 [sym::linkage, ..] => self.check_linkage(attr, span, target),
268 [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs),
269 [
270 sym::allow
272 | sym::expect
273 | sym::warn
274 | sym::deny
275 | sym::forbid
276 | sym::cfg
277 | sym::cfg_attr
278 | sym::cfg_trace
279 | sym::cfg_attr_trace
280 | sym::export_stable | sym::cfi_encoding | sym::pointee | sym::omit_gdb_pretty_printer_section | sym::used | sym::repr | sym::instruction_set | sym::windows_subsystem | sym::patchable_function_entry | sym::deprecated_safe | sym::prelude_import
293 | sym::panic_handler
294 | sym::allow_internal_unsafe
295 | sym::fundamental
296 | sym::lang
297 | sym::needs_allocator
298 | sym::default_lib_allocator
299 | sym::custom_mir,
300 ..
301 ] => {}
302 [name, ..] => {
303 match BUILTIN_ATTRIBUTE_MAP.get(name) {
304 Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {}
306 Some(_) => {
307 if !name.as_str().starts_with("rustc_") {
311 span_bug!(
312 attr.span(),
313 "builtin attribute {name:?} not handled by `CheckAttrVisitor`"
314 )
315 }
316 }
317 None => (),
318 }
319 }
320 [] => unreachable!(),
321 }
322 }
323 }
324
325 let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
326
327 if hir_id != CRATE_HIR_ID {
328 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
329 attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
330 {
331 match attr.style() {
332 ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
333 UNUSED_ATTRIBUTES,
334 hir_id,
335 attr.span(),
336 errors::OuterCrateLevelAttr,
337 ),
338 ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
339 UNUSED_ATTRIBUTES,
340 hir_id,
341 attr.span(),
342 errors::InnerCrateLevelAttr,
343 ),
344 }
345 }
346 }
347
348 if let Some(BuiltinAttribute { duplicates, .. }) = builtin {
349 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
350 }
351
352 self.check_unused_attribute(hir_id, attr)
353 }
354
355 self.check_repr(attrs, span, target, item, hir_id);
356 self.check_used(attrs, target, span);
357 self.check_rustc_force_inline(hir_id, attrs, span, target);
358 }
359
360 fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
361 self.tcx.emit_node_span_lint(
362 UNUSED_ATTRIBUTES,
363 hir_id,
364 attr.span(),
365 errors::IgnoredAttrWithMacro { sym },
366 );
367 }
368
369 fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) {
370 self.tcx.emit_node_span_lint(
371 UNUSED_ATTRIBUTES,
372 hir_id,
373 attr_span,
374 errors::IgnoredAttr { sym },
375 );
376 }
377
378 fn check_do_not_recommend(
380 &self,
381 attr_span: Span,
382 hir_id: HirId,
383 target: Target,
384 attr: &Attribute,
385 item: Option<ItemLike<'_>>,
386 ) {
387 if !matches!(target, Target::Impl)
388 || matches!(
389 item,
390 Some(ItemLike::Item(hir::Item { kind: hir::ItemKind::Impl(_impl),.. }))
391 if _impl.of_trait.is_none()
392 )
393 {
394 self.tcx.emit_node_span_lint(
395 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
396 hir_id,
397 attr_span,
398 errors::IncorrectDoNotRecommendLocation,
399 );
400 }
401 if !attr.is_word() {
402 self.tcx.emit_node_span_lint(
403 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
404 hir_id,
405 attr_span,
406 errors::DoNotRecommendDoesNotExpectArgs,
407 );
408 }
409 }
410
411 fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
413 if !matches!(target, Target::Trait) {
414 self.tcx.emit_node_span_lint(
415 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
416 hir_id,
417 attr_span,
418 DiagnosticOnUnimplementedOnlyForTraits,
419 );
420 }
421 }
422
423 fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
425 match target {
426 Target::Fn
427 | Target::Closure
428 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {}
429 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
430 self.tcx.emit_node_span_lint(
431 UNUSED_ATTRIBUTES,
432 hir_id,
433 attr.span(),
434 errors::IgnoredInlineAttrFnProto,
435 )
436 }
437 Target::AssocConst => self.tcx.emit_node_span_lint(
442 UNUSED_ATTRIBUTES,
443 hir_id,
444 attr.span(),
445 errors::IgnoredInlineAttrConstants,
446 ),
447 Target::Field | Target::Arm | Target::MacroDef => {
449 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline")
450 }
451 _ => {
452 self.dcx().emit_err(errors::InlineNotFnOrClosure {
453 attr_span: attr.span(),
454 defn_span: span,
455 });
456 }
457 }
458
459 if let Some(did) = hir_id.as_owner()
461 && self.tcx.def_kind(did).has_codegen_attrs()
462 && !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never))
463 {
464 let attrs = self.tcx.codegen_fn_attrs(did);
465 if attrs.contains_extern_indicator() {
467 self.tcx.emit_node_span_lint(
468 UNUSED_ATTRIBUTES,
469 hir_id,
470 attr.span(),
471 errors::InlineIgnoredForExported {},
472 );
473 }
474 }
475 }
476
477 fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
480 let mut not_fn_impl_mod = None;
481 let mut no_body = None;
482
483 match target {
484 Target::Fn
485 | Target::Closure
486 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
487 | Target::Impl
488 | Target::Mod => return,
489
490 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
493 no_body = Some(target_span);
494 }
495
496 _ => {
497 not_fn_impl_mod = Some(target_span);
498 }
499 }
500
501 self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
502 attr_span: attr.span(),
503 not_fn_impl_mod,
504 no_body,
505 help: (),
506 });
507 }
508
509 fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
512 let is_valid = matches!(
513 target,
514 Target::Fn
515 | Target::Closure
516 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
517 );
518 if !is_valid {
519 self.dcx().emit_err(errors::OptimizeInvalidTarget {
520 attr_span: attr.span(),
521 defn_span: span,
522 on_crate: hir_id == CRATE_HIR_ID,
523 });
524 }
525 }
526
527 fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) {
528 if let Some(list) = attr.meta_item_list() {
529 for item in list.iter() {
530 let sym = item.name();
531 match sym {
532 Some(s @ sym::address | s @ sym::hwaddress) => {
533 let is_valid =
534 matches!(target, Target::Fn | Target::Method(..) | Target::Static);
535 if !is_valid {
536 self.dcx().emit_err(errors::NoSanitize {
537 attr_span: item.span(),
538 defn_span: span,
539 accepted_kind: "a function or static",
540 attr_str: s.as_str(),
541 });
542 }
543 }
544 _ => {
545 let is_valid = matches!(target, Target::Fn | Target::Method(..));
546 if !is_valid {
547 self.dcx().emit_err(errors::NoSanitize {
548 attr_span: item.span(),
549 defn_span: span,
550 accepted_kind: "a function",
551 attr_str: &match sym {
552 Some(name) => name.to_string(),
553 None => "...".to_string(),
554 },
555 });
556 }
557 }
558 }
559 }
560 }
561 }
562
563 fn check_generic_attr(
564 &self,
565 hir_id: HirId,
566 attr: &Attribute,
567 target: Target,
568 allowed_target: Target,
569 ) {
570 if target != allowed_target {
571 let path = attr.path();
572 let path: Vec<_> = path.iter().map(|s| s.as_str()).collect();
573 let attr_name = path.join("::");
574 self.tcx.emit_node_span_lint(
575 UNUSED_ATTRIBUTES,
576 hir_id,
577 attr.span(),
578 errors::OnlyHasEffectOn {
579 attr_name,
580 target_name: allowed_target.name().replace(' ', "_"),
581 },
582 );
583 }
584 }
585
586 fn check_naked(
588 &self,
589 hir_id: HirId,
590 attr: &Attribute,
591 span: Span,
592 target: Target,
593 attrs: &[Attribute],
594 ) {
595 const ALLOW_LIST: &[rustc_span::Symbol] = &[
605 sym::cfg_trace,
607 sym::cfg_attr_trace,
608 sym::test,
610 sym::ignore,
611 sym::should_panic,
612 sym::bench,
613 sym::allow,
615 sym::warn,
616 sym::deny,
617 sym::forbid,
618 sym::deprecated,
620 sym::must_use,
621 sym::export_name,
623 sym::link_section,
624 sym::linkage,
625 sym::no_mangle,
626 sym::naked,
627 sym::instruction_set,
628 sym::repr,
629 sym::rustc_std_internal_symbol,
630 sym::cold,
632 sym::doc,
634 ];
635
636 match target {
637 Target::Fn
638 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {
639 let fn_sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
640 let abi = fn_sig.header.abi;
641 if abi.is_rustic_abi() && !self.tcx.features().naked_functions_rustic_abi() {
642 feature_err(
643 &self.tcx.sess,
644 sym::naked_functions_rustic_abi,
645 fn_sig.span,
646 format!(
647 "`#[naked]` is currently unstable on `extern \"{}\"` functions",
648 abi.as_str()
649 ),
650 )
651 .emit();
652 }
653
654 for other_attr in attrs {
655 if other_attr.is_doc_comment() {
658 continue;
659 }
660
661 match other_attr {
664 Attribute::Parsed(
665 AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. },
666 ) => {
667 continue;
668 }
669 _ => {}
670 }
671
672 if other_attr.has_name(sym::target_feature) {
673 if !self.tcx.features().naked_functions_target_feature() {
674 feature_err(
675 &self.tcx.sess,
676 sym::naked_functions_target_feature,
677 other_attr.span(),
678 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
679 ).emit();
680
681 return;
682 } else {
683 continue;
684 }
685 }
686
687 if !other_attr.has_any_name(ALLOW_LIST)
688 && !matches!(other_attr.path().as_slice(), [sym::rustfmt, ..])
689 {
690 let path = other_attr.path();
691 let path: Vec<_> = path.iter().map(|s| s.as_str()).collect();
692 let other_attr_name = path.join("::");
693
694 self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
695 span: other_attr.span(),
696 naked_span: attr.span(),
697 attr: other_attr_name,
698 });
699
700 return;
701 }
702 }
703 }
704 _ => {
705 self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
706 attr_span: attr.span(),
707 defn_span: span,
708 on_crate: hir_id == CRATE_HIR_ID,
709 });
710 }
711 }
712 }
713
714 fn check_object_lifetime_default(&self, hir_id: HirId) {
716 let tcx = self.tcx;
717 if let Some(owner_id) = hir_id.as_owner()
718 && let Some(generics) = tcx.hir_get_generics(owner_id.def_id)
719 {
720 for p in generics.params {
721 let hir::GenericParamKind::Type { .. } = p.kind else { continue };
722 let default = tcx.object_lifetime_default(p.def_id);
723 let repr = match default {
724 ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
725 ObjectLifetimeDefault::Static => "'static".to_owned(),
726 ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
727 ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
728 };
729 tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr });
730 }
731 }
732 }
733
734 fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) {
736 match target {
737 Target::MacroDef => {}
738 _ => {
739 self.tcx.dcx().emit_err(errors::CollapseDebuginfo {
740 attr_span: attr.span(),
741 defn_span: span,
742 });
743 }
744 }
745 }
746
747 fn check_track_caller(
749 &self,
750 hir_id: HirId,
751 attr_span: Span,
752 attrs: &[Attribute],
753 span: Span,
754 target: Target,
755 ) {
756 match target {
757 Target::Fn => {
758 if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
761 && let Some(item) = hir::LangItem::from_name(lang_item)
762 && item.is_weak()
763 {
764 let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
765
766 self.dcx().emit_err(errors::LangItemWithTrackCaller {
767 attr_span,
768 name: lang_item,
769 sig_span: sig.span,
770 });
771 }
772 }
773 Target::Method(..) | Target::ForeignFn | Target::Closure => {}
774 Target::Field | Target::Arm | Target::MacroDef => {
779 for attr in attrs {
780 self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
781 }
782 }
783 _ => {
784 self.dcx().emit_err(errors::TrackedCallerWrongLocation {
785 attr_span,
786 defn_span: span,
787 on_crate: hir_id == CRATE_HIR_ID,
788 });
789 }
790 }
791 }
792
793 fn check_non_exhaustive(
795 &self,
796 hir_id: HirId,
797 attr: &Attribute,
798 span: Span,
799 target: Target,
800 item: Option<ItemLike<'_>>,
801 ) {
802 match target {
803 Target::Struct => {
804 if let Some(ItemLike::Item(hir::Item {
805 kind: hir::ItemKind::Struct(_, _, hir::VariantData::Struct { fields, .. }),
806 ..
807 })) = item
808 && !fields.is_empty()
809 && fields.iter().any(|f| f.default.is_some())
810 {
811 self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues {
812 attr_span: attr.span(),
813 defn_span: span,
814 });
815 }
816 }
817 Target::Enum | Target::Variant => {}
818 Target::Field | Target::Arm | Target::MacroDef => {
823 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
824 }
825 _ => {
826 self.dcx().emit_err(errors::NonExhaustiveWrongLocation {
827 attr_span: attr.span(),
828 defn_span: span,
829 });
830 }
831 }
832 }
833
834 fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
836 match target {
837 Target::Trait => {}
838 Target::Field | Target::Arm | Target::MacroDef => {
843 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
844 }
845 _ => {
846 self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
847 attr_span: attr.span(),
848 defn_span: span,
849 });
850 }
851 }
852 }
853
854 fn check_target_feature(
856 &self,
857 hir_id: HirId,
858 attr: &Attribute,
859 span: Span,
860 target: Target,
861 attrs: &[Attribute],
862 ) {
863 match target {
864 Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
865 | Target::Fn => {
866 if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
868 && !self.tcx.sess.target.is_like_wasm
871 && !self.tcx.sess.opts.actually_rustdoc
872 {
873 let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
874
875 self.dcx().emit_err(errors::LangItemWithTargetFeature {
876 attr_span: attr.span(),
877 name: lang_item,
878 sig_span: sig.span,
879 });
880 }
881 }
882 Target::Statement => {
885 self.tcx.emit_node_span_lint(
886 UNUSED_ATTRIBUTES,
887 hir_id,
888 attr.span(),
889 errors::TargetFeatureOnStatement,
890 );
891 }
892 Target::Field | Target::Arm | Target::MacroDef => {
897 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
898 }
899 _ => {
900 self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
901 attr_span: attr.span(),
902 defn_span: span,
903 on_crate: hir_id == CRATE_HIR_ID,
904 });
905 }
906 }
907 }
908
909 fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) {
911 match target {
912 Target::ForeignStatic | Target::Static => {}
913 _ => {
914 self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic {
915 attr_span: attr.span(),
916 defn_span: span,
917 });
918 }
919 }
920 }
921
922 fn doc_attr_str_error(&self, meta: &MetaItemInner, attr_name: &str) {
923 self.dcx().emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
924 }
925
926 fn check_doc_alias_value(
927 &self,
928 meta: &MetaItemInner,
929 doc_alias: Symbol,
930 hir_id: HirId,
931 target: Target,
932 is_list: bool,
933 aliases: &mut FxHashMap<String, Span>,
934 ) {
935 let tcx = self.tcx;
936 let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
937 let attr_str =
938 &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
939 if doc_alias == sym::empty {
940 tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str });
941 return;
942 }
943
944 let doc_alias_str = doc_alias.as_str();
945 if let Some(c) = doc_alias_str
946 .chars()
947 .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
948 {
949 tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
950 return;
951 }
952 if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
953 tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str });
954 return;
955 }
956
957 let span = meta.span();
958 if let Some(location) = match target {
959 Target::AssocTy => {
960 let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id;
961 let containing_item = self.tcx.hir_expect_item(parent_def_id);
962 if Target::from_item(containing_item) == Target::Impl {
963 Some("type alias in implementation block")
964 } else {
965 None
966 }
967 }
968 Target::AssocConst => {
969 let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id;
970 let containing_item = self.tcx.hir_expect_item(parent_def_id);
971 let err = "associated constant in trait implementation block";
973 match containing_item.kind {
974 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
975 _ => None,
976 }
977 }
978 Target::Param => return,
980 Target::Expression
981 | Target::Statement
982 | Target::Arm
983 | Target::ForeignMod
984 | Target::Closure
985 | Target::Impl
986 | Target::WherePredicate => Some(target.name()),
987 Target::ExternCrate
988 | Target::Use
989 | Target::Static
990 | Target::Const
991 | Target::Fn
992 | Target::Mod
993 | Target::GlobalAsm
994 | Target::TyAlias
995 | Target::Enum
996 | Target::Variant
997 | Target::Struct
998 | Target::Field
999 | Target::Union
1000 | Target::Trait
1001 | Target::TraitAlias
1002 | Target::Method(..)
1003 | Target::ForeignFn
1004 | Target::ForeignStatic
1005 | Target::ForeignTy
1006 | Target::GenericParam(..)
1007 | Target::MacroDef
1008 | Target::PatField
1009 | Target::ExprField => None,
1010 } {
1011 tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location });
1012 return;
1013 }
1014 if self.tcx.hir_opt_name(hir_id) == Some(doc_alias) {
1015 tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str });
1016 return;
1017 }
1018 if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
1019 self.tcx.emit_node_span_lint(
1020 UNUSED_ATTRIBUTES,
1021 hir_id,
1022 span,
1023 errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
1024 );
1025 }
1026 }
1027
1028 fn check_doc_alias(
1029 &self,
1030 meta: &MetaItemInner,
1031 hir_id: HirId,
1032 target: Target,
1033 aliases: &mut FxHashMap<String, Span>,
1034 ) {
1035 if let Some(values) = meta.meta_item_list() {
1036 for v in values {
1037 match v.lit() {
1038 Some(l) => match l.kind {
1039 LitKind::Str(s, _) => {
1040 self.check_doc_alias_value(v, s, hir_id, target, true, aliases);
1041 }
1042 _ => {
1043 self.tcx
1044 .dcx()
1045 .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
1046 }
1047 },
1048 None => {
1049 self.tcx
1050 .dcx()
1051 .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
1052 }
1053 }
1054 }
1055 } else if let Some(doc_alias) = meta.value_str() {
1056 self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
1057 } else {
1058 self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() });
1059 }
1060 }
1061
1062 fn check_doc_keyword(&self, meta: &MetaItemInner, hir_id: HirId) {
1063 fn is_doc_keyword(s: Symbol) -> bool {
1064 s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
1068 }
1069
1070 let doc_keyword = match meta.value_str() {
1071 Some(value) if value != sym::empty => value,
1072 _ => return self.doc_attr_str_error(meta, "keyword"),
1073 };
1074
1075 let item_kind = match self.tcx.hir_node(hir_id) {
1076 hir::Node::Item(item) => Some(&item.kind),
1077 _ => None,
1078 };
1079 match item_kind {
1080 Some(ItemKind::Mod(_, module)) => {
1081 if !module.item_ids.is_empty() {
1082 self.dcx().emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
1083 return;
1084 }
1085 }
1086 _ => {
1087 self.dcx().emit_err(errors::DocKeywordNotMod { span: meta.span() });
1088 return;
1089 }
1090 }
1091 if !is_doc_keyword(doc_keyword) {
1092 self.dcx().emit_err(errors::DocKeywordNotKeyword {
1093 span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
1094 keyword: doc_keyword,
1095 });
1096 }
1097 }
1098
1099 fn check_doc_fake_variadic(&self, meta: &MetaItemInner, hir_id: HirId) {
1100 let item_kind = match self.tcx.hir_node(hir_id) {
1101 hir::Node::Item(item) => Some(&item.kind),
1102 _ => None,
1103 };
1104 match item_kind {
1105 Some(ItemKind::Impl(i)) => {
1106 let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty)
1107 || if let Some(&[hir::GenericArg::Type(ty)]) = i
1108 .of_trait
1109 .as_ref()
1110 .and_then(|trait_ref| trait_ref.path.segments.last())
1111 .map(|last_segment| last_segment.args().args)
1112 {
1113 matches!(&ty.kind, hir::TyKind::Tup([_]))
1114 } else {
1115 false
1116 };
1117 if !is_valid {
1118 self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
1119 }
1120 }
1121 _ => {
1122 self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
1123 }
1124 }
1125 }
1126
1127 fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) {
1128 let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {
1129 self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
1130 return;
1131 };
1132 match item.kind {
1133 ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _)
1134 if generics.params.len() != 0 => {}
1135 ItemKind::Trait(_, _, _, generics, _, items)
1136 if generics.params.len() != 0
1137 || items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {}
1138 ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}
1139 _ => {
1140 self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
1141 }
1142 }
1143 }
1144
1145 fn check_doc_inline(
1155 &self,
1156 attr: &Attribute,
1157 meta: &MetaItemInner,
1158 hir_id: HirId,
1159 target: Target,
1160 specified_inline: &mut Option<(bool, Span)>,
1161 ) {
1162 match target {
1163 Target::Use | Target::ExternCrate => {
1164 let do_inline = meta.has_name(sym::inline);
1165 if let Some((prev_inline, prev_span)) = *specified_inline {
1166 if do_inline != prev_inline {
1167 let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
1168 spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first);
1169 spans.push_span_label(
1170 meta.span(),
1171 fluent::passes_doc_inline_conflict_second,
1172 );
1173 self.dcx().emit_err(errors::DocKeywordConflict { spans });
1174 }
1175 } else {
1176 *specified_inline = Some((do_inline, meta.span()));
1177 }
1178 }
1179 _ => {
1180 self.tcx.emit_node_span_lint(
1181 INVALID_DOC_ATTRIBUTES,
1182 hir_id,
1183 meta.span(),
1184 errors::DocInlineOnlyUse {
1185 attr_span: meta.span(),
1186 item_span: (attr.style() == AttrStyle::Outer)
1187 .then(|| self.tcx.hir_span(hir_id)),
1188 },
1189 );
1190 }
1191 }
1192 }
1193
1194 fn check_doc_masked(
1195 &self,
1196 attr: &Attribute,
1197 meta: &MetaItemInner,
1198 hir_id: HirId,
1199 target: Target,
1200 ) {
1201 if target != Target::ExternCrate {
1202 self.tcx.emit_node_span_lint(
1203 INVALID_DOC_ATTRIBUTES,
1204 hir_id,
1205 meta.span(),
1206 errors::DocMaskedOnlyExternCrate {
1207 attr_span: meta.span(),
1208 item_span: (attr.style() == AttrStyle::Outer)
1209 .then(|| self.tcx.hir_span(hir_id)),
1210 },
1211 );
1212 return;
1213 }
1214
1215 if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() {
1216 self.tcx.emit_node_span_lint(
1217 INVALID_DOC_ATTRIBUTES,
1218 hir_id,
1219 meta.span(),
1220 errors::DocMaskedNotExternCrateSelf {
1221 attr_span: meta.span(),
1222 item_span: (attr.style() == AttrStyle::Outer)
1223 .then(|| self.tcx.hir_span(hir_id)),
1224 },
1225 );
1226 }
1227 }
1228
1229 fn check_attr_not_crate_level(
1231 &self,
1232 meta: &MetaItemInner,
1233 hir_id: HirId,
1234 attr_name: &str,
1235 ) -> bool {
1236 if CRATE_HIR_ID == hir_id {
1237 self.dcx().emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
1238 return false;
1239 }
1240 true
1241 }
1242
1243 fn check_attr_crate_level(
1245 &self,
1246 attr: &Attribute,
1247 meta: &MetaItemInner,
1248 hir_id: HirId,
1249 ) -> bool {
1250 if hir_id != CRATE_HIR_ID {
1251 let bang_span = attr.span().lo() + BytePos(1);
1253 let sugg = (attr.style() == AttrStyle::Outer
1254 && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
1255 .then_some(errors::AttrCrateLevelOnlySugg {
1256 attr: attr.span().with_lo(bang_span).with_hi(bang_span),
1257 });
1258 self.tcx.emit_node_span_lint(
1259 INVALID_DOC_ATTRIBUTES,
1260 hir_id,
1261 meta.span(),
1262 errors::AttrCrateLevelOnly { sugg },
1263 );
1264 return false;
1265 }
1266 true
1267 }
1268
1269 fn check_test_attr(&self, meta: &MetaItemInner, hir_id: HirId) {
1272 if let Some(metas) = meta.meta_item_list() {
1273 for i_meta in metas {
1274 match (i_meta.name(), i_meta.meta_item()) {
1275 (Some(sym::attr | sym::no_crate_inject), _) => {}
1276 (_, Some(m)) => {
1277 self.tcx.emit_node_span_lint(
1278 INVALID_DOC_ATTRIBUTES,
1279 hir_id,
1280 i_meta.span(),
1281 errors::DocTestUnknown {
1282 path: rustc_ast_pretty::pprust::path_to_string(&m.path),
1283 },
1284 );
1285 }
1286 (_, None) => {
1287 self.tcx.emit_node_span_lint(
1288 INVALID_DOC_ATTRIBUTES,
1289 hir_id,
1290 i_meta.span(),
1291 errors::DocTestLiteral,
1292 );
1293 }
1294 }
1295 }
1296 } else {
1297 self.tcx.emit_node_span_lint(
1298 INVALID_DOC_ATTRIBUTES,
1299 hir_id,
1300 meta.span(),
1301 errors::DocTestTakesList,
1302 );
1303 }
1304 }
1305
1306 fn check_doc_cfg_hide(&self, meta: &MetaItemInner, hir_id: HirId) {
1309 if meta.meta_item_list().is_none() {
1310 self.tcx.emit_node_span_lint(
1311 INVALID_DOC_ATTRIBUTES,
1312 hir_id,
1313 meta.span(),
1314 errors::DocCfgHideTakesList,
1315 );
1316 }
1317 }
1318
1319 fn check_doc_attrs(
1326 &self,
1327 attr: &Attribute,
1328 hir_id: HirId,
1329 target: Target,
1330 specified_inline: &mut Option<(bool, Span)>,
1331 aliases: &mut FxHashMap<String, Span>,
1332 ) {
1333 if let Some(list) = attr.meta_item_list() {
1334 for meta in &list {
1335 if let Some(i_meta) = meta.meta_item() {
1336 match i_meta.name() {
1337 Some(sym::alias) => {
1338 if self.check_attr_not_crate_level(meta, hir_id, "alias") {
1339 self.check_doc_alias(meta, hir_id, target, aliases);
1340 }
1341 }
1342
1343 Some(sym::keyword) => {
1344 if self.check_attr_not_crate_level(meta, hir_id, "keyword") {
1345 self.check_doc_keyword(meta, hir_id);
1346 }
1347 }
1348
1349 Some(sym::fake_variadic) => {
1350 if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
1351 self.check_doc_fake_variadic(meta, hir_id);
1352 }
1353 }
1354
1355 Some(sym::search_unbox) => {
1356 if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
1357 self.check_doc_search_unbox(meta, hir_id);
1358 }
1359 }
1360
1361 Some(sym::test) => {
1362 if self.check_attr_crate_level(attr, meta, hir_id) {
1363 self.check_test_attr(meta, hir_id);
1364 }
1365 }
1366
1367 Some(
1368 sym::html_favicon_url
1369 | sym::html_logo_url
1370 | sym::html_playground_url
1371 | sym::issue_tracker_base_url
1372 | sym::html_root_url
1373 | sym::html_no_source,
1374 ) => {
1375 self.check_attr_crate_level(attr, meta, hir_id);
1376 }
1377
1378 Some(sym::cfg_hide) => {
1379 if self.check_attr_crate_level(attr, meta, hir_id) {
1380 self.check_doc_cfg_hide(meta, hir_id);
1381 }
1382 }
1383
1384 Some(sym::inline | sym::no_inline) => {
1385 self.check_doc_inline(attr, meta, hir_id, target, specified_inline)
1386 }
1387
1388 Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target),
1389
1390 Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
1391
1392 Some(sym::rust_logo) => {
1393 if self.check_attr_crate_level(attr, meta, hir_id)
1394 && !self.tcx.features().rustdoc_internals()
1395 {
1396 feature_err(
1397 &self.tcx.sess,
1398 sym::rustdoc_internals,
1399 meta.span(),
1400 fluent::passes_doc_rust_logo,
1401 )
1402 .emit();
1403 }
1404 }
1405
1406 _ => {
1407 let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
1408 if i_meta.has_name(sym::spotlight) {
1409 self.tcx.emit_node_span_lint(
1410 INVALID_DOC_ATTRIBUTES,
1411 hir_id,
1412 i_meta.span,
1413 errors::DocTestUnknownSpotlight { path, span: i_meta.span },
1414 );
1415 } else if i_meta.has_name(sym::include)
1416 && let Some(value) = i_meta.value_str()
1417 {
1418 let applicability = if list.len() == 1 {
1419 Applicability::MachineApplicable
1420 } else {
1421 Applicability::MaybeIncorrect
1422 };
1423 self.tcx.emit_node_span_lint(
1426 INVALID_DOC_ATTRIBUTES,
1427 hir_id,
1428 i_meta.span,
1429 errors::DocTestUnknownInclude {
1430 path,
1431 value: value.to_string(),
1432 inner: match attr.style() {
1433 AttrStyle::Inner => "!",
1434 AttrStyle::Outer => "",
1435 },
1436 sugg: (attr.span(), applicability),
1437 },
1438 );
1439 } else if i_meta.has_name(sym::passes)
1440 || i_meta.has_name(sym::no_default_passes)
1441 {
1442 self.tcx.emit_node_span_lint(
1443 INVALID_DOC_ATTRIBUTES,
1444 hir_id,
1445 i_meta.span,
1446 errors::DocTestUnknownPasses { path, span: i_meta.span },
1447 );
1448 } else if i_meta.has_name(sym::plugins) {
1449 self.tcx.emit_node_span_lint(
1450 INVALID_DOC_ATTRIBUTES,
1451 hir_id,
1452 i_meta.span,
1453 errors::DocTestUnknownPlugins { path, span: i_meta.span },
1454 );
1455 } else {
1456 self.tcx.emit_node_span_lint(
1457 INVALID_DOC_ATTRIBUTES,
1458 hir_id,
1459 i_meta.span,
1460 errors::DocTestUnknownAny { path },
1461 );
1462 }
1463 }
1464 }
1465 } else {
1466 self.tcx.emit_node_span_lint(
1467 INVALID_DOC_ATTRIBUTES,
1468 hir_id,
1469 meta.span(),
1470 errors::DocInvalid,
1471 );
1472 }
1473 }
1474 }
1475 }
1476
1477 fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) {
1479 match target {
1480 Target::Struct | Target::Enum | Target::TyAlias => {}
1481 _ => {
1482 self.dcx().emit_err(errors::PassByValue { attr_span: attr.span(), span });
1483 }
1484 }
1485 }
1486
1487 fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) {
1488 match target {
1489 Target::Method(MethodKind::Inherent) => {}
1490 _ => {
1491 self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span(), span });
1492 }
1493 }
1494 }
1495
1496 fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) {
1497 match target {
1498 Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {}
1499 _ => {
1500 self.tcx
1501 .dcx()
1502 .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span(), span });
1503 }
1504 }
1505 }
1506
1507 fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) {
1508 if target != Target::ForeignFn {
1509 self.dcx().emit_err(errors::FfiPureInvalidTarget { attr_span });
1510 return;
1511 }
1512 if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
1513 self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span });
1515 }
1516 }
1517
1518 fn check_ffi_const(&self, attr_span: Span, target: Target) {
1519 if target != Target::ForeignFn {
1520 self.dcx().emit_err(errors::FfiConstInvalidTarget { attr_span });
1521 }
1522 }
1523
1524 fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1526 if matches!(
1527 target,
1528 Target::Fn
1529 | Target::Enum
1530 | Target::Struct
1531 | Target::Union
1532 | Target::Method(MethodKind::Trait { body: false } | MethodKind::Inherent)
1533 | Target::ForeignFn
1534 | Target::Trait
1538 ) {
1539 return;
1540 }
1541
1542 if let Target::Method(MethodKind::Trait { body: true }) = target
1544 && let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id
1545 && let containing_item = self.tcx.hir_expect_item(parent_def_id)
1546 && let hir::ItemKind::Trait(..) = containing_item.kind
1547 {
1548 return;
1549 }
1550
1551 let article = match target {
1552 Target::ExternCrate
1553 | Target::Enum
1554 | Target::Impl
1555 | Target::Expression
1556 | Target::Arm
1557 | Target::AssocConst
1558 | Target::AssocTy => "an",
1559 _ => "a",
1560 };
1561
1562 self.tcx.emit_node_span_lint(
1563 UNUSED_ATTRIBUTES,
1564 hir_id,
1565 attr.span(),
1566 errors::MustUseNoEffect { article, target },
1567 );
1568 }
1569
1570 fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
1572 match target {
1573 Target::Struct | Target::Enum | Target::Union | Target::Trait => {}
1574 _ => {
1575 self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span(), span });
1576 }
1577 }
1578 }
1579
1580 fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
1582 if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
1583 && matches!(
1584 param.kind,
1585 hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }
1586 )
1587 && matches!(param.source, hir::GenericParamSource::Generics)
1588 && let parent_hir_id = self.tcx.parent_hir_id(hir_id)
1589 && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)
1590 && let hir::ItemKind::Impl(impl_) = item.kind
1591 && let Some(trait_) = impl_.of_trait
1592 && let Some(def_id) = trait_.trait_def_id()
1593 && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)
1594 {
1595 return;
1596 }
1597
1598 self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() });
1599 }
1600
1601 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1603 match target {
1604 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1605 Target::Field | Target::Arm | Target::MacroDef => {
1610 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1611 }
1612 _ => {
1613 self.tcx.emit_node_span_lint(
1616 UNUSED_ATTRIBUTES,
1617 hir_id,
1618 attr.span(),
1619 errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
1620 );
1621 }
1622 }
1623 }
1624
1625 fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1627 if target == Target::ForeignMod
1628 && let hir::Node::Item(item) = self.tcx.hir_node(hir_id)
1629 && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1630 && !matches!(abi, ExternAbi::Rust)
1631 {
1632 return;
1633 }
1634
1635 self.tcx.emit_node_span_lint(
1636 UNUSED_ATTRIBUTES,
1637 hir_id,
1638 attr.span(),
1639 errors::Link { span: (target != Target::ForeignMod).then_some(span) },
1640 );
1641 }
1642
1643 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1645 match target {
1646 Target::ForeignFn | Target::ForeignStatic => {}
1647 Target::Field | Target::Arm | Target::MacroDef => {
1652 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1653 }
1654 _ => {
1655 let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span());
1658 if let Some(s) = attr.value_str() {
1659 self.tcx.emit_node_span_lint(
1660 UNUSED_ATTRIBUTES,
1661 hir_id,
1662 attr.span(),
1663 errors::LinkName { span, attr_span, value: s.as_str() },
1664 );
1665 } else {
1666 self.tcx.emit_node_span_lint(
1667 UNUSED_ATTRIBUTES,
1668 hir_id,
1669 attr.span(),
1670 errors::LinkName { span, attr_span, value: "..." },
1671 );
1672 };
1673 }
1674 }
1675 }
1676
1677 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1679 match target {
1680 Target::ExternCrate => {}
1681 Target::Field | Target::Arm | Target::MacroDef => {
1686 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1687 }
1688 _ => {
1689 self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span });
1690 }
1691 }
1692 }
1693
1694 fn is_impl_item(&self, hir_id: HirId) -> bool {
1695 matches!(self.tcx.hir_node(hir_id), hir::Node::ImplItem(..))
1696 }
1697
1698 fn check_export_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1700 match target {
1701 Target::Static | Target::Fn => {}
1702 Target::Method(..) if self.is_impl_item(hir_id) => {}
1703 Target::Field | Target::Arm | Target::MacroDef => {
1708 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1709 }
1710 _ => {
1711 self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span });
1712 }
1713 }
1714 }
1715
1716 fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) {
1717 if target != Target::Struct {
1718 self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct {
1719 attr_span: attr.span(),
1720 span,
1721 });
1722 return;
1723 }
1724
1725 let Some(list) = attr.meta_item_list() else {
1726 return;
1727 };
1728
1729 if !matches!(&list[..], &[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) {
1730 self.tcx
1731 .dcx()
1732 .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span() });
1733 }
1734 }
1735
1736 fn check_rustc_legacy_const_generics(
1738 &self,
1739 hir_id: HirId,
1740 attr: &Attribute,
1741 span: Span,
1742 target: Target,
1743 item: Option<ItemLike<'_>>,
1744 ) {
1745 let is_function = matches!(target, Target::Fn);
1746 if !is_function {
1747 self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
1748 attr_span: attr.span(),
1749 defn_span: span,
1750 on_crate: hir_id == CRATE_HIR_ID,
1751 });
1752 return;
1753 }
1754
1755 let Some(list) = attr.meta_item_list() else {
1756 return;
1758 };
1759
1760 let Some(ItemLike::Item(Item {
1761 kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. },
1762 ..
1763 })) = item
1764 else {
1765 bug!("should be a function item");
1766 };
1767
1768 for param in generics.params {
1769 match param.kind {
1770 hir::GenericParamKind::Const { .. } => {}
1771 _ => {
1772 self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly {
1773 attr_span: attr.span(),
1774 param_span: param.span,
1775 });
1776 return;
1777 }
1778 }
1779 }
1780
1781 if list.len() != generics.params.len() {
1782 self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex {
1783 attr_span: attr.span(),
1784 generics_span: generics.span,
1785 });
1786 return;
1787 }
1788
1789 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1790 let mut invalid_args = vec![];
1791 for meta in list {
1792 if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
1793 if *val >= arg_count {
1794 let span = meta.span();
1795 self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexExceed {
1796 span,
1797 arg_count: arg_count as usize,
1798 });
1799 return;
1800 }
1801 } else {
1802 invalid_args.push(meta.span());
1803 }
1804 }
1805
1806 if !invalid_args.is_empty() {
1807 self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
1808 }
1809 }
1810
1811 fn check_applied_to_fn_or_method(
1814 &self,
1815 hir_id: HirId,
1816 attr: &Attribute,
1817 span: Span,
1818 target: Target,
1819 ) {
1820 let is_function = matches!(target, Target::Fn | Target::Method(..));
1821 if !is_function {
1822 self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
1823 attr_span: attr.span(),
1824 defn_span: span,
1825 on_crate: hir_id == CRATE_HIR_ID,
1826 });
1827 }
1828 }
1829
1830 fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) {
1832 match target {
1833 Target::Struct => {}
1834 _ => {
1835 self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span(), span });
1836 }
1837 }
1838 }
1839
1840 fn check_rustc_lint_opt_deny_field_access(&self, attr: &Attribute, span: Span, target: Target) {
1842 match target {
1843 Target::Field => {}
1844 _ => {
1845 self.tcx
1846 .dcx()
1847 .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span(), span });
1848 }
1849 }
1850 }
1851
1852 fn check_rustc_dirty_clean(&self, attr: &Attribute) {
1855 if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
1856 self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span() });
1857 }
1858 }
1859
1860 fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) {
1862 match target {
1863 Target::Trait => {}
1864 _ => {
1865 self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
1866 attr_span: attr.span(),
1867 defn_span: span,
1868 });
1869 }
1870 }
1871 }
1872
1873 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1875 match target {
1876 Target::Static | Target::Fn | Target::Method(..) => {}
1877 Target::Field | Target::Arm | Target::MacroDef => {
1882 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1883 }
1884 _ => {
1885 self.tcx.emit_node_span_lint(
1888 UNUSED_ATTRIBUTES,
1889 hir_id,
1890 attr.span(),
1891 errors::LinkSection { span },
1892 );
1893 }
1894 }
1895 }
1896
1897 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1899 match target {
1900 Target::Static | Target::Fn => {}
1901 Target::Method(..) if self.is_impl_item(hir_id) => {}
1902 Target::Field | Target::Arm | Target::MacroDef => {
1907 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1908 }
1909 Target::ForeignFn | Target::ForeignStatic => {
1913 let foreign_item_kind = match target {
1914 Target::ForeignFn => "function",
1915 Target::ForeignStatic => "static",
1916 _ => unreachable!(),
1917 };
1918 self.tcx.emit_node_span_lint(
1919 UNUSED_ATTRIBUTES,
1920 hir_id,
1921 attr.span(),
1922 errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind },
1923 );
1924 }
1925 _ => {
1926 self.tcx.emit_node_span_lint(
1929 UNUSED_ATTRIBUTES,
1930 hir_id,
1931 attr.span(),
1932 errors::NoMangle { span },
1933 );
1934 }
1935 }
1936 }
1937
1938 fn check_repr(
1940 &self,
1941 attrs: &[Attribute],
1942 span: Span,
1943 target: Target,
1944 item: Option<ItemLike<'_>>,
1945 hir_id: HirId,
1946 ) {
1947 let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]);
1953
1954 let mut int_reprs = 0;
1955 let mut is_explicit_rust = false;
1956 let mut is_c = false;
1957 let mut is_simd = false;
1958 let mut is_transparent = false;
1959
1960 for (repr, repr_span) in reprs {
1961 match repr {
1962 ReprAttr::ReprRust => {
1963 is_explicit_rust = true;
1964 match target {
1965 Target::Struct | Target::Union | Target::Enum => continue,
1966 _ => {
1967 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1968 hint_span: *repr_span,
1969 span,
1970 });
1971 }
1972 }
1973 }
1974 ReprAttr::ReprC => {
1975 is_c = true;
1976 match target {
1977 Target::Struct | Target::Union | Target::Enum => continue,
1978 _ => {
1979 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1980 hint_span: *repr_span,
1981 span,
1982 });
1983 }
1984 }
1985 }
1986 ReprAttr::ReprAlign(align) => {
1987 match target {
1988 Target::Struct | Target::Union | Target::Enum => {}
1989 Target::Fn | Target::Method(_) => {
1990 if !self.tcx.features().fn_align() {
1991 feature_err(
1992 &self.tcx.sess,
1993 sym::fn_align,
1994 *repr_span,
1995 fluent::passes_repr_align_function,
1996 )
1997 .emit();
1998 }
1999 }
2000 _ => {
2001 self.dcx().emit_err(
2002 errors::AttrApplication::StructEnumFunctionMethodUnion {
2003 hint_span: *repr_span,
2004 span,
2005 },
2006 );
2007 }
2008 }
2009
2010 self.check_align_value(*align, *repr_span);
2011 }
2012 ReprAttr::ReprPacked(_) => {
2013 if target != Target::Struct && target != Target::Union {
2014 self.dcx().emit_err(errors::AttrApplication::StructUnion {
2015 hint_span: *repr_span,
2016 span,
2017 });
2018 } else {
2019 continue;
2020 }
2021 }
2022 ReprAttr::ReprSimd => {
2023 is_simd = true;
2024 if target != Target::Struct {
2025 self.dcx().emit_err(errors::AttrApplication::Struct {
2026 hint_span: *repr_span,
2027 span,
2028 });
2029 } else {
2030 continue;
2031 }
2032 }
2033 ReprAttr::ReprTransparent => {
2034 is_transparent = true;
2035 match target {
2036 Target::Struct | Target::Union | Target::Enum => continue,
2037 _ => {
2038 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
2039 hint_span: *repr_span,
2040 span,
2041 });
2042 }
2043 }
2044 }
2045 ReprAttr::ReprInt(_) => {
2046 int_reprs += 1;
2047 if target != Target::Enum {
2048 self.dcx().emit_err(errors::AttrApplication::Enum {
2049 hint_span: *repr_span,
2050 span,
2051 });
2052 } else {
2053 continue;
2054 }
2055 }
2056 ReprAttr::ReprEmpty => {
2059 if item.is_some() {
2061 match target {
2062 Target::Struct | Target::Union | Target::Enum => continue,
2063 Target::Fn | Target::Method(_) => {
2064 feature_err(
2065 &self.tcx.sess,
2066 sym::fn_align,
2067 *repr_span,
2068 fluent::passes_repr_align_function,
2069 )
2070 .emit();
2071 }
2072 _ => {
2073 self.dcx().emit_err(
2074 errors::AttrApplication::StructEnumFunctionMethodUnion {
2075 hint_span: *repr_span,
2076 span,
2077 },
2078 );
2079 }
2080 }
2081 }
2082
2083 return;
2084 }
2085 };
2086 }
2087
2088 let hint_spans = reprs.iter().map(|(_, span)| *span);
2091
2092 if is_transparent && reprs.len() > 1 {
2094 let hint_spans = hint_spans.clone().collect();
2095 self.dcx().emit_err(errors::TransparentIncompatible {
2096 hint_spans,
2097 target: target.to_string(),
2098 });
2099 }
2100 if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {
2101 let hint_spans = hint_spans.clone().collect();
2102 self.dcx().emit_err(errors::ReprConflicting { hint_spans });
2103 }
2104 if (int_reprs > 1)
2106 || (is_simd && is_c)
2107 || (int_reprs == 1
2108 && is_c
2109 && item.is_some_and(|item| {
2110 if let ItemLike::Item(item) = item { is_c_like_enum(item) } else { false }
2111 }))
2112 {
2113 self.tcx.emit_node_span_lint(
2114 CONFLICTING_REPR_HINTS,
2115 hir_id,
2116 hint_spans.collect::<Vec<Span>>(),
2117 errors::ReprConflictingLint,
2118 );
2119 }
2120 }
2121
2122 fn check_align_value(&self, align: Align, span: Span) {
2123 if align.bytes() > 2_u64.pow(29) {
2124 self.dcx().span_delayed_bug(
2126 span,
2127 "alignment greater than 2^29 should be errored on elsewhere",
2128 );
2129 } else {
2130 let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
2135 if align.bytes() > max {
2136 self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max });
2137 }
2138 }
2139 }
2140
2141 fn check_used(&self, attrs: &[Attribute], target: Target, target_span: Span) {
2142 let mut used_linker_span = None;
2143 let mut used_compiler_span = None;
2144 for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
2145 if target != Target::Static {
2146 self.dcx().emit_err(errors::UsedStatic {
2147 attr_span: attr.span(),
2148 span: target_span,
2149 target: target.name(),
2150 });
2151 }
2152 let inner = attr.meta_item_list();
2153 match inner.as_deref() {
2154 Some([item]) if item.has_name(sym::linker) => {
2155 if used_linker_span.is_none() {
2156 used_linker_span = Some(attr.span());
2157 }
2158 }
2159 Some([item]) if item.has_name(sym::compiler) => {
2160 if used_compiler_span.is_none() {
2161 used_compiler_span = Some(attr.span());
2162 }
2163 }
2164 Some(_) => {
2165 }
2167 None => {
2168 if used_compiler_span.is_none() {
2170 used_compiler_span = Some(attr.span());
2171 }
2172 }
2173 }
2174 }
2175 if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
2176 self.tcx
2177 .dcx()
2178 .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
2179 }
2180 }
2181
2182 fn check_allow_internal_unstable(
2186 &self,
2187 hir_id: HirId,
2188 attr_span: Span,
2189 span: Span,
2190 target: Target,
2191 attrs: &[Attribute],
2192 ) {
2193 match target {
2194 Target::Fn => {
2195 for attr in attrs {
2196 if attr.is_proc_macro_attr() {
2197 return;
2199 }
2200 }
2201 }
2203 Target::MacroDef => return,
2205 Target::Field | Target::Arm => {
2210 self.inline_attr_str_error_without_macro_def(
2211 hir_id,
2212 attr_span,
2213 "allow_internal_unstable",
2214 );
2215 return;
2216 }
2217 _ => {}
2219 }
2220
2221 self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span });
2222 }
2223
2224 fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) {
2226 match target {
2231 Target::Mod => {}
2232 _ => {
2233 self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() });
2234 }
2235 }
2236 }
2237
2238 fn check_rustc_allow_const_fn_unstable(
2241 &self,
2242 hir_id: HirId,
2243 attr: &Attribute,
2244 span: Span,
2245 target: Target,
2246 ) {
2247 match target {
2248 Target::Fn | Target::Method(_)
2249 if self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) => {}
2250 Target::Field | Target::Arm | Target::MacroDef => {
2255 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable")
2256 }
2257 _ => {
2258 self.tcx
2259 .dcx()
2260 .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span(), span });
2261 }
2262 }
2263 }
2264
2265 fn check_rustc_std_internal_symbol(&self, attr: &Attribute, span: Span, target: Target) {
2266 match target {
2267 Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {}
2268 _ => {
2269 self.tcx
2270 .dcx()
2271 .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span(), span });
2272 }
2273 }
2274 }
2275
2276 fn check_stability_promotable(&self, span: Span, target: Target) {
2277 match target {
2278 Target::Expression => {
2279 self.dcx().emit_err(errors::StabilityPromotable { attr_span: span });
2280 }
2281 _ => {}
2282 }
2283 }
2284
2285 fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) {
2286 match target {
2287 Target::ForeignFn | Target::ForeignStatic => {}
2288 _ => {
2289 self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span() });
2290 }
2291 }
2292 }
2293
2294 fn check_confusables(&self, span: Span, target: Target) {
2295 if !matches!(target, Target::Method(MethodKind::Inherent)) {
2296 self.dcx().emit_err(errors::Confusables { attr_span: span });
2297 }
2298 }
2299
2300 fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
2301 match target {
2302 Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
2303 self.tcx.emit_node_span_lint(
2304 UNUSED_ATTRIBUTES,
2305 hir_id,
2306 attr.span(),
2307 errors::Deprecated,
2308 );
2309 }
2310 _ => {}
2311 }
2312 }
2313
2314 fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2315 let name = attr.name().unwrap();
2316 match target {
2317 Target::ExternCrate | Target::Mod => {}
2318 _ => {
2319 self.tcx.emit_node_span_lint(
2320 UNUSED_ATTRIBUTES,
2321 hir_id,
2322 attr.span(),
2323 errors::MacroUse { name },
2324 );
2325 }
2326 }
2327 }
2328
2329 fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2330 if target != Target::MacroDef {
2331 self.tcx.emit_node_span_lint(
2332 UNUSED_ATTRIBUTES,
2333 hir_id,
2334 attr.span(),
2335 errors::MacroExport::Normal,
2336 );
2337 } else if let Some(meta_item_list) = attr.meta_item_list()
2338 && !meta_item_list.is_empty()
2339 {
2340 if meta_item_list.len() > 1 {
2341 self.tcx.emit_node_span_lint(
2342 INVALID_MACRO_EXPORT_ARGUMENTS,
2343 hir_id,
2344 attr.span(),
2345 errors::MacroExport::TooManyItems,
2346 );
2347 } else if !meta_item_list[0].has_name(sym::local_inner_macros) {
2348 self.tcx.emit_node_span_lint(
2349 INVALID_MACRO_EXPORT_ARGUMENTS,
2350 hir_id,
2351 meta_item_list[0].span(),
2352 errors::MacroExport::InvalidArgument,
2353 );
2354 }
2355 } else {
2356 let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro();
2358 let is_decl_macro = !macro_definition.macro_rules;
2359
2360 if is_decl_macro {
2361 self.tcx.emit_node_span_lint(
2362 UNUSED_ATTRIBUTES,
2363 hir_id,
2364 attr.span(),
2365 errors::MacroExport::OnDeclMacro,
2366 );
2367 }
2368 }
2369 }
2370
2371 fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
2372 if let Attribute::Parsed(p) = attr {
2375 match p {
2376 AttributeKind::Repr(reprs) => {
2377 for (r, span) in reprs {
2378 if let ReprAttr::ReprEmpty = r {
2379 self.tcx.emit_node_span_lint(
2380 UNUSED_ATTRIBUTES,
2381 hir_id,
2382 *span,
2383 errors::Unused {
2384 attr_span: *span,
2385 note: errors::UnusedNote::EmptyList { name: sym::repr },
2386 },
2387 );
2388 }
2389 }
2390 return;
2391 }
2392 _ => {}
2393 }
2394 }
2395
2396 let note = if attr.has_any_name(&[
2398 sym::macro_use,
2399 sym::allow,
2400 sym::expect,
2401 sym::warn,
2402 sym::deny,
2403 sym::forbid,
2404 sym::feature,
2405 sym::target_feature,
2406 ]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
2407 {
2408 errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
2409 } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
2410 && let Some(meta) = attr.meta_item_list()
2411 && let [meta] = meta.as_slice()
2412 && let Some(item) = meta.meta_item()
2413 && let MetaItemKind::NameValue(_) = &item.kind
2414 && item.path == sym::reason
2415 {
2416 errors::UnusedNote::NoLints { name: attr.name().unwrap() }
2417 } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
2418 && let Some(meta) = attr.meta_item_list()
2419 && meta.iter().any(|meta| {
2420 meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
2421 })
2422 {
2423 if hir_id != CRATE_HIR_ID {
2424 match attr.style() {
2425 ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
2426 UNUSED_ATTRIBUTES,
2427 hir_id,
2428 attr.span(),
2429 errors::OuterCrateLevelAttr,
2430 ),
2431 ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
2432 UNUSED_ATTRIBUTES,
2433 hir_id,
2434 attr.span(),
2435 errors::InnerCrateLevelAttr,
2436 ),
2437 };
2438 return;
2439 } else {
2440 let never_needs_link = self
2441 .tcx
2442 .crate_types()
2443 .iter()
2444 .all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
2445 if never_needs_link {
2446 errors::UnusedNote::LinkerMessagesBinaryCrateOnly
2447 } else {
2448 return;
2449 }
2450 }
2451 } else if attr.has_name(sym::default_method_body_is_const) {
2452 errors::UnusedNote::DefaultMethodBodyConst
2453 } else {
2454 return;
2455 };
2456
2457 self.tcx.emit_node_span_lint(
2458 UNUSED_ATTRIBUTES,
2459 hir_id,
2460 attr.span(),
2461 errors::Unused { attr_span: attr.span(), note },
2462 );
2463 }
2464
2465 fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2469 if target != Target::Fn {
2470 return;
2471 }
2472
2473 let tcx = self.tcx;
2474 let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else {
2475 return;
2476 };
2477 let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else {
2478 return;
2479 };
2480
2481 let def_id = hir_id.expect_owner().def_id;
2482 let param_env = ty::ParamEnv::empty();
2483
2484 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
2485 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
2486
2487 let span = tcx.def_span(def_id);
2488 let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id());
2489 let sig = tcx.liberate_late_bound_regions(
2490 def_id.to_def_id(),
2491 tcx.fn_sig(def_id).instantiate(tcx, fresh_args),
2492 );
2493
2494 let mut cause = ObligationCause::misc(span, def_id);
2495 let sig = ocx.normalize(&cause, param_env, sig);
2496
2497 let errors = ocx.select_where_possible();
2499 if !errors.is_empty() {
2500 return;
2501 }
2502
2503 let expected_sig = tcx.mk_fn_sig(
2504 std::iter::repeat(token_stream).take(match kind {
2505 ProcMacroKind::Attribute => 2,
2506 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2507 }),
2508 token_stream,
2509 false,
2510 Safety::Safe,
2511 ExternAbi::Rust,
2512 );
2513
2514 if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) {
2515 let mut diag = tcx.dcx().create_err(errors::ProcMacroBadSig { span, kind });
2516
2517 let hir_sig = tcx.hir_fn_sig_by_hir_id(hir_id);
2518 if let Some(hir_sig) = hir_sig {
2519 #[allow(rustc::diagnostic_outside_of_impl)] match terr {
2521 TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {
2522 if let Some(ty) = hir_sig.decl.inputs.get(idx) {
2523 diag.span(ty.span);
2524 cause.span = ty.span;
2525 } else if idx == hir_sig.decl.inputs.len() {
2526 let span = hir_sig.decl.output.span();
2527 diag.span(span);
2528 cause.span = span;
2529 }
2530 }
2531 TypeError::ArgCount => {
2532 if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {
2533 diag.span(ty.span);
2534 cause.span = ty.span;
2535 }
2536 }
2537 TypeError::SafetyMismatch(_) => {
2538 }
2540 TypeError::AbiMismatch(_) => {
2541 }
2543 TypeError::VariadicMismatch(_) => {
2544 }
2546 _ => {}
2547 }
2548 }
2549
2550 infcx.err_ctxt().note_type_err(
2551 &mut diag,
2552 &cause,
2553 None,
2554 Some(param_env.and(ValuePairs::PolySigs(ExpectedFound {
2555 expected: ty::Binder::dummy(expected_sig),
2556 found: ty::Binder::dummy(sig),
2557 }))),
2558 terr,
2559 false,
2560 None,
2561 );
2562 diag.emit();
2563 self.abort.set(true);
2564 }
2565
2566 let errors = ocx.select_all_or_error();
2567 if !errors.is_empty() {
2568 infcx.err_ctxt().report_fulfillment_errors(errors);
2569 self.abort.set(true);
2570 }
2571 }
2572
2573 fn check_coroutine(&self, attr: &Attribute, target: Target) {
2574 match target {
2575 Target::Closure => return,
2576 _ => {
2577 self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span() });
2578 }
2579 }
2580 }
2581
2582 fn check_type_const(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2583 let tcx = self.tcx;
2584 if target == Target::AssocConst
2585 && let parent = tcx.parent(hir_id.expect_owner().to_def_id())
2586 && self.tcx.def_kind(parent) == DefKind::Trait
2587 {
2588 return;
2589 } else {
2590 self.dcx()
2591 .struct_span_err(
2592 attr.span(),
2593 "`#[type_const]` must only be applied to trait associated constants",
2594 )
2595 .emit();
2596 }
2597 }
2598
2599 fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) {
2600 match target {
2601 Target::Fn
2602 | Target::Method(..)
2603 | Target::Static
2604 | Target::ForeignStatic
2605 | Target::ForeignFn => {}
2606 _ => {
2607 self.dcx().emit_err(errors::Linkage { attr_span: attr.span(), span });
2608 }
2609 }
2610 }
2611
2612 fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
2613 if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
2614 .unwrap_or(false)
2615 {
2616 self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
2617 }
2618 }
2619
2620 fn check_rustc_force_inline(
2621 &self,
2622 hir_id: HirId,
2623 attrs: &[Attribute],
2624 span: Span,
2625 target: Target,
2626 ) {
2627 let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline));
2628 match (target, force_inline_attr) {
2629 (Target::Closure, None) => {
2630 let is_coro = matches!(
2631 self.tcx.hir_expect_expr(hir_id).kind,
2632 hir::ExprKind::Closure(hir::Closure {
2633 kind: hir::ClosureKind::Coroutine(..)
2634 | hir::ClosureKind::CoroutineClosure(..),
2635 ..
2636 })
2637 );
2638 let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id();
2639 let parent_span = self.tcx.def_span(parent_did);
2640 let parent_force_inline_attr =
2641 self.tcx.get_attr(parent_did, sym::rustc_force_inline);
2642 if let Some(attr) = parent_force_inline_attr
2643 && is_coro
2644 {
2645 self.dcx().emit_err(errors::RustcForceInlineCoro {
2646 attr_span: attr.span(),
2647 span: parent_span,
2648 });
2649 }
2650 }
2651 (Target::Fn, _) => (),
2652 (_, Some(attr)) => {
2653 self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span(), span });
2654 }
2655 (_, None) => (),
2656 }
2657 }
2658
2659 fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) {
2661 debug!("check_autodiff");
2662 match target {
2663 Target::Fn => {}
2664 _ => {
2665 self.dcx().emit_err(errors::AutoDiffAttr { attr_span: span });
2666 self.abort.set(true);
2667 }
2668 }
2669 }
2670}
2671
2672impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2673 type NestedFilter = nested_filter::OnlyBodies;
2674
2675 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
2676 self.tcx
2677 }
2678
2679 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2680 if let ItemKind::Macro(_, macro_def, _) = item.kind {
2684 let def_id = item.owner_id.to_def_id();
2685 if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
2686 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2687 }
2688 }
2689
2690 let target = Target::from_item(item);
2691 self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2692 intravisit::walk_item(self, item)
2693 }
2694
2695 fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) {
2696 const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg_trace, sym::cfg_attr_trace];
2701 let spans = self
2702 .tcx
2703 .hir_attrs(where_predicate.hir_id)
2704 .iter()
2705 .filter(|attr| !ATTRS_ALLOWED.iter().any(|&sym| attr.has_name(sym)))
2706 .map(|attr| attr.span())
2707 .collect::<Vec<_>>();
2708 if !spans.is_empty() {
2709 self.tcx.dcx().emit_err(errors::UnsupportedAttributesInWhere { span: spans.into() });
2710 }
2711 self.check_attributes(
2712 where_predicate.hir_id,
2713 where_predicate.span,
2714 Target::WherePredicate,
2715 None,
2716 );
2717 intravisit::walk_where_predicate(self, where_predicate)
2718 }
2719
2720 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2721 let target = Target::from_generic_param(generic_param);
2722 self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2723 intravisit::walk_generic_param(self, generic_param)
2724 }
2725
2726 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2727 let target = Target::from_trait_item(trait_item);
2728 self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2729 intravisit::walk_trait_item(self, trait_item)
2730 }
2731
2732 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2733 self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2734 intravisit::walk_field_def(self, struct_field);
2735 }
2736
2737 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2738 self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2739 intravisit::walk_arm(self, arm);
2740 }
2741
2742 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2743 let target = Target::from_foreign_item(f_item);
2744 self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));
2745 intravisit::walk_foreign_item(self, f_item)
2746 }
2747
2748 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2749 let target = target_from_impl_item(self.tcx, impl_item);
2750 self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2751 intravisit::walk_impl_item(self, impl_item)
2752 }
2753
2754 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2755 if let hir::StmtKind::Let(l) = stmt.kind {
2757 self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2758 }
2759 intravisit::walk_stmt(self, stmt)
2760 }
2761
2762 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2763 let target = match expr.kind {
2764 hir::ExprKind::Closure { .. } => Target::Closure,
2765 _ => Target::Expression,
2766 };
2767
2768 self.check_attributes(expr.hir_id, expr.span, target, None);
2769 intravisit::walk_expr(self, expr)
2770 }
2771
2772 fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
2773 self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
2774 intravisit::walk_expr_field(self, field)
2775 }
2776
2777 fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
2778 self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);
2779 intravisit::walk_variant(self, variant)
2780 }
2781
2782 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2783 self.check_attributes(param.hir_id, param.span, Target::Param, None);
2784
2785 intravisit::walk_param(self, param);
2786 }
2787
2788 fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
2789 self.check_attributes(field.hir_id, field.span, Target::PatField, None);
2790 intravisit::walk_pat_field(self, field);
2791 }
2792}
2793
2794fn is_c_like_enum(item: &Item<'_>) -> bool {
2795 if let ItemKind::Enum(_, _, ref def) = item.kind {
2796 for variant in def.variants {
2797 match variant.data {
2798 hir::VariantData::Unit(..) => { }
2799 _ => return false,
2800 }
2801 }
2802 true
2803 } else {
2804 false
2805 }
2806}
2807
2808fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2811 const ATTRS_TO_CHECK: &[Symbol] = &[
2815 sym::macro_export,
2816 sym::path,
2817 sym::automatically_derived,
2818 sym::rustc_main,
2819 sym::derive,
2820 sym::test,
2821 sym::test_case,
2822 sym::global_allocator,
2823 sym::bench,
2824 ];
2825
2826 for attr in attrs {
2827 let (span, name) = if let Some(a) =
2829 ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
2830 {
2831 (attr.span(), *a)
2832 } else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr {
2833 (r.first().unwrap().1, sym::repr)
2834 } else {
2835 continue;
2836 };
2837
2838 let item = tcx
2839 .hir_free_items()
2840 .map(|id| tcx.hir_item(id))
2841 .find(|item| !item.span.is_dummy()) .map(|item| errors::ItemFollowingInnerAttr {
2843 span: if let Some(ident) = item.kind.ident() { ident.span } else { item.span },
2844 kind: item.kind.descr(),
2845 });
2846 let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
2847 span,
2848 sugg_span: tcx
2849 .sess
2850 .source_map()
2851 .span_to_snippet(span)
2852 .ok()
2853 .filter(|src| src.starts_with("#!["))
2854 .map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))),
2855 name,
2856 item,
2857 });
2858
2859 if let Attribute::Unparsed(p) = attr {
2860 tcx.dcx().try_steal_replace_and_emit_err(
2861 p.path.span,
2862 StashKey::UndeterminedMacroResolution,
2863 err,
2864 );
2865 } else {
2866 err.emit();
2867 }
2868 }
2869}
2870
2871fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2872 let attrs = tcx.hir_attrs(item.hir_id());
2873
2874 for attr in attrs {
2875 if attr.has_name(sym::inline) {
2876 tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() });
2877 }
2878 }
2879}
2880
2881fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
2882 let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
2883 tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor);
2884 if module_def_id.to_local_def_id().is_top_level_module() {
2885 check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2886 check_invalid_crate_level_attr(tcx, tcx.hir_krate_attrs());
2887 }
2888 if check_attr_visitor.abort.get() {
2889 tcx.dcx().abort_if_errors()
2890 }
2891}
2892
2893pub(crate) fn provide(providers: &mut Providers) {
2894 *providers = Providers { check_mod_attrs, ..*providers };
2895}
2896
2897fn check_duplicates(
2898 tcx: TyCtxt<'_>,
2899 attr: &Attribute,
2900 hir_id: HirId,
2901 duplicates: AttributeDuplicates,
2902 seen: &mut FxHashMap<Symbol, Span>,
2903) {
2904 use AttributeDuplicates::*;
2905 if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2906 return;
2907 }
2908 let attr_name = attr.name().unwrap();
2909 match duplicates {
2910 DuplicatesOk => {}
2911 WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2912 match seen.entry(attr_name) {
2913 Entry::Occupied(mut entry) => {
2914 let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2915 let to_remove = entry.insert(attr.span());
2916 (to_remove, attr.span())
2917 } else {
2918 (attr.span(), *entry.get())
2919 };
2920 tcx.emit_node_span_lint(
2921 UNUSED_ATTRIBUTES,
2922 hir_id,
2923 this,
2924 errors::UnusedDuplicate {
2925 this,
2926 other,
2927 warning: matches!(
2928 duplicates,
2929 FutureWarnFollowing | FutureWarnPreceding
2930 ),
2931 },
2932 );
2933 }
2934 Entry::Vacant(entry) => {
2935 entry.insert(attr.span());
2936 }
2937 }
2938 }
2939 ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) {
2940 Entry::Occupied(mut entry) => {
2941 let (this, other) = if matches!(duplicates, ErrorPreceding) {
2942 let to_remove = entry.insert(attr.span());
2943 (to_remove, attr.span())
2944 } else {
2945 (attr.span(), *entry.get())
2946 };
2947 tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name });
2948 }
2949 Entry::Vacant(entry) => {
2950 entry.insert(attr.span());
2951 }
2952 },
2953 }
2954}
2955
2956fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool {
2957 matches!(&self_ty.kind, hir::TyKind::Tup([_]))
2958 || if let hir::TyKind::BareFn(bare_fn_ty) = &self_ty.kind {
2959 bare_fn_ty.decl.inputs.len() == 1
2960 } else {
2961 false
2962 }
2963 || (if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &self_ty.kind
2964 && let Some(&[hir::GenericArg::Type(ty)]) =
2965 path.segments.last().map(|last| last.args().args)
2966 {
2967 doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty())
2968 } else {
2969 false
2970 })
2971}