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