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