1
Fork 0
rust/src/librustc/hir/check_attr.rs

362 lines
12 KiB
Rust
Raw Normal View History

2017-06-03 18:14:29 +02:00
//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.
2019-02-05 11:20:45 -06:00
use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::ty::TyCtxt;
use crate::ty::query::Providers;
use std::fmt::{self, Display};
use syntax::symbol::sym;
use syntax_pos::Span;
2015-09-25 15:25:59 +09:00
#[derive(Copy, Clone, PartialEq)]
pub(crate) enum Target {
ExternCrate,
Use,
Static,
Const,
2015-09-25 15:25:59 +09:00
Fn,
Closure,
Mod,
ForeignMod,
GlobalAsm,
TyAlias,
2019-08-01 00:41:54 +01:00
OpaqueTy,
Enum,
2015-09-25 15:25:59 +09:00
Struct,
Union,
Trait,
TraitAlias,
Impl,
Expression,
Statement,
}
impl Display for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", match *self {
Target::ExternCrate => "extern crate",
Target::Use => "use",
Target::Static => "static item",
Target::Const => "constant item",
Target::Fn => "function",
Target::Closure => "closure",
Target::Mod => "module",
Target::ForeignMod => "foreign module",
Target::GlobalAsm => "global asm",
Target::TyAlias => "type alias",
2019-08-01 00:41:54 +01:00
Target::OpaqueTy => "opaque type",
Target::Enum => "enum",
Target::Struct => "struct",
Target::Union => "union",
Target::Trait => "trait",
Target::TraitAlias => "trait alias",
Target::Impl => "item",
Target::Expression => "expression",
Target::Statement => "statement",
})
}
2015-09-25 15:25:59 +09:00
}
impl Target {
pub(crate) fn from_item(item: &hir::Item) -> Target {
2015-09-25 15:25:59 +09:00
match item.node {
hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
hir::ItemKind::Use(..) => Target::Use,
hir::ItemKind::Static(..) => Target::Static,
hir::ItemKind::Const(..) => Target::Const,
2018-07-11 23:36:06 +08:00
hir::ItemKind::Fn(..) => Target::Fn,
hir::ItemKind::Mod(..) => Target::Mod,
hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
hir::ItemKind::TyAlias(..) => Target::TyAlias,
2019-08-01 00:41:54 +01:00
hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
hir::ItemKind::Enum(..) => Target::Enum,
2018-07-11 23:36:06 +08:00
hir::ItemKind::Struct(..) => Target::Struct,
hir::ItemKind::Union(..) => Target::Union,
hir::ItemKind::Trait(..) => Target::Trait,
hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
hir::ItemKind::Impl(..) => Target::Impl,
2015-09-25 15:25:59 +09:00
}
}
}
struct CheckAttrVisitor<'tcx> {
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
2015-09-25 15:25:59 +09:00
}
impl CheckAttrVisitor<'tcx> {
2019-02-08 14:53:55 +01:00
/// Checks any attribute.
fn check_attributes(&self, item: &hir::Item, target: Target) {
Upgrade to LLVM's master branch (LLVM 7) This commit upgrades the main LLVM submodule to LLVM's current master branch. The LLD submodule is updated in tandem as well as compiler-builtins. Along the way support was also added for LLVM 7's new features. This primarily includes the support for custom section concatenation natively in LLD so we now add wasm custom sections in LLVM IR rather than having custom support in rustc itself for doing so. Some other miscellaneous changes are: * We now pass `--gc-sections` to `wasm-ld` * The optimization level is now passed to `wasm-ld` * A `--stack-first` option is passed to LLD to have stack overflow always cause a trap instead of corrupting static data * The wasm target for LLVM switched to `wasm32-unknown-unknown`. * The syntax for aligned pointers has changed in LLVM IR and tests are updated to reflect this. * The `thumbv6m-none-eabi` target is disabled due to an [LLVM bug][llbug] Nowadays we've been mostly only upgrading whenever there's a major release of LLVM but enough changes have been happening on the wasm target that there's been growing motivation for quite some time now to upgrade out version of LLD. To upgrade LLD, however, we need to upgrade LLVM to avoid needing to build yet another version of LLVM on the builders. The revision of LLVM in use here is arbitrarily chosen. We will likely need to continue to update it over time if and when we discover bugs. Once LLVM 7 is fully released we can switch to that channel as well. [llbug]: https://bugs.llvm.org/show_bug.cgi?id=37382
2018-06-01 10:20:00 -07:00
if target == Target::Fn || target == Target::Const {
self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
} else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) {
self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
.span_label(item.span, "not a function")
.emit();
}
for attr in &item.attrs {
if attr.check_name(sym::inline) {
self.check_inline(attr, &item.span, target)
} else if attr.check_name(sym::non_exhaustive) {
self.check_non_exhaustive(attr, item, target)
} else if attr.check_name(sym::marker) {
self.check_marker(attr, item, target)
2017-06-03 18:14:29 +02:00
}
}
self.check_repr(item, target);
self.check_used(item, target);
2017-06-03 18:14:29 +02:00
}
2019-02-08 14:53:55 +01:00
/// Checks if an `#[inline]` is applied to a function or a closure.
fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
if target != Target::Fn && target != Target::Closure {
struct_span_err!(self.tcx.sess,
attr.span,
E0518,
"attribute should be applied to function or closure")
.span_label(*span, "not a function or closure")
.emit();
2015-09-25 15:25:59 +09:00
}
}
2019-02-08 14:53:55 +01:00
/// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
match target {
Target::Struct | Target::Enum => { /* Valid */ },
_ => {
struct_span_err!(self.tcx.sess,
attr.span,
2018-06-10 14:04:48 +02:00
E0701,
"attribute can only be applied to a struct or enum")
.span_label(item.span, "not a struct or enum")
.emit();
return;
}
}
}
2019-02-08 14:53:55 +01:00
/// Checks if the `#[marker]` attribute on an `item` is valid.
fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
match target {
Target::Trait => { /* Valid */ },
_ => {
self.tcx.sess
.struct_span_err(attr.span, "attribute can only be applied to a trait")
.span_label(item.span, "not a trait")
.emit();
return;
}
}
}
2019-02-08 14:53:55 +01:00
/// Checks if the `#[repr]` attributes on `item` are valid.
fn check_repr(&self, item: &hir::Item, target: Target) {
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
// ```
// #[repr(foo)]
// #[repr(bar, align(8))]
// ```
let hints: Vec<_> = item.attrs
.iter()
.filter(|attr| attr.check_name(sym::repr))
.filter_map(|attr| attr.meta_item_list())
2018-09-12 12:31:11 +02:00
.flatten()
.collect();
let mut int_reprs = 0;
let mut is_c = false;
let mut is_simd = false;
2018-01-03 17:43:30 +01:00
let mut is_transparent = false;
for hint in &hints {
let (article, allowed_targets) = match hint.name_or_empty() {
name @ sym::C | name @ sym::align => {
is_c |= name == sym::C;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => ("a", "struct, enum, or union"),
2015-09-25 15:25:59 +09:00
}
}
sym::packed => {
if target != Target::Struct &&
target != Target::Union {
("a", "struct or union")
} else {
continue
}
}
sym::simd => {
is_simd = true;
2015-09-25 15:25:59 +09:00
if target != Target::Struct {
("a", "struct")
2015-11-09 22:13:55 +05:30
} else {
continue
2015-09-25 15:25:59 +09:00
}
}
sym::transparent => {
2018-01-03 17:43:30 +01:00
is_transparent = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => ("a", "struct, enum, or union"),
2018-01-03 17:43:30 +01:00
}
}
sym::i8 | sym::u8 | sym::i16 | sym::u16 |
sym::i32 | sym::u32 | sym::i64 | sym::u64 |
sym::isize | sym::usize => {
int_reprs += 1;
2015-09-25 15:25:59 +09:00
if target != Target::Enum {
("an", "enum")
2015-11-09 22:13:55 +05:30
} else {
continue
2015-09-25 15:25:59 +09:00
}
}
2015-11-09 22:13:55 +05:30
_ => continue,
};
self.emit_repr_error(
hint.span(),
item.span,
&format!("attribute should be applied to {}", allowed_targets),
&format!("not {} {}", article, allowed_targets),
)
2015-09-25 15:25:59 +09:00
}
2018-01-03 17:43:30 +01:00
// Just point at all repr hints if there are any incompatibilities.
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
let hint_spans = hints.iter().map(|hint| hint.span());
2018-01-03 17:43:30 +01:00
// Error on repr(transparent, <anything else>).
if is_transparent && hints.len() > 1 {
let hint_spans: Vec<_> = hint_spans.clone().collect();
span_err!(self.tcx.sess, hint_spans, E0692,
"transparent {} cannot have other repr hints", target);
2018-01-03 17:43:30 +01:00
}
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
if (int_reprs > 1)
|| (is_simd && is_c)
|| (int_reprs == 1 && is_c && is_c_like_enum(item)) {
2018-01-03 17:43:30 +01:00
let hint_spans: Vec<_> = hint_spans.collect();
span_warn!(self.tcx.sess, hint_spans, E0566,
"conflicting representation hints");
}
2015-09-25 15:25:59 +09:00
}
fn emit_repr_error(
&self,
hint_span: Span,
label_span: Span,
hint_message: &str,
label_message: &str,
) {
struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
.span_label(label_span, label_message)
.emit();
}
fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
// When checking statements ignore expressions, they will be checked later
if let hir::StmtKind::Local(ref l) = stmt.node {
for attr in l.attrs.iter() {
if attr.check_name(sym::inline) {
self.check_inline(attr, &stmt.span, Target::Statement);
}
if attr.check_name(sym::repr) {
self.emit_repr_error(
attr.span,
stmt.span,
2018-07-28 14:40:32 +02:00
"attribute should not be applied to a statement",
"not a struct, enum, or union",
);
}
}
}
}
fn check_expr_attributes(&self, expr: &hir::Expr) {
let target = match expr.node {
2018-07-11 20:05:29 +08:00
hir::ExprKind::Closure(..) => Target::Closure,
_ => Target::Expression,
};
for attr in expr.attrs.iter() {
if attr.check_name(sym::inline) {
self.check_inline(attr, &expr.span, target);
}
if attr.check_name(sym::repr) {
self.emit_repr_error(
attr.span,
expr.span,
2018-07-28 14:40:32 +02:00
"attribute should not be applied to an expression",
"not defining a struct, enum, or union",
);
}
}
}
fn check_used(&self, item: &hir::Item, target: Target) {
for attr in &item.attrs {
if attr.check_name(sym::used) && target != Target::Static {
self.tcx.sess
.span_err(attr.span, "attribute must be applied to a `static` variable");
}
}
}
2015-09-25 15:25:59 +09:00
}
impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
fn visit_item(&mut self, item: &'tcx hir::Item) {
2015-09-25 15:25:59 +09:00
let target = Target::from_item(item);
self.check_attributes(item, target);
intravisit::walk_item(self, item)
}
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
self.check_stmt_attributes(stmt);
intravisit::walk_stmt(self, stmt)
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
self.check_expr_attributes(expr);
intravisit::walk_expr(self, expr)
2015-09-25 15:25:59 +09:00
}
}
fn is_c_like_enum(item: &hir::Item) -> bool {
2018-07-11 23:36:06 +08:00
if let hir::ItemKind::Enum(ref def, _) = item.node {
for variant in &def.variants {
2019-08-13 21:40:21 -03:00
match variant.data {
2019-02-02 15:40:08 +01:00
hir::VariantData::Unit(..) => { /* continue */ }
_ => { return false; }
}
}
true
} else {
false
}
}
2018-06-06 22:13:52 +02:00
2019-06-21 20:05:14 +02:00
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
2019-01-11 04:58:46 +01:00
tcx.hir().visit_item_likes_in_module(
2018-06-06 22:13:52 +02:00
module_def_id,
&mut CheckAttrVisitor { tcx }.as_deep_visitor()
);
}
pub(crate) fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
check_mod_attrs,
..*providers
};
}