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};
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,
664 "unknown `doc` attribute `{}`",
665 rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
667 lint.build(&msg).emit();
674 self.tcx.struct_span_lint_hir(
675 INVALID_DOC_ATTRIBUTES,
679 lint.build(&format!("invalid `doc` attribute")).emit();
690 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
691 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
693 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
694 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
695 // `#[cold]` attribute with just a lint, because we previously
696 // erroneously allowed it and some crates used it accidentally, to to be compatible
697 // with crates depending on them, we can't throw an error here.
698 Target::Field | Target::Arm | Target::MacroDef => {
699 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
702 // FIXME: #[cold] was previously allowed on non-functions and some crates used
703 // this, so only emit a warning.
704 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
705 lint.build("attribute should be applied to a function")
707 "this was previously accepted by the compiler but is \
708 being phased out; it will become a hard error in \
711 .span_label(*span, "not a function")
718 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
719 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
721 Target::ForeignFn | Target::ForeignStatic => {}
722 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
723 // `#[link_name]` attribute with just a lint, because we previously
724 // erroneously allowed it and some crates used it accidentally, to to be compatible
725 // with crates depending on them, we can't throw an error here.
726 Target::Field | Target::Arm | Target::MacroDef => {
727 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
730 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
731 // used this, so only emit a warning.
732 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
734 lint.build("attribute should be applied to a foreign function or static");
736 "this was previously accepted by the compiler but is \
737 being phased out; it will become a hard error in \
742 if let Target::ForeignMod = target {
743 if let Some(value) = attr.value_str() {
746 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
749 diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
753 diag.span_label(*span, "not a foreign function or static");
760 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
761 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
763 Target::ExternCrate => true,
764 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
765 // `#[no_link]` attribute with just a lint, because we previously
766 // erroneously allowed it and some crates used it accidentally, to to be compatible
767 // with crates depending on them, we can't throw an error here.
768 Target::Field | Target::Arm | Target::MacroDef => {
769 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
777 "attribute should be applied to an `extern crate` item",
779 .span_label(*span, "not an `extern crate` item")
786 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
787 fn check_export_name(
795 Target::Static | Target::Fn | Target::Method(..) => true,
796 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
797 // `#[export_name]` attribute with just a lint, because we previously
798 // erroneously allowed it and some crates used it accidentally, to to be compatible
799 // with crates depending on them, we can't throw an error here.
800 Target::Field | Target::Arm | Target::MacroDef => {
801 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
809 "attribute should be applied to a function or static",
811 .span_label(*span, "not a function or static")
818 /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
819 fn check_rustc_args_required_const(
824 item: Option<ItemLike<'_>>,
826 let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
830 .struct_span_err(attr.span, "attribute should be applied to a function")
831 .span_label(*span, "not a function")
836 let list = match attr.meta_item_list() {
837 // The attribute form is validated on AST.
838 None => return false,
842 let mut invalid_args = vec![];
844 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
845 if let Some(ItemLike::Item(Item {
846 kind: ItemKind::Fn(FnSig { decl, .. }, ..),
849 | Some(ItemLike::ForeignItem(ForeignItem {
850 kind: ForeignItemKind::Fn(decl, ..),
854 let arg_count = decl.inputs.len() as u128;
855 if *val >= arg_count {
856 let span = meta.span();
859 .struct_span_err(span, "index exceeds number of arguments")
863 "there {} only {} argument{}",
864 if arg_count != 1 { "are" } else { "is" },
866 pluralize!(arg_count)
873 bug!("should be a function item");
876 invalid_args.push(meta.span());
880 if !invalid_args.is_empty() {
883 .struct_span_err(invalid_args, "arguments should be non-negative integers")
891 fn check_rustc_layout_scalar_valid_range(
897 if target != Target::Struct {
900 .struct_span_err(attr.span, "attribute should be applied to a struct")
901 .span_label(*span, "not a struct")
906 let list = match attr.meta_item_list() {
907 None => return false,
911 if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
916 .struct_span_err(attr.span, "expected exactly one integer literal argument")
922 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
923 fn check_rustc_legacy_const_generics(
928 item: Option<ItemLike<'_>>,
930 let is_function = matches!(target, Target::Fn | Target::Method(..));
934 .struct_span_err(attr.span, "attribute should be applied to a function")
935 .span_label(*span, "not a function")
940 let list = match attr.meta_item_list() {
941 // The attribute form is validated on AST.
942 None => return false,
946 let (decl, generics) = match item {
947 Some(ItemLike::Item(Item {
948 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
950 })) => (decl, generics),
951 _ => bug!("should be a function item"),
954 for param in generics.params {
956 hir::GenericParamKind::Const { .. } => {}
962 "#[rustc_legacy_const_generics] functions must \
963 only have const generics",
965 .span_label(param.span, "non-const generic parameter")
972 if list.len() != generics.params.len() {
977 "#[rustc_legacy_const_generics] must have one index for each generic parameter",
979 .span_label(generics.span, "generic parameters")
984 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
985 let mut invalid_args = vec![];
987 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
988 if *val >= arg_count {
989 let span = meta.span();
992 .struct_span_err(span, "index exceeds number of arguments")
996 "there {} only {} argument{}",
997 if arg_count != 1 { "are" } else { "is" },
999 pluralize!(arg_count)
1006 invalid_args.push(meta.span());
1010 if !invalid_args.is_empty() {
1013 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1021 /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1022 /// option is passed to the compiler.
1023 fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1024 if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1029 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1035 /// Checks if `#[link_section]` is applied to a function or static.
1036 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1038 Target::Static | Target::Fn | Target::Method(..) => {}
1039 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1040 // `#[link_section]` attribute with just a lint, because we previously
1041 // erroneously allowed it and some crates used it accidentally, to to be compatible
1042 // with crates depending on them, we can't throw an error here.
1043 Target::Field | Target::Arm | Target::MacroDef => {
1044 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1047 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1048 // crates used this, so only emit a warning.
1049 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1050 lint.build("attribute should be applied to a function or static")
1052 "this was previously accepted by the compiler but is \
1053 being phased out; it will become a hard error in \
1056 .span_label(*span, "not a function or static")
1063 /// Checks if `#[no_mangle]` is applied to a function or static.
1064 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1066 Target::Static | Target::Fn | Target::Method(..) => {}
1067 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1068 // `#[no_mangle]` attribute with just a lint, because we previously
1069 // erroneously allowed it and some crates used it accidentally, to to be compatible
1070 // with crates depending on them, we can't throw an error here.
1071 Target::Field | Target::Arm | Target::MacroDef => {
1072 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1075 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1076 // crates used this, so only emit a warning.
1077 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1078 lint.build("attribute should be applied to a function or static")
1080 "this was previously accepted by the compiler but is \
1081 being phased out; it will become a hard error in \
1084 .span_label(*span, "not a function or static")
1091 /// Checks if the `#[repr]` attributes on `item` are valid.
1094 attrs: &'hir [Attribute],
1097 item: Option<ItemLike<'_>>,
1100 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1103 // #[repr(bar, align(8))]
1105 let hints: Vec<_> = attrs
1107 .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
1108 .filter_map(|attr| attr.meta_item_list())
1112 let mut int_reprs = 0;
1113 let mut is_c = false;
1114 let mut is_simd = false;
1115 let mut is_transparent = false;
1117 for hint in &hints {
1118 let (article, allowed_targets) = match hint.name_or_empty() {
1119 _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => {
1120 ("a", "struct, enum, or union")
1122 name @ sym::C | name @ sym::align => {
1123 is_c |= name == sym::C;
1125 Target::Struct | Target::Union | Target::Enum => continue,
1126 _ => ("a", "struct, enum, or union"),
1130 if target != Target::Struct && target != Target::Union {
1131 ("a", "struct or union")
1138 if target != Target::Struct {
1144 sym::transparent => {
1145 is_transparent = true;
1147 Target::Struct | Target::Union | Target::Enum => continue,
1148 _ => ("a", "struct, enum, or union"),
1152 if !self.tcx.features().enabled(sym::no_niche) {
1154 &self.tcx.sess.parse_sess,
1157 "the attribute `repr(no_niche)` is currently unstable",
1162 Target::Struct | Target::Enum => continue,
1163 _ => ("a", "struct or enum"),
1179 if target != Target::Enum {
1193 &format!("attribute should be applied to {} {}", article, allowed_targets)
1195 .span_label(*span, &format!("not {} {}", article, allowed_targets))
1199 // Just point at all repr hints if there are any incompatibilities.
1200 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1201 let hint_spans = hints.iter().map(|hint| hint.span());
1203 // Error on repr(transparent, <anything else apart from no_niche>).
1204 let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1205 let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1206 if is_transparent && non_no_niche_count > 1 {
1207 let hint_spans: Vec<_> = hint_spans.clone().collect();
1212 "transparent {} cannot have other repr hints",
1217 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1219 || (is_simd && is_c)
1222 && item.map_or(false, |item| {
1223 if let ItemLike::Item(item) = item {
1224 return is_c_like_enum(item);
1229 self.tcx.struct_span_lint_hir(
1230 CONFLICTING_REPR_HINTS,
1232 hint_spans.collect::<Vec<Span>>(),
1234 lint.build("conflicting representation hints")
1235 .code(rustc_errors::error_code!(E0566))
1242 fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1244 if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
1247 .span_err(attr.span, "attribute must be applied to a `static` variable");
1252 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1253 /// (Allows proc_macro functions)
1254 fn check_allow_internal_unstable(
1260 attrs: &[Attribute],
1262 debug!("Checking target: {:?}", target);
1266 if self.tcx.sess.is_proc_macro_attr(attr) {
1267 debug!("Is proc macro attr");
1271 debug!("Is not proc macro attr");
1274 Target::MacroDef => true,
1275 // FIXME(#80564): We permit struct fields and match arms to have an
1276 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1277 // erroneously allowed it and some crates used it accidentally, to to be compatible
1278 // with crates depending on them, we can't throw an error here.
1279 Target::Field | Target::Arm => {
1280 self.inline_attr_str_error_without_macro_def(
1283 "allow_internal_unstable",
1290 .struct_span_err(attr.span, "attribute should be applied to a macro")
1291 .span_label(*span, "not a macro")
1298 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1299 /// (Allows proc_macro functions)
1300 fn check_rustc_allow_const_fn_unstable(
1308 Target::Fn | Target::Method(_)
1309 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1313 // FIXME(#80564): We permit struct fields and match arms to have an
1314 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1315 // erroneously allowed it and some crates used it accidentally, to to be compatible
1316 // with crates depending on them, we can't throw an error here.
1317 Target::Field | Target::Arm | Target::MacroDef => {
1318 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1324 .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1325 .span_label(*span, "not a `const fn`")
1333 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
1334 type Map = Map<'tcx>;
1336 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1337 NestedVisitorMap::OnlyBodies(self.tcx.hir())
1340 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
1341 let target = Target::from_item(item);
1342 self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
1343 intravisit::walk_item(self, item)
1346 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1347 let target = Target::from_generic_param(generic_param);
1348 self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
1349 intravisit::walk_generic_param(self, generic_param)
1352 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
1353 let target = Target::from_trait_item(trait_item);
1354 self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
1355 intravisit::walk_trait_item(self, trait_item)
1358 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1359 self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1360 intravisit::walk_field_def(self, struct_field);
1363 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
1364 self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
1365 intravisit::walk_arm(self, arm);
1368 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
1369 let target = Target::from_foreign_item(f_item);
1370 self.check_attributes(
1374 Some(ItemLike::ForeignItem(f_item)),
1376 intravisit::walk_foreign_item(self, f_item)
1379 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
1380 let target = target_from_impl_item(self.tcx, impl_item);
1381 self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
1382 intravisit::walk_impl_item(self, impl_item)
1385 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
1386 // When checking statements ignore expressions, they will be checked later.
1387 if let hir::StmtKind::Local(ref l) = stmt.kind {
1388 self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
1390 intravisit::walk_stmt(self, stmt)
1393 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
1394 let target = match expr.kind {
1395 hir::ExprKind::Closure(..) => Target::Closure,
1396 _ => Target::Expression,
1399 self.check_attributes(expr.hir_id, &expr.span, target, None);
1400 intravisit::walk_expr(self, expr)
1405 variant: &'tcx hir::Variant<'tcx>,
1406 generics: &'tcx hir::Generics<'tcx>,
1409 self.check_attributes(variant.id, &variant.span, Target::Variant, None);
1410 intravisit::walk_variant(self, variant, generics, item_id)
1413 fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1414 self.check_attributes(macro_def.hir_id(), ¯o_def.span, Target::MacroDef, None);
1415 intravisit::walk_macro_def(self, macro_def);
1418 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1419 self.check_attributes(param.hir_id, ¶m.span, Target::Param, None);
1421 intravisit::walk_param(self, param);
1425 fn is_c_like_enum(item: &Item<'_>) -> bool {
1426 if let ItemKind::Enum(ref def, _) = item.kind {
1427 for variant in def.variants {
1428 match variant.data {
1429 hir::VariantData::Unit(..) => { /* continue */ }
1439 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1440 const ATTRS_TO_CHECK: &[Symbol] = &[
1444 sym::automatically_derived,
1450 for attr_to_check in ATTRS_TO_CHECK {
1451 if tcx.sess.check_name(attr, *attr_to_check) {
1456 "`{}` attribute cannot be used at crate level",
1457 attr_to_check.to_ident_string()
1466 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1468 if tcx.sess.check_name(attr, sym::inline) {
1473 "attribute should be applied to function or closure",
1475 .span_label(attr.span, "not a function or closure")
1481 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1482 let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1483 tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1484 tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1485 check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
1486 if module_def_id.is_top_level_module() {
1487 check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
1488 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1492 pub(crate) fn provide(providers: &mut Providers) {
1493 *providers = Providers { check_mod_attrs, ..*providers };