Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3417,6 +3417,7 @@ dependencies = [
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_macros",
"rustc_session",
"rustc_span",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
Expand Down
23 changes: 18 additions & 5 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use rustc_ast as ast;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{NodeId, PatKind, attr, token};
use rustc_attr_parsing::AttributeParser;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
use rustc_span::source_map::Spanned;
use rustc_span::{Span, Symbol, sym};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use thin_vec::ThinVec;

use crate::errors;
Expand Down Expand Up @@ -587,17 +590,27 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
return;
}
let mut errored = false;
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {

if let Some(Attribute::Parsed(AttributeKind::Feature(feature_idents, first_span))) =
AttributeParser::parse_limited(
sess,
&krate.attrs,
sym::feature,
DUMMY_SP,
krate.id,
Some(&features),
)
{
// `feature(...)` used on non-nightly. This is definitely an error.
let mut err = errors::FeatureOnNonNightly {
span: attr.span,
span: first_span,
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
stable_features: vec![],
sugg: None,
};

let mut all_stable = true;
for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) {
for ident in feature_idents {
let name = ident.name;
let stable_since = features
.enabled_lang_features()
Expand All @@ -612,7 +625,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
}
}
if all_stable {
err.sugg = Some(attr.span);
err.sugg = Some(first_span);
}
sess.dcx().emit_err(err);
errored = true;
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,7 @@ attr_parsing_whole_archive_needs_static =
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}

attr_parsing_feature_single_word =
rust features are always a single identifier, not paths with multiple segments
.help = did you maybe mean `{$first_segment}`?
53 changes: 53 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::prelude::*;
use crate::session_diagnostics::{FeatureExpectedSingleWord, LimitInvalid};

pub(crate) struct CrateNameParser;

Expand Down Expand Up @@ -147,3 +148,55 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
}

pub(crate) struct FeatureParser;

impl<S: Stage> CombineAttributeParser<S> for FeatureParser {
const PATH: &[Symbol] = &[sym::feature];
type Item = Ident;
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Feature;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]);

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
return Vec::new();
};

if list.is_empty() {
cx.warn_empty_attribute(cx.attr_span);
}

let mut res = Vec::new();

for elem in list.mixed() {
let Some(elem) = elem.meta_item() else {
cx.expected_identifier(elem.span());
continue;
};
if let Err(arg_span) = elem.args().no_args() {
cx.expected_no_args(arg_span);
continue;
}

let path = elem.path();
let Some(ident) = path.word() else {
let first_segment = elem.path().segments().next().expect("at least one segment");
cx.emit_err(FeatureExpectedSingleWord {
span: path.span(),
first_segment_span: first_segment.span,
first_segment: first_segment.name,
});
continue;
};

res.push(ident);
}

res
}
}
6 changes: 4 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ use crate::attributes::codegen_attrs::{
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
CrateNameParser, FeatureParser, MoveSizeLimitParser, NoCoreParser, NoStdParser,
PatternComplexityLimitParser, RecursionLimitParser, RustcCoherenceIsCoreParser,
TypeLengthLimitParser,
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
Expand Down Expand Up @@ -165,6 +166,7 @@ attribute_parsers!(
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<DebuggerViualizerParser>,
Combine<FeatureParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,3 +968,14 @@ pub(crate) struct LimitInvalid<'a> {
pub value_span: Span,
pub error_str: &'a str,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_feature_single_word)]
pub(crate) struct FeatureExpectedSingleWord {
#[primary_span]
pub span: Span,

#[help]
pub first_segment_span: Span,
pub first_segment: Symbol,
}
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0556.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.

The `feature` attribute was badly formed.

Erroneous code example:

```compile_fail,E0556
```compile_fail
#![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] // error!
#![feature] // error!
#![feature = "foo"] // error!
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ E0550: 0550,
E0551: 0551,
E0552: 0552,
E0554: 0554,
E0556: 0556,
E0556: 0556, // REMOVED: merged with other attribute error codes
E0557: 0557,
E0559: 0559,
E0560: 0560,
Expand Down
95 changes: 41 additions & 54 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
};
use rustc_ast::{
self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner,
NodeId, NormalAttr,
self as ast, AttrKind, AttrStyle, Attribute, DUMMY_NODE_ID, HasAttrs, HasTokens, MetaItem,
MetaItemInner, NodeId, NormalAttr,
};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety;
Expand All @@ -21,16 +21,16 @@ use rustc_feature::{
ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features,
REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES,
};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::{self as hir};
use rustc_session::Session;
use rustc_session::parse::feature_err;
use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym};
use thin_vec::ThinVec;
use rustc_span::{DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym};
use tracing::instrument;

