Skip to content

Commit ce358ff

Browse files
authored
Rollup merge of rust-lang#146652 - jdonszelmann:convert-feature-attr-parser, r=nnethercote
Port `feature` to the new attribute system
2 parents 63a296a + 858e26c commit ce358ff

File tree

25 files changed

+348
-285
lines changed

25 files changed

+348
-285
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,6 +3409,7 @@ dependencies = [
34093409
"rustc_errors",
34103410
"rustc_feature",
34113411
"rustc_fluent_macro",
3412+
"rustc_hir",
34123413
"rustc_macros",
34133414
"rustc_session",
34143415
"rustc_span",

compiler/rustc_ast_passes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
1414
rustc_errors = { path = "../rustc_errors" }
1515
rustc_feature = { path = "../rustc_feature" }
1616
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
17+
rustc_hir = { path = "../rustc_hir" }
1718
rustc_macros = { path = "../rustc_macros" }
1819
rustc_session = { path = "../rustc_session" }
1920
rustc_span = { path = "../rustc_span" }

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use rustc_ast as ast;
22
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
33
use rustc_ast::{NodeId, PatKind, attr, token};
4+
use rustc_attr_parsing::AttributeParser;
45
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
6+
use rustc_hir::Attribute;
7+
use rustc_hir::attrs::AttributeKind;
58
use rustc_session::Session;
69
use rustc_session::parse::{feature_err, feature_warn};
710
use rustc_span::source_map::Spanned;
8-
use rustc_span::{Span, Symbol, sym};
11+
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
912
use thin_vec::ThinVec;
1013

1114
use crate::errors;
@@ -587,17 +590,27 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
587590
return;
588591
}
589592
let mut errored = false;
590-
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
593+
594+
if let Some(Attribute::Parsed(AttributeKind::Feature(feature_idents, first_span))) =
595+
AttributeParser::parse_limited(
596+
sess,
597+
&krate.attrs,
598+
sym::feature,
599+
DUMMY_SP,
600+
krate.id,
601+
Some(&features),
602+
)
603+
{
591604
// `feature(...)` used on non-nightly. This is definitely an error.
592605
let mut err = errors::FeatureOnNonNightly {
593-
span: attr.span,
606+
span: first_span,
594607
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
595608
stable_features: vec![],
596609
sugg: None,
597610
};
598611

599612
let mut all_stable = true;
600-
for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) {
613+
for ident in feature_idents {
601614
let name = ident.name;
602615
let stable_since = features
603616
.enabled_lang_features()
@@ -612,7 +625,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
612625
}
613626
}
614627
if all_stable {
615-
err.sugg = Some(attr.span);
628+
err.sugg = Some(first_span);
616629
}
617630
sess.dcx().emit_err(err);
618631
errored = true;

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,7 @@ attr_parsing_whole_archive_needs_static =
259259
attr_parsing_limit_invalid =
260260
`limit` must be a non-negative integer
261261
.label = {$error_str}
262+
263+
attr_parsing_feature_single_word =
264+
rust features are always a single identifier, not paths with multiple segments
265+
.help = did you maybe mean `{$first_segment}`?

compiler/rustc_attr_parsing/src/attributes/crate_level.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::num::IntErrorKind;
33
use rustc_hir::limit::Limit;
44

55
use super::prelude::*;
6-
use crate::session_diagnostics::LimitInvalid;
6+
use crate::session_diagnostics::{FeatureExpectedSingleWord, LimitInvalid};
77

