]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/check_attr.rs
Skip checking for unused mutable locals that have no name
[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.trans_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                                  E0910,
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                              E0911,
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| match attr.name() {
157                 Some(name) => name == "repr",
158                 None => false,
159             })
160             .filter_map(|attr| attr.meta_item_list())
161             .flat_map(|hints| hints)
162             .collect();
163
164         let mut int_reprs = 0;
165         let mut is_c = false;
166         let mut is_simd = false;
167         let mut is_transparent = false;
168
169         for hint in &hints {
170             let name = if let Some(name) = hint.name() {
171                 name
172             } else {
173                 // Invalid repr hint like repr(42). We don't check for unrecognized hints here
174                 // (libsyntax does that), so just ignore it.
175                 continue;
176             };
177
178             let (article, allowed_targets) = match &*name.as_str() {
179                 "C" => {
180                     is_c = true;
181                     if target != Target::Struct &&
182                             target != Target::Union &&
183                             target != Target::Enum {
184                                 ("a", "struct, enum or union")
185                     } else {
186                         continue
187                     }
188                 }
189                 "packed" => {
190                     if target != Target::Struct &&
191                             target != Target::Union {
192                                 ("a", "struct or union")
193                     } else {
194                         continue
195                     }
196                 }
197                 "simd" => {
198                     is_simd = true;
199                     if target != Target::Struct {
200                         ("a", "struct")
201                     } else {
202                         continue
203                     }
204                 }
205                 "align" => {
206                     if target != Target::Struct &&
207                             target != Target::Union {
208                         ("a", "struct or union")
209                     } else {
210                         continue
211                     }
212                 }
213                 "transparent" => {
214                     is_transparent = true;
215                     if target != Target::Struct {
216                         ("a", "struct")
217                     } else {
218                         continue
219                     }
220                 }
221                 "i8" | "u8" | "i16" | "u16" |
222                 "i32" | "u32" | "i64" | "u64" |
223                 "isize" | "usize" => {
224                     int_reprs += 1;
225                     if target != Target::Enum {
226                         ("an", "enum")
227                     } else {
228                         continue
229                     }
230                 }
231                 _ => continue,
232             };
233             self.emit_repr_error(
234                 hint.span,
235                 item.span,
236                 &format!("attribute should be applied to {}", allowed_targets),
237                 &format!("not {} {}", article, allowed_targets),
238             )
239         }
240
241         // Just point at all repr hints if there are any incompatibilities.
242         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
243         let hint_spans = hints.iter().map(|hint| hint.span);
244
245         // Error on repr(transparent, <anything else>).
246         if is_transparent && hints.len() > 1 {
247             let hint_spans: Vec<_> = hint_spans.clone().collect();
248             span_err!(self.tcx.sess, hint_spans, E0692,
249                       "transparent struct cannot have other repr hints");
250         }
251         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
252         if (int_reprs > 1)
253            || (is_simd && is_c)
254            || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
255             let hint_spans: Vec<_> = hint_spans.collect();
256             span_warn!(self.tcx.sess, hint_spans, E0566,
257                        "conflicting representation hints");
258         }
259     }
260
261     fn emit_repr_error(
262         &self,
263         hint_span: Span,
264         label_span: Span,
265         hint_message: &str,
266         label_message: &str,
267     ) {
268         struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
269             .span_label(label_span, label_message)
270             .emit();
271     }
272
273     fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
274         // When checking statements ignore expressions, they will be checked later
275         if let hir::Stmt_::StmtDecl(_, _) = stmt.node {
276             for attr in stmt.node.attrs() {
277                 if attr.check_name("inline") {
278                     self.check_inline(attr, &stmt.span, Target::Statement);
279                 }
280                 if attr.check_name("repr") {
281                     self.emit_repr_error(
282                         attr.span,
283                         stmt.span,
284                         &format!("attribute should not be applied to a statement"),
285                         &format!("not a struct, enum or union"),
286                     );
287                 }
288             }
289         }
290     }
291
292     fn check_expr_attributes(&self, expr: &hir::Expr) {
293         let target = match expr.node {
294             hir::ExprClosure(..) => Target::Closure,
295             _ => Target::Expression,
296         };
297         for attr in expr.attrs.iter() {
298             if attr.check_name("inline") {
299                 self.check_inline(attr, &expr.span, target);
300             }
301             if attr.check_name("repr") {
302                 self.emit_repr_error(
303                     attr.span,
304                     expr.span,
305                     &format!("attribute should not be applied to an expression"),
306                     &format!("not defining a struct, enum or union"),
307                 );
308             }
309         }
310     }
311
312     fn check_used(&self, item: &hir::Item, target: Target) {
313         for attr in &item.attrs {
314             if attr.name().map(|name| name == "used").unwrap_or(false) && target != Target::Static {
315                 self.tcx.sess
316                     .span_err(attr.span, "attribute must be applied to a `static` variable");
317             }
318         }
319     }
320 }
321
322 impl<'a, 'tcx> Visitor<'tcx> for CheckAttrVisitor<'a, 'tcx> {
323     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
324         NestedVisitorMap::OnlyBodies(&self.tcx.hir)
325     }
326
327     fn visit_item(&mut self, item: &'tcx hir::Item) {
328         let target = Target::from_item(item);
329         self.check_attributes(item, target);
330         intravisit::walk_item(self, item)
331     }
332
333
334     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
335         self.check_stmt_attributes(stmt);
336         intravisit::walk_stmt(self, stmt)
337     }
338
339     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
340         self.check_expr_attributes(expr);
341         intravisit::walk_expr(self, expr)
342     }
343 }
344
345 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
346     let mut checker = CheckAttrVisitor { tcx };
347     tcx.hir.krate().visit_all_item_likes(&mut checker.as_deep_visitor());
348 }
349
350 fn is_c_like_enum(item: &hir::Item) -> bool {
351     if let hir::ItemEnum(ref def, _) = item.node {
352         for variant in &def.variants {
353             match variant.node.data {
354                 hir::VariantData::Unit(_) => { /* continue */ }
355                 _ => { return false; }
356             }
357         }
358         true
359     } else {
360         false
361     }
362 }