aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/attr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/attr.rs')
-rw-r--r--crates/ra_hir_def/src/attr.rs156
1 files changed, 122 insertions, 34 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 0e961ca12..53456fc08 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -1,17 +1,93 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts. 1//! A higher level attributes based on TokenTree, with also some shortcuts.
2 2
3use std::sync::Arc; 3use std::{ops, sync::Arc};
4 4
5use hir_expand::hygiene::Hygiene; 5use hir_expand::{either::Either, hygiene::Hygiene, AstId, Source};
6use mbe::ast_to_token_tree; 6use mbe::ast_to_token_tree;
7use ra_cfg::CfgOptions;
8use ra_syntax::{ 7use ra_syntax::{
9 ast::{self, AstNode, AttrsOwner}, 8 ast::{self, AstNode, AttrsOwner},
10 SmolStr, 9 SmolStr,
11}; 10};
12use tt::Subtree; 11use tt::Subtree;
13 12
14use crate::path::Path; 13use crate::{
14 db::DefDatabase, path::Path, AdtId, AstItemDef, AttrDefId, HasChildSource, HasSource, Lookup,
15};
16
17#[derive(Default, Debug, Clone, PartialEq, Eq)]
18pub struct Attrs {
19 entries: Option<Arc<[Attr]>>,
20}
21
22impl ops::Deref for Attrs {
23 type Target = [Attr];
24
25 fn deref(&self) -> &[Attr] {
26 match &self.entries {
27 Some(it) => &*it,
28 None => &[],
29 }
30 }
31}
32
33impl Attrs {
34 pub(crate) fn attrs_query(db: &impl DefDatabase, def: AttrDefId) -> Attrs {
35 match def {
36 AttrDefId::ModuleId(module) => {
37 let def_map = db.crate_def_map(module.krate);
38 let src = match def_map[module.module_id].declaration_source(db) {
39 Some(it) => it,
40 None => return Attrs::default(),
41 };
42 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
43 }
44 AttrDefId::StructFieldId(it) => {
45 let src = it.parent.child_source(db);
46 match &src.value[it.local_id] {
47 Either::A(_tuple) => Attrs::default(),
48 Either::B(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
49 }
50 }
51 AttrDefId::EnumVariantId(var_id) => {
52 let src = var_id.parent.child_source(db);
53 let src = src.as_ref().map(|it| &it[var_id.local_id]);
54 Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
55 }
56 AttrDefId::AdtId(it) => match it {
57 AdtId::StructId(it) => attrs_from_ast(it.0.lookup_intern(db).ast_id, db),
58 AdtId::EnumId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
59 AdtId::UnionId(it) => attrs_from_ast(it.0.lookup_intern(db).ast_id, db),
60 },
61 AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
62 AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db),
63 AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
64 AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
65 AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
66 AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db),
67 AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db),
68 }
69 }
70
71 fn from_attrs_owner(db: &impl DefDatabase, owner: Source<&dyn AttrsOwner>) -> Attrs {
72 let hygiene = Hygiene::new(db, owner.file_id);
73 Attrs::new(owner.value, &hygiene)
74 }
75
76 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
77 let mut attrs = owner.attrs().peekable();
78 let entries = if attrs.peek().is_none() {
79 // Avoid heap allocation
80 None
81 } else {
82 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
83 };
84 Attrs { entries }
85 }
86
87 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
88 AttrQuery { attrs: self, key }
89 }
90}
15 91
16#[derive(Debug, Clone, PartialEq, Eq)] 92#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Attr { 93pub struct Attr {
@@ -26,7 +102,7 @@ pub enum AttrInput {
26} 102}
27 103
28impl Attr { 104impl Attr {
29 pub(crate) fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { 105 fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
30 let path = Path::from_src(ast.path()?, hygiene)?; 106 let path = Path::from_src(ast.path()?, hygiene)?;
31 let input = match ast.input() { 107 let input = match ast.input() {
32 None => None, 108 None => None,
@@ -42,43 +118,55 @@ impl Attr {
42 118
43 Some(Attr { path, input }) 119 Some(Attr { path, input })
44 } 120 }
121}
45 122
46 pub fn from_attrs_owner(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Option<Arc<[Attr]>> { 123pub struct AttrQuery<'a> {
47 let mut attrs = owner.attrs().peekable(); 124 attrs: &'a Attrs,
48 if attrs.peek().is_none() { 125 key: &'static str,
49 // Avoid heap allocation 126}
50 return None;
51 }
52 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
53 }
54 127
55 pub fn is_simple_atom(&self, name: &str) -> bool { 128impl<'a> AttrQuery<'a> {
56 // FIXME: Avoid cloning 129 pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
57 self.path.as_ident().map_or(false, |s| s.to_string() == name) 130 self.attrs().filter_map(|attr| match attr.input.as_ref()? {
131 AttrInput::TokenTree(it) => Some(it),
132 _ => None,
133 })
58 } 134 }
59 135
60 // FIXME: handle cfg_attr :-) 136 pub fn string_value(self) -> Option<&'a SmolStr> {
61 pub fn as_cfg(&self) -> Option<&Subtree> { 137 self.attrs().find_map(|attr| match attr.input.as_ref()? {
62 if !self.is_simple_atom("cfg") { 138 AttrInput::Literal(it) => Some(it),
63 return None;
64 }
65 match &self.input {
66 Some(AttrInput::TokenTree(subtree)) => Some(subtree),
67 _ => None, 139 _ => None,
68 } 140 })
69 } 141 }
70 142
71 pub fn as_path(&self) -> Option<&SmolStr> { 143 pub fn exists(self) -> bool {
72 if !self.is_simple_atom("path") { 144 self.attrs().next().is_some()
73 return None;
74 }
75 match &self.input {
76 Some(AttrInput::Literal(it)) => Some(it),
77 _ => None,
78 }
79 } 145 }
80 146
81 pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> { 147 fn attrs(self) -> impl Iterator<Item = &'a Attr> {
82 cfg_options.is_cfg_enabled(self.as_cfg()?) 148 let key = self.key;
149 self.attrs
150 .iter()
151 .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
83 } 152 }
84} 153}
154
155fn attrs_from_ast<D, N>(src: AstId<N>, db: &D) -> Attrs
156where
157 N: ast::AttrsOwner,
158 D: DefDatabase,
159{
160 let src = Source::new(src.file_id(), src.to_node(db));
161 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
162}
163
164fn attrs_from_loc<T, D>(node: T, db: &D) -> Attrs
165where
166 T: HasSource,
167 T::Value: ast::AttrsOwner,
168 D: DefDatabase,
169{
170 let src = node.source(db);
171 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
172}