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.