1 //! This module implements some validity checks for attributes.
2 //! In particular it verifies that `#[inline]` and `#[repr]` attributes are
3 //! attached to items that actually support them and if there are
4 //! conflicts between multiple such attributes attached to the same
8 self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
9 OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments,
10 ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint,
12 use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
13 use rustc_data_structures::fx::FxHashMap;
14 use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
15 use rustc_expand::base::resolve_path;
16 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
18 use rustc_hir::def_id::LocalDefId;
19 use rustc_hir::intravisit::{self, Visitor};
21 self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
23 use rustc_hir::{MethodKind, Target, Unsafety};
24 use rustc_middle::hir::nested_filter;
25 use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
26 use rustc_middle::ty::query::Providers;
27 use rustc_middle::ty::{ParamEnv, TyCtxt};
28 use rustc_session::lint::builtin::{
29 CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
31 use rustc_session::parse::feature_err;
32 use rustc_span::symbol::{kw, sym, Symbol};
33 use rustc_span::{Span, DUMMY_SP};
34 use rustc_target::spec::abi::Abi;
36 use std::collections::hash_map::Entry;
38 pub(crate) fn target_from_impl_item<'tcx>(
40 impl_item: &hir::ImplItem<'_>,
42 match impl_item.kind {
43 hir::ImplItemKind::Const(..) => Target::AssocConst,
44 hir::ImplItemKind::Fn(..) => {
45 let parent_def_id = tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
46 let containing_item = tcx.hir().expect_item(parent_def_id);
47 let containing_impl_is_for_trait = match &containing_item.kind {
48 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
49 _ => bug!("parent of an ImplItem must be an Impl"),
51 if containing_impl_is_for_trait {
52 Target::Method(MethodKind::Trait { body: true })
54 Target::Method(MethodKind::Inherent)
57 hir::ImplItemKind::Type(..) => Target::AssocTy,
61 #[derive(Clone, Copy)]
63 Item(&'tcx Item<'tcx>),
67 #[derive(Copy, Clone)]
68 pub(crate) enum ProcMacroKind {
74 impl IntoDiagnosticArg for ProcMacroKind {
75 fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
77 ProcMacroKind::Attribute => "attribute proc macro",
78 ProcMacroKind::Derive => "derive proc macro",
79 ProcMacroKind::FunctionLike => "function-like proc macro",
81 .into_diagnostic_arg()
85 struct CheckAttrVisitor<'tcx> {
90 impl CheckAttrVisitor<'_> {
91 /// Checks any attribute.
97 item: Option<ItemLike<'_>>,
99 let mut doc_aliases = FxHashMap::default();
100 let mut is_valid = true;
101 let mut specified_inline = None;
102 let mut seen = FxHashMap::default();
103 let attrs = self.tcx.hir().attrs(hir_id);
105 let attr_is_valid = match attr.name_or_empty() {
106 sym::inline => self.check_inline(hir_id, attr, span, target),
107 sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
108 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
109 sym::marker => self.check_marker(hir_id, attr, span, target),
110 sym::rustc_must_implement_one_of => {
111 self.check_rustc_must_implement_one_of(attr, span, target)
113 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
114 sym::thread_local => self.check_thread_local(attr, span, target),
115 sym::track_caller => {
116 self.check_track_caller(hir_id, attr.span, attrs, span, target)
118 sym::doc => self.check_doc_attrs(
122 &mut specified_inline,
125 sym::no_link => self.check_no_link(hir_id, &attr, span, target),
126 sym::export_name => self.check_export_name(hir_id, &attr, span, target),
127 sym::rustc_layout_scalar_valid_range_start
128 | sym::rustc_layout_scalar_valid_range_end => {
129 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
131 sym::allow_internal_unstable => {
132 self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
134 sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
135 sym::rustc_allow_const_fn_unstable => {
136 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
138 sym::rustc_std_internal_symbol => {
139 self.check_rustc_std_internal_symbol(&attr, span, target)
141 sym::naked => self.check_naked(hir_id, attr, span, target),
142 sym::rustc_legacy_const_generics => {
143 self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item)
145 sym::rustc_lint_query_instability => {
146 self.check_rustc_lint_query_instability(hir_id, &attr, span, target)
148 sym::rustc_lint_diagnostics => {
149 self.check_rustc_lint_diagnostics(hir_id, &attr, span, target)
151 sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
152 sym::rustc_lint_opt_deny_field_access => {
153 self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
157 | sym::rustc_if_this_changed
158 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
159 sym::cmse_nonsecure_entry => {
160 self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
162 sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
163 sym::const_trait => self.check_const_trait(attr, span, target),
164 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
165 sym::must_use => self.check_must_use(hir_id, &attr, target),
166 sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
167 sym::rustc_allow_incoherent_impl => {
168 self.check_allow_incoherent_impl(&attr, span, target)
170 sym::rustc_has_incoherent_inherent_impls => {
171 self.check_has_incoherent_inherent_impls(&attr, span, target)
173 sym::rustc_const_unstable
174 | sym::rustc_const_stable
177 | sym::rustc_allowed_through_unstable_modules
178 | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
179 sym::link_ordinal => self.check_link_ordinal(&attr, span, target),
182 is_valid &= attr_is_valid;
185 match attr.name_or_empty() {
186 sym::cold => self.check_cold(hir_id, attr, span, target),
187 sym::link => self.check_link(hir_id, attr, span, target),
188 sym::link_name => self.check_link_name(hir_id, attr, span, target),
189 sym::link_section => self.check_link_section(hir_id, attr, span, target),
190 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
191 sym::deprecated => self.check_deprecated(hir_id, attr, span, target),
192 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
193 sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
194 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
195 sym::macro_export => self.check_macro_export(hir_id, attr, target),
196 sym::ignore | sym::should_panic => {
197 self.check_generic_attr(hir_id, attr, target, Target::Fn)
199 sym::automatically_derived => {
200 self.check_generic_attr(hir_id, attr, target, Target::Impl)
202 sym::no_implicit_prelude => {
203 self.check_generic_attr(hir_id, attr, target, Target::Mod)
205 sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
207 self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
209 sym::proc_macro_attribute => {
210 self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
212 sym::proc_macro_derive => {
213 self.check_generic_attr(hir_id, attr, target, Target::Fn);
214 self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
219 let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
221 if hir_id != CRATE_HIR_ID {
222 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
223 attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
226 ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
230 errors::OuterCrateLevelAttr,
232 ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
236 errors::InnerCrateLevelAttr,
242 if let Some(BuiltinAttribute { duplicates, .. }) = builtin {
243 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
246 self.check_unused_attribute(hir_id, attr)
253 self.check_repr(attrs, span, target, item, hir_id);
254 self.check_used(attrs, target);
257 fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
258 self.tcx.emit_spanned_lint(
262 errors::IgnoredAttrWithMacro { sym },
266 fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
267 self.tcx.emit_spanned_lint(
271 errors::IgnoredAttr { sym },
275 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
276 fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
280 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
281 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
282 self.tcx.emit_spanned_lint(
286 errors::IgnoredInlineAttrFnProto,
290 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
291 // just a lint, because we previously erroneously allowed it and some crates used it
292 // accidentally, to be compatible with crates depending on them, we can't throw an
294 Target::AssocConst => {
295 self.tcx.emit_spanned_lint(
299 errors::IgnoredInlineAttrConstants,
303 // FIXME(#80564): Same for fields, arms, and macro defs
304 Target::Field | Target::Arm | Target::MacroDef => {
305 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
309 self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
310 attr_span: attr.span,
318 /// Checks if a `#[no_coverage]` is applied directly to a function
319 fn check_no_coverage(
327 // no_coverage on function is fine
330 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
332 // function prototypes can't be covered
333 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
334 self.tcx.emit_spanned_lint(
338 errors::IgnoredNoCoverageFnProto,
343 Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
344 self.tcx.emit_spanned_lint(
348 errors::IgnoredNoCoveragePropagate,
353 Target::Expression | Target::Statement | Target::Arm => {
354 self.tcx.emit_spanned_lint(
358 errors::IgnoredNoCoverageFnDefn,
364 self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
365 attr_span: attr.span,
373 fn check_generic_attr(
378 allowed_target: Target,
380 if target != allowed_target {
381 self.tcx.emit_spanned_lint(
386 attr_name: attr.name_or_empty(),
387 target_name: allowed_target.name().replace(' ', "_"),
393 /// Checks if `#[naked]` is applied to a function definition.
394 fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
397 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
398 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
399 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
400 // erroneously allowed it and some crates used it accidentally, to be compatible
401 // with crates depending on them, we can't throw an error here.
402 Target::Field | Target::Arm | Target::MacroDef => {
403 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
407 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
408 attr_span: attr.span,
410 on_crate: hir_id == CRATE_HIR_ID,
417 /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition.
418 fn check_cmse_nonsecure_entry(
427 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
429 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
430 attr_span: attr.span,
432 on_crate: hir_id == CRATE_HIR_ID,
439 /// Debugging aid for `object_lifetime_default` query.
440 fn check_object_lifetime_default(&self, hir_id: HirId) {
442 if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
443 for p in generics.params {
444 let hir::GenericParamKind::Type { .. } = p.kind else { continue };
445 let default = tcx.object_lifetime_default(p.def_id);
446 let repr = match default {
447 ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
448 ObjectLifetimeDefault::Static => "'static".to_owned(),
449 ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
450 ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
452 tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr });
457 /// Checks if `#[collapse_debuginfo]` is applied to a macro.
458 fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool {
460 Target::MacroDef => true,
464 .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span });
470 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
471 fn check_track_caller(
480 _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
481 self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
484 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
485 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
486 // `#[track_caller]` attribute with just a lint, because we previously
487 // erroneously allowed it and some crates used it accidentally, to be compatible
488 // with crates depending on them, we can't throw an error here.
489 Target::Field | Target::Arm | Target::MacroDef => {
491 self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
496 self.tcx.sess.emit_err(errors::TrackedCallerWrongLocation {
499 on_crate: hir_id == CRATE_HIR_ID,
506 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
507 fn check_non_exhaustive(
515 Target::Struct | Target::Enum | Target::Variant => true,
516 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
517 // `#[non_exhaustive]` attribute with just a lint, because we previously
518 // erroneously allowed it and some crates used it accidentally, to be compatible
519 // with crates depending on them, we can't throw an error here.
520 Target::Field | Target::Arm | Target::MacroDef => {
521 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
525 self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
526 attr_span: attr.span,
534 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
535 fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
537 Target::Trait => true,
538 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
539 // `#[marker]` attribute with just a lint, because we previously
540 // erroneously allowed it and some crates used it accidentally, to be compatible
541 // with crates depending on them, we can't throw an error here.
542 Target::Field | Target::Arm | Target::MacroDef => {
543 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
547 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
548 attr_span: attr.span,
556 /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid.
557 fn check_rustc_must_implement_one_of(
564 Target::Trait => true,
566 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
567 attr_span: attr.span,
575 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
576 fn check_target_feature(
585 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
586 // FIXME: #[target_feature] was previously erroneously allowed on statements and some
587 // crates used this, so only emit a warning.
588 Target::Statement => {
589 self.tcx.emit_spanned_lint(
593 errors::TargetFeatureOnStatement,
597 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
598 // `#[target_feature]` attribute with just a lint, because we previously
599 // erroneously allowed it and some crates used it accidentally, to be compatible
600 // with crates depending on them, we can't throw an error here.
601 Target::Field | Target::Arm | Target::MacroDef => {
602 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
606 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
607 attr_span: attr.span,
609 on_crate: hir_id == CRATE_HIR_ID,
616 /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid.
617 fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool {
619 Target::ForeignStatic | Target::Static => true,
621 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
622 attr_span: attr.span,
630 fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
631 self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
634 fn check_doc_alias_value(
636 meta: &NestedMetaItem,
641 aliases: &mut FxHashMap<String, Span>,
644 let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
646 &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
647 if doc_alias == kw::Empty {
648 tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
652 let doc_alias_str = doc_alias.as_str();
653 if let Some(c) = doc_alias_str
655 .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
657 tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
660 if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
661 tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
665 let span = meta.span();
666 if let Some(location) = match target {
668 let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id;
669 let containing_item = self.tcx.hir().expect_item(parent_def_id);
670 if Target::from_item(containing_item) == Target::Impl {
671 Some("type alias in implementation block")
676 Target::AssocConst => {
677 let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id;
678 let containing_item = self.tcx.hir().expect_item(parent_def_id);
679 // We can't link to trait impl's consts.
680 let err = "associated constant in trait implementation block";
681 match containing_item.kind {
682 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
686 // we check the validity of params elsewhere
687 Target::Param => return false,
693 | Target::Impl => Some(target.name()),
703 | Target::ImplTraitPlaceholder
713 | Target::ForeignStatic
715 | Target::GenericParam(..)
718 | Target::ExprField => None,
720 tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
723 let item_name = self.tcx.hir().name(hir_id);
724 if item_name == doc_alias {
725 tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
728 if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
729 self.tcx.emit_spanned_lint(
733 errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
741 meta: &NestedMetaItem,
744 aliases: &mut FxHashMap<String, Span>,
746 if let Some(values) = meta.meta_item_list() {
750 Some(l) => match l.kind {
751 LitKind::Str(s, _) => {
752 if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) {
759 .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
764 self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
770 } else if let Some(doc_alias) = meta.value_str() {
771 self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
773 self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
778 fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
779 let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
780 if doc_keyword == kw::Empty {
781 self.doc_attr_str_error(meta, "keyword");
784 match self.tcx.hir().find(hir_id).and_then(|node| match node {
785 hir::Node::Item(item) => Some(&item.kind),
788 Some(ItemKind::Mod(ref module)) => {
789 if !module.item_ids.is_empty() {
790 self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
795 self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
799 if !rustc_lexer::is_ident(doc_keyword.as_str()) {
800 self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
801 span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
809 fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
810 match self.tcx.hir().find(hir_id).and_then(|node| match node {
811 hir::Node::Item(item) => Some(&item.kind),
814 Some(ItemKind::Impl(ref i)) => {
815 let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_]))
816 || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind {
817 bare_fn_ty.decl.inputs.len() == 1
822 self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
827 self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
834 /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
836 /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
837 /// if there are conflicting attributes for one item.
839 /// `specified_inline` is used to keep track of whether we have
840 /// already seen an inlining attribute for this item.
841 /// If so, `specified_inline` holds the value and the span of
842 /// the first `inline`/`no_inline` attribute.
846 meta: &NestedMetaItem,
849 specified_inline: &mut Option<(bool, Span)>,
851 if target == Target::Use || target == Target::ExternCrate {
852 let do_inline = meta.name_or_empty() == sym::inline;
853 if let Some((prev_inline, prev_span)) = *specified_inline {
854 if do_inline != prev_inline {
855 let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
856 spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first);
857 spans.push_span_label(meta.span(), fluent::passes_doc_inline_conflict_second);
858 self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
863 *specified_inline = Some((do_inline, meta.span()));
867 self.tcx.emit_spanned_lint(
868 INVALID_DOC_ATTRIBUTES,
871 errors::DocInlineOnlyUse {
872 attr_span: meta.span(),
873 item_span: (attr.style == AttrStyle::Outer)
874 .then(|| self.tcx.hir().span(hir_id)),
881 /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
882 fn check_attr_not_crate_level(
884 meta: &NestedMetaItem,
888 if CRATE_HIR_ID == hir_id {
889 self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
895 /// Checks that an attribute is used at the crate level. Returns `true` if valid.
896 fn check_attr_crate_level(
899 meta: &NestedMetaItem,
902 if hir_id != CRATE_HIR_ID {
903 self.tcx.struct_span_lint_hir(
904 INVALID_DOC_ATTRIBUTES,
907 fluent::passes_attr_crate_level,
909 if attr.style == AttrStyle::Outer
910 && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID
912 if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
914 err.span_suggestion_verbose(
918 Applicability::MaybeIncorrect,
921 err.span_help(attr.span, fluent::help);
924 err.note(fluent::note);
933 /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if
935 fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
936 let mut is_valid = true;
937 if let Some(metas) = meta.meta_item_list() {
938 for i_meta in metas {
939 match i_meta.name_or_empty() {
940 sym::attr | sym::no_crate_inject => {}
942 self.tcx.emit_spanned_lint(
943 INVALID_DOC_ATTRIBUTES,
946 errors::DocTestUnknown {
947 path: rustc_ast_pretty::pprust::path_to_string(
948 &i_meta.meta_item().unwrap().path,
957 self.tcx.emit_spanned_lint(
958 INVALID_DOC_ATTRIBUTES,
961 errors::DocTestTakesList,
968 /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes.
969 /// Returns `true` if valid.
970 fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
971 if meta.meta_item_list().is_some() {
974 self.tcx.emit_spanned_lint(
975 INVALID_DOC_ATTRIBUTES,
978 errors::DocCfgHideTakesList,
984 /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
986 /// `specified_inline` should be initialized to `None` and kept for the scope
987 /// of one item. Read the documentation of [`check_doc_inline`] for more information.
989 /// [`check_doc_inline`]: Self::check_doc_inline
995 specified_inline: &mut Option<(bool, Span)>,
996 aliases: &mut FxHashMap<String, Span>,
998 let mut is_valid = true;
1000 if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() {
1002 if let Some(i_meta) = meta.meta_item() {
1003 match i_meta.name_or_empty() {
1005 if !self.check_attr_not_crate_level(meta, hir_id, "alias")
1006 || !self.check_doc_alias(meta, hir_id, target, aliases) =>
1012 if !self.check_attr_not_crate_level(meta, hir_id, "keyword")
1013 || !self.check_doc_keyword(meta, hir_id) =>
1019 if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic")
1020 || !self.check_doc_fake_variadic(meta, hir_id) =>
1025 sym::html_favicon_url
1026 | sym::html_logo_url
1027 | sym::html_playground_url
1028 | sym::issue_tracker_base_url
1029 | sym::html_root_url
1030 | sym::html_no_source
1032 if !self.check_attr_crate_level(attr, meta, hir_id) =>
1038 if !self.check_attr_crate_level(attr, meta, hir_id)
1039 || !self.check_doc_cfg_hide(meta, hir_id) =>
1044 sym::inline | sym::no_inline
1045 if !self.check_doc_inline(
1056 // no_default_passes: deprecated
1057 // passes: deprecated
1058 // plugins: removed, but rustdoc warns about it itself
1063 | sym::html_favicon_url
1064 | sym::html_logo_url
1065 | sym::html_no_source
1066 | sym::html_playground_url
1067 | sym::html_root_url
1069 | sym::issue_tracker_base_url
1072 | sym::no_default_passes
1074 | sym::notable_trait
1077 | sym::fake_variadic => {}
1080 if !self.check_test_attr(meta, hir_id) {
1086 if !self.tcx.features().rustdoc_internals {
1087 self.tcx.emit_spanned_lint(
1088 INVALID_DOC_ATTRIBUTES,
1091 errors::DocPrimitive,
1097 let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
1098 if i_meta.has_name(sym::spotlight) {
1099 self.tcx.emit_spanned_lint(
1100 INVALID_DOC_ATTRIBUTES,
1103 errors::DocTestUnknownSpotlight {
1108 } else if i_meta.has_name(sym::include) &&
1109 let Some(value) = i_meta.value_str() {
1110 let applicability = if list.len() == 1 {
1111 Applicability::MachineApplicable
1113 Applicability::MaybeIncorrect
1115 // If there are multiple attributes, the suggestion would suggest
1116 // deleting all of them, which is incorrect.
1117 self.tcx.emit_spanned_lint(
1118 INVALID_DOC_ATTRIBUTES,
1121 errors::DocTestUnknownInclude {
1123 value: value.to_string(),
1124 inner: if attr.style == AttrStyle::Inner { "!" } else { "" },
1125 sugg: (attr.meta().unwrap().span, applicability),
1129 self.tcx.emit_spanned_lint(
1130 INVALID_DOC_ATTRIBUTES,
1133 errors::DocTestUnknownAny { path }
1140 self.tcx.emit_spanned_lint(
1141 INVALID_DOC_ATTRIBUTES,
1154 /// Warns against some misuses of `#[pass_by_value]`
1155 fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1157 Target::Struct | Target::Enum | Target::TyAlias => true,
1159 self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
1165 fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1167 Target::Method(MethodKind::Inherent) => true,
1169 self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
1175 fn check_has_incoherent_inherent_impls(
1182 Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
1188 .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
1194 /// Warns against some misuses of `#[must_use]`
1195 fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool {
1204 // `impl Trait` in return position can trip
1205 // `unused_must_use` if `Trait` is marked as
1209 let article = match target {
1214 | Target::Expression
1216 | Target::AssocConst
1217 | Target::AssocTy => "an",
1221 self.tcx.emit_spanned_lint(
1225 errors::MustUseNoEffect { article, target },
1229 // For now, its always valid
1233 /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
1234 fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1236 Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
1238 self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
1244 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
1245 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1247 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1248 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1249 // `#[cold]` attribute with just a lint, because we previously
1250 // erroneously allowed it and some crates used it accidentally, to be compatible
1251 // with crates depending on them, we can't throw an error here.
1252 Target::Field | Target::Arm | Target::MacroDef => {
1253 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1256 // FIXME: #[cold] was previously allowed on non-functions and some crates used
1257 // this, so only emit a warning.
1258 self.tcx.emit_spanned_lint(
1262 errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
1268 /// Checks if `#[link]` is applied to an item other than a foreign module.
1269 fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1270 if target == Target::ForeignMod
1271 && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
1272 && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1273 && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
1278 self.tcx.emit_spanned_lint(
1282 errors::Link { span: (target != Target::ForeignMod).then_some(span) },
1286 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1287 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1289 Target::ForeignFn | Target::ForeignStatic => {}
1290 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1291 // `#[link_name]` attribute with just a lint, because we previously
1292 // erroneously allowed it and some crates used it accidentally, to be compatible
1293 // with crates depending on them, we can't throw an error here.
1294 Target::Field | Target::Arm | Target::MacroDef => {
1295 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1298 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1299 // used this, so only emit a warning.
1300 let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span);
1301 if let Some(s) = attr.value_str() {
1302 self.tcx.emit_spanned_lint(
1306 errors::LinkName { span, attr_span, value: s.as_str() },
1309 self.tcx.emit_spanned_lint(
1313 errors::LinkName { span, attr_span, value: "..." },
1320 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
1321 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1323 Target::ExternCrate => true,
1324 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1325 // `#[no_link]` attribute with just a lint, because we previously
1326 // erroneously allowed it and some crates used it accidentally, to be compatible
1327 // with crates depending on them, we can't throw an error here.
1328 Target::Field | Target::Arm | Target::MacroDef => {
1329 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1333 self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span });
1339 fn is_impl_item(&self, hir_id: HirId) -> bool {
1340 matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
1343 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
1344 fn check_export_name(
1352 Target::Static | Target::Fn => true,
1353 Target::Method(..) if self.is_impl_item(hir_id) => true,
1354 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1355 // `#[export_name]` attribute with just a lint, because we previously
1356 // erroneously allowed it and some crates used it accidentally, to be compatible
1357 // with crates depending on them, we can't throw an error here.
1358 Target::Field | Target::Arm | Target::MacroDef => {
1359 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1363 self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span });
1369 fn check_rustc_layout_scalar_valid_range(
1375 if target != Target::Struct {
1376 self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct {
1377 attr_span: attr.span,
1383 let Some(list) = attr.meta_item_list() else {
1387 if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) {
1390 self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span });
1395 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1396 fn check_rustc_legacy_const_generics(
1402 item: Option<ItemLike<'_>>,
1404 let is_function = matches!(target, Target::Fn);
1406 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1407 attr_span: attr.span,
1409 on_crate: hir_id == CRATE_HIR_ID,
1414 let Some(list) = attr.meta_item_list() else {
1415 // The attribute form is validated on AST.
1419 let Some(ItemLike::Item(Item {
1420 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1423 bug!("should be a function item");
1426 for param in generics.params {
1428 hir::GenericParamKind::Const { .. } => {}
1430 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly {
1431 attr_span: attr.span,
1432 param_span: param.span,
1439 if list.len() != generics.params.len() {
1440 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex {
1441 attr_span: attr.span,
1442 generics_span: generics.span,
1447 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1448 let mut invalid_args = vec![];
1450 if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
1451 if *val >= arg_count {
1452 let span = meta.span();
1453 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed {
1455 arg_count: arg_count as usize,
1460 invalid_args.push(meta.span());
1464 if !invalid_args.is_empty() {
1465 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
1472 /// Helper function for checking that the provided attribute is only applied to a function or
1474 fn check_applied_to_fn_or_method(
1481 let is_function = matches!(target, Target::Fn | Target::Method(..));
1483 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1484 attr_span: attr.span,
1486 on_crate: hir_id == CRATE_HIR_ID,
1494 /// Checks that the `#[rustc_lint_query_instability]` attribute is only applied to a function
1496 fn check_rustc_lint_query_instability(
1503 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
1506 /// Checks that the `#[rustc_lint_diagnostics]` attribute is only applied to a function or
1508 fn check_rustc_lint_diagnostics(
1515 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
1518 /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
1519 fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1521 Target::Struct => true,
1523 self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
1529 /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
1530 fn check_rustc_lint_opt_deny_field_access(
1537 Target::Field => true,
1541 .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
1547 /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1548 /// option is passed to the compiler.
1549 fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1550 if self.tcx.sess.opts.unstable_opts.query_dep_graph {
1553 self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span });
1558 /// Checks if `#[link_section]` is applied to a function or static.
1559 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1561 Target::Static | Target::Fn | Target::Method(..) => {}
1562 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1563 // `#[link_section]` attribute with just a lint, because we previously
1564 // erroneously allowed it and some crates used it accidentally, to be compatible
1565 // with crates depending on them, we can't throw an error here.
1566 Target::Field | Target::Arm | Target::MacroDef => {
1567 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1570 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1571 // crates used this, so only emit a warning.
1572 self.tcx.emit_spanned_lint(
1576 errors::LinkSection { span },
1582 /// Checks if `#[no_mangle]` is applied to a function or static.
1583 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1585 Target::Static | Target::Fn => {}
1586 Target::Method(..) if self.is_impl_item(hir_id) => {}
1587 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1588 // `#[no_mangle]` attribute with just a lint, because we previously
1589 // erroneously allowed it and some crates used it accidentally, to be compatible
1590 // with crates depending on them, we can't throw an error here.
1591 Target::Field | Target::Arm | Target::MacroDef => {
1592 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1594 // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
1595 // The error should specify that the item that is wrong is specifically a *foreign* fn/static
1596 // otherwise the error seems odd
1597 Target::ForeignFn | Target::ForeignStatic => {
1598 let foreign_item_kind = match target {
1599 Target::ForeignFn => "function",
1600 Target::ForeignStatic => "static",
1601 _ => unreachable!(),
1603 self.tcx.emit_spanned_lint(
1607 errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind },
1611 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1612 // crates used this, so only emit a warning.
1613 self.tcx.emit_spanned_lint(
1617 errors::NoMangle { span },
1623 /// Checks if the `#[repr]` attributes on `item` are valid.
1626 attrs: &[Attribute],
1629 item: Option<ItemLike<'_>>,
1632 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1635 // #[repr(bar, align(8))]
1637 let hints: Vec<_> = attrs
1639 .filter(|attr| attr.has_name(sym::repr))
1640 .filter_map(|attr| attr.meta_item_list())
1644 let mut int_reprs = 0;
1645 let mut is_c = false;
1646 let mut is_simd = false;
1647 let mut is_transparent = false;
1649 for hint in &hints {
1650 if !hint.is_meta_item() {
1651 self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() });
1655 match hint.name_or_empty() {
1659 Target::Struct | Target::Union | Target::Enum => continue,
1661 self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
1662 hint_span: hint.span(),
1669 if let (Target::Fn, false) = (target, self.tcx.features().fn_align) {
1671 &self.tcx.sess.parse_sess,
1674 "`repr(align)` attributes on functions are unstable",
1680 Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1682 self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion {
1683 hint_span: hint.span(),
1690 if target != Target::Struct && target != Target::Union {
1691 self.tcx.sess.emit_err(AttrApplication::StructUnion {
1692 hint_span: hint.span(),
1701 if target != Target::Struct {
1704 .emit_err(AttrApplication::Struct { hint_span: hint.span(), span });
1709 sym::transparent => {
1710 is_transparent = true;
1712 Target::Struct | Target::Union | Target::Enum => continue,
1714 self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
1715 hint_span: hint.span(),
1734 if target != Target::Enum {
1737 .emit_err(AttrApplication::Enum { hint_span: hint.span(), span });
1743 self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() });
1749 // Just point at all repr hints if there are any incompatibilities.
1750 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1751 let hint_spans = hints.iter().map(|hint| hint.span());
1753 // Error on repr(transparent, <anything else>).
1754 if is_transparent && hints.len() > 1 {
1755 let hint_spans: Vec<_> = hint_spans.clone().collect();
1758 .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() });
1760 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1762 || (is_simd && is_c)
1765 && item.map_or(false, |item| {
1766 if let ItemLike::Item(item) = item {
1767 return is_c_like_enum(item);
1772 self.tcx.emit_spanned_lint(
1773 CONFLICTING_REPR_HINTS,
1775 hint_spans.collect::<Vec<Span>>(),
1776 errors::ReprConflicting,
1781 fn check_used(&self, attrs: &[Attribute], target: Target) {
1782 let mut used_linker_span = None;
1783 let mut used_compiler_span = None;
1784 for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
1785 if target != Target::Static {
1786 self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span });
1788 let inner = attr.meta_item_list();
1789 match inner.as_deref() {
1790 Some([item]) if item.has_name(sym::linker) => {
1791 if used_linker_span.is_none() {
1792 used_linker_span = Some(attr.span);
1795 Some([item]) if item.has_name(sym::compiler) => {
1796 if used_compiler_span.is_none() {
1797 used_compiler_span = Some(attr.span);
1801 // This error case is handled in rustc_hir_analysis::collect.
1804 // Default case (compiler) when arg isn't defined.
1805 if used_compiler_span.is_none() {
1806 used_compiler_span = Some(attr.span);
1811 if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
1814 .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
1818 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1819 /// (Allows proc_macro functions)
1820 fn check_allow_internal_unstable(
1826 attrs: &[Attribute],
1828 debug!("Checking target: {:?}", target);
1832 if self.tcx.sess.is_proc_macro_attr(attr) {
1833 debug!("Is proc macro attr");
1837 debug!("Is not proc macro attr");
1840 Target::MacroDef => true,
1841 // FIXME(#80564): We permit struct fields and match arms to have an
1842 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1843 // erroneously allowed it and some crates used it accidentally, to be compatible
1844 // with crates depending on them, we can't throw an error here.
1845 Target::Field | Target::Arm => {
1846 self.inline_attr_str_error_without_macro_def(
1849 "allow_internal_unstable",
1856 .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span });
1862 /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
1863 fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
1867 self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span });
1872 let Some(hints) = attr.meta_item_list() else {
1873 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1877 let hint = match hints.len() {
1880 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1885 let Some(meta_item) = hint.meta_item() else {
1886 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1890 let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
1891 (sym::natvis_file, Some(value)) => value,
1892 (sym::gdb_script_file, Some(value)) => value,
1894 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span });
1900 match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
1908 match std::fs::File::open(&file) {
1911 self.tcx.sess.emit_err(DebugVisualizerUnreadable {
1912 span: meta_item.span,
1921 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1922 /// (Allows proc_macro functions)
1923 fn check_rustc_allow_const_fn_unstable(
1931 Target::Fn | Target::Method(_)
1932 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id).to_def_id()) =>
1936 // FIXME(#80564): We permit struct fields and match arms to have an
1937 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1938 // erroneously allowed it and some crates used it accidentally, to be compatible
1939 // with crates depending on them, we can't throw an error here.
1940 Target::Field | Target::Arm | Target::MacroDef => {
1941 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1947 .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span });
1953 fn check_rustc_std_internal_symbol(
1960 Target::Fn | Target::Static => true,
1964 .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span });
1970 /// `#[const_trait]` only applies to traits.
1971 fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1973 Target::Trait => true,
1975 self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span });
1981 fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1983 Target::Expression => {
1984 self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span });
1991 fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1993 Target::ForeignFn | Target::ForeignStatic => true,
1995 self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span });
2001 fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
2003 Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
2004 self.tcx.emit_spanned_lint(
2015 fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2016 let name = attr.name_or_empty();
2018 Target::ExternCrate | Target::Mod => {}
2020 self.tcx.emit_spanned_lint(
2024 errors::MacroUse { name },
2030 fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2031 if target != Target::MacroDef {
2032 self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport);
2036 fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2037 if target != Target::Fn {
2038 self.tcx.emit_spanned_lint(
2042 errors::PluginRegistrar,
2047 fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
2048 // Warn on useless empty attributes.
2049 let note = if matches!(
2050 attr.name_or_empty(),
2059 | sym::target_feature
2060 ) && attr.meta_item_list().map_or(false, |list| list.is_empty())
2062 errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
2064 attr.name_or_empty(),
2065 sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
2066 ) && let Some(meta) = attr.meta_item_list()
2068 && let Some(item) = meta[0].meta_item()
2069 && let MetaItemKind::NameValue(_) = &item.kind
2070 && item.path == sym::reason
2072 errors::UnusedNote::NoLints { name: attr.name_or_empty() }
2073 } else if attr.name_or_empty() == sym::default_method_body_is_const {
2074 errors::UnusedNote::DefaultMethodBodyConst
2079 self.tcx.emit_spanned_lint(
2083 errors::Unused { attr_span: attr.span, note },
2087 fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2088 let expected_input_count = match kind {
2089 ProcMacroKind::Attribute => 2,
2090 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2093 let expected_signature = match kind {
2094 ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
2095 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
2099 if target == Target::Fn {
2100 let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
2101 let tokenstream = tcx.type_of(tokenstream);
2103 let id = hir_id.expect_owner();
2104 let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
2106 let sig = tcx.fn_sig(id);
2108 if sig.abi() != Abi::Rust {
2110 .emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi().name() });
2111 self.abort.set(true);
2114 if sig.unsafety() == Unsafety::Unsafe {
2115 tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
2116 self.abort.set(true);
2119 let output = sig.output().skip_binder();
2121 // Typecheck the output
2122 if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream {
2123 tcx.sess.emit_err(ProcMacroTypeError {
2124 span: hir_sig.decl.output.span(),
2129 self.abort.set(true);
2132 // Typecheck "expected_input_count" inputs, emitting
2133 // `ProcMacroMissingArguments` if there are not enough.
2134 if let Some(args) = sig.inputs().skip_binder().get(0..expected_input_count) {
2135 for (arg, input) in args.iter().zip(hir_sig.decl.inputs) {
2136 if tcx.normalize_erasing_regions(ParamEnv::empty(), *arg) != tokenstream {
2137 tcx.sess.emit_err(ProcMacroTypeError {
2143 self.abort.set(true);
2147 tcx.sess.emit_err(ProcMacroMissingArguments {
2148 expected_input_count,
2153 self.abort.set(true);
2156 // Check that there are not too many arguments
2157 let body_id = tcx.hir().body_owned_by(id.def_id);
2158 let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
2159 if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
2160 tcx.sess.emit_err(ProcMacroDiffArguments {
2161 span: begin.span.to(end.span),
2162 count: excess.len(),
2166 self.abort.set(true);
2172 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2173 type NestedFilter = nested_filter::OnlyBodies;
2175 fn nested_visit_map(&mut self) -> Self::Map {
2179 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2180 // Historically we've run more checks on non-exported than exported macros,
2181 // so this lets us continue to run them while maintaining backwards compatibility.
2182 // In the long run, the checks should be harmonized.
2183 if let ItemKind::Macro(ref macro_def, _) = item.kind {
2184 let def_id = item.owner_id.to_def_id();
2185 if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
2186 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2190 let target = Target::from_item(item);
2191 self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2192 intravisit::walk_item(self, item)
2195 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2196 let target = Target::from_generic_param(generic_param);
2197 self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2198 intravisit::walk_generic_param(self, generic_param)
2201 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2202 let target = Target::from_trait_item(trait_item);
2203 self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2204 intravisit::walk_trait_item(self, trait_item)
2207 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2208 self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2209 intravisit::walk_field_def(self, struct_field);
2212 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2213 self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2214 intravisit::walk_arm(self, arm);
2217 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2218 let target = Target::from_foreign_item(f_item);
2219 self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));
2220 intravisit::walk_foreign_item(self, f_item)
2223 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2224 let target = target_from_impl_item(self.tcx, impl_item);
2225 self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2226 intravisit::walk_impl_item(self, impl_item)
2229 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2230 // When checking statements ignore expressions, they will be checked later.
2231 if let hir::StmtKind::Local(ref l) = stmt.kind {
2232 self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2234 intravisit::walk_stmt(self, stmt)
2237 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2238 let target = match expr.kind {
2239 hir::ExprKind::Closure { .. } => Target::Closure,
2240 _ => Target::Expression,
2243 self.check_attributes(expr.hir_id, expr.span, target, None);
2244 intravisit::walk_expr(self, expr)
2247 fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
2248 self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
2249 intravisit::walk_expr_field(self, field)
2252 fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
2253 self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);
2254 intravisit::walk_variant(self, variant)
2257 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2258 self.check_attributes(param.hir_id, param.span, Target::Param, None);
2260 intravisit::walk_param(self, param);
2263 fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
2264 self.check_attributes(field.hir_id, field.span, Target::PatField, None);
2265 intravisit::walk_pat_field(self, field);
2269 fn is_c_like_enum(item: &Item<'_>) -> bool {
2270 if let ItemKind::Enum(ref def, _) = item.kind {
2271 for variant in def.variants {
2272 match variant.data {
2273 hir::VariantData::Unit(..) => { /* continue */ }
2283 // FIXME: Fix "Cannot determine resolution" error and remove built-in macros
2285 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2286 // Check for builtin attributes at the crate level
2287 // which were unsuccessfully resolved due to cannot determine
2288 // resolution for the attribute macro error.
2289 const ATTRS_TO_CHECK: &[Symbol] = &[
2293 sym::automatically_derived,
2300 sym::global_allocator,
2305 // This function should only be called with crate attributes
2306 // which are inner attributes always but lets check to make sure
2307 if attr.style == AttrStyle::Inner {
2308 for attr_to_check in ATTRS_TO_CHECK {
2309 if attr.has_name(*attr_to_check) {
2310 tcx.sess.emit_err(InvalidAttrAtCrateLevel {
2312 snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
2313 name: *attr_to_check,
2321 fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2322 let attrs = tcx.hir().attrs(item.hir_id());
2325 if attr.has_name(sym::inline) {
2326 tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span });
2331 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
2332 let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
2333 tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
2334 if module_def_id.is_top_level_module() {
2335 check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2336 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
2338 if check_attr_visitor.abort.get() {
2339 tcx.sess.abort_if_errors()
2343 pub(crate) fn provide(providers: &mut Providers) {
2344 *providers = Providers { check_mod_attrs, ..*providers };
2347 fn check_duplicates(
2351 duplicates: AttributeDuplicates,
2352 seen: &mut FxHashMap<Symbol, Span>,
2354 use AttributeDuplicates::*;
2355 if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2360 WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2361 match seen.entry(attr.name_or_empty()) {
2362 Entry::Occupied(mut entry) => {
2363 let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2364 let to_remove = entry.insert(attr.span);
2365 (to_remove, attr.span)
2367 (attr.span, *entry.get())
2369 tcx.emit_spanned_lint(
2373 errors::UnusedDuplicate {
2378 FutureWarnFollowing | FutureWarnPreceding
2384 Entry::Vacant(entry) => {
2385 entry.insert(attr.span);
2389 ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) {
2390 Entry::Occupied(mut entry) => {
2391 let (this, other) = if matches!(duplicates, ErrorPreceding) {
2392 let to_remove = entry.insert(attr.span);
2393 (to_remove, attr.span)
2395 (attr.span, *entry.get())
2397 tcx.sess.emit_err(errors::UnusedMultiple {
2400 name: attr.name_or_empty(),
2403 Entry::Vacant(entry) => {
2404 entry.insert(attr.span);