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.
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.
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
20 use hir::intravisit::{self, Visitor, NestedVisitorMap};
22 #[derive(Copy, Clone, PartialEq)]
32 fn from_item(item: &hir::Item) -> Target {
34 hir::ItemFn(..) => Target::Fn,
35 hir::ItemStruct(..) => Target::Struct,
36 hir::ItemUnion(..) => Target::Union,
37 hir::ItemEnum(..) => Target::Enum,
43 struct CheckAttrVisitor<'a, 'tcx: 'a> {
44 tcx: TyCtxt<'a, 'tcx, 'tcx>,
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));
52 for attr in &item.attrs {
53 if let Some(name) = attr.name() {
55 self.check_inline(attr, item, target)
60 self.check_repr(item, target);
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,
69 "attribute should be applied to function")
70 .span_label(item.span, "not a function")
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:
80 // #[repr(bar, align(8))]
82 let hints: Vec<_> = item.attrs
84 .filter(|attr| match attr.name() {
85 Some(name) => name == "repr",
88 .filter_map(|attr| attr.meta_item_list())
89 .flat_map(|hints| hints)
92 let mut int_reprs = 0;
94 let mut is_simd = false;
97 let name = if let Some(name) = hint.name() {
100 // Invalid repr hint like repr(42). We don't check for unrecognized hints here
101 // (libsyntax does that), so just ignore it.
105 let (article, allowed_targets) = match &*name.as_str() {
108 if target != Target::Struct &&
109 target != Target::Union &&
110 target != Target::Enum {
111 ("a", "struct, enum or union")
117 if target != Target::Struct &&
118 target != Target::Union {
119 ("a", "struct or union")
126 if target != Target::Struct {
133 if target != Target::Struct &&
134 target != Target::Union {
135 ("a", "struct or union")
140 "i8" | "u8" | "i16" | "u16" |
141 "i32" | "u32" | "i64" | "u64" |
142 "isize" | "usize" => {
144 if target != Target::Enum {
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))
158 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
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");
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
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);
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());
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; }