Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/attrs.rs')
-rw-r--r--crates/hir-def/src/attrs.rs86
1 files changed, 78 insertions, 8 deletions
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index b560d08492..5cf5a9b6be 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -159,7 +159,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
None => match &*first_segment {
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"doc" => extract_doc_tt_attr(attr_flags, tt),
- "repr" => attr_flags.insert(AttrFlags::HAS_REPR),
+ "repr" | "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR),
"target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
"proc_macro_derive" | "rustc_builtin_macro" => {
attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
@@ -200,7 +200,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
let segment4 = segment4.and_then(|it| it.segment()?.name_ref());
segment1.text() == "test"
&& segment3.is_none_or(|it| it.text() == "prelude")
- && segment4.is_none_or(|it| it.text() == "core")
+ && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std"))
});
if is_test {
attr_flags.insert(AttrFlags::IS_TEST);
@@ -217,6 +217,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
"rustc_allow_incoherent_impl" => {
attr_flags.insert(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
}
+ "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR),
"fundamental" => attr_flags.insert(AttrFlags::FUNDAMENTAL),
"no_std" => attr_flags.insert(AttrFlags::IS_NO_STD),
"may_dangle" => attr_flags.insert(AttrFlags::MAY_DANGLE),
@@ -257,6 +258,9 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
Some(second_segment) => match &*first_segment {
"rust_analyzer" => match &*second_segment {
"skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP),
+ "prefer_underscore_import" => {
+ attr_flags.insert(AttrFlags::PREFER_UNDERSCORE_IMPORT)
+ }
_ => {}
},
_ => {}
@@ -329,6 +333,8 @@ bitflags::bitflags! {
const MACRO_STYLE_BRACES = 1 << 46;
const MACRO_STYLE_BRACKETS = 1 << 47;
const MACRO_STYLE_PARENTHESES = 1 << 48;
+
+ const PREFER_UNDERSCORE_IMPORT = 1 << 49;
}
}
@@ -724,14 +730,40 @@ impl AttrFlags {
fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> {
let mut result = None;
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let ast::Meta::TokenTreeMeta(attr) = attr
- && attr.path().is1("repr")
+ let mut current = None;
+ if let ast::Meta::TokenTreeMeta(attr) = &attr
+ && let Some(path) = attr.path()
&& let Some(tt) = attr.token_tree()
- && let Some(repr) = parse_repr_tt(&tt)
{
+ if path.is1("repr")
+ && let Some(repr) = parse_repr_tt(&tt)
+ {
+ current = Some(repr);
+ } else if path.is1("rustc_scalable_vector")
+ && let mut tt = TokenTreeChildren::new(&tt)
+ && let Some(NodeOrToken::Token(scalable)) = tt.next()
+ && let Some(scalable) = ast::IntNumber::cast(scalable)
+ && let Ok(scalable) = scalable.value()
+ && let Ok(scalable) = scalable.try_into()
+ {
+ current = Some(ReprOptions {
+ scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)),
+ ..ReprOptions::default()
+ });
+ }
+ } else if let ast::Meta::PathMeta(attr) = &attr
+ && attr.path().is1("rustc_scalable_vector")
+ {
+ current = Some(ReprOptions {
+ scalable: Some(rustc_abi::ScalableElt::Container),
+ ..ReprOptions::default()
+ });
+ }
+
+ if let Some(current) = current {
match &mut result {
- Some(existing) => merge_repr(existing, repr),
- None => result = Some(repr),
+ Some(existing) => merge_repr(existing, current),
+ None => result = Some(current),
}
}
ControlFlow::Continue(())
@@ -1076,10 +1108,45 @@ impl AttrFlags {
})
}
}
+
+ pub fn unstable_feature(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ if !self.contains(AttrFlags::IS_UNSTABLE) {
+ return None;
+ }
+
+ return unstable_feature(db, owner);
+
+ #[salsa::tracked]
+ fn unstable_feature(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ collect_attrs(db, owner, |attr| {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && let path = attr.path()
+ && path.is1("unstable")
+ && let Some(tt) = attr.token_tree()
+ {
+ let mut tt = TokenTreeChildren::new(&tt);
+ // Technically the `feature = "..."` always comes first, but it's not a requirement.
+ while let Some(token) = tt.next() {
+ if let NodeOrToken::Token(token) = token
+ && token.text() == "feature"
+ && let Some(NodeOrToken::Token(eq)) = tt.next()
+ && eq.kind() == T![=]
+ && let Some(NodeOrToken::Token(feature)) = tt.next()
+ && let Some(feature) = ast::String::cast(feature)
+ && let Ok(feature) = feature.value()
+ {
+ return ControlFlow::Break(Symbol::intern(&feature));
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ })
+ }
+ }
}
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
- let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
+ let ReprOptions { int, align, pack, flags, scalable, field_shuffle_seed: _ } = this;
flags.insert(other.flags);
*align = (*align).max(other.align);
*pack = match (*pack, other.pack) {
@@ -1089,6 +1156,9 @@ fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
if other.int.is_some() {
*int = other.int;
}
+ if other.scalable.is_some() {
+ *scalable = other.scalable;
+ }
}
fn parse_repr_tt(tt: &ast::TokenTree) -> Option<ReprOptions> {