Skip to content

Commit ce78d6f

Browse files
committed
port #[feature] to the new attribute parsing infrastructure
1 parent 9f32ccf commit ce78d6f

File tree

7 files changed

+152
-68
lines changed

7 files changed

+152
-68
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 & 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,58 @@ 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+
194+
// FIXME: recursion limit is allowed on all targets and ignored,
195+
// even though it should only be valid on crates of course
196+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
197+
const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]);
198+
199+
fn extend<'c>(
200+
cx: &'c mut AcceptContext<'_, '_, S>,
201+
args: &'c ArgParser<'_>,
202+
) -> impl IntoIterator<Item = Self::Item> + 'c {
203+
let ArgParser::List(list) = args else {
204+
cx.expected_list(cx.attr_span);
205+
return Vec::new();
206+
};
207+
208+
if list.is_empty() {
209+
cx.warn_empty_attribute(cx.attr_span);
210+
}
211+
212+
let mut res = Vec::new();
213+
214+
for elem in list.mixed() {
215+
let Some(elem) = elem.meta_item() else {
216+
cx.expected_identifier(elem.span());
217+
continue;
218+
};
219+
if let Err(arg_span) = elem.args().no_args() {
220+
cx.expected_no_args(arg_span);
221+
continue;
222+
}
223+
224+
let path = elem.path();
225+
let Some(ident) = path.word() else {
226+
let first_segment = elem.path().segments().next().expect("at least one segment");
227+
cx.emit_err(FeatureExpectedSingleWord {
228+
span: path.span(),
229+
first_segment_span: first_segment.span,
230+
first_segment: first_segment.name,
231+
});
232+
continue;
233+
};
234+
235+
res.push(ident);
236+
}
237+
238+
res
239+
}
240+
}

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_hir/src/attrs/data_structures.rs

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

514+
/// Represents `#[feature(...)]`
515+
Feature(ThinVec<Ident>, Span),
516+
514517
/// Represents `#[ffi_const]`.
515518
FfiConst(Span),
516519

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

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

compiler/rustc_passes/src/check_attr.rs

Lines changed: 73 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
281281
| AttributeKind::ObjcClass { .. }
282282
| AttributeKind::ObjcSelector { .. }
283283
| AttributeKind::RustcCoherenceIsCore(..)
284+
| AttributeKind::Feature(..)
284285
) => { /* do nothing */ }
285286
Attribute::Unparsed(attr_item) => {
286287
style = Some(attr_item.style);
@@ -1874,75 +1875,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
18741875
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
18751876
// Warn on useless empty attributes.
18761877
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
1877-
let note = if attr.has_any_name(&[
1878-
sym::allow,
1879-
sym::expect,
1880-
sym::warn,
1881-
sym::deny,
1882-
sym::forbid,
1883-
sym::feature,
1884-
]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
1885-
{
1886-
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1887-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1888-
&& let Some(meta) = attr.meta_item_list()
1889-
&& let [meta] = meta.as_slice()
1890-
&& let Some(item) = meta.meta_item()
1891-
&& let MetaItemKind::NameValue(_) = &item.kind
1892-
&& item.path == sym::reason
1893-
{
1894-
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1895-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1896-
&& let Some(meta) = attr.meta_item_list()
1897-
&& meta.iter().any(|meta| {
1898-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1899-
})
1900-
{
1901-
if hir_id != CRATE_HIR_ID {
1902-
match style {
1903-
Some(ast::AttrStyle::Outer) => {
1904-
let attr_span = attr.span();
1905-
let bang_position = self
1906-
.tcx
1907-
.sess
1908-
.source_map()
1909-
.span_until_char(attr_span, '[')
1910-
.shrink_to_hi();
1911-
1912-
self.tcx.emit_node_span_lint(
1878+
let note =
1879+
if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])
1880+
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
1881+
{
1882+
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1883+
} else if attr.has_any_name(&[
1884+
sym::allow,
1885+
sym::warn,
1886+
sym::deny,
1887+
sym::forbid,
1888+
sym::expect,
1889+
]) && let Some(meta) = attr.meta_item_list()
1890+
&& let [meta] = meta.as_slice()
1891+
&& let Some(item) = meta.meta_item()
1892+
&& let MetaItemKind::NameValue(_) = &item.kind
1893+
&& item.path == sym::reason
1894+
{
1895+
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1896+
} else if attr.has_any_name(&[
1897+
sym::allow,
1898+
sym::warn,
1899+
sym::deny,
1900+
sym::forbid,
1901+
sym::expect,
1902+
]) && let Some(meta) = attr.meta_item_list()
1903+
&& meta.iter().any(|meta| {
1904+
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1905+
})
1906+
{
1907+
if hir_id != CRATE_HIR_ID {
1908+
match style {
1909+
Some(ast::AttrStyle::Outer) => {
1910+
let attr_span = attr.span();
1911+
let bang_position = self
1912+
.tcx
1913+
.sess
1914+
.source_map()
1915+
.span_until_char(attr_span, '[')
1916+
.shrink_to_hi();
1917+
1918+
self.tcx.emit_node_span_lint(
1919+
UNUSED_ATTRIBUTES,
1920+
hir_id,
1921+
attr_span,
1922+
errors::OuterCrateLevelAttr {
1923+
suggestion: errors::OuterCrateLevelAttrSuggestion {
1924+
bang_position,
1925+
},
1926+
},
1927+
)
1928+
}
1929+
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
19131930
UNUSED_ATTRIBUTES,
19141931
hir_id,
1915-
attr_span,
1916-
errors::OuterCrateLevelAttr {
1917-
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
1918-
},
1919-
)
1920-
}
1921-
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
1922-
UNUSED_ATTRIBUTES,
1923-
hir_id,
1924-
attr.span(),
1925-
errors::InnerCrateLevelAttr,
1926-
),
1927-
};
1928-
return;
1929-
} else {
1930-
let never_needs_link = self
1931-
.tcx
1932-
.crate_types()
1933-
.iter()
1934-
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1935-
if never_needs_link {
1936-
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1937-
} else {
1932+
attr.span(),
1933+
errors::InnerCrateLevelAttr,
1934+
),
1935+
};
19381936
return;
1937+
} else {
1938+
let never_needs_link = self
1939+
.tcx
1940+
.crate_types()
1941+
.iter()
1942+
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1943+
if never_needs_link {
1944+
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1945+
} else {
1946+
return;
1947+
}
19391948
}
1940-
}
1941-
} else if attr.has_name(sym::default_method_body_is_const) {
1942-
errors::UnusedNote::DefaultMethodBodyConst
1943-
} else {
1944-
return;
1945-
};
1949+
} else if attr.has_name(sym::default_method_body_is_const) {
1950+
errors::UnusedNote::DefaultMethodBodyConst
1951+
} else {
1952+
return;
1953+
};
19461954

19471955
self.tcx.emit_node_span_lint(
19481956
UNUSED_ATTRIBUTES,

0 commit comments

Comments
 (0)