Skip to content

Commit aec8376

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

File tree

8 files changed

+157
-73
lines changed

8 files changed

+157
-73
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
@@ -278,6 +278,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
278278
| AttributeKind::ObjcClass { .. }
279279
| AttributeKind::ObjcSelector { .. }
280280
| AttributeKind::RustcCoherenceIsCore(..)
281+
| AttributeKind::Feature(..)
281282
) => { /* do nothing */ }
282283
Attribute::Unparsed(attr_item) => {
283284
style = Some(attr_item.style);
@@ -1895,75 +1896,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
18951896
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
18961897
// Warn on useless empty attributes.
18971898
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
1898-
let note = if attr.has_any_name(&[
1899-
sym::allow,
1900-
sym::expect,
1901-
sym::warn,
1902-
sym::deny,
1903-
sym::forbid,
1904-
sym::feature,
1905-
]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
1906-
{
1907-
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1908-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1909-
&& let Some(meta) = attr.meta_item_list()
1910-
&& let [meta] = meta.as_slice()
1911-
&& let Some(item) = meta.meta_item()
1912-
&& let MetaItemKind::NameValue(_) = &item.kind
1913-
&& item.path == sym::reason
1914-
{
1915-
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1916-
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
1917-
&& let Some(meta) = attr.meta_item_list()
1918-
&& meta.iter().any(|meta| {
1919-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1920-
})
1921-
{
1922-
if hir_id != CRATE_HIR_ID {
1923-
match style {
1924-
Some(ast::AttrStyle::Outer) => {
1925-
let attr_span = attr.span();
1926-
let bang_position = self
1927-
.tcx
1928-
.sess
1929-
.source_map()
1930-
.span_until_char(attr_span, '[')
1931-
.shrink_to_hi();
1932-
1933-
self.tcx.emit_node_span_lint(
1899+
let note =
1900+
if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])
1901+
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
1902+
{
1903+
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
1904+
} else if attr.has_any_name(&[
1905+
sym::allow,
1906+
sym::warn,
1907+
sym::deny,
1908+
sym::forbid,
1909+
sym::expect,
1910+
]) && let Some(meta) = attr.meta_item_list()
1911+
&& let [meta] = meta.as_slice()
1912+
&& let Some(item) = meta.meta_item()
1913+
&& let MetaItemKind::NameValue(_) = &item.kind
1914+
&& item.path == sym::reason
1915+
{
1916+
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
1917+
} else if attr.has_any_name(&[
1918+
sym::allow,
1919+
sym::warn,
1920+
sym::deny,
1921+
sym::forbid,
1922+
sym::expect,
1923+
]) && let Some(meta) = attr.meta_item_list()
1924+
&& meta.iter().any(|meta| {
1925+
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1926+
})
1927+
{
1928+
if hir_id != CRATE_HIR_ID {
1929+
match style {
1930+
Some(ast::AttrStyle::Outer) => {
1931+
let attr_span = attr.span();
1932+
let bang_position = self
1933+
.tcx
1934+
.sess
1935+
.source_map()
1936+
.span_until_char(attr_span, '[')
1937+
.shrink_to_hi();
1938+
1939+
self.tcx.emit_node_span_lint(
1940+
UNUSED_ATTRIBUTES,
1941+
hir_id,
1942+
attr_span,
1943+
errors::OuterCrateLevelAttr {
1944+
suggestion: errors::OuterCrateLevelAttrSuggestion {
1945+
bang_position,
1946+
},
1947+
},
1948+
)
1949+
}
1950+
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
19341951
UNUSED_ATTRIBUTES,
19351952
hir_id,
1936-
attr_span,
1937-
errors::OuterCrateLevelAttr {
1938-
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
1939-
},
1940-
)
1941-
}
1942-
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
1943-
UNUSED_ATTRIBUTES,
1944-
hir_id,
1945-
attr.span(),
1946-
errors::InnerCrateLevelAttr,
1947-
),
1948-
};
1949-
return;
1950-
} else {
1951-
let never_needs_link = self
1952-
.tcx
1953-
.crate_types()
1954-
.iter()
1955-
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1956-
if never_needs_link {
1957-
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1958-
} else {
1953+
attr.span(),
1954+
errors::InnerCrateLevelAttr,
1955+
),
1956+
};
19591957
return;
1958+
} else {
1959+
let never_needs_link = self
1960+
.tcx
1961+
.crate_types()
1962+
.iter()
1963+
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
1964+
if never_needs_link {
1965+
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
1966+
} else {
1967+
return;
1968+
}
19601969
}
1961-
}
1962-
} else if attr.has_name(sym::default_method_body_is_const) {
1963-
errors::UnusedNote::DefaultMethodBodyConst
1964-
} else {
1965-
return;
1966-
};
1970+
} else if attr.has_name(sym::default_method_body_is_const) {
1971+
errors::UnusedNote::DefaultMethodBodyConst
1972+
} else {
1973+
return;
1974+
};
19671975

19681976
self.tcx.emit_node_span_lint(
19691977
UNUSED_ATTRIBUTES,

src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ use std::{borrow::Cow, fmt, ops};
55
use base_db::Crate;
66
use cfg::{CfgExpr, CfgOptions};
77
use either::Either;
8-
use intern::{Interned, Symbol, sym};
8+
use intern::{sym, Interned, Symbol};
99

1010
use mbe::{DelimiterKind, Punct};
11-
use smallvec::{SmallVec, smallvec};
11+
use smallvec::{smallvec, SmallVec};
1212
use span::{Span, SyntaxContext};
1313
use syntax::unescape;
14-
use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast};
15-
use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree};
14+
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode};
15+
use syntax_bridge::{desugar_doc_comment_text, syntax_node_to_token_tree, DocCommentDesugarMode};
1616
use triomphe::ThinArc;
1717

1818
use crate::{
1919
db::ExpandDatabase,
2020
mod_path::ModPath,
2121
name::Name,
2222
span_map::SpanMapRef,
23-
tt::{self, TopSubtree, token_to_literal},
23+
tt::{self, token_to_literal, TopSubtree},
2424
};
2525

2626
/// Syntactical attributes, without filtering of `cfg_attr`s.

0 commit comments

Comments
 (0)