Skip to content

Commit 7ad49e9

Browse files
committed
port #[feature] to the new attribute parsing infrastructure
1 parent a00a515 commit 7ad49e9

File tree

7 files changed

+152
-67
lines changed

7 files changed

+152
-67
lines changed

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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::prelude::*;
2+
use crate::session_diagnostics::{FeatureExpectedSingleWord, LimitInvalid};
23

34
pub(crate) struct CrateNameParser;
45

@@ -147,3 +148,58 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
147148
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
148149
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
149150
}
151+
152+
pub(crate) struct FeatureParser;
153+
154+
impl<S: Stage> CombineAttributeParser<S> for FeatureParser {
155+
const PATH: &[Symbol] = &[sym::feature];
156+
type Item = Ident;
157+
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Feature;
158+
159+
// FIXME: recursion limit is allowed on all targets and ignored,
160+
// even though it should only be valid on crates of course
161+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
162+
const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]);
163+
164+
fn extend<'c>(
165+
cx: &'c mut AcceptContext<'_, '_, S>,
166+
args: &'c ArgParser<'_>,
167+
) -> impl IntoIterator<Item = Self::Item> + 'c {
168+
let ArgParser::List(list) = args else {
169+
cx.expected_list(cx.attr_span);
170+
return Vec::new();
171+
};
172+
173+
if list.is_empty() {
174+
cx.warn_empty_attribute(cx.attr_span);
175+
}
176+
177+
let mut res = Vec::new();
178+
179+
for elem in list.mixed() {
180+
let Some(elem) = elem.meta_item() else {
181+
cx.expected_identifier(elem.span());
182+
continue;
183+
};
184+
if let Err(arg_span) = elem.args().no_args() {
185+
cx.expected_no_args(arg_span);
186+
continue;
187+
}
188+
189+
let path = elem.path();
190+
let Some(ident) = path.word() else {
191+
let first_segment = elem.path().segments().next().expect("at least one segment");
192+
cx.emit_err(FeatureExpectedSingleWord {
193+
span: path.span(),
194+
first_segment_span: first_segment.span,
195+
first_segment: first_segment.name,
196+
});
197+
continue;
198+
};
199+
200+
res.push(ident);
201+
}
202+
203+
res
204+
}
205+
}

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::debugger::DebuggerViualizerParser;
3233
use crate::attributes::deprecation::DeprecationParser;
@@ -165,6 +166,7 @@ attribute_parsers!(
165166
Combine<AllowConstFnUnstableParser>,
166167
Combine<AllowInternalUnstableParser>,
167168
Combine<DebuggerViualizerParser>,
169+
Combine<FeatureParser>,
168170
Combine<ForceTargetFeatureParser>,
169171
Combine<LinkParser>,
170172
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_hir/src/attrs/data_structures.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ pub enum AttributeKind {
528528
/// Represents `#[export_stable]`.
529529
ExportStable,
530530

531+
/// Represents `#[feature(...)]`
532+
Feature(ThinVec<Ident>, Span),
533+
531534
/// Represents `#[ffi_const]`.
532535
FfiConst(Span),
533536

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl AttributeKind {
4545
Dummy => No,
4646
ExportName { .. } => Yes,
4747
ExportStable => No,
48+
Feature(..) => No,
4849
FfiConst(..) => No,
4950
FfiPure(..) => No,
5051
Fundamental { .. } => Yes,

compiler/rustc_passes/src/check_attr.rs

Lines changed: 73 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
283283
| AttributeKind::ObjcSelector { .. }
284284
| AttributeKind::RustcCoherenceIsCore(..)
285285
| AttributeKind::DebuggerVisualizer(..)
286+
| AttributeKind::Feature(..)
286287
) => { /* do nothing */ }
287288
Attribute::Unparsed(attr_item) => {
288289
style = Some(attr_item.style);
@@ -1902,75 +1903,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
19021903
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
19031904
// Warn on useless empty attributes.
19041905
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
1905-
let note = if attr.has_any_name(&[
1906-
sym::allow,
1907-
sym::expect,
1908-
sym::warn,
1909-
sym::deny,
1910-
sym::forbid,
1911-
sym::feature,
1912-
]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
1913-
{
1914-
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1915-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1916-
&& let Some(meta) = attr.meta_item_list()
1917-
&& let [meta] = meta.as_slice()
1918-
&& let Some(item) = meta.meta_item()
1919-
&& let MetaItemKind::NameValue(_) = &item.kind
1920-
&& item.path == sym::reason
1921-
{
1922-
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1923-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1924-
&& let Some(meta) = attr.meta_item_list()
1925-
&& meta.iter().any(|meta| {
1926-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1927-
})
1928-
{
1929-
if hir_id != CRATE_HIR_ID {
1930-
match style {
1931-
Some(ast::AttrStyle::Outer) => {
1932-
let attr_span = attr.span();
1933-
let bang_position = self
1934-
.tcx
1935-
.sess
1936-
.source_map()
1937-
.span_until_char(attr_span, '[')
1938-
.shrink_to_hi();
1939-
1940-
self.tcx.emit_node_span_lint(
1906+
let note =
1907+
if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])
1908+
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
1909+
{
1910+
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1911+
} else if attr.has_any_name(&[
1912+
sym::allow,
1913+
sym::warn,
1914+
sym::deny,
1915+
sym::forbid,
1916+
sym::expect,
1917+
]) && let Some(meta) = attr.meta_item_list()
1918+
&& let [meta] = meta.as_slice()
1919+
&& let Some(item) = meta.meta_item()
1920+
&& let MetaItemKind::NameValue(_) = &item.kind
1921+
&& item.path == sym::reason
1922+
{
1923+
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1924+
} else if attr.has_any_name(&[
1925+
sym::allow,
1926+
sym::warn,
1927+
sym::deny,
1928+
sym::forbid,
1929+
sym::expect,
1930+
]) && let Some(meta) = attr.meta_item_list()
1931+
&& meta.iter().any(|meta| {
1932+
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1933+
})
1934+
{
1935+
if hir_id != CRATE_HIR_ID {
1936+
match style {
1937+
Some(ast::AttrStyle::Outer) => {
1938+
let attr_span = attr.span();
1939+
let bang_position = self
1940+
.tcx
1941+
.sess
1942+
.source_map()
1943+
.span_until_char(attr_span, '[')
1944+
.shrink_to_hi();
1945+
1946+
self.tcx.emit_node_span_lint(
1947+
UNUSED_ATTRIBUTES,
1948+
hir_id,
1949+
attr_span,
1950+
errors::OuterCrateLevelAttr {
1951+
suggestion: errors::OuterCrateLevelAttrSuggestion {
1952+
bang_position,
1953+
},
1954+
},
1955+
)
1956+
}
1957+
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
19411958
UNUSED_ATTRIBUTES,
19421959
hir_id,
1943-
attr_span,
1944-
errors::OuterCrateLevelAttr {
1945-
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
1946-
},
1947-
)
1948-
}
1949-
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
1950-
UNUSED_ATTRIBUTES,
1951-
hir_id,
1952-
attr.span(),
1953-
errors::InnerCrateLevelAttr,
1954-
),
1955-
};
1956-
return;
1957-
} else {
1958-
let never_needs_link = self
1959-
.tcx
1960-
.crate_types()
1961-
.iter()
1962-
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1963-
if never_needs_link {
1964-
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1965-
} else {
1960+
attr.span(),
1961+
errors::InnerCrateLevelAttr,
1962+
),
1963+
};
19661964
return;
1965+
} else {
1966+
let never_needs_link = self
1967+
.tcx
1968+
.crate_types()
1969+
.iter()
1970+
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1971+
if never_needs_link {
1972+
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1973+
} else {
1974+
return;
1975+
}
19671976
}
1968-
}
1969-
} else if attr.has_name(sym::default_method_body_is_const) {
1970-
errors::UnusedNote::DefaultMethodBodyConst
1971-
} else {
1972-
return;
1973-
};
1977+
} else if attr.has_name(sym::default_method_body_is_const) {
1978+
errors::UnusedNote::DefaultMethodBodyConst
1979+
} else {
1980+
return;
1981+
};
19741982

19751983
self.tcx.emit_node_span_lint(
19761984
UNUSED_ATTRIBUTES,

0 commit comments

Comments
 (0)