Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/attr.rs')
-rw-r--r--crates/hir-def/src/attr.rs162
1 files changed, 88 insertions, 74 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 184dab8367..ba88495e14 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -1,6 +1,6 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
+use std::{borrow::Cow, hash::Hash, ops, slice};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -9,17 +9,18 @@ use hir_expand::{
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
HirFileId, InFile,
};
+use intern::{sym, Symbol};
use la_arena::{ArenaMap, Idx, RawIdx};
use mbe::DelimiterKind;
use syntax::{
ast::{self, HasAttrs},
- AstPtr, SmolStr,
+ AstPtr,
};
use triomphe::Arc;
use crate::{
db::DefDatabase,
- item_tree::{AttrOwner, Fields, ItemTreeNode},
+ item_tree::{AttrOwner, FieldParent, ItemTreeNode},
lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
@@ -75,40 +76,36 @@ impl Attrs {
let mut res = ArenaMap::default();
let crate_graph = db.crate_graph();
- let (fields, item_tree, krate) = match v {
+ let item_tree;
+ let (parent, fields, krate) = match v {
VariantId::EnumVariantId(it) => {
let loc = it.lookup(db);
let krate = loc.parent.lookup(db).container.krate;
- let item_tree = loc.id.item_tree(db);
+ item_tree = loc.id.item_tree(db);
let variant = &item_tree[loc.id.value];
- (variant.fields.clone(), item_tree, krate)
+ (FieldParent::Variant(loc.id.value), &variant.fields, krate)
}
VariantId::StructId(it) => {
let loc = it.lookup(db);
let krate = loc.container.krate;
- let item_tree = loc.id.item_tree(db);
+ item_tree = loc.id.item_tree(db);
let struct_ = &item_tree[loc.id.value];
- (struct_.fields.clone(), item_tree, krate)
+ (FieldParent::Struct(loc.id.value), &struct_.fields, krate)
}
VariantId::UnionId(it) => {
let loc = it.lookup(db);
let krate = loc.container.krate;
- let item_tree = loc.id.item_tree(db);
+ item_tree = loc.id.item_tree(db);
let union_ = &item_tree[loc.id.value];
- (union_.fields.clone(), item_tree, krate)
+ (FieldParent::Union(loc.id.value), &union_.fields, krate)
}
};
- let fields = match fields {
- Fields::Record(fields) | Fields::Tuple(fields) => fields,
- Fields::Unit => return Arc::new(res),
- };
-
let cfg_options = &crate_graph[krate].cfg_options;
let mut idx = 0;
- for field in fields {
- let attrs = item_tree.attrs(db, krate, field.into());
+ for (id, _field) in fields.iter().enumerate() {
+ let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, id));
if attrs.is_cfg_enabled(cfg_options) {
res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
idx += 1;
@@ -120,12 +117,12 @@ impl Attrs {
}
impl Attrs {
- pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
+ pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
pub fn cfg(&self) -> Option<CfgExpr> {
- let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
+ let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
match cfgs.next() {
Some(second) => {
@@ -137,7 +134,7 @@ impl Attrs {
}
pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
- self.by_key("cfg").tt_values().map(CfgExpr::parse)
+ self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse)
}
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
@@ -147,50 +144,50 @@ impl Attrs {
}
}
- pub fn lang(&self) -> Option<&str> {
- self.by_key("lang").string_value()
+ pub fn lang(&self) -> Option<&Symbol> {
+ self.by_key(&sym::lang).string_value()
}
pub fn lang_item(&self) -> Option<LangItem> {
- self.by_key("lang").string_value().and_then(LangItem::from_str)
+ self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol)
}
pub fn has_doc_hidden(&self) -> bool {
- self.by_key("doc").tt_values().any(|tt| {
+ self.by_key(&sym::doc).tt_values().any(|tt| {
tt.delimiter.kind == DelimiterKind::Parenthesis &&
- matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden")
+ matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden)
})
}
pub fn has_doc_notable_trait(&self) -> bool {
- self.by_key("doc").tt_values().any(|tt| {
+ self.by_key(&sym::doc).tt_values().any(|tt| {
tt.delimiter.kind == DelimiterKind::Parenthesis &&
- matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait")
+ matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait)
})
}
pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
- self.by_key("doc").tt_values().map(DocExpr::parse)
+ self.by_key(&sym::doc).tt_values().map(DocExpr::parse)
}
- pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+ pub fn doc_aliases(&self) -> impl Iterator<Item = Symbol> + '_ {
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
}
- pub fn export_name(&self) -> Option<&str> {
- self.by_key("export_name").string_value()
+ pub fn export_name(&self) -> Option<&Symbol> {
+ self.by_key(&sym::export_name).string_value()
}
pub fn is_proc_macro(&self) -> bool {
- self.by_key("proc_macro").exists()
+ self.by_key(&sym::proc_macro).exists()
}
pub fn is_proc_macro_attribute(&self) -> bool {
- self.by_key("proc_macro_attribute").exists()
+ self.by_key(&sym::proc_macro_attribute).exists()
}
pub fn is_proc_macro_derive(&self) -> bool {
- self.by_key("proc_macro_derive").exists()
+ self.by_key(&sym::proc_macro_derive).exists()
}
pub fn is_test(&self) -> bool {
@@ -199,33 +196,37 @@ impl Attrs {
.segments()
.iter()
.rev()
- .zip(["core", "prelude", "v1", "test"].iter().rev())
- .all(|it| it.0.as_str() == Some(it.1))
+ .zip(
+ [sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()]
+ .iter()
+ .rev(),
+ )
+ .all(|it| it.0 == it.1)
})
}
pub fn is_ignore(&self) -> bool {
- self.by_key("ignore").exists()
+ self.by_key(&sym::ignore).exists()
}
pub fn is_bench(&self) -> bool {
- self.by_key("bench").exists()
+ self.by_key(&sym::bench).exists()
}
pub fn is_unstable(&self) -> bool {
- self.by_key("unstable").exists()
+ self.by_key(&sym::unstable).exists()
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DocAtom {
/// eg. `#[doc(hidden)]`
- Flag(SmolStr),
+ Flag(Symbol),
/// eg. `#[doc(alias = "it")]`
///
/// Note that a key can have multiple values that are all considered "active" at the same time.
/// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
- KeyValue { key: SmolStr, value: SmolStr },
+ KeyValue { key: Symbol, value: Symbol },
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -234,7 +235,7 @@ pub enum DocExpr {
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
Atom(DocAtom),
/// eg. `#[doc(alias("x", "y"))]`
- Alias(Vec<SmolStr>),
+ Alias(Vec<Symbol>),
}
impl From<DocAtom> for DocExpr {
@@ -248,9 +249,9 @@ impl DocExpr {
next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
}
- pub fn aliases(&self) -> &[SmolStr] {
+ pub fn aliases(&self) -> &[Symbol] {
match self {
- DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+ DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => {
std::slice::from_ref(value)
}
DocExpr::Alias(aliases) => aliases,
@@ -259,10 +260,10 @@ impl DocExpr {
}
}
-fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+fn next_doc_expr<S>(it: &mut slice::Iter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
let name = match it.next() {
None => return None,
- Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
Some(_) => return Some(DocExpr::Invalid),
};
@@ -270,13 +271,14 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
let ret = match it.as_slice().first() {
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
match it.as_slice().get(1) {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ symbol: text,
+ kind: tt::LitKind::Str,
+ ..
+ }))) => {
it.next();
it.next();
- // FIXME: escape? raw string?
- let value =
- SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
- DocAtom::KeyValue { key: name, value }.into()
+ DocAtom::KeyValue { key: name, value: text.clone() }.into()
}
_ => return Some(DocExpr::Invalid),
}
@@ -284,8 +286,8 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
Some(tt::TokenTree::Subtree(subtree)) => {
it.next();
let subs = parse_comma_sep(subtree);
- match name.as_str() {
- "alias" => DocExpr::Alias(subs),
+ match &name {
+ s if *s == sym::alias => DocExpr::Alias(subs),
_ => DocExpr::Invalid,
}
}
@@ -301,15 +303,16 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
Some(ret)
}
-fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<Symbol> {
subtree
.token_trees
.iter()
.filter_map(|tt| match tt {
- tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
- // FIXME: escape? raw string?
- Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
- }
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ kind: tt::LitKind::Str,
+ symbol,
+ ..
+ })) => Some(symbol.clone()),
_ => None,
})
.collect()
@@ -556,7 +559,7 @@ impl AttrSourceMap {
#[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'attr> {
attrs: &'attr Attrs,
- key: &'static str,
+ key: &'attr Symbol,
}
impl<'attr> AttrQuery<'attr> {
@@ -564,10 +567,14 @@ impl<'attr> AttrQuery<'attr> {
self.attrs().filter_map(|attr| attr.token_tree_value())
}
- pub fn string_value(self) -> Option<&'attr str> {
+ pub fn string_value(self) -> Option<&'attr Symbol> {
self.attrs().find_map(|attr| attr.string_value())
}
+ pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
+ self.attrs().find_map(|attr| attr.string_value_with_span())
+ }
+
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
self.attrs().find_map(|attr| attr.string_value_unescape())
}
@@ -578,9 +585,7 @@ impl<'attr> AttrQuery<'attr> {
pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
let key = self.key;
- self.attrs
- .iter()
- .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_smol_str() == key))
+ self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key))
}
/// Find string value for a specific key inside token tree
@@ -589,14 +594,14 @@ impl<'attr> AttrQuery<'attr> {
/// #[doc(html_root_url = "url")]
/// ^^^^^^^^^^^^^ key
/// ```
- pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&SmolStr> {
+ pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&str> {
self.tt_values().find_map(|tt| {
let name = tt.token_trees.iter()
- .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, ..} )) if text == key))
+ .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key))
.nth(2);
match name {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()),
_ => None
}
})
@@ -647,11 +652,13 @@ mod tests {
//! This module contains tests for doc-expression parsing.
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+ use intern::Symbol;
+ use span::EditionedFileId;
use triomphe::Arc;
- use base_db::FileId;
use hir_expand::span_map::{RealSpanMap, SpanMap};
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
+ use span::FileId;
use syntax::{ast, AstNode, TextRange};
use crate::attr::{DocAtom, DocExpr};
@@ -659,7 +666,9 @@ mod tests {
fn assert_parse_result(input: &str, expected: DocExpr) {
let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
- let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
+ let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(
+ EditionedFileId::current_edition(FileId::from_raw(0)),
+ )));
let tt = syntax_node_to_token_tree(
tt.syntax(),
map.as_ref(),
@@ -672,24 +681,29 @@ mod tests {
#[test]
fn test_doc_expr_parser() {
- assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
+ assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into());
assert_parse_result(
r#"#![doc(alias = "foo")]"#,
- DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
+ DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(),
);
- assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
+ assert_parse_result(
+ r#"#![doc(alias("foo"))]"#,
+ DocExpr::Alias([Symbol::intern("foo")].into()),
+ );
assert_parse_result(
r#"#![doc(alias("foo", "bar", "baz"))]"#,
- DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
+ DocExpr::Alias(
+ [Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(),
+ ),
);
assert_parse_result(
r#"
#[doc(alias("Bar", "Qux"))]
struct Foo;"#,
- DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
+ DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()),
);
}
}