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
9 use crate::ty::query::Providers;
12 use crate::hir::def_id::DefId;
13 use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
14 use std::fmt::{self, Display};
15 use syntax::symbol::sym;
18 #[derive(Copy, Clone, PartialEq)]
19 pub(crate) enum Target {
41 impl Display for Target {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 write!(f, "{}", match *self {
44 Target::ExternCrate => "extern crate",
46 Target::Static => "static item",
47 Target::Const => "constant item",
48 Target::Fn => "function",
49 Target::Closure => "closure",
50 Target::Mod => "module",
51 Target::ForeignMod => "foreign module",
52 Target::GlobalAsm => "global asm",
53 Target::TyAlias => "type alias",
54 Target::OpaqueTy => "opaque type",
55 Target::Enum => "enum",
56 Target::Struct => "struct",
57 Target::Union => "union",
58 Target::Trait => "trait",
59 Target::TraitAlias => "trait alias",
60 Target::Impl => "item",
61 Target::Expression => "expression",
62 Target::Statement => "statement",
68 pub(crate) fn from_item(item: &hir::Item) -> Target {
70 hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
71 hir::ItemKind::Use(..) => Target::Use,
72 hir::ItemKind::Static(..) => Target::Static,
73 hir::ItemKind::Const(..) => Target::Const,
74 hir::ItemKind::Fn(..) => Target::Fn,
75 hir::ItemKind::Mod(..) => Target::Mod,
76 hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
77 hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
78 hir::ItemKind::TyAlias(..) => Target::TyAlias,
79 hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
80 hir::ItemKind::Enum(..) => Target::Enum,
81 hir::ItemKind::Struct(..) => Target::Struct,
82 hir::ItemKind::Union(..) => Target::Union,
83 hir::ItemKind::Trait(..) => Target::Trait,
84 hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
85 hir::ItemKind::Impl(..) => Target::Impl,
90 struct CheckAttrVisitor<'tcx> {
94 impl CheckAttrVisitor<'tcx> {
95 /// Checks any attribute.
96 fn check_attributes(&self, item: &hir::Item, target: Target) {
97 if target == Target::Fn || target == Target::Const {
98 self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
99 } else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) {
100 self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
101 .span_label(item.span, "not a function")
105 for attr in &item.attrs {
106 if attr.check_name(sym::inline) {
107 self.check_inline(attr, &item.span, target)
108 } else if attr.check_name(sym::non_exhaustive) {
109 self.check_non_exhaustive(attr, item, target)
110 } else if attr.check_name(sym::marker) {
111 self.check_marker(attr, item, target)
115 self.check_repr(item, target);
116 self.check_used(item, target);
119 /// Checks if an `#[inline]` is applied to a function or a closure.
120 fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
121 if target != Target::Fn && target != Target::Closure {
122 struct_span_err!(self.tcx.sess,
125 "attribute should be applied to function or closure")
126 .span_label(*span, "not a function or closure")
131 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
132 fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
134 Target::Struct | Target::Enum => { /* Valid */ },
136 struct_span_err!(self.tcx.sess,
139 "attribute can only be applied to a struct or enum")
140 .span_label(item.span, "not a struct or enum")
147 /// Checks if the `#[marker]` attribute on an `item` is valid.
148 fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
150 Target::Trait => { /* Valid */ },
153 .struct_span_err(attr.span, "attribute can only be applied to a trait")
154 .span_label(item.span, "not a trait")
161 /// Checks if the `#[repr]` attributes on `item` are valid.
162 fn check_repr(&self, item: &hir::Item, target: Target) {
163 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
166 // #[repr(bar, align(8))]
168 let hints: Vec<_> = item.attrs
170 .filter(|attr| attr.check_name(sym::repr))
171 .filter_map(|attr| attr.meta_item_list())
175 let mut int_reprs = 0;
176 let mut is_c = false;
177 let mut is_simd = false;
178 let mut is_transparent = false;
181 let (article, allowed_targets) = match hint.name_or_empty() {
182 name @ sym::C | name @ sym::align => {
183 is_c |= name == sym::C;
185 Target::Struct | Target::Union | Target::Enum => continue,
186 _ => ("a", "struct, enum, or union"),
190 if target != Target::Struct &&
191 target != Target::Union {
192 ("a", "struct or union")
199 if target != Target::Struct {
205 sym::transparent => {
206 is_transparent = true;
208 Target::Struct | Target::Union | Target::Enum => continue,
209 _ => ("a", "struct, enum, or union"),
212 sym::i8 | sym::u8 | sym::i16 | sym::u16 |
213 sym::i32 | sym::u32 | sym::i64 | sym::u64 |
214 sym::isize | sym::usize => {
216 if target != Target::Enum {
224 self.emit_repr_error(
227 &format!("attribute should be applied to {}", allowed_targets),
228 &format!("not {} {}", article, allowed_targets),
232 // Just point at all repr hints if there are any incompatibilities.
233 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
234 let hint_spans = hints.iter().map(|hint| hint.span());
236 // Error on repr(transparent, <anything else>).
237 if is_transparent && hints.len() > 1 {
238 let hint_spans: Vec<_> = hint_spans.clone().collect();
239 span_err!(self.tcx.sess, hint_spans, E0692,
240 "transparent {} cannot have other repr hints", target);
242 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
245 || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
246 let hint_spans: Vec<_> = hint_spans.collect();
247 span_warn!(self.tcx.sess, hint_spans, E0566,
248 "conflicting representation hints");
259 struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
260 .span_label(label_span, label_message)
264 fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
265 // When checking statements ignore expressions, they will be checked later
266 if let hir::StmtKind::Local(ref l) = stmt.node {
267 for attr in l.attrs.iter() {
268 if attr.check_name(sym::inline) {
269 self.check_inline(attr, &stmt.span, Target::Statement);
271 if attr.check_name(sym::repr) {
272 self.emit_repr_error(
275 "attribute should not be applied to a statement",
276 "not a struct, enum, or union",
283 fn check_expr_attributes(&self, expr: &hir::Expr) {
284 let target = match expr.node {
285 hir::ExprKind::Closure(..) => Target::Closure,
286 _ => Target::Expression,
288 for attr in expr.attrs.iter() {
289 if attr.check_name(sym::inline) {
290 self.check_inline(attr, &expr.span, target);
292 if attr.check_name(sym::repr) {
293 self.emit_repr_error(
296 "attribute should not be applied to an expression",
297 "not defining a struct, enum, or union",
303 fn check_used(&self, item: &hir::Item, target: Target) {
304 for attr in &item.attrs {
305 if attr.check_name(sym::used) && target != Target::Static {
307 .span_err(attr.span, "attribute must be applied to a `static` variable");
313 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
314 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
315 NestedVisitorMap::OnlyBodies(&self.tcx.hir())
318 fn visit_item(&mut self, item: &'tcx hir::Item) {
319 let target = Target::from_item(item);
320 self.check_attributes(item, target);
321 intravisit::walk_item(self, item)
325 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
326 self.check_stmt_attributes(stmt);
327 intravisit::walk_stmt(self, stmt)
330 fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
331 self.check_expr_attributes(expr);
332 intravisit::walk_expr(self, expr)
336 fn is_c_like_enum(item: &hir::Item) -> bool {
337 if let hir::ItemKind::Enum(ref def, _) = item.node {
338 for variant in &def.variants {
340 hir::VariantData::Unit(..) => { /* continue */ }
341 _ => { return false; }
350 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
351 tcx.hir().visit_item_likes_in_module(
353 &mut CheckAttrVisitor { tcx }.as_deep_visitor()
357 pub(crate) fn provide(providers: &mut Providers<'_>) {
358 *providers = Providers {