compiler/rustc_passes/src/check_attr.rs RUST 1,826 lines View on github.com → Search inside
1// FIXME(jdonszelmann): should become rustc_attr_validation2//! This module implements some validity checks for attributes.3//! In particular it verifies that `#[inline]` and `#[repr]` attributes are4//! attached to items that actually support them and if there are5//! conflicts between multiple such attributes attached to the same6//! item.78use std::cell::Cell;9use std::slice;1011use rustc_abi::ExternAbi;12use rustc_ast::{AttrStyle, MetaItemKind, ast};13use rustc_attr_parsing::AttributeParser;14use rustc_data_structures::thin_vec::ThinVec;15use rustc_data_structures::unord::UnordMap;16use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg};17use rustc_feature::BUILTIN_ATTRIBUTE_MAP;18use rustc_hir::attrs::diagnostic::Directive;19use rustc_hir::attrs::{20    AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,21    OptimizeAttr, ReprAttr,22};23use rustc_hir::def::DefKind;24use rustc_hir::def_id::LocalModDefId;25use rustc_hir::intravisit::{self, Visitor};26use rustc_hir::{27    self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId,28    Item, ItemKind, MethodKind, Node, ParamName, Target, TraitItem, find_attr,29};30use rustc_macros::Diagnostic;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, Unnormalized};37use rustc_middle::{bug, span_bug};38use rustc_session::config::CrateType;39use rustc_session::errors::feature_err;40use rustc_session::lint;41use rustc_session::lint::builtin::{42    CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES,43    MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,44};45use rustc_span::edition::Edition;46use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};47use rustc_trait_selection::error_reporting::InferCtxtErrorExt;48use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};49use rustc_trait_selection::traits::ObligationCtxt;5051use crate::diagnostics;5253#[derive(Diagnostic)]54#[diag("`#[diagnostic::on_const]` can only be applied to non-const trait implementations")]55struct DiagnosticOnConstOnlyForNonConstTraitImpls {56    #[label("this is a const trait implementation")]57    item_span: Span,58}5960fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {61    match impl_item.kind {62        hir::ImplItemKind::Const(..) => Target::AssocConst,63        hir::ImplItemKind::Fn(..) => {64            let parent_def_id = tcx.hir_get_parent_item(impl_item.hir_id()).def_id;65            let containing_item = tcx.hir_expect_item(parent_def_id);66            let containing_impl_is_for_trait = match &containing_item.kind {67                hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),68                _ => bug!("parent of an ImplItem must be an Impl"),69            };70            if containing_impl_is_for_trait {71                Target::Method(MethodKind::Trait { body: true })72            } else {73                Target::Method(MethodKind::Inherent)74            }75        }76        hir::ImplItemKind::Type(..) => Target::AssocTy,77    }78}7980#[derive(Clone, Copy)]81enum ItemLike<'tcx> {82    Item(&'tcx Item<'tcx>),83    ForeignItem,84}8586#[derive(Copy, Clone)]87pub(crate) enum ProcMacroKind {88    FunctionLike,89    Derive,90    Attribute,91}9293impl IntoDiagArg for ProcMacroKind {94    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {95        match self {96            ProcMacroKind::Attribute => "attribute proc macro",97            ProcMacroKind::Derive => "derive proc macro",98            ProcMacroKind::FunctionLike => "function-like proc macro",99        }100        .into_diag_arg(&mut None)101    }102}103104struct CheckAttrVisitor<'tcx> {105    tcx: TyCtxt<'tcx>,106107    // Whether or not this visitor should abort after finding errors108    abort: Cell<bool>,109}110111impl<'tcx> CheckAttrVisitor<'tcx> {112    fn dcx(&self) -> DiagCtxtHandle<'tcx> {113        self.tcx.dcx()114    }115116    /// Checks any attribute.117    fn check_attributes(118        &self,119        hir_id: HirId,120        span: Span,121        target: Target,122        item: Option<ItemLike<'_>>,123    ) {124        let attrs = self.tcx.hir_attrs(hir_id);125        for attr in attrs {126            match attr {127                Attribute::Parsed(attr_kind) => {128                    self.check_one_parsed_attribute(hir_id, span, target, item, attrs, attr_kind);129                    self.check_unused_attribute(hir_id, attr, None);130                }131                Attribute::Unparsed(attr_item) => {132                    match attr.path().as_slice() {133                        // ok134                        [sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid, ..] => {}135136                        [name, rest @ ..] => {137                            if let Some(_) = BUILTIN_ATTRIBUTE_MAP.get(name) {138                                if rest.len() > 0139                                    && AttributeParser::is_parsed_attribute(slice::from_ref(name))140                                {141                                    // Check if we tried to use a builtin attribute as an attribute142                                    // namespace, like `#[must_use::skip]`. This check is here to143                                    // solve <https://github.com/rust-lang/rust/issues/137590>.144                                    // An error is already produced for this case elsewhere.145                                    return;146                                }147148                                span_bug!(149                                    attr.span(),150                                    "builtin attribute {name:?} not handled by `CheckAttrVisitor`"151                                )152                            }153                        }154155                        [] => unreachable!(),156                    }157158                    self.check_unused_attribute(hir_id, attr, Some(attr_item.style));159                }160            }161        }162163        self.check_repr(attrs, span, target, item, hir_id);164        self.check_rustc_force_inline(hir_id, attrs, target);165        self.check_mix_no_mangle_export(hir_id, attrs);166        self.check_optimize_and_inline(attrs);167    }168169    /// Called by [`Self::check_attributes()`] to check a single attribute which is170    /// [`Attribute::Parsed`].171    ///172    /// This is a separate function to help with comprehensibility and rustfmt-ability.173    fn check_one_parsed_attribute(174        &self,175        hir_id: HirId,176        span: Span,177        target: Target,178        item: Option<ItemLike<'_>>,179        attrs: &[Attribute],180        attr: &AttributeKind,181    ) {182        match attr {183            AttributeKind::ProcMacro => {184                self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)185            }186            AttributeKind::ProcMacroAttribute => {187                self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);188            }189            AttributeKind::ProcMacroDerive { .. } => {190                self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)191            }192            AttributeKind::Inline(InlineAttr::Force { .. }, ..) => {} // handled separately below193            AttributeKind::Inline(kind, attr_span) => {194                self.check_inline(hir_id, *attr_span, kind, target)195            }196            AttributeKind::AllowInternalUnsafe(attr_span)197            | AttributeKind::AllowInternalUnstable(.., attr_span) => {198                self.check_macro_only_attr(*attr_span, span, target, attrs)199            }200            AttributeKind::RustcAllowConstFnUnstable(_, first_span) => {201                self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target)202            }203            AttributeKind::Deprecated { span: attr_span, .. } => {204                self.check_deprecated(hir_id, *attr_span, target)205            }206            AttributeKind::TargetFeature { attr_span, .. } => {207                self.check_target_feature(hir_id, *attr_span, target, attrs)208            }209            AttributeKind::RustcDumpObjectLifetimeDefaults => {210                self.check_dump_object_lifetime_defaults(hir_id);211            }212            &AttributeKind::RustcPubTransparent(attr_span) => {213                self.check_rustc_pub_transparent(attr_span, span, attrs)214            }215            AttributeKind::Naked(..) => self.check_naked(hir_id, target),216            AttributeKind::TrackCaller(attr_span) => {217                self.check_track_caller(hir_id, *attr_span, attrs, target)218            }219            AttributeKind::NonExhaustive(attr_span) => {220                self.check_non_exhaustive(*attr_span, span, target, item)221            }222            &AttributeKind::FfiPure(attr_span) => self.check_ffi_pure(attr_span, attrs),223            AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span),224            AttributeKind::Link(_, attr_span) => self.check_link(hir_id, *attr_span, target),225            AttributeKind::MacroExport { span, .. } => {226                self.check_macro_export(hir_id, *span, target)227            }228            AttributeKind::RustcLegacyConstGenerics { attr_span, fn_indexes } => {229                self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes)230            }231            AttributeKind::Doc(attr) => self.check_doc_attrs(attr, hir_id, target),232            AttributeKind::EiiImpls(impls) => self.check_eii_impl(impls, target),233            AttributeKind::RustcMustImplementOneOf { attr_span, fn_names } => {234                self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id, target)235            }236            AttributeKind::OnUnimplemented { directive } => {237                self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref())238            }239            AttributeKind::OnConst { span, .. } => {240                self.check_diagnostic_on_const(*span, hir_id, target, item)241            }242            AttributeKind::OnMove { directive } => {243                self.check_diagnostic_on_move(hir_id, directive.as_deref())244            }245            AttributeKind::OnTypeError { directive, .. } => {246                self.check_diagnostic_on_type_error(hir_id, directive.as_deref())247            }248249            // All of the following attributes have no specific checks.250            // tidy-alphabetical-start251            AttributeKind::AutomaticallyDerived => (),252            AttributeKind::CfgAttrTrace => (),253            AttributeKind::CfgTrace(..) => (),254            AttributeKind::CfiEncoding { .. } => (),255            AttributeKind::Cold => (),256            AttributeKind::CollapseDebugInfo(..) => (),257            AttributeKind::CompilerBuiltins => (),258            AttributeKind::ConstContinue(..) => {}259            AttributeKind::Coroutine => (),260            AttributeKind::Coverage(..) => (),261            AttributeKind::CrateName { .. } => (),262            AttributeKind::CrateType(..) => (),263            AttributeKind::CustomMir(..) => (),264            AttributeKind::DebuggerVisualizer(..) => (),265            AttributeKind::DefaultLibAllocator => (),266            AttributeKind::DoNotRecommend => (),267            // `#[doc]` is actually a lot more than just doc comments, so is checked below268            AttributeKind::DocComment { .. } => (),269            AttributeKind::EiiDeclaration { .. } => (),270            AttributeKind::ExportName { .. } => (),271            AttributeKind::ExportStable => (),272            AttributeKind::Feature(..) => (),273            AttributeKind::FfiConst => (),274            AttributeKind::Fundamental => (),275            AttributeKind::Ignore { .. } => (),276            AttributeKind::InstructionSet(..) => (),277            AttributeKind::InstrumentFn(..) => (),278            AttributeKind::Lang(..) => (),279            AttributeKind::LinkName { .. } => (),280            AttributeKind::LinkOrdinal { .. } => (),281            AttributeKind::LinkSection { .. } => (),282            AttributeKind::Linkage(..) => (),283            AttributeKind::LoopMatch(..) => {}284            AttributeKind::MacroEscape => (),285            AttributeKind::MacroUse { .. } => (),286            AttributeKind::Marker => (),287            AttributeKind::MoveSizeLimit { .. } => (),288            AttributeKind::MustNotSupend { .. } => (),289            AttributeKind::MustUse { .. } => (),290            AttributeKind::NeedsAllocator => (),291            AttributeKind::NeedsPanicRuntime => (),292            AttributeKind::NoBuiltins => (),293            AttributeKind::NoCore { .. } => (),294            AttributeKind::NoImplicitPrelude => (),295            AttributeKind::NoLink => (),296            AttributeKind::NoMain => (),297            AttributeKind::NoMangle(..) => (),298            AttributeKind::NoStd { .. } => (),299            AttributeKind::OnUnknown { .. } => (),300            AttributeKind::OnUnmatchedArgs { .. } => (),301            AttributeKind::Optimize(..) => (),302            AttributeKind::PanicRuntime => (),303            AttributeKind::PatchableFunctionEntry { .. } => (),304            AttributeKind::Path(..) => (),305            AttributeKind::PatternComplexityLimit { .. } => (),306            AttributeKind::PinV2(..) => (),307            AttributeKind::PreludeImport => (),308            AttributeKind::ProfilerRuntime => (),309            AttributeKind::RecursionLimit { .. } => (),310            AttributeKind::ReexportTestHarnessMain(..) => (),311            AttributeKind::RegisterTool(..) => (),312            // handled below this loop and elsewhere313            AttributeKind::Repr { .. } => (),314            AttributeKind::RustcAbi { .. } => (),315            AttributeKind::RustcAlign { .. } => {}316            AttributeKind::RustcAllocator => (),317            AttributeKind::RustcAllocatorZeroed => (),318            AttributeKind::RustcAllocatorZeroedVariant { .. } => (),319            AttributeKind::RustcAllowIncoherentImpl(..) => (),320            AttributeKind::RustcAsPtr => (),321            AttributeKind::RustcAutodiff(..) => (),322            AttributeKind::RustcBodyStability { .. } => (),323            AttributeKind::RustcBuiltinMacro { .. } => (),324            AttributeKind::RustcCaptureAnalysis => (),325            AttributeKind::RustcCguTestAttr(..) => (),326            AttributeKind::RustcClean(..) => (),327            AttributeKind::RustcCoherenceIsCore => (),328            AttributeKind::RustcCoinductive => (),329            AttributeKind::RustcComptime(_) => (),330            AttributeKind::RustcConfusables { .. } => (),331            AttributeKind::RustcConstStability { .. } => (),332            AttributeKind::RustcConstStableIndirect => (),333            AttributeKind::RustcConversionSuggestion => (),334            AttributeKind::RustcDeallocator => (),335            AttributeKind::RustcDelayedBugFromInsideQuery => (),336            AttributeKind::RustcDenyExplicitImpl => (),337            AttributeKind::RustcDeprecatedSafe2024 { .. } => (),338            AttributeKind::RustcDiagnosticItem(..) => (),339            AttributeKind::RustcDoNotConstCheck => (),340            AttributeKind::RustcDocPrimitive(..) => (),341            AttributeKind::RustcDummy => (),342            AttributeKind::RustcDumpDefParents => (),343            AttributeKind::RustcDumpDefPath(..) => (),344            AttributeKind::RustcDumpGenerics => (),345            AttributeKind::RustcDumpHiddenTypeOfOpaques => (),346            AttributeKind::RustcDumpInferredOutlives => (),347            AttributeKind::RustcDumpItemBounds => (),348            AttributeKind::RustcDumpLayout(..) => (),349            AttributeKind::RustcDumpPredicates => (),350            AttributeKind::RustcDumpSymbolName(..) => (),351            AttributeKind::RustcDumpUserArgs => (),352            AttributeKind::RustcDumpVariances => (),353            AttributeKind::RustcDumpVariancesOfOpaques => (),354            AttributeKind::RustcDumpVtable(..) => (),355            AttributeKind::RustcDynIncompatibleTrait(..) => (),356            AttributeKind::RustcEffectiveVisibility => (),357            AttributeKind::RustcEiiForeignItem => (),358            AttributeKind::RustcEvaluateWhereClauses => (),359            AttributeKind::RustcHasIncoherentInherentImpls => (),360            AttributeKind::RustcIfThisChanged(..) => (),361            AttributeKind::RustcInheritOverflowChecks => (),362            AttributeKind::RustcInsignificantDtor => (),363            AttributeKind::RustcIntrinsic => (),364            AttributeKind::RustcIntrinsicConstStableIndirect => (),365            AttributeKind::RustcLintOptDenyFieldAccess { .. } => (),366            AttributeKind::RustcLintOptTy => (),367            AttributeKind::RustcLintQueryInstability => (),368            AttributeKind::RustcLintUntrackedQueryInformation => (),369            AttributeKind::RustcMacroTransparency(_) => (),370            AttributeKind::RustcMain => (),371            AttributeKind::RustcMir(_) => (),372            AttributeKind::RustcMustMatchExhaustively(..) => (),373            AttributeKind::RustcNeverReturnsNullPtr => (),374            AttributeKind::RustcNeverTypeOptions { .. } => (),375            AttributeKind::RustcNoImplicitAutorefs => (),376            AttributeKind::RustcNoImplicitBounds => (),377            AttributeKind::RustcNoMirInline => (),378            AttributeKind::RustcNoWritable => (),379            AttributeKind::RustcNonConstTraitMethod => (),380            AttributeKind::RustcNonnullOptimizationGuaranteed => (),381            AttributeKind::RustcNounwind => (),382            AttributeKind::RustcObjcClass { .. } => (),383            AttributeKind::RustcObjcSelector { .. } => (),384            AttributeKind::RustcOffloadKernel => (),385            AttributeKind::RustcParenSugar => (),386            AttributeKind::RustcPassByValue => (),387            AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) => (),388            AttributeKind::RustcPreserveUbChecks => (),389            AttributeKind::RustcProcMacroDecls => (),390            AttributeKind::RustcReallocator => (),391            AttributeKind::RustcRegions => (),392            AttributeKind::RustcReservationImpl(..) => (),393            AttributeKind::RustcScalableVector { .. } => (),394            AttributeKind::RustcShouldNotBeCalledOnConstItems => (),395            AttributeKind::RustcSimdMonomorphizeLaneLimit(..) => (),396            AttributeKind::RustcSkipDuringMethodDispatch { .. } => (),397            AttributeKind::RustcSpecializationTrait => (),398            AttributeKind::RustcStdInternalSymbol => (),399            AttributeKind::RustcStrictCoherence(..) => (),400            AttributeKind::RustcTestMarker(..) => (),401            AttributeKind::RustcThenThisWouldNeed(..) => (),402            AttributeKind::RustcTrivialFieldReads => (),403            AttributeKind::RustcUnsafeSpecializationMarker => (),404            AttributeKind::Sanitize { .. } => {}405            AttributeKind::ShouldPanic { .. } => (),406            AttributeKind::Splat(..) => (),407            AttributeKind::Stability { .. } => (),408            AttributeKind::TestRunner(..) => (),409            AttributeKind::ThreadLocal => (),410            AttributeKind::TypeLengthLimit { .. } => (),411            AttributeKind::Unroll(..) => (),412            AttributeKind::UnstableFeatureBound(..) => (),413            AttributeKind::UnstableRemoved(..) => (),414            AttributeKind::Used { .. } => (),415            AttributeKind::WindowsSubsystem(..) => (),416            // tidy-alphabetical-end417        }418    }419420    fn check_rustc_must_implement_one_of(421        &self,422        attr_span: Span,423        list: &ThinVec<Ident>,424        hir_id: HirId,425        target: Target,426    ) {427        // Ignoring invalid targets because TyCtxt::associated_items emits bug if the target isn't valid428        // the parser has already produced an error for the target being invalid429        if !matches!(target, Target::Trait) {430            return;431        }432433        let def_id = hir_id.owner.def_id;434435        let items = self.tcx.associated_items(def_id);436        // Check that all arguments of `#[rustc_must_implement_one_of]` reference437        // functions in the trait with default implementations438        for ident in list {439            let item = items440                .filter_by_name_unhygienic(ident.name)441                .find(|item| item.ident(self.tcx) == *ident);442443            match item {444                Some(item) if matches!(item.kind, ty::AssocKind::Fn { .. }) => {445                    if !item.defaultness(self.tcx).has_value() {446                        self.tcx.dcx().emit_err(447                            diagnostics::FunctionNotHaveDefaultImplementation {448                                span: self.tcx.def_span(item.def_id),449                                note_span: attr_span,450                            },451                        );452                    }453                }454                Some(item) => {455                    self.dcx().emit_err(diagnostics::MustImplementNotFunction {456                        span: self.tcx.def_span(item.def_id),457                        span_note: diagnostics::MustImplementNotFunctionSpanNote {458                            span: attr_span,459                        },460                        note: diagnostics::MustImplementNotFunctionNote {},461                    });462                }463                None => {464                    self.dcx().emit_err(diagnostics::FunctionNotFoundInTrait { span: ident.span });465                }466            }467        }468        // Check for duplicates469470        let mut set: UnordMap<Symbol, Span> = Default::default();471472        for ident in &*list {473            if let Some(dup) = set.insert(ident.name, ident.span) {474                self.tcx.dcx().emit_err(diagnostics::FunctionNamesDuplicated {475                    spans: vec![dup, ident.span],476                });477            }478        }479    }480481    fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) {482        for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls {483            match target {484                Target::Fn | Target::Static => {}485                _ => {486                    self.dcx().emit_err(diagnostics::EiiImplTarget { span: *span });487                }488            }489490            if let EiiImplResolution::Macro(eii_macro) = resolution491                && find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe)492                && !impl_marked_unsafe493            {494                self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe {495                    span: *span,496                    name: self.tcx.item_name(*eii_macro),497                    suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion {498                        left: inner_span.shrink_to_lo(),499                        right: inner_span.shrink_to_hi(),500                    },501                });502            }503        }504    }505506    /// Checks use of generic formatting parameters in `#[diagnostic::on_unimplemented]`507    fn check_diagnostic_on_unimplemented(&self, hir_id: HirId, directive: Option<&Directive>) {508        if let Some(directive) = directive {509            if let Node::Item(Item {510                kind: ItemKind::Trait { ident: trait_name, generics, .. },511                ..512            }) = self.tcx.hir_node(hir_id)513            {514                directive.visit_params(&mut |argument_name, span| {515                    let has_generic = generics.params.iter().any(|p| {516                        if !matches!(p.kind, GenericParamKind::Lifetime { .. })517                            && let ParamName::Plain(name) = p.name518                            && name.name == argument_name519                        {520                            true521                        } else {522                            false523                        }524                    });525                    if !has_generic {526                        self.tcx.emit_node_span_lint(527                            MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,528                            hir_id,529                            span,530                            diagnostics::UnknownFormatParameterForOnUnimplementedAttr {531                                argument_name,532                                trait_name: *trait_name,533                                help: !directive.is_rustc_attr,534                            },535                        )536                    }537                })538            }539        }540    }541542    /// Checks if `#[diagnostic::on_const]` is applied to a on-const trait impl543    fn check_diagnostic_on_const(544        &self,545        attr_span: Span,546        hir_id: HirId,547        target: Target,548        item: Option<ItemLike<'_>>,549    ) {550        // We only check the non-constness here. A diagnostic for use551        // on not-trait impl items is issued during attribute parsing.552        if target == (Target::Impl { of_trait: true }) {553            match item.unwrap() {554                ItemLike::Item(it) => match it.expect_impl().constness {555                    Constness::Const { .. } => {556                        let item_span = self.tcx.hir_span(hir_id);557                        self.tcx.emit_node_span_lint(558                            MISPLACED_DIAGNOSTIC_ATTRIBUTES,559                            hir_id,560                            attr_span,561                            DiagnosticOnConstOnlyForNonConstTraitImpls { item_span },562                        );563                        return;564                    }565                    Constness::NotConst => return,566                },567                ItemLike::ForeignItem => {}568            }569        }570        // FIXME(#155570) Can we do something with generic args here?571        // regardless, we don't check the validity of generic args here572        // ...whose generics would that be, anyway? The traits' or the impls'?573    }574575    /// Checks use of generic formatting parameters in `#[diagnostic::on_move]`576    fn check_diagnostic_on_move(&self, hir_id: HirId, directive: Option<&Directive>) {577        if let Some(directive) = directive {578            if let Node::Item(Item {579                kind:580                    ItemKind::Struct(_, generics, _)581                    | ItemKind::Enum(_, generics, _)582                    | ItemKind::Union(_, generics, _),583                ..584            }) = self.tcx.hir_node(hir_id)585            {586                directive.visit_params(&mut |argument_name, span| {587                    let has_generic = generics.params.iter().any(|p| {588                        if !matches!(p.kind, GenericParamKind::Lifetime { .. })589                            && let ParamName::Plain(name) = p.name590                            && name.name == argument_name591                        {592                            true593                        } else {594                            false595                        }596                    });597                    if !has_generic {598                        self.tcx.emit_node_span_lint(599                            MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,600                            hir_id,601                            span,602                            diagnostics::OnMoveMalformedFormatLiterals { name: argument_name },603                        )604                    }605                });606            }607        }608    }609610    fn check_diagnostic_on_type_error(&self, hir_id: HirId, directive: Option<&Directive>) {611        if let Some(directive) = directive {612            if let Node::Item(Item {613                kind:614                    ItemKind::Struct(_, generics, _)615                    | ItemKind::Enum(_, generics, _)616                    | ItemKind::Union(_, generics, _),617                ..618            }) = self.tcx.hir_node(hir_id)619            {620                let generic_count = generics621                    .params622                    .iter()623                    .filter(|p| !matches!(p.kind, GenericParamKind::Lifetime { .. }))624                    .count();625626                // Enforce: at most one generic627                if generic_count != 1 {628                    self.tcx.emit_node_span_lint(629                        MALFORMED_DIAGNOSTIC_ATTRIBUTES,630                        hir_id,631                        generics.span,632                        diagnostics::OnTypeErrorNotExactlyOneGeneric { count: generic_count },633                    );634                }635636                directive.visit_params(&mut |argument_name, span| {637                    let has_generic = generics.params.iter().any(|p| {638                        if !matches!(p.kind, GenericParamKind::Lifetime { .. })639                            && let ParamName::Plain(name) = p.name640                            && name.name == argument_name641                        {642                            true643                        } else {644                            false645                        }646                    });647648                    let is_allowed = argument_name == sym::Expected || argument_name == sym::Found;649                    if !(has_generic | is_allowed) {650                        self.tcx.emit_node_span_lint(651                            MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,652                            hir_id,653                            span,654                            diagnostics::OnTypeErrorMalformedFormatLiterals { name: argument_name },655                        )656                    }657                });658            }659        }660    }661662    /// Checks if an `#[inline]` is applied to a function or a closure.663    fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) {664        match target {665            Target::Fn666            | Target::Closure667            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {668                // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.669                if let Some(did) = hir_id.as_owner()670                    && self.tcx.def_kind(did).has_codegen_attrs()671                    && kind != &InlineAttr::Never672                {673                    let attrs = self.tcx.codegen_fn_attrs(did);674                    // Not checking naked as `#[inline]` is forbidden for naked functions anyways.675                    if attrs.contains_extern_indicator() {676                        self.tcx.emit_node_span_lint(677                            UNUSED_ATTRIBUTES,678                            hir_id,679                            attr_span,680                            diagnostics::InlineIgnoredForExported,681                        );682                    }683                }684            }685            _ => {}686        }687    }688689    /// Checks if `#[naked]` is applied to a function definition.690    fn check_naked(&self, hir_id: HirId, target: Target) {691        match target {692            Target::Fn693            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {694                let fn_sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();695                let abi = fn_sig.header.abi;696                if abi.is_rustic_abi() && !self.tcx.features().naked_functions_rustic_abi() {697                    feature_err(698                        &self.tcx.sess,699                        sym::naked_functions_rustic_abi,700                        fn_sig.span,701                        format!(702                            "`#[naked]` is currently unstable on `extern \"{}\"` functions",703                            abi.as_str()704                        ),705                    )706                    .emit();707                }708            }709            _ => {}710        }711    }712713    /// Debugging aid for the `object_lifetime_default` query.714    fn check_dump_object_lifetime_defaults(&self, hir_id: HirId) {715        let tcx = self.tcx;716        let Some(owner_id) = hir_id.as_owner() else { return };717        for param in &tcx.generics_of(owner_id.def_id).own_params {718            let ty::GenericParamDefKind::Type { .. } = param.kind else { continue };719            let default = tcx.object_lifetime_default(param.def_id);720            let repr = match default {721                ObjectLifetimeDefault::Empty => "Empty".to_owned(),722                ObjectLifetimeDefault::Static => "'static".to_owned(),723                ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),724                ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),725            };726            tcx.dcx().span_err(tcx.def_span(param.def_id), repr);727        }728    }729730    /// Checks if a `#[track_caller]` is applied to a function.731    fn check_track_caller(732        &self,733        hir_id: HirId,734        attr_span: Span,735        attrs: &[Attribute],736        target: Target,737    ) {738        match target {739            Target::Fn => {740                // `#[track_caller]` is not valid on weak lang items because they are called via741                // `extern` declarations and `#[track_caller]` would alter their ABI.742                if let Some(item) = find_attr!(attrs, Lang(item) => item)743                    && item.is_weak()744                {745                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();746747                    self.dcx().emit_err(diagnostics::LangItemWithTrackCaller {748                        attr_span,749                        name: item.name(),750                        sig_span: sig.span,751                    });752                }753754                if let Some(impls) = find_attr!(attrs, EiiImpls(impls) => impls) {755                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();756                    for i in impls {757                        let name = match i.resolution {758                            EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id),759                            EiiImplResolution::Known(decl) => decl.name.name,760                            EiiImplResolution::Error(_eg) => continue,761                        };762                        self.dcx().emit_err(diagnostics::EiiWithTrackCaller {763                            attr_span,764                            name,765                            sig_span: sig.span,766                        });767                    }768                }769            }770            _ => {}771        }772    }773774    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.775    fn check_non_exhaustive(776        &self,777        attr_span: Span,778        span: Span,779        target: Target,780        item: Option<ItemLike<'_>>,781    ) {782        match target {783            Target::Struct => {784                if let Some(ItemLike::Item(hir::Item {785                    kind: hir::ItemKind::Struct(_, _, hir::VariantData::Struct { fields, .. }),786                    ..787                })) = item788                    && !fields.is_empty()789                    && fields.iter().any(|f| f.default.is_some())790                {791                    self.dcx().emit_err(diagnostics::NonExhaustiveWithDefaultFieldValues {792                        attr_span,793                        defn_span: span,794                    });795                }796            }797            _ => {}798        }799    }800801    /// Checks if the `#[target_feature]` attribute on `item` is valid.802    fn check_target_feature(803        &self,804        hir_id: HirId,805        attr_span: Span,806        target: Target,807        attrs: &[Attribute],808    ) {809        match target {810            Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)811            | Target::Fn => {812                // `#[target_feature]` is not allowed in lang items.813                if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang)814                    // Calling functions with `#[target_feature]` is815                    // not unsafe on WASM, see #84988816                    && !self.tcx.sess.target.is_like_wasm817                    && !self.tcx.sess.opts.actually_rustdoc818                {819                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();820821                    self.dcx().emit_err(diagnostics::LangItemWithTargetFeature {822                        attr_span,823                        name: lang_item.name(),824                        sig_span: sig.span,825                    });826                }827            }828            _ => {}829        }830    }831832    fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) {833        if let Some(location) = match target {834            Target::AssocTy => {835                if let DefKind::Impl { .. } =836                    self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id))837                {838                    Some("type alias in implementation block")839                } else {840                    None841                }842            }843            Target::AssocConst => {844                let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id;845                let containing_item = self.tcx.hir_expect_item(parent_def_id);846                // We can't link to trait impl's consts.847                let err = "associated constant in trait implementation block";848                match containing_item.kind {849                    ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),850                    _ => None,851                }852            }853            // we check the validity of params elsewhere854            Target::Param => return,855            Target::Expression856            | Target::Statement857            | Target::Arm858            | Target::ForeignMod859            | Target::Closure860            | Target::Impl { .. }861            | Target::WherePredicate => Some(target.name()),862            Target::ExternCrate863            | Target::Use864            | Target::Static865            | Target::Const866            | Target::Fn867            | Target::Mod868            | Target::GlobalAsm869            | Target::TyAlias870            | Target::Enum871            | Target::Variant872            | Target::Struct873            | Target::Field874            | Target::Union875            | Target::Trait876            | Target::TraitAlias877            | Target::Method(..)878            | Target::ForeignFn879            | Target::ForeignStatic880            | Target::ForeignTy881            | Target::GenericParam { .. }882            | Target::MacroDef883            | Target::PatField884            | Target::ExprField885            | Target::Crate886            | Target::MacroCall887            | Target::Delegation { .. }888            | Target::Loop889            | Target::ForLoop890            | Target::While891            | Target::Break => None,892        } {893            self.tcx.dcx().emit_err(diagnostics::DocAliasBadLocation { span, location });894            return;895        }896        if self.tcx.hir_opt_name(hir_id) == Some(alias) {897            self.tcx.dcx().emit_err(diagnostics::DocAliasNotAnAlias { span, attr_str: alias });898            return;899        }900    }901902    fn check_doc_fake_variadic(&self, span: Span, hir_id: HirId) {903        let item_kind = match self.tcx.hir_node(hir_id) {904            hir::Node::Item(item) => Some(&item.kind),905            _ => None,906        };907        match item_kind {908            Some(ItemKind::Impl(i)) => {909                let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty)910                    || if let Some(&[hir::GenericArg::Type(ty)]) = i911                        .of_trait912                        .and_then(|of_trait| of_trait.trait_ref.path.segments.last())913                        .map(|last_segment| last_segment.args().args)914                    {915                        matches!(&ty.kind, hir::TyKind::Tup([_]))916                    } else {917                        false918                    };919                if !is_valid {920                    self.dcx().emit_err(diagnostics::DocFakeVariadicNotValid { span });921                }922            }923            _ => {924                self.dcx().emit_err(diagnostics::DocKeywordOnlyImpl { span });925            }926        }927    }928929    fn check_doc_search_unbox(&self, span: Span, hir_id: HirId) {930        let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {931            self.dcx().emit_err(diagnostics::DocSearchUnboxInvalid { span });932            return;933        };934        match item.kind {935            ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _)936                if generics.params.len() != 0 => {}937            ItemKind::Trait { generics, items, .. }938                if generics.params.len() != 0939                    || items.iter().any(|item| {940                        matches!(self.tcx.def_kind(item.owner_id), DefKind::AssocTy)941                    }) => {}942            ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}943            _ => {944                self.dcx().emit_err(diagnostics::DocSearchUnboxInvalid { span });945            }946        }947    }948949    /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes.950    ///951    /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or952    /// if there are conflicting attributes for one item.953    ///954    /// `specified_inline` is used to keep track of whether we have955    /// already seen an inlining attribute for this item.956    /// If so, `specified_inline` holds the value and the span of957    /// the first `inline`/`no_inline` attribute.958    fn check_doc_inline(&self, hir_id: HirId, target: Target, inline: &[(DocInline, Span)]) {959        let span = match inline {960            [] => return,961            [(_, span)] => *span,962            [(inline, span), rest @ ..] => {963                for (inline2, span2) in rest {964                    if inline2 != inline {965                        let mut spans = MultiSpan::from_spans(vec![*span, *span2]);966                        spans.push_span_label(*span, msg!("this attribute..."));967                        spans.push_span_label(968                            *span2,969                            msg!("{\".\"}..conflicts with this attribute"),970                        );971                        self.dcx().emit_err(diagnostics::DocInlineConflict { spans });972                        return;973                    }974                }975                *span976            }977        };978979        match target {980            Target::Use | Target::ExternCrate => {}981            _ => {982                self.tcx.emit_node_span_lint(983                    INVALID_DOC_ATTRIBUTES,984                    hir_id,985                    span,986                    diagnostics::DocInlineOnlyUse {987                        attr_span: span,988                        item_span: self.tcx.hir_span(hir_id),989                    },990                );991            }992        }993    }994995    fn check_doc_masked(&self, span: Span, hir_id: HirId, target: Target) {996        if target != Target::ExternCrate {997            self.tcx.emit_node_span_lint(998                INVALID_DOC_ATTRIBUTES,999                hir_id,1000                span,1001                diagnostics::DocMaskedOnlyExternCrate {1002                    attr_span: span,1003                    item_span: self.tcx.hir_span(hir_id),1004                },1005            );1006            return;1007        }10081009        if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() {1010            self.tcx.emit_node_span_lint(1011                INVALID_DOC_ATTRIBUTES,1012                hir_id,1013                span,1014                diagnostics::DocMaskedNotExternCrateSelf {1015                    attr_span: span,1016                    item_span: self.tcx.hir_span(hir_id),1017                },1018            );1019        }1020    }10211022    fn check_doc_keyword_and_attribute(&self, span: Span, hir_id: HirId, attr_name: &'static str) {1023        let item_kind = match self.tcx.hir_node(hir_id) {1024            hir::Node::Item(item) => Some(&item.kind),1025            _ => None,1026        };1027        match item_kind {1028            Some(ItemKind::Mod(_, module)) => {1029                if !module.item_ids.is_empty() {1030                    self.dcx()1031                        .emit_err(diagnostics::DocKeywordAttributeEmptyMod { span, attr_name });1032                    return;1033                }1034            }1035            _ => {1036                self.dcx().emit_err(diagnostics::DocKeywordAttributeNotMod { span, attr_name });1037                return;1038            }1039        }1040    }10411042    /// Runs various checks on `#[doc]` attributes.1043    ///1044    /// `specified_inline` should be initialized to `None` and kept for the scope1045    /// of one item. Read the documentation of [`check_doc_inline`] for more information.1046    ///1047    /// [`check_doc_inline`]: Self::check_doc_inline1048    fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) {1049        let DocAttribute {1050            first_span: _,1051            aliases,1052            // valid pretty much anywhere, not checked here?1053            // FIXME: should we?1054            hidden: _,1055            inline,1056            // FIXME: currently unchecked1057            cfg: _,1058            // already checked in attr_parsing1059            auto_cfg: _,1060            // already checked in attr_parsing1061            auto_cfg_change: _,1062            fake_variadic,1063            keyword,1064            masked,1065            // FIXME: currently unchecked1066            notable_trait: _,1067            search_unbox,1068            // already checked in attr_parsing1069            html_favicon_url: _,1070            // already checked in attr_parsing1071            html_logo_url: _,1072            // already checked in attr_parsing1073            html_playground_url: _,1074            // already checked in attr_parsing1075            html_root_url: _,1076            // already checked in attr_parsing1077            html_no_source: _,1078            // already checked in attr_parsing1079            issue_tracker_base_url: _,1080            // already checked in attr_parsing1081            rust_logo: _,1082            // allowed anywhere1083            test_attrs: _,1084            // already checked in attr_parsing1085            no_crate_inject: _,1086            attribute,1087        } = attr;10881089        for (alias, span) in aliases {1090            self.check_doc_alias_value(*span, hir_id, target, *alias);1091        }10921093        if let Some((_, span)) = keyword {1094            self.check_doc_keyword_and_attribute(*span, hir_id, "keyword");1095        }1096        if let Some((_, span)) = attribute {1097            self.check_doc_keyword_and_attribute(*span, hir_id, "attribute");1098        }10991100        if let Some(span) = fake_variadic {1101            self.check_doc_fake_variadic(*span, hir_id);1102        }11031104        if let Some(span) = search_unbox {1105            self.check_doc_search_unbox(*span, hir_id);1106        }11071108        self.check_doc_inline(hir_id, target, inline);11091110        if let Some(span) = masked {1111            self.check_doc_masked(*span, hir_id, target);1112        }1113    }11141115    fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) {1116        if find_attr!(attrs, FfiConst) {1117            // `#[ffi_const]` functions cannot be `#[ffi_pure]`1118            self.dcx().emit_err(diagnostics::BothFfiConstAndPure { attr_span });1119        }1120    }11211122    /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.1123    fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {1124        if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)1125            && matches!(1126                param.kind,1127                hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }1128            )1129            && matches!(param.source, hir::GenericParamSource::Generics)1130            && let parent_hir_id = self.tcx.parent_hir_id(hir_id)1131            && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)1132            && let hir::ItemKind::Impl(impl_) = item.kind1133            && let Some(of_trait) = impl_.of_trait1134            && let Some(def_id) = of_trait.trait_ref.trait_def_id()1135            && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)1136        {1137            return;1138        }11391140        self.dcx().emit_err(diagnostics::InvalidMayDangle { attr_span });1141    }11421143    /// Checks if `#[link]` is applied to an item other than a foreign module.1144    fn check_link(&self, hir_id: HirId, attr_span: Span, target: Target) {1145        if target != Target::ForeignMod {1146            return; // Checked by attribute parser1147        }11481149        if let hir::Node::Item(item) = self.tcx.hir_node(hir_id)1150            && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item1151            && !matches!(abi, ExternAbi::Rust)1152        {1153            return;1154        }11551156        self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr_span, diagnostics::Link);1157    }11581159    /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.1160    fn check_rustc_legacy_const_generics(1161        &self,1162        item: Option<ItemLike<'_>>,1163        attr_span: Span,1164        index_list: &ThinVec<(usize, Span)>,1165    ) {1166        let Some(ItemLike::Item(Item {1167            kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. },1168            ..1169        })) = item1170        else {1171            // No error here, since it's already given by the parser1172            return;1173        };11741175        for param in generics.params {1176            match param.kind {1177                hir::GenericParamKind::Const { .. } => {}1178                _ => {1179                    self.dcx().emit_err(diagnostics::RustcLegacyConstGenericsOnly {1180                        attr_span,1181                        param_span: param.span,1182                    });1183                    return;1184                }1185            }1186        }11871188        if index_list.len() != generics.params.len() {1189            self.dcx().emit_err(diagnostics::RustcLegacyConstGenericsIndex {1190                attr_span,1191                generics_span: generics.span,1192            });1193            return;1194        }11951196        let arg_count = decl.inputs.len() + generics.params.len();1197        for (index, span) in index_list {1198            if *index >= arg_count {1199                self.dcx().emit_err(diagnostics::RustcLegacyConstGenericsIndexExceed {1200                    span: *span,1201                    arg_count,1202                });1203            }1204        }1205    }12061207    /// Checks if the `#[repr]` attributes on `item` are valid.1208    fn check_repr(1209        &self,1210        attrs: &[Attribute],1211        span: Span,1212        target: Target,1213        item: Option<ItemLike<'_>>,1214        hir_id: HirId,1215    ) {1216        // Extract the names of all repr hints, e.g., [foo, bar, align] for:1217        // ```1218        // #[repr(foo)]1219        // #[repr(bar, align(8))]1220        // ```1221        let (reprs, _first_attr_span) =1222            find_attr!(attrs, Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span)))1223                .unwrap_or((&[], None));12241225        let mut int_reprs = 0;1226        let mut is_explicit_rust = false;1227        let mut is_c = false;1228        let mut is_simd = false;1229        let mut is_transparent = false;12301231        for (repr, _repr_span) in reprs {1232            match repr {1233                ReprAttr::ReprRust => {1234                    is_explicit_rust = true;1235                }1236                ReprAttr::ReprC => {1237                    is_c = true;1238                }1239                ReprAttr::ReprAlign(..) => {}1240                ReprAttr::ReprPacked(_) => {}1241                ReprAttr::ReprSimd => {1242                    is_simd = true;1243                }1244                ReprAttr::ReprTransparent => {1245                    is_transparent = true;1246                }1247                ReprAttr::ReprInt(_) => {1248                    int_reprs += 1;1249                }1250            };1251        }12521253        // Just point at all repr hints if there are any incompatibilities.1254        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.1255        let hint_spans = reprs.iter().map(|(_, span)| *span);12561257        // Error on repr(transparent, <anything else>).1258        if is_transparent && reprs.len() > 1 {1259            let hint_spans = hint_spans.clone().collect();1260            self.dcx().emit_err(diagnostics::TransparentIncompatible {1261                hint_spans,1262                target: target.to_string(),1263            });1264        }1265        // Error on `#[repr(transparent)]` in combination with1266        // `#[rustc_pass_indirectly_in_non_rustic_abis]`1267        if is_transparent1268            && let Some(&pass_indirectly_span) =1269                find_attr!(attrs, RustcPassIndirectlyInNonRusticAbis(span) => span)1270        {1271            self.dcx().emit_err(diagnostics::TransparentIncompatible {1272                hint_spans: vec![span, pass_indirectly_span],1273                target: target.to_string(),1274            });1275        }1276        if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {1277            let hint_spans = hint_spans.clone().collect();1278            self.dcx().emit_err(diagnostics::ReprConflicting { hint_spans });1279        }1280        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)1281        if (int_reprs > 1)1282            || (is_simd && is_c)1283            || (int_reprs == 11284                && is_c1285                && item.is_some_and(|item| {1286                    if let ItemLike::Item(item) = item { is_c_like_enum(item) } else { false }1287                }))1288        {1289            self.tcx.emit_node_span_lint(1290                CONFLICTING_REPR_HINTS,1291                hir_id,1292                hint_spans.collect::<Vec<Span>>(),1293                diagnostics::ReprConflictingLint,1294            );1295        }1296    }12971298    /// Outputs an error for attributes that can only be applied to macros, such as1299    /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`.1300    /// (Allows proc_macro functions)1301    // FIXME(jdonszelmann): if possible, move to attr parsing1302    fn check_macro_only_attr(1303        &self,1304        attr_span: Span,1305        span: Span,1306        target: Target,1307        attrs: &[Attribute],1308    ) {1309        match target {1310            Target::Fn => {1311                for attr in attrs {1312                    if attr.is_proc_macro_attr() {1313                        // return on proc macros1314                        return;1315                    }1316                }1317                self.tcx.dcx().emit_err(diagnostics::MacroOnlyAttribute { attr_span, span });1318            }1319            _ => {}1320        }1321    }13221323    /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.1324    /// (Allows proc_macro functions)1325    fn check_rustc_allow_const_fn_unstable(1326        &self,1327        hir_id: HirId,1328        attr_span: Span,1329        span: Span,1330        target: Target,1331    ) {1332        match target {1333            Target::Fn | Target::Method(_) => {1334                if !self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) {1335                    self.tcx1336                        .dcx()1337                        .emit_err(diagnostics::RustcAllowConstFnUnstable { attr_span, span });1338                }1339            }1340            _ => {}1341        }1342    }13431344    fn check_deprecated(&self, hir_id: HirId, attr_span: Span, target: Target) {1345        match target {1346            Target::AssocConst | Target::Method(..) | Target::AssocTy1347                if self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id))1348                    == DefKind::Impl { of_trait: true } =>1349            {1350                self.tcx.emit_node_span_lint(1351                    UNUSED_ATTRIBUTES,1352                    hir_id,1353                    attr_span,1354                    diagnostics::DeprecatedAnnotationHasNoEffect { span: attr_span },1355                );1356            }1357            _ => {}1358        }1359    }13601361    fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) {1362        if target != Target::MacroDef {1363            return;1364        }13651366        // special case when `#[macro_export]` is applied to a macro 2.01367        let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro();1368        let is_decl_macro = !macro_definition.macro_rules;13691370        if is_decl_macro {1371            self.tcx.emit_node_span_lint(1372                UNUSED_ATTRIBUTES,1373                hir_id,1374                attr_span,1375                diagnostics::MacroExport::OnDeclMacro,1376            );1377        }1378    }13791380    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {1381        // Warn on useless empty attributes.1382        // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`1383        let note =1384            if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])1385                && attr.meta_item_list().is_some_and(|list| list.is_empty())1386            {1387                diagnostics::UnusedNote::EmptyList { name: attr.name().unwrap() }1388            } else if attr.has_any_name(&[1389                sym::allow,1390                sym::warn,1391                sym::deny,1392                sym::forbid,1393                sym::expect,1394            ]) && let Some(meta) = attr.meta_item_list()1395                && let [meta] = meta.as_slice()1396                && let Some(item) = meta.meta_item()1397                && let MetaItemKind::NameValue(_) = &item.kind1398                && item.path == sym::reason1399            {1400                diagnostics::UnusedNote::NoLints { name: attr.name().unwrap() }1401            } else if attr.has_any_name(&[1402                sym::allow,1403                sym::warn,1404                sym::deny,1405                sym::forbid,1406                sym::expect,1407            ]) && let Some(meta) = attr.meta_item_list()1408                && meta.iter().any(|meta| {1409                    meta.meta_item().map_or(false, |item| {1410                        item.path == sym::linker_messages || item.path == sym::linker_info1411                    })1412                })1413            {1414                if hir_id != CRATE_HIR_ID {1415                    match style {1416                        Some(ast::AttrStyle::Outer) => {1417                            let attr_span = attr.span();1418                            let bang_position = self1419                                .tcx1420                                .sess1421                                .source_map()1422                                .span_until_char(attr_span, '[')1423                                .shrink_to_hi();14241425                            self.tcx.emit_node_span_lint(1426                                UNUSED_ATTRIBUTES,1427                                hir_id,1428                                attr_span,1429                                diagnostics::OuterCrateLevelAttr {1430                                    suggestion: diagnostics::OuterCrateLevelAttrSuggestion {1431                                        bang_position,1432                                    },1433                                },1434                            )1435                        }1436                        Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(1437                            UNUSED_ATTRIBUTES,1438                            hir_id,1439                            attr.span(),1440                            diagnostics::InnerCrateLevelAttr,1441                        ),1442                    };1443                    return;1444                } else {1445                    let never_needs_link = self1446                        .tcx1447                        .crate_types()1448                        .iter()1449                        .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib));1450                    if never_needs_link {1451                        diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly1452                    } else {1453                        return;1454                    }1455                }1456            } else if hir_id == CRATE_HIR_ID1457                && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])1458                && let Some(meta) = attr.meta_item_list()1459                && meta.iter().any(|meta| {1460                    meta.meta_item().is_some_and(|item| item.path == sym::dead_code_pub_in_binary)1461                })1462                && !self.tcx.crate_types().contains(&CrateType::Executable)1463            {1464                diagnostics::UnusedNote::NoEffectDeadCodePubInBinary1465            } else if attr.has_name(sym::default_method_body_is_const) {1466                diagnostics::UnusedNote::DefaultMethodBodyConst1467            } else {1468                return;1469            };14701471        self.tcx.emit_node_span_lint(1472            UNUSED_ATTRIBUTES,1473            hir_id,1474            attr.span(),1475            diagnostics::Unused { attr_span: attr.span(), note },1476        );1477    }14781479    /// A best effort attempt to create an error for a mismatching proc macro signature.1480    ///1481    /// If this best effort goes wrong, it will just emit a worse error later (see #102923)1482    fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {1483        if target != Target::Fn {1484            return;1485        }14861487        let tcx = self.tcx;1488        let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else {1489            return;1490        };1491        let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else {1492            return;1493        };14941495        let def_id = hir_id.expect_owner().def_id;1496        let param_env = ty::ParamEnv::empty();14971498        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());1499        let ocx = ObligationCtxt::new_with_diagnostics(&infcx);15001501        let span = tcx.def_span(def_id);1502        let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id());1503        let sig = tcx.liberate_late_bound_regions(1504            def_id.to_def_id(),1505            tcx.fn_sig(def_id).instantiate(tcx, fresh_args).skip_norm_wip(),1506        );15071508        let mut cause = ObligationCause::misc(span, def_id);1509        let sig = ocx.normalize(&cause, param_env, Unnormalized::new_wip(sig));15101511        // proc macro is not WF.1512        let errors = ocx.try_evaluate_obligations();1513        if !errors.is_empty() {1514            return;1515        }15161517        let expected_sig = tcx.mk_fn_sig_safe_rust_abi(1518            std::iter::repeat_n(1519                token_stream,1520                match kind {1521                    ProcMacroKind::Attribute => 2,1522                    ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,1523                },1524            ),1525            token_stream,1526        );15271528        if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) {1529            let mut diag = tcx.dcx().create_err(diagnostics::ProcMacroBadSig { span, kind });15301531            let hir_sig = tcx.hir_fn_sig_by_hir_id(hir_id);1532            if let Some(hir_sig) = hir_sig {1533                match terr {1534                    TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {1535                        if let Some(ty) = hir_sig.decl.inputs.get(idx) {1536                            diag.span(ty.span);1537                            cause.span = ty.span;1538                        } else if idx == hir_sig.decl.inputs.len() {1539                            let span = hir_sig.decl.output.span();1540                            diag.span(span);1541                            cause.span = span;1542                        }1543                    }1544                    TypeError::ArgCount => {1545                        if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {1546                            diag.span(ty.span);1547                            cause.span = ty.span;1548                        }1549                    }1550                    TypeError::SafetyMismatch(_) => {1551                        // FIXME: Would be nice if we had a span here..1552                    }1553                    TypeError::AbiMismatch(_) => {1554                        // FIXME: Would be nice if we had a span here..1555                    }1556                    TypeError::VariadicMismatch(_) => {1557                        // FIXME: Would be nice if we had a span here..1558                    }1559                    _ => {}1560                }1561            }15621563            infcx.err_ctxt().note_type_err(1564                &mut diag,1565                &cause,1566                None,1567                Some(param_env.and(ValuePairs::PolySigs(ExpectedFound {1568                    expected: ty::Binder::dummy(expected_sig),1569                    found: ty::Binder::dummy(sig),1570                }))),1571                terr,1572                false,1573                None,1574            );1575            diag.emit();1576            self.abort.set(true);1577        }15781579        let errors = ocx.evaluate_obligations_error_on_ambiguity();1580        if !errors.is_empty() {1581            infcx.err_ctxt().report_fulfillment_errors(errors);1582            self.abort.set(true);1583        }1584    }15851586    fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {1587        if !find_attr!(attrs, Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))1588            .unwrap_or(false)1589        {1590            self.dcx().emit_err(diagnostics::RustcPubTransparent { span, attr_span });1591        }1592    }15931594    fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) {1595        if let (Target::Closure, None) = (1596            target,1597            find_attr!(attrs, Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span),1598        ) {1599            let is_coro = matches!(1600                self.tcx.hir_expect_expr(hir_id).kind,1601                hir::ExprKind::Closure(hir::Closure {1602                    kind: hir::ClosureKind::Coroutine(..) | hir::ClosureKind::CoroutineClosure(..),1603                    ..1604                })1605            );1606            let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id();1607            let parent_span = self.tcx.def_span(parent_did);16081609            if let Some(attr_span) = find_attr!(1610                self.tcx, parent_did,1611                Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span1612            ) && is_coro1613            {1614                self.dcx()1615                    .emit_err(diagnostics::RustcForceInlineCoro { attr_span, span: parent_span });1616            }1617        }1618    }16191620    fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) {1621        if let Some(export_name_span) =1622            find_attr!(attrs, ExportName { span: export_name_span, .. } => *export_name_span)1623            && let Some(no_mangle_span) =1624                find_attr!(attrs, NoMangle(no_mangle_span) => *no_mangle_span)1625        {1626            let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 {1627                "#[unsafe(no_mangle)]"1628            } else {1629                "#[no_mangle]"1630            };1631            let export_name_attr = if export_name_span.edition() >= Edition::Edition2024 {1632                "#[unsafe(export_name)]"1633            } else {1634                "#[export_name]"1635            };16361637            self.tcx.emit_node_span_lint(1638                lint::builtin::UNUSED_ATTRIBUTES,1639                hir_id,1640                no_mangle_span,1641                diagnostics::MixedExportNameAndNoMangle {1642                    no_mangle_span,1643                    export_name_span,1644                    no_mangle_attr,1645                    export_name_attr,1646                },1647            );1648        }1649    }16501651    fn check_optimize_and_inline(&self, attrs: &[Attribute]) {1652        if let Some(optimize_span) =1653            find_attr!(attrs, Optimize(OptimizeAttr::DoNotOptimize, span) => *span)1654            && let Some((inline_attr, inline_span)) =1655                find_attr!(attrs, Inline(inline_attr, span) => (inline_attr, *span))1656            && inline_attr != &InlineAttr::Never1657        {1658            self.dcx()1659                .emit_err(diagnostics::BothOptimizeNoneAndInline { optimize_span, inline_span });1660        }1661    }1662}16631664impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {1665    type NestedFilter = nested_filter::OnlyBodies;16661667    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {1668        self.tcx1669    }16701671    fn visit_item(&mut self, item: &'tcx Item<'tcx>) {1672        // Historically we've run more checks on non-exported than exported macros,1673        // so this lets us continue to run them while maintaining backwards compatibility.1674        // In the long run, the checks should be harmonized.1675        if let ItemKind::Macro(_, macro_def, _) = item.kind {1676            let def_id = item.owner_id.to_def_id();1677            if macro_def.macro_rules && !find_attr!(self.tcx, def_id, MacroExport { .. }) {1678                check_non_exported_macro_for_invalid_attrs(self.tcx, item);1679            }1680        }16811682        let target = Target::from_item(item);1683        self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));1684        intravisit::walk_item(self, item)1685    }16861687    fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) {1688        self.check_attributes(1689            where_predicate.hir_id,1690            where_predicate.span,1691            Target::WherePredicate,1692            None,1693        );1694        intravisit::walk_where_predicate(self, where_predicate)1695    }16961697    fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {1698        let target = Target::from_generic_param(generic_param);1699        self.check_attributes(generic_param.hir_id, generic_param.span, target, None);1700        intravisit::walk_generic_param(self, generic_param)1701    }17021703    fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {1704        let target = Target::from_trait_item(trait_item);1705        self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);1706        intravisit::walk_trait_item(self, trait_item)1707    }17081709    fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {1710        self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);1711        intravisit::walk_field_def(self, struct_field);1712    }17131714    fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {1715        self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);1716        intravisit::walk_arm(self, arm);1717    }17181719    fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {1720        let target = Target::from_foreign_item(f_item);1721        self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));1722        intravisit::walk_foreign_item(self, f_item)1723    }17241725    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {1726        let target = target_from_impl_item(self.tcx, impl_item);1727        self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);1728        intravisit::walk_impl_item(self, impl_item)1729    }17301731    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {1732        // When checking statements ignore expressions, they will be checked later.1733        if let hir::StmtKind::Let(l) = stmt.kind {1734            self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);1735        }1736        intravisit::walk_stmt(self, stmt)1737    }17381739    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {1740        let target = match expr.kind {1741            hir::ExprKind::Closure { .. } => Target::Closure,1742            _ => Target::Expression,1743        };17441745        self.check_attributes(expr.hir_id, expr.span, target, None);1746        intravisit::walk_expr(self, expr)1747    }17481749    fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {1750        self.check_attributes(field.hir_id, field.span, Target::ExprField, None);1751        intravisit::walk_expr_field(self, field)1752    }17531754    fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {1755        self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);1756        intravisit::walk_variant(self, variant)1757    }17581759    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {1760        self.check_attributes(param.hir_id, param.span, Target::Param, None);17611762        intravisit::walk_param(self, param);1763    }17641765    fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {1766        self.check_attributes(field.hir_id, field.span, Target::PatField, None);1767        intravisit::walk_pat_field(self, field);1768    }1769}17701771fn is_c_like_enum(item: &Item<'_>) -> bool {1772    if let ItemKind::Enum(_, _, ref def) = item.kind {1773        for variant in def.variants {1774            match variant.data {1775                hir::VariantData::Unit(..) => { /* continue */ }1776                _ => return false,1777            }1778        }1779        true1780    } else {1781        false1782    }1783}17841785fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {1786    let attrs = tcx.hir_attrs(item.hir_id());17871788    if let Some(attr_span) =1789        find_attr!(attrs, Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span)1790    {1791        tcx.dcx().emit_err(diagnostics::NonExportedMacroInvalidAttrs { attr_span });1792    }1793}17941795fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {1796    let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };1797    tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor);1798    if module_def_id.to_local_def_id().is_top_level_module() {1799        check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);1800    }1801    if check_attr_visitor.abort.get() {1802        tcx.dcx().abort_if_errors()1803    }1804}18051806pub(crate) fn provide(providers: &mut Providers) {1807    *providers = Providers { check_mod_attrs, ..*providers };1808}18091810fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool {1811    matches!(&self_ty.kind, hir::TyKind::Tup([_]))1812        || if let hir::TyKind::FnPtr(fn_ptr_ty) = &self_ty.kind {1813            fn_ptr_ty.decl.inputs.len() == 11814        } else {1815            false1816        }1817        || (if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &self_ty.kind1818            && let Some(&[hir::GenericArg::Type(ty)]) =1819                path.segments.last().map(|last| last.args().args)1820        {1821            doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty())1822        } else {1823            false1824        })1825}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.