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