Skip to content

Commit c6e9289

Browse files
committed
port #[feature] to the new attribute parsing infrastructure
1 parent ade8487 commit c6e9289

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);
@@ -1861,75 +1862,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
18611862
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
18621863
// Warn on useless empty attributes.
18631864
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
1864-
let note = if attr.has_any_name(&[
1865-
sym::allow,
1866-
sym::expect,
1867-
sym::warn,
1868-
sym::deny,
1869-
sym::forbid,
1870-
sym::feature,
1871-
]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
1872-
{
1873-
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1874-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1875-
&& let Some(meta) = attr.meta_item_list()
1876-
&& let [meta] = meta.as_slice()
1877-
&& let Some(item) = meta.meta_item()
1878-
&& let MetaItemKind::NameValue(_) = &item.kind
1879-
&& item.path == sym::reason
1880-
{
1881-
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1882-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1883-
&& let Some(meta) = attr.meta_item_list()
1884-
&& meta.iter().any(|meta| {
1885-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1886-
})
1887-
{
1888-
if hir_id != CRATE_HIR_ID {
1889-
match style {
1890-
Some(ast::AttrStyle::Outer) => {
1891-
let attr_span = attr.span();
1892-
let bang_position = self
1893-
.tcx
1894-
.sess
1895-
.source_map()
1896-
.span_until_char(attr_span, '[')
1897-
.shrink_to_hi();
1898-
1899-
self.tcx.emit_node_span_lint(
1865+
let note =
1866+
if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])
1867+
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
1868+
{
1869+
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1870+
} else if attr.has_any_name(&[
1871+
sym::allow,
1872+
sym::warn,
1873+
sym::deny,
1874+
sym::forbid,
1875+
sym::expect,
1876+
]) && let Some(meta) = attr.meta_item_list()
1877+
&& let [meta] = meta.as_slice()
1878+
&& let Some(item) = meta.meta_item()
1879+
&& let MetaItemKind::NameValue(_) = &item.kind
1880+
&& item.path == sym::reason
1881+
{
1882+
errors::UnusedNote::NoLints { 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+
&& meta.iter().any(|meta| {
1891+
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1892+
})
1893+
{
1894+
if hir_id != CRATE_HIR_ID {
1895+
match style {
1896+
Some(ast::AttrStyle::Outer) => {
1897+
let attr_span = attr.span();
1898+
let bang_position = self
1899+
.tcx
1900+
.sess
1901+
.source_map()
1902+
.span_until_char(attr_span, '[')
1903+
.shrink_to_hi();
1904+
1905+
self.tcx.emit_node_span_lint(
1906+
UNUSED_ATTRIBUTES,
1907+
hir_id,
1908+
attr_span,
1909+
errors::OuterCrateLevelAttr {
1910+
suggestion: errors::OuterCrateLevelAttrSuggestion {
1911+
bang_position,
1912+
},
1913+
},
1914+
)
1915+
}
1916+
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
19001917
UNUSED_ATTRIBUTES,
19011918
hir_id,
1902-
attr_span,
1903-
errors::OuterCrateLevelAttr {
1904-
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
1905-
},
1906-
)
1907-
}
1908-
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
1909-
UNUSED_ATTRIBUTES,
1910-
hir_id,
1911-
attr.span(),
1912-
errors::InnerCrateLevelAttr,
1913-
),
1914-
};
1915-
return;
1916-
} else {
1917-
let never_needs_link = self
1918-
.tcx
1919-
.crate_types()
1920-
.iter()
1921-
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1922-
if never_needs_link {
1923-
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1924-
} else {
1919+
attr.span(),
1920+
errors::InnerCrateLevelAttr,
1921+
),
1922+
};
19251923
return;
1924+
} else {
1925+
let never_needs_link = self
1926+
.tcx
1927+
.crate_types()
1928+
.iter()
1929+
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1930+
if never_needs_link {
1931+
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1932+
} else {
1933+
return;
1934+
}
19261935
}
1927-
}
1928-
} else if attr.has_name(sym::default_method_body_is_const) {
1929-
errors::UnusedNote::DefaultMethodBodyConst
1930-
} else {
1931-
return;
1932-
};
1936+
} else if attr.has_name(sym::default_method_body_is_const) {
1937+
errors::UnusedNote::DefaultMethodBodyConst
1938+
} else {
1939+
return;
1940+
};
19331941

19341942
self.tcx.emit_node_span_lint(
19351943
UNUSED_ATTRIBUTES,

0 commit comments

Comments
 (0)