]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/check_attr.rs
Rollup merge of #47277 - tspiteri:log-correctness, r=frewsxcv
[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 ty::TyCtxt;
18
19 use hir;
20 use hir::intravisit::{self, Visitor, NestedVisitorMap};
21
22 #[derive(Copy, Clone, PartialEq)]
23 enum Target {
24     Fn,
25     Struct,
26     Union,
27     Enum,
28     Other,
29 }
30
31 impl Target {
32     fn from_item(item: &hir::Item) -> Target {
33         match item.node {
34             hir::ItemFn(..) => Target::Fn,
35             hir::ItemStruct(..) => Target::Struct,
36             hir::ItemUnion(..) => Target::Union,
37             hir::ItemEnum(..) => Target::Enum,
38             _ => Target::Other,
39         }
40     }
41 }
42
43 struct CheckAttrVisitor<'a, 'tcx: 'a> {
44     tcx: TyCtxt<'a, 'tcx, 'tcx>,
45 }
46
47 impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
48     /// Check any attribute.
49     fn check_attributes(&self, item: &hir::Item, target: Target) {
50         self.tcx.target_features_enabled(self.tcx.hir.local_def_id(item.id));
51
52         for attr in &item.attrs {
53             if let Some(name) = attr.name() {
54                 if name == "inline" {
55                     self.check_inline(attr, item, target)
56                 }
57             }
58         }
59
60         self.check_repr(item, target);
61     }
62
63     /// Check if an `#[inline]` is applied to a function.
64     fn check_inline(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
65         if target != Target::Fn {
66             struct_span_err!(self.tcx.sess,
67                              attr.span,
68                              E0518,
69                              "attribute should be applied to function")
70                 .span_label(item.span, "not a function")
71                 .emit();
72         }
73     }
74
75     /// Check if the `#[repr]` attributes on `item` are valid.
76     fn check_repr(&self, item: &hir::Item, target: Target) {
77         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
78         // ```
79         // #[repr(foo)]
80         // #[repr(bar, align(8))]
81         // ```
82         let hints: Vec<_> = item.attrs
83             .iter()
84             .filter(|attr| match attr.name() {
85                 Some(name) => name == "repr",
86                 None => false,
87             })
88             .filter_map(|attr| attr.meta_item_list())
89             .flat_map(|hints| hints)
90             .collect();
91
92         let mut int_reprs = 0;
93         let mut is_c = false;
94         let mut is_simd = false;
95
96         for hint in &hints {
97             let name = if let Some(name) = hint.name() {
98                 name
99             } else {
100                 // Invalid repr hint like repr(42). We don't check for unrecognized hints here
101                 // (libsyntax does that), so just ignore it.
102                 continue;
103             };
104
105             let (article, allowed_targets) = match &*name.as_str() {
106                 "C" => {
107                     is_c = true;
108                     if target != Target::Struct &&
109                             target != Target::Union &&
110                             target != Target::Enum {
111                                 ("a", "struct, enum or union")
112                     } else {
113                         continue
114                     }
115                 }
116                 "packed" => {
117                     if target != Target::Struct &&
118                             target != Target::Union {
119                                 ("a", "struct or union")
120                     } else {
121                         continue
122                     }
123                 }
124                 "simd" => {
125                     is_simd = true;
126                     if target != Target::Struct {
127                         ("a", "struct")
128                     } else {
129                         continue
130                     }
131                 }
132                 "align" => {
133                     if target != Target::Struct &&
134                             target != Target::Union {
135                         ("a", "struct or union")
136                     } else {
137                         continue
138                     }
139                 }
140                 "i8" | "u8" | "i16" | "u16" |
141                 "i32" | "u32" | "i64" | "u64" |
142                 "isize" | "usize" => {
143                     int_reprs += 1;
144                     if target != Target::Enum {
145                         ("an", "enum")
146                     } else {
147                         continue
148                     }
149                 }
150                 _ => continue,
151             };
152             struct_span_err!(self.tcx.sess, hint.span, E0517,
153                              "attribute should be applied to {}", allowed_targets)
154                 .span_label(item.span, format!("not {} {}", article, allowed_targets))
155                 .emit();
156         }
157
158         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
159         if (int_reprs > 1)
160            || (is_simd && is_c)
161            || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
162             // Just point at all repr hints. This is not ideal, but tracking
163             // precisely which ones are at fault is a huge hassle.
164             let spans: Vec<_> = hints.iter().map(|hint| hint.span).collect();
165             span_warn!(self.tcx.sess, spans, E0566,
166                        "conflicting representation hints");
167         }
168     }
169 }
170
171 impl<'a, 'tcx> Visitor<'tcx> for CheckAttrVisitor<'a, 'tcx> {
172     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
173         NestedVisitorMap::None
174     }
175
176     fn visit_item(&mut self, item: &'tcx hir::Item) {
177         let target = Target::from_item(item);
178         self.check_attributes(item, target);
179         intravisit::walk_item(self, item);
180     }
181 }
182
183 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
184     let mut checker = CheckAttrVisitor { tcx };
185     tcx.hir.krate().visit_all_item_likes(&mut checker.as_deep_visitor());
186 }
187
188 fn is_c_like_enum(item: &hir::Item) -> bool {
189     if let hir::ItemEnum(ref def, _) = item.node {
190         for variant in &def.variants {
191             match variant.node.data {
192                 hir::VariantData::Unit(_) => { /* continue */ }
193                 _ => { return false; }
194             }
195         }
196         true
197     } else {
198         false
199     }
200 }