diff options
author | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
commit | c26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch) | |
tree | 7cff36c38234be0afb65273146d8247083a5cfeb /crates/hir_def/src/attr.rs | |
parent | 3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff) | |
parent | f1f73649a686dc6e6449afc35e0fa6fed00e225d (diff) |
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r-- | crates/hir_def/src/attr.rs | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs new file mode 100644 index 000000000..dea552a60 --- /dev/null +++ b/crates/hir_def/src/attr.rs | |||
@@ -0,0 +1,212 @@ | |||
1 | //! A higher level attributes based on TokenTree, with also some shortcuts. | ||
2 | |||
3 | use std::{ops, sync::Arc}; | ||
4 | |||
5 | use cfg::{CfgExpr, CfgOptions}; | ||
6 | use either::Either; | ||
7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | ||
8 | use mbe::ast_to_token_tree; | ||
9 | use syntax::{ | ||
10 | ast::{self, AstNode, AttrsOwner}, | ||
11 | SmolStr, | ||
12 | }; | ||
13 | use tt::Subtree; | ||
14 | |||
15 | use crate::{ | ||
16 | db::DefDatabase, | ||
17 | item_tree::{ItemTreeId, ItemTreeNode}, | ||
18 | nameres::ModuleSource, | ||
19 | path::ModPath, | ||
20 | src::HasChildSource, | ||
21 | AdtId, AttrDefId, Lookup, | ||
22 | }; | ||
23 | |||
24 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | ||
25 | pub struct Attrs { | ||
26 | entries: Option<Arc<[Attr]>>, | ||
27 | } | ||
28 | |||
29 | impl ops::Deref for Attrs { | ||
30 | type Target = [Attr]; | ||
31 | |||
32 | fn deref(&self) -> &[Attr] { | ||
33 | match &self.entries { | ||
34 | Some(it) => &*it, | ||
35 | None => &[], | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl Attrs { | ||
41 | pub const EMPTY: Attrs = Attrs { entries: None }; | ||
42 | |||
43 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | ||
44 | match def { | ||
45 | AttrDefId::ModuleId(module) => { | ||
46 | let def_map = db.crate_def_map(module.krate); | ||
47 | let mod_data = &def_map[module.local_id]; | ||
48 | match mod_data.declaration_source(db) { | ||
49 | Some(it) => { | ||
50 | Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner)) | ||
51 | } | ||
52 | None => Attrs::from_attrs_owner( | ||
53 | db, | ||
54 | mod_data.definition_source(db).as_ref().map(|src| match src { | ||
55 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, | ||
56 | ModuleSource::Module(module) => module as &dyn AttrsOwner, | ||
57 | }), | ||
58 | ), | ||
59 | } | ||
60 | } | ||
61 | AttrDefId::FieldId(it) => { | ||
62 | let src = it.parent.child_source(db); | ||
63 | match &src.value[it.local_id] { | ||
64 | Either::Left(_tuple) => Attrs::default(), | ||
65 | Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)), | ||
66 | } | ||
67 | } | ||
68 | AttrDefId::EnumVariantId(var_id) => { | ||
69 | let src = var_id.parent.child_source(db); | ||
70 | let src = src.as_ref().map(|it| &it[var_id.local_id]); | ||
71 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) | ||
72 | } | ||
73 | AttrDefId::AdtId(it) => match it { | ||
74 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
75 | AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
76 | AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
77 | }, | ||
78 | AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
79 | AttrDefId::MacroDefId(it) => { | ||
80 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) | ||
81 | } | ||
82 | AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
83 | AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
84 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
85 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
86 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
87 | } | ||
88 | } | ||
89 | |||
90 | pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { | ||
91 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | ||
92 | Attrs::new(owner.value, &hygiene) | ||
93 | } | ||
94 | |||
95 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | ||
96 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( | ||
97 | |docs_text| Attr { | ||
98 | input: Some(AttrInput::Literal(SmolStr::new(docs_text))), | ||
99 | path: ModPath::from(hir_expand::name!(doc)), | ||
100 | }, | ||
101 | ); | ||
102 | let mut attrs = owner.attrs().peekable(); | ||
103 | let entries = if attrs.peek().is_none() { | ||
104 | // Avoid heap allocation | ||
105 | None | ||
106 | } else { | ||
107 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) | ||
108 | }; | ||
109 | Attrs { entries } | ||
110 | } | ||
111 | |||
112 | pub fn merge(&self, other: Attrs) -> Attrs { | ||
113 | match (&self.entries, &other.entries) { | ||
114 | (None, None) => Attrs { entries: None }, | ||
115 | (Some(entries), None) | (None, Some(entries)) => { | ||
116 | Attrs { entries: Some(entries.clone()) } | ||
117 | } | ||
118 | (Some(a), Some(b)) => { | ||
119 | Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | ||
125 | AttrQuery { attrs: self, key } | ||
126 | } | ||
127 | |||
128 | pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ { | ||
129 | // FIXME: handle cfg_attr :-) | ||
130 | self.by_key("cfg").tt_values().map(CfgExpr::parse) | ||
131 | } | ||
132 | pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { | ||
133 | self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false)) | ||
134 | } | ||
135 | } | ||
136 | |||
137 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
138 | pub struct Attr { | ||
139 | pub(crate) path: ModPath, | ||
140 | pub(crate) input: Option<AttrInput>, | ||
141 | } | ||
142 | |||
143 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
144 | pub enum AttrInput { | ||
145 | /// `#[attr = "string"]` | ||
146 | Literal(SmolStr), | ||
147 | /// `#[attr(subtree)]` | ||
148 | TokenTree(Subtree), | ||
149 | } | ||
150 | |||
151 | impl Attr { | ||
152 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | ||
153 | let path = ModPath::from_src(ast.path()?, hygiene)?; | ||
154 | let input = if let Some(lit) = ast.literal() { | ||
155 | // FIXME: escape? raw string? | ||
156 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | ||
157 | Some(AttrInput::Literal(value)) | ||
158 | } else if let Some(tt) = ast.token_tree() { | ||
159 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) | ||
160 | } else { | ||
161 | None | ||
162 | }; | ||
163 | Some(Attr { path, input }) | ||
164 | } | ||
165 | } | ||
166 | |||
167 | #[derive(Debug, Clone, Copy)] | ||
168 | pub struct AttrQuery<'a> { | ||
169 | attrs: &'a Attrs, | ||
170 | key: &'static str, | ||
171 | } | ||
172 | |||
173 | impl<'a> AttrQuery<'a> { | ||
174 | pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> { | ||
175 | self.attrs().filter_map(|attr| match attr.input.as_ref()? { | ||
176 | AttrInput::TokenTree(it) => Some(it), | ||
177 | _ => None, | ||
178 | }) | ||
179 | } | ||
180 | |||
181 | pub fn string_value(self) -> Option<&'a SmolStr> { | ||
182 | self.attrs().find_map(|attr| match attr.input.as_ref()? { | ||
183 | AttrInput::Literal(it) => Some(it), | ||
184 | _ => None, | ||
185 | }) | ||
186 | } | ||
187 | |||
188 | pub fn exists(self) -> bool { | ||
189 | self.attrs().next().is_some() | ||
190 | } | ||
191 | |||
192 | fn attrs(self) -> impl Iterator<Item = &'a Attr> { | ||
193 | let key = self.key; | ||
194 | self.attrs | ||
195 | .iter() | ||
196 | .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key)) | ||
197 | } | ||
198 | } | ||
199 | |||
200 | fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> Attrs | ||
201 | where | ||
202 | N: ast::AttrsOwner, | ||
203 | { | ||
204 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); | ||
205 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | ||
206 | } | ||
207 | |||
208 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs { | ||
209 | let tree = db.item_tree(id.file_id); | ||
210 | let mod_item = N::id_to_mod_item(id.value); | ||
211 | tree.attrs(mod_item.into()).clone() | ||
212 | } | ||