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