rustc_passes/
check_attr.rs

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