use self::AttributeType::*;
use self::AttributeGate::*;
-use abi::Abi;
+use rustc_target::spec::abi::Abi;
use ast::{self, NodeId, PatKind, RangeEnd};
use attr;
-use edition::Edition;
+use edition::{ALL_EDITIONS, Edition};
use codemap::Spanned;
use syntax_pos::{Span, DUMMY_SP};
use errors::{DiagnosticBuilder, Handler, FatalError};
}
};
- ($((removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
+ ($((removed, $feature: ident, $ver: expr, $issue: expr, None, $reason: expr),)+) => {
/// Represents unstable features which have since been removed (it was once Active)
- const REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
- $((stringify!($feature), $ver, $issue)),+
+ const REMOVED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[
+ $((stringify!($feature), $ver, $issue, $reason)),+
];
};
($((stable_removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
/// Represents stable features which have since been removed (it was once Accepted)
- const STABLE_REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
- $((stringify!($feature), $ver, $issue)),+
+ const STABLE_REMOVED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[
+ $((stringify!($feature), $ver, $issue, None)),+
];
};
($((accepted, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
/// Those language feature has since been Accepted (it was once Active)
- const ACCEPTED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
- $((stringify!($feature), $ver, $issue)),+
+ const ACCEPTED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[
+ $((stringify!($feature), $ver, $issue, None)),+
];
}
}
(active, rustc_diagnostic_macros, "1.0.0", None, None),
(active, rustc_const_unstable, "1.0.0", None, None),
(active, box_syntax, "1.0.0", Some(27779), None),
- (active, placement_in_syntax, "1.0.0", Some(27779), None),
(active, unboxed_closures, "1.0.0", Some(29625), None),
(active, fundamental, "1.0.0", Some(29635), None),
// OIBIT specific features
(active, optin_builtin_traits, "1.0.0", Some(13231), None),
- // macro re-export needs more discussion and stabilization
- (active, macro_reexport, "1.0.0", Some(29638), None),
-
// Allows use of #[staged_api]
// rustc internal
(active, staged_api, "1.0.0", None, None),
(active, rustc_attrs, "1.0.0", Some(29642), None),
// Allows the use of non lexical lifetimes; RFC 2094
- (active, nll, "1.0.0", Some(43234), None),
+ (active, nll, "1.0.0", Some(43234), Some(Edition::Edition2018)),
// Allows the use of #[allow_internal_unstable]. This is an
// attribute on macro_rules! and can't use the attribute handling
// allow `repr(simd)`, and importing the various simd intrinsics
(active, repr_simd, "1.4.0", Some(27731), None),
- // Allows cfg(target_feature = "...").
- (active, cfg_target_feature, "1.4.0", Some(29717), None),
-
// allow `extern "platform-intrinsic" { ... }`
(active, platform_intrinsics, "1.4.0", Some(27731), None),
// rustc internal
(active, compiler_builtins, "1.13.0", None, None),
- // Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
- (active, generic_param_attrs, "1.11.0", Some(34761), None),
-
// Allows #[link(..., cfg(..))]
(active, link_cfg, "1.14.0", Some(37406), None),
(active, use_extern_macros, "1.15.0", Some(35896), None),
- // Allows #[target_feature(...)]
- (active, target_feature, "1.15.0", None, None),
-
// `extern "ptx-*" fn()`
(active, abi_ptx, "1.15.0", None, None),
(active, abi_unadjusted, "1.16.0", None, None),
// Procedural macros 2.0.
- (active, proc_macro, "1.16.0", Some(38356), None),
+ (active, proc_macro, "1.16.0", Some(38356), Some(Edition::Edition2018)),
// Declarative macros 2.0 (`macro`).
(active, decl_macro, "1.17.0", Some(39412), None),
// Allows the `catch {...}` expression
- (active, catch_expr, "1.17.0", Some(31436), None),
+ (active, catch_expr, "1.17.0", Some(31436), Some(Edition::Edition2018)),
// Used to preserve symbols (see llvm.used)
(active, used, "1.18.0", Some(40289), None),
// #[doc(include="some-file")]
(active, external_doc, "1.22.0", Some(44732), None),
- // allow `#[must_use]` on functions and comparison operators (RFC 1940)
- (active, fn_must_use, "1.21.0", Some(43302), None),
-
// Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008)
(active, non_exhaustive, "1.22.0", Some(44109), None),
- // Trait object syntax with `dyn` prefix
- (active, dyn_trait, "1.22.0", Some(44662), Some(Edition::Edition2018)),
-
// `crate` as visibility modifier, synonymous to `pub(crate)`
- (active, crate_visibility_modifier, "1.23.0", Some(45388), None),
+ (active, crate_visibility_modifier, "1.23.0", Some(45388), Some(Edition::Edition2018)),
// extern types
(active, extern_types, "1.23.0", Some(43467), None),
(active, arbitrary_self_types, "1.23.0", Some(44874), None),
// `crate` in paths
- (active, crate_in_paths, "1.23.0", Some(45477), None),
+ (active, crate_in_paths, "1.23.0", Some(45477), Some(Edition::Edition2018)),
// In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`)
- (active, in_band_lifetimes, "1.23.0", Some(44524), None),
+ (active, in_band_lifetimes, "1.23.0", Some(44524), Some(Edition::Edition2018)),
// generic associated types (RFC 1598)
(active, generic_associated_types, "1.23.0", Some(44265), None),
// Resolve absolute paths as paths from other crates
- (active, extern_absolute_paths, "1.24.0", Some(44660), None),
+ (active, extern_absolute_paths, "1.24.0", Some(44660), Some(Edition::Edition2018)),
// `foo.rs` as an alternative to `foo/mod.rs`
- (active, non_modrs_mods, "1.24.0", Some(44660), None),
+ (active, non_modrs_mods, "1.24.0", Some(44660), Some(Edition::Edition2018)),
// Termination trait in tests (RFC 1937)
- (active, termination_trait_test, "1.24.0", Some(48854), None),
+ (active, termination_trait_test, "1.24.0", Some(48854), Some(Edition::Edition2018)),
// Allows use of the :lifetime macro fragment specifier
(active, macro_lifetime_matcher, "1.24.0", Some(46895), None),
// Use `?` as the Kleene "at most one" operator
(active, macro_at_most_once_rep, "1.25.0", Some(48075), None),
+ // Infer outlives requirements; RFC 2093
+ (active, infer_outlives_requirements, "1.26.0", Some(44493), None),
+
// Multiple patterns with `|` in `if let` and `while let`
(active, if_while_or_patterns, "1.26.0", Some(48215), None),
// Parentheses in patterns
(active, pattern_parentheses, "1.26.0", None, None),
+ // Allows `#[repr(packed)]` attribute on structs
+ (active, repr_packed, "1.26.0", Some(33158), None),
+
// `use path as _;` and `extern crate c as _;`
(active, underscore_imports, "1.26.0", Some(48216), None),
// Allows keywords to be escaped for use as identifiers
(active, raw_identifiers, "1.26.0", Some(48589), None),
+
+ // Allows macro invocations in `extern {}` blocks
+ (active, macros_in_extern, "1.27.0", Some(49476), None),
+
+ // unstable #[target_feature] directives
+ (active, arm_target_feature, "1.27.0", None, None),
+ (active, aarch64_target_feature, "1.27.0", None, None),
+ (active, hexagon_target_feature, "1.27.0", None, None),
+ (active, powerpc_target_feature, "1.27.0", None, None),
+ (active, mips_target_feature, "1.27.0", None, None),
+ (active, avx512_target_feature, "1.27.0", None, None),
+ (active, mmx_target_feature, "1.27.0", None, None),
+ (active, sse4a_target_feature, "1.27.0", None, None),
+ (active, tbm_target_feature, "1.27.0", None, None),
+
+ // Allows macro invocations of the form `#[foo::bar]`
+ (active, proc_macro_path_invoc, "1.27.0", None, None),
+
+ // Allows macro invocations on modules expressions and statements and
+ // procedural macros to expand to non-items.
+ (active, proc_macro_mod, "1.27.0", None, None),
+ (active, proc_macro_expr, "1.27.0", None, None),
+ (active, proc_macro_non_items, "1.27.0", None, None),
+
+ // #[doc(alias = "...")]
+ (active, doc_alias, "1.27.0", Some(50146), None),
+
+ // Access to crate names passed via `--extern` through prelude
+ (active, extern_prelude, "1.27.0", Some(44660), Some(Edition::Edition2018)),
+
+ // Scoped attributes
+ (active, tool_attributes, "1.25.0", Some(44690), None),
);
declare_features! (
- (removed, import_shadowing, "1.0.0", None, None),
- (removed, managed_boxes, "1.0.0", None, None),
+ (removed, import_shadowing, "1.0.0", None, None, None),
+ (removed, managed_boxes, "1.0.0", None, None, None),
// Allows use of unary negate on unsigned integers, e.g. -e for e: u8
- (removed, negate_unsigned, "1.0.0", Some(29645), None),
- (removed, reflect, "1.0.0", Some(27749), None),
+ (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
+ (removed, reflect, "1.0.0", Some(27749), None, None),
// A way to temporarily opt out of opt in copy. This will *never* be accepted.
- (removed, opt_out_copy, "1.0.0", None, None),
- (removed, quad_precision_float, "1.0.0", None, None),
- (removed, struct_inherit, "1.0.0", None, None),
- (removed, test_removed_feature, "1.0.0", None, None),
- (removed, visible_private_types, "1.0.0", None, None),
- (removed, unsafe_no_drop_flag, "1.0.0", None, None),
+ (removed, opt_out_copy, "1.0.0", None, None, None),
+ (removed, quad_precision_float, "1.0.0", None, None, None),
+ (removed, struct_inherit, "1.0.0", None, None, None),
+ (removed, test_removed_feature, "1.0.0", None, None, None),
+ (removed, visible_private_types, "1.0.0", None, None, None),
+ (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
// Allows using items which are missing stability attributes
// rustc internal
- (removed, unmarked_api, "1.0.0", None, None),
- (removed, pushpop_unsafe, "1.2.0", None, None),
- (removed, allocator, "1.0.0", None, None),
- // Allows the `#[simd]` attribute -- removed in favor of `#[repr(simd)]`
- (removed, simd, "1.0.0", Some(27731), None),
- // Merged into `slice_patterns`
- (removed, advanced_slice_patterns, "1.0.0", Some(23121), None),
+ (removed, unmarked_api, "1.0.0", None, None, None),
+ (removed, pushpop_unsafe, "1.2.0", None, None, None),
+ (removed, allocator, "1.0.0", None, None, None),
+ (removed, simd, "1.0.0", Some(27731), None,
+ Some("removed in favor of `#[repr(simd)]`")),
+ (removed, advanced_slice_patterns, "1.0.0", Some(23121), None,
+ Some("merged into `#![feature(slice_patterns)]`")),
+ (removed, macro_reexport, "1.0.0", Some(29638), None,
+ Some("subsumed by `#![feature(use_extern_macros)]` and `pub use`")),
);
declare_features! (
// allow empty structs and enum variants with braces
(accepted, braced_empty_structs, "1.8.0", Some(29720), None),
// Allows indexing into constant arrays.
- (accepted, const_indexing, "1.24.0", Some(29947), None),
+ (accepted, const_indexing, "1.26.0", Some(29947), None),
(accepted, default_type_params, "1.0.0", None, None),
(accepted, globs, "1.0.0", None, None),
(accepted, if_let, "1.0.0", None, None),
(accepted, match_default_bindings, "1.26.0", Some(42640), None),
// allow `'_` placeholder lifetimes
(accepted, underscore_lifetimes, "1.26.0", Some(44524), None),
+ // Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
+ (accepted, generic_param_attrs, "1.27.0", Some(48848), None),
+ // Allows cfg(target_feature = "...").
+ (accepted, cfg_target_feature, "1.27.0", Some(29717), None),
+ // Allows #[target_feature(...)]
+ (accepted, target_feature, "1.27.0", None, None),
+ // Trait object syntax with `dyn` prefix
+ (accepted, dyn_trait, "1.27.0", Some(44662), None),
+ // allow `#[must_use]` on functions; and, must-use operators (RFC 1940)
+ (accepted, fn_must_use, "1.27.0", Some(43302), None),
);
// If you change this, please modify src/doc/unstable-book as well. You must
("forbid", Normal, Ungated),
("deny", Normal, Ungated),
- ("macro_reexport", Normal, Ungated),
("macro_use", Normal, Ungated),
("macro_export", Normal, Ungated),
("plugin_registrar", Normal, Ungated),
"the `#[naked]` attribute \
is an experimental feature",
cfg_fn!(naked_functions))),
- ("target_feature", Whitelisted, Gated(
- Stability::Unstable, "target_feature",
- "the `#[target_feature]` attribute is an experimental feature",
- cfg_fn!(target_feature))),
+ ("target_feature", Whitelisted, Ungated),
("export_name", Whitelisted, Ungated),
("inline", Whitelisted, Ungated),
("link", Whitelisted, Ungated),
"never will be stable",
cfg_fn!(rustc_attrs))),
+ // RFC #2093
+ ("infer_outlives_requirements", Normal, Gated(Stability::Unstable,
+ "infer_outlives_requirements",
+ "infer outlives requirements is an experimental feature",
+ cfg_fn!(infer_outlives_requirements))),
+
("wasm_custom_section", Whitelisted, Gated(Stability::Unstable,
"wasm_custom_section",
"attribute is currently unstable",
// cfg(...)'s that are feature gated
const GATED_CFGS: &[(&str, &str, fn(&Features) -> bool)] = &[
// (name in cfg, feature, function to check if the feature is enabled)
- ("target_feature", "cfg_target_feature", cfg_fn!(cfg_target_feature)),
("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)),
("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)),
("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)),
impl<'a> Context<'a> {
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
debug!("check_attribute(attr = {:?})", attr);
- let name = unwrap_or!(attr.name(), return).as_str();
+ let name = attr.name().as_str();
for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
if name == n {
if let Gated(_, name, desc, ref has_feature) = *gateage {
// before the plugin attributes are registered
// so we skip this then
if !is_macro {
- gate_feature!(self, custom_attribute, attr.span,
- &format!("The attribute `{}` is currently \
- unknown to the compiler and \
- may have meaning \
- added to it in the future",
- attr.path));
+ if attr.is_scoped() {
+ gate_feature!(self, tool_attributes, attr.span,
+ &format!("scoped attribute `{}` is experimental", attr.path));
+ if attr::is_known_tool(attr) {
+ attr::mark_used(attr);
+ } else {
+ span_err!(
+ self.parse_sess.span_diagnostic,
+ attr.span,
+ E0694,
+ "an unknown tool name found in scoped attribute: `{}`.",
+ attr.path
+ );
+ }
+ } else {
+ gate_feature!(self, custom_attribute, attr.span,
+ &format!("The attribute `{}` is currently \
+ unknown to the compiler and \
+ may have meaning \
+ added to it in the future",
+ attr.path));
+ }
}
}
}
let found = ACCEPTED_FEATURES.iter().chain(REMOVED_FEATURES).chain(STABLE_REMOVED_FEATURES)
.find(|t| t.0 == feature);
match found {
- Some(&(_, _, issue)) => issue,
+ Some(&(_, _, issue, _)) => issue,
None => panic!("Feature `{}` is not declared anywhere", feature),
}
}
GateIssue::Library(lib) => lib,
};
- let explanation = if let Some(n) = issue {
- format!("{} (see issue #{})", explain, n)
- } else {
- explain.to_owned()
+ let explanation = match issue {
+ None | Some(0) => explain.to_owned(),
+ Some(n) => format!("{} (see issue #{})", explain, n)
};
let mut err = match level {
pub const EXPLAIN_LIFETIME_MATCHER: &'static str =
":lifetime fragment specifier is experimental and subject to change";
-pub const EXPLAIN_PLACEMENT_IN: &'static str =
- "placement-in expression syntax is experimental and subject to change.";
-
pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str =
"Unsized tuple coercion is not stable enough for use and is subject to change";
pub const EXPLAIN_MACRO_AT_MOST_ONCE_REP: &'static str =
"Using the `?` macro Kleene operator for \"at most one\" repetition is unstable";
+pub const EXPLAIN_MACROS_IN_EXTERN: &'static str =
+ "Macro invocations in `extern {}` blocks are experimental.";
+
+// mention proc-macros when enabled
+pub const EXPLAIN_PROC_MACROS_IN_EXTERN: &'static str =
+ "Macro and proc-macro invocations in `extern {}` blocks are experimental.";
+
struct PostExpansionVisitor<'a> {
context: &'a Context<'a>,
}
gate_feature_post!(&self, doc_spotlight, attr.span,
"#[doc(spotlight)] is experimental"
);
+ } else if content.iter().any(|c| c.check_name("alias")) {
+ gate_feature_post!(&self, doc_alias, attr.span,
+ "#[doc(alias = \"...\")] is experimental"
+ );
}
}
}
- // allow attr_literals in #[repr(align(x))]
- let mut is_repr_align = false;
+ // allow attr_literals in #[repr(align(x))] and #[repr(packed(n))]
+ let mut allow_attr_literal = false;
if attr.path == "repr" {
if let Some(content) = attr.meta_item_list() {
- is_repr_align = content.iter().any(|c| c.check_name("align"));
+ allow_attr_literal = content.iter().any(
+ |c| c.check_name("align") || c.check_name("packed"));
}
}
return
}
- if !is_repr_align {
+ if !allow_attr_literal {
let meta = panictry!(attr.parse_meta(self.context.parse_sess));
if contains_novel_literal(&meta) {
gate_feature_post!(&self, attr_literals, attr.span,
gate_feature_post!(&self, underscore_imports, i.span,
"renaming extern crates with `_` is unstable");
}
- if let Some(attr) = attr::find_by_name(&i.attrs[..], "macro_reexport") {
- gate_feature_post!(&self, macro_reexport, attr.span,
- "macros re-exports are experimental \
- and possibly buggy");
- }
}
ast::ItemKind::ForeignMod(ref foreign_module) => {
function may change over time, for now \
a top-level `fn main()` is required");
}
- if let Some(attr) = attr::find_by_name(&i.attrs[..], "must_use") {
- gate_feature_post!(&self, fn_must_use, attr.span,
- "`#[must_use]` on functions is experimental",
- GateStrength::Soft);
- }
}
ast::ItemKind::Struct(..) => {
"the `#[repr(transparent)]` attribute \
is experimental");
}
+ if let Some((name, _)) = item.name_value_literal() {
+ if name == "packed" {
+ gate_feature_post!(&self, repr_packed, attr.span,
+ "the `#[repr(packed(n))]` attribute \
+ is experimental");
+ }
+ }
}
}
}
"trait aliases are not yet fully implemented");
}
- ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, ref impl_items) => {
+ ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => {
if polarity == ast::ImplPolarity::Negative {
gate_feature_post!(&self, optin_builtin_traits,
i.span,
i.span,
"specialization is unstable");
}
-
- for impl_item in impl_items {
- if let ast::ImplItemKind::Method(..) = impl_item.node {
- if let Some(attr) = attr::find_by_name(&impl_item.attrs[..], "must_use") {
- gate_feature_post!(&self, fn_must_use, attr.span,
- "`#[must_use]` on methods is experimental",
- GateStrength::Soft);
- }
- }
- }
}
ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
gate_feature_post!(&self, extern_types, i.span,
"extern types are experimental");
}
+ ast::ForeignItemKind::Macro(..) => {}
}
visit::walk_foreign_item(self, i)
gate_feature_post!(&self, never_type, ty.span,
"The `!` type is experimental");
}
- ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::Dyn) => {
- gate_feature_post!(&self, dyn_trait, ty.span,
- "`dyn Trait` syntax is unstable");
- }
_ => {}
}
visit::walk_ty(self, ty)
gate_feature_post!(&self, type_ascription, e.span,
"type ascription is experimental");
}
- ast::ExprKind::InPlace(..) => {
- gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN);
+ ast::ExprKind::ObsoleteInPlace(..) => {
+ // these get a hard error in ast-validation
}
ast::ExprKind::Yield(..) => {
gate_feature_post!(&self, generators,
fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) {
for segment in &path.segments {
- if segment.identifier.name == keywords::Crate.name() {
- gate_feature_post!(&self, crate_in_paths, segment.span,
+ // Identifiers we are going to check could come from a legacy macro (e.g. `#[test]`).
+ // For such macros identifiers must have empty context, because this context is
+ // used during name resolution and produced names must be unhygienic for compatibility.
+ // On the other hand, we need the actual non-empty context for feature gate checking
+ // because it's hygienic even for legacy macros. As previously stated, such context
+ // cannot be kept in identifiers, so it's kept in paths instead and we take it from
+ // there while keeping location info from the ident span.
+ let span = segment.ident.span.with_ctxt(path.span.ctxt());
+ if segment.ident.name == keywords::Crate.name() {
+ gate_feature_post!(&self, crate_in_paths, span,
"`crate` in paths is experimental");
- } else if segment.identifier.name == keywords::Extern.name() {
- gate_feature_post!(&self, extern_in_paths, segment.span,
+ } else if segment.ident.name == keywords::Extern.name() {
+ gate_feature_post!(&self, extern_in_paths, span,
"`extern` in paths is experimental");
}
}
}
visit::walk_vis(self, vis);
}
+}
- fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
- let (attrs, explain) = match *param {
- ast::GenericParam::Lifetime(ref ld) =>
- (&ld.attrs, "attributes on lifetime bindings are experimental"),
- ast::GenericParam::Type(ref t) =>
- (&t.attrs, "attributes on type parameter bindings are experimental"),
- };
-
- if !attrs.is_empty() {
- gate_feature_post!(&self, generic_param_attrs, attrs[0].span, explain);
+pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
+ crate_edition: Edition) -> 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);
}
-
- visit::walk_generic_param(self, param)
+ err.emit();
}
-}
-pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
- edition: Edition) -> Features {
let mut features = Features::new();
let mut feature_checker = FeatureChecker::default();
for &(.., f_edition, set) in ACTIVE_FEATURES.iter() {
if let Some(f_edition) = f_edition {
- if edition >= f_edition {
- // FIXME(Manishearth) there is currently no way to set
- // lang features by edition
+ if f_edition <= crate_edition {
set(&mut features, DUMMY_SP);
}
}
}
Some(list) => {
for mi in list {
+
let name = if let Some(word) = mi.word() {
word.name()
} else {
set(&mut features, mi.span);
feature_checker.collect(&features, mi.span);
}
- else if let Some(&(_, _, _)) = REMOVED_FEATURES.iter()
- .find(|& &(n, _, _)| name == n)
+ else if let Some(&(.., reason)) = REMOVED_FEATURES.iter()
+ .find(|& &(n, ..)| name == n)
.or_else(|| STABLE_REMOVED_FEATURES.iter()
- .find(|& &(n, _, _)| name == n)) {
- span_err!(span_handler, mi.span, E0557, "feature has been removed");
+ .find(|& &(n, ..)| name == n)) {
+ feature_removed(span_handler, mi.span, reason);
}
- else if let Some(&(_, _, _)) = ACCEPTED_FEATURES.iter()
- .find(|& &(n, _, _)| name == n) {
+ else if let Some(&(..)) = ACCEPTED_FEATURES.iter()
+ .find(|& &(n, ..)| name == n) {
features.declared_stable_lang_features.push((name, mi.span));
+ } else if let Some(&edition) = ALL_EDITIONS.iter()
+ .find(|e| name == e.feature_name()) {
+ if edition <= crate_edition {
+ feature_removed(span_handler, mi.span, None);
+ } else {
+ for &(.., f_edition, set) in ACTIVE_FEATURES.iter() {
+ if let Some(f_edition) = f_edition {
+ if edition >= f_edition {
+ // FIXME(Manishearth) there is currently no way to set
+ // lib features by edition
+ set(&mut features, DUMMY_SP);
+ }
+ }
+ }
+ }
} else {
features.declared_lib_features.push((name, mi.span));
}