88
impl<S: Stage> AcceptContext<'_, '_, S> {
99
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
@@ -183,3 +183,55 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
183183
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
184184
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
185185
}
186+
187+
pub(crate) struct FeatureParser;
188+
189+
impl<S: Stage> CombineAttributeParser<S> for FeatureParser {
190+
const PATH: &[Symbol] = &[sym::feature];
191+
type Item = Ident;
192+
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Feature;
193+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
194+
const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]);
195+
196+
fn extend<'c>(
197+
cx: &'c mut AcceptContext<'_, '_, S>,
198+
args: &'c ArgParser<'_>,
199+
) -> impl IntoIterator<Item = Self::Item> + 'c {
200+
let ArgParser::List(list) = args else {
201+
cx.expected_list(cx.attr_span);
202+
return Vec::new();
203+
};
204+
205+
if list.is_empty() {
206+
cx.warn_empty_attribute(cx.attr_span);
207+
}
208+
209+
let mut res = Vec::new();
210+
211+
for elem in list.mixed() {
212+
let Some(elem) = elem.meta_item() else {
213+
cx.expected_identifier(elem.span());
214+
continue;
215+
};
216+
if let Err(arg_span) = elem.args().no_args() {
217+
cx.expected_no_args(arg_span);
218+
continue;
219+
}
220+
221+
let path = elem.path();
222+
let Some(ident) = path.word() else {
223+
let first_segment = elem.path().segments().next().expect("at least one segment");
224+
cx.emit_err(FeatureExpectedSingleWord {
225+
span: path.span(),
226+
first_segment_span: first_segment.span,
227+
first_segment: first_segment.name,
228+
});
229+
continue;
230+
};
231+
232+
res.push(ident);
233+
}
234+
235+
res
236+
}
237+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ use crate::attributes::codegen_attrs::{
2525
};
2626
use crate::attributes::confusables::ConfusablesParser;
2727
use crate::attributes::crate_level::{
28-
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
29-
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
28+
CrateNameParser, FeatureParser, MoveSizeLimitParser, NoCoreParser, NoStdParser,
29+
PatternComplexityLimitParser, RecursionLimitParser, RustcCoherenceIsCoreParser,
30+
TypeLengthLimitParser,
3031
};
3132
use crate::attributes::deprecation::DeprecationParser;
3233
use crate::attributes::dummy::DummyParser;
@@ -163,6 +164,7 @@ attribute_parsers!(
163164
// tidy-alphabetical-start
164165
Combine<AllowConstFnUnstableParser>,
165166
Combine<AllowInternalUnstableParser>,
167+
Combine<FeatureParser>,
166168
Combine<ForceTargetFeatureParser>,
167169
Combine<LinkParser>,
168170
Combine<ReprParser>,

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,3 +968,14 @@ pub(crate) struct LimitInvalid<'a> {
968968
pub value_span: Span,
969969
pub error_str: &'a str,
970970
}
971+
972+
#[derive(Diagnostic)]
973+
#[diag(attr_parsing_feature_single_word)]
974+
pub(crate) struct FeatureExpectedSingleWord {
975+
#[primary_span]
976+
pub span: Span,
977+
978+
#[help]
979+
pub first_segment_span: Span,
980+
pub first_segment: Symbol,
981+
}

compiler/rustc_error_codes/src/error_codes/E0556.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
The `feature` attribute was badly formed.
24

35
Erroneous code example:
46

5-
```compile_fail,E0556
7+
```compile_fail
68
#![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] // error!
79
#![feature] // error!
810
#![feature = "foo"] // error!

compiler/rustc_error_codes/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ E0550: 0550,
338338
E0551: 0551,
339339
E0552: 0552,
340340
E0554: 0554,
341-
E0556: 0556,
341+
E0556: 0556, // REMOVED: merged with other attribute error codes
342342
E0557: 0557,
343343
E0559: 0559,
344344
E0560: 0560,

compiler/rustc_expand/src/config.rs

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{
77
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
88
};
99
use rustc_ast::{
10-
self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner,
11-
NodeId, NormalAttr,
10+
self as ast, AttrKind, AttrStyle, Attribute, DUMMY_NODE_ID, HasAttrs, HasTokens, MetaItem,
11+
MetaItemInner, NodeId, NormalAttr,
1212
};
1313
use rustc_attr_parsing as attr;
1414
use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety;
@@ -21,16 +21,16 @@ use rustc_feature::{
2121
ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features,
2222
REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES,
2323
};
24+
use rustc_hir::attrs::AttributeKind;
25+
use rustc_hir::{self as hir};
2426
use rustc_session::Session;
2527
use rustc_session::parse::feature_err;
26-
use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym};
27-
use thin_vec::ThinVec;
28+
use rustc_span::{DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym};
2829
use tracing::instrument;
2930

3031
use crate::errors::{
3132
CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved,
32-
FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, MalformedFeatureAttributeHelp,
33-
RemoveExprNotSupported,
33+
FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported,
3434
};
3535

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

