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
)
104 if self.tcx
.sess
.check_name(attr
, sym
::cold
) {
105 self.check_cold(hir_id
, attr
, span
, target
);
106 } else if self.tcx
.sess
.check_name(attr
, sym
::link_name
) {
107 self.check_link_name(hir_id
, attr
, span
, target
);
108 } else if self.tcx
.sess
.check_name(attr
, sym
::link_section
) {
109 self.check_link_section(hir_id
, attr
, span
, target
);
110 } else if self.tcx
.sess
.check_name(attr
, sym
::no_mangle
) {
111 self.check_no_mangle(hir_id
, attr
, span
, target
);
121 if matches
!(target
, Target
::Closure
| Target
::Fn
| Target
::Method(_
) | Target
::ForeignFn
) {
122 self.tcx
.ensure().codegen_fn_attrs(self.tcx
.hir().local_def_id(hir_id
));
125 self.check_repr(attrs
, span
, target
, item
, hir_id
);
126 self.check_used(attrs
, target
);
129 fn inline_attr_str_error_with_macro_def(&self, hir_id
: HirId
, attr
: &Attribute
, sym
: &str) {
130 self.tcx
.struct_span_lint_hir(UNUSED_ATTRIBUTES
, hir_id
, attr
.span
, |lint
| {
132 "`#[{}]` is ignored on struct fields, match arms and macro defs",
136 "this was previously accepted by the compiler but is \
137 being phased out; it will become a hard error in \
141 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
142 for more information",
148 fn inline_attr_str_error_without_macro_def(&self, hir_id
: HirId
, attr
: &Attribute
, sym
: &str) {
149 self.tcx
.struct_span_lint_hir(UNUSED_ATTRIBUTES
, hir_id
, attr
.span
, |lint
| {
150 lint
.build(&format
!("`#[{}]` is ignored on struct fields and match arms", sym
))
152 "this was previously accepted by the compiler but is \
153 being phased out; it will become a hard error in \
157 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
158 for more information",
164 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
165 fn check_inline(&self, hir_id
: HirId
, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
169 | Target
::Method(MethodKind
::Trait { body: true }
| MethodKind
::Inherent
) => true,
170 Target
::Method(MethodKind
::Trait { body: false }
) | Target
::ForeignFn
=> {
171 self.tcx
.struct_span_lint_hir(UNUSED_ATTRIBUTES
, hir_id
, attr
.span
, |lint
| {
172 lint
.build("`#[inline]` is ignored on function prototypes").emit()
176 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
177 // just a lint, because we previously erroneously allowed it and some crates used it
178 // accidentally, to to be compatible with crates depending on them, we can't throw an
180 Target
::AssocConst
=> {
181 self.tcx
.struct_span_lint_hir(UNUSED_ATTRIBUTES
, hir_id
, attr
.span
, |lint
| {
182 lint
.build("`#[inline]` is ignored on constants")
184 "this was previously accepted by the compiler but is \
185 being phased out; it will become a hard error in \
189 "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
190 for more information",
196 // FIXME(#80564): Same for fields, arms, and macro defs
197 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
198 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "inline");
206 "attribute should be applied to function or closure",
208 .span_label(*span
, "not a function or closure")
215 /// Checks if `#[naked]` is applied to a function definition.
216 fn check_naked(&self, hir_id
: HirId
, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
219 | Target
::Method(MethodKind
::Trait { body: true }
| MethodKind
::Inherent
) => true,
220 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
221 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
222 // erroneously allowed it and some crates used it accidentally, to to be compatible
223 // with crates depending on them, we can't throw an error here.
224 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
225 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "naked");
233 "attribute should be applied to a function definition",
235 .span_label(*span
, "not a function definition")
242 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
243 fn check_track_caller(
247 attrs
: &'hir
[Attribute
],
252 _
if attrs
.iter().any(|attr
| attr
.has_name(sym
::naked
)) => {
257 "cannot use `#[track_caller]` with `#[naked]`",
262 Target
::Fn
| Target
::Method(..) | Target
::ForeignFn
| Target
::Closure
=> true,
263 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
264 // `#[track_caller]` attribute with just a lint, because we previously
265 // erroneously allowed it and some crates used it accidentally, to to be compatible
266 // with crates depending on them, we can't throw an error here.
267 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
269 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "track_caller");
278 "attribute should be applied to function"
280 .span_label(*span
, "not a function")
287 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
288 fn check_non_exhaustive(
296 Target
::Struct
| Target
::Enum
| Target
::Variant
=> true,
297 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
298 // `#[non_exhaustive]` attribute with just a lint, because we previously
299 // erroneously allowed it and some crates used it accidentally, to to be compatible
300 // with crates depending on them, we can't throw an error here.
301 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
302 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "non_exhaustive");
310 "attribute can only be applied to a struct or enum"
312 .span_label(*span
, "not a struct or enum")
319 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
320 fn check_marker(&self, hir_id
: HirId
, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
322 Target
::Trait
=> true,
323 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
324 // `#[marker]` attribute with just a lint, because we previously
325 // erroneously allowed it and some crates used it accidentally, to to be compatible
326 // with crates depending on them, we can't throw an error here.
327 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
328 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "marker");
334 .struct_span_err(attr
.span
, "attribute can only be applied to a trait")
335 .span_label(*span
, "not a trait")
342 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
343 fn check_target_feature(
352 | Target
::Method(MethodKind
::Trait { body: true }
| MethodKind
::Inherent
) => true,
353 // FIXME: #[target_feature] was previously erroneously allowed on statements and some
354 // crates used this, so only emit a warning.
355 Target
::Statement
=> {
356 self.tcx
.struct_span_lint_hir(UNUSED_ATTRIBUTES
, hir_id
, attr
.span
, |lint
| {
357 lint
.build("attribute should be applied to a function")
359 "this was previously accepted by the compiler but is \
360 being phased out; it will become a hard error in \
363 .span_label(*span
, "not a function")
368 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
369 // `#[target_feature]` attribute with just a lint, because we previously
370 // erroneously allowed it and some crates used it accidentally, to to be compatible
371 // with crates depending on them, we can't throw an error here.
372 Target
::Field
| Target
::Arm
| Target
::MacroDef
=> {
373 self.inline_attr_str_error_with_macro_def(hir_id
, attr
, "target_feature");
379 .struct_span_err(attr
.span
, "attribute should be applied to a function")
380 .span_label(*span
, "not a function")
387 fn doc_attr_str_error(&self, meta
: &NestedMetaItem
, attr_name
: &str) {
392 &format
!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name
),
397 fn check_doc_alias_value(
399 meta
: &NestedMetaItem
,
406 let err_fn
= move |span
: Span
, msg
: &str| {
410 "`#[doc(alias{})]` {}",
411 if is_list { "(\"...\")" }
else { " = \"...\"" }
,
417 if doc_alias
.is_empty() {
419 meta
.name_value_literal_span().unwrap_or_else(|| meta
.span()),
420 "attribute cannot have empty value",
424 doc_alias
.chars().find(|&c
| c
== '
"' || c == '\'' || (c.is_whitespace() && c != ' '))
426 self.tcx.sess.span_err(
427 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
429 "{:?} character isn't allowed
in `
#[doc(alias{})]`",
431 if is_list { "(\"...\")" } else { " = \"...\"" },
436 if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
438 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
439 "cannot start or end with ' '",
442 if let Some(err) = match target {
443 Target::Impl => Some("implementation block"),
444 Target::ForeignMod => Some("extern block"),
446 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
447 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
448 if Target::from_item(containing_item) == Target::Impl {
449 Some("type alias in implementation block")
454 Target::AssocConst => {
455 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
456 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
457 // We can't link to trait impl's consts.
458 let err = "associated constant in trait implementation block";
459 match containing_item.kind {
460 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
466 return err_fn(meta.span(), &format!("isn't allowed on {}", err));
468 let item_name = self.tcx.hir().name(hir_id);
469 if &*item_name.as_str() == doc_alias {
470 return err_fn(meta.span(), "is the same as the item's name");
475 fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
476 if let Some(values) = meta.meta_item_list() {
480 Some(l) => match l.kind {
481 LitKind::Str(s, _) => {
482 if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
491 "`#[doc(alias(\"a\"))]` expects string literals",
502 "`#[doc(alias(\"a\"))]` expects string literals",
510 } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
511 self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
517 "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
518 strings `#[doc(alias(\"a\", \"b\"))]`",
525 fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
526 let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
527 if doc_keyword.is_empty() {
528 self.doc_attr_str_error(meta, "keyword");
531 match self.tcx.hir().expect_item(hir_id).kind {
532 ItemKind::Mod(ref module) => {
533 if !module.item_ids.is_empty() {
538 "`#[doc(keyword = \"...\")]` can only be used on empty modules",
549 "`#[doc(keyword = \"...\")]` can only be used on modules",
555 if !rustc_lexer::is_ident(&doc_keyword) {
559 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
560 &format!("`{}` is not a valid identifier", doc_keyword),
568 fn check_attr_crate_level(
570 meta: &NestedMetaItem,
574 if CRATE_HIR_ID == hir_id {
580 "`#![doc({} = \"...\")]` isn't allowed as a crate-level attribute",
590 fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
591 let mut is_valid = true;
593 if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
595 if let Some(i_meta) = meta.meta_item() {
596 match i_meta.name_or_empty() {
598 if !self.check_attr_crate_level(&meta, hir_id, "alias")
599 || !self.check_doc_alias(&meta, hir_id, target) =>
605 if !self.check_attr_crate_level(&meta, hir_id, "keyword")
606 || !self.check_doc_keyword(&meta, hir_id) =>
611 sym::test if CRATE_HIR_ID != hir_id => {
612 self.tcx.struct_span_lint_hir(
613 INVALID_DOC_ATTRIBUTES,
618 "`#![doc(test(...)]` is only allowed \
619 as a crate-level attribute",
627 // no_default_passes: deprecated
628 // passes: deprecated
629 // plugins: removed, but rustdoc warns about it itself
633 | sym::html_favicon_url
635 | sym::html_no_source
636 | sym::html_playground_url
640 | sym::issue_tracker_base_url
643 | sym::no_default_passes
652 self.tcx.struct_span_lint_hir(
653 INVALID_DOC_ATTRIBUTES,
658 "unknown `doc` attribute `{}`",
659 rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
661 lint.build(&msg).emit();
668 self.tcx.struct_span_lint_hir(
669 INVALID_DOC_ATTRIBUTES,
673 lint.build(&format!("invalid `doc` attribute")).emit();
684 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
685 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
687 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
688 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
689 // `#[cold]` attribute with just a lint, because we previously
690 // erroneously allowed it and some crates used it accidentally, to to be compatible
691 // with crates depending on them, we can't throw an error here.
692 Target::Field | Target::Arm | Target::MacroDef => {
693 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
696 // FIXME: #[cold] was previously allowed on non-functions and some crates used
697 // this, so only emit a warning.
698 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
699 lint.build("attribute should be applied to a function")
701 "this was previously accepted by the compiler but is \
702 being phased out; it will become a hard error in \
705 .span_label(*span, "not a function")
712 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
713 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
715 Target::ForeignFn | Target::ForeignStatic => {}
716 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
717 // `#[link_name]` attribute with just a lint, because we previously
718 // erroneously allowed it and some crates used it accidentally, to to be compatible
719 // with crates depending on them, we can't throw an error here.
720 Target::Field | Target::Arm | Target::MacroDef => {
721 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
724 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
725 // used this, so only emit a warning.
726 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
728 lint.build("attribute should be applied to a foreign function or static");
730 "this was previously accepted by the compiler but is \
731 being phased out; it will become a hard error in \
736 if let Target::ForeignMod = target {
737 if let Some(value) = attr.value_str() {
740 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
743 diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
747 diag.span_label(*span, "not a foreign function or static");
754 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
755 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
757 Target::ExternCrate => true,
758 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
759 // `#[no_link]` attribute with just a lint, because we previously
760 // erroneously allowed it and some crates used it accidentally, to to be compatible
761 // with crates depending on them, we can't throw an error here.
762 Target::Field | Target::Arm | Target::MacroDef => {
763 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
771 "attribute should be applied to an `extern crate` item",
773 .span_label(*span, "not an `extern crate` item")
780 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
781 fn check_export_name(
789 Target::Static | Target::Fn | Target::Method(..) => true,
790 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
791 // `#[export_name]` attribute with just a lint, because we previously
792 // erroneously allowed it and some crates used it accidentally, to to be compatible
793 // with crates depending on them, we can't throw an error here.
794 Target::Field | Target::Arm | Target::MacroDef => {
795 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
803 "attribute should be applied to a function or static",
805 .span_label(*span, "not a function or static")
812 /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
813 fn check_rustc_args_required_const(
818 item: Option<ItemLike<'_>>,
820 let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
824 .struct_span_err(attr.span, "attribute should be applied to a function")
825 .span_label(*span, "not a function")
830 let list = match attr.meta_item_list() {
831 // The attribute form is validated on AST.
832 None => return false,
836 let mut invalid_args = vec![];
838 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
839 if let Some(ItemLike::Item(Item {
840 kind: ItemKind::Fn(FnSig { decl, .. }, ..),
843 | Some(ItemLike::ForeignItem(ForeignItem {
844 kind: ForeignItemKind::Fn(decl, ..),
848 let arg_count = decl.inputs.len() as u128;
849 if *val >= arg_count {
850 let span = meta.span();
853 .struct_span_err(span, "index exceeds number of arguments")
857 "there {} only {} argument{}",
858 if arg_count != 1 { "are" } else { "is" },
860 pluralize!(arg_count)
867 bug!("should be a function item");
870 invalid_args.push(meta.span());
874 if !invalid_args.is_empty() {
877 .struct_span_err(invalid_args, "arguments should be non-negative integers")
885 fn check_rustc_layout_scalar_valid_range(
891 if target != Target::Struct {
894 .struct_span_err(attr.span, "attribute should be applied to a struct")
895 .span_label(*span, "not a struct")
900 let list = match attr.meta_item_list() {
901 None => return false,
905 if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
910 .struct_span_err(attr.span, "expected exactly one integer literal argument")
916 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
917 fn check_rustc_legacy_const_generics(
922 item: Option<ItemLike<'_>>,
924 let is_function = matches!(target, Target::Fn | Target::Method(..));
928 .struct_span_err(attr.span, "attribute should be applied to a function")
929 .span_label(*span, "not a function")
934 let list = match attr.meta_item_list() {
935 // The attribute form is validated on AST.
936 None => return false,
940 let (decl, generics) = match item {
941 Some(ItemLike::Item(Item {
942 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
944 })) => (decl, generics),
945 _ => bug!("should be a function item"),
948 for param in generics.params {
950 hir::GenericParamKind::Const { .. } => {}
956 "#[rustc_legacy_const_generics] functions must \
957 only have const generics",
959 .span_label(param.span, "non-const generic parameter")
966 if list.len() != generics.params.len() {
971 "#[rustc_legacy_const_generics] must have one index for each generic parameter",
973 .span_label(generics.span, "generic parameters")
978 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
979 let mut invalid_args = vec![];
981 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
982 if *val >= arg_count {
983 let span = meta.span();
986 .struct_span_err(span, "index exceeds number of arguments")
990 "there {} only {} argument{}",
991 if arg_count != 1 { "are" } else { "is" },
993 pluralize!(arg_count)
1000 invalid_args.push(meta.span());
1004 if !invalid_args.is_empty() {
1007 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1015 /// Checks if `#[link_section]` is applied to a function or static.
1016 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1018 Target::Static | Target::Fn | Target::Method(..) => {}
1019 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1020 // `#[link_section]` attribute with just a lint, because we previously
1021 // erroneously allowed it and some crates used it accidentally, to to be compatible
1022 // with crates depending on them, we can't throw an error here.
1023 Target::Field | Target::Arm | Target::MacroDef => {
1024 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1027 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1028 // crates used this, so only emit a warning.
1029 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1030 lint.build("attribute should be applied to a function or static")
1032 "this was previously accepted by the compiler but is \
1033 being phased out; it will become a hard error in \
1036 .span_label(*span, "not a function or static")
1043 /// Checks if `#[no_mangle]` is applied to a function or static.
1044 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1046 Target::Static | Target::Fn | Target::Method(..) => {}
1047 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1048 // `#[no_mangle]` attribute with just a lint, because we previously
1049 // erroneously allowed it and some crates used it accidentally, to to be compatible
1050 // with crates depending on them, we can't throw an error here.
1051 Target::Field | Target::Arm | Target::MacroDef => {
1052 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1055 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1056 // crates used this, so only emit a warning.
1057 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1058 lint.build("attribute should be applied to a function or static")
1060 "this was previously accepted by the compiler but is \
1061 being phased out; it will become a hard error in \
1064 .span_label(*span, "not a function or static")
1071 /// Checks if the `#[repr]` attributes on `item` are valid.
1074 attrs: &'hir [Attribute],
1077 item: Option<ItemLike<'_>>,
1080 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1083 // #[repr(bar, align(8))]
1085 let hints: Vec<_> = attrs
1087 .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
1088 .filter_map(|attr| attr.meta_item_list())
1092 let mut int_reprs = 0;
1093 let mut is_c = false;
1094 let mut is_simd = false;
1095 let mut is_transparent = false;
1097 for hint in &hints {
1098 let (article, allowed_targets) = match hint.name_or_empty() {
1099 _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => {
1100 ("a", "struct, enum, or union")
1102 name @ sym::C | name @ sym::align => {
1103 is_c |= name == sym::C;
1105 Target::Struct | Target::Union | Target::Enum => continue,
1106 _ => ("a", "struct, enum, or union"),
1110 if target != Target::Struct && target != Target::Union {
1111 ("a", "struct or union")
1118 if target != Target::Struct {
1124 sym::transparent => {
1125 is_transparent = true;
1127 Target::Struct | Target::Union | Target::Enum => continue,
1128 _ => ("a", "struct, enum, or union"),
1132 if !self.tcx.features().enabled(sym::no_niche) {
1134 &self.tcx.sess.parse_sess,
1137 "the attribute `repr(no_niche)` is currently unstable",
1142 Target::Struct | Target::Enum => continue,
1143 _ => ("a", "struct or enum"),
1159 if target != Target::Enum {
1173 &format!("attribute should be applied to {} {}", article, allowed_targets)
1175 .span_label(*span, &format!("not {} {}", article, allowed_targets))
1179 // Just point at all repr hints if there are any incompatibilities.
1180 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1181 let hint_spans = hints.iter().map(|hint| hint.span());
1183 // Error on repr(transparent, <anything else apart from no_niche>).
1184 let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1185 let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1186 if is_transparent && non_no_niche_count > 1 {
1187 let hint_spans: Vec<_> = hint_spans.clone().collect();
1192 "transparent {} cannot have other repr hints",
1197 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1199 || (is_simd && is_c)
1202 && item.map_or(false, |item| {
1203 if let ItemLike::Item(item) = item {
1204 return is_c_like_enum(item);
1209 self.tcx.struct_span_lint_hir(
1210 CONFLICTING_REPR_HINTS,
1212 hint_spans.collect::<Vec<Span>>(),
1214 lint.build("conflicting representation hints")
1215 .code(rustc_errors::error_code!(E0566))
1222 fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1224 if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
1227 .span_err(attr.span, "attribute must be applied to a `static` variable");
1232 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1233 /// (Allows proc_macro functions)
1234 fn check_allow_internal_unstable(
1240 attrs: &[Attribute],
1242 debug!("Checking target: {:?}", target);
1246 if self.tcx.sess.is_proc_macro_attr(attr) {
1247 debug!("Is proc macro attr");
1251 debug!("Is not proc macro attr");
1254 Target::MacroDef => true,
1255 // FIXME(#80564): We permit struct fields and match arms to have an
1256 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1257 // erroneously allowed it and some crates used it accidentally, to to be compatible
1258 // with crates depending on them, we can't throw an error here.
1259 Target::Field | Target::Arm => {
1260 self.inline_attr_str_error_without_macro_def(
1263 "allow_internal_unstable",
1270 .struct_span_err(attr.span, "attribute should be applied to a macro")
1271 .span_label(*span, "not a macro")
1278 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1279 /// (Allows proc_macro functions)
1280 fn check_rustc_allow_const_fn_unstable(
1288 Target::Fn | Target::Method(_)
1289 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1293 // FIXME(#80564): We permit struct fields and match arms to have an
1294 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1295 // erroneously allowed it and some crates used it accidentally, to to be compatible
1296 // with crates depending on them, we can't throw an error here.
1297 Target::Field | Target::Arm | Target::MacroDef => {
1298 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1304 .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1305 .span_label(*span, "not a `const fn`")
1313 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
1314 type Map = Map<'tcx>;
1316 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1317 NestedVisitorMap::OnlyBodies(self.tcx.hir())
1320 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
1321 let target = Target::from_item(item);
1322 self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
1323 intravisit::walk_item(self, item)
1326 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1327 let target = Target::from_generic_param(generic_param);
1328 self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
1329 intravisit::walk_generic_param(self, generic_param)
1332 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
1333 let target = Target::from_trait_item(trait_item);
1334 self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
1335 intravisit::walk_trait_item(self, trait_item)
1338 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1339 self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1340 intravisit::walk_field_def(self, struct_field);
1343 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
1344 self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
1345 intravisit::walk_arm(self, arm);
1348 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
1349 let target = Target::from_foreign_item(f_item);
1350 self.check_attributes(
1354 Some(ItemLike::ForeignItem(f_item)),
1356 intravisit::walk_foreign_item(self, f_item)
1359 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
1360 let target = target_from_impl_item(self.tcx, impl_item);
1361 self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
1362 intravisit::walk_impl_item(self, impl_item)
1365 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
1366 // When checking statements ignore expressions, they will be checked later.
1367 if let hir::StmtKind::Local(ref l) = stmt.kind {
1368 self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
1370 intravisit::walk_stmt(self, stmt)
1373 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
1374 let target = match expr.kind {
1375 hir::ExprKind::Closure(..) => Target::Closure,
1376 _ => Target::Expression,
1379 self.check_attributes(expr.hir_id, &expr.span, target, None);
1380 intravisit::walk_expr(self, expr)
1385 variant: &'tcx hir::Variant<'tcx>,
1386 generics: &'tcx hir::Generics<'tcx>,
1389 self.check_attributes(variant.id, &variant.span, Target::Variant, None);
1390 intravisit::walk_variant(self, variant, generics, item_id)
1393 fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1394 self.check_attributes(macro_def.hir_id(), ¯o_def.span, Target::MacroDef, None);
1395 intravisit::walk_macro_def(self, macro_def);
1398 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1399 self.check_attributes(param.hir_id, ¶m.span, Target::Param, None);
1401 intravisit::walk_param(self, param);
1405 fn is_c_like_enum(item: &Item<'_>) -> bool {
1406 if let ItemKind::Enum(ref def, _) = item.kind {
1407 for variant in def.variants {
1408 match variant.data {
1409 hir::VariantData::Unit(..) => { /* continue */ }
1419 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1420 const ATTRS_TO_CHECK: &[Symbol] = &[
1424 sym::automatically_derived,
1430 for attr_to_check in ATTRS_TO_CHECK {
1431 if tcx.sess.check_name(attr, *attr_to_check) {
1436 "`{}` attribute cannot be used at crate level",
1437 attr_to_check.to_ident_string()
1446 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1448 if tcx.sess.check_name(attr, sym::inline) {
1453 "attribute should be applied to function or closure",
1455 .span_label(attr.span, "not a function or closure")
1461 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1462 let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1463 tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1464 tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1465 check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
1466 if module_def_id.is_top_level_module() {
1467 check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
1468 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1472 pub(crate) fn provide(providers: &mut Providers) {
1473 *providers = Providers { check_mod_attrs, ..*providers };