rust/src/libsyntax/feature_gate.rs

976 lines
37 KiB
Rust
Raw Normal View History

//! # Feature gating
//!
2015-04-16 02:18:29 -05:00
//! This module implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! `#![feature(...)]` with a comma-separated list of features.
//!
//! For the purpose of future feature-tracking, once code for detection of feature
//! gate usage is added, *do not remove it again* even once the feature
//! becomes stable.
2015-02-03 16:31:06 -06:00
mod accepted;
use accepted::ACCEPTED_FEATURES;
mod removed;
use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
mod active;
use active::{ACTIVE_FEATURES};
pub use active::{Features, INCOMPLETE_FEATURES};
2019-08-22 11:32:31 -05:00
mod builtin_attrs;
pub use builtin_attrs::{
AttributeGate, AttributeType, GatedCfg,
BuiltinAttribute, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
deprecated_attributes, is_builtin_attr, is_builtin_attr_name,
};
2019-02-06 11:33:01 -06:00
2019-03-21 13:40:00 -05:00
use crate::ast::{
self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
PatKind, RangeEnd,
};
2019-08-22 11:32:31 -05:00
use crate::attr::{self, check_builtin_attribute};
2019-02-06 11:33:01 -06:00
use crate::source_map::Spanned;
use crate::edition::{ALL_EDITIONS, Edition};
use crate::visit::{self, FnKind, Visitor};
use crate::parse::{token, ParseSess};
2019-05-09 16:08:55 -05:00
use crate::parse::parser::Parser;
2019-05-30 05:30:03 -05:00
use crate::symbol::{Symbol, sym};
use crate::tokenstream::TokenTree;
use errors::{Applicability, DiagnosticBuilder, Handler};
use rustc_data_structures::fx::FxHashMap;
use rustc_target::spec::abi::Abi;
2019-05-09 16:08:55 -05:00
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
2019-02-06 11:33:01 -06:00
use log::debug;
2019-01-13 07:18:00 -06:00
use std::env;
#[derive(Copy, Clone, Debug)]
pub enum Stability {
Unstable,
// First argument is tracking issue link; second argument is an optional
// help message, which defaults to "remove this attribute"
Deprecated(&'static str, Option<&'static str>),
}
2014-03-05 08:36:01 -06:00
struct Context<'a> {
features: &'a Features,
2016-09-24 11:42:54 -05:00
parse_sess: &'a ParseSess,
plugin_attributes: &'a [(Symbol, AttributeType)],
}
macro_rules! gate_feature_fn {
($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
let (cx, has_feature, span,
name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level);
let has_feature: bool = has_feature(&$cx.features);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
if !has_feature && !span.allows_unstable($name) {
leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
.emit();
}
}}
}
macro_rules! gate_feature {
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
sym::$feature, $explain, GateStrength::Hard)
};
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
sym::$feature, $explain, $level)
};
}
impl<'a> Context<'a> {
fn check_attribute(
&self,
attr: &ast::Attribute,
attr_info: Option<&BuiltinAttribute>,
is_macro: bool
) {
debug!("check_attribute(attr = {:?})", attr);
if let Some(&(name, ty, _template, ref gateage)) = attr_info {
2019-08-22 11:32:31 -05:00
if let AttributeGate::Gated(_, name, desc, ref has_feature) = *gateage {
if !attr.span.allows_unstable(name) {
gate_feature_fn!(
self, has_feature, attr.span, name, desc, GateStrength::Hard
);
}
} else if name == sym::doc {
if let Some(content) = attr.meta_item_list() {
if content.iter().any(|c| c.check_name(sym::include)) {
gate_feature!(self, external_doc, attr.span,
"`#[doc(include = \"...\")]` is experimental"
);
}
}
}
debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage);
return;
2019-06-30 05:00:45 -05:00
} else {
for segment in &attr.path.segments {
if segment.ident.as_str().starts_with("rustc") {
let msg = "attributes starting with `rustc` are \
reserved for use by the `rustc` compiler";
gate_feature!(self, rustc_attrs, segment.ident.span, msg);
}
}
}
for &(n, ty) in self.plugin_attributes {
if attr.path == n {
// Plugins can't gate attributes, so we don't check for it
2015-05-13 01:53:43 -05:00
// unlike the code above; we only use this loop to
// short-circuit to avoid the checks below.
debug!("check_attribute: {:?} is registered by a plugin, {:?}", attr.path, ty);
return;
}
}
if !is_macro && !attr::is_known(attr) {
// Only run the custom attribute lint during regular feature gate
// checking. Macro gating runs before the plugin attributes are
// registered, so we skip this in that case.
2019-07-12 21:33:51 -05:00
let msg = format!("the attribute `{}` is currently unknown to the compiler and \
may have meaning added to it in the future", attr.path);
gate_feature!(self, custom_attribute, attr.span, &msg);
}
}
}
2017-03-16 23:04:41 -05:00
pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
2019-06-25 16:22:45 -05:00
let cx = Context { features, parse_sess, plugin_attributes: &[] };
cx.check_attribute(
attr,
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)),
true
);
}
fn find_lang_feature_issue(feature: Symbol) -> Option<u32> {
if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.0 == feature) {
let issue = info.2;
// FIXME (#28244): enforce that active features have issue numbers
// assert!(issue.is_some())
issue
} else {
// search in Accepted, Removed, or Stable Removed features
let found = ACCEPTED_FEATURES.iter().chain(REMOVED_FEATURES).chain(STABLE_REMOVED_FEATURES)
.find(|t| t.0 == feature);
match found {
Some(&(_, _, issue, _)) => issue,
None => panic!("Feature `{}` is not declared anywhere", feature),
}
}
}
pub enum GateIssue {
Language,
Library(Option<u32>)
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum GateStrength {
/// A hard error. (Most feature gates should use this.)
Hard,
/// Only a warning. (Use this only as backwards-compatibility demands.)
Soft,
}
2019-04-10 18:40:12 -05:00
pub fn emit_feature_err(
sess: &ParseSess,
feature: Symbol,
2019-04-10 18:40:12 -05:00
span: Span,
issue: GateIssue,
explain: &str,
) {
2016-10-04 12:10:33 -05:00
feature_err(sess, feature, span, issue, explain).emit();
}
2019-05-09 16:08:55 -05:00
pub fn feature_err<'a, S: Into<MultiSpan>>(
2019-04-10 18:40:12 -05:00
sess: &'a ParseSess,
feature: Symbol,
2019-05-09 16:08:55 -05:00
span: S,
2019-04-10 18:40:12 -05:00
issue: GateIssue,
explain: &str,
) -> DiagnosticBuilder<'a> {
leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard)
}
2019-05-09 16:08:55 -05:00
fn leveled_feature_err<'a, S: Into<MultiSpan>>(
2019-04-10 18:40:12 -05:00
sess: &'a ParseSess,
feature: Symbol,
2019-05-09 16:08:55 -05:00
span: S,
2019-04-10 18:40:12 -05:00
issue: GateIssue,
explain: &str,
level: GateStrength,
) -> DiagnosticBuilder<'a> {
2016-09-24 11:42:54 -05:00
let diag = &sess.span_diagnostic;
let issue = match issue {
GateIssue::Language => find_lang_feature_issue(feature),
GateIssue::Library(lib) => lib,
};
let mut err = match level {
GateStrength::Hard => {
2019-04-10 18:40:12 -05:00
diag.struct_span_err_with_code(span, explain, stringify_error_code!(E0658))
}
2019-04-10 18:40:12 -05:00
GateStrength::Soft => diag.struct_span_warn(span, explain),
2015-12-20 15:00:43 -06:00
};
2019-04-10 18:40:12 -05:00
match issue {
2019-04-11 13:42:06 -05:00
None | Some(0) => {} // We still accept `0` as a stand-in for backwards compatibility
2019-04-10 18:40:12 -05:00
Some(n) => {
2019-04-11 13:42:06 -05:00
err.note(&format!(
"for more information, see https://github.com/rust-lang/rust/issues/{}",
n,
));
2019-04-10 18:40:12 -05:00
}
}
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
2016-09-24 12:28:46 -05:00
if sess.unstable_features.is_nightly_build() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
2015-12-20 15:00:43 -06:00
}
2016-09-24 12:28:46 -05:00
// If we're on stable and only emitting a "soft" warning, add a note to
// clarify that the feature isn't "on" (rather than being on but
// warning-worthy).
if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft {
err.help("a nightly build of the compiler is required to enable this feature");
}
2016-10-04 12:10:33 -05:00
err
}
const EXPLAIN_BOX_SYNTAX: &str =
2019-04-10 18:40:12 -05:00
"box expression syntax is experimental; you can call `Box::new` instead";
2016-04-09 11:01:14 -05:00
pub const EXPLAIN_STMT_ATTR_SYNTAX: &str =
2019-04-10 18:40:12 -05:00
"attributes on expressions are experimental";
2016-04-09 11:01:14 -05:00
pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &str =
"allow_internal_unstable side-steps feature gating and stability checks";
pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &str =
"allow_internal_unsafe side-steps the unsafe_code lint";
pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str =
2018-06-25 21:29:13 -05:00
"unsized tuple coercion is not stable enough for use and is subject to change";
struct PostExpansionVisitor<'a> {
2015-09-28 19:46:01 -05:00
context: &'a Context<'a>,
builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
}
macro_rules! gate_feature_post {
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
let (cx, span) = ($cx, $span);
if !span.allows_unstable(sym::$feature) {
gate_feature!(cx.context, $feature, span, $explain)
}
}};
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
let (cx, span) = ($cx, $span);
if !span.allows_unstable(sym::$feature) {
gate_feature!(cx.context, $feature, span, $explain, $level)
}
}}
}
impl<'a> PostExpansionVisitor<'a> {
fn check_abi(&self, abi: Abi, span: Span) {
match abi {
Abi::RustIntrinsic => {
gate_feature_post!(&self, intrinsics, span,
"intrinsics are subject to change");
},
Abi::PlatformIntrinsic => {
gate_feature_post!(&self, platform_intrinsics, span,
"platform intrinsics are experimental and possibly buggy");
},
Abi::Vectorcall => {
gate_feature_post!(&self, abi_vectorcall, span,
"vectorcall is experimental and subject to change");
},
Abi::Thiscall => {
gate_feature_post!(&self, abi_thiscall, span,
"thiscall is experimental and subject to change");
},
Abi::RustCall => {
gate_feature_post!(&self, unboxed_closures, span,
"rust-call ABI is subject to change");
},
Abi::PtxKernel => {
gate_feature_post!(&self, abi_ptx, span,
"PTX ABIs are experimental and subject to change");
},
Abi::Unadjusted => {
gate_feature_post!(&self, abi_unadjusted, span,
"unadjusted ABI is an implementation detail and perma-unstable");
},
Abi::Msp430Interrupt => {
gate_feature_post!(&self, abi_msp430_interrupt, span,
"msp430-interrupt ABI is experimental and subject to change");
},
Abi::X86Interrupt => {
gate_feature_post!(&self, abi_x86_interrupt, span,
"x86-interrupt ABI is experimental and subject to change");
},
Abi::AmdGpuKernel => {
gate_feature_post!(&self, abi_amdgpu_kernel, span,
"amdgpu-kernel ABI is experimental and subject to change");
},
// Stable
Abi::Cdecl |
Abi::Stdcall |
Abi::Fastcall |
Abi::Aapcs |
Abi::Win64 |
2017-12-05 18:19:35 -06:00
Abi::SysV64 |
Abi::Rust |
Abi::C |
Abi::System => {}
}
}
}
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_attribute(&mut self, attr: &ast::Attribute) {
let attr_info = attr.ident().and_then(|ident| {
self.builtin_attributes.get(&ident.name).map(|a| *a)
});
2019-03-21 13:40:00 -05:00
// Check for gated attributes.
self.context.check_attribute(attr, attr_info, false);
if attr.check_name(sym::doc) {
if let Some(content) = attr.meta_item_list() {
if content.len() == 1 && content[0].check_name(sym::cfg) {
gate_feature_post!(&self, doc_cfg, attr.span,
"`#[doc(cfg(...))]` is experimental"
);
} else if content.iter().any(|c| c.check_name(sym::masked)) {
2017-08-21 20:20:21 -05:00
gate_feature_post!(&self, doc_masked, attr.span,
"`#[doc(masked)]` is experimental"
2017-08-21 20:20:21 -05:00
);
} else if content.iter().any(|c| c.check_name(sym::spotlight)) {
gate_feature_post!(&self, doc_spotlight, attr.span,
"`#[doc(spotlight)]` is experimental"
);
} else if content.iter().any(|c| c.check_name(sym::alias)) {
2018-04-19 10:46:13 -05:00
gate_feature_post!(&self, doc_alias, attr.span,
"`#[doc(alias = \"...\")]` is experimental"
2018-04-19 10:46:13 -05:00
);
} else if content.iter().any(|c| c.check_name(sym::keyword)) {
2018-06-02 17:45:49 -05:00
gate_feature_post!(&self, doc_keyword, attr.span,
"`#[doc(keyword = \"...\")]` is experimental"
2018-06-02 17:45:49 -05:00
);
}
}
}
match attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
check_builtin_attribute(self.context.parse_sess, attr, name, template),
_ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() {
if token == token::Eq {
// All key-value attributes are restricted to meta-item syntax.
attr.parse_meta(self.context.parse_sess).map_err(|mut err| err.emit()).ok();
}
}
}
}
fn visit_name(&mut self, sp: Span, name: ast::Name) {
if !name.as_str().is_ascii() {
2019-04-10 18:40:12 -05:00
gate_feature_post!(
&self,
non_ascii_idents,
self.context.parse_sess.source_map().def_span(sp),
"non-ascii idents are not fully supported"
);
}
}
fn visit_item(&mut self, i: &'a ast::Item) {
match i.node {
ast::ItemKind::ForeignMod(ref foreign_module) => {
self.check_abi(foreign_module.abi, i.span);
Add generation of static libraries to rustc This commit implements the support necessary for generating both intermediate and result static rust libraries. This is an implementation of my thoughts in https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html. When compiling a library, we still retain the "lib" option, although now there are "rlib", "staticlib", and "dylib" as options for crate_type (and these are stackable). The idea of "lib" is to generate the "compiler default" instead of having too choose (although all are interchangeable). For now I have left the "complier default" to be a dynamic library for size reasons. Of the rust libraries, lib{std,extra,rustuv} will bootstrap with an rlib/dylib pair, but lib{rustc,syntax,rustdoc,rustpkg} will only be built as a dynamic object. I chose this for size reasons, but also because you're probably not going to be embedding the rustc compiler anywhere any time soon. Other than the options outlined above, there are a few defaults/preferences that are now opinionated in the compiler: * If both a .dylib and .rlib are found for a rust library, the compiler will prefer the .rlib variant. This is overridable via the -Z prefer-dynamic option * If generating a "lib", the compiler will generate a dynamic library. This is overridable by explicitly saying what flavor you'd like (rlib, staticlib, dylib). * If no options are passed to the command line, and no crate_type is found in the destination crate, then an executable is generated With this change, you can successfully build a rust program with 0 dynamic dependencies on rust libraries. There is still a dynamic dependency on librustrt, but I plan on removing that in a subsequent commit. This change includes no tests just yet. Our current testing infrastructure/harnesses aren't very amenable to doing flavorful things with linking, so I'm planning on adding a new mode of testing which I believe belongs as a separate commit. Closes #552
2013-11-15 16:03:29 -06:00
}
ast::ItemKind::Fn(..) => {
if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
gate_feature_post!(&self, plugin_registrar, i.span,
"compiler plugins are experimental and possibly buggy");
2013-12-25 12:10:33 -06:00
}
if attr::contains_name(&i.attrs[..], sym::start) {
gate_feature_post!(&self, start, i.span,
"a `#[start]` function is an experimental \
feature whose signature may change \
over time");
}
if attr::contains_name(&i.attrs[..], sym::main) {
gate_feature_post!(&self, main, i.span,
"declaration of a non-standard `#[main]` \
function may change over time, for now \
a top-level `fn main()` is required");
}
2013-12-25 12:10:33 -06:00
}
ast::ItemKind::Struct(..) => {
for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.check_name(sym::simd) {
gate_feature_post!(&self, repr_simd, attr.span,
"SIMD types are experimental and possibly buggy");
}
}
}
}
2019-05-09 16:08:55 -05:00
ast::ItemKind::Enum(ast::EnumDef{ref variants, ..}, ..) => {
for variant in variants {
2019-08-13 19:40:21 -05:00
match (&variant.data, &variant.disr_expr) {
2019-05-09 16:08:55 -05:00
(ast::VariantData::Unit(..), _) => {},
(_, Some(disr_expr)) =>
gate_feature_post!(
&self,
arbitrary_enum_discriminant,
disr_expr.value.span,
"discriminants on non-unit variants are experimental"),
_ => {},
}
}
let has_feature = self.context.features.arbitrary_enum_discriminant;
if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
Parser::maybe_report_invalid_custom_discriminants(
self.context.parse_sess,
&variants,
);
}
}
ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => {
if polarity == ast::ImplPolarity::Negative {
gate_feature_post!(&self, optin_builtin_traits,
i.span,
"negative trait bounds are not yet fully implemented; \
use marker types for now");
}
if let ast::Defaultness::Default = defaultness {
gate_feature_post!(&self, specialization,
i.span,
"specialization is unstable");
}
}
ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
gate_feature_post!(&self, optin_builtin_traits,
i.span,
"auto traits are experimental and possibly buggy");
}
ast::ItemKind::TraitAlias(..) => {
gate_feature_post!(
&self,
trait_alias,
i.span,
"trait aliases are experimental"
);
}
ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
let msg = "`macro` is experimental";
gate_feature_post!(&self, decl_macro, i.span, msg);
}
2019-07-31 18:41:54 -05:00
ast::ItemKind::OpaqueTy(..) => {
2018-07-03 12:38:14 -05:00
gate_feature_post!(
&self,
type_alias_impl_trait,
2018-07-03 12:38:14 -05:00
i.span,
2019-07-31 18:41:54 -05:00
"`impl Trait` in type aliases is unstable"
2018-07-03 12:38:14 -05:00
);
}
_ => {}
}
visit::walk_item(self, i);
}
fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
2017-09-03 13:53:58 -05:00
match i.node {
ast::ForeignItemKind::Fn(..) |
ast::ForeignItemKind::Static(..) => {
let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
2017-09-03 13:53:58 -05:00
let links_to_llvm = match link_name {
Some(val) => val.as_str().starts_with("llvm."),
_ => false
};
if links_to_llvm {
gate_feature_post!(&self, link_llvm_intrinsics, i.span,
"linking to LLVM intrinsics is experimental");
}
}
ast::ForeignItemKind::Ty => {
gate_feature_post!(&self, extern_types, i.span,
"extern types are experimental");
}
ast::ForeignItemKind::Macro(..) => {}
}
visit::walk_foreign_item(self, i)
}
fn visit_ty(&mut self, ty: &'a ast::Ty) {
match ty.node {
ast::TyKind::BareFn(ref bare_fn_ty) => {
self.check_abi(bare_fn_ty.abi, ty.span);
}
ast::TyKind::Never => {
gate_feature_post!(&self, never_type, ty.span,
"The `!` type is experimental");
}
_ => {}
}
visit::walk_ty(self, ty)
}
fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
if let ast::TyKind::Never = output_ty.node {
2019-03-21 13:40:00 -05:00
// Do nothing.
} else {
self.visit_ty(output_ty)
}
}
}
fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.node {
ast::ExprKind::Box(_) => {
gate_feature_post!(&self, box_syntax, e.span, EXPLAIN_BOX_SYNTAX);
}
ast::ExprKind::Type(..) => {
// To avoid noise about type ascription in common syntax errors, only emit if it
// is the *only* error.
if self.context.parse_sess.span_diagnostic.err_count() == 0 {
gate_feature_post!(&self, type_ascription, e.span,
"type ascription is experimental");
}
}
ast::ExprKind::TryBlock(_) => {
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
}
2018-05-12 04:33:33 -05:00
ast::ExprKind::Block(_, opt_label) => {
if let Some(label) = opt_label {
gate_feature_post!(&self, label_break_value, label.ident.span,
"labels on blocks are unstable");
}
}
_ => {}
}
2019-03-21 13:40:00 -05:00
visit::walk_expr(self, e)
}
2017-08-26 17:09:31 -05:00
fn visit_arm(&mut self, arm: &'a ast::Arm) {
2017-09-01 14:39:46 -05:00
visit::walk_arm(self, arm)
2017-08-26 17:09:31 -05:00
}
fn visit_pat(&mut self, pattern: &'a ast::Pat) {
match &pattern.node {
PatKind::Slice(pats) => {
for pat in &*pats {
let span = pat.span;
let inner_pat = match &pat.node {
PatKind::Ident(.., Some(pat)) => pat,
_ => pat,
};
if inner_pat.is_rest() {
gate_feature_post!(
&self,
slice_patterns,
span,
"subslice patterns are unstable"
);
}
}
}
2016-02-11 12:16:33 -06:00
PatKind::Box(..) => {
gate_feature_post!(&self, box_patterns,
pattern.span,
"box pattern syntax is experimental");
}
PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
"exclusive range pattern syntax is experimental");
}
_ => {}
}
visit::walk_pat(self, pattern)
}
fn visit_fn(&mut self,
fn_kind: FnKind<'a>,
fn_decl: &'a ast::FnDecl,
span: Span,
_node_id: NodeId) {
2019-04-18 13:15:43 -05:00
if let Some(header) = fn_kind.header() {
// Stability of const fn methods are covered in
// `visit_trait_item` and `visit_impl_item` below; this is
// because default methods don't pass through this point.
self.check_abi(header.abi, span);
}
2019-04-18 13:15:43 -05:00
if fn_decl.c_variadic {
gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
}
2019-04-18 13:15:43 -05:00
2019-03-21 13:40:00 -05:00
visit::walk_fn(self, fn_kind, fn_decl, span)
}
2015-03-26 14:06:26 -05:00
fn visit_generic_param(&mut self, param: &'a GenericParam) {
2019-03-21 13:40:00 -05:00
match param.kind {
GenericParamKind::Const { .. } =>
gate_feature_post!(&self, const_generics, param.ident.span,
"const generics are unstable"),
_ => {}
}
visit::walk_generic_param(self, param)
}
fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
match constraint.kind {
AssocTyConstraintKind::Bound { .. } =>
gate_feature_post!(&self, associated_type_bounds, constraint.span,
"associated type bounds are unstable"),
_ => {}
}
2019-03-21 13:40:00 -05:00
visit::walk_assoc_ty_constraint(self, constraint)
}
fn visit_trait_item(&mut self, ti: &'a ast::TraitItem) {
2015-03-26 14:06:26 -05:00
match ti.node {
ast::TraitItemKind::Method(ref sig, ref block) => {
if block.is_none() {
self.check_abi(sig.header.abi, ti.span);
}
2019-02-24 16:40:11 -06:00
if sig.decl.c_variadic {
gate_feature_post!(&self, c_variadic, ti.span,
2019-04-18 14:36:32 -05:00
"C-variadic functions are unstable");
2019-02-24 16:40:11 -06:00
}
if sig.header.constness.node == ast::Constness::Const {
gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
}
}
ast::TraitItemKind::Type(_, ref default) => {
// We use three if statements instead of something like match guards so that all
// of these errors can be emitted if all cases apply.
if default.is_some() {
gate_feature_post!(&self, associated_type_defaults, ti.span,
"associated type defaults are unstable");
}
if !ti.generics.params.is_empty() {
gate_feature_post!(&self, generic_associated_types, ti.span,
"generic associated types are unstable");
}
if !ti.generics.where_clause.predicates.is_empty() {
gate_feature_post!(&self, generic_associated_types, ti.span,
"where clauses on associated types are unstable");
}
2017-11-09 20:40:14 -06:00
}
2015-03-26 14:06:26 -05:00
_ => {}
}
2019-03-21 13:40:00 -05:00
visit::walk_trait_item(self, ti)
2015-03-26 14:06:26 -05:00
}
fn visit_impl_item(&mut self, ii: &'a ast::ImplItem) {
2015-12-30 17:16:43 -06:00
if ii.defaultness == ast::Defaultness::Default {
gate_feature_post!(&self, specialization,
2015-12-30 17:16:43 -06:00
ii.span,
"specialization is unstable");
}
2015-03-26 14:06:26 -05:00
match ii.node {
2018-10-05 03:17:16 -05:00
ast::ImplItemKind::Method(..) => {}
2019-07-31 18:41:54 -05:00
ast::ImplItemKind::OpaqueTy(..) => {
2018-07-03 12:38:14 -05:00
gate_feature_post!(
&self,
type_alias_impl_trait,
2018-07-03 12:38:14 -05:00
ii.span,
2019-07-31 18:41:54 -05:00
"`impl Trait` in type aliases is unstable"
2018-07-03 12:38:14 -05:00
);
}
ast::ImplItemKind::TyAlias(_) => {
if !ii.generics.params.is_empty() {
gate_feature_post!(&self, generic_associated_types, ii.span,
"generic associated types are unstable");
}
if !ii.generics.where_clause.predicates.is_empty() {
gate_feature_post!(&self, generic_associated_types, ii.span,
"where clauses on associated types are unstable");
}
2017-11-09 20:40:14 -06:00
}
2015-03-26 14:06:26 -05:00
_ => {}
}
2019-03-21 13:40:00 -05:00
visit::walk_impl_item(self, ii)
2015-03-26 14:06:26 -05:00
}
2016-04-10 18:33:36 -05:00
fn visit_vis(&mut self, vis: &'a ast::Visibility) {
2018-01-28 23:12:09 -06:00
if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
gate_feature_post!(&self, crate_visibility_modifier, vis.span,
"`crate` visibility modifier is experimental");
}
2019-03-21 13:40:00 -05:00
visit::walk_vis(self, vis)
}
}
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
2019-03-13 18:29:24 -05:00
crate_edition: Edition, allow_features: &Option<Vec<String>>) -> Features {
fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
if let Some(reason) = reason {
err.span_note(span, reason);
} else {
err.span_label(span, "feature has been removed");
}
err.emit();
2018-03-21 17:48:56 -05:00
}
let mut features = Features::new();
let mut edition_enabled_features = FxHashMap::default();
for &edition in ALL_EDITIONS {
if edition <= crate_edition {
// The `crate_edition` implies its respective umbrella feature-gate
// (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
edition_enabled_features.insert(edition.feature_name(), edition);
}
}
for &(name, .., f_edition, set) in ACTIVE_FEATURES {
if let Some(f_edition) = f_edition {
if f_edition <= crate_edition {
set(&mut features, DUMMY_SP);
edition_enabled_features.insert(name, crate_edition);
}
2018-05-04 13:18:33 -05:00
}
}
// Process the edition umbrella feature-gates first, to ensure
// `edition_enabled_features` is completed before it's queried.
2016-06-10 20:37:24 -05:00
for attr in krate_attrs {
if !attr.check_name(sym::feature) {
continue
}
let list = match attr.meta_item_list() {
Some(list) => list,
None => continue,
};
for mi in list {
if !mi.is_word() {
continue;
}
let name = mi.name_or_empty();
if let Some(edition) = ALL_EDITIONS.iter().find(|e| name == e.feature_name()) {
if *edition <= crate_edition {
continue;
}
for &(name, .., f_edition, set) in ACTIVE_FEATURES {
if let Some(f_edition) = f_edition {
if f_edition <= *edition {
// FIXME(Manishearth) there is currently no way to set
// lib features by edition
set(&mut features, DUMMY_SP);
edition_enabled_features.insert(name, *edition);
}
}
}
}
}
}
for attr in krate_attrs {
if !attr.check_name(sym::feature) {
continue
}
let list = match attr.meta_item_list() {
Some(list) => list,
None => continue,
};
let bad_input = |span| {
struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
};
for mi in list {
let name = match mi.ident() {
Some(ident) if mi.is_word() => ident.name,
Some(ident) => {
bad_input(mi.span()).span_suggestion(
mi.span(),
"expected just one word",
format!("{}", ident.name),
Applicability::MaybeIncorrect,
).emit();
continue
}
None => {
bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
continue
}
};
if let Some(edition) = edition_enabled_features.get(&name) {
struct_span_warn!(
span_handler,
mi.span(),
E0705,
"the feature `{}` is included in the Rust {} edition",
name,
edition,
).emit();
continue;
}
if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
// Handled in the separate loop above.
continue;
}
let removed = REMOVED_FEATURES.iter().find(|f| name == f.0);
let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.0);
if let Some((.., reason)) = removed.or(stable_removed) {
feature_removed(span_handler, mi.span(), *reason);
continue;
}
2018-07-22 20:03:01 -05:00
if let Some((_, since, ..)) = ACCEPTED_FEATURES.iter().find(|f| name == f.0) {
let since = Some(Symbol::intern(since));
features.declared_lang_features.push((name, mi.span(), since));
continue;
}
if let Some(allowed) = allow_features.as_ref() {
if allowed.iter().find(|f| *f == name.as_str()).is_none() {
span_err!(span_handler, mi.span(), E0725,
"the feature `{}` is not in the list of allowed features",
name);
continue;
}
}
if let Some((.., set)) = ACTIVE_FEATURES.iter().find(|f| name == f.0) {
set(&mut features, mi.span());
features.declared_lang_features.push((name, mi.span(), None));
continue;
}
features.declared_lib_features.push((name, mi.span()));
}
}
features
}
2016-06-10 20:37:24 -05:00
pub fn check_crate(krate: &ast::Crate,
sess: &ParseSess,
features: &Features,
plugin_attributes: &[(Symbol, AttributeType)],
2016-06-10 20:37:24 -05:00
unstable: UnstableFeatures) {
maybe_stage_features(&sess.span_diagnostic, krate, unstable);
let ctx = Context {
features,
2016-09-24 11:42:54 -05:00
parse_sess: sess,
plugin_attributes,
2016-06-10 20:37:24 -05:00
};
macro_rules! gate_all {
($gate:ident, $msg:literal) => { gate_all!($gate, $gate, $msg); };
($spans:ident, $gate:ident, $msg:literal) => {
for span in &*sess.gated_spans.$spans.borrow() {
gate_feature!(&ctx, $gate, *span, $msg);
}
}
}
gate_all!(param_attrs, "attributes on function parameters are unstable");
gate_all!(let_chains, "`let` expressions in this position are experimental");
gate_all!(async_closure, "async closures are unstable");
gate_all!(yields, generators, "yield syntax is experimental");
gate_all!(or_patterns, "or-patterns syntax is experimental");
2019-08-13 22:44:32 -05:00
let visitor = &mut PostExpansionVisitor {
context: &ctx,
builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,
};
2017-11-27 20:14:24 -06:00
visit::walk_crate(visitor, krate);
}
#[derive(Clone, Copy, Hash)]
pub enum UnstableFeatures {
2019-02-08 07:53:55 -06:00
/// Hard errors for unstable features are active, as on beta/stable channels.
Disallow,
2016-10-25 22:14:46 -05:00
/// Allow features to be activated, as on nightly.
Allow,
/// Errors are bypassed for bootstrapping. This is required any time
/// during the build that feature-related lints are set to warn or above
/// because the build turns on warnings-as-errors and uses lots of unstable
/// features. As a result, this is always required for building Rust itself.
Cheat
}
2016-09-24 12:04:07 -05:00
impl UnstableFeatures {
pub fn from_environment() -> UnstableFeatures {
// Whether this is a feature-staged build, i.e., on the beta or stable channel
2016-09-24 12:04:07 -05:00
let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
// Whether we should enable unstable features for bootstrapping
let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
match (disable_unstable_features, bootstrap) {
(_, true) => UnstableFeatures::Cheat,
(true, _) => UnstableFeatures::Disallow,
(false, _) => UnstableFeatures::Allow
2016-09-24 12:04:07 -05:00
}
}
pub fn is_nightly_build(&self) -> bool {
match *self {
UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
_ => false,
}
}
2016-09-24 12:04:07 -05:00
}
fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
unstable: UnstableFeatures) {
let allow_features = match unstable {
UnstableFeatures::Allow => true,
UnstableFeatures::Disallow => false,
UnstableFeatures::Cheat => true
};
if !allow_features {
for attr in &krate.attrs {
if attr.check_name(sym::feature) {
let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
2016-06-29 10:23:51 -05:00
span_err!(span_handler, attr.span, E0554,
"`#![feature]` may not be used on the {} release channel",
2016-06-28 12:40:40 -05:00
release_channel);
}
}
}
}