4747
pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features {
48-
fn feature_list(attr: &Attribute) -> ThinVec<ast::MetaItemInner> {
49-
if attr.has_name(sym::feature)
50-
&& let Some(list) = attr.meta_item_list()
51-
{
52-
list
53-
} else {
54-
ThinVec::new()
55-
}
56-
}
57-
5848
let mut features = Features::default();
5949

60-
// Process all features enabled in the code.
61-
for attr in krate_attrs {
62-
for mi in feature_list(attr) {
63-
let name = match mi.ident() {
64-
Some(ident) if mi.is_word() => ident.name,
65-
Some(ident) => {
66-
sess.dcx().emit_err(MalformedFeatureAttribute {
67-
span: mi.span(),
68-
help: MalformedFeatureAttributeHelp::Suggestion {
69-
span: mi.span(),
70-
suggestion: ident.name,
71-
},
72-
});
73-
continue;
74-
}
75-
None => {
76-
sess.dcx().emit_err(MalformedFeatureAttribute {
77-
span: mi.span(),
78-
help: MalformedFeatureAttributeHelp::Label { span: mi.span() },
79-
});
80-
continue;
81-
}
82-
};
83-
50+
if let Some(hir::Attribute::Parsed(AttributeKind::Feature(feature_idents, _))) =
51+
AttributeParser::parse_limited(
52+
sess,
53+
krate_attrs,
54+
sym::feature,
55+
DUMMY_SP,
56+
DUMMY_NODE_ID,
57+
Some(&features),
58+
)
59+
{
60+
for feature_ident in feature_idents {
8461
// If the enabled feature has been removed, issue an error.
85-
if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) {
62+
if let Some(f) =
63+
REMOVED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.feature.name)
64+
{
8665
let pull_note = if let Some(pull) = f.pull {
8766
format!(
8867
"; see <https://github.com/rust-lang/rust/pull/{}> for more information",
@@ -92,7 +71,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
9271
"".to_owned()
9372
};
9473
sess.dcx().emit_err(FeatureRemoved {
95-
span: mi.span(),
74+
span: feature_ident.span,
9675
reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
9776
removed_rustc_version: f.feature.since,
9877
pull_note,
@@ -101,10 +80,10 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
10180
}
10281

10382
// If the enabled feature is stable, record it.
104-
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) {
83+
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name) {
10584
features.set_enabled_lang_feature(EnabledLangFeature {
106-
gate_name: name,
107-
attr_sp: mi.span(),
85+
gate_name: feature_ident.name,
86+
attr_sp: feature_ident.span,
10887
stable_since: Some(Symbol::intern(f.since)),
10988
});
11089
continue;
@@ -114,38 +93,46 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
11493
// unstable and not also listed as one of the allowed features,
11594
// issue an error.
11695
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
117-
if allowed.iter().all(|f| name.as_str() != f) {
118-
sess.dcx().emit_err(FeatureNotAllowed { span: mi.span(), name });
96+
if allowed.iter().all(|f| feature_ident.name.as_str() != f) {
97+
sess.dcx().emit_err(FeatureNotAllowed {
98+
span: feature_ident.span,
99+
name: feature_ident.name,
100+
});
119101
continue;
120102
}
121103
}
122104

123105
// If the enabled feature is unstable, record it.
124-
if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() {
106+
if UNSTABLE_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name).is_some() {
125107
// When the ICE comes a standard library crate, there's a chance that the person
126108
// hitting the ICE may be using -Zbuild-std or similar with an untested target.
127109
// The bug is probably in the standard library and not the compiler in that case,
128110
// but that doesn't really matter - we want a bug report.
129-
if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) {
111+
if features.internal(feature_ident.name)
112+
&& !STDLIB_STABLE_CRATES.contains(&crate_name)
113+
{
130114
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
131115
}
132116

133117
features.set_enabled_lang_feature(EnabledLangFeature {
134-
gate_name: name,
135-
attr_sp: mi.span(),
118+
gate_name: feature_ident.name,
119+
attr_sp: feature_ident.span,
136120
stable_since: None,
137121
});
138122
continue;
139123
}
140124

141125
// Otherwise, the feature is unknown. Enable it as a lib feature.
142126
// It will be checked later whether the feature really exists.
143-
features
144-
.set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() });
127+
features.set_enabled_lib_feature(EnabledLibFeature {
128+
gate_name: feature_ident.name,
129+
attr_sp: feature_ident.span,
130+
});
145131

146132
// Similar to above, detect internal lib features to suppress
147133
// the ICE message that asks for a report.
148-
if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) {
134+
if features.internal(feature_ident.name) && !STDLIB_STABLE_CRATES.contains(&crate_name)
135+
{
149136
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
150137
}
151138
}

0 commit comments

Comments
 (0)