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 crate::hir
::{self, HirId, HirVec, Attribute, Item, ItemKind, TraitItem, TraitItemKind}
;
8 use crate::hir
::DUMMY_HIR_ID
;
9 use crate::hir
::def_id
::DefId
;
10 use crate::hir
::intravisit
::{self, Visitor, NestedVisitorMap}
;
11 use crate::lint
::builtin
::UNUSED_ATTRIBUTES
;
12 use crate::ty
::TyCtxt
;
13 use crate::ty
::query
::Providers
;
15 use std
::fmt
::{self, Display}
;
16 use syntax
::{attr, symbol::sym}
;
19 use rustc_error_codes
::*;
21 #[derive(Copy, Clone, PartialEq)]
22 pub(crate) enum MethodKind
{
27 #[derive(Copy, Clone, PartialEq)]
28 pub(crate) enum Target
{
56 impl Display
for Target
{
57 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
58 write
!(f
, "{}", match *self {
59 Target
::ExternCrate
=> "extern crate",
61 Target
::Static
=> "static item",
62 Target
::Const
=> "constant item",
63 Target
::Fn
=> "function",
64 Target
::Closure
=> "closure",
65 Target
::Mod
=> "module",
66 Target
::ForeignMod
=> "foreign module",
67 Target
::GlobalAsm
=> "global asm",
68 Target
::TyAlias
=> "type alias",
69 Target
::OpaqueTy
=> "opaque type",
70 Target
::Enum
=> "enum",
71 Target
::Struct
=> "struct",
72 Target
::Union
=> "union",
73 Target
::Trait
=> "trait",
74 Target
::TraitAlias
=> "trait alias",
75 Target
::Impl
=> "item",
76 Target
::Expression
=> "expression",
77 Target
::Statement
=> "statement",
78 Target
::AssocConst
=> "associated const",
79 Target
::Method(_
) => "method",
80 Target
::AssocTy
=> "associated type",
81 Target
::ForeignFn
=> "foreign function",
82 Target
::ForeignStatic
=> "foreign static item",
83 Target
::ForeignTy
=> "foreign type",
89 pub(crate) fn from_item(item
: &Item
) -> Target
{
91 ItemKind
::ExternCrate(..) => Target
::ExternCrate
,
92 ItemKind
::Use(..) => Target
::Use
,
93 ItemKind
::Static(..) => Target
::Static
,
94 ItemKind
::Const(..) => Target
::Const
,
95 ItemKind
::Fn(..) => Target
::Fn
,
96 ItemKind
::Mod(..) => Target
::Mod
,
97 ItemKind
::ForeignMod(..) => Target
::ForeignMod
,
98 ItemKind
::GlobalAsm(..) => Target
::GlobalAsm
,
99 ItemKind
::TyAlias(..) => Target
::TyAlias
,
100 ItemKind
::OpaqueTy(..) => Target
::OpaqueTy
,
101 ItemKind
::Enum(..) => Target
::Enum
,
102 ItemKind
::Struct(..) => Target
::Struct
,
103 ItemKind
::Union(..) => Target
::Union
,
104 ItemKind
::Trait(..) => Target
::Trait
,
105 ItemKind
::TraitAlias(..) => Target
::TraitAlias
,
106 ItemKind
::Impl(..) => Target
::Impl
,
110 fn from_trait_item(trait_item
: &TraitItem
) -> Target
{
111 match trait_item
.kind
{
112 TraitItemKind
::Const(..) => Target
::AssocConst
,
113 TraitItemKind
::Method(_
, hir
::TraitMethod
::Required(_
)) => {
114 Target
::Method(MethodKind
::Trait { body: false }
)
116 TraitItemKind
::Method(_
, hir
::TraitMethod
::Provided(_
)) => {
117 Target
::Method(MethodKind
::Trait { body: true }
)
119 TraitItemKind
::Type(..) => Target
::AssocTy
,
123 fn from_foreign_item(foreign_item
: &hir
::ForeignItem
) -> Target
{
124 match foreign_item
.kind
{
125 hir
::ForeignItemKind
::Fn(..) => Target
::ForeignFn
,
126 hir
::ForeignItemKind
::Static(..) => Target
::ForeignStatic
,
127 hir
::ForeignItemKind
::Type
=> Target
::ForeignTy
,
131 fn from_impl_item
<'tcx
>(tcx
: TyCtxt
<'tcx
>, impl_item
: &hir
::ImplItem
) -> Target
{
132 match impl_item
.kind
{
133 hir
::ImplItemKind
::Const(..) => Target
::AssocConst
,
134 hir
::ImplItemKind
::Method(..) => {
135 let parent_hir_id
= tcx
.hir().get_parent_item(impl_item
.hir_id
);
136 let containing_item
= tcx
.hir().expect_item(parent_hir_id
);
137 let containing_impl_is_for_trait
= match &containing_item
.kind
{
138 hir
::ItemKind
::Impl(_
, _
, _
, _
, tr
, _
, _
) => tr
.is_some(),
139 _
=> bug
!("parent of an ImplItem must be an Impl"),
141 if containing_impl_is_for_trait
{
142 Target
::Method(MethodKind
::Trait { body: true }
)
144 Target
::Method(MethodKind
::Inherent
)
147 hir
::ImplItemKind
::TyAlias(..) | hir
::ImplItemKind
::OpaqueTy(..) => Target
::AssocTy
,
152 struct CheckAttrVisitor
<'tcx
> {
156 impl CheckAttrVisitor
<'tcx
> {
157 /// Checks any attribute.
161 attrs
: &HirVec
<Attribute
>,
166 let mut is_valid
= true;
168 is_valid
&= if attr
.check_name(sym
::inline
) {
169 self.check_inline(hir_id
, attr
, span
, target
)
170 } else if attr
.check_name(sym
::non_exhaustive
) {
171 self.check_non_exhaustive(attr
, span
, target
)
172 } else if attr
.check_name(sym
::marker
) {
173 self.check_marker(attr
, span
, target
)
174 } else if attr
.check_name(sym
::target_feature
) {
175 self.check_target_feature(attr
, span
, target
)
176 } else if attr
.check_name(sym
::track_caller
) {
177 self.check_track_caller(&attr
.span
, attrs
, span
, target
)
187 if target
== Target
::Fn
{
188 self.tcx
.codegen_fn_attrs(self.tcx
.hir().local_def_id(hir_id
));
191 self.check_repr(attrs
, span
, target
, item
);
192 self.check_used(attrs
, target
);
195 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
196 fn check_inline(&self, hir_id
: HirId
, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
198 Target
::Fn
| Target
::Closure
| Target
::Method(MethodKind
::Trait { body: true }
)
199 | Target
::Method(MethodKind
::Inherent
) => true,
200 Target
::Method(MethodKind
::Trait { body: false }
) | Target
::ForeignFn
=> {
201 self.tcx
.struct_span_lint_hir(
205 "`#[inline]` is ignored on function prototypes",
209 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
210 // just a lint, because we previously erroneously allowed it and some crates used it
211 // accidentally, to to be compatible with crates depending on them, we can't throw an
213 Target
::AssocConst
=> {
214 self.tcx
.struct_span_lint_hir(
218 "`#[inline]` is ignored on constants",
219 ).warn("this was previously accepted by the compiler but is \
220 being phased out; it will become a hard error in \
222 .note("for more information, see issue #65833 \
223 <https://github.com/rust-lang/rust/issues/65833>")
232 "attribute should be applied to function or closure",
233 ).span_label(*span
, "not a function or closure")
240 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
241 fn check_track_caller(
244 attrs
: &HirVec
<Attribute
>,
249 Target
::Fn
if attr
::contains_name(attrs
, sym
::naked
) => {
254 "cannot use `#[track_caller]` with `#[naked]`",
258 Target
::Fn
| Target
::Method(MethodKind
::Inherent
) => true,
259 Target
::Method(_
) => {
264 "`#[track_caller]` may not be used on trait methods",
273 "attribute should be applied to function"
275 .span_label(*span
, "not a function")
282 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
283 fn check_non_exhaustive(
290 Target
::Struct
| Target
::Enum
=> true,
292 struct_span_err
!(self.tcx
.sess
,
295 "attribute can only be applied to a struct or enum")
296 .span_label(*span
, "not a struct or enum")
303 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
304 fn check_marker(&self, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
306 Target
::Trait
=> true,
309 .struct_span_err(attr
.span
, "attribute can only be applied to a trait")
310 .span_label(*span
, "not a trait")
317 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
318 fn check_target_feature(&self, attr
: &Attribute
, span
: &Span
, target
: Target
) -> bool
{
320 Target
::Fn
| Target
::Method(MethodKind
::Trait { body: true }
)
321 | Target
::Method(MethodKind
::Inherent
) => true,
324 .struct_span_err(attr
.span
, "attribute should be applied to a function")
325 .span_label(*span
, "not a function")
332 /// Checks if the `#[repr]` attributes on `item` are valid.
335 attrs
: &HirVec
<Attribute
>,
340 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
343 // #[repr(bar, align(8))]
345 let hints
: Vec
<_
> = attrs
347 .filter(|attr
| attr
.check_name(sym
::repr
))
348 .filter_map(|attr
| attr
.meta_item_list())
352 let mut int_reprs
= 0;
353 let mut is_c
= false;
354 let mut is_simd
= false;
355 let mut is_transparent
= false;
358 let (article
, allowed_targets
) = match hint
.name_or_empty() {
359 name @ sym
::C
| name @ sym
::align
=> {
360 is_c
|= name
== sym
::C
;
362 Target
::Struct
| Target
::Union
| Target
::Enum
=> continue,
363 _
=> ("a", "struct, enum, or union"),
367 if target
!= Target
::Struct
&&
368 target
!= Target
::Union
{
369 ("a", "struct or union")
376 if target
!= Target
::Struct
{
382 sym
::transparent
=> {
383 is_transparent
= true;
385 Target
::Struct
| Target
::Union
| Target
::Enum
=> continue,
386 _
=> ("a", "struct, enum, or union"),
389 sym
::i8 | sym
::u8 | sym
::i16 | sym
::u16 |
390 sym
::i32 | sym
::u32 | sym
::i64 | sym
::u64 |
391 sym
::isize | sym
::usize => {
393 if target
!= Target
::Enum
{
401 self.emit_repr_error(
404 &format
!("attribute should be applied to {}", allowed_targets
),
405 &format
!("not {} {}", article
, allowed_targets
),
409 // Just point at all repr hints if there are any incompatibilities.
410 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
411 let hint_spans
= hints
.iter().map(|hint
| hint
.span());
413 // Error on repr(transparent, <anything else>).
414 if is_transparent
&& hints
.len() > 1 {
415 let hint_spans
: Vec
<_
> = hint_spans
.clone().collect();
416 span_err
!(self.tcx
.sess
, hint_spans
, E0692
,
417 "transparent {} cannot have other repr hints", target
);
419 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
422 || (int_reprs
== 1 && is_c
&& item
.map_or(false, |item
| is_c_like_enum(item
))) {
423 let hint_spans
: Vec
<_
> = hint_spans
.collect();
424 span_warn
!(self.tcx
.sess
, hint_spans
, E0566
,
425 "conflicting representation hints");
436 struct_span_err
!(self.tcx
.sess
, hint_span
, E0517
, "{}", hint_message
)
437 .span_label(label_span
, label_message
)
441 fn check_stmt_attributes(&self, stmt
: &hir
::Stmt
) {
442 // When checking statements ignore expressions, they will be checked later
443 if let hir
::StmtKind
::Local(ref l
) = stmt
.kind
{
444 for attr
in l
.attrs
.iter() {
445 if attr
.check_name(sym
::inline
) {
446 self.check_inline(DUMMY_HIR_ID
, attr
, &stmt
.span
, Target
::Statement
);
448 if attr
.check_name(sym
::repr
) {
449 self.emit_repr_error(
452 "attribute should not be applied to a statement",
453 "not a struct, enum, or union",
460 fn check_expr_attributes(&self, expr
: &hir
::Expr
) {
461 let target
= match expr
.kind
{
462 hir
::ExprKind
::Closure(..) => Target
::Closure
,
463 _
=> Target
::Expression
,
465 for attr
in expr
.attrs
.iter() {
466 if attr
.check_name(sym
::inline
) {
467 self.check_inline(DUMMY_HIR_ID
, attr
, &expr
.span
, target
);
469 if attr
.check_name(sym
::repr
) {
470 self.emit_repr_error(
473 "attribute should not be applied to an expression",
474 "not defining a struct, enum, or union",
480 fn check_used(&self, attrs
: &HirVec
<Attribute
>, target
: Target
) {
482 if attr
.check_name(sym
::used
) && target
!= Target
::Static
{
484 .span_err(attr
.span
, "attribute must be applied to a `static` variable");
490 impl Visitor
<'tcx
> for CheckAttrVisitor
<'tcx
> {
491 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
492 NestedVisitorMap
::OnlyBodies(&self.tcx
.hir())
495 fn visit_item(&mut self, item
: &'tcx Item
) {
496 let target
= Target
::from_item(item
);
497 self.check_attributes(item
.hir_id
, &item
.attrs
, &item
.span
, target
, Some(item
));
498 intravisit
::walk_item(self, item
)
501 fn visit_trait_item(&mut self, trait_item
: &'tcx TraitItem
) {
502 let target
= Target
::from_trait_item(trait_item
);
503 self.check_attributes(trait_item
.hir_id
, &trait_item
.attrs
, &trait_item
.span
, target
, None
);
504 intravisit
::walk_trait_item(self, trait_item
)
507 fn visit_foreign_item(&mut self, f_item
: &'tcx hir
::ForeignItem
) {
508 let target
= Target
::from_foreign_item(f_item
);
509 self.check_attributes(f_item
.hir_id
, &f_item
.attrs
, &f_item
.span
, target
, None
);
510 intravisit
::walk_foreign_item(self, f_item
)
513 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
) {
514 let target
= Target
::from_impl_item(self.tcx
, impl_item
);
515 self.check_attributes(impl_item
.hir_id
, &impl_item
.attrs
, &impl_item
.span
, target
, None
);
516 intravisit
::walk_impl_item(self, impl_item
)
519 fn visit_stmt(&mut self, stmt
: &'tcx hir
::Stmt
) {
520 self.check_stmt_attributes(stmt
);
521 intravisit
::walk_stmt(self, stmt
)
524 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
525 self.check_expr_attributes(expr
);
526 intravisit
::walk_expr(self, expr
)
530 fn is_c_like_enum(item
: &Item
) -> bool
{
531 if let ItemKind
::Enum(ref def
, _
) = item
.kind
{
532 for variant
in &def
.variants
{
534 hir
::VariantData
::Unit(..) => { /* continue */ }
544 fn check_mod_attrs(tcx
: TyCtxt
<'_
>, module_def_id
: DefId
) {
545 tcx
.hir().visit_item_likes_in_module(
547 &mut CheckAttrVisitor { tcx }
.as_deep_visitor()
551 pub(crate) fn provide(providers
: &mut Providers
<'_
>) {
552 *providers
= Providers
{