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