]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/check_attr.rs
Ensure StorageDead is created even if variable initialization fails
[rust.git] / src / librustc / hir / check_attr.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This module implements some validity checks for attributes.
12 //! In particular it verifies that `#[inline]` and `#[repr]` attributes are
13 //! attached to items that actually support them and if there are
14 //! conflicts between multiple such attributes attached to the same
15 //! item.
16
17 use syntax_pos::Span;
18 use ty::TyCtxt;
19
20 use hir;
21 use hir::intravisit::{self, Visitor, NestedVisitorMap};
22
23 #[derive(Copy, Clone, PartialEq)]
24 enum Target {
25     Fn,
26     Struct,
27     Union,
28     Enum,
29     Const,
30     ForeignMod,
31     Expression,
32     Statement,
33     Closure,
34     Static,
35     Other,
36 }
37
38 impl Target {
39     fn from_item(item: &hir::Item) -> Target {
40         match item.node {
41             hir::ItemFn(..) => Target::Fn,
42             hir::ItemStruct(..) => Target::Struct,
43             hir::ItemUnion(..) => Target::Union,
44             hir::ItemEnum(..) => Target::Enum,
45             hir::ItemConst(..) => Target::Const,
46             hir::ItemForeignMod(..) => Target::ForeignMod,
47             hir::ItemStatic(..) => Target::Static,
48             _ => Target::Other,
49         }
50     }
51 }
52
53 struct CheckAttrVisitor<'a, 'tcx: 'a> {
54     tcx: TyCtxt<'a, 'tcx, 'tcx>,
55 }
56
57 impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
58     /// Check any attribute.
59     fn check_attributes(&self, item: &hir::Item, target: Target) {
60         if target == Target::Fn {
61             self.tcx.codegen_fn_attrs(self.tcx.hir.local_def_id(item.id));
62         } else if let Some(a) = item.attrs.iter().find(|a| a.check_name("target_feature")) {
63             self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
64                 .span_label(item.span, "not a function")
65                 .emit();
66         }
67
68         let mut has_wasm_import_module = false;
69         for attr in &item.attrs {
70             if attr.check_name("inline") {
71                 self.check_inline(attr, &item.span, target)
72             } else if attr.check_name("non_exhaustive") {
73                 self.check_non_exhaustive(attr, item, target)
74             } else if attr.check_name("wasm_import_module") {
75                 has_wasm_import_module = true;
76                 if attr.value_str().is_none() {
77                     self.tcx.sess.span_err(attr.span, "\
78                         must be of the form #[wasm_import_module = \"...\"]");
79                 }
80                 if target != Target::ForeignMod {
81                     self.tcx.sess.span_err(attr.span, "\
82                         must only be attached to foreign modules");
83                 }
84             } else if attr.check_name("wasm_custom_section") {
85                 if target != Target::Const {
86                     self.tcx.sess.span_err(attr.span, "only allowed on consts");
87                 }
88
89                 if attr.value_str().is_none() {
90                     self.tcx.sess.span_err(attr.span, "must be of the form \
91                         #[wasm_custom_section = \"foo\"]");
92                 }
93             }
94         }
95
96         if target == Target::ForeignMod &&
97             !has_wasm_import_module &&
98             self.tcx.sess.target.target.arch == "wasm32" &&
99             false // FIXME: eventually enable this warning when stable
100         {
101             self.tcx.sess.span_warn(item.span, "\
102                 must have a #[wasm_import_module = \"...\"] attribute, this \
103                 will become a hard error before too long");
104         }
105
106         self.check_repr(item, target);
107         self.check_used(item, target);
108     }
109
110     /// Check if an `#[inline]` is applied to a function or a closure.
111     fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
112         if target != Target::Fn && target != Target::Closure {
113             struct_span_err!(self.tcx.sess,
114                              attr.span,
115                              E0518,
116                              "attribute should be applied to function or closure")
117                 .span_label(*span, "not a function or closure")
118                 .emit();
119         }
120     }
121
122     /// Check if the `#[non_exhaustive]` attribute on an `item` is valid.
123     fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
124         match target {
125             Target::Struct | Target::Enum => { /* Valid */ },
126             _ => {
127                 struct_span_err!(self.tcx.sess,
128                                  attr.span,
129                                  E0701,
130                                  "attribute can only be applied to a struct or enum")
131                     .span_label(item.span, "not a struct or enum")
132                     .emit();
133                 return;
134             }
135         }
136
137         if attr.meta_item_list().is_some() || attr.value_str().is_some() {
138             struct_span_err!(self.tcx.sess,
139                              attr.span,
140                              E0702,
141                              "attribute should be empty")
142                 .span_label(item.span, "not empty")
143                 .emit();
144         }
145     }
146
147     /// Check if the `#[repr]` attributes on `item` are valid.
148     fn check_repr(&self, item: &hir::Item, target: Target) {
149         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
150         // ```
151         // #[repr(foo)]
152         // #[repr(bar, align(8))]
153         // ```
154         let hints: Vec<_> = item.attrs
155             .iter()
156             .filter(|attr| attr.name() == "repr")
157             .filter_map(|attr| attr.meta_item_list())
158             .flat_map(|hints| hints)
159             .collect();
160
161         let mut int_reprs = 0;
162         let mut is_c = false;
163         let mut is_simd = false;
164         let mut is_transparent = false;
165
166         for hint in &hints {
167             let name = if let Some(name) = hint.name() {
168                 name
169             } else {
170                 // Invalid repr hint like repr(42). We don't check for unrecognized hints here
171                 // (libsyntax does that), so just ignore it.
172                 continue;
173             };
174
175             let (article, allowed_targets) = match &*name.as_str() {
176                 "C" => {
177                     is_c = true;
178                     if target != Target::Struct &&
179                             target != Target::Union &&
180                             target != Target::Enum {
181                                 ("a", "struct, enum or union")
182                     } else {
183                         continue
184                     }
185                 }
186                 "packed" => {
187                     if target != Target::Struct &&
188                             target != Target::Union {
189                                 ("a", "struct or union")
190                     } else {
191                         continue
192                     }
193                 }
194                 "simd" => {
195                     is_simd = true;
196                     if target != Target::Struct {
197                         ("a", "struct")
198                     } else {
199                         continue
200                     }
201                 }
202                 "align" => {
203                     if target != Target::Struct &&
204                             target != Target::Union {
205                         ("a", "struct or union")
206                     } else {
207                         continue
208                     }
209                 }
210                 "transparent" => {
211                     is_transparent = true;
212                     if target != Target::Struct {
213                         ("a", "struct")
214                     } else {
215                         continue
216                     }
217                 }
218                 "i8" | "u8" | "i16" | "u16" |
219                 "i32" | "u32" | "i64" | "u64" |
220                 "isize" | "usize" => {
221                     int_reprs += 1;
222                     if target != Target::Enum {
223                         ("an", "enum")
224                     } else {
225                         continue
226                     }
227                 }
228                 _ => continue,
229             };
230             self.emit_repr_error(
231                 hint.span,
232                 item.span,
233                 &format!("attribute should be applied to {}", allowed_targets),
234                 &format!("not {} {}", article, allowed_targets),
235             )
236         }
237
238         // Just point at all repr hints if there are any incompatibilities.
239         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
240         let hint_spans = hints.iter().map(|hint| hint.span);
241
242         // Error on repr(transparent, <anything else>).
243         if is_transparent && hints.len() > 1 {
244             let hint_spans: Vec<_> = hint_spans.clone().collect();
245             span_err!(self.tcx.sess, hint_spans, E0692,
246                       "transparent struct cannot have other repr hints");
247         }
248         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
249         if (int_reprs > 1)
250            || (is_simd && is_c)
251            || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
252             let hint_spans: Vec<_> = hint_spans.collect();
253             span_warn!(self.tcx.sess, hint_spans, E0566,
254                        "conflicting representation hints");
255         }
256     }
257
258     fn emit_repr_error(
259         &self,
260         hint_span: Span,
261         label_span: Span,
262         hint_message: &str,
263         label_message: &str,
264     ) {
265         struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
266             .span_label(label_span, label_message)
267             .emit();
268     }
269
270     fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
271         // When checking statements ignore expressions, they will be checked later
272         if let hir::Stmt_::StmtDecl(_, _) = stmt.node {
273             for attr in stmt.node.attrs() {
274                 if attr.check_name("inline") {
275                     self.check_inline(attr, &stmt.span, Target::Statement);
276                 }
277                 if attr.check_name("repr") {
278                     self.emit_repr_error(
279                         attr.span,
280                         stmt.span,
281                         &format!("attribute should not be applied to a statement"),
282                         &format!("not a struct, enum or union"),
283                     );
284                 }
285             }
286         }
287     }
288
289     fn check_expr_attributes(&self, expr: &hir::Expr) {
290         let target = match expr.node {
291             hir::ExprClosure(..) => Target::Closure,
292             _ => Target::Expression,
293         };
294         for attr in expr.attrs.iter() {
295             if attr.check_name("inline") {
296                 self.check_inline(attr, &expr.span, target);
297             }
298             if attr.check_name("repr") {
299                 self.emit_repr_error(
300                     attr.span,
301                     expr.span,
302                     &format!("attribute should not be applied to an expression"),
303                     &format!("not defining a struct, enum or union"),
304                 );
305             }
306         }
307     }
308
309     fn check_used(&self, item: &hir::Item, target: Target) {
310         for attr in &item.attrs {
311             if attr.name() == "used" && target != Target::Static {
312                 self.tcx.sess
313                     .span_err(attr.span, "attribute must be applied to a `static` variable");
314             }
315         }
316     }
317 }
318
319 impl<'a, 'tcx> Visitor<'tcx> for CheckAttrVisitor<'a, 'tcx> {
320     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
321         NestedVisitorMap::OnlyBodies(&self.tcx.hir)
322     }
323
324     fn visit_item(&mut self, item: &'tcx hir::Item) {
325         let target = Target::from_item(item);
326         self.check_attributes(item, target);
327         intravisit::walk_item(self, item)
328     }
329
330
331     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
332         self.check_stmt_attributes(stmt);
333         intravisit::walk_stmt(self, stmt)
334     }
335
336     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
337         self.check_expr_attributes(expr);
338         intravisit::walk_expr(self, expr)
339     }
340 }
341
342 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
343     let mut checker = CheckAttrVisitor { tcx };
344     tcx.hir.krate().visit_all_item_likes(&mut checker.as_deep_visitor());
345 }
346
347 fn is_c_like_enum(item: &hir::Item) -> bool {
348     if let hir::ItemEnum(ref def, _) = item.node {
349         for variant in &def.variants {
350             match variant.node.data {
351                 hir::VariantData::Unit(_) => { /* continue */ }
352                 _ => { return false; }
353             }
354         }
355         true
356     } else {
357         false
358     }
359 }