diff options
Diffstat (limited to 'crates/ra_hir_def/src/attr.rs')
-rw-r--r-- | crates/ra_hir_def/src/attr.rs | 156 |
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 | ||
3 | use std::sync::Arc; | 3 | use std::{ops, sync::Arc}; |
4 | 4 | ||
5 | use hir_expand::hygiene::Hygiene; | 5 | use hir_expand::{either::Either, hygiene::Hygiene, AstId, Source}; |
6 | use mbe::ast_to_token_tree; | 6 | use mbe::ast_to_token_tree; |
7 | use ra_cfg::CfgOptions; | ||
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | ast::{self, AstNode, AttrsOwner}, | 8 | ast::{self, AstNode, AttrsOwner}, |
10 | SmolStr, | 9 | SmolStr, |
11 | }; | 10 | }; |
12 | use tt::Subtree; | 11 | use tt::Subtree; |
13 | 12 | ||
14 | use crate::path::Path; | 13 | use crate::{ |
14 | db::DefDatabase, path::Path, AdtId, AstItemDef, AttrDefId, HasChildSource, HasSource, Lookup, | ||
15 | }; | ||
16 | |||
17 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | ||
18 | pub struct Attrs { | ||
19 | entries: Option<Arc<[Attr]>>, | ||
20 | } | ||
21 | |||
22 | impl 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 | |||
33 | impl 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)] |
17 | pub struct Attr { | 93 | pub struct Attr { |
@@ -26,7 +102,7 @@ pub enum AttrInput { | |||
26 | } | 102 | } |
27 | 103 | ||
28 | impl Attr { | 104 | impl 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]>> { | 123 | pub 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 { | 128 | impl<'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 | |||
155 | fn attrs_from_ast<D, N>(src: AstId<N>, db: &D) -> Attrs | ||
156 | where | ||
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 | |||
164 | fn attrs_from_loc<T, D>(node: T, db: &D) -> Attrs | ||
165 | where | ||
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 | } | ||