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
7 use rustc_middle::hir::map::Map;
8 use rustc_middle::ty::query::Providers;
9 use rustc_middle::ty::TyCtxt;
11 use rustc_ast::{Attribute, Lit, LitKind, NestedMetaItem};
12 use rustc_errors::{pluralize, struct_span_err, Applicability};
14 use rustc_hir::def_id::LocalDefId;
15 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
17 self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID,
19 use rustc_hir::{MethodKind, Target};
20 use rustc_session::lint::builtin::{
21 CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
23 use rustc_session::parse::feature_err;
24 use rustc_span::symbol::{sym, Symbol};
25 use rustc_span::{Span, DUMMY_SP};
27 pub(crate) fn target_from_impl_item<'tcx>(
29 impl_item: &hir::ImplItem<'_>,
31 match impl_item.kind {
32 hir::ImplItemKind::Const(..) => Target::AssocConst,
33 hir::ImplItemKind::Fn(..) => {
34 let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id());
35 let containing_item = tcx.hir().expect_item(parent_hir_id);
36 let containing_impl_is_for_trait = match &containing_item.kind {
37 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
38 _ => bug!("parent of an ImplItem must be an Impl"),
40 if containing_impl_is_for_trait {
41 Target::Method(MethodKind::Trait { body: true })
43 Target::Method(MethodKind::Inherent)
46 hir::ImplItemKind::TyAlias(..) => Target::AssocTy,
50 #[derive(Clone, Copy)]
52 Item(&'tcx Item<'tcx>),
53 ForeignItem(&'tcx ForeignItem<'tcx>),
56 struct CheckAttrVisitor<'tcx> {
60 impl CheckAttrVisitor<'tcx> {
61 /// Checks any attribute.
67 item: Option<ItemLike<'_>>,
69 let mut is_valid = true;
70 let attrs = self.tcx.hir().attrs(hir_id);
72 is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
73 self.check_inline(hir_id, attr, span, target)
74 } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
75 self.check_non_exhaustive(hir_id, attr, span, target)
76 } else if self.tcx.sess.check_name(attr, sym::marker) {
77 self.check_marker(hir_id, attr, span, target)
78 } else if self.tcx.sess.check_name(attr, sym::target_feature) {
79 self.check_target_feature(hir_id, attr, span, target)
80 } else if self.tcx.sess.check_name(attr, sym::track_caller) {
81 self.check_track_caller(hir_id, &attr.span, attrs, span, target)
82 } else if self.tcx.sess.check_name(attr, sym::doc) {
83 self.check_doc_attrs(attr, hir_id, target)
84 } else if self.tcx.sess.check_name(attr, sym::no_link) {
85 self.check_no_link(hir_id, &attr, span, target)
86 } else if self.tcx.sess.check_name(attr, sym::export_name) {
87 self.check_export_name(hir_id, &attr, span, target)
88 } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
89 self.check_rustc_args_required_const(&attr, span, target, item)
90 } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_start) {
91 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
92 } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_end) {
93 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
94 } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) {
95 self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
96 } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) {
97 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
98 } else if self.tcx.sess.check_name(attr, sym::naked) {
99 self.check_naked(hir_id, attr, span, target)
100 } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
101 self.check_rustc_legacy_const_generics(&attr, span, target, item)
102 } else if self.tcx.sess.check_name(attr, sym::rustc_clean)
103 || self.tcx.sess.check_name(attr, sym::rustc_dirty)
104 || self.tcx.sess.check_name(attr, sym::rustc_if_this_changed)
105 || self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need)
107 self.check_rustc_dirty_clean(&attr)
110 if self.tcx.sess.check_name(attr, sym::cold) {
111 self.check_cold(hir_id, attr, span, target);
112 } else if self.tcx.sess.check_name(attr, sym::link_name) {
113 self.check_link_name(hir_id, attr, span, target);
114 } else if self.tcx.sess.check_name(attr, sym::link_section) {
115 self.check_link_section(hir_id, attr, span, target);
116 } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
117 self.check_no_mangle(hir_id, attr, span, target);
127 if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
128 self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
131 self.check_repr(attrs, span, target, item, hir_id);
132 self.check_used(attrs, target);
135 fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
136 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
138 "`#[{}]` is ignored on struct fields, match arms and macro defs",
142 "this was previously accepted by the compiler but is \
143 being phased out; it will become a hard error in \
147 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
148 for more information",
154 fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
155 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
156 lint.build(&format!("`#[{}]` is ignored on struct fields and match arms", sym))
158 "this was previously accepted by the compiler but is \
159 being phased out; it will become a hard error in \
163 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
164 for more information",
170 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
171 fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
175 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
176 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
177 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
178 lint.build("`#[inline]` is ignored on function prototypes").emit()
182 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
183 // just a lint, because we previously erroneously allowed it and some crates used it
184 // accidentally, to to be compatible with crates depending on them, we can't throw an
186 Target::AssocConst => {
187 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
188 lint.build("`#[inline]` is ignored on constants")
190 "this was previously accepted by the compiler but is \
191 being phased out; it will become a hard error in \
195 "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
196 for more information",
202 // FIXME(#80564): Same for fields, arms, and macro defs
203 Target::Field | Target::Arm | Target::MacroDef => {
204 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
212 "attribute should be applied to function or closure",
214 .span_label(*span, "not a function or closure")
221 /// Checks if `#[naked]` is applied to a function definition.
222 fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
225 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
226 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
227 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
228 // erroneously allowed it and some crates used it accidentally, to to be compatible
229 // with crates depending on them, we can't throw an error here.
230 Target::Field | Target::Arm | Target::MacroDef => {
231 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
239 "attribute should be applied to a function definition",
241 .span_label(*span, "not a function definition")
248 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
249 fn check_track_caller(
253 attrs: &'hir [Attribute],
258 _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
263 "cannot use `#[track_caller]` with `#[naked]`",
268 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
269 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
270 // `#[track_caller]` attribute with just a lint, because we previously
271 // erroneously allowed it and some crates used it accidentally, to to be compatible
272 // with crates depending on them, we can't throw an error here.
273 Target::Field | Target::Arm | Target::MacroDef => {
275 self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
284 "attribute should be applied to function"
286 .span_label(*span, "not a function")
293 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
294 fn check_non_exhaustive(
302 Target::Struct | Target::Enum | Target::Variant => true,
303 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
304 // `#[non_exhaustive]` attribute with just a lint, because we previously
305 // erroneously allowed it and some crates used it accidentally, to to be compatible
306 // with crates depending on them, we can't throw an error here.
307 Target::Field | Target::Arm | Target::MacroDef => {
308 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
316 "attribute can only be applied to a struct or enum"
318 .span_label(*span, "not a struct or enum")
325 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
326 fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
328 Target::Trait => true,
329 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
330 // `#[marker]` attribute with just a lint, because we previously
331 // erroneously allowed it and some crates used it accidentally, to to be compatible
332 // with crates depending on them, we can't throw an error here.
333 Target::Field | Target::Arm | Target::MacroDef => {
334 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
340 .struct_span_err(attr.span, "attribute can only be applied to a trait")
341 .span_label(*span, "not a trait")
348 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
349 fn check_target_feature(
358 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
359 // FIXME: #[target_feature] was previously erroneously allowed on statements and some
360 // crates used this, so only emit a warning.
361 Target::Statement => {
362 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
363 lint.build("attribute should be applied to a function")
365 "this was previously accepted by the compiler but is \
366 being phased out; it will become a hard error in \
369 .span_label(*span, "not a function")
374 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
375 // `#[target_feature]` attribute with just a lint, because we previously
376 // erroneously allowed it and some crates used it accidentally, to to be compatible
377 // with crates depending on them, we can't throw an error here.
378 Target::Field | Target::Arm | Target::MacroDef => {
379 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
385 .struct_span_err(attr.span, "attribute should be applied to a function")
386 .span_label(*span, "not a function")
393 fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
398 &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
403 fn check_doc_alias_value(
405 meta: &NestedMetaItem,
412 let err_fn = move |span: Span, msg: &str| {
416 "`#[doc(alias{})]` {}",
417 if is_list { "(\"...\")" } else { " = \"...\"" },
423 if doc_alias.is_empty() {
425 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
426 "attribute cannot have empty value",
430 doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
432 self.tcx.sess.span_err(
433 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
435 "{:?} character isn't allowed in `#[doc(alias{})]`",
437 if is_list { "(\"...\")" } else { " = \"...\"" },
442 if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
444 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
445 "cannot start or end with ' '",
448 if let Some(err) = match target {
449 Target::Impl => Some("implementation block"),
450 Target::ForeignMod => Some("extern block"),
452 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
453 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
454 if Target::from_item(containing_item) == Target::Impl {
455 Some("type alias in implementation block")
460 Target::AssocConst => {
461 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
462 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
463 // We can't link to trait impl's consts.
464 let err = "associated constant in trait implementation block";
465 match containing_item.kind {
466 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
472 return err_fn(meta.span(), &format!("isn't allowed on {}", err));
474 let item_name = self.tcx.hir().name(hir_id);
475 if &*item_name.as_str() == doc_alias {
476 return err_fn(meta.span(), "is the same as the item's name");
481 fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
482 if let Some(values) = meta.meta_item_list() {
486 Some(l) => match l.kind {
487 LitKind::Str(s, _) => {
488 if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
497 "`#[doc(alias(\"a\"))]` expects string literals",
508 "`#[doc(alias(\"a\"))]` expects string literals",
516 } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
517 self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
523 "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
524 strings `#[doc(alias(\"a\", \"b\"))]`",
531 fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
532 let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
533 if doc_keyword.is_empty() {
534 self.doc_attr_str_error(meta, "keyword");
537 match self.tcx.hir().expect_item(hir_id).kind {
538 ItemKind::Mod(ref module) => {
539 if !module.item_ids.is_empty() {
544 "`#[doc(keyword = \"...\")]` can only be used on empty modules",
555 "`#[doc(keyword = \"...\")]` can only be used on modules",
561 if !rustc_lexer::is_ident(&doc_keyword) {
565 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
566 &format!("`{}` is not a valid identifier", doc_keyword),
574 fn check_attr_crate_level(
576 meta: &NestedMetaItem,
580 if CRATE_HIR_ID == hir_id {
586 "`#![doc({} = \"...\")]` isn't allowed as a crate-level attribute",
596 fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
597 let mut is_valid = true;
599 if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
601 if let Some(i_meta) = meta.meta_item() {
602 match i_meta.name_or_empty() {
604 if !self.check_attr_crate_level(&meta, hir_id, "alias")
605 || !self.check_doc_alias(&meta, hir_id, target) =>
611 if !self.check_attr_crate_level(&meta, hir_id, "keyword")
612 || !self.check_doc_keyword(&meta, hir_id) =>
617 sym::test if CRATE_HIR_ID != hir_id => {
618 self.tcx.struct_span_lint_hir(
619 INVALID_DOC_ATTRIBUTES,
624 "`#![doc(test(...)]` is only allowed \
625 as a crate-level attribute",
633 // no_default_passes: deprecated
634 // passes: deprecated
635 // plugins: removed, but rustdoc warns about it itself
639 | sym::html_favicon_url
641 | sym::html_no_source
642 | sym::html_playground_url
646 | sym::issue_tracker_base_url
649 | sym::no_default_passes
658 self.tcx.struct_span_lint_hir(
659 INVALID_DOC_ATTRIBUTES,
663 let mut diag = lint.build(&format!(
664 "unknown `doc` attribute `{}`",
665 rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
667 if i_meta.has_name(sym::spotlight) {
669 "`doc(spotlight)` was renamed to `doc(notable_trait)`",
671 diag.span_suggestion_short(
673 "use `notable_trait` instead",
674 String::from("notable_trait"),
675 Applicability::MachineApplicable,
677 diag.note("`doc(spotlight)` is now a no-op");
686 self.tcx.struct_span_lint_hir(
687 INVALID_DOC_ATTRIBUTES,
691 lint.build(&format!("invalid `doc` attribute")).emit();
702 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
703 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
705 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
706 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
707 // `#[cold]` attribute with just a lint, because we previously
708 // erroneously allowed it and some crates used it accidentally, to to be compatible
709 // with crates depending on them, we can't throw an error here.
710 Target::Field | Target::Arm | Target::MacroDef => {
711 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
714 // FIXME: #[cold] was previously allowed on non-functions and some crates used
715 // this, so only emit a warning.
716 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
717 lint.build("attribute should be applied to a function")
719 "this was previously accepted by the compiler but is \
720 being phased out; it will become a hard error in \
723 .span_label(*span, "not a function")
730 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
731 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
733 Target::ForeignFn | Target::ForeignStatic => {}
734 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
735 // `#[link_name]` attribute with just a lint, because we previously
736 // erroneously allowed it and some crates used it accidentally, to to be compatible
737 // with crates depending on them, we can't throw an error here.
738 Target::Field | Target::Arm | Target::MacroDef => {
739 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
742 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
743 // used this, so only emit a warning.
744 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
746 lint.build("attribute should be applied to a foreign function or static");
748 "this was previously accepted by the compiler but is \
749 being phased out; it will become a hard error in \
754 if let Target::ForeignMod = target {
755 if let Some(value) = attr.value_str() {
758 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
761 diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
765 diag.span_label(*span, "not a foreign function or static");
772 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
773 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
775 Target::ExternCrate => true,
776 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
777 // `#[no_link]` attribute with just a lint, because we previously
778 // erroneously allowed it and some crates used it accidentally, to to be compatible
779 // with crates depending on them, we can't throw an error here.
780 Target::Field | Target::Arm | Target::MacroDef => {
781 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
789 "attribute should be applied to an `extern crate` item",
791 .span_label(*span, "not an `extern crate` item")
798 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
799 fn check_export_name(
807 Target::Static | Target::Fn | Target::Method(..) => true,
808 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
809 // `#[export_name]` attribute with just a lint, because we previously
810 // erroneously allowed it and some crates used it accidentally, to to be compatible
811 // with crates depending on them, we can't throw an error here.
812 Target::Field | Target::Arm | Target::MacroDef => {
813 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
821 "attribute should be applied to a function or static",
823 .span_label(*span, "not a function or static")
830 /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
831 fn check_rustc_args_required_const(
836 item: Option<ItemLike<'_>>,
838 let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
842 .struct_span_err(attr.span, "attribute should be applied to a function")
843 .span_label(*span, "not a function")
848 let list = match attr.meta_item_list() {
849 // The attribute form is validated on AST.
850 None => return false,
854 let mut invalid_args = vec![];
856 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
857 if let Some(ItemLike::Item(Item {
858 kind: ItemKind::Fn(FnSig { decl, .. }, ..),
861 | Some(ItemLike::ForeignItem(ForeignItem {
862 kind: ForeignItemKind::Fn(decl, ..),
866 let arg_count = decl.inputs.len() as u128;
867 if *val >= arg_count {
868 let span = meta.span();
871 .struct_span_err(span, "index exceeds number of arguments")
875 "there {} only {} argument{}",
876 if arg_count != 1 { "are" } else { "is" },
878 pluralize!(arg_count)
885 bug!("should be a function item");
888 invalid_args.push(meta.span());
892 if !invalid_args.is_empty() {
895 .struct_span_err(invalid_args, "arguments should be non-negative integers")
903 fn check_rustc_layout_scalar_valid_range(
909 if target != Target::Struct {
912 .struct_span_err(attr.span, "attribute should be applied to a struct")
913 .span_label(*span, "not a struct")
918 let list = match attr.meta_item_list() {
919 None => return false,
923 if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
928 .struct_span_err(attr.span, "expected exactly one integer literal argument")
934 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
935 fn check_rustc_legacy_const_generics(
940 item: Option<ItemLike<'_>>,
942 let is_function = matches!(target, Target::Fn | Target::Method(..));
946 .struct_span_err(attr.span, "attribute should be applied to a function")
947 .span_label(*span, "not a function")
952 let list = match attr.meta_item_list() {
953 // The attribute form is validated on AST.
954 None => return false,
958 let (decl, generics) = match item {
959 Some(ItemLike::Item(Item {
960 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
962 })) => (decl, generics),
963 _ => bug!("should be a function item"),
966 for param in generics.params {
968 hir::GenericParamKind::Const { .. } => {}
974 "#[rustc_legacy_const_generics] functions must \
975 only have const generics",
977 .span_label(param.span, "non-const generic parameter")
984 if list.len() != generics.params.len() {
989 "#[rustc_legacy_const_generics] must have one index for each generic parameter",
991 .span_label(generics.span, "generic parameters")
996 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
997 let mut invalid_args = vec![];
999 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1000 if *val >= arg_count {
1001 let span = meta.span();
1004 .struct_span_err(span, "index exceeds number of arguments")
1008 "there {} only {} argument{}",
1009 if arg_count != 1 { "are" } else { "is" },
1011 pluralize!(arg_count)
1018 invalid_args.push(meta.span());
1022 if !invalid_args.is_empty() {
1025 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1033 /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1034 /// option is passed to the compiler.
1035 fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1036 if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1041 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1047 /// Checks if `#[link_section]` is applied to a function or static.
1048 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1050 Target::Static | Target::Fn | Target::Method(..) => {}
1051 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1052 // `#[link_section]` attribute with just a lint, because we previously
1053 // erroneously allowed it and some crates used it accidentally, to to be compatible
1054 // with crates depending on them, we can't throw an error here.
1055 Target::Field | Target::Arm | Target::MacroDef => {
1056 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1059 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1060 // crates used this, so only emit a warning.
1061 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1062 lint.build("attribute should be applied to a function or static")
1064 "this was previously accepted by the compiler but is \
1065 being phased out; it will become a hard error in \
1068 .span_label(*span, "not a function or static")
1075 /// Checks if `#[no_mangle]` is applied to a function or static.
1076 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1078 Target::Static | Target::Fn | Target::Method(..) => {}
1079 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1080 // `#[no_mangle]` attribute with just a lint, because we previously
1081 // erroneously allowed it and some crates used it accidentally, to to be compatible
1082 // with crates depending on them, we can't throw an error here.
1083 Target::Field | Target::Arm | Target::MacroDef => {
1084 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1087 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1088 // crates used this, so only emit a warning.
1089 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1090 lint.build("attribute should be applied to a function or static")
1092 "this was previously accepted by the compiler but is \
1093 being phased out; it will become a hard error in \
1096 .span_label(*span, "not a function or static")
1103 /// Checks if the `#[repr]` attributes on `item` are valid.
1106 attrs: &'hir [Attribute],
1109 item: Option<ItemLike<'_>>,
1112 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1115 // #[repr(bar, align(8))]
1117 let hints: Vec<_> = attrs
1119 .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
1120 .filter_map(|attr| attr.meta_item_list())
1124 let mut int_reprs = 0;
1125 let mut is_c = false;
1126 let mut is_simd = false;
1127 let mut is_transparent = false;
1129 for hint in &hints {
1130 if !hint.is_meta_item() {
1135 "meta item in `repr` must be an identifier"
1141 let (article, allowed_targets) = match hint.name_or_empty() {
1145 Target::Struct | Target::Union | Target::Enum => continue,
1146 _ => ("a", "struct, enum, or union"),
1150 if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) {
1152 &self.tcx.sess.parse_sess,
1155 "`repr(align)` attributes on functions are unstable",
1161 Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1162 _ => ("a", "struct, enum, function, or union"),
1166 if target != Target::Struct && target != Target::Union {
1167 ("a", "struct or union")
1174 if target != Target::Struct {
1180 sym::transparent => {
1181 is_transparent = true;
1183 Target::Struct | Target::Union | Target::Enum => continue,
1184 _ => ("a", "struct, enum, or union"),
1188 if !self.tcx.features().enabled(sym::no_niche) {
1190 &self.tcx.sess.parse_sess,
1193 "the attribute `repr(no_niche)` is currently unstable",
1198 Target::Struct | Target::Enum => continue,
1199 _ => ("a", "struct or enum"),
1215 if target != Target::Enum {
1226 "unrecognized representation hint"
1239 &format!("attribute should be applied to {} {}", article, allowed_targets)
1241 .span_label(*span, &format!("not {} {}", article, allowed_targets))
1245 // Just point at all repr hints if there are any incompatibilities.
1246 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1247 let hint_spans = hints.iter().map(|hint| hint.span());
1249 // Error on repr(transparent, <anything else apart from no_niche>).
1250 let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1251 let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1252 if is_transparent && non_no_niche_count > 1 {
1253 let hint_spans: Vec<_> = hint_spans.clone().collect();
1258 "transparent {} cannot have other repr hints",
1263 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1265 || (is_simd && is_c)
1268 && item.map_or(false, |item| {
1269 if let ItemLike::Item(item) = item {
1270 return is_c_like_enum(item);
1275 self.tcx.struct_span_lint_hir(
1276 CONFLICTING_REPR_HINTS,
1278 hint_spans.collect::<Vec<Span>>(),
1280 lint.build("conflicting representation hints")
1281 .code(rustc_errors::error_code!(E0566))
1288 fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1290 if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
1293 .span_err(attr.span, "attribute must be applied to a `static` variable");
1298 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1299 /// (Allows proc_macro functions)
1300 fn check_allow_internal_unstable(
1306 attrs: &[Attribute],
1308 debug!("Checking target: {:?}", target);
1312 if self.tcx.sess.is_proc_macro_attr(attr) {
1313 debug!("Is proc macro attr");
1317 debug!("Is not proc macro attr");
1320 Target::MacroDef => true,
1321 // FIXME(#80564): We permit struct fields and match arms to have an
1322 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1323 // erroneously allowed it and some crates used it accidentally, to to be compatible
1324 // with crates depending on them, we can't throw an error here.
1325 Target::Field | Target::Arm => {
1326 self.inline_attr_str_error_without_macro_def(
1329 "allow_internal_unstable",
1336 .struct_span_err(attr.span, "attribute should be applied to a macro")
1337 .span_label(*span, "not a macro")
1344 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1345 /// (Allows proc_macro functions)
1346 fn check_rustc_allow_const_fn_unstable(
1354 Target::Fn | Target::Method(_)
1355 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1359 // FIXME(#80564): We permit struct fields and match arms to have an
1360 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1361 // erroneously allowed it and some crates used it accidentally, to to be compatible
1362 // with crates depending on them, we can't throw an error here.
1363 Target::Field | Target::Arm | Target::MacroDef => {
1364 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1370 .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1371 .span_label(*span, "not a `const fn`")
1379 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
1380 type Map = Map<'tcx>;
1382 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1383 NestedVisitorMap::OnlyBodies(self.tcx.hir())
1386 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
1387 let target = Target::from_item(item);
1388 self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
1389 intravisit::walk_item(self, item)
1392 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1393 let target = Target::from_generic_param(generic_param);
1394 self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
1395 intravisit::walk_generic_param(self, generic_param)
1398 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
1399 let target = Target::from_trait_item(trait_item);
1400 self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
1401 intravisit::walk_trait_item(self, trait_item)
1404 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1405 self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1406 intravisit::walk_field_def(self, struct_field);
1409 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
1410 self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
1411 intravisit::walk_arm(self, arm);
1414 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
1415 let target = Target::from_foreign_item(f_item);
1416 self.check_attributes(
1420 Some(ItemLike::ForeignItem(f_item)),
1422 intravisit::walk_foreign_item(self, f_item)
1425 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
1426 let target = target_from_impl_item(self.tcx, impl_item);
1427 self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
1428 intravisit::walk_impl_item(self, impl_item)
1431 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
1432 // When checking statements ignore expressions, they will be checked later.
1433 if let hir::StmtKind::Local(ref l) = stmt.kind {
1434 self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
1436 intravisit::walk_stmt(self, stmt)
1439 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
1440 let target = match expr.kind {
1441 hir::ExprKind::Closure(..) => Target::Closure,
1442 _ => Target::Expression,
1445 self.check_attributes(expr.hir_id, &expr.span, target, None);
1446 intravisit::walk_expr(self, expr)
1451 variant: &'tcx hir::Variant<'tcx>,
1452 generics: &'tcx hir::Generics<'tcx>,
1455 self.check_attributes(variant.id, &variant.span, Target::Variant, None);
1456 intravisit::walk_variant(self, variant, generics, item_id)
1459 fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1460 self.check_attributes(macro_def.hir_id(), ¯o_def.span, Target::MacroDef, None);
1461 intravisit::walk_macro_def(self, macro_def);
1464 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1465 self.check_attributes(param.hir_id, ¶m.span, Target::Param, None);
1467 intravisit::walk_param(self, param);
1471 fn is_c_like_enum(item: &Item<'_>) -> bool {
1472 if let ItemKind::Enum(ref def, _) = item.kind {
1473 for variant in def.variants {
1474 match variant.data {
1475 hir::VariantData::Unit(..) => { /* continue */ }
1485 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1486 const ATTRS_TO_CHECK: &[Symbol] = &[
1490 sym::automatically_derived,
1496 for attr_to_check in ATTRS_TO_CHECK {
1497 if tcx.sess.check_name(attr, *attr_to_check) {
1502 "`{}` attribute cannot be used at crate level",
1503 attr_to_check.to_ident_string()
1512 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1514 if tcx.sess.check_name(attr, sym::inline) {
1519 "attribute should be applied to function or closure",
1521 .span_label(attr.span, "not a function or closure")
1527 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1528 let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1529 tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1530 tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1531 check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
1532 if module_def_id.is_top_level_module() {
1533 check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
1534 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1538 pub(crate) fn provide(providers: &mut Providers) {
1539 *providers = Providers { check_mod_attrs, ..*providers };