]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/check_attr.rs
Merge pull request #2 from Wind-River/bpang-runtest
[rust.git] / src / librustc / hir / check_attr.rs
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
5 //! item.
6
7
8 use crate::ty::TyCtxt;
9 use crate::ty::query::Providers;
10
11 use crate::hir;
12 use crate::hir::def_id::DefId;
13 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
14 use std::fmt::{self, Display};
15 use syntax::symbol::sym;
16 use syntax_pos::Span;
17
18 #[derive(Copy, Clone, PartialEq)]
19 pub(crate) enum Target {
20     ExternCrate,
21     Use,
22     Static,
23     Const,
24     Fn,
25     Closure,
26     Mod,
27     ForeignMod,
28     GlobalAsm,
29     TyAlias,
30     OpaqueTy,
31     Enum,
32     Struct,
33     Union,
34     Trait,
35     TraitAlias,
36     Impl,
37     Expression,
38     Statement,
39 }
40
41 impl Display for Target {
42     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43         write!(f, "{}", match *self {
44             Target::ExternCrate => "extern crate",
45             Target::Use => "use",
46             Target::Static => "static item",
47             Target::Const => "constant item",
48             Target::Fn => "function",
49             Target::Closure => "closure",
50             Target::Mod => "module",
51             Target::ForeignMod => "foreign module",
52             Target::GlobalAsm => "global asm",
53             Target::TyAlias => "type alias",
54             Target::OpaqueTy => "opaque type",
55             Target::Enum => "enum",
56             Target::Struct => "struct",
57             Target::Union => "union",
58             Target::Trait => "trait",
59             Target::TraitAlias => "trait alias",
60             Target::Impl => "item",
61             Target::Expression => "expression",
62             Target::Statement => "statement",
63         })
64     }
65 }
66
67 impl Target {
68     pub(crate) fn from_item(item: &hir::Item) -> Target {
69         match item.node {
70             hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
71             hir::ItemKind::Use(..) => Target::Use,
72             hir::ItemKind::Static(..) => Target::Static,
73             hir::ItemKind::Const(..) => Target::Const,
74             hir::ItemKind::Fn(..) => Target::Fn,
75             hir::ItemKind::Mod(..) => Target::Mod,
76             hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
77             hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
78             hir::ItemKind::TyAlias(..) => Target::TyAlias,
79             hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
80             hir::ItemKind::Enum(..) => Target::Enum,
81             hir::ItemKind::Struct(..) => Target::Struct,
82             hir::ItemKind::Union(..) => Target::Union,
83             hir::ItemKind::Trait(..) => Target::Trait,
84             hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
85             hir::ItemKind::Impl(..) => Target::Impl,
86         }
87     }
88 }
89
90 struct CheckAttrVisitor<'tcx> {
91     tcx: TyCtxt<'tcx>,
92 }
93
94 impl CheckAttrVisitor<'tcx> {
95     /// Checks any attribute.
96     fn check_attributes(&self, item: &hir::Item, target: Target) {
97         if target == Target::Fn || target == Target::Const {
98             self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
99         } else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) {
100             self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
101                 .span_label(item.span, "not a function")
102                 .emit();
103         }
104
105         for attr in &item.attrs {
106             if attr.check_name(sym::inline) {
107                 self.check_inline(attr, &item.span, target)
108             } else if attr.check_name(sym::non_exhaustive) {
109                 self.check_non_exhaustive(attr, item, target)
110             } else if attr.check_name(sym::marker) {
111                 self.check_marker(attr, item, target)
112             }
113         }
114
115         self.check_repr(item, target);
116         self.check_used(item, target);
117     }
118
119     /// Checks if an `#[inline]` is applied to a function or a closure.
120     fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
121         if target != Target::Fn && target != Target::Closure {
122             struct_span_err!(self.tcx.sess,
123                              attr.span,
124                              E0518,
125                              "attribute should be applied to function or closure")
126                 .span_label(*span, "not a function or closure")
127                 .emit();
128         }
129     }
130
131     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
132     fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
133         match target {
134             Target::Struct | Target::Enum => { /* Valid */ },
135             _ => {
136                 struct_span_err!(self.tcx.sess,
137                                  attr.span,
138                                  E0701,
139                                  "attribute can only be applied to a struct or enum")
140                     .span_label(item.span, "not a struct or enum")
141                     .emit();
142                 return;
143             }
144         }
145     }
146
147     /// Checks if the `#[marker]` attribute on an `item` is valid.
148     fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
149         match target {
150             Target::Trait => { /* Valid */ },
151             _ => {
152                 self.tcx.sess
153                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
154                     .span_label(item.span, "not a trait")
155                     .emit();
156                 return;
157             }
158         }
159     }
160
161     /// Checks if the `#[repr]` attributes on `item` are valid.
162     fn check_repr(&self, item: &hir::Item, target: Target) {
163         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
164         // ```
165         // #[repr(foo)]
166         // #[repr(bar, align(8))]
167         // ```
168         let hints: Vec<_> = item.attrs
169             .iter()
170             .filter(|attr| attr.check_name(sym::repr))
171             .filter_map(|attr| attr.meta_item_list())
172             .flatten()
173             .collect();
174
175         let mut int_reprs = 0;
176         let mut is_c = false;
177         let mut is_simd = false;
178         let mut is_transparent = false;
179
180         for hint in &hints {
181             let (article, allowed_targets) = match hint.name_or_empty() {
182                 name @ sym::C | name @ sym::align => {
183                     is_c |= name == sym::C;
184                     match target {
185                         Target::Struct | Target::Union | Target::Enum => continue,
186                         _ => ("a", "struct, enum, or union"),
187                     }
188                 }
189                 sym::packed => {
190                     if target != Target::Struct &&
191                             target != Target::Union {
192                                 ("a", "struct or union")
193                     } else {
194                         continue
195                     }
196                 }
197                 sym::simd => {
198                     is_simd = true;
199                     if target != Target::Struct {
200                         ("a", "struct")
201                     } else {
202                         continue
203                     }
204                 }
205                 sym::transparent => {
206                     is_transparent = true;
207                     match target {
208                         Target::Struct | Target::Union | Target::Enum => continue,
209                         _ => ("a", "struct, enum, or union"),
210                     }
211                 }
212                 sym::i8  | sym::u8  | sym::i16 | sym::u16 |
213                 sym::i32 | sym::u32 | sym::i64 | sym::u64 |
214                 sym::isize | sym::usize => {
215                     int_reprs += 1;
216                     if target != Target::Enum {
217                         ("an", "enum")
218                     } else {
219                         continue
220                     }
221                 }
222                 _ => continue,
223             };
224             self.emit_repr_error(
225                 hint.span(),
226                 item.span,
227                 &format!("attribute should be applied to {}", allowed_targets),
228                 &format!("not {} {}", article, allowed_targets),
229             )
230         }
231
232         // Just point at all repr hints if there are any incompatibilities.
233         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
234         let hint_spans = hints.iter().map(|hint| hint.span());
235
236         // Error on repr(transparent, <anything else>).
237         if is_transparent && hints.len() > 1 {
238             let hint_spans: Vec<_> = hint_spans.clone().collect();
239             span_err!(self.tcx.sess, hint_spans, E0692,
240                       "transparent {} cannot have other repr hints", target);
241         }
242         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
243         if (int_reprs > 1)
244            || (is_simd && is_c)
245            || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
246             let hint_spans: Vec<_> = hint_spans.collect();
247             span_warn!(self.tcx.sess, hint_spans, E0566,
248                        "conflicting representation hints");
249         }
250     }
251
252     fn emit_repr_error(
253         &self,
254         hint_span: Span,
255         label_span: Span,
256         hint_message: &str,
257         label_message: &str,
258     ) {
259         struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
260             .span_label(label_span, label_message)
261             .emit();
262     }
263
264     fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
265         // When checking statements ignore expressions, they will be checked later
266         if let hir::StmtKind::Local(ref l) = stmt.node {
267             for attr in l.attrs.iter() {
268                 if attr.check_name(sym::inline) {
269                     self.check_inline(attr, &stmt.span, Target::Statement);
270                 }
271                 if attr.check_name(sym::repr) {
272                     self.emit_repr_error(
273                         attr.span,
274                         stmt.span,
275                         "attribute should not be applied to a statement",
276                         "not a struct, enum, or union",
277                     );
278                 }
279             }
280         }
281     }
282
283     fn check_expr_attributes(&self, expr: &hir::Expr) {
284         let target = match expr.node {
285             hir::ExprKind::Closure(..) => Target::Closure,
286             _ => Target::Expression,
287         };
288         for attr in expr.attrs.iter() {
289             if attr.check_name(sym::inline) {
290                 self.check_inline(attr, &expr.span, target);
291             }
292             if attr.check_name(sym::repr) {
293                 self.emit_repr_error(
294                     attr.span,
295                     expr.span,
296                     "attribute should not be applied to an expression",
297                     "not defining a struct, enum, or union",
298                 );
299             }
300         }
301     }
302
303     fn check_used(&self, item: &hir::Item, target: Target) {
304         for attr in &item.attrs {
305             if attr.check_name(sym::used) && target != Target::Static {
306                 self.tcx.sess
307                     .span_err(attr.span, "attribute must be applied to a `static` variable");
308             }
309         }
310     }
311 }
312
313 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
314     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
315         NestedVisitorMap::OnlyBodies(&self.tcx.hir())
316     }
317
318     fn visit_item(&mut self, item: &'tcx hir::Item) {
319         let target = Target::from_item(item);
320         self.check_attributes(item, target);
321         intravisit::walk_item(self, item)
322     }
323
324
325     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
326         self.check_stmt_attributes(stmt);
327         intravisit::walk_stmt(self, stmt)
328     }
329
330     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
331         self.check_expr_attributes(expr);
332         intravisit::walk_expr(self, expr)
333     }
334 }
335
336 fn is_c_like_enum(item: &hir::Item) -> bool {
337     if let hir::ItemKind::Enum(ref def, _) = item.node {
338         for variant in &def.variants {
339             match variant.data {
340                 hir::VariantData::Unit(..) => { /* continue */ }
341                 _ => { return false; }
342             }
343         }
344         true
345     } else {
346         false
347     }
348 }
349
350 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
351     tcx.hir().visit_item_likes_in_module(
352         module_def_id,
353         &mut CheckAttrVisitor { tcx }.as_deep_visitor()
354     );
355 }
356
357 pub(crate) fn provide(providers: &mut Providers<'_>) {
358     *providers = Providers {
359         check_mod_attrs,
360         ..*providers
361     };
362 }