use crate::errors::{
CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved,
FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, MalformedFeatureAttributeHelp,
RemoveExprNotSupported,
FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported,
};

/// A folder that strips out items that do not belong in the current configuration.
Expand All @@ -45,44 +45,23 @@ pub struct StripUnconfigured<'a> {
}

pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features {
fn feature_list(attr: &Attribute) -> ThinVec<ast::MetaItemInner> {
if attr.has_name(sym::feature)
&& let Some(list) = attr.meta_item_list()
{
list
} else {
ThinVec::new()
}
}

let mut features = Features::default();

// Process all features enabled 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) => {
sess.dcx().emit_err(MalformedFeatureAttribute {
span: mi.span(),
help: MalformedFeatureAttributeHelp::Suggestion {
span: mi.span(),
suggestion: ident.name,
},
});
continue;
}
None => {
sess.dcx().emit_err(MalformedFeatureAttribute {
span: mi.span(),
help: MalformedFeatureAttributeHelp::Label { span: mi.span() },
});
continue;
}
};

if let Some(hir::Attribute::Parsed(AttributeKind::Feature(feature_idents, _))) =
AttributeParser::parse_limited(
sess,
krate_attrs,
sym::feature,
DUMMY_SP,
DUMMY_NODE_ID,
Some(&features),
)
{
for feature_ident in feature_idents {
// If the enabled feature has been removed, issue an error.
if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) {
if let Some(f) =
REMOVED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.feature.name)
{
let pull_note = if let Some(pull) = f.pull {
format!(
"; see <https://github.com/rust-lang/rust/pull/{}> for more information",
Expand All @@ -92,7 +71,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
"".to_owned()
};
sess.dcx().emit_err(FeatureRemoved {
span: mi.span(),
span: feature_ident.span,
reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
removed_rustc_version: f.feature.since,
pull_note,
Expand All @@ -101,10 +80,10 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
}

// If the enabled feature is stable, record it.
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) {
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name) {
features.set_enabled_lang_feature(EnabledLangFeature {
gate_name: name,
attr_sp: mi.span(),
gate_name: feature_ident.name,
attr_sp: feature_ident.span,
stable_since: Some(Symbol::intern(f.since)),
});
continue;
Expand All @@ -114,38 +93,46 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
// 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.dcx().emit_err(FeatureNotAllowed { span: mi.span(), name });
if allowed.iter().all(|f| feature_ident.name.as_str() != f) {
sess.dcx().emit_err(FeatureNotAllowed {
span: feature_ident.span,
name: feature_ident.name,
});
continue;
}
}

// If the enabled feature is unstable, record it.
if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() {
if UNSTABLE_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name).is_some() {
// When the ICE comes a standard library crate, 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) && !STDLIB_STABLE_CRATES.contains(&crate_name) {
if features.internal(feature_ident.name)
&& !STDLIB_STABLE_CRATES.contains(&crate_name)
{
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
}

features.set_enabled_lang_feature(EnabledLangFeature {
gate_name: name,
attr_sp: mi.span(),
gate_name: feature_ident.name,
attr_sp: feature_ident.span,
stable_since: None,
});
continue;
}

// Otherwise, the feature is unknown. Enable it as a lib feature.
// It will be checked later whether the feature really exists.
features
.set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() });
features.set_enabled_lib_feature(EnabledLibFeature {
gate_name: feature_ident.name,
attr_sp: feature_ident.span,
});

// Similar to above, detect internal lib features to suppress
// the ICE message that asks for a report.
if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) {
if features.internal(feature_ident.name) && !STDLIB_STABLE_CRATES.contains(&crate_name)
{
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
Expand Down
24 changes: 0 additions & 24 deletions compiler/rustc_expand/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,30 +143,6 @@ pub(crate) struct RecursionLimitReached {
pub crate_name: Symbol,
}

#[derive(Diagnostic)]
#[diag(expand_malformed_feature_attribute, code = E0556)]
pub(crate) struct MalformedFeatureAttribute {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub help: MalformedFeatureAttributeHelp,
}

#[derive(Subdiagnostic)]
pub(crate) enum MalformedFeatureAttributeHelp {
#[label(expand_expected)]
Label {
#[primary_span]
span: Span,
},
#[suggestion(expand_expected, code = "{suggestion}", applicability = "maybe-incorrect")]
Suggestion {
#[primary_span]
span: Span,
suggestion: Symbol,
},
}

#[derive(Diagnostic)]
#[diag(expand_remove_expr_not_supported)]
pub(crate) struct RemoveExprNotSupported {
Expand Down
Loading
Loading