Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
// not unsafe on WASM, see #84988
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 ReprAttr, SanitizerSet,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_FORMAT_LITERALS,43 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::errors;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 let mut style = None;127 match attr {128 Attribute::Parsed(AttributeKind::ProcMacro) => {129 self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)130 }131 Attribute::Parsed(AttributeKind::ProcMacroAttribute) => {132 self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);133 }134 Attribute::Parsed(AttributeKind::ProcMacroDerive { .. }) => {135 self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)136 }137 Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below138 Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {139 self.check_inline(hir_id, *attr_span, kind, target)140 }141 Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => {142 self.check_loop_match(hir_id, *attr_span, target)143 }144 Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {145 self.check_const_continue(hir_id, *attr_span, target)146 }147 Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span) | AttributeKind::AllowInternalUnstable(.., attr_span)) => {148 self.check_macro_only_attr(*attr_span, span, target, attrs)149 }150 Attribute::Parsed(AttributeKind::RustcAllowConstFnUnstable(_, first_span)) => {151 self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target)152 }153 Attribute::Parsed(AttributeKind::Deprecated { span: attr_span, .. }) => {154 self.check_deprecated(hir_id, *attr_span, target)155 }156 Attribute::Parsed(AttributeKind::TargetFeature{ attr_span, ..}) => {157 self.check_target_feature(hir_id, *attr_span, target, attrs)158 }159 Attribute::Parsed(AttributeKind::RustcDumpObjectLifetimeDefaults) => {160 self.check_dump_object_lifetime_defaults(hir_id);161 }162 &Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => {163 self.check_rustc_pub_transparent(attr_span, span, attrs)164 }165 Attribute::Parsed(AttributeKind::RustcAlign {..}) => {166167 }168 Attribute::Parsed(AttributeKind::Naked(..)) => {169 self.check_naked(hir_id, target)170 }171 Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {172 self.check_track_caller(hir_id, *attr_span, attrs, target)173 }174 Attribute::Parsed(AttributeKind::NonExhaustive(attr_span)) => {175 self.check_non_exhaustive(*attr_span, span, target, item)176 }177 &Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => {178 self.check_ffi_pure(attr_span, attrs)179 }180 Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {181 self.check_may_dangle(hir_id, *attr_span)182 }183 &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => {184 self.check_sanitize(attr_span, on_set | off_set, span, target);185 },186 Attribute::Parsed(AttributeKind::Link(_, attr_span)) => {187 self.check_link(hir_id, *attr_span, span, target)188 },189 Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => {190 self.check_macro_export(hir_id, *span, target)191 },192 Attribute::Parsed(AttributeKind::RustcLegacyConstGenerics{attr_span, fn_indexes}) => {193 self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes)194 },195 Attribute::Parsed(AttributeKind::Doc(attr)) => self.check_doc_attrs(attr, hir_id, target),196 Attribute::Parsed(AttributeKind::EiiImpls(impls)) => {197 self.check_eii_impl(impls, target)198 },199 Attribute::Parsed(AttributeKind::RustcMustImplementOneOf { attr_span, fn_names }) => {200 self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target)201 },202 Attribute::Parsed(AttributeKind::OnUnimplemented{directive}) => {self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref())},203 Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)},204 Attribute::Parsed(AttributeKind::OnMove { directive }) => {205 self.check_diagnostic_on_move(hir_id, directive.as_deref())206 },207 Attribute::Parsed(208 // tidy-alphabetical-start209 AttributeKind::RustcAllowIncoherentImpl(..)210 | AttributeKind::AutomaticallyDerived211 | AttributeKind::CfgAttrTrace212 | AttributeKind::CfgTrace(..)213 | AttributeKind::CfiEncoding { .. }214 | AttributeKind::Cold215 | AttributeKind::CollapseDebugInfo(..)216 | AttributeKind::CompilerBuiltins217 | AttributeKind::Coroutine218 | AttributeKind::Coverage (..)219 | AttributeKind::CrateName { .. }220 | AttributeKind::CrateType(..)221 | AttributeKind::CustomMir(..)222 | AttributeKind::DebuggerVisualizer(..)223 | AttributeKind::DefaultLibAllocator224 | AttributeKind::DoNotRecommend225 // `#[doc]` is actually a lot more than just doc comments, so is checked below226 | AttributeKind::DocComment {..}227 | AttributeKind::EiiDeclaration { .. }228 | AttributeKind::ExportName { .. }229 | AttributeKind::ExportStable230 | AttributeKind::Feature(..)231 | AttributeKind::FfiConst232 | AttributeKind::Fundamental233 | AttributeKind::Ignore { .. }234 | AttributeKind::InstructionSet(..)235 | AttributeKind::Lang(..)236 | AttributeKind::LinkName { .. }237 | AttributeKind::LinkOrdinal { .. }238 | AttributeKind::LinkSection { .. }239 | AttributeKind::Linkage(..)240 | AttributeKind::MacroEscape241 | AttributeKind::MacroUse { .. }242 | AttributeKind::Marker243 | AttributeKind::MoveSizeLimit { .. }244 | AttributeKind::MustNotSupend { .. }245 | AttributeKind::MustUse { .. }246 | AttributeKind::NeedsAllocator247 | AttributeKind::NeedsPanicRuntime248 | AttributeKind::NoBuiltins249 | AttributeKind::NoCore { .. }250 | AttributeKind::NoImplicitPrelude251 | AttributeKind::NoLink252 | AttributeKind::NoMain253 | AttributeKind::NoMangle(..)254 | AttributeKind::NoStd { .. }255 | AttributeKind::OnUnknown { .. }256 | AttributeKind::OnUnmatchArgs { .. }257 | AttributeKind::Optimize(..)258 | AttributeKind::PanicRuntime259 | AttributeKind::PatchableFunctionEntry { .. }260 | AttributeKind::Path(..)261 | AttributeKind::PatternComplexityLimit { .. }262 | AttributeKind::PinV2263 | AttributeKind::PreludeImport264 | AttributeKind::ProfilerRuntime265 | AttributeKind::RecursionLimit { .. }266 | AttributeKind::ReexportTestHarnessMain(..)267 | AttributeKind::RegisterTool(..)268 // handled below this loop and elsewhere269 | AttributeKind::Repr { .. }270 | AttributeKind::RustcAbi { .. }271 | AttributeKind::RustcAllocator272 | AttributeKind::RustcAllocatorZeroed273 | AttributeKind::RustcAllocatorZeroedVariant { .. }274 | AttributeKind::RustcAsPtr275 | AttributeKind::RustcAutodiff(..)276 | AttributeKind::RustcBodyStability { .. }277 | AttributeKind::RustcBuiltinMacro { .. }278 | AttributeKind::RustcCaptureAnalysis279 | AttributeKind::RustcCguTestAttr(..)280 | AttributeKind::RustcClean(..)281 | AttributeKind::RustcCoherenceIsCore282 | AttributeKind::RustcCoinductive283 | AttributeKind::RustcConfusables { .. }284 | AttributeKind::RustcConstStability { .. }285 | AttributeKind::RustcConstStableIndirect286 | AttributeKind::RustcConversionSuggestion287 | AttributeKind::RustcDeallocator288 | AttributeKind::RustcDelayedBugFromInsideQuery289 | AttributeKind::RustcDenyExplicitImpl290 | AttributeKind::RustcDeprecatedSafe2024 {..}291 | AttributeKind::RustcDiagnosticItem(..)292 | AttributeKind::RustcDoNotConstCheck293 | AttributeKind::RustcDocPrimitive(..)294 | AttributeKind::RustcDummy295 | AttributeKind::RustcDumpDefParents296 | AttributeKind::RustcDumpDefPath(..)297 | AttributeKind::RustcDumpHiddenTypeOfOpaques298 | AttributeKind::RustcDumpInferredOutlives299 | AttributeKind::RustcDumpItemBounds300 | AttributeKind::RustcDumpLayout(..)301 | AttributeKind::RustcDumpPredicates302 | AttributeKind::RustcDumpSymbolName(..)303 | AttributeKind::RustcDumpUserArgs304 | AttributeKind::RustcDumpVariances305 | AttributeKind::RustcDumpVariancesOfOpaques306 | AttributeKind::RustcDumpVtable(..)307 | AttributeKind::RustcDynIncompatibleTrait(..)308 | AttributeKind::RustcEffectiveVisibility309 | AttributeKind::RustcEiiForeignItem310 | AttributeKind::RustcEvaluateWhereClauses311 | AttributeKind::RustcHasIncoherentInherentImpls312 | AttributeKind::RustcIfThisChanged(..)313 | AttributeKind::RustcInheritOverflowChecks314 | AttributeKind::RustcInsignificantDtor315 | AttributeKind::RustcIntrinsic316 | AttributeKind::RustcIntrinsicConstStableIndirect317 | AttributeKind::RustcLintOptDenyFieldAccess { .. }318 | AttributeKind::RustcLintOptTy319 | AttributeKind::RustcLintQueryInstability320 | AttributeKind::RustcLintUntrackedQueryInformation321 | AttributeKind::RustcMacroTransparency(_)322 | AttributeKind::RustcMain323 | AttributeKind::RustcMir(_)324 | AttributeKind::RustcMustMatchExhaustively(..)325 | AttributeKind::RustcNeverReturnsNullPtr326 | AttributeKind::RustcNeverTypeOptions {..}327 | AttributeKind::RustcNoImplicitAutorefs328 | AttributeKind::RustcNoImplicitBounds329 | AttributeKind::RustcNoMirInline330 | AttributeKind::RustcNoWritable331 | AttributeKind::RustcNonConstTraitMethod332 | AttributeKind::RustcNonnullOptimizationGuaranteed333 | AttributeKind::RustcNounwind334 | AttributeKind::RustcObjcClass { .. }335 | AttributeKind::RustcObjcSelector { .. }336 | AttributeKind::RustcOffloadKernel337 | AttributeKind::RustcParenSugar338 | AttributeKind::RustcPassByValue339 | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)340 | AttributeKind::RustcPreserveUbChecks341 | AttributeKind::RustcProcMacroDecls342 | AttributeKind::RustcReallocator343 | AttributeKind::RustcRegions344 | AttributeKind::RustcReservationImpl(..)345 | AttributeKind::RustcScalableVector { .. }346 | AttributeKind::RustcShouldNotBeCalledOnConstItems347 | AttributeKind::RustcSimdMonomorphizeLaneLimit(..)348 | AttributeKind::RustcSkipDuringMethodDispatch { .. }349 | AttributeKind::RustcSpecializationTrait350 | AttributeKind::RustcStdInternalSymbol351 | AttributeKind::RustcStrictCoherence(..)352 | AttributeKind::RustcTestMarker(..)353 | AttributeKind::RustcThenThisWouldNeed(..)354 | AttributeKind::RustcTrivialFieldReads355 | AttributeKind::RustcUnsafeSpecializationMarker356 | AttributeKind::ShouldPanic { .. }357 | AttributeKind::Stability { .. }358 | AttributeKind::TestRunner(..)359 | AttributeKind::ThreadLocal360 | AttributeKind::TypeLengthLimit { .. }361 | AttributeKind::UnstableFeatureBound(..)362 | AttributeKind::UnstableRemoved(..)363 | AttributeKind::Used { .. }364 | AttributeKind::WindowsSubsystem(..)365 // tidy-alphabetical-end366 ) => { /* do nothing */ }367 Attribute::Unparsed(attr_item) => {368 style = Some(attr_item.style);369 match attr.path().as_slice() {370 [371 // ok372 sym::allow373 | sym::expect374 | sym::warn375 | sym::deny376 | sym::forbid,377 ..378 ] => {}379 [name, rest@..] => {380 match BUILTIN_ATTRIBUTE_MAP.get(name) {381 Some(_) => {382 if rest.len() > 0 && AttributeParser::is_parsed_attribute(slice::from_ref(name)) {383 // Check if we tried to use a builtin attribute as an attribute namespace, like `#[must_use::skip]`.384 // This check is here to solve https://github.com/rust-lang/rust/issues/137590385 // An error is already produced for this case elsewhere386 continue387 }388389 span_bug!(390 attr.span(),391 "builtin attribute {name:?} not handled by `CheckAttrVisitor`"392 )393 }394 None => (),395 }396 }397 [] => unreachable!(),398 }399 }400 }401402 self.check_unused_attribute(hir_id, attr, style)403 }404405 self.check_repr(attrs, span, target, item, hir_id);406 self.check_rustc_force_inline(hir_id, attrs, target);407 self.check_mix_no_mangle_export(hir_id, attrs);408 }409410 fn check_rustc_must_implement_one_of(411 &self,412 attr_span: Span,413 list: &ThinVec<Ident>,414 hir_id: HirId,415 target: Target,416 ) {417 // Ignoring invalid targets because TyCtxt::associated_items emits bug if the target isn't valid418 // the parser has already produced an error for the target being invalid419 if !matches!(target, Target::Trait) {420 return;421 }422423 let def_id = hir_id.owner.def_id;424425 let items = self.tcx.associated_items(def_id);426 // Check that all arguments of `#[rustc_must_implement_one_of]` reference427 // functions in the trait with default implementations428 for ident in list {429 let item = items430 .filter_by_name_unhygienic(ident.name)431 .find(|item| item.ident(self.tcx) == *ident);432433 match item {434 Some(item) if matches!(item.kind, ty::AssocKind::Fn { .. }) => {435 if !item.defaultness(self.tcx).has_value() {436 self.tcx.dcx().emit_err(errors::FunctionNotHaveDefaultImplementation {437 span: self.tcx.def_span(item.def_id),438 note_span: attr_span,439 });440 }441 }442 Some(item) => {443 self.dcx().emit_err(errors::MustImplementNotFunction {444 span: self.tcx.def_span(item.def_id),445 span_note: errors::MustImplementNotFunctionSpanNote { span: attr_span },446 note: errors::MustImplementNotFunctionNote {},447 });448 }449 None => {450 self.dcx().emit_err(errors::FunctionNotFoundInTrait { span: ident.span });451 }452 }453 }454 // Check for duplicates455456 let mut set: UnordMap<Symbol, Span> = Default::default();457458 for ident in &*list {459 if let Some(dup) = set.insert(ident.name, ident.span) {460 self.tcx461 .dcx()462 .emit_err(errors::FunctionNamesDuplicated { spans: vec![dup, ident.span] });463 }464 }465 }466467 fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) {468 for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls {469 match target {470 Target::Fn | Target::Static => {}471 _ => {472 self.dcx().emit_err(errors::EiiImplTarget { span: *span });473 }474 }475476 if let EiiImplResolution::Macro(eii_macro) = resolution477 && find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe)478 && !impl_marked_unsafe479 {480 self.dcx().emit_err(errors::EiiImplRequiresUnsafe {481 span: *span,482 name: self.tcx.item_name(*eii_macro),483 suggestion: errors::EiiImplRequiresUnsafeSuggestion {484 left: inner_span.shrink_to_lo(),485 right: inner_span.shrink_to_hi(),486 },487 });488 }489 }490 }491492 /// Checks use of generic formatting parameters in `#[diagnostic::on_unimplemented]`493 fn check_diagnostic_on_unimplemented(&self, hir_id: HirId, directive: Option<&Directive>) {494 if let Some(directive) = directive {495 if let Node::Item(Item {496 kind: ItemKind::Trait { ident: trait_name, generics, .. },497 ..498 }) = self.tcx.hir_node(hir_id)499 {500 directive.visit_params(&mut |argument_name, span| {501 let has_generic = generics.params.iter().any(|p| {502 if !matches!(p.kind, GenericParamKind::Lifetime { .. })503 && let ParamName::Plain(name) = p.name504 && name.name == argument_name505 {506 true507 } else {508 false509 }510 });511 if !has_generic {512 self.tcx.emit_node_span_lint(513 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,514 hir_id,515 span,516 errors::UnknownFormatParameterForOnUnimplementedAttr {517 argument_name,518 trait_name: *trait_name,519 help: !directive.is_rustc_attr,520 },521 )522 }523 })524 }525 }526 }527528 /// Checks if `#[diagnostic::on_const]` is applied to a on-const trait impl529 fn check_diagnostic_on_const(530 &self,531 attr_span: Span,532 hir_id: HirId,533 target: Target,534 item: Option<ItemLike<'_>>,535 ) {536 // We only check the non-constness here. A diagnostic for use537 // on not-trait impl items is issued during attribute parsing.538 if target == (Target::Impl { of_trait: true }) {539 match item.unwrap() {540 ItemLike::Item(it) => match it.expect_impl().constness {541 Constness::Const => {542 let item_span = self.tcx.hir_span(hir_id);543 self.tcx.emit_node_span_lint(544 MISPLACED_DIAGNOSTIC_ATTRIBUTES,545 hir_id,546 attr_span,547 DiagnosticOnConstOnlyForNonConstTraitImpls { item_span },548 );549 return;550 }551 Constness::NotConst => return,552 },553 ItemLike::ForeignItem => {}554 }555 }556 // FIXME(#155570) Can we do something with generic args here?557 // regardless, we don't check the validity of generic args here558 // ...whose generics would that be, anyway? The traits' or the impls'?559 }560561 /// Checks use of generic formatting parameters in `#[diagnostic::on_move]`562 fn check_diagnostic_on_move(&self, hir_id: HirId, directive: Option<&Directive>) {563 if let Some(directive) = directive {564 if let Node::Item(Item {565 kind:566 ItemKind::Struct(_, generics, _)567 | ItemKind::Enum(_, generics, _)568 | ItemKind::Union(_, generics, _),569 ..570 }) = self.tcx.hir_node(hir_id)571 {572 directive.visit_params(&mut |argument_name, span| {573 let has_generic = generics.params.iter().any(|p| {574 if !matches!(p.kind, GenericParamKind::Lifetime { .. })575 && let ParamName::Plain(name) = p.name576 && name.name == argument_name577 {578 true579 } else {580 false581 }582 });583 if !has_generic {584 self.tcx.emit_node_span_lint(585 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,586 hir_id,587 span,588 errors::OnMoveMalformedFormatLiterals { name: argument_name },589 )590 }591 });592 }593 }594 }595596 /// Checks if an `#[inline]` is applied to a function or a closure.597 fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) {598 match target {599 Target::Fn600 | Target::Closure601 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {602 // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.603 if let Some(did) = hir_id.as_owner()604 && self.tcx.def_kind(did).has_codegen_attrs()605 && kind != &InlineAttr::Never606 {607 let attrs = self.tcx.codegen_fn_attrs(did);608 // Not checking naked as `#[inline]` is forbidden for naked functions anyways.609 if attrs.contains_extern_indicator() {610 self.tcx.emit_node_span_lint(611 UNUSED_ATTRIBUTES,612 hir_id,613 attr_span,614 errors::InlineIgnoredForExported,615 );616 }617 }618 }619 _ => {}620 }621 }622623 /// Checks that the `#[sanitize(..)]` attribute is applied to a624 /// function/closure/method, or to an impl block or module.625 fn check_sanitize(626 &self,627 attr_span: Span,628 set: SanitizerSet,629 target_span: Span,630 target: Target,631 ) {632 let mut not_fn_impl_mod = None;633 let mut no_body = None;634635 match target {636 Target::Fn637 | Target::Closure638 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)639 | Target::Impl { .. }640 | Target::Mod => return,641 Target::Static642 // if we mask out the address bits, i.e. *only* address was set,643 // we allow it644 if set & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)645 == SanitizerSet::empty() =>646 {647 return;648 }649650 // These are "functions", but they aren't allowed because they don't651 // have a body, so the usual explanation would be confusing.652 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {653 no_body = Some(target_span);654 }655656 _ => {657 not_fn_impl_mod = Some(target_span);658 }659 }660661 self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {662 attr_span,663 not_fn_impl_mod,664 no_body,665 help: (),666 });667 }668669 /// Checks if `#[naked]` is applied to a function definition.670 fn check_naked(&self, hir_id: HirId, target: Target) {671 match target {672 Target::Fn673 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {674 let fn_sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();675 let abi = fn_sig.header.abi;676 if abi.is_rustic_abi() && !self.tcx.features().naked_functions_rustic_abi() {677 feature_err(678 &self.tcx.sess,679 sym::naked_functions_rustic_abi,680 fn_sig.span,681 format!(682 "`#[naked]` is currently unstable on `extern \"{}\"` functions",683 abi.as_str()684 ),685 )686 .emit();687 }688 }689 _ => {}690 }691 }692693 /// Debugging aid for the `object_lifetime_default` query.694 fn check_dump_object_lifetime_defaults(&self, hir_id: HirId) {695 let tcx = self.tcx;696 if let Some(owner_id) = hir_id.as_owner()697 && let Some(generics) = tcx.hir_get_generics(owner_id.def_id)698 {699 for p in generics.params {700 let hir::GenericParamKind::Type { .. } = p.kind else { continue };701 let default = tcx.object_lifetime_default(p.def_id);702 let repr = match default {703 ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),704 ObjectLifetimeDefault::Static => "'static".to_owned(),705 ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),706 ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),707 };708 tcx.dcx().span_err(p.span, repr);709 }710 }711 }712713 /// Checks if a `#[track_caller]` is applied to a function.714 fn check_track_caller(715 &self,716 hir_id: HirId,717 attr_span: Span,718 attrs: &[Attribute],719 target: Target,720 ) {721 match target {722 Target::Fn => {723 // `#[track_caller]` is not valid on weak lang items because they are called via724 // `extern` declarations and `#[track_caller]` would alter their ABI.725 if let Some(item) = find_attr!(attrs, Lang(item) => item)726 && item.is_weak()727 {728 let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();729730 self.dcx().emit_err(errors::LangItemWithTrackCaller {731 attr_span,732 name: item.name(),733 sig_span: sig.span,734 });735 }736737 if let Some(impls) = find_attr!(attrs, EiiImpls(impls) => impls) {738 let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();739 for i in impls {740 let name = match i.resolution {741 EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id),742 EiiImplResolution::Known(decl) => decl.name.name,743 EiiImplResolution::Error(_eg) => continue,744 };745 self.dcx().emit_err(errors::EiiWithTrackCaller {746 attr_span,747 name,748 sig_span: sig.span,749 });750 }751 }752 }753 _ => {}754 }755 }756757 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.758 fn check_non_exhaustive(759 &self,760 attr_span: Span,761 span: Span,762 target: Target,763 item: Option<ItemLike<'_>>,764 ) {765 match target {766 Target::Struct => {767 if let Some(ItemLike::Item(hir::Item {768 kind: hir::ItemKind::Struct(_, _, hir::VariantData::Struct { fields, .. }),769 ..770 })) = item771 && !fields.is_empty()772 && fields.iter().any(|f| f.default.is_some())773 {774 self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues {775 attr_span,776 defn_span: span,777 });778 }779 }780 _ => {}781 }782 }783784 /// Checks if the `#[target_feature]` attribute on `item` is valid.785 fn check_target_feature(786 &self,787 hir_id: HirId,788 attr_span: Span,789 target: Target,790 attrs: &[Attribute],791 ) {792 match target {793 Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)794 | Target::Fn => {795 // `#[target_feature]` is not allowed in lang items.796 if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang)797 // Calling functions with `#[target_feature]` is798 // not unsafe on WASM, see #84988799 && !self.tcx.sess.target.is_like_wasm800 && !self.tcx.sess.opts.actually_rustdoc801 {802 let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();803804 self.dcx().emit_err(errors::LangItemWithTargetFeature {805 attr_span,806 name: lang_item.name(),807 sig_span: sig.span,808 });809 }810 }811 _ => {}812 }813 }814815 fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) {816 if let Some(location) = match target {817 Target::AssocTy => {818 if let DefKind::Impl { .. } =819 self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id))820 {821 Some("type alias in implementation block")822 } else {823 None824 }825 }826 Target::AssocConst => {827 let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id;828 let containing_item = self.tcx.hir_expect_item(parent_def_id);829 // We can't link to trait impl's consts.830 let err = "associated constant in trait implementation block";831 match containing_item.kind {832 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),833 _ => None,834 }835 }836 // we check the validity of params elsewhere837 Target::Param => return,838 Target::Expression839 | Target::Statement840 | Target::Arm841 | Target::ForeignMod842 | Target::Closure843 | Target::Impl { .. }844 | Target::WherePredicate => Some(target.name()),845 Target::ExternCrate846 | Target::Use847 | Target::Static848 | Target::Const849 | Target::Fn850 | Target::Mod851 | Target::GlobalAsm852 | Target::TyAlias853 | Target::Enum854 | Target::Variant855 | Target::Struct856 | Target::Field857 | Target::Union858 | Target::Trait859 | Target::TraitAlias860 | Target::Method(..)861 | Target::ForeignFn862 | Target::ForeignStatic863 | Target::ForeignTy864 | Target::GenericParam { .. }865 | Target::MacroDef866 | Target::PatField867 | Target::ExprField868 | Target::Crate869 | Target::MacroCall870 | Target::Delegation { .. } => None,871 } {872 self.tcx.dcx().emit_err(errors::DocAliasBadLocation { span, location });873 return;874 }875 if self.tcx.hir_opt_name(hir_id) == Some(alias) {876 self.tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str: alias });877 return;878 }879 }880881 fn check_doc_fake_variadic(&self, span: Span, hir_id: HirId) {882 let item_kind = match self.tcx.hir_node(hir_id) {883 hir::Node::Item(item) => Some(&item.kind),884 _ => None,885 };886 match item_kind {887 Some(ItemKind::Impl(i)) => {888 let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty)889 || if let Some(&[hir::GenericArg::Type(ty)]) = i890 .of_trait891 .and_then(|of_trait| of_trait.trait_ref.path.segments.last())892 .map(|last_segment| last_segment.args().args)893 {894 matches!(&ty.kind, hir::TyKind::Tup([_]))895 } else {896 false897 };898 if !is_valid {899 self.dcx().emit_err(errors::DocFakeVariadicNotValid { span });900 }901 }902 _ => {903 self.dcx().emit_err(errors::DocKeywordOnlyImpl { span });904 }905 }906 }907908 fn check_doc_search_unbox(&self, span: Span, hir_id: HirId) {909 let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {910 self.dcx().emit_err(errors::DocSearchUnboxInvalid { span });911 return;912 };913 match item.kind {914 ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _)915 if generics.params.len() != 0 => {}916 ItemKind::Trait { generics, items, .. }917 if generics.params.len() != 0918 || items.iter().any(|item| {919 matches!(self.tcx.def_kind(item.owner_id), DefKind::AssocTy)920 }) => {}921 ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}922 _ => {923 self.dcx().emit_err(errors::DocSearchUnboxInvalid { span });924 }925 }926 }927928 /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes.929 ///930 /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or931 /// if there are conflicting attributes for one item.932 ///933 /// `specified_inline` is used to keep track of whether we have934 /// already seen an inlining attribute for this item.935 /// If so, `specified_inline` holds the value and the span of936 /// the first `inline`/`no_inline` attribute.937 fn check_doc_inline(&self, hir_id: HirId, target: Target, inline: &[(DocInline, Span)]) {938 let span = match inline {939 [] => return,940 [(_, span)] => *span,941 [(inline, span), rest @ ..] => {942 for (inline2, span2) in rest {943 if inline2 != inline {944 let mut spans = MultiSpan::from_spans(vec![*span, *span2]);945 spans.push_span_label(*span, msg!("this attribute..."));946 spans.push_span_label(947 *span2,948 msg!("{\".\"}..conflicts with this attribute"),949 );950 self.dcx().emit_err(errors::DocInlineConflict { spans });951 return;952 }953 }954 *span955 }956 };957958 match target {959 Target::Use | Target::ExternCrate => {}960 _ => {961 self.tcx.emit_node_span_lint(962 INVALID_DOC_ATTRIBUTES,963 hir_id,964 span,965 errors::DocInlineOnlyUse {966 attr_span: span,967 item_span: self.tcx.hir_span(hir_id),968 },969 );970 }971 }972 }973974 fn check_doc_masked(&self, span: Span, hir_id: HirId, target: Target) {975 if target != Target::ExternCrate {976 self.tcx.emit_node_span_lint(977 INVALID_DOC_ATTRIBUTES,978 hir_id,979 span,980 errors::DocMaskedOnlyExternCrate {981 attr_span: span,982 item_span: self.tcx.hir_span(hir_id),983 },984 );985 return;986 }987988 if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() {989 self.tcx.emit_node_span_lint(990 INVALID_DOC_ATTRIBUTES,991 hir_id,992 span,993 errors::DocMaskedNotExternCrateSelf {994 attr_span: span,995 item_span: self.tcx.hir_span(hir_id),996 },997 );998 }999 }10001001 fn check_doc_keyword_and_attribute(&self, span: Span, hir_id: HirId, attr_name: &'static str) {1002 let item_kind = match self.tcx.hir_node(hir_id) {1003 hir::Node::Item(item) => Some(&item.kind),1004 _ => None,1005 };1006 match item_kind {1007 Some(ItemKind::Mod(_, module)) => {1008 if !module.item_ids.is_empty() {1009 self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod { span, attr_name });1010 return;1011 }1012 }1013 _ => {1014 self.dcx().emit_err(errors::DocKeywordAttributeNotMod { span, attr_name });1015 return;1016 }1017 }1018 }10191020 /// Runs various checks on `#[doc]` attributes.1021 ///1022 /// `specified_inline` should be initialized to `None` and kept for the scope1023 /// of one item. Read the documentation of [`check_doc_inline`] for more information.1024 ///1025 /// [`check_doc_inline`]: Self::check_doc_inline1026 fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) {1027 let DocAttribute {1028 first_span: _,1029 aliases,1030 // valid pretty much anywhere, not checked here?1031 // FIXME: should we?1032 hidden: _,1033 inline,1034 // FIXME: currently unchecked1035 cfg: _,1036 // already checked in attr_parsing1037 auto_cfg: _,1038 // already checked in attr_parsing1039 auto_cfg_change: _,1040 fake_variadic,1041 keyword,1042 masked,1043 // FIXME: currently unchecked1044 notable_trait: _,1045 search_unbox,1046 // already checked in attr_parsing1047 html_favicon_url: _,1048 // already checked in attr_parsing1049 html_logo_url: _,1050 // already checked in attr_parsing1051 html_playground_url: _,1052 // already checked in attr_parsing1053 html_root_url: _,1054 // already checked in attr_parsing1055 html_no_source: _,1056 // already checked in attr_parsing1057 issue_tracker_base_url: _,1058 // already checked in attr_parsing1059 rust_logo: _,1060 // allowed anywhere1061 test_attrs: _,1062 // already checked in attr_parsing1063 no_crate_inject: _,1064 attribute,1065 } = attr;10661067 for (alias, span) in aliases {1068 self.check_doc_alias_value(*span, hir_id, target, *alias);1069 }10701071 if let Some((_, span)) = keyword {1072 self.check_doc_keyword_and_attribute(*span, hir_id, "keyword");1073 }1074 if let Some((_, span)) = attribute {1075 self.check_doc_keyword_and_attribute(*span, hir_id, "attribute");1076 }10771078 if let Some(span) = fake_variadic {1079 self.check_doc_fake_variadic(*span, hir_id);1080 }10811082 if let Some(span) = search_unbox {1083 self.check_doc_search_unbox(*span, hir_id);1084 }10851086 self.check_doc_inline(hir_id, target, inline);10871088 if let Some(span) = masked {1089 self.check_doc_masked(*span, hir_id, target);1090 }1091 }10921093 fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) {1094 if find_attr!(attrs, FfiConst) {1095 // `#[ffi_const]` functions cannot be `#[ffi_pure]`1096 self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span });1097 }1098 }10991100 /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.1101 fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {1102 if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)1103 && matches!(1104 param.kind,1105 hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }1106 )1107 && matches!(param.source, hir::GenericParamSource::Generics)1108 && let parent_hir_id = self.tcx.parent_hir_id(hir_id)1109 && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)1110 && let hir::ItemKind::Impl(impl_) = item.kind1111 && let Some(of_trait) = impl_.of_trait1112 && let Some(def_id) = of_trait.trait_ref.trait_def_id()1113 && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)1114 {1115 return;1116 }11171118 self.dcx().emit_err(errors::InvalidMayDangle { attr_span });1119 }11201121 /// Checks if `#[link]` is applied to an item other than a foreign module.1122 fn check_link(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {1123 if target == Target::ForeignMod1124 && let hir::Node::Item(item) = self.tcx.hir_node(hir_id)1125 && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item1126 && !matches!(abi, ExternAbi::Rust)1127 {1128 return;1129 }11301131 self.tcx.emit_node_span_lint(1132 UNUSED_ATTRIBUTES,1133 hir_id,1134 attr_span,1135 errors::Link { span: (target != Target::ForeignMod).then_some(span) },1136 );1137 }11381139 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.1140 fn check_rustc_legacy_const_generics(1141 &self,1142 item: Option<ItemLike<'_>>,1143 attr_span: Span,1144 index_list: &ThinVec<(usize, Span)>,1145 ) {1146 let Some(ItemLike::Item(Item {1147 kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. },1148 ..1149 })) = item1150 else {1151 // No error here, since it's already given by the parser1152 return;1153 };11541155 for param in generics.params {1156 match param.kind {1157 hir::GenericParamKind::Const { .. } => {}1158 _ => {1159 self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly {1160 attr_span,1161 param_span: param.span,1162 });1163 return;1164 }1165 }1166 }11671168 if index_list.len() != generics.params.len() {1169 self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex {1170 attr_span,1171 generics_span: generics.span,1172 });1173 return;1174 }11751176 let arg_count = decl.inputs.len() + generics.params.len();1177 for (index, span) in index_list {1178 if *index >= arg_count {1179 self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexExceed {1180 span: *span,1181 arg_count,1182 });1183 }1184 }1185 }11861187 /// Checks if the `#[repr]` attributes on `item` are valid.1188 fn check_repr(1189 &self,1190 attrs: &[Attribute],1191 span: Span,1192 target: Target,1193 item: Option<ItemLike<'_>>,1194 hir_id: HirId,1195 ) {1196 // Extract the names of all repr hints, e.g., [foo, bar, align] for:1197 // ```1198 // #[repr(foo)]1199 // #[repr(bar, align(8))]1200 // ```1201 let (reprs, first_attr_span) =1202 find_attr!(attrs, Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span)))1203 .unwrap_or((&[], None));12041205 let mut int_reprs = 0;1206 let mut is_explicit_rust = false;1207 let mut is_c = false;1208 let mut is_simd = false;1209 let mut is_transparent = false;12101211 for (repr, repr_span) in reprs {1212 match repr {1213 ReprAttr::ReprRust => {1214 is_explicit_rust = true;1215 match target {1216 Target::Struct | Target::Union | Target::Enum => continue,1217 _ => {1218 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {1219 hint_span: *repr_span,1220 span,1221 });1222 }1223 }1224 }1225 ReprAttr::ReprC => {1226 is_c = true;1227 match target {1228 Target::Struct | Target::Union | Target::Enum => continue,1229 _ => {1230 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {1231 hint_span: *repr_span,1232 span,1233 });1234 }1235 }1236 }1237 ReprAttr::ReprAlign(..) => match target {1238 Target::Struct | Target::Union | Target::Enum => {}1239 Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => {1240 self.dcx().emit_err(errors::ReprAlignShouldBeAlign {1241 span: *repr_span,1242 item: target.plural_name(),1243 });1244 }1245 Target::Static if self.tcx.features().static_align() => {1246 self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic {1247 span: *repr_span,1248 item: target.plural_name(),1249 });1250 }1251 _ => {1252 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {1253 hint_span: *repr_span,1254 span,1255 });1256 }1257 },1258 ReprAttr::ReprPacked(_) => {1259 if target != Target::Struct && target != Target::Union {1260 self.dcx().emit_err(errors::AttrApplication::StructUnion {1261 hint_span: *repr_span,1262 span,1263 });1264 } else {1265 continue;1266 }1267 }1268 ReprAttr::ReprSimd => {1269 is_simd = true;1270 if target != Target::Struct {1271 self.dcx().emit_err(errors::AttrApplication::Struct {1272 hint_span: *repr_span,1273 span,1274 });1275 } else {1276 continue;1277 }1278 }1279 ReprAttr::ReprTransparent => {1280 is_transparent = true;1281 match target {1282 Target::Struct | Target::Union | Target::Enum => continue,1283 _ => {1284 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {1285 hint_span: *repr_span,1286 span,1287 });1288 }1289 }1290 }1291 ReprAttr::ReprInt(_) => {1292 int_reprs += 1;1293 if target != Target::Enum {1294 self.dcx().emit_err(errors::AttrApplication::Enum {1295 hint_span: *repr_span,1296 span,1297 });1298 } else {1299 continue;1300 }1301 }1302 };1303 }13041305 // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)1306 if let Some(first_attr_span) = first_attr_span1307 && reprs.is_empty()1308 && item.is_some()1309 {1310 match target {1311 Target::Struct | Target::Union | Target::Enum => {}1312 Target::Fn | Target::Method(_) => {1313 self.dcx().emit_err(errors::ReprAlignShouldBeAlign {1314 span: first_attr_span,1315 item: target.plural_name(),1316 });1317 }1318 _ => {1319 self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {1320 hint_span: first_attr_span,1321 span,1322 });1323 }1324 }1325 return;1326 }13271328 // Just point at all repr hints if there are any incompatibilities.1329 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.1330 let hint_spans = reprs.iter().map(|(_, span)| *span);13311332 // Error on repr(transparent, <anything else>).1333 if is_transparent && reprs.len() > 1 {1334 let hint_spans = hint_spans.clone().collect();1335 self.dcx().emit_err(errors::TransparentIncompatible {1336 hint_spans,1337 target: target.to_string(),1338 });1339 }1340 // Error on `#[repr(transparent)]` in combination with1341 // `#[rustc_pass_indirectly_in_non_rustic_abis]`1342 if is_transparent1343 && let Some(&pass_indirectly_span) =1344 find_attr!(attrs, RustcPassIndirectlyInNonRusticAbis(span) => span)1345 {1346 self.dcx().emit_err(errors::TransparentIncompatible {1347 hint_spans: vec![span, pass_indirectly_span],1348 target: target.to_string(),1349 });1350 }1351 if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {1352 let hint_spans = hint_spans.clone().collect();1353 self.dcx().emit_err(errors::ReprConflicting { hint_spans });1354 }1355 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)1356 if (int_reprs > 1)1357 || (is_simd && is_c)1358 || (int_reprs == 11359 && is_c1360 && item.is_some_and(|item| {1361 if let ItemLike::Item(item) = item { is_c_like_enum(item) } else { false }1362 }))1363 {1364 self.tcx.emit_node_span_lint(1365 CONFLICTING_REPR_HINTS,1366 hir_id,1367 hint_spans.collect::<Vec<Span>>(),1368 errors::ReprConflictingLint,1369 );1370 }1371 }13721373 /// Outputs an error for attributes that can only be applied to macros, such as1374 /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`.1375 /// (Allows proc_macro functions)1376 // FIXME(jdonszelmann): if possible, move to attr parsing1377 fn check_macro_only_attr(1378 &self,1379 attr_span: Span,1380 span: Span,1381 target: Target,1382 attrs: &[Attribute],1383 ) {1384 match target {1385 Target::Fn => {1386 for attr in attrs {1387 if attr.is_proc_macro_attr() {1388 // return on proc macros1389 return;1390 }1391 }1392 self.tcx.dcx().emit_err(errors::MacroOnlyAttribute { attr_span, span });1393 }1394 _ => {}1395 }1396 }13971398 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.1399 /// (Allows proc_macro functions)1400 fn check_rustc_allow_const_fn_unstable(1401 &self,1402 hir_id: HirId,1403 attr_span: Span,1404 span: Span,1405 target: Target,1406 ) {1407 match target {1408 Target::Fn | Target::Method(_) => {1409 if !self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) {1410 self.tcx.dcx().emit_err(errors::RustcAllowConstFnUnstable { attr_span, span });1411 }1412 }1413 _ => {}1414 }1415 }14161417 fn check_deprecated(&self, hir_id: HirId, attr_span: Span, target: Target) {1418 match target {1419 Target::AssocConst | Target::Method(..) | Target::AssocTy1420 if self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id))1421 == DefKind::Impl { of_trait: true } =>1422 {1423 self.tcx.emit_node_span_lint(1424 UNUSED_ATTRIBUTES,1425 hir_id,1426 attr_span,1427 errors::DeprecatedAnnotationHasNoEffect { span: attr_span },1428 );1429 }1430 _ => {}1431 }1432 }14331434 fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) {1435 if target != Target::MacroDef {1436 return;1437 }14381439 // special case when `#[macro_export]` is applied to a macro 2.01440 let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro();1441 let is_decl_macro = !macro_definition.macro_rules;14421443 if is_decl_macro {1444 self.tcx.emit_node_span_lint(1445 UNUSED_ATTRIBUTES,1446 hir_id,1447 attr_span,1448 errors::MacroExport::OnDeclMacro,1449 );1450 }1451 }14521453 fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {1454 // Warn on useless empty attributes.1455 // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`1456 let note =1457 if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])1458 && attr.meta_item_list().is_some_and(|list| list.is_empty())1459 {1460 errors::UnusedNote::EmptyList { name: attr.name().unwrap() }1461 } else if attr.has_any_name(&[1462 sym::allow,1463 sym::warn,1464 sym::deny,1465 sym::forbid,1466 sym::expect,1467 ]) && let Some(meta) = attr.meta_item_list()1468 && let [meta] = meta.as_slice()1469 && let Some(item) = meta.meta_item()1470 && let MetaItemKind::NameValue(_) = &item.kind1471 && item.path == sym::reason1472 {1473 errors::UnusedNote::NoLints { name: attr.name().unwrap() }1474 } else if attr.has_any_name(&[1475 sym::allow,1476 sym::warn,1477 sym::deny,1478 sym::forbid,1479 sym::expect,1480 ]) && let Some(meta) = attr.meta_item_list()1481 && meta.iter().any(|meta| {1482 meta.meta_item().map_or(false, |item| {1483 item.path == sym::linker_messages || item.path == sym::linker_info1484 })1485 })1486 {1487 if hir_id != CRATE_HIR_ID {1488 match style {1489 Some(ast::AttrStyle::Outer) => {1490 let attr_span = attr.span();1491 let bang_position = self1492 .tcx1493 .sess1494 .source_map()1495 .span_until_char(attr_span, '[')1496 .shrink_to_hi();14971498 self.tcx.emit_node_span_lint(1499 UNUSED_ATTRIBUTES,1500 hir_id,1501 attr_span,1502 errors::OuterCrateLevelAttr {1503 suggestion: errors::OuterCrateLevelAttrSuggestion {1504 bang_position,1505 },1506 },1507 )1508 }1509 Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(1510 UNUSED_ATTRIBUTES,1511 hir_id,1512 attr.span(),1513 errors::InnerCrateLevelAttr,1514 ),1515 };1516 return;1517 } else {1518 let never_needs_link = self1519 .tcx1520 .crate_types()1521 .iter()1522 .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib));1523 if never_needs_link {1524 errors::UnusedNote::LinkerMessagesBinaryCrateOnly1525 } else {1526 return;1527 }1528 }1529 } else if attr.has_name(sym::default_method_body_is_const) {1530 errors::UnusedNote::DefaultMethodBodyConst1531 } else {1532 return;1533 };15341535 self.tcx.emit_node_span_lint(1536 UNUSED_ATTRIBUTES,1537 hir_id,1538 attr.span(),1539 errors::Unused { attr_span: attr.span(), note },1540 );1541 }15421543 /// A best effort attempt to create an error for a mismatching proc macro signature.1544 ///1545 /// If this best effort goes wrong, it will just emit a worse error later (see #102923)1546 fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {1547 if target != Target::Fn {1548 return;1549 }15501551 let tcx = self.tcx;1552 let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else {1553 return;1554 };1555 let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else {1556 return;1557 };15581559 let def_id = hir_id.expect_owner().def_id;1560 let param_env = ty::ParamEnv::empty();15611562 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());1563 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);15641565 let span = tcx.def_span(def_id);1566 let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id());1567 let sig = tcx.liberate_late_bound_regions(1568 def_id.to_def_id(),1569 tcx.fn_sig(def_id).instantiate(tcx, fresh_args).skip_norm_wip(),1570 );15711572 let mut cause = ObligationCause::misc(span, def_id);1573 let sig = ocx.normalize(&cause, param_env, Unnormalized::new_wip(sig));15741575 // proc macro is not WF.1576 let errors = ocx.try_evaluate_obligations();1577 if !errors.is_empty() {1578 return;1579 }15801581 let expected_sig = tcx.mk_fn_sig_safe_rust_abi(1582 std::iter::repeat_n(1583 token_stream,1584 match kind {1585 ProcMacroKind::Attribute => 2,1586 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,1587 },1588 ),1589 token_stream,1590 );15911592 if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) {1593 let mut diag = tcx.dcx().create_err(errors::ProcMacroBadSig { span, kind });15941595 let hir_sig = tcx.hir_fn_sig_by_hir_id(hir_id);1596 if let Some(hir_sig) = hir_sig {1597 match terr {1598 TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {1599 if let Some(ty) = hir_sig.decl.inputs.get(idx) {1600 diag.span(ty.span);1601 cause.span = ty.span;1602 } else if idx == hir_sig.decl.inputs.len() {1603 let span = hir_sig.decl.output.span();1604 diag.span(span);1605 cause.span = span;1606 }1607 }1608 TypeError::ArgCount => {1609 if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {1610 diag.span(ty.span);1611 cause.span = ty.span;1612 }1613 }1614 TypeError::SafetyMismatch(_) => {1615 // FIXME: Would be nice if we had a span here..1616 }1617 TypeError::AbiMismatch(_) => {1618 // FIXME: Would be nice if we had a span here..1619 }1620 TypeError::VariadicMismatch(_) => {1621 // FIXME: Would be nice if we had a span here..1622 }1623 _ => {}1624 }1625 }16261627 infcx.err_ctxt().note_type_err(1628 &mut diag,1629 &cause,1630 None,1631 Some(param_env.and(ValuePairs::PolySigs(ExpectedFound {1632 expected: ty::Binder::dummy(expected_sig),1633 found: ty::Binder::dummy(sig),1634 }))),1635 terr,1636 false,1637 None,1638 );1639 diag.emit();1640 self.abort.set(true);1641 }16421643 let errors = ocx.evaluate_obligations_error_on_ambiguity();1644 if !errors.is_empty() {1645 infcx.err_ctxt().report_fulfillment_errors(errors);1646 self.abort.set(true);1647 }1648 }16491650 fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {1651 if !find_attr!(attrs, Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))1652 .unwrap_or(false)1653 {1654 self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });1655 }1656 }16571658 fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) {1659 if let (Target::Closure, None) = (1660 target,1661 find_attr!(attrs, Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span),1662 ) {1663 let is_coro = matches!(1664 self.tcx.hir_expect_expr(hir_id).kind,1665 hir::ExprKind::Closure(hir::Closure {1666 kind: hir::ClosureKind::Coroutine(..) | hir::ClosureKind::CoroutineClosure(..),1667 ..1668 })1669 );1670 let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id();1671 let parent_span = self.tcx.def_span(parent_did);16721673 if let Some(attr_span) = find_attr!(1674 self.tcx, parent_did,1675 Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span1676 ) && is_coro1677 {1678 self.dcx().emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span });1679 }1680 }1681 }16821683 fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) {1684 if let Some(export_name_span) =1685 find_attr!(attrs, ExportName { span: export_name_span, .. } => *export_name_span)1686 && let Some(no_mangle_span) =1687 find_attr!(attrs, NoMangle(no_mangle_span) => *no_mangle_span)1688 {1689 let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 {1690 "#[unsafe(no_mangle)]"1691 } else {1692 "#[no_mangle]"1693 };1694 let export_name_attr = if export_name_span.edition() >= Edition::Edition2024 {1695 "#[unsafe(export_name)]"1696 } else {1697 "#[export_name]"1698 };16991700 self.tcx.emit_node_span_lint(1701 lint::builtin::UNUSED_ATTRIBUTES,1702 hir_id,1703 no_mangle_span,1704 errors::MixedExportNameAndNoMangle {1705 no_mangle_span,1706 export_name_span,1707 no_mangle_attr,1708 export_name_attr,1709 },1710 );1711 }1712 }17131714 fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) {1715 let node_span = self.tcx.hir_span(hir_id);17161717 if !matches!(target, Target::Expression) {1718 return; // Handled in target checking during attr parse1719 }17201721 if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) {1722 self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });1723 };1724 }17251726 fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {1727 let node_span = self.tcx.hir_span(hir_id);17281729 if !matches!(target, Target::Expression) {1730 return; // Handled in target checking during attr parse1731 }17321733 if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) {1734 self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });1735 };1736 }1737}17381739impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {1740 type NestedFilter = nested_filter::OnlyBodies;17411742 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {1743 self.tcx1744 }17451746 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {1747 // Historically we've run more checks on non-exported than exported macros,1748 // so this lets us continue to run them while maintaining backwards compatibility.1749 // In the long run, the checks should be harmonized.1750 if let ItemKind::Macro(_, macro_def, _) = item.kind {1751 let def_id = item.owner_id.to_def_id();1752 if macro_def.macro_rules && !find_attr!(self.tcx, def_id, MacroExport { .. }) {1753 check_non_exported_macro_for_invalid_attrs(self.tcx, item);1754 }1755 }17561757 let target = Target::from_item(item);1758 self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));1759 intravisit::walk_item(self, item)1760 }17611762 fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) {1763 self.check_attributes(1764 where_predicate.hir_id,1765 where_predicate.span,1766 Target::WherePredicate,1767 None,1768 );1769 intravisit::walk_where_predicate(self, where_predicate)1770 }17711772 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {1773 let target = Target::from_generic_param(generic_param);1774 self.check_attributes(generic_param.hir_id, generic_param.span, target, None);1775 intravisit::walk_generic_param(self, generic_param)1776 }17771778 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {1779 let target = Target::from_trait_item(trait_item);1780 self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);1781 intravisit::walk_trait_item(self, trait_item)1782 }17831784 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {1785 self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);1786 intravisit::walk_field_def(self, struct_field);1787 }17881789 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {1790 self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);1791 intravisit::walk_arm(self, arm);1792 }17931794 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {1795 let target = Target::from_foreign_item(f_item);1796 self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));1797 intravisit::walk_foreign_item(self, f_item)1798 }17991800 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {1801 let target = target_from_impl_item(self.tcx, impl_item);1802 self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);1803 intravisit::walk_impl_item(self, impl_item)1804 }18051806 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {1807 // When checking statements ignore expressions, they will be checked later.1808 if let hir::StmtKind::Let(l) = stmt.kind {1809 self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);1810 }1811 intravisit::walk_stmt(self, stmt)1812 }18131814 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {1815 let target = match expr.kind {1816 hir::ExprKind::Closure { .. } => Target::Closure,1817 _ => Target::Expression,1818 };18191820 self.check_attributes(expr.hir_id, expr.span, target, None);1821 intravisit::walk_expr(self, expr)1822 }18231824 fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {1825 self.check_attributes(field.hir_id, field.span, Target::ExprField, None);1826 intravisit::walk_expr_field(self, field)1827 }18281829 fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {1830 self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);1831 intravisit::walk_variant(self, variant)1832 }18331834 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {1835 self.check_attributes(param.hir_id, param.span, Target::Param, None);18361837 intravisit::walk_param(self, param);1838 }18391840 fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {1841 self.check_attributes(field.hir_id, field.span, Target::PatField, None);1842 intravisit::walk_pat_field(self, field);1843 }1844}18451846fn is_c_like_enum(item: &Item<'_>) -> bool {1847 if let ItemKind::Enum(_, _, ref def) = item.kind {1848 for variant in def.variants {1849 match variant.data {1850 hir::VariantData::Unit(..) => { /* continue */ }1851 _ => return false,1852 }1853 }1854 true1855 } else {1856 false1857 }1858}18591860fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {1861 let attrs = tcx.hir_attrs(item.hir_id());18621863 if let Some(attr_span) =1864 find_attr!(attrs, Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span)1865 {1866 tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span });1867 }1868}18691870fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {1871 let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };1872 tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor);1873 if module_def_id.to_local_def_id().is_top_level_module() {1874 check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);1875 }1876 if check_attr_visitor.abort.get() {1877 tcx.dcx().abort_if_errors()1878 }1879}18801881pub(crate) fn provide(providers: &mut Providers) {1882 *providers = Providers { check_mod_attrs, ..*providers };1883}18841885fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool {1886 matches!(&self_ty.kind, hir::TyKind::Tup([_]))1887 || if let hir::TyKind::FnPtr(fn_ptr_ty) = &self_ty.kind {1888 fn_ptr_ty.decl.inputs.len() == 11889 } else {1890 false1891 }1892 || (if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &self_ty.kind1893 && let Some(&[hir::GenericArg::Type(ty)]) =1894 path.segments.last().map(|last| last.args().args)1895 {1896 doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty())1897 } else {1898 false1899 })1900}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.