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