rustc_passes/
check_attr.rs

1// FIXME(jdonszelmann): should become rustc_attr_validation
2//! This module implements some validity checks for attributes.
3//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
4//! attached to items that actually support them and if there are
5//! conflicts between multiple such attributes attached to the same
6//! item.
7
8use std::cell::Cell;
9use std::collections::hash_map::Entry;
10use std::slice;
11
12use rustc_abi::{Align, ExternAbi, Size};
13use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast};
14use rustc_attr_parsing::{AttributeParser, Late};
15use rustc_data_structures::fx::FxHashMap;
16use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
17use rustc_feature::{
18    ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
19    BuiltinAttribute,
20};
21use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet};
22use rustc_hir::def::DefKind;
23use rustc_hir::def_id::LocalModDefId;
24use rustc_hir::intravisit::{self, Visitor};
25use rustc_hir::{
26    self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, Constness, FnSig, ForeignItem, HirId,
27    Item, ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target,
28    TraitItem, find_attr,
29};
30use rustc_macros::LintDiagnostic;
31use rustc_middle::hir::nested_filter;
32use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
33use rustc_middle::query::Providers;
34use rustc_middle::traits::ObligationCause;
35use rustc_middle::ty::error::{ExpectedFound, TypeError};
36use rustc_middle::ty::{self, TyCtxt, TypingMode};
37use rustc_middle::{bug, span_bug};
38use rustc_session::config::CrateType;
39use rustc_session::lint;
40use rustc_session::lint::builtin::{
41    CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES,
42    MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
43};
44use rustc_session::parse::feature_err;
45use rustc_span::edition::Edition;
46use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
47use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
48use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
49use rustc_trait_selection::traits::ObligationCtxt;
50use tracing::debug;
51
52use crate::{errors, fluent_generated as fluent};
53
54#[derive(LintDiagnostic)]
55#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)]
56struct DiagnosticOnUnimplementedOnlyForTraits;
57
58#[derive(LintDiagnostic)]
59#[diag(passes_diagnostic_diagnostic_on_const_only_for_trait_impls)]
60struct DiagnosticOnConstOnlyForTraitImpls;
61
62fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
63    match impl_item.kind {
64        hir::ImplItemKind::Const(..) => Target::AssocConst,
65        hir::ImplItemKind::Fn(..) => {
66            let parent_def_id = tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
67            let containing_item = tcx.hir_expect_item(parent_def_id);
68            let containing_impl_is_for_trait = match &containing_item.kind {
69                hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
70                _ => bug!("parent of an ImplItem must be an Impl"),
71            };
72            if containing_impl_is_for_trait {
73                Target::Method(MethodKind::Trait { body: true })
74            } else {
75                Target::Method(MethodKind::Inherent)
76            }
77        }
78        hir::ImplItemKind::Type(..) => Target::AssocTy,
79    }
80}
81
82#[derive(Clone, Copy)]
83enum ItemLike<'tcx> {
84    Item(&'tcx Item<'tcx>),
85    ForeignItem,
86}
87
88#[derive(Copy, Clone)]
89pub(crate) enum ProcMacroKind {
90    FunctionLike,
91    Derive,
92    Attribute,
93}
94
95impl IntoDiagArg for ProcMacroKind {
96    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
97        match self {
98            ProcMacroKind::Attribute => "attribute proc macro",
99            ProcMacroKind::Derive => "derive proc macro",
100            ProcMacroKind::FunctionLike => "function-like proc macro",
101        }
102        .into_diag_arg(&mut None)
103    }
104}
105
106#[derive(Clone, Copy)]
107enum DocFakeItemKind {
108    Attribute,
109    Keyword,
110}
111
112impl DocFakeItemKind {
113    fn name(self) -> &'static str {
114        match self {
115            Self::Attribute => "attribute",
116            Self::Keyword => "keyword",
117        }
118    }
119}
120
121struct CheckAttrVisitor<'tcx> {
122    tcx: TyCtxt<'tcx>,
123
124    // Whether or not this visitor should abort after finding errors
125    abort: Cell<bool>,
126}
127
128impl<'tcx> CheckAttrVisitor<'tcx> {
129    fn dcx(&self) -> DiagCtxtHandle<'tcx> {
130        self.tcx.dcx()
131    }
132
133    /// Checks any attribute.
134    fn check_attributes(
135        &self,
136        hir_id: HirId,
137        span: Span,
138        target: Target,
139        item: Option<ItemLike<'_>>,
140    ) {
141        let mut doc_aliases = FxHashMap::default();
142        let mut specified_inline = None;
143        let mut seen = FxHashMap::default();
144        let attrs = self.tcx.hir_attrs(hir_id);
145        for attr in attrs {
146            let mut style = None;
147            match attr {
148                Attribute::Parsed(AttributeKind::ProcMacro(_)) => {
149                    self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
150                }
151                Attribute::Parsed(AttributeKind::ProcMacroAttribute(_)) => {
152                    self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
153                }
154                Attribute::Parsed(AttributeKind::ProcMacroDerive { .. }) => {
155                    self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
156                }
157                Attribute::Parsed(
158                    AttributeKind::Stability {
159                        span: attr_span,
160                        stability: Stability { level, feature },
161                    }
162                    | AttributeKind::ConstStability {
163                        span: attr_span,
164                        stability: PartialConstStability { level, feature, .. },
165                    },
166                ) => self.check_stability(*attr_span, span, level, *feature),
167                Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below
168                Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {
169                    self.check_inline(hir_id, *attr_span, kind, target)
170                }
171                Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => {
172                    self.check_loop_match(hir_id, *attr_span, target)
173                }
174                Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {
175                    self.check_const_continue(hir_id, *attr_span, target)
176                }
177                Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span) | AttributeKind::AllowInternalUnstable(.., attr_span)) => {
178                    self.check_macro_only_attr(*attr_span, span, target, attrs)
179                }
180                Attribute::Parsed(AttributeKind::AllowConstFnUnstable(_, first_span)) => {
181                    self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target)
182                }
183                Attribute::Parsed(AttributeKind::Deprecation {span: attr_span, .. }) => {
184                    self.check_deprecated(hir_id, *attr_span, target)
185                }
186                Attribute::Parsed(AttributeKind::TargetFeature{ attr_span, ..}) => {
187                    self.check_target_feature(hir_id, *attr_span, target, attrs)
188                }
189                Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
190                    self.check_object_lifetime_default(hir_id);
191                }
192                &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => {
193                    self.check_rustc_pub_transparent(attr_span, span, attrs)
194                }
195                Attribute::Parsed(AttributeKind::Align { align, span: attr_span }) => {
196                    self.check_align(*align, *attr_span)
197                }
198                Attribute::Parsed(AttributeKind::Naked(..)) => {
199                    self.check_naked(hir_id, target)
200                }
201                Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {
202                    self.check_track_caller(hir_id, *attr_span, attrs, target)
203                }
204                Attribute::Parsed(AttributeKind::NonExhaustive(attr_span)) => {
205                    self.check_non_exhaustive(*attr_span, span, target, item)
206                }
207                &Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => {
208                    self.check_ffi_pure(attr_span, attrs)
209                }
210                Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
211                    self.check_may_dangle(hir_id, *attr_span)
212                }
213                &Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => {
214                    self.check_custom_mir(dialect, phase, attr_span)
215                }
216                &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => {
217                    self.check_sanitize(attr_span, on_set | off_set, span, target);
218                },
219                Attribute::Parsed(AttributeKind::Link(_, attr_span)) => {
220                    self.check_link(hir_id, *attr_span, span, target)
221                },
222                Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => {
223                    self.check_macro_export(hir_id, *span, target)
224                },
225                Attribute::Parsed(
226                    AttributeKind::BodyStability { .. }
227                    | AttributeKind::ConstStabilityIndirect
228                    | AttributeKind::MacroTransparency(_)
229                    | AttributeKind::Pointee(..)
230                    | AttributeKind::Dummy
231                    | AttributeKind::RustcBuiltinMacro { .. }
232                    | AttributeKind::Ignore { .. }
233                    | AttributeKind::Path(..)
234                    | AttributeKind::NoImplicitPrelude(..)
235                    | AttributeKind::AutomaticallyDerived(..)
236                    | AttributeKind::Marker(..)
237                    | AttributeKind::SkipDuringMethodDispatch { .. }
238                    | AttributeKind::Coinductive(..)
239                    | AttributeKind::DenyExplicitImpl(..)
240                    | AttributeKind::DoNotImplementViaObject(..)
241                    | AttributeKind::SpecializationTrait(..)
242                    | AttributeKind::UnsafeSpecializationMarker(..)
243                    | AttributeKind::ParenSugar(..)
244                    | AttributeKind::AllowIncoherentImpl(..)
245                    | AttributeKind::Confusables { .. }
246                    | AttributeKind::TypeConst{..}
247                    // `#[doc]` is actually a lot more than just doc comments, so is checked below
248                    | AttributeKind::DocComment {..}
249                    // handled below this loop and elsewhere
250                    | AttributeKind::Repr { .. }
251                    | AttributeKind::Cold(..)
252                    | AttributeKind::ExportName { .. }
253                    | AttributeKind::Fundamental
254                    | AttributeKind::Optimize(..)
255                    | AttributeKind::LinkSection { .. }
256                    | AttributeKind::MacroUse { .. }
257                    | AttributeKind::MacroEscape( .. )
258                    | AttributeKind::RustcLayoutScalarValidRangeStart(..)
259                    | AttributeKind::RustcLayoutScalarValidRangeEnd(..)
260                    | AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
261                    | AttributeKind::RustcShouldNotBeCalledOnConstItems(..)
262                    | AttributeKind::ExportStable
263                    | AttributeKind::FfiConst(..)
264                    | AttributeKind::UnstableFeatureBound(..)
265                    | AttributeKind::AsPtr(..)
266                    | AttributeKind::LinkName { .. }
267                    | AttributeKind::LinkOrdinal { .. }
268                    | AttributeKind::NoMangle(..)
269                    | AttributeKind::Used { .. }
270                    | AttributeKind::PassByValue (..)
271                    | AttributeKind::StdInternalSymbol (..)
272                    | AttributeKind::Coverage (..)
273                    | AttributeKind::ShouldPanic { .. }
274                    | AttributeKind::Coroutine(..)
275                    | AttributeKind::Linkage(..)
276                    | AttributeKind::MustUse { .. }
277                    | AttributeKind::CrateName { .. }
278                    | AttributeKind::RecursionLimit { .. }
279                    | AttributeKind::MoveSizeLimit { .. }
280                    | AttributeKind::TypeLengthLimit { .. }
281                    | AttributeKind::PatternComplexityLimit { .. }
282                    | AttributeKind::NoCore { .. }
283                    | AttributeKind::NoStd { .. }
284                    | AttributeKind::ObjcClass { .. }
285                    | AttributeKind::ObjcSelector { .. }
286                    | AttributeKind::RustcCoherenceIsCore(..)
287                    | AttributeKind::DebuggerVisualizer(..)
288                    | AttributeKind::RustcMain
289                    | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
290                    | AttributeKind::PinV2(..)
291                    | AttributeKind::WindowsSubsystem(..)
292                ) => { /* do nothing  */ }
293                Attribute::Unparsed(attr_item) => {
294                    style = Some(attr_item.style);
295                    match attr.path().as_slice() {
296                        [sym::diagnostic, sym::do_not_recommend, ..] => {
297                            self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
298                        }
299                        [sym::diagnostic, sym::on_unimplemented, ..] => {
300                            self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
301                        }
302                        [sym::diagnostic, sym::on_const, ..] => {
303                            self.check_diagnostic_on_const(attr.span(), hir_id, target, item)
304                        }
305                        [sym::thread_local, ..] => self.check_thread_local(attr, span, target),
306                        [sym::doc, ..] => self.check_doc_attrs(
307                            attr,
308                            attr.span(),
309                            attr_item.style,
310                            hir_id,
311                            target,
312                            &mut specified_inline,
313                            &mut doc_aliases,
314                        ),
315                        [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target),
316                        [sym::rustc_no_implicit_autorefs, ..] => {
317                            self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
318                        }
319                        [sym::rustc_never_returns_null_ptr, ..] => {
320                            self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
321                        }
322                        [sym::rustc_legacy_const_generics, ..] => {
323                            self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item)
324                        }
325                        [sym::rustc_lint_query_instability, ..] => {
326                            self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
327                        }
328                        [sym::rustc_lint_untracked_query_information, ..] => {
329                            self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
330                        }
331                        [sym::rustc_lint_diagnostics, ..] => {
332                            self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
333                        }
334                        [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target),
335                        [sym::rustc_lint_opt_deny_field_access, ..] => {
336                            self.check_rustc_lint_opt_deny_field_access(attr, span, target)
337                        }
338                        [sym::rustc_clean, ..]
339                        | [sym::rustc_dirty, ..]
340                        | [sym::rustc_if_this_changed, ..]
341                        | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr),
342                        [sym::rustc_must_implement_one_of, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target),
343                        [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
344                        [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
345                        [sym::rustc_has_incoherent_inherent_impls, ..] => {
346                            self.check_has_incoherent_inherent_impls(attr, span, target)
347                        }
348                        [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
349                            self.check_autodiff(hir_id, attr, span, target)
350                        }
351                        [
352                            // ok
353                            sym::allow
354                            | sym::expect
355                            | sym::warn
356                            | sym::deny
357                            | sym::forbid
358                            | sym::cfg
359                            | sym::cfg_attr
360                            | sym::cfg_trace
361                            | sym::cfg_attr_trace
362                            // need to be fixed
363                            | sym::cfi_encoding // FIXME(cfi_encoding)
364                            | sym::instruction_set // broken on stable!!!
365                            | sym::patchable_function_entry // FIXME(patchable_function_entry)
366                            | sym::deprecated_safe // FIXME(deprecated_safe)
367                            // internal
368                            | sym::prelude_import
369                            | sym::panic_handler
370                            | sym::lang
371                            | sym::needs_allocator
372                            | sym::default_lib_allocator,
373                            ..
374                        ] => {}
375                        [name, rest@..] => {
376                            match BUILTIN_ATTRIBUTE_MAP.get(name) {
377                                // checked below
378                                Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {}
379                                Some(_) => {
380                                    if rest.len() > 0 && AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(name)) {
381                                        // Check if we tried to use a builtin attribute as an attribute namespace, like `#[must_use::skip]`.
382                                        // This check is here to solve https://github.com/rust-lang/rust/issues/137590
383                                        // An error is already produced for this case elsewhere
384                                        continue
385                                    }
386
387                                    // FIXME: differentiate between unstable and internal attributes just
388                                    // like we do with features instead of just accepting `rustc_`
389                                    // attributes by name. That should allow trimming the above list, too.
390                                    if !name.as_str().starts_with("rustc_") {
391                                        span_bug!(
392                                            attr.span(),
393                                            "builtin attribute {name:?} not handled by `CheckAttrVisitor`"
394                                        )
395                                    }
396                                }
397                                None => (),
398                            }
399                        }
400                        [] => unreachable!(),
401                    }
402                }
403            }
404
405            if hir_id != CRATE_HIR_ID {
406                match attr {
407                    Attribute::Parsed(_) => { /* Already validated. */ }
408                    Attribute::Unparsed(attr) => {
409                        // FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by
410                        // the above
411                        if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
412                            attr.path
413                                .segments
414                                .first()
415                                .and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
416                        {
417                            match attr.style {
418                                ast::AttrStyle::Outer => {
419                                    let attr_span = attr.span;
420                                    let bang_position = self
421                                        .tcx
422                                        .sess
423                                        .source_map()
424                                        .span_until_char(attr_span, '[')
425                                        .shrink_to_hi();
426
427                                    self.tcx.emit_node_span_lint(
428                                        UNUSED_ATTRIBUTES,
429                                        hir_id,
430                                        attr.span,
431                                        errors::OuterCrateLevelAttr {
432                                            suggestion: errors::OuterCrateLevelAttrSuggestion {
433                                                bang_position,
434                                            },
435                                        },
436                                    )
437                                }
438                                ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
439                                    UNUSED_ATTRIBUTES,
440                                    hir_id,
441                                    attr.span,
442                                    errors::InnerCrateLevelAttr,
443                                ),
444                            }
445                        }
446                    }
447                }
448            }
449
450            if let Attribute::Unparsed(unparsed_attr) = attr
451                && let Some(BuiltinAttribute { duplicates, .. }) =
452                    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
453            {
454                check_duplicates(
455                    self.tcx,
456                    unparsed_attr.span,
457                    attr,
458                    hir_id,
459                    *duplicates,
460                    &mut seen,
461                );
462            }
463
464            self.check_unused_attribute(hir_id, attr, style)
465        }
466
467        self.check_repr(attrs, span, target, item, hir_id);
468        self.check_rustc_force_inline(hir_id, attrs, target);
469        self.check_mix_no_mangle_export(hir_id, attrs);
470    }
471
472    fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) {
473        self.tcx.emit_node_span_lint(
474            UNUSED_ATTRIBUTES,
475            hir_id,
476            attr_span,
477            errors::IgnoredAttrWithMacro { sym },
478        );
479    }
480
481    /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl and that it has no
482    /// arguments.
483    fn check_do_not_recommend(
484        &self,
485        attr_span: Span,
486        hir_id: HirId,
487        target: Target,
488        attr: &Attribute,
489        item: Option<ItemLike<'_>>,
490    ) {
491        if !matches!(target, Target::Impl { .. })
492            || matches!(
493                item,
494                Some(ItemLike::Item(hir::Item {  kind: hir::ItemKind::Impl(_impl),.. }))
495                    if _impl.of_trait.is_none()
496            )
497        {
498            self.tcx.emit_node_span_lint(
499                MISPLACED_DIAGNOSTIC_ATTRIBUTES,
500                hir_id,
501                attr_span,
502                errors::IncorrectDoNotRecommendLocation,
503            );
504        }
505        if !attr.is_word() {
506            self.tcx.emit_node_span_lint(
507                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
508                hir_id,
509                attr_span,
510                errors::DoNotRecommendDoesNotExpectArgs,
511            );
512        }
513    }
514
515    /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
516    fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
517        if !matches!(target, Target::Trait) {
518            self.tcx.emit_node_span_lint(
519                MISPLACED_DIAGNOSTIC_ATTRIBUTES,
520                hir_id,
521                attr_span,
522                DiagnosticOnUnimplementedOnlyForTraits,
523            );
524        }
525    }
526
527    /// Checks if `#[diagnostic::on_const]` is applied to a trait impl
528    fn check_diagnostic_on_const(
529        &self,
530        attr_span: Span,
531        hir_id: HirId,
532        target: Target,
533        item: Option<ItemLike<'_>>,
534    ) {
535        if matches!(target, Target::Impl { of_trait: true }) {
536            match item.unwrap() {
537                ItemLike::Item(it) => match it.expect_impl().constness {
538                    Constness::Const => {}
539                    Constness::NotConst => return,
540                },
541                ItemLike::ForeignItem => {}
542            }
543        }
544        self.tcx.emit_node_span_lint(
545            MISPLACED_DIAGNOSTIC_ATTRIBUTES,
546            hir_id,
547            attr_span,
548            DiagnosticOnConstOnlyForTraitImpls,
549        );
550    }
551
552    /// Checks if an `#[inline]` is applied to a function or a closure.
553    fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) {
554        match target {
555            Target::Fn
556            | Target::Closure
557            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {
558                // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.
559                if let Some(did) = hir_id.as_owner()
560                    && self.tcx.def_kind(did).has_codegen_attrs()
561                    && kind != &InlineAttr::Never
562                {
563                    let attrs = self.tcx.codegen_fn_attrs(did);
564                    // Not checking naked as `#[inline]` is forbidden for naked functions anyways.
565                    if attrs.contains_extern_indicator() {
566                        self.tcx.emit_node_span_lint(
567                            UNUSED_ATTRIBUTES,
568                            hir_id,
569                            attr_span,
570                            errors::InlineIgnoredForExported {},
571                        );
572                    }
573                }
574            }
575            _ => {}
576        }
577    }
578
579    /// Checks that the `#[sanitize(..)]` attribute is applied to a
580    /// function/closure/method, or to an impl block or module.
581    fn check_sanitize(
582        &self,
583        attr_span: Span,
584        set: SanitizerSet,
585        target_span: Span,
586        target: Target,
587    ) {
588        let mut not_fn_impl_mod = None;
589        let mut no_body = None;
590
591        match target {
592            Target::Fn
593            | Target::Closure
594            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
595            | Target::Impl { .. }
596            | Target::Mod => return,
597            Target::Static
598                // if we mask out the address bits, i.e. *only* address was set,
599                // we allow it
600                if set & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
601                    == SanitizerSet::empty() =>
602            {
603                return;
604            }
605
606            // These are "functions", but they aren't allowed because they don't
607            // have a body, so the usual explanation would be confusing.
608            Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
609                no_body = Some(target_span);
610            }
611
612            _ => {
613                not_fn_impl_mod = Some(target_span);
614            }
615        }
616
617        self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {
618            attr_span,
619            not_fn_impl_mod,
620            no_body,
621            help: (),
622        });
623    }
624
625    /// Checks if `#[naked]` is applied to a function definition.
626    fn check_naked(&self, hir_id: HirId, target: Target) {
627        match target {
628            Target::Fn
629            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {
630                let fn_sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
631                let abi = fn_sig.header.abi;
632                if abi.is_rustic_abi() && !self.tcx.features().naked_functions_rustic_abi() {
633                    feature_err(
634                        &self.tcx.sess,
635                        sym::naked_functions_rustic_abi,
636                        fn_sig.span,
637                        format!(
638                            "`#[naked]` is currently unstable on `extern \"{}\"` functions",
639                            abi.as_str()
640                        ),
641                    )
642                    .emit();
643                }
644            }
645            _ => {}
646        }
647    }
648
649    /// Debugging aid for `object_lifetime_default` query.
650    fn check_object_lifetime_default(&self, hir_id: HirId) {
651        let tcx = self.tcx;
652        if let Some(owner_id) = hir_id.as_owner()
653            && let Some(generics) = tcx.hir_get_generics(owner_id.def_id)
654        {
655            for p in generics.params {
656                let hir::GenericParamKind::Type { .. } = p.kind else { continue };
657                let default = tcx.object_lifetime_default(p.def_id);
658                let repr = match default {
659                    ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
660                    ObjectLifetimeDefault::Static => "'static".to_owned(),
661                    ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
662                    ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
663                };
664                tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr });
665            }
666        }
667    }
668    /// Checks if `#[collapse_debuginfo]` is applied to a macro.
669    fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) {
670        match target {
671            Target::MacroDef => {}
672            _ => {
673                self.tcx.dcx().emit_err(errors::CollapseDebuginfo {
674                    attr_span: attr.span(),
675                    defn_span: span,
676                });
677            }
678        }
679    }
680
681    /// Checks if a `#[track_caller]` is applied to a function.
682    fn check_track_caller(
683        &self,
684        hir_id: HirId,
685        attr_span: Span,
686        attrs: &[Attribute],
687        target: Target,
688    ) {
689        match target {
690            Target::Fn => {
691                // `#[track_caller]` is not valid on weak lang items because they are called via
692                // `extern` declarations and `#[track_caller]` would alter their ABI.
693                if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
694                    && let Some(item) = hir::LangItem::from_name(lang_item)
695                    && item.is_weak()
696                {
697                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
698
699                    self.dcx().emit_err(errors::LangItemWithTrackCaller {
700                        attr_span,
701                        name: lang_item,
702                        sig_span: sig.span,
703                    });
704                }
705            }
706            _ => {}
707        }
708    }
709
710    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
711    fn check_non_exhaustive(
712        &self,
713        attr_span: Span,
714        span: Span,
715        target: Target,
716        item: Option<ItemLike<'_>>,
717    ) {
718        match target {
719            Target::Struct => {
720                if let Some(ItemLike::Item(hir::Item {
721                    kind: hir::ItemKind::Struct(_, _, hir::VariantData::Struct { fields, .. }),
722                    ..
723                })) = item
724                    && !fields.is_empty()
725                    && fields.iter().any(|f| f.default.is_some())
726                {
727                    self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues {
728                        attr_span,
729                        defn_span: span,
730                    });
731                }
732            }
733            _ => {}
734        }
735    }
736
737    /// Checks if the `#[target_feature]` attribute on `item` is valid.
738    fn check_target_feature(
739        &self,
740        hir_id: HirId,
741        attr_span: Span,
742        target: Target,
743        attrs: &[Attribute],
744    ) {
745        match target {
746            Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
747            | Target::Fn => {
748                // `#[target_feature]` is not allowed in lang items.
749                if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
750                    // Calling functions with `#[target_feature]` is
751                    // not unsafe on WASM, see #84988
752                    && !self.tcx.sess.target.is_like_wasm
753                    && !self.tcx.sess.opts.actually_rustdoc
754                {
755                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
756
757                    self.dcx().emit_err(errors::LangItemWithTargetFeature {
758                        attr_span,
759                        name: lang_item,
760                        sig_span: sig.span,
761                    });
762                }
763            }
764            _ => {}
765        }
766    }
767
768    /// Checks if the `#[thread_local]` attribute on `item` is valid.
769    fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) {
770        match target {
771            Target::ForeignStatic | Target::Static => {}
772            _ => {
773                self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic {
774                    attr_span: attr.span(),
775                    defn_span: span,
776                });
777            }
778        }
779    }
780
781    fn doc_attr_str_error(&self, meta: &MetaItemInner, attr_name: &str) {
782        self.dcx().emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
783    }
784
785    fn check_doc_alias_value(
786        &self,
787        meta: &MetaItemInner,
788        doc_alias: Symbol,
789        hir_id: HirId,
790        target: Target,
791        is_list: bool,
792        aliases: &mut FxHashMap<String, Span>,
793    ) {
794        let tcx = self.tcx;
795        let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
796        let attr_str =
797            &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
798        if doc_alias == sym::empty {
799            tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str });
800            return;
801        }
802
803        let doc_alias_str = doc_alias.as_str();
804        if let Some(c) = doc_alias_str
805            .chars()
806            .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
807        {
808            tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
809            return;
810        }
811        if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
812            tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str });
813            return;
814        }
815
816        let span = meta.span();
817        if let Some(location) = match target {
818            Target::AssocTy => {
819                if let DefKind::Impl { .. } =
820                    self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id))
821                {
822                    Some("type alias in implementation block")
823                } else {
824                    None
825                }
826            }
827            Target::AssocConst => {
828                let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id;
829                let containing_item = self.tcx.hir_expect_item(parent_def_id);
830                // We can't link to trait impl's consts.
831                let err = "associated constant in trait implementation block";
832                match containing_item.kind {
833                    ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
834                    _ => None,
835                }
836            }
837            // we check the validity of params elsewhere
838            Target::Param => return,
839            Target::Expression
840            | Target::Statement
841            | Target::Arm
842            | Target::ForeignMod
843            | Target::Closure
844            | Target::Impl { .. }
845            | Target::WherePredicate => Some(target.name()),
846            Target::ExternCrate
847            | Target::Use
848            | Target::Static
849            | Target::Const
850            | Target::Fn
851            | Target::Mod
852            | Target::GlobalAsm
853            | Target::TyAlias
854            | Target::Enum
855            | Target::Variant
856            | Target::Struct
857            | Target::Field
858            | Target::Union
859            | Target::Trait
860            | Target::TraitAlias
861            | Target::Method(..)
862            | Target::ForeignFn
863            | Target::ForeignStatic
864            | Target::ForeignTy
865            | Target::GenericParam { .. }
866            | Target::MacroDef
867            | Target::PatField
868            | Target::ExprField
869            | Target::Crate
870            | Target::MacroCall
871            | Target::Delegation { .. } => None,
872        } {
873            tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location });
874            return;
875        }
876        if self.tcx.hir_opt_name(hir_id) == Some(doc_alias) {
877            tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str });
878            return;
879        }
880        if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
881            self.tcx.emit_node_span_lint(
882                UNUSED_ATTRIBUTES,
883                hir_id,
884                span,
885                errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
886            );
887        }
888    }
889
890    fn check_doc_alias(
891        &self,
892        meta: &MetaItemInner,
893        hir_id: HirId,
894        target: Target,
895        aliases: &mut FxHashMap<String, Span>,
896    ) {
897        if let Some(values) = meta.meta_item_list() {
898            for v in values {
899                match v.lit() {
900                    Some(l) => match l.kind {
901                        LitKind::Str(s, _) => {
902                            self.check_doc_alias_value(v, s, hir_id, target, true, aliases);
903                        }
904                        _ => {
905                            self.tcx
906                                .dcx()
907                                .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
908                        }
909                    },
910                    None => {
911                        self.tcx
912                            .dcx()
913                            .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
914                    }
915                }
916            }
917        } else if let Some(doc_alias) = meta.value_str() {
918            self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
919        } else {
920            self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() });
921        }
922    }
923
924    fn check_doc_keyword_and_attribute(
925        &self,
926        meta: &MetaItemInner,
927        hir_id: HirId,
928        attr_kind: DocFakeItemKind,
929    ) {
930        fn is_doc_keyword(s: Symbol) -> bool {
931            // FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
932            // can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
933            // `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
934            s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
935        }
936
937        // FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
938        fn is_builtin_attr(s: Symbol) -> bool {
939            rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&s)
940        }
941
942        let value = match meta.value_str() {
943            Some(value) if value != sym::empty => value,
944            _ => return self.doc_attr_str_error(meta, attr_kind.name()),
945        };
946
947        let item_kind = match self.tcx.hir_node(hir_id) {
948            hir::Node::Item(item) => Some(&item.kind),
949            _ => None,
950        };
951        match item_kind {
952            Some(ItemKind::Mod(_, module)) => {
953                if !module.item_ids.is_empty() {
954                    self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod {
955                        span: meta.span(),
956                        attr_name: attr_kind.name(),
957                    });
958                    return;
959                }
960            }
961            _ => {
962                self.dcx().emit_err(errors::DocKeywordAttributeNotMod {
963                    span: meta.span(),
964                    attr_name: attr_kind.name(),
965                });
966                return;
967            }
968        }
969        match attr_kind {
970            DocFakeItemKind::Keyword => {
971                if !is_doc_keyword(value) {
972                    self.dcx().emit_err(errors::DocKeywordNotKeyword {
973                        span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
974                        keyword: value,
975                    });
976                }
977            }
978            DocFakeItemKind::Attribute => {
979                if !is_builtin_attr(value) {
980                    self.dcx().emit_err(errors::DocAttributeNotAttribute {
981                        span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
982                        attribute: value,
983                    });
984                }
985            }
986        }
987    }
988
989    fn check_doc_fake_variadic(&self, meta: &MetaItemInner, hir_id: HirId) {
990        let item_kind = match self.tcx.hir_node(hir_id) {
991            hir::Node::Item(item) => Some(&item.kind),
992            _ => None,
993        };
994        match item_kind {
995            Some(ItemKind::Impl(i)) => {
996                let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty)
997                    || if let Some(&[hir::GenericArg::Type(ty)]) = i
998                        .of_trait
999                        .and_then(|of_trait| of_trait.trait_ref.path.segments.last())
1000                        .map(|last_segment| last_segment.args().args)
1001                    {
1002                        matches!(&ty.kind, hir::TyKind::Tup([_]))
1003                    } else {
1004                        false
1005                    };
1006                if !is_valid {
1007                    self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
1008                }
1009            }
1010            _ => {
1011                self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
1012            }
1013        }
1014    }
1015
1016    fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) {
1017        let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {
1018            self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
1019            return;
1020        };
1021        match item.kind {
1022            ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _)
1023                if generics.params.len() != 0 => {}
1024            ItemKind::Trait(_, _, _, _, generics, _, items)
1025                if generics.params.len() != 0
1026                    || items.iter().any(|item| {
1027                        matches!(self.tcx.def_kind(item.owner_id), DefKind::AssocTy)
1028                    }) => {}
1029            ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}
1030            _ => {
1031                self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
1032            }
1033        }
1034    }
1035
1036    /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes.
1037    ///
1038    /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
1039    /// if there are conflicting attributes for one item.
1040    ///
1041    /// `specified_inline` is used to keep track of whether we have
1042    /// already seen an inlining attribute for this item.
1043    /// If so, `specified_inline` holds the value and the span of
1044    /// the first `inline`/`no_inline` attribute.
1045    fn check_doc_inline(
1046        &self,
1047        style: AttrStyle,
1048        meta: &MetaItemInner,
1049        hir_id: HirId,
1050        target: Target,
1051        specified_inline: &mut Option<(bool, Span)>,
1052    ) {
1053        match target {
1054            Target::Use | Target::ExternCrate => {
1055                let do_inline = meta.has_name(sym::inline);
1056                if let Some((prev_inline, prev_span)) = *specified_inline {
1057                    if do_inline != prev_inline {
1058                        let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
1059                        spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first);
1060                        spans.push_span_label(
1061                            meta.span(),
1062                            fluent::passes_doc_inline_conflict_second,
1063                        );
1064                        self.dcx().emit_err(errors::DocKeywordConflict { spans });
1065                    }
1066                } else {
1067                    *specified_inline = Some((do_inline, meta.span()));
1068                }
1069            }
1070            _ => {
1071                self.tcx.emit_node_span_lint(
1072                    INVALID_DOC_ATTRIBUTES,
1073                    hir_id,
1074                    meta.span(),
1075                    errors::DocInlineOnlyUse {
1076                        attr_span: meta.span(),
1077                        item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
1078                    },
1079                );
1080            }
1081        }
1082    }
1083
1084    fn check_doc_masked(
1085        &self,
1086        style: AttrStyle,
1087        meta: &MetaItemInner,
1088        hir_id: HirId,
1089        target: Target,
1090    ) {
1091        if target != Target::ExternCrate {
1092            self.tcx.emit_node_span_lint(
1093                INVALID_DOC_ATTRIBUTES,
1094                hir_id,
1095                meta.span(),
1096                errors::DocMaskedOnlyExternCrate {
1097                    attr_span: meta.span(),
1098                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
1099                },
1100            );
1101            return;
1102        }
1103
1104        if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() {
1105            self.tcx.emit_node_span_lint(
1106                INVALID_DOC_ATTRIBUTES,
1107                hir_id,
1108                meta.span(),
1109                errors::DocMaskedNotExternCrateSelf {
1110                    attr_span: meta.span(),
1111                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
1112                },
1113            );
1114        }
1115    }
1116
1117    /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
1118    fn check_attr_not_crate_level(
1119        &self,
1120        meta: &MetaItemInner,
1121        hir_id: HirId,
1122        attr_name: &str,
1123    ) -> bool {
1124        if CRATE_HIR_ID == hir_id {
1125            self.dcx().emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
1126            return false;
1127        }
1128        true
1129    }
1130
1131    /// Checks that an attribute is used at the crate level. Returns `true` if valid.
1132    fn check_attr_crate_level(
1133        &self,
1134        attr_span: Span,
1135        style: AttrStyle,
1136        meta: &MetaItemInner,
1137        hir_id: HirId,
1138    ) -> bool {
1139        if hir_id != CRATE_HIR_ID {
1140            // insert a bang between `#` and `[...`
1141            let bang_span = attr_span.lo() + BytePos(1);
1142            let sugg = (style == AttrStyle::Outer
1143                && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
1144                .then_some(errors::AttrCrateLevelOnlySugg {
1145                    attr: attr_span.with_lo(bang_span).with_hi(bang_span),
1146                });
1147            self.tcx.emit_node_span_lint(
1148                INVALID_DOC_ATTRIBUTES,
1149                hir_id,
1150                meta.span(),
1151                errors::AttrCrateLevelOnly { sugg },
1152            );
1153            return false;
1154        }
1155        true
1156    }
1157
1158    fn check_doc_attr_string_value(&self, meta: &MetaItemInner, hir_id: HirId) {
1159        if meta.value_str().is_none() {
1160            self.tcx.emit_node_span_lint(
1161                INVALID_DOC_ATTRIBUTES,
1162                hir_id,
1163                meta.span(),
1164                errors::DocAttrExpectsString { attr_name: meta.name().unwrap() },
1165            );
1166        }
1167    }
1168
1169    fn check_doc_attr_no_value(&self, meta: &MetaItemInner, hir_id: HirId) {
1170        if !meta.is_word() {
1171            self.tcx.emit_node_span_lint(
1172                INVALID_DOC_ATTRIBUTES,
1173                hir_id,
1174                meta.span(),
1175                errors::DocAttrExpectsNoValue { attr_name: meta.name().unwrap() },
1176            );
1177        }
1178    }
1179
1180    /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
1181    fn check_test_attr(
1182        &self,
1183        attr_span: Span,
1184        style: AttrStyle,
1185        meta: &MetaItemInner,
1186        hir_id: HirId,
1187    ) {
1188        if let Some(metas) = meta.meta_item_list() {
1189            for i_meta in metas {
1190                match (i_meta.name(), i_meta.meta_item()) {
1191                    (Some(sym::attr), _) => {
1192                        // Allowed everywhere like `#[doc]`
1193                    }
1194                    (Some(sym::no_crate_inject), _) => {
1195                        self.check_attr_crate_level(attr_span, style, meta, hir_id);
1196                    }
1197                    (_, Some(m)) => {
1198                        self.tcx.emit_node_span_lint(
1199                            INVALID_DOC_ATTRIBUTES,
1200                            hir_id,
1201                            i_meta.span(),
1202                            errors::DocTestUnknown {
1203                                path: rustc_ast_pretty::pprust::path_to_string(&m.path),
1204                            },
1205                        );
1206                    }
1207                    (_, None) => {
1208                        self.tcx.emit_node_span_lint(
1209                            INVALID_DOC_ATTRIBUTES,
1210                            hir_id,
1211                            i_meta.span(),
1212                            errors::DocTestLiteral,
1213                        );
1214                    }
1215                }
1216            }
1217        } else {
1218            self.tcx.emit_node_span_lint(
1219                INVALID_DOC_ATTRIBUTES,
1220                hir_id,
1221                meta.span(),
1222                errors::DocTestTakesList,
1223            );
1224        }
1225    }
1226
1227    /// Check that the `#![doc(auto_cfg)]` attribute has the expected input.
1228    fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) {
1229        match &meta.kind {
1230            MetaItemKind::Word => {}
1231            MetaItemKind::NameValue(lit) => {
1232                if !matches!(lit.kind, LitKind::Bool(_)) {
1233                    self.tcx.emit_node_span_lint(
1234                        INVALID_DOC_ATTRIBUTES,
1235                        hir_id,
1236                        meta.span,
1237                        errors::DocAutoCfgWrongLiteral,
1238                    );
1239                }
1240            }
1241            MetaItemKind::List(list) => {
1242                for item in list {
1243                    let Some(attr_name @ (sym::hide | sym::show)) = item.name() else {
1244                        self.tcx.emit_node_span_lint(
1245                            INVALID_DOC_ATTRIBUTES,
1246                            hir_id,
1247                            meta.span,
1248                            errors::DocAutoCfgExpectsHideOrShow,
1249                        );
1250                        continue;
1251                    };
1252                    if let Some(list) = item.meta_item_list() {
1253                        for item in list {
1254                            let valid = item.meta_item().is_some_and(|meta| {
1255                                meta.path.segments.len() == 1
1256                                    && matches!(
1257                                        &meta.kind,
1258                                        MetaItemKind::Word | MetaItemKind::NameValue(_)
1259                                    )
1260                            });
1261                            if !valid {
1262                                self.tcx.emit_node_span_lint(
1263                                    INVALID_DOC_ATTRIBUTES,
1264                                    hir_id,
1265                                    item.span(),
1266                                    errors::DocAutoCfgHideShowUnexpectedItem { attr_name },
1267                                );
1268                            }
1269                        }
1270                    } else {
1271                        self.tcx.emit_node_span_lint(
1272                            INVALID_DOC_ATTRIBUTES,
1273                            hir_id,
1274                            meta.span,
1275                            errors::DocAutoCfgHideShowExpectsList { attr_name },
1276                        );
1277                    }
1278                }
1279            }
1280        }
1281    }
1282
1283    /// Runs various checks on `#[doc]` attributes.
1284    ///
1285    /// `specified_inline` should be initialized to `None` and kept for the scope
1286    /// of one item. Read the documentation of [`check_doc_inline`] for more information.
1287    ///
1288    /// [`check_doc_inline`]: Self::check_doc_inline
1289    fn check_doc_attrs(
1290        &self,
1291        attr: &Attribute,
1292        attr_span: Span,
1293        style: AttrStyle,
1294        hir_id: HirId,
1295        target: Target,
1296        specified_inline: &mut Option<(bool, Span)>,
1297        aliases: &mut FxHashMap<String, Span>,
1298    ) {
1299        if let Some(list) = attr.meta_item_list() {
1300            for meta in &list {
1301                if let Some(i_meta) = meta.meta_item() {
1302                    match i_meta.name() {
1303                        Some(sym::alias) => {
1304                            if self.check_attr_not_crate_level(meta, hir_id, "alias") {
1305                                self.check_doc_alias(meta, hir_id, target, aliases);
1306                            }
1307                        }
1308
1309                        Some(sym::keyword) => {
1310                            if self.check_attr_not_crate_level(meta, hir_id, "keyword") {
1311                                self.check_doc_keyword_and_attribute(
1312                                    meta,
1313                                    hir_id,
1314                                    DocFakeItemKind::Keyword,
1315                                );
1316                            }
1317                        }
1318
1319                        Some(sym::attribute) => {
1320                            if self.check_attr_not_crate_level(meta, hir_id, "attribute") {
1321                                self.check_doc_keyword_and_attribute(
1322                                    meta,
1323                                    hir_id,
1324                                    DocFakeItemKind::Attribute,
1325                                );
1326                            }
1327                        }
1328
1329                        Some(sym::fake_variadic) => {
1330                            if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
1331                                self.check_doc_fake_variadic(meta, hir_id);
1332                            }
1333                        }
1334
1335                        Some(sym::search_unbox) => {
1336                            if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
1337                                self.check_doc_search_unbox(meta, hir_id);
1338                            }
1339                        }
1340
1341                        Some(sym::test) => {
1342                            self.check_test_attr(attr_span, style, meta, hir_id);
1343                        }
1344
1345                        Some(
1346                            sym::html_favicon_url
1347                            | sym::html_logo_url
1348                            | sym::html_playground_url
1349                            | sym::issue_tracker_base_url
1350                            | sym::html_root_url,
1351                        ) => {
1352                            self.check_attr_crate_level(attr_span, style, meta, hir_id);
1353                            self.check_doc_attr_string_value(meta, hir_id);
1354                        }
1355
1356                        Some(sym::html_no_source) => {
1357                            self.check_attr_crate_level(attr_span, style, meta, hir_id);
1358                            self.check_doc_attr_no_value(meta, hir_id);
1359                        }
1360
1361                        Some(sym::auto_cfg) => {
1362                            self.check_doc_auto_cfg(i_meta, hir_id);
1363                        }
1364
1365                        Some(sym::inline | sym::no_inline) => {
1366                            self.check_doc_inline(style, meta, hir_id, target, specified_inline)
1367                        }
1368
1369                        Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target),
1370
1371                        Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
1372
1373                        Some(sym::rust_logo) => {
1374                            if self.check_attr_crate_level(attr_span, style, meta, hir_id)
1375                                && !self.tcx.features().rustdoc_internals()
1376                            {
1377                                feature_err(
1378                                    &self.tcx.sess,
1379                                    sym::rustdoc_internals,
1380                                    meta.span(),
1381                                    fluent::passes_doc_rust_logo,
1382                                )
1383                                .emit();
1384                            }
1385                        }
1386
1387                        _ => {
1388                            let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
1389                            if i_meta.has_name(sym::spotlight) {
1390                                self.tcx.emit_node_span_lint(
1391                                    INVALID_DOC_ATTRIBUTES,
1392                                    hir_id,
1393                                    i_meta.span,
1394                                    errors::DocTestUnknownSpotlight { path, span: i_meta.span },
1395                                );
1396                            } else if i_meta.has_name(sym::include)
1397                                && let Some(value) = i_meta.value_str()
1398                            {
1399                                let applicability = if list.len() == 1 {
1400                                    Applicability::MachineApplicable
1401                                } else {
1402                                    Applicability::MaybeIncorrect
1403                                };
1404                                // If there are multiple attributes, the suggestion would suggest
1405                                // deleting all of them, which is incorrect.
1406                                self.tcx.emit_node_span_lint(
1407                                    INVALID_DOC_ATTRIBUTES,
1408                                    hir_id,
1409                                    i_meta.span,
1410                                    errors::DocTestUnknownInclude {
1411                                        path,
1412                                        value: value.to_string(),
1413                                        inner: match style {
1414                                            AttrStyle::Inner => "!",
1415                                            AttrStyle::Outer => "",
1416                                        },
1417                                        sugg: (attr.span(), applicability),
1418                                    },
1419                                );
1420                            } else if i_meta.has_name(sym::passes)
1421                                || i_meta.has_name(sym::no_default_passes)
1422                            {
1423                                self.tcx.emit_node_span_lint(
1424                                    INVALID_DOC_ATTRIBUTES,
1425                                    hir_id,
1426                                    i_meta.span,
1427                                    errors::DocTestUnknownPasses { path, span: i_meta.span },
1428                                );
1429                            } else if i_meta.has_name(sym::plugins) {
1430                                self.tcx.emit_node_span_lint(
1431                                    INVALID_DOC_ATTRIBUTES,
1432                                    hir_id,
1433                                    i_meta.span,
1434                                    errors::DocTestUnknownPlugins { path, span: i_meta.span },
1435                                );
1436                            } else {
1437                                self.tcx.emit_node_span_lint(
1438                                    INVALID_DOC_ATTRIBUTES,
1439                                    hir_id,
1440                                    i_meta.span,
1441                                    errors::DocTestUnknownAny { path },
1442                                );
1443                            }
1444                        }
1445                    }
1446                } else {
1447                    self.tcx.emit_node_span_lint(
1448                        INVALID_DOC_ATTRIBUTES,
1449                        hir_id,
1450                        meta.span(),
1451                        errors::DocInvalid,
1452                    );
1453                }
1454            }
1455        }
1456    }
1457
1458    fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) {
1459        match target {
1460            Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {}
1461            _ => {
1462                self.tcx
1463                    .dcx()
1464                    .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span(), span });
1465            }
1466        }
1467    }
1468
1469    fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) {
1470        if find_attr!(attrs, AttributeKind::FfiConst(_)) {
1471            // `#[ffi_const]` functions cannot be `#[ffi_pure]`
1472            self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span });
1473        }
1474    }
1475
1476    /// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
1477    fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
1478        match target {
1479            Target::Struct | Target::Enum | Target::Union | Target::Trait => {}
1480            _ => {
1481                self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span(), span });
1482            }
1483        }
1484    }
1485
1486    /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
1487    fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {
1488        if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
1489            && matches!(
1490                param.kind,
1491                hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }
1492            )
1493            && matches!(param.source, hir::GenericParamSource::Generics)
1494            && let parent_hir_id = self.tcx.parent_hir_id(hir_id)
1495            && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)
1496            && let hir::ItemKind::Impl(impl_) = item.kind
1497            && let Some(of_trait) = impl_.of_trait
1498            && let Some(def_id) = of_trait.trait_ref.trait_def_id()
1499            && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)
1500        {
1501            return;
1502        }
1503
1504        self.dcx().emit_err(errors::InvalidMayDangle { attr_span });
1505    }
1506
1507    /// Checks if `#[link]` is applied to an item other than a foreign module.
1508    fn check_link(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
1509        if target == Target::ForeignMod
1510            && let hir::Node::Item(item) = self.tcx.hir_node(hir_id)
1511            && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1512            && !matches!(abi, ExternAbi::Rust)
1513        {
1514            return;
1515        }
1516
1517        self.tcx.emit_node_span_lint(
1518            UNUSED_ATTRIBUTES,
1519            hir_id,
1520            attr_span,
1521            errors::Link { span: (target != Target::ForeignMod).then_some(span) },
1522        );
1523    }
1524
1525    /// Checks if `#[no_link]` is applied to an `extern crate`.
1526    fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1527        match target {
1528            Target::ExternCrate => {}
1529            // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1530            // `#[no_link]` attribute with just a lint, because we previously
1531            // erroneously allowed it and some crates used it accidentally, to be compatible
1532            // with crates depending on them, we can't throw an error here.
1533            Target::Field | Target::Arm | Target::MacroDef => {
1534                self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_link");
1535            }
1536            _ => {
1537                self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span });
1538            }
1539        }
1540    }
1541
1542    /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1543    fn check_rustc_legacy_const_generics(
1544        &self,
1545        hir_id: HirId,
1546        attr: &Attribute,
1547        span: Span,
1548        target: Target,
1549        item: Option<ItemLike<'_>>,
1550    ) {
1551        let is_function = matches!(target, Target::Fn);
1552        if !is_function {
1553            self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
1554                attr_span: attr.span(),
1555                defn_span: span,
1556                on_crate: hir_id == CRATE_HIR_ID,
1557            });
1558            return;
1559        }
1560
1561        let Some(list) = attr.meta_item_list() else {
1562            // The attribute form is validated on AST.
1563            return;
1564        };
1565
1566        let Some(ItemLike::Item(Item {
1567            kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. },
1568            ..
1569        })) = item
1570        else {
1571            bug!("should be a function item");
1572        };
1573
1574        for param in generics.params {
1575            match param.kind {
1576                hir::GenericParamKind::Const { .. } => {}
1577                _ => {
1578                    self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly {
1579                        attr_span: attr.span(),
1580                        param_span: param.span,
1581                    });
1582                    return;
1583                }
1584            }
1585        }
1586
1587        if list.len() != generics.params.len() {
1588            self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex {
1589                attr_span: attr.span(),
1590                generics_span: generics.span,
1591            });
1592            return;
1593        }
1594
1595        let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1596        let mut invalid_args = vec![];
1597        for meta in list {
1598            if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
1599                if *val >= arg_count {
1600                    let span = meta.span();
1601                    self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexExceed {
1602                        span,
1603                        arg_count: arg_count as usize,
1604                    });
1605                    return;
1606                }
1607            } else {
1608                invalid_args.push(meta.span());
1609            }
1610        }
1611
1612        if !invalid_args.is_empty() {
1613            self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
1614        }
1615    }
1616
1617    /// Helper function for checking that the provided attribute is only applied to a function or
1618    /// method.
1619    fn check_applied_to_fn_or_method(
1620        &self,
1621        hir_id: HirId,
1622        attr_span: Span,
1623        defn_span: Span,
1624        target: Target,
1625    ) {
1626        let is_function = matches!(target, Target::Fn | Target::Method(..));
1627        if !is_function {
1628            self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
1629                attr_span,
1630                defn_span,
1631                on_crate: hir_id == CRATE_HIR_ID,
1632            });
1633        }
1634    }
1635
1636    /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
1637    fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) {
1638        match target {
1639            Target::Struct => {}
1640            _ => {
1641                self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span(), span });
1642            }
1643        }
1644    }
1645
1646    /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
1647    fn check_rustc_lint_opt_deny_field_access(&self, attr: &Attribute, span: Span, target: Target) {
1648        match target {
1649            Target::Field => {}
1650            _ => {
1651                self.tcx
1652                    .dcx()
1653                    .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span(), span });
1654            }
1655        }
1656    }
1657
1658    /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1659    /// option is passed to the compiler.
1660    fn check_rustc_dirty_clean(&self, attr: &Attribute) {
1661        if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
1662            self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span() });
1663        }
1664    }
1665
1666    /// Checks if the attribute is applied to a trait.
1667    fn check_must_be_applied_to_trait(&self, attr_span: Span, defn_span: Span, target: Target) {
1668        match target {
1669            Target::Trait => {}
1670            _ => {
1671                self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span, defn_span });
1672            }
1673        }
1674    }
1675
1676    /// Checks if the `#[repr]` attributes on `item` are valid.
1677    fn check_repr(
1678        &self,
1679        attrs: &[Attribute],
1680        span: Span,
1681        target: Target,
1682        item: Option<ItemLike<'_>>,
1683        hir_id: HirId,
1684    ) {
1685        // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1686        // ```
1687        // #[repr(foo)]
1688        // #[repr(bar, align(8))]
1689        // ```
1690        let (reprs, first_attr_span) = find_attr!(attrs, AttributeKind::Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span))).unwrap_or((&[], None));
1691
1692        let mut int_reprs = 0;
1693        let mut is_explicit_rust = false;
1694        let mut is_c = false;
1695        let mut is_simd = false;
1696        let mut is_transparent = false;
1697
1698        for (repr, repr_span) in reprs {
1699            match repr {
1700                ReprAttr::ReprRust => {
1701                    is_explicit_rust = true;
1702                    match target {
1703                        Target::Struct | Target::Union | Target::Enum => continue,
1704                        _ => {
1705                            self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1706                                hint_span: *repr_span,
1707                                span,
1708                            });
1709                        }
1710                    }
1711                }
1712                ReprAttr::ReprC => {
1713                    is_c = true;
1714                    match target {
1715                        Target::Struct | Target::Union | Target::Enum => continue,
1716                        _ => {
1717                            self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1718                                hint_span: *repr_span,
1719                                span,
1720                            });
1721                        }
1722                    }
1723                }
1724                ReprAttr::ReprAlign(align) => {
1725                    match target {
1726                        Target::Struct | Target::Union | Target::Enum => {}
1727                        Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => {
1728                            self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
1729                                span: *repr_span,
1730                                item: target.plural_name(),
1731                            });
1732                        }
1733                        Target::Static if self.tcx.features().static_align() => {
1734                            self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic {
1735                                span: *repr_span,
1736                                item: target.plural_name(),
1737                            });
1738                        }
1739                        _ => {
1740                            self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1741                                hint_span: *repr_span,
1742                                span,
1743                            });
1744                        }
1745                    }
1746
1747                    self.check_align(*align, *repr_span);
1748                }
1749                ReprAttr::ReprPacked(_) => {
1750                    if target != Target::Struct && target != Target::Union {
1751                        self.dcx().emit_err(errors::AttrApplication::StructUnion {
1752                            hint_span: *repr_span,
1753                            span,
1754                        });
1755                    } else {
1756                        continue;
1757                    }
1758                }
1759                ReprAttr::ReprSimd => {
1760                    is_simd = true;
1761                    if target != Target::Struct {
1762                        self.dcx().emit_err(errors::AttrApplication::Struct {
1763                            hint_span: *repr_span,
1764                            span,
1765                        });
1766                    } else {
1767                        continue;
1768                    }
1769                }
1770                ReprAttr::ReprTransparent => {
1771                    is_transparent = true;
1772                    match target {
1773                        Target::Struct | Target::Union | Target::Enum => continue,
1774                        _ => {
1775                            self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1776                                hint_span: *repr_span,
1777                                span,
1778                            });
1779                        }
1780                    }
1781                }
1782                ReprAttr::ReprInt(_) => {
1783                    int_reprs += 1;
1784                    if target != Target::Enum {
1785                        self.dcx().emit_err(errors::AttrApplication::Enum {
1786                            hint_span: *repr_span,
1787                            span,
1788                        });
1789                    } else {
1790                        continue;
1791                    }
1792                }
1793            };
1794        }
1795
1796        // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
1797        if let Some(first_attr_span) = first_attr_span
1798            && reprs.is_empty()
1799            && item.is_some()
1800        {
1801            match target {
1802                Target::Struct | Target::Union | Target::Enum => {}
1803                Target::Fn | Target::Method(_) => {
1804                    self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
1805                        span: first_attr_span,
1806                        item: target.plural_name(),
1807                    });
1808                }
1809                _ => {
1810                    self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1811                        hint_span: first_attr_span,
1812                        span,
1813                    });
1814                }
1815            }
1816            return;
1817        }
1818
1819        // Just point at all repr hints if there are any incompatibilities.
1820        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1821        let hint_spans = reprs.iter().map(|(_, span)| *span);
1822
1823        // Error on repr(transparent, <anything else>).
1824        if is_transparent && reprs.len() > 1 {
1825            let hint_spans = hint_spans.clone().collect();
1826            self.dcx().emit_err(errors::TransparentIncompatible {
1827                hint_spans,
1828                target: target.to_string(),
1829            });
1830        }
1831        // Error on `#[repr(transparent)]` in combination with
1832        // `#[rustc_pass_indirectly_in_non_rustic_abis]`
1833        if is_transparent
1834            && let Some(&pass_indirectly_span) =
1835                find_attr!(attrs, AttributeKind::RustcPassIndirectlyInNonRusticAbis(span) => span)
1836        {
1837            self.dcx().emit_err(errors::TransparentIncompatible {
1838                hint_spans: vec![span, pass_indirectly_span],
1839                target: target.to_string(),
1840            });
1841        }
1842        if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {
1843            let hint_spans = hint_spans.clone().collect();
1844            self.dcx().emit_err(errors::ReprConflicting { hint_spans });
1845        }
1846        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1847        if (int_reprs > 1)
1848            || (is_simd && is_c)
1849            || (int_reprs == 1
1850                && is_c
1851                && item.is_some_and(|item| {
1852                    if let ItemLike::Item(item) = item { is_c_like_enum(item) } else { false }
1853                }))
1854        {
1855            self.tcx.emit_node_span_lint(
1856                CONFLICTING_REPR_HINTS,
1857                hir_id,
1858                hint_spans.collect::<Vec<Span>>(),
1859                errors::ReprConflictingLint,
1860            );
1861        }
1862    }
1863
1864    fn check_align(&self, align: Align, span: Span) {
1865        if align.bytes() > 2_u64.pow(29) {
1866            // for values greater than 2^29, a different error will be emitted, make sure that happens
1867            self.dcx().span_delayed_bug(
1868                span,
1869                "alignment greater than 2^29 should be errored on elsewhere",
1870            );
1871        } else {
1872            // only do this check when <= 2^29 to prevent duplicate errors:
1873            // alignment greater than 2^29 not supported
1874            // alignment is too large for the current target
1875
1876            let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
1877            if align.bytes() > max {
1878                self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max });
1879            }
1880        }
1881    }
1882
1883    /// Outputs an error for attributes that can only be applied to macros, such as
1884    /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`.
1885    /// (Allows proc_macro functions)
1886    // FIXME(jdonszelmann): if possible, move to attr parsing
1887    fn check_macro_only_attr(
1888        &self,
1889        attr_span: Span,
1890        span: Span,
1891        target: Target,
1892        attrs: &[Attribute],
1893    ) {
1894        match target {
1895            Target::Fn => {
1896                for attr in attrs {
1897                    if attr.is_proc_macro_attr() {
1898                        // return on proc macros
1899                        return;
1900                    }
1901                }
1902                self.tcx.dcx().emit_err(errors::MacroOnlyAttribute { attr_span, span });
1903            }
1904            _ => {}
1905        }
1906    }
1907
1908    /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1909    /// (Allows proc_macro functions)
1910    fn check_rustc_allow_const_fn_unstable(
1911        &self,
1912        hir_id: HirId,
1913        attr_span: Span,
1914        span: Span,
1915        target: Target,
1916    ) {
1917        match target {
1918            Target::Fn | Target::Method(_) => {
1919                if !self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) {
1920                    self.tcx.dcx().emit_err(errors::RustcAllowConstFnUnstable { attr_span, span });
1921                }
1922            }
1923            _ => {}
1924        }
1925    }
1926
1927    fn check_stability(
1928        &self,
1929        attr_span: Span,
1930        item_span: Span,
1931        level: &StabilityLevel,
1932        feature: Symbol,
1933    ) {
1934        // Stable *language* features shouldn't be used as unstable library features.
1935        // (Not doing this for stable library features is checked by tidy.)
1936        if level.is_unstable()
1937            && ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some()
1938        {
1939            self.tcx
1940                .dcx()
1941                .emit_err(errors::UnstableAttrForAlreadyStableFeature { attr_span, item_span });
1942        }
1943    }
1944
1945    fn check_deprecated(&self, hir_id: HirId, attr_span: Span, target: Target) {
1946        match target {
1947            Target::AssocConst | Target::Method(..) | Target::AssocTy
1948                if matches!(
1949                    self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id)),
1950                    DefKind::Impl { of_trait: true }
1951                ) =>
1952            {
1953                self.tcx.emit_node_span_lint(
1954                    UNUSED_ATTRIBUTES,
1955                    hir_id,
1956                    attr_span,
1957                    errors::DeprecatedAnnotationHasNoEffect { span: attr_span },
1958                );
1959            }
1960            _ => {}
1961        }
1962    }
1963
1964    fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) {
1965        if target != Target::MacroDef {
1966            return;
1967        }
1968
1969        // special case when `#[macro_export]` is applied to a macro 2.0
1970        let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro();
1971        let is_decl_macro = !macro_definition.macro_rules;
1972
1973        if is_decl_macro {
1974            self.tcx.emit_node_span_lint(
1975                UNUSED_ATTRIBUTES,
1976                hir_id,
1977                attr_span,
1978                errors::MacroExport::OnDeclMacro,
1979            );
1980        }
1981    }
1982
1983    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
1984        // Warn on useless empty attributes.
1985        // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
1986        let note = if attr.has_any_name(&[
1987            sym::allow,
1988            sym::expect,
1989            sym::warn,
1990            sym::deny,
1991            sym::forbid,
1992            sym::feature,
1993        ]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
1994        {
1995            errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1996        } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1997            && let Some(meta) = attr.meta_item_list()
1998            && let [meta] = meta.as_slice()
1999            && let Some(item) = meta.meta_item()
2000            && let MetaItemKind::NameValue(_) = &item.kind
2001            && item.path == sym::reason
2002        {
2003            errors::UnusedNote::NoLints { name: attr.name().unwrap() }
2004        } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
2005            && let Some(meta) = attr.meta_item_list()
2006            && meta.iter().any(|meta| {
2007                meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
2008            })
2009        {
2010            if hir_id != CRATE_HIR_ID {
2011                match style {
2012                    Some(ast::AttrStyle::Outer) => {
2013                        let attr_span = attr.span();
2014                        let bang_position = self
2015                            .tcx
2016                            .sess
2017                            .source_map()
2018                            .span_until_char(attr_span, '[')
2019                            .shrink_to_hi();
2020
2021                        self.tcx.emit_node_span_lint(
2022                            UNUSED_ATTRIBUTES,
2023                            hir_id,
2024                            attr_span,
2025                            errors::OuterCrateLevelAttr {
2026                                suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
2027                            },
2028                        )
2029                    }
2030                    Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
2031                        UNUSED_ATTRIBUTES,
2032                        hir_id,
2033                        attr.span(),
2034                        errors::InnerCrateLevelAttr,
2035                    ),
2036                };
2037                return;
2038            } else {
2039                let never_needs_link = self
2040                    .tcx
2041                    .crate_types()
2042                    .iter()
2043                    .all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
2044                if never_needs_link {
2045                    errors::UnusedNote::LinkerMessagesBinaryCrateOnly
2046                } else {
2047                    return;
2048                }
2049            }
2050        } else if attr.has_name(sym::default_method_body_is_const) {
2051            errors::UnusedNote::DefaultMethodBodyConst
2052        } else {
2053            return;
2054        };
2055
2056        self.tcx.emit_node_span_lint(
2057            UNUSED_ATTRIBUTES,
2058            hir_id,
2059            attr.span(),
2060            errors::Unused { attr_span: attr.span(), note },
2061        );
2062    }
2063
2064    /// A best effort attempt to create an error for a mismatching proc macro signature.
2065    ///
2066    /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
2067    fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2068        if target != Target::Fn {
2069            return;
2070        }
2071
2072        let tcx = self.tcx;
2073        let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else {
2074            return;
2075        };
2076        let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else {
2077            return;
2078        };
2079
2080        let def_id = hir_id.expect_owner().def_id;
2081        let param_env = ty::ParamEnv::empty();
2082
2083        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
2084        let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
2085
2086        let span = tcx.def_span(def_id);
2087        let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id());
2088        let sig = tcx.liberate_late_bound_regions(
2089            def_id.to_def_id(),
2090            tcx.fn_sig(def_id).instantiate(tcx, fresh_args),
2091        );
2092
2093        let mut cause = ObligationCause::misc(span, def_id);
2094        let sig = ocx.normalize(&cause, param_env, sig);
2095
2096        // proc macro is not WF.
2097        let errors = ocx.try_evaluate_obligations();
2098        if !errors.is_empty() {
2099            return;
2100        }
2101
2102        let expected_sig = tcx.mk_fn_sig(
2103            std::iter::repeat_n(
2104                token_stream,
2105                match kind {
2106                    ProcMacroKind::Attribute => 2,
2107                    ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2108                },
2109            ),
2110            token_stream,
2111            false,
2112            Safety::Safe,
2113            ExternAbi::Rust,
2114        );
2115
2116        if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) {
2117            let mut diag = tcx.dcx().create_err(errors::ProcMacroBadSig { span, kind });
2118
2119            let hir_sig = tcx.hir_fn_sig_by_hir_id(hir_id);
2120            if let Some(hir_sig) = hir_sig {
2121                #[allow(rustc::diagnostic_outside_of_impl)] // FIXME
2122                match terr {
2123                    TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {
2124                        if let Some(ty) = hir_sig.decl.inputs.get(idx) {
2125                            diag.span(ty.span);
2126                            cause.span = ty.span;
2127                        } else if idx == hir_sig.decl.inputs.len() {
2128                            let span = hir_sig.decl.output.span();
2129                            diag.span(span);
2130                            cause.span = span;
2131                        }
2132                    }
2133                    TypeError::ArgCount => {
2134                        if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {
2135                            diag.span(ty.span);
2136                            cause.span = ty.span;
2137                        }
2138                    }
2139                    TypeError::SafetyMismatch(_) => {
2140                        // FIXME: Would be nice if we had a span here..
2141                    }
2142                    TypeError::AbiMismatch(_) => {
2143                        // FIXME: Would be nice if we had a span here..
2144                    }
2145                    TypeError::VariadicMismatch(_) => {
2146                        // FIXME: Would be nice if we had a span here..
2147                    }
2148                    _ => {}
2149                }
2150            }
2151
2152            infcx.err_ctxt().note_type_err(
2153                &mut diag,
2154                &cause,
2155                None,
2156                Some(param_env.and(ValuePairs::PolySigs(ExpectedFound {
2157                    expected: ty::Binder::dummy(expected_sig),
2158                    found: ty::Binder::dummy(sig),
2159                }))),
2160                terr,
2161                false,
2162                None,
2163            );
2164            diag.emit();
2165            self.abort.set(true);
2166        }
2167
2168        let errors = ocx.evaluate_obligations_error_on_ambiguity();
2169        if !errors.is_empty() {
2170            infcx.err_ctxt().report_fulfillment_errors(errors);
2171            self.abort.set(true);
2172        }
2173    }
2174
2175    fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
2176        if !find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
2177            .unwrap_or(false)
2178        {
2179            self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
2180        }
2181    }
2182
2183    fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) {
2184        if let (Target::Closure, None) = (
2185            target,
2186            find_attr!(attrs, AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span),
2187        ) {
2188            let is_coro = matches!(
2189                self.tcx.hir_expect_expr(hir_id).kind,
2190                hir::ExprKind::Closure(hir::Closure {
2191                    kind: hir::ClosureKind::Coroutine(..) | hir::ClosureKind::CoroutineClosure(..),
2192                    ..
2193                })
2194            );
2195            let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id();
2196            let parent_span = self.tcx.def_span(parent_did);
2197
2198            if let Some(attr_span) = find_attr!(
2199                self.tcx.get_all_attrs(parent_did),
2200                AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span
2201            ) && is_coro
2202            {
2203                self.dcx().emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span });
2204            }
2205        }
2206    }
2207
2208    fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) {
2209        if let Some(export_name_span) = find_attr!(attrs, AttributeKind::ExportName { span: export_name_span, .. } => *export_name_span)
2210            && let Some(no_mangle_span) =
2211                find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
2212        {
2213            let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 {
2214                "#[unsafe(no_mangle)]"
2215            } else {
2216                "#[no_mangle]"
2217            };
2218            let export_name_attr = if export_name_span.edition() >= Edition::Edition2024 {
2219                "#[unsafe(export_name)]"
2220            } else {
2221                "#[export_name]"
2222            };
2223
2224            self.tcx.emit_node_span_lint(
2225                lint::builtin::UNUSED_ATTRIBUTES,
2226                hir_id,
2227                no_mangle_span,
2228                errors::MixedExportNameAndNoMangle {
2229                    no_mangle_span,
2230                    export_name_span,
2231                    no_mangle_attr,
2232                    export_name_attr,
2233                },
2234            );
2235        }
2236    }
2237
2238    /// Checks if `#[autodiff]` is applied to an item other than a function item.
2239    fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) {
2240        debug!("check_autodiff");
2241        match target {
2242            Target::Fn => {}
2243            _ => {
2244                self.dcx().emit_err(errors::AutoDiffAttr { attr_span: span });
2245                self.abort.set(true);
2246            }
2247        }
2248    }
2249
2250    fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) {
2251        let node_span = self.tcx.hir_span(hir_id);
2252
2253        if !matches!(target, Target::Expression) {
2254            return; // Handled in target checking during attr parse
2255        }
2256
2257        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) {
2258            self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
2259        };
2260    }
2261
2262    fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {
2263        let node_span = self.tcx.hir_span(hir_id);
2264
2265        if !matches!(target, Target::Expression) {
2266            return; // Handled in target checking during attr parse
2267        }
2268
2269        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) {
2270            self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
2271        };
2272    }
2273
2274    fn check_custom_mir(
2275        &self,
2276        dialect: Option<(MirDialect, Span)>,
2277        phase: Option<(MirPhase, Span)>,
2278        attr_span: Span,
2279    ) {
2280        let Some((dialect, dialect_span)) = dialect else {
2281            if let Some((_, phase_span)) = phase {
2282                self.dcx()
2283                    .emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span });
2284            }
2285            return;
2286        };
2287
2288        match dialect {
2289            MirDialect::Analysis => {
2290                if let Some((MirPhase::Optimized, phase_span)) = phase {
2291                    self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
2292                        dialect,
2293                        phase: MirPhase::Optimized,
2294                        attr_span,
2295                        dialect_span,
2296                        phase_span,
2297                    });
2298                }
2299            }
2300
2301            MirDialect::Built => {
2302                if let Some((phase, phase_span)) = phase {
2303                    self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
2304                        dialect,
2305                        phase,
2306                        attr_span,
2307                        dialect_span,
2308                        phase_span,
2309                    });
2310                }
2311            }
2312            MirDialect::Runtime => {}
2313        }
2314    }
2315}
2316
2317impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2318    type NestedFilter = nested_filter::OnlyBodies;
2319
2320    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
2321        self.tcx
2322    }
2323
2324    fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2325        // Historically we've run more checks on non-exported than exported macros,
2326        // so this lets us continue to run them while maintaining backwards compatibility.
2327        // In the long run, the checks should be harmonized.
2328        if let ItemKind::Macro(_, macro_def, _) = item.kind {
2329            let def_id = item.owner_id.to_def_id();
2330            if macro_def.macro_rules
2331                && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. })
2332            {
2333                check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2334            }
2335        }
2336
2337        let target = Target::from_item(item);
2338        self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2339        intravisit::walk_item(self, item)
2340    }
2341
2342    fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) {
2343        // FIXME(where_clause_attrs): Currently, as the following check shows,
2344        // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
2345        // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
2346        // in where clauses. After that, only `self.check_attributes` should be enough.
2347        const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg_trace, sym::cfg_attr_trace];
2348        let spans = self
2349            .tcx
2350            .hir_attrs(where_predicate.hir_id)
2351            .iter()
2352            .filter(|attr| !ATTRS_ALLOWED.iter().any(|&sym| attr.has_name(sym)))
2353            .filter(|attr| !attr.is_parsed_attr())
2354            .map(|attr| attr.span())
2355            .collect::<Vec<_>>();
2356        if !spans.is_empty() {
2357            self.tcx.dcx().emit_err(errors::UnsupportedAttributesInWhere { span: spans.into() });
2358        }
2359        self.check_attributes(
2360            where_predicate.hir_id,
2361            where_predicate.span,
2362            Target::WherePredicate,
2363            None,
2364        );
2365        intravisit::walk_where_predicate(self, where_predicate)
2366    }
2367
2368    fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2369        let target = Target::from_generic_param(generic_param);
2370        self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2371        intravisit::walk_generic_param(self, generic_param)
2372    }
2373
2374    fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2375        let target = Target::from_trait_item(trait_item);
2376        self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2377        intravisit::walk_trait_item(self, trait_item)
2378    }
2379
2380    fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2381        self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2382        intravisit::walk_field_def(self, struct_field);
2383    }
2384
2385    fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2386        self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2387        intravisit::walk_arm(self, arm);
2388    }
2389
2390    fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2391        let target = Target::from_foreign_item(f_item);
2392        self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));
2393        intravisit::walk_foreign_item(self, f_item)
2394    }
2395
2396    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2397        let target = target_from_impl_item(self.tcx, impl_item);
2398        self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2399        intravisit::walk_impl_item(self, impl_item)
2400    }
2401
2402    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2403        // When checking statements ignore expressions, they will be checked later.
2404        if let hir::StmtKind::Let(l) = stmt.kind {
2405            self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2406        }
2407        intravisit::walk_stmt(self, stmt)
2408    }
2409
2410    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2411        let target = match expr.kind {
2412            hir::ExprKind::Closure { .. } => Target::Closure,
2413            _ => Target::Expression,
2414        };
2415
2416        self.check_attributes(expr.hir_id, expr.span, target, None);
2417        intravisit::walk_expr(self, expr)
2418    }
2419
2420    fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
2421        self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
2422        intravisit::walk_expr_field(self, field)
2423    }
2424
2425    fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
2426        self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);
2427        intravisit::walk_variant(self, variant)
2428    }
2429
2430    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2431        self.check_attributes(param.hir_id, param.span, Target::Param, None);
2432
2433        intravisit::walk_param(self, param);
2434    }
2435
2436    fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
2437        self.check_attributes(field.hir_id, field.span, Target::PatField, None);
2438        intravisit::walk_pat_field(self, field);
2439    }
2440}
2441
2442fn is_c_like_enum(item: &Item<'_>) -> bool {
2443    if let ItemKind::Enum(_, _, ref def) = item.kind {
2444        for variant in def.variants {
2445            match variant.data {
2446                hir::VariantData::Unit(..) => { /* continue */ }
2447                _ => return false,
2448            }
2449        }
2450        true
2451    } else {
2452        false
2453    }
2454}
2455
2456// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
2457// from this check.
2458fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2459    // Check for builtin attributes at the crate level
2460    // which were unsuccessfully resolved due to cannot determine
2461    // resolution for the attribute macro error.
2462    const ATTRS_TO_CHECK: &[Symbol] =
2463        &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
2464
2465    for attr in attrs {
2466        // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
2467        let (span, name) = if let Some(a) =
2468            ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
2469        {
2470            (attr.span(), *a)
2471        } else if let Attribute::Parsed(AttributeKind::Repr {
2472            reprs: _,
2473            first_span: first_attr_span,
2474        }) = attr
2475        {
2476            (*first_attr_span, sym::repr)
2477        } else {
2478            continue;
2479        };
2480
2481        let item = tcx
2482            .hir_free_items()
2483            .map(|id| tcx.hir_item(id))
2484            .find(|item| !item.span.is_dummy()) // Skip prelude `use`s
2485            .map(|item| errors::ItemFollowingInnerAttr {
2486                span: if let Some(ident) = item.kind.ident() { ident.span } else { item.span },
2487                kind: tcx.def_descr(item.owner_id.to_def_id()),
2488            });
2489        let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
2490            span,
2491            sugg_span: tcx
2492                .sess
2493                .source_map()
2494                .span_to_snippet(span)
2495                .ok()
2496                .filter(|src| src.starts_with("#!["))
2497                .map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))),
2498            name,
2499            item,
2500        });
2501
2502        if let Attribute::Unparsed(p) = attr {
2503            tcx.dcx().try_steal_replace_and_emit_err(
2504                p.path.span,
2505                StashKey::UndeterminedMacroResolution,
2506                err,
2507            );
2508        } else {
2509            err.emit();
2510        }
2511    }
2512}
2513
2514fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2515    let attrs = tcx.hir_attrs(item.hir_id());
2516
2517    if let Some(attr_span) = find_attr!(attrs, AttributeKind::Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span)
2518    {
2519        tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span });
2520    }
2521}
2522
2523fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
2524    let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
2525    tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor);
2526    if module_def_id.to_local_def_id().is_top_level_module() {
2527        check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2528        check_invalid_crate_level_attr(tcx, tcx.hir_krate_attrs());
2529    }
2530    if check_attr_visitor.abort.get() {
2531        tcx.dcx().abort_if_errors()
2532    }
2533}
2534
2535pub(crate) fn provide(providers: &mut Providers) {
2536    *providers = Providers { check_mod_attrs, ..*providers };
2537}
2538
2539// FIXME(jdonszelmann): remove, check during parsing
2540fn check_duplicates(
2541    tcx: TyCtxt<'_>,
2542    attr_span: Span,
2543    attr: &Attribute,
2544    hir_id: HirId,
2545    duplicates: AttributeDuplicates,
2546    seen: &mut FxHashMap<Symbol, Span>,
2547) {
2548    use AttributeDuplicates::*;
2549    if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2550        return;
2551    }
2552    let attr_name = attr.name().unwrap();
2553    match duplicates {
2554        DuplicatesOk => {}
2555        WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2556            match seen.entry(attr_name) {
2557                Entry::Occupied(mut entry) => {
2558                    let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2559                        let to_remove = entry.insert(attr_span);
2560                        (to_remove, attr_span)
2561                    } else {
2562                        (attr_span, *entry.get())
2563                    };
2564                    tcx.emit_node_span_lint(
2565                        UNUSED_ATTRIBUTES,
2566                        hir_id,
2567                        this,
2568                        errors::UnusedDuplicate {
2569                            this,
2570                            other,
2571                            warning: matches!(
2572                                duplicates,
2573                                FutureWarnFollowing | FutureWarnPreceding
2574                            ),
2575                        },
2576                    );
2577                }
2578                Entry::Vacant(entry) => {
2579                    entry.insert(attr_span);
2580                }
2581            }
2582        }
2583        ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) {
2584            Entry::Occupied(mut entry) => {
2585                let (this, other) = if matches!(duplicates, ErrorPreceding) {
2586                    let to_remove = entry.insert(attr_span);
2587                    (to_remove, attr_span)
2588                } else {
2589                    (attr_span, *entry.get())
2590                };
2591                tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name });
2592            }
2593            Entry::Vacant(entry) => {
2594                entry.insert(attr_span);
2595            }
2596        },
2597    }
2598}
2599
2600fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool {
2601    matches!(&self_ty.kind, hir::TyKind::Tup([_]))
2602        || if let hir::TyKind::FnPtr(fn_ptr_ty) = &self_ty.kind {
2603            fn_ptr_ty.decl.inputs.len() == 1
2604        } else {
2605            false
2606        }
2607        || (if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &self_ty.kind
2608            && let Some(&[hir::GenericArg::Type(ty)]) =
2609                path.segments.last().map(|last| last.args().args)
2610        {
2611            doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty())
2612        } else {
2613            false
2614        })
2615}