summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_expand/src/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_expand/src/config.rs')
-rw-r--r--compiler/rustc_expand/src/config.rs211
1 files changed, 105 insertions, 106 deletions
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 8658cea13..bef487659 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -13,17 +13,16 @@ use rustc_ast::NodeId;
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem};
use rustc_attr as attr;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_feature::{Feature, Features, State as FeatureState};
-use rustc_feature::{
- ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
-};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_feature::Features;
+use rustc_feature::{ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES};
use rustc_parse::validate_attr;
use rustc_session::parse::feature_err;
use rustc_session::Session;
-use rustc_span::edition::{Edition, ALL_EDITIONS};
+use rustc_span::edition::ALL_EDITIONS;
use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
+use thin_vec::ThinVec;
/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
@@ -36,85 +35,56 @@ pub struct StripUnconfigured<'a> {
pub lint_node_id: NodeId,
}
-pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
- fn feature_removed(sess: &Session, span: Span, reason: Option<&str>) {
- sess.emit_err(FeatureRemoved {
- span,
- reason: reason.map(|reason| FeatureRemovedReason { reason }),
- });
- }
-
- fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
- ACTIVE_FEATURES.iter().filter(move |feature| {
- if let Some(feature_edition) = feature.edition {
- feature_edition <= edition
- } else {
- false
- }
- })
+pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features {
+ fn feature_list(attr: &Attribute) -> ThinVec<ast::NestedMetaItem> {
+ if attr.has_name(sym::feature)
+ && let Some(list) = attr.meta_item_list()
+ {
+ list
+ } else {
+ ThinVec::new()
+ }
}
let mut features = Features::default();
- let mut edition_enabled_features = FxHashMap::default();
- let crate_edition = sess.edition();
-
- 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 feature in active_features_up_to(crate_edition) {
- feature.set(&mut features, DUMMY_SP);
- edition_enabled_features.insert(feature.name, crate_edition);
- }
+ // The edition from `--edition`.
+ let crate_edition = sess.edition();
- // Process the edition umbrella feature-gates first, to ensure
- // `edition_enabled_features` is completed before it's queried.
+ // The maximum of (a) the edition from `--edition` and (b) any edition
+ // umbrella feature-gates declared in the code.
+ // - E.g. if `crate_edition` is 2015 but `rust_2018_preview` is present,
+ // `feature_edition` is 2018
+ let mut features_edition = crate_edition;
for attr in krate_attrs {
- if !attr.has_name(sym::feature) {
- continue;
- }
-
- let Some(list) = attr.meta_item_list() else {
- continue;
- };
-
- for mi in list {
- if !mi.is_word() {
- continue;
- }
-
- let name = mi.name_or_empty();
-
- let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
- if let Some(edition) = edition {
- if edition <= crate_edition {
- continue;
- }
-
- for feature in active_features_up_to(edition) {
- // FIXME(Manishearth) there is currently no way to set
- // lib features by edition
- feature.set(&mut features, DUMMY_SP);
- edition_enabled_features.insert(feature.name, edition);
+ for mi in feature_list(attr) {
+ if mi.is_word() {
+ let name = mi.name_or_empty();
+ let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
+ if let Some(edition) = edition
+ && edition > features_edition
+ {
+ features_edition = edition;
}
}
}
}
- for attr in krate_attrs {
- if !attr.has_name(sym::feature) {
- continue;
+ // Enable edition-dependent features based on `features_edition`.
+ // - E.g. enable `test_2018_feature` if `features_edition` is 2018 or higher
+ let mut edition_enabled_features = FxHashSet::default();
+ for f in UNSTABLE_FEATURES {
+ if let Some(edition) = f.feature.edition && edition <= features_edition {
+ // FIXME(Manishearth) there is currently no way to set lib features by
+ // edition.
+ edition_enabled_features.insert(f.feature.name);
+ (f.set_enabled)(&mut features);
}
+ }
- let Some(list) = attr.meta_item_list() else {
- continue;
- };
-
- for mi in list {
+ // Process all features declared in the code.
+ for attr in krate_attrs {
+ for mi in feature_list(attr) {
let name = match mi.ident() {
Some(ident) if mi.is_word() => ident.name,
Some(ident) => {
@@ -136,38 +106,57 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
}
};
- if let Some(&edition) = edition_enabled_features.get(&name) {
+ // If the declared feature is an edition umbrella feature-gate,
+ // warn if it was redundant w.r.t. `crate_edition`.
+ // - E.g. warn if `rust_2018_preview` is declared when
+ // `crate_edition` is 2018
+ // - E.g. don't warn if `rust_2018_preview` is declared when
+ // `crate_edition` is 2015.
+ if let Some(&edition) = ALL_EDITIONS.iter().find(|e| name == e.feature_name()) {
+ if edition <= crate_edition {
+ sess.emit_warning(FeatureIncludedInEdition {
+ span: mi.span(),
+ feature: name,
+ edition,
+ });
+ }
+ features.set_declared_lang_feature(name, mi.span(), None);
+ continue;
+ }
+
+ // If the declared feature is edition-dependent and was already
+ // enabled due to `feature_edition`, give a warning.
+ // - E.g. warn if `test_2018_feature` is declared when
+ // `feature_edition` is 2018 or higher.
+ if edition_enabled_features.contains(&name) {
sess.emit_warning(FeatureIncludedInEdition {
span: mi.span(),
feature: name,
- edition,
+ edition: features_edition,
});
+ features.set_declared_lang_feature(name, mi.span(), None);
continue;
}
- if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
- // Handled in the separate loop above.
+ // If the declared feature has been removed, issue an error.
+ if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
+ sess.emit_err(FeatureRemoved {
+ span: mi.span(),
+ reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
+ });
continue;
}
- let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
- let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
- if let Some(Feature { state, .. }) = removed.or(stable_removed) {
- if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
- state
- {
- feature_removed(sess, mi.span(), *reason);
- continue;
- }
- }
-
- if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
- let since = Some(Symbol::intern(since));
- features.declared_lang_features.push((name, mi.span(), since));
- features.active_features.insert(name);
+ // If the declared feature is stable, record it.
+ if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
+ let since = Some(Symbol::intern(f.since));
+ features.set_declared_lang_feature(name, mi.span(), since);
continue;
}
+ // If `-Z allow-features` is used and the declared feature is
+ // unstable and not also listed as one of the allowed features,
+ // issue an error.
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
if allowed.iter().all(|f| name.as_str() != f) {
sess.emit_err(FeatureNotAllowed { span: mi.span(), name });
@@ -175,15 +164,25 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
}
}
- if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
- f.set(&mut features, mi.span());
- features.declared_lang_features.push((name, mi.span(), None));
- features.active_features.insert(name);
+ // If the declared feature is unstable, record it.
+ if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
+ (f.set_enabled)(&mut features);
+ // When the ICE comes from core, alloc or std (approximation of the standard library), there's a chance
+ // that the person hitting the ICE may be using -Zbuild-std or similar with an untested target.
+ // The bug is probably in the standard library and not the compiler in that case, but that doesn't
+ // really matter - we want a bug report.
+ if features.internal(name)
+ && ![sym::core, sym::alloc, sym::std].contains(&crate_name)
+ {
+ sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
+ }
+ features.set_declared_lang_feature(name, mi.span(), None);
continue;
}
- features.declared_lib_features.push((name, mi.span()));
- features.active_features.insert(name);
+ // Otherwise, the feature is unknown. Record it as a lib feature.
+ // It will be checked later.
+ features.set_declared_lib_feature(name, mi.span());
}
}
@@ -252,7 +251,8 @@ impl<'a> StripUnconfigured<'a> {
let trees: Vec<_> = stream
.0
.iter()
- .flat_map(|tree| match tree.clone() {
+ .flat_map(|tree| {
+ match tree.clone() {
AttrTokenTree::Attributes(mut data) => {
data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
@@ -267,18 +267,17 @@ impl<'a> StripUnconfigured<'a> {
}
AttrTokenTree::Delimited(sp, delim, mut inner) => {
inner = self.configure_tokens(&inner);
- Some(AttrTokenTree::Delimited(sp, delim, inner))
- .into_iter()
+ Some(AttrTokenTree::Delimited(sp, delim, inner)).into_iter()
}
- AttrTokenTree::Token(ref token, _) if let TokenKind::Interpolated(nt) = &token.kind => {
- panic!(
- "Nonterminal should have been flattened at {:?}: {:?}",
- token.span, nt
- );
+ AttrTokenTree::Token(ref token, _)
+ if let TokenKind::Interpolated(nt) = &token.kind =>
+ {
+ panic!("Nonterminal should have been flattened at {:?}: {:?}", token.span, nt);
}
AttrTokenTree::Token(token, spacing) => {
Some(AttrTokenTree::Token(token, spacing)).into_iter()
}
+ }
})
.collect();
AttrTokenStream::new(trees)