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::fast_reject::{DeepRejectCtxt, TreatParams};
27 use rustc_middle::ty::query::Providers;
28 use rustc_middle::ty::{ParamEnv, TyCtxt};
29 use rustc_session::lint::builtin::{
30 CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
32 use rustc_session::parse::feature_err;
33 use rustc_span::symbol::{kw, sym, Symbol};
34 use rustc_span::{Span, DUMMY_SP};
35 use rustc_target::spec::abi::Abi;
37 use std::collections::hash_map::Entry;
39 pub(crate) fn target_from_impl_item<'tcx>(
41 impl_item: &hir::ImplItem<'_>,
43 match impl_item.kind {
44 hir::ImplItemKind::Const(..) => Target::AssocConst,
45 hir::ImplItemKind::Fn(..) => {
46 let parent_def_id = tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
47 let containing_item = tcx.hir().expect_item(parent_def_id);
48 let containing_impl_is_for_trait = match &containing_item.kind {
49 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
50 _ => bug!("parent of an ImplItem must be an Impl"),
52 if containing_impl_is_for_trait {
53 Target::Method(MethodKind::Trait { body: true })
55 Target::Method(MethodKind::Inherent)
58 hir::ImplItemKind::Type(..) => Target::AssocTy,
62 #[derive(Clone, Copy)]
64 Item(&'tcx Item<'tcx>),
68 #[derive(Copy, Clone)]
69 pub(crate) enum ProcMacroKind {
75 impl IntoDiagnosticArg for ProcMacroKind {
76 fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
78 ProcMacroKind::Attribute => "attribute proc macro",
79 ProcMacroKind::Derive => "derive proc macro",
80 ProcMacroKind::FunctionLike => "function-like proc macro",
82 .into_diagnostic_arg()
86 struct CheckAttrVisitor<'tcx> {
89 // Whether or not this visitor should abort after finding errors
93 impl CheckAttrVisitor<'_> {
94 /// Checks any attribute.
100 item: Option<ItemLike<'_>>,
102 let mut doc_aliases = FxHashMap::default();
103 let mut is_valid = true;
104 let mut specified_inline = None;
105 let mut seen = FxHashMap::default();
106 let attrs = self.tcx.hir().attrs(hir_id);
108 let attr_is_valid = match attr.name_or_empty() {
109 sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
110 sym::inline => self.check_inline(hir_id, attr, span, target),
111 sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
112 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
113 sym::marker => self.check_marker(hir_id, attr, span, target),
114 sym::rustc_must_implement_one_of => {
115 self.check_rustc_must_implement_one_of(attr, span, target)
117 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
118 sym::thread_local => self.check_thread_local(attr, span, target),
119 sym::track_caller => {
120 self.check_track_caller(hir_id, attr.span, attrs, span, target)
122 sym::doc => self.check_doc_attrs(
126 &mut specified_inline,
129 sym::no_link => self.check_no_link(hir_id, &attr, span, target),
130 sym::export_name => self.check_export_name(hir_id, &attr, span, target),
131 sym::rustc_layout_scalar_valid_range_start
132 | sym::rustc_layout_scalar_valid_range_end => {
133 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
135 sym::allow_internal_unstable => {
136 self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
138 sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
139 sym::rustc_allow_const_fn_unstable => {
140 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
142 sym::rustc_std_internal_symbol => {
143 self.check_rustc_std_internal_symbol(&attr, span, target)
145 sym::naked => self.check_naked(hir_id, attr, span, target),
146 sym::rustc_legacy_const_generics => {
147 self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item)
149 sym::rustc_lint_query_instability => {
150 self.check_rustc_lint_query_instability(hir_id, &attr, span, target)
152 sym::rustc_lint_diagnostics => {
153 self.check_rustc_lint_diagnostics(hir_id, &attr, span, target)
155 sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
156 sym::rustc_lint_opt_deny_field_access => {
157 self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
161 | sym::rustc_if_this_changed
162 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
163 sym::cmse_nonsecure_entry => {
164 self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
166 sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
167 sym::const_trait => self.check_const_trait(attr, span, target),
168 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
169 sym::must_use => self.check_must_use(hir_id, &attr, target),
170 sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
171 sym::rustc_allow_incoherent_impl => {
172 self.check_allow_incoherent_impl(&attr, span, target)
174 sym::rustc_has_incoherent_inherent_impls => {
175 self.check_has_incoherent_inherent_impls(&attr, span, target)
177 sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target),
178 sym::ffi_const => self.check_ffi_const(attr.span, target),
179 sym::ffi_returns_twice => self.check_ffi_returns_twice(attr.span, target),
180 sym::rustc_const_unstable
181 | sym::rustc_const_stable
184 | sym::rustc_allowed_through_unstable_modules
185 | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
186 sym::link_ordinal => self.check_link_ordinal(&attr, span, target),
189 is_valid &= attr_is_valid;
192 match attr.name_or_empty() {
193 sym::cold => self.check_cold(hir_id, attr, span, target),
194 sym::link => self.check_link(hir_id, attr, span, target),
195 sym::link_name => self.check_link_name(hir_id, attr, span, target),
196 sym::link_section => self.check_link_section(hir_id, attr, span, target),
197 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
198 sym::deprecated => self.check_deprecated(hir_id, attr, span, target),
199 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
200 sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
201 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
202 sym::macro_export => self.check_macro_export(hir_id, attr, target),
203 sym::ignore | sym::should_panic => {
204 self.check_generic_attr(hir_id, attr, target, Target::Fn)
206 sym::automatically_derived => {
207 self.check_generic_attr(hir_id, attr, target, Target::Impl)
209 sym::no_implicit_prelude => {
210 self.check_generic_attr(hir_id, attr, target, Target::Mod)
212 sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
214 self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
216 sym::proc_macro_attribute => {
217 self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
219 sym::proc_macro_derive => {
220 self.check_generic_attr(hir_id, attr, target, Target::Fn);
221 self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
226 let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
228 if hir_id != CRATE_HIR_ID {
229 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
230 attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
233 ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
237 errors::OuterCrateLevelAttr,
239 ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
243 errors::InnerCrateLevelAttr,
249 if let Some(BuiltinAttribute { duplicates, .. }) = builtin {
250 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
253 self.check_unused_attribute(hir_id, attr)
260 self.check_repr(attrs, span, target, item, hir_id);
261 self.check_used(attrs, target);
264 fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
265 self.tcx.emit_spanned_lint(
269 errors::IgnoredAttrWithMacro { sym },
273 fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
274 self.tcx.emit_spanned_lint(
278 errors::IgnoredAttr { sym },
282 /// Checks if `#[do_not_recommend]` is applied on a trait impl.
283 fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool {
284 if let Target::Impl = target {
287 self.tcx.sess.emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span });
292 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
293 fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
297 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
298 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
299 self.tcx.emit_spanned_lint(
303 errors::IgnoredInlineAttrFnProto,
307 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
308 // just a lint, because we previously erroneously allowed it and some crates used it
309 // accidentally, to be compatible with crates depending on them, we can't throw an
311 Target::AssocConst => {
312 self.tcx.emit_spanned_lint(
316 errors::IgnoredInlineAttrConstants,
320 // FIXME(#80564): Same for fields, arms, and macro defs
321 Target::Field | Target::Arm | Target::MacroDef => {
322 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
326 self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
327 attr_span: attr.span,
335 /// Checks if a `#[no_coverage]` is applied directly to a function
336 fn check_no_coverage(
344 // no_coverage on function is fine
347 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
349 // function prototypes can't be covered
350 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
351 self.tcx.emit_spanned_lint(
355 errors::IgnoredNoCoverageFnProto,
360 Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
361 self.tcx.emit_spanned_lint(
365 errors::IgnoredNoCoveragePropagate,
370 Target::Expression | Target::Statement | Target::Arm => {
371 self.tcx.emit_spanned_lint(
375 errors::IgnoredNoCoverageFnDefn,
381 self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
382 attr_span: attr.span,
390 fn check_generic_attr(
395 allowed_target: Target,
397 if target != allowed_target {
398 self.tcx.emit_spanned_lint(
403 attr_name: attr.name_or_empty(),
404 target_name: allowed_target.name().replace(' ', "_"),
410 /// Checks if `#[naked]` is applied to a function definition.
411 fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
414 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
415 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
416 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
417 // erroneously allowed it and some crates used it accidentally, to be compatible
418 // with crates depending on them, we can't throw an error here.
419 Target::Field | Target::Arm | Target::MacroDef => {
420 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
424 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
425 attr_span: attr.span,
427 on_crate: hir_id == CRATE_HIR_ID,
434 /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition.
435 fn check_cmse_nonsecure_entry(
444 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
446 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
447 attr_span: attr.span,
449 on_crate: hir_id == CRATE_HIR_ID,
456 /// Debugging aid for `object_lifetime_default` query.
457 fn check_object_lifetime_default(&self, hir_id: HirId) {
459 if let Some(owner_id) = hir_id.as_owner()
460 && let Some(generics) = tcx.hir().get_generics(owner_id.def_id)
462 for p in generics.params {
463 let hir::GenericParamKind::Type { .. } = p.kind else { continue };
464 let default = tcx.object_lifetime_default(p.def_id);
465 let repr = match default {
466 ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
467 ObjectLifetimeDefault::Static => "'static".to_owned(),
468 ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
469 ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
471 tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr });
476 /// Checks if `#[collapse_debuginfo]` is applied to a macro.
477 fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool {
479 Target::MacroDef => true,
483 .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span });
489 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
490 fn check_track_caller(
499 _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
500 self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
503 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
504 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
505 // `#[track_caller]` attribute with just a lint, because we previously
506 // erroneously allowed it and some crates used it accidentally, to be compatible
507 // with crates depending on them, we can't throw an error here.
508 Target::Field | Target::Arm | Target::MacroDef => {
510 self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
515 self.tcx.sess.emit_err(errors::TrackedCallerWrongLocation {
518 on_crate: hir_id == CRATE_HIR_ID,
525 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
526 fn check_non_exhaustive(
534 Target::Struct | Target::Enum | Target::Variant => true,
535 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
536 // `#[non_exhaustive]` attribute with just a lint, because we previously
537 // erroneously allowed it and some crates used it accidentally, to be compatible
538 // with crates depending on them, we can't throw an error here.
539 Target::Field | Target::Arm | Target::MacroDef => {
540 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
544 self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
545 attr_span: attr.span,
553 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
554 fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
556 Target::Trait => true,
557 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
558 // `#[marker]` attribute with just a lint, because we previously
559 // erroneously allowed it and some crates used it accidentally, to be compatible
560 // with crates depending on them, we can't throw an error here.
561 Target::Field | Target::Arm | Target::MacroDef => {
562 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
566 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
567 attr_span: attr.span,
575 /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid.
576 fn check_rustc_must_implement_one_of(
583 Target::Trait => true,
585 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
586 attr_span: attr.span,
594 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
595 fn check_target_feature(
604 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
605 // FIXME: #[target_feature] was previously erroneously allowed on statements and some
606 // crates used this, so only emit a warning.
607 Target::Statement => {
608 self.tcx.emit_spanned_lint(
612 errors::TargetFeatureOnStatement,
616 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
617 // `#[target_feature]` attribute with just a lint, because we previously
618 // erroneously allowed it and some crates used it accidentally, to be compatible
619 // with crates depending on them, we can't throw an error here.
620 Target::Field | Target::Arm | Target::MacroDef => {
621 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
625 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
626 attr_span: attr.span,
628 on_crate: hir_id == CRATE_HIR_ID,
635 /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid.
636 fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool {
638 Target::ForeignStatic | Target::Static => true,
640 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
641 attr_span: attr.span,
649 fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
650 self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
653 fn check_doc_alias_value(
655 meta: &NestedMetaItem,
660 aliases: &mut FxHashMap<String, Span>,
663 let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
665 &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
666 if doc_alias == kw::Empty {
667 tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
671 let doc_alias_str = doc_alias.as_str();
672 if let Some(c) = doc_alias_str
674 .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
676 tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
679 if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
680 tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
684 let span = meta.span();
685 if let Some(location) = match target {
687 let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id;
688 let containing_item = self.tcx.hir().expect_item(parent_def_id);
689 if Target::from_item(containing_item) == Target::Impl {
690 Some("type alias in implementation block")
695 Target::AssocConst => {
696 let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id;
697 let containing_item = self.tcx.hir().expect_item(parent_def_id);
698 // We can't link to trait impl's consts.
699 let err = "associated constant in trait implementation block";
700 match containing_item.kind {
701 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
705 // we check the validity of params elsewhere
706 Target::Param => return false,
712 | Target::Impl => Some(target.name()),
722 | Target::ImplTraitPlaceholder
732 | Target::ForeignStatic
734 | Target::GenericParam(..)
737 | Target::ExprField => None,
739 tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
742 let item_name = self.tcx.hir().name(hir_id);
743 if item_name == doc_alias {
744 tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
747 if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
748 self.tcx.emit_spanned_lint(
752 errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
760 meta: &NestedMetaItem,
763 aliases: &mut FxHashMap<String, Span>,
765 if let Some(values) = meta.meta_item_list() {
769 Some(l) => match l.kind {
770 LitKind::Str(s, _) => {
771 if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) {
778 .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
783 self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
789 } else if let Some(doc_alias) = meta.value_str() {
790 self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
792 self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
797 fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
798 let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
799 if doc_keyword == kw::Empty {
800 self.doc_attr_str_error(meta, "keyword");
803 match self.tcx.hir().find(hir_id).and_then(|node| match node {
804 hir::Node::Item(item) => Some(&item.kind),
807 Some(ItemKind::Mod(ref module)) => {
808 if !module.item_ids.is_empty() {
809 self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
814 self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
818 if !rustc_lexer::is_ident(doc_keyword.as_str()) {
819 self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
820 span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
828 fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
829 match self.tcx.hir().find(hir_id).and_then(|node| match node {
830 hir::Node::Item(item) => Some(&item.kind),
833 Some(ItemKind::Impl(ref i)) => {
834 let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_]))
835 || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind {
836 bare_fn_ty.decl.inputs.len() == 1
841 self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
846 self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
853 /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
855 /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
856 /// if there are conflicting attributes for one item.
858 /// `specified_inline` is used to keep track of whether we have
859 /// already seen an inlining attribute for this item.
860 /// If so, `specified_inline` holds the value and the span of
861 /// the first `inline`/`no_inline` attribute.
865 meta: &NestedMetaItem,
868 specified_inline: &mut Option<(bool, Span)>,
871 Target::Use | Target::ExternCrate => {
872 let do_inline = meta.name_or_empty() == sym::inline;
873 if let Some((prev_inline, prev_span)) = *specified_inline {
874 if do_inline != prev_inline {
875 let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
876 spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first);
877 spans.push_span_label(
879 fluent::passes_doc_inline_conflict_second,
881 self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
886 *specified_inline = Some((do_inline, meta.span()));
891 self.tcx.emit_spanned_lint(
892 INVALID_DOC_ATTRIBUTES,
895 errors::DocInlineOnlyUse {
896 attr_span: meta.span(),
897 item_span: (attr.style == AttrStyle::Outer)
898 .then(|| self.tcx.hir().span(hir_id)),
906 /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
907 fn check_attr_not_crate_level(
909 meta: &NestedMetaItem,
913 if CRATE_HIR_ID == hir_id {
914 self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
920 /// Checks that an attribute is used at the crate level. Returns `true` if valid.
921 fn check_attr_crate_level(
924 meta: &NestedMetaItem,
927 if hir_id != CRATE_HIR_ID {
928 self.tcx.struct_span_lint_hir(
929 INVALID_DOC_ATTRIBUTES,
932 fluent::passes_attr_crate_level,
934 if attr.style == AttrStyle::Outer
935 && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID
937 if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
939 err.span_suggestion_verbose(
943 Applicability::MaybeIncorrect,
946 err.span_help(attr.span, fluent::help);
949 err.note(fluent::note);
958 /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if
960 fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
961 let mut is_valid = true;
962 if let Some(metas) = meta.meta_item_list() {
963 for i_meta in metas {
964 match i_meta.name_or_empty() {
965 sym::attr | sym::no_crate_inject => {}
967 self.tcx.emit_spanned_lint(
968 INVALID_DOC_ATTRIBUTES,
971 errors::DocTestUnknown {
972 path: rustc_ast_pretty::pprust::path_to_string(
973 &i_meta.meta_item().unwrap().path,
982 self.tcx.emit_spanned_lint(
983 INVALID_DOC_ATTRIBUTES,
986 errors::DocTestTakesList,
993 /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes.
994 /// Returns `true` if valid.
995 fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
996 if meta.meta_item_list().is_some() {
999 self.tcx.emit_spanned_lint(
1000 INVALID_DOC_ATTRIBUTES,
1003 errors::DocCfgHideTakesList,
1009 /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
1011 /// `specified_inline` should be initialized to `None` and kept for the scope
1012 /// of one item. Read the documentation of [`check_doc_inline`] for more information.
1014 /// [`check_doc_inline`]: Self::check_doc_inline
1020 specified_inline: &mut Option<(bool, Span)>,
1021 aliases: &mut FxHashMap<String, Span>,
1023 let mut is_valid = true;
1025 if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() {
1027 if let Some(i_meta) = meta.meta_item() {
1028 match i_meta.name_or_empty() {
1030 if !self.check_attr_not_crate_level(meta, hir_id, "alias")
1031 || !self.check_doc_alias(meta, hir_id, target, aliases) =>
1037 if !self.check_attr_not_crate_level(meta, hir_id, "keyword")
1038 || !self.check_doc_keyword(meta, hir_id) =>
1044 if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic")
1045 || !self.check_doc_fake_variadic(meta, hir_id) =>
1050 sym::html_favicon_url
1051 | sym::html_logo_url
1052 | sym::html_playground_url
1053 | sym::issue_tracker_base_url
1054 | sym::html_root_url
1055 | sym::html_no_source
1057 if !self.check_attr_crate_level(attr, meta, hir_id) =>
1063 if !self.check_attr_crate_level(attr, meta, hir_id)
1064 || !self.check_doc_cfg_hide(meta, hir_id) =>
1069 sym::inline | sym::no_inline
1070 if !self.check_doc_inline(
1081 // no_default_passes: deprecated
1082 // passes: deprecated
1083 // plugins: removed, but rustdoc warns about it itself
1088 | sym::html_favicon_url
1089 | sym::html_logo_url
1090 | sym::html_no_source
1091 | sym::html_playground_url
1092 | sym::html_root_url
1094 | sym::issue_tracker_base_url
1097 | sym::no_default_passes
1099 | sym::notable_trait
1102 | sym::fake_variadic => {}
1105 if !self.check_test_attr(meta, hir_id) {
1111 if !self.tcx.features().rustdoc_internals {
1112 self.tcx.emit_spanned_lint(
1113 INVALID_DOC_ATTRIBUTES,
1116 errors::DocPrimitive,
1122 let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
1123 if i_meta.has_name(sym::spotlight) {
1124 self.tcx.emit_spanned_lint(
1125 INVALID_DOC_ATTRIBUTES,
1128 errors::DocTestUnknownSpotlight {
1133 } else if i_meta.has_name(sym::include) &&
1134 let Some(value) = i_meta.value_str() {
1135 let applicability = if list.len() == 1 {
1136 Applicability::MachineApplicable
1138 Applicability::MaybeIncorrect
1140 // If there are multiple attributes, the suggestion would suggest
1141 // deleting all of them, which is incorrect.
1142 self.tcx.emit_spanned_lint(
1143 INVALID_DOC_ATTRIBUTES,
1146 errors::DocTestUnknownInclude {
1148 value: value.to_string(),
1149 inner: match attr.style { AttrStyle::Inner=> "!" , AttrStyle::Outer => "" },
1150 sugg: (attr.meta().unwrap().span, applicability),
1154 self.tcx.emit_spanned_lint(
1155 INVALID_DOC_ATTRIBUTES,
1158 errors::DocTestUnknownAny { path }
1165 self.tcx.emit_spanned_lint(
1166 INVALID_DOC_ATTRIBUTES,
1179 /// Warns against some misuses of `#[pass_by_value]`
1180 fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1182 Target::Struct | Target::Enum | Target::TyAlias => true,
1184 self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
1190 fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1192 Target::Method(MethodKind::Inherent) => true,
1194 self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
1200 fn check_has_incoherent_inherent_impls(
1207 Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
1213 .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
1219 fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) -> bool {
1220 if target != Target::ForeignFn {
1221 self.tcx.sess.emit_err(errors::FfiPureInvalidTarget { attr_span });
1224 if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
1225 // `#[ffi_const]` functions cannot be `#[ffi_pure]`
1226 self.tcx.sess.emit_err(errors::BothFfiConstAndPure { attr_span });
1233 fn check_ffi_const(&self, attr_span: Span, target: Target) -> bool {
1234 if target == Target::ForeignFn {
1237 self.tcx.sess.emit_err(errors::FfiConstInvalidTarget { attr_span });
1242 fn check_ffi_returns_twice(&self, attr_span: Span, target: Target) -> bool {
1243 if target == Target::ForeignFn {
1246 self.tcx.sess.emit_err(errors::FfiReturnsTwiceInvalidTarget { attr_span });
1251 /// Warns against some misuses of `#[must_use]`
1252 fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool {
1261 // `impl Trait` in return position can trip
1262 // `unused_must_use` if `Trait` is marked as
1266 let article = match target {
1271 | Target::Expression
1273 | Target::AssocConst
1274 | Target::AssocTy => "an",
1278 self.tcx.emit_spanned_lint(
1282 errors::MustUseNoEffect { article, target },
1286 // For now, its always valid
1290 /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
1291 fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1293 Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
1295 self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
1301 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
1302 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1304 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1305 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1306 // `#[cold]` attribute with just a lint, because we previously
1307 // erroneously allowed it and some crates used it accidentally, to be compatible
1308 // with crates depending on them, we can't throw an error here.
1309 Target::Field | Target::Arm | Target::MacroDef => {
1310 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1313 // FIXME: #[cold] was previously allowed on non-functions and some crates used
1314 // this, so only emit a warning.
1315 self.tcx.emit_spanned_lint(
1319 errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
1325 /// Checks if `#[link]` is applied to an item other than a foreign module.
1326 fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1327 if target == Target::ForeignMod
1328 && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
1329 && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1330 && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
1335 self.tcx.emit_spanned_lint(
1339 errors::Link { span: (target != Target::ForeignMod).then_some(span) },
1343 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1344 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1346 Target::ForeignFn | Target::ForeignStatic => {}
1347 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1348 // `#[link_name]` attribute with just a lint, because we previously
1349 // erroneously allowed it and some crates used it accidentally, to be compatible
1350 // with crates depending on them, we can't throw an error here.
1351 Target::Field | Target::Arm | Target::MacroDef => {
1352 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1355 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1356 // used this, so only emit a warning.
1357 let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span);
1358 if let Some(s) = attr.value_str() {
1359 self.tcx.emit_spanned_lint(
1363 errors::LinkName { span, attr_span, value: s.as_str() },
1366 self.tcx.emit_spanned_lint(
1370 errors::LinkName { span, attr_span, value: "..." },
1377 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
1378 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1380 Target::ExternCrate => true,
1381 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1382 // `#[no_link]` attribute with just a lint, because we previously
1383 // erroneously allowed it and some crates used it accidentally, to be compatible
1384 // with crates depending on them, we can't throw an error here.
1385 Target::Field | Target::Arm | Target::MacroDef => {
1386 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1390 self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span });
1396 fn is_impl_item(&self, hir_id: HirId) -> bool {
1397 matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
1400 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
1401 fn check_export_name(
1409 Target::Static | Target::Fn => true,
1410 Target::Method(..) if self.is_impl_item(hir_id) => true,
1411 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1412 // `#[export_name]` attribute with just a lint, because we previously
1413 // erroneously allowed it and some crates used it accidentally, to be compatible
1414 // with crates depending on them, we can't throw an error here.
1415 Target::Field | Target::Arm | Target::MacroDef => {
1416 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1420 self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span });
1426 fn check_rustc_layout_scalar_valid_range(
1432 if target != Target::Struct {
1433 self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct {
1434 attr_span: attr.span,
1440 let Some(list) = attr.meta_item_list() else {
1444 if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) {
1447 self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span });
1452 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1453 fn check_rustc_legacy_const_generics(
1459 item: Option<ItemLike<'_>>,
1461 let is_function = matches!(target, Target::Fn);
1463 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1464 attr_span: attr.span,
1466 on_crate: hir_id == CRATE_HIR_ID,
1471 let Some(list) = attr.meta_item_list() else {
1472 // The attribute form is validated on AST.
1476 let Some(ItemLike::Item(Item {
1477 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1480 bug!("should be a function item");
1483 for param in generics.params {
1485 hir::GenericParamKind::Const { .. } => {}
1487 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly {
1488 attr_span: attr.span,
1489 param_span: param.span,
1496 if list.len() != generics.params.len() {
1497 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex {
1498 attr_span: attr.span,
1499 generics_span: generics.span,
1504 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1505 let mut invalid_args = vec![];
1507 if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
1508 if *val >= arg_count {
1509 let span = meta.span();
1510 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed {
1512 arg_count: arg_count as usize,
1517 invalid_args.push(meta.span());
1521 if !invalid_args.is_empty() {
1522 self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
1529 /// Helper function for checking that the provided attribute is only applied to a function or
1531 fn check_applied_to_fn_or_method(
1538 let is_function = matches!(target, Target::Fn | Target::Method(..));
1540 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1541 attr_span: attr.span,
1543 on_crate: hir_id == CRATE_HIR_ID,
1551 /// Checks that the `#[rustc_lint_query_instability]` attribute is only applied to a function
1553 fn check_rustc_lint_query_instability(
1560 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
1563 /// Checks that the `#[rustc_lint_diagnostics]` attribute is only applied to a function or
1565 fn check_rustc_lint_diagnostics(
1572 self.check_applied_to_fn_or_method(hir_id, attr, span, target)
1575 /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
1576 fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1578 Target::Struct => true,
1580 self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
1586 /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
1587 fn check_rustc_lint_opt_deny_field_access(
1594 Target::Field => true,
1598 .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
1604 /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1605 /// option is passed to the compiler.
1606 fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1607 if self.tcx.sess.opts.unstable_opts.query_dep_graph {
1610 self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span });
1615 /// Checks if `#[link_section]` is applied to a function or static.
1616 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1618 Target::Static | Target::Fn | Target::Method(..) => {}
1619 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1620 // `#[link_section]` attribute with just a lint, because we previously
1621 // erroneously allowed it and some crates used it accidentally, to be compatible
1622 // with crates depending on them, we can't throw an error here.
1623 Target::Field | Target::Arm | Target::MacroDef => {
1624 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1627 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1628 // crates used this, so only emit a warning.
1629 self.tcx.emit_spanned_lint(
1633 errors::LinkSection { span },
1639 /// Checks if `#[no_mangle]` is applied to a function or static.
1640 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1642 Target::Static | Target::Fn => {}
1643 Target::Method(..) if self.is_impl_item(hir_id) => {}
1644 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1645 // `#[no_mangle]` attribute with just a lint, because we previously
1646 // erroneously allowed it and some crates used it accidentally, to be compatible
1647 // with crates depending on them, we can't throw an error here.
1648 Target::Field | Target::Arm | Target::MacroDef => {
1649 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1651 // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
1652 // The error should specify that the item that is wrong is specifically a *foreign* fn/static
1653 // otherwise the error seems odd
1654 Target::ForeignFn | Target::ForeignStatic => {
1655 let foreign_item_kind = match target {
1656 Target::ForeignFn => "function",
1657 Target::ForeignStatic => "static",
1658 _ => unreachable!(),
1660 self.tcx.emit_spanned_lint(
1664 errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind },
1668 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1669 // crates used this, so only emit a warning.
1670 self.tcx.emit_spanned_lint(
1674 errors::NoMangle { span },
1680 /// Checks if the `#[repr]` attributes on `item` are valid.
1683 attrs: &[Attribute],
1686 item: Option<ItemLike<'_>>,
1689 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1692 // #[repr(bar, align(8))]
1694 let hints: Vec<_> = attrs
1696 .filter(|attr| attr.has_name(sym::repr))
1697 .filter_map(|attr| attr.meta_item_list())
1701 let mut int_reprs = 0;
1702 let mut is_c = false;
1703 let mut is_simd = false;
1704 let mut is_transparent = false;
1706 for hint in &hints {
1707 if !hint.is_meta_item() {
1708 self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() });
1712 match hint.name_or_empty() {
1716 Target::Struct | Target::Union | Target::Enum => continue,
1718 self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
1719 hint_span: hint.span(),
1726 if let (Target::Fn, false) = (target, self.tcx.features().fn_align) {
1728 &self.tcx.sess.parse_sess,
1731 "`repr(align)` attributes on functions are unstable",
1737 Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1739 self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion {
1740 hint_span: hint.span(),
1747 if target != Target::Struct && target != Target::Union {
1748 self.tcx.sess.emit_err(AttrApplication::StructUnion {
1749 hint_span: hint.span(),
1758 if target != Target::Struct {
1761 .emit_err(AttrApplication::Struct { hint_span: hint.span(), span });
1766 sym::transparent => {
1767 is_transparent = true;
1769 Target::Struct | Target::Union | Target::Enum => continue,
1771 self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
1772 hint_span: hint.span(),
1791 if target != Target::Enum {
1794 .emit_err(AttrApplication::Enum { hint_span: hint.span(), span });
1800 self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() });
1806 // Just point at all repr hints if there are any incompatibilities.
1807 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1808 let hint_spans = hints.iter().map(|hint| hint.span());
1810 // Error on repr(transparent, <anything else>).
1811 if is_transparent && hints.len() > 1 {
1812 let hint_spans: Vec<_> = hint_spans.clone().collect();
1815 .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() });
1817 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1819 || (is_simd && is_c)
1822 && item.map_or(false, |item| {
1823 if let ItemLike::Item(item) = item {
1824 return is_c_like_enum(item);
1829 self.tcx.emit_spanned_lint(
1830 CONFLICTING_REPR_HINTS,
1832 hint_spans.collect::<Vec<Span>>(),
1833 errors::ReprConflicting,
1838 fn check_used(&self, attrs: &[Attribute], target: Target) {
1839 let mut used_linker_span = None;
1840 let mut used_compiler_span = None;
1841 for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
1842 if target != Target::Static {
1843 self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span });
1845 let inner = attr.meta_item_list();
1846 match inner.as_deref() {
1847 Some([item]) if item.has_name(sym::linker) => {
1848 if used_linker_span.is_none() {
1849 used_linker_span = Some(attr.span);
1852 Some([item]) if item.has_name(sym::compiler) => {
1853 if used_compiler_span.is_none() {
1854 used_compiler_span = Some(attr.span);
1858 // This error case is handled in rustc_hir_analysis::collect.
1861 // Default case (compiler) when arg isn't defined.
1862 if used_compiler_span.is_none() {
1863 used_compiler_span = Some(attr.span);
1868 if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
1871 .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
1875 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1876 /// (Allows proc_macro functions)
1877 fn check_allow_internal_unstable(
1883 attrs: &[Attribute],
1885 debug!("Checking target: {:?}", target);
1889 if self.tcx.sess.is_proc_macro_attr(attr) {
1890 debug!("Is proc macro attr");
1894 debug!("Is not proc macro attr");
1897 Target::MacroDef => true,
1898 // FIXME(#80564): We permit struct fields and match arms to have an
1899 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1900 // erroneously allowed it and some crates used it accidentally, to be compatible
1901 // with crates depending on them, we can't throw an error here.
1902 Target::Field | Target::Arm => {
1903 self.inline_attr_str_error_without_macro_def(
1906 "allow_internal_unstable",
1913 .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span });
1919 /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
1920 fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
1924 self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span });
1929 let Some(hints) = attr.meta_item_list() else {
1930 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1934 let hint = match hints.len() {
1937 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1942 let Some(meta_item) = hint.meta_item() else {
1943 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1947 let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
1948 (sym::natvis_file, Some(value)) => value,
1949 (sym::gdb_script_file, Some(value)) => value,
1951 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span });
1957 match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
1965 match std::fs::File::open(&file) {
1968 self.tcx.sess.emit_err(DebugVisualizerUnreadable {
1969 span: meta_item.span,
1978 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1979 /// (Allows proc_macro functions)
1980 fn check_rustc_allow_const_fn_unstable(
1988 Target::Fn | Target::Method(_)
1989 if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) =>
1993 // FIXME(#80564): We permit struct fields and match arms to have an
1994 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1995 // erroneously allowed it and some crates used it accidentally, to be compatible
1996 // with crates depending on them, we can't throw an error here.
1997 Target::Field | Target::Arm | Target::MacroDef => {
1998 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
2004 .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span });
2010 fn check_rustc_std_internal_symbol(
2017 Target::Fn | Target::Static => true,
2021 .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span });
2027 /// `#[const_trait]` only applies to traits.
2028 fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
2030 Target::Trait => true,
2032 self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span });
2038 fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
2040 Target::Expression => {
2041 self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span });
2048 fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
2050 Target::ForeignFn | Target::ForeignStatic => true,
2052 self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span });
2058 fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
2060 Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
2061 self.tcx.emit_spanned_lint(
2072 fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2073 let name = attr.name_or_empty();
2075 Target::ExternCrate | Target::Mod => {}
2077 self.tcx.emit_spanned_lint(
2081 errors::MacroUse { name },
2087 fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2088 if target != Target::MacroDef {
2089 self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport);
2093 fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2094 if target != Target::Fn {
2095 self.tcx.emit_spanned_lint(
2099 errors::PluginRegistrar,
2104 fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
2105 // Warn on useless empty attributes.
2106 let note = if matches!(
2107 attr.name_or_empty(),
2116 | sym::target_feature
2117 ) && attr.meta_item_list().map_or(false, |list| list.is_empty())
2119 errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
2121 attr.name_or_empty(),
2122 sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
2123 ) && let Some(meta) = attr.meta_item_list()
2125 && let Some(item) = meta[0].meta_item()
2126 && let MetaItemKind::NameValue(_) = &item.kind
2127 && item.path == sym::reason
2129 errors::UnusedNote::NoLints { name: attr.name_or_empty() }
2130 } else if attr.name_or_empty() == sym::default_method_body_is_const {
2131 errors::UnusedNote::DefaultMethodBodyConst
2136 self.tcx.emit_spanned_lint(
2140 errors::Unused { attr_span: attr.span, note },
2144 /// A best effort attempt to create an error for a mismatching proc macro signature.
2146 /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
2147 fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2148 let expected_input_count = match kind {
2149 ProcMacroKind::Attribute => 2,
2150 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2153 let expected_signature = match kind {
2154 ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
2155 ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
2159 if target == Target::Fn {
2160 let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
2161 let tokenstream = tcx.type_of(tokenstream);
2163 let id = hir_id.expect_owner();
2164 let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
2167 tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id).subst_identity());
2168 let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
2170 // We don't currently require that the function signature is equal to
2171 // `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
2172 // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
2174 // Properly checking this means pulling in additional `rustc` crates, so we don't.
2175 let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
2177 if sig.abi != Abi::Rust {
2178 tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
2179 self.abort.set(true);
2182 if sig.unsafety == Unsafety::Unsafe {
2183 tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
2184 self.abort.set(true);
2187 let output = sig.output();
2189 // Typecheck the output
2190 if !drcx.types_may_unify(output, tokenstream) {
2191 tcx.sess.emit_err(ProcMacroTypeError {
2192 span: hir_sig.decl.output.span(),
2197 self.abort.set(true);
2200 if sig.inputs().len() < expected_input_count {
2201 tcx.sess.emit_err(ProcMacroMissingArguments {
2202 expected_input_count,
2207 self.abort.set(true);
2210 // Check that the inputs are correct, if there are enough.
2211 if sig.inputs().len() >= expected_input_count {
2213 sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
2215 if !drcx.types_may_unify(*arg, tokenstream) {
2216 tcx.sess.emit_err(ProcMacroTypeError {
2222 self.abort.set(true);
2227 // Check that there are not too many arguments
2228 let body_id = tcx.hir().body_owned_by(id.def_id);
2229 let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
2230 if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
2231 tcx.sess.emit_err(ProcMacroDiffArguments {
2232 span: begin.span.to(end.span),
2233 count: excess.len(),
2237 self.abort.set(true);
2243 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2244 type NestedFilter = nested_filter::OnlyBodies;
2246 fn nested_visit_map(&mut self) -> Self::Map {
2250 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2251 // Historically we've run more checks on non-exported than exported macros,
2252 // so this lets us continue to run them while maintaining backwards compatibility.
2253 // In the long run, the checks should be harmonized.
2254 if let ItemKind::Macro(ref macro_def, _) = item.kind {
2255 let def_id = item.owner_id.to_def_id();
2256 if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
2257 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2261 let target = Target::from_item(item);
2262 self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2263 intravisit::walk_item(self, item)
2266 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2267 let target = Target::from_generic_param(generic_param);
2268 self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2269 intravisit::walk_generic_param(self, generic_param)
2272 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2273 let target = Target::from_trait_item(trait_item);
2274 self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2275 intravisit::walk_trait_item(self, trait_item)
2278 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2279 self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2280 intravisit::walk_field_def(self, struct_field);
2283 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2284 self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2285 intravisit::walk_arm(self, arm);
2288 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2289 let target = Target::from_foreign_item(f_item);
2290 self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));
2291 intravisit::walk_foreign_item(self, f_item)
2294 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2295 let target = target_from_impl_item(self.tcx, impl_item);
2296 self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2297 intravisit::walk_impl_item(self, impl_item)
2300 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2301 // When checking statements ignore expressions, they will be checked later.
2302 if let hir::StmtKind::Local(ref l) = stmt.kind {
2303 self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2305 intravisit::walk_stmt(self, stmt)
2308 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2309 let target = match expr.kind {
2310 hir::ExprKind::Closure { .. } => Target::Closure,
2311 _ => Target::Expression,
2314 self.check_attributes(expr.hir_id, expr.span, target, None);
2315 intravisit::walk_expr(self, expr)
2318 fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
2319 self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
2320 intravisit::walk_expr_field(self, field)
2323 fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
2324 self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);
2325 intravisit::walk_variant(self, variant)
2328 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2329 self.check_attributes(param.hir_id, param.span, Target::Param, None);
2331 intravisit::walk_param(self, param);
2334 fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
2335 self.check_attributes(field.hir_id, field.span, Target::PatField, None);
2336 intravisit::walk_pat_field(self, field);
2340 fn is_c_like_enum(item: &Item<'_>) -> bool {
2341 if let ItemKind::Enum(ref def, _) = item.kind {
2342 for variant in def.variants {
2343 match variant.data {
2344 hir::VariantData::Unit(..) => { /* continue */ }
2354 // FIXME: Fix "Cannot determine resolution" error and remove built-in macros
2356 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2357 // Check for builtin attributes at the crate level
2358 // which were unsuccessfully resolved due to cannot determine
2359 // resolution for the attribute macro error.
2360 const ATTRS_TO_CHECK: &[Symbol] = &[
2364 sym::automatically_derived,
2371 sym::global_allocator,
2376 // This function should only be called with crate attributes
2377 // which are inner attributes always but lets check to make sure
2378 if attr.style == AttrStyle::Inner {
2379 for attr_to_check in ATTRS_TO_CHECK {
2380 if attr.has_name(*attr_to_check) {
2381 tcx.sess.emit_err(InvalidAttrAtCrateLevel {
2383 snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
2384 name: *attr_to_check,
2392 fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2393 let attrs = tcx.hir().attrs(item.hir_id());
2396 if attr.has_name(sym::inline) {
2397 tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span });
2402 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
2403 let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
2404 tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
2405 if module_def_id.is_top_level_module() {
2406 check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2407 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
2409 if check_attr_visitor.abort.get() {
2410 tcx.sess.abort_if_errors()
2414 pub(crate) fn provide(providers: &mut Providers) {
2415 *providers = Providers { check_mod_attrs, ..*providers };
2418 fn check_duplicates(
2422 duplicates: AttributeDuplicates,
2423 seen: &mut FxHashMap<Symbol, Span>,
2425 use AttributeDuplicates::*;
2426 if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2431 WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2432 match seen.entry(attr.name_or_empty()) {
2433 Entry::Occupied(mut entry) => {
2434 let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2435 let to_remove = entry.insert(attr.span);
2436 (to_remove, attr.span)
2438 (attr.span, *entry.get())
2440 tcx.emit_spanned_lint(
2444 errors::UnusedDuplicate {
2449 FutureWarnFollowing | FutureWarnPreceding
2455 Entry::Vacant(entry) => {
2456 entry.insert(attr.span);
2460 ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) {
2461 Entry::Occupied(mut entry) => {
2462 let (this, other) = if matches!(duplicates, ErrorPreceding) {
2463 let to_remove = entry.insert(attr.span);
2464 (to_remove, attr.span)
2466 (attr.span, *entry.get())
2468 tcx.sess.emit_err(errors::UnusedMultiple {
2471 name: attr.name_or_empty(),
2474 Entry::Vacant(entry) => {
2475 entry.insert(attr.span);