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
9 use crate::ty
::query
::Providers
;
12 use crate::hir
::def_id
::DefId
;
13 use crate::hir
::intravisit
::{self, Visitor, NestedVisitorMap}
;
14 use std
::fmt
::{self, Display}
;
17 #[derive(Copy, Clone, PartialEq)]
18 pub(crate) enum Target
{
40 impl Display
for Target
{
41 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
42 write
!(f
, "{}", match *self {
43 Target
::ExternCrate
=> "extern crate",
45 Target
::Static
=> "static item",
46 Target
::Const
=> "constant item",
47 Target
::Fn
=> "function",
48 Target
::Closure
=> "closure",
49 Target
::Mod
=> "module",
50 Target
::ForeignMod
=> "foreign module",
51 Target
::GlobalAsm
=> "global asm",
52 Target
::Ty
=> "type alias",
53 Target
::Existential
=> "existential type",
54 Target
::Enum
=> "enum",
55 Target
::Struct
=> "struct",
56 Target
::Union
=> "union",
57 Target
::Trait
=> "trait",
58 Target
::TraitAlias
=> "trait alias",
59 Target
::Impl
=> "item",
60 Target
::Expression
=> "expression",
61 Target
::Statement
=> "statement",
67 pub(crate) fn from_item(item
: &hir
::Item
) -> Target
{
69 hir
::ItemKind
::ExternCrate(..) => Target
::ExternCrate
,
70 hir
::ItemKind
::Use(..) => Target
::Use
,
71 hir
::ItemKind
::Static(..) => Target
::Static
,
72 hir
::ItemKind
::Const(..) => Target
::Const
,
73 hir
::ItemKind
::Fn(..) => Target
::Fn
,
74 hir
::ItemKind
::Mod(..) => Target
::Mod
,
75 hir
::ItemKind
::ForeignMod(..) => Target
::ForeignMod
,
76 hir
::ItemKind
::GlobalAsm(..) => Target
::GlobalAsm
,
77 hir
::ItemKind
::Ty(..) => Target
::Ty
,
78 hir
::ItemKind
::Existential(..) => Target
::Existential
,
79 hir
::ItemKind
::Enum(..) => Target
::Enum
,
80 hir
::ItemKind
::Struct(..) => Target
::Struct
,
81 hir
::ItemKind
::Union(..) => Target
::Union
,
82 hir
::ItemKind
::Trait(..) => Target
::Trait
,
83 hir
::ItemKind
::TraitAlias(..) => Target
::TraitAlias
,
84 hir
::ItemKind
::Impl(..) => Target
::Impl
,
89 struct CheckAttrVisitor
<'a
, 'tcx
: 'a
> {
90 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
93 impl<'a
, 'tcx
> CheckAttrVisitor
<'a
, 'tcx
> {
94 /// Checks any attribute.
95 fn check_attributes(&self, item
: &hir
::Item
, target
: Target
) {
96 if target
== Target
::Fn
|| target
== Target
::Const
{
97 self.tcx
.codegen_fn_attrs(self.tcx
.hir().local_def_id_from_hir_id(item
.hir_id
));
98 } else if let Some(a
) = item
.attrs
.iter().find(|a
| a
.check_name("target_feature")) {
99 self.tcx
.sess
.struct_span_err(a
.span
, "attribute should be applied to a function")
100 .span_label(item
.span
, "not a function")
104 for attr
in &item
.attrs
{
105 if attr
.check_name("inline") {
106 self.check_inline(attr
, &item
.span
, target
)
107 } else if attr
.check_name("non_exhaustive") {
108 self.check_non_exhaustive(attr
, item
, target
)
109 } else if attr
.check_name("marker") {
110 self.check_marker(attr
, item
, target
)
114 self.check_repr(item
, target
);
115 self.check_used(item
, target
);
118 /// Checks if an `#[inline]` is applied to a function or a closure.
119 fn check_inline(&self, attr
: &hir
::Attribute
, span
: &Span
, target
: Target
) {
120 if target
!= Target
::Fn
&& target
!= Target
::Closure
{
121 struct_span_err
!(self.tcx
.sess
,
124 "attribute should be applied to function or closure")
125 .span_label(*span
, "not a function or closure")
130 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
131 fn check_non_exhaustive(&self, attr
: &hir
::Attribute
, item
: &hir
::Item
, target
: Target
) {
133 Target
::Struct
| Target
::Enum
=> { /* Valid */ }
,
135 struct_span_err
!(self.tcx
.sess
,
138 "attribute can only be applied to a struct or enum")
139 .span_label(item
.span
, "not a struct or enum")
146 /// Checks if the `#[marker]` attribute on an `item` is valid.
147 fn check_marker(&self, attr
: &hir
::Attribute
, item
: &hir
::Item
, target
: Target
) {
149 Target
::Trait
=> { /* Valid */ }
,
152 .struct_span_err(attr
.span
, "attribute can only be applied to a trait")
153 .span_label(item
.span
, "not a trait")
160 /// Checks if the `#[repr]` attributes on `item` are valid.
161 fn check_repr(&self, item
: &hir
::Item
, target
: Target
) {
162 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
165 // #[repr(bar, align(8))]
167 let hints
: Vec
<_
> = item
.attrs
169 .filter(|attr
| attr
.check_name("repr"))
170 .filter_map(|attr
| attr
.meta_item_list())
174 let mut int_reprs
= 0;
175 let mut is_c
= false;
176 let mut is_simd
= false;
177 let mut is_transparent
= false;
180 let (article
, allowed_targets
) = match hint
.name_or_empty().get() {
181 name @
"C" | name @
"align" => {
183 if target
!= Target
::Struct
&&
184 target
!= Target
::Union
&&
185 target
!= Target
::Enum
{
186 ("a", "struct, enum or union")
192 if target
!= Target
::Struct
&&
193 target
!= Target
::Union
{
194 ("a", "struct or union")
201 if target
!= Target
::Struct
{
208 is_transparent
= true;
209 if target
!= Target
::Struct
{
215 "i8" | "u8" | "i16" | "u16" |
216 "i32" | "u32" | "i64" | "u64" |
217 "isize" | "usize" => {
219 if target
!= Target
::Enum
{
227 self.emit_repr_error(
230 &format
!("attribute should be applied to {}", allowed_targets
),
231 &format
!("not {} {}", article
, allowed_targets
),
235 // Just point at all repr hints if there are any incompatibilities.
236 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
237 let hint_spans
= hints
.iter().map(|hint
| hint
.span());
239 // Error on repr(transparent, <anything else>).
240 if is_transparent
&& hints
.len() > 1 {
241 let hint_spans
: Vec
<_
> = hint_spans
.clone().collect();
242 span_err
!(self.tcx
.sess
, hint_spans
, E0692
,
243 "transparent struct cannot have other repr hints");
245 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
248 || (int_reprs
== 1 && is_c
&& is_c_like_enum(item
)) {
249 let hint_spans
: Vec
<_
> = hint_spans
.collect();
250 span_warn
!(self.tcx
.sess
, hint_spans
, E0566
,
251 "conflicting representation hints");
262 struct_span_err
!(self.tcx
.sess
, hint_span
, E0517
, "{}", hint_message
)
263 .span_label(label_span
, label_message
)
267 fn check_stmt_attributes(&self, stmt
: &hir
::Stmt
) {
268 // When checking statements ignore expressions, they will be checked later
269 if let hir
::StmtKind
::Local(ref l
) = stmt
.node
{
270 for attr
in l
.attrs
.iter() {
271 if attr
.check_name("inline") {
272 self.check_inline(attr
, &stmt
.span
, Target
::Statement
);
274 if attr
.check_name("repr") {
275 self.emit_repr_error(
278 "attribute should not be applied to a statement",
279 "not a struct, enum or union",
286 fn check_expr_attributes(&self, expr
: &hir
::Expr
) {
287 let target
= match expr
.node
{
288 hir
::ExprKind
::Closure(..) => Target
::Closure
,
289 _
=> Target
::Expression
,
291 for attr
in expr
.attrs
.iter() {
292 if attr
.check_name("inline") {
293 self.check_inline(attr
, &expr
.span
, target
);
295 if attr
.check_name("repr") {
296 self.emit_repr_error(
299 "attribute should not be applied to an expression",
300 "not defining a struct, enum or union",
306 fn check_used(&self, item
: &hir
::Item
, target
: Target
) {
307 for attr
in &item
.attrs
{
308 if attr
.check_name("used") && target
!= Target
::Static
{
310 .span_err(attr
.span
, "attribute must be applied to a `static` variable");
316 impl<'a
, 'tcx
> Visitor
<'tcx
> for CheckAttrVisitor
<'a
, 'tcx
> {
317 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
318 NestedVisitorMap
::OnlyBodies(&self.tcx
.hir())
321 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
322 let target
= Target
::from_item(item
);
323 self.check_attributes(item
, target
);
324 intravisit
::walk_item(self, item
)
328 fn visit_stmt(&mut self, stmt
: &'tcx hir
::Stmt
) {
329 self.check_stmt_attributes(stmt
);
330 intravisit
::walk_stmt(self, stmt
)
333 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
334 self.check_expr_attributes(expr
);
335 intravisit
::walk_expr(self, expr
)
339 fn is_c_like_enum(item
: &hir
::Item
) -> bool
{
340 if let hir
::ItemKind
::Enum(ref def
, _
) = item
.node
{
341 for variant
in &def
.variants
{
342 match variant
.node
.data
{
343 hir
::VariantData
::Unit(..) => { /* continue */ }
344 _
=> { return false; }
353 fn check_mod_attrs
<'tcx
>(tcx
: TyCtxt
<'_
, 'tcx
, 'tcx
>, module_def_id
: DefId
) {
354 tcx
.hir().visit_item_likes_in_module(
356 &mut CheckAttrVisitor { tcx }
.as_deep_visitor()
360 pub(crate) fn provide(providers
: &mut Providers
<'_
>) {
361 *providers
= Providers
{