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