diff options
Diffstat (limited to 'crates/ra_hir_expand/src/lib.rs')
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 217 |
1 files changed, 126 insertions, 91 deletions
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index b6a739cda..2fa5d5140 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -6,14 +6,14 @@ | |||
6 | 6 | ||
7 | pub mod db; | 7 | pub mod db; |
8 | pub mod ast_id_map; | 8 | pub mod ast_id_map; |
9 | pub mod either; | ||
10 | pub mod name; | 9 | pub mod name; |
11 | pub mod hygiene; | 10 | pub mod hygiene; |
12 | pub mod diagnostics; | 11 | pub mod diagnostics; |
12 | pub mod builtin_derive; | ||
13 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
14 | pub mod quote; | 14 | pub mod quote; |
15 | 15 | ||
16 | use std::hash::{Hash, Hasher}; | 16 | use std::hash::Hash; |
17 | use std::sync::Arc; | 17 | use std::sync::Arc; |
18 | 18 | ||
19 | use ra_db::{salsa, CrateId, FileId}; | 19 | use ra_db::{salsa, CrateId, FileId}; |
@@ -24,6 +24,7 @@ use ra_syntax::{ | |||
24 | }; | 24 | }; |
25 | 25 | ||
26 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_derive::BuiltinDeriveExpander; | ||
27 | use crate::builtin_macro::BuiltinFnLikeExpander; | 28 | use crate::builtin_macro::BuiltinFnLikeExpander; |
28 | 29 | ||
29 | #[cfg(test)] | 30 | #[cfg(test)] |
@@ -70,7 +71,18 @@ impl HirFileId { | |||
70 | HirFileIdRepr::FileId(file_id) => file_id, | 71 | HirFileIdRepr::FileId(file_id) => file_id, |
71 | HirFileIdRepr::MacroFile(macro_file) => { | 72 | HirFileIdRepr::MacroFile(macro_file) => { |
72 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | 73 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); |
73 | loc.ast_id.file_id().original_file(db) | 74 | loc.kind.file_id().original_file(db) |
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /// If this is a macro call, returns the syntax node of the call. | ||
80 | pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { | ||
81 | match self.0 { | ||
82 | HirFileIdRepr::FileId(_) => None, | ||
83 | HirFileIdRepr::MacroFile(macro_file) => { | ||
84 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | ||
85 | Some(loc.kind.node(db)) | ||
74 | } | 86 | } |
75 | } | 87 | } |
76 | } | 88 | } |
@@ -82,17 +94,17 @@ impl HirFileId { | |||
82 | HirFileIdRepr::MacroFile(macro_file) => { | 94 | HirFileIdRepr::MacroFile(macro_file) => { |
83 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 95 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
84 | 96 | ||
85 | let arg_tt = loc.ast_id.to_node(db).token_tree()?; | 97 | let arg_tt = loc.kind.arg(db)?; |
86 | let def_tt = loc.def.ast_id.to_node(db).token_tree()?; | 98 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
87 | 99 | ||
88 | let macro_def = db.macro_def(loc.def)?; | 100 | let macro_def = db.macro_def(loc.def)?; |
89 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 101 | let (parse, exp_map) = db.parse_macro(macro_file)?; |
90 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 102 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
91 | 103 | ||
92 | Some(ExpansionInfo { | 104 | Some(ExpansionInfo { |
93 | expanded: Source::new(self, parse.syntax_node()), | 105 | expanded: InFile::new(self, parse.syntax_node()), |
94 | arg: Source::new(loc.ast_id.file_id, arg_tt), | 106 | arg: InFile::new(loc.kind.file_id(), arg_tt), |
95 | def: Source::new(loc.ast_id.file_id, def_tt), | 107 | def: InFile::new(loc.def.ast_id?.file_id, def_tt), |
96 | macro_arg, | 108 | macro_arg, |
97 | macro_def, | 109 | macro_def, |
98 | exp_map, | 110 | exp_map, |
@@ -105,14 +117,6 @@ impl HirFileId { | |||
105 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
106 | pub struct MacroFile { | 118 | pub struct MacroFile { |
107 | macro_call_id: MacroCallId, | 119 | macro_call_id: MacroCallId, |
108 | macro_file_kind: MacroFileKind, | ||
109 | } | ||
110 | |||
111 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
112 | pub enum MacroFileKind { | ||
113 | Items, | ||
114 | Expr, | ||
115 | Statements, | ||
116 | } | 120 | } |
117 | 121 | ||
118 | /// `MacroCallId` identifies a particular macro invocation, like | 122 | /// `MacroCallId` identifies a particular macro invocation, like |
@@ -130,18 +134,20 @@ impl salsa::InternKey for MacroCallId { | |||
130 | 134 | ||
131 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
132 | pub struct MacroDefId { | 136 | pub struct MacroDefId { |
133 | pub krate: CrateId, | 137 | // FIXME: krate and ast_id are currently optional because we don't have a |
134 | pub ast_id: AstId<ast::MacroCall>, | 138 | // definition location for built-in derives. There is one, though: the |
139 | // standard library defines them. The problem is that it uses the new | ||
140 | // `macro` syntax for this, which we don't support yet. As soon as we do | ||
141 | // (which will probably require touching this code), we can instead use | ||
142 | // that (and also remove the hacks for resolving built-in derives). | ||
143 | pub krate: Option<CrateId>, | ||
144 | pub ast_id: Option<AstId<ast::MacroCall>>, | ||
135 | pub kind: MacroDefKind, | 145 | pub kind: MacroDefKind, |
136 | } | 146 | } |
137 | 147 | ||
138 | impl MacroDefId { | 148 | impl MacroDefId { |
139 | pub fn as_call_id( | 149 | pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId { |
140 | self, | 150 | db.intern_macro(MacroCallLoc { def: self, kind }) |
141 | db: &dyn db::AstDatabase, | ||
142 | ast_id: AstId<ast::MacroCall>, | ||
143 | ) -> MacroCallId { | ||
144 | db.intern_macro(MacroCallLoc { def: self, ast_id }) | ||
145 | } | 151 | } |
146 | } | 152 | } |
147 | 153 | ||
@@ -149,64 +155,103 @@ impl MacroDefId { | |||
149 | pub enum MacroDefKind { | 155 | pub enum MacroDefKind { |
150 | Declarative, | 156 | Declarative, |
151 | BuiltIn(BuiltinFnLikeExpander), | 157 | BuiltIn(BuiltinFnLikeExpander), |
158 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | ||
159 | BuiltInDerive(BuiltinDeriveExpander), | ||
152 | } | 160 | } |
153 | 161 | ||
154 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 162 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
155 | pub struct MacroCallLoc { | 163 | pub struct MacroCallLoc { |
156 | pub(crate) def: MacroDefId, | 164 | pub(crate) def: MacroDefId, |
157 | pub(crate) ast_id: AstId<ast::MacroCall>, | 165 | pub(crate) kind: MacroCallKind, |
166 | } | ||
167 | |||
168 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
169 | pub enum MacroCallKind { | ||
170 | FnLike(AstId<ast::MacroCall>), | ||
171 | Attr(AstId<ast::ModuleItem>), | ||
172 | } | ||
173 | |||
174 | impl MacroCallKind { | ||
175 | pub fn file_id(&self) -> HirFileId { | ||
176 | match self { | ||
177 | MacroCallKind::FnLike(ast_id) => ast_id.file_id, | ||
178 | MacroCallKind::Attr(ast_id) => ast_id.file_id, | ||
179 | } | ||
180 | } | ||
181 | |||
182 | pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { | ||
183 | match self { | ||
184 | MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), | ||
185 | MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), | ||
186 | } | ||
187 | } | ||
188 | |||
189 | pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { | ||
190 | match self { | ||
191 | MacroCallKind::FnLike(ast_id) => { | ||
192 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | ||
193 | } | ||
194 | MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()), | ||
195 | } | ||
196 | } | ||
158 | } | 197 | } |
159 | 198 | ||
160 | impl MacroCallId { | 199 | impl MacroCallId { |
161 | pub fn as_file(self, kind: MacroFileKind) -> HirFileId { | 200 | pub fn as_file(self) -> HirFileId { |
162 | let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind }; | 201 | MacroFile { macro_call_id: self }.into() |
163 | macro_file.into() | ||
164 | } | 202 | } |
165 | } | 203 | } |
166 | 204 | ||
167 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro | 205 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro |
168 | #[derive(Debug, Clone, PartialEq, Eq)] | 206 | #[derive(Debug, Clone, PartialEq, Eq)] |
169 | pub struct ExpansionInfo { | 207 | pub struct ExpansionInfo { |
170 | expanded: Source<SyntaxNode>, | 208 | expanded: InFile<SyntaxNode>, |
171 | arg: Source<ast::TokenTree>, | 209 | arg: InFile<SyntaxNode>, |
172 | def: Source<ast::TokenTree>, | 210 | def: InFile<ast::TokenTree>, |
173 | 211 | ||
174 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 212 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, |
175 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 213 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
176 | exp_map: Arc<mbe::TokenMap>, | 214 | exp_map: Arc<mbe::TokenMap>, |
177 | } | 215 | } |
178 | 216 | ||
217 | pub use mbe::Origin; | ||
218 | |||
179 | impl ExpansionInfo { | 219 | impl ExpansionInfo { |
180 | pub fn map_token_down(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> { | 220 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { |
221 | Some(self.arg.with_value(self.arg.value.parent()?)) | ||
222 | } | ||
223 | |||
224 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { | ||
181 | assert_eq!(token.file_id, self.arg.file_id); | 225 | assert_eq!(token.file_id, self.arg.file_id); |
182 | let range = | 226 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
183 | token.value.text_range().checked_sub(self.arg.value.syntax().text_range().start())?; | ||
184 | let token_id = self.macro_arg.1.token_by_range(range)?; | 227 | let token_id = self.macro_arg.1.token_by_range(range)?; |
185 | let token_id = self.macro_def.0.map_id_down(token_id); | 228 | let token_id = self.macro_def.0.map_id_down(token_id); |
186 | 229 | ||
187 | let range = self.exp_map.range_by_token(token_id)?; | 230 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
188 | 231 | ||
189 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; | 232 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; |
190 | 233 | ||
191 | Some(self.expanded.with_value(token)) | 234 | Some(self.expanded.with_value(token)) |
192 | } | 235 | } |
193 | 236 | ||
194 | pub fn map_token_up(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> { | 237 | pub fn map_token_up( |
238 | &self, | ||
239 | token: InFile<&SyntaxToken>, | ||
240 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | ||
195 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | 241 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; |
196 | 242 | ||
197 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 243 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
198 | let (token_map, tt) = match origin { | 244 | let (token_map, tt) = match origin { |
199 | mbe::Origin::Call => (&self.macro_arg.1, &self.arg), | 245 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), |
200 | mbe::Origin::Def => (&self.macro_def.1, &self.def), | 246 | mbe::Origin::Def => { |
247 | (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone())) | ||
248 | } | ||
201 | }; | 249 | }; |
202 | 250 | ||
203 | let range = token_map.range_by_token(token_id)?; | 251 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
204 | let token = algo::find_covering_element( | 252 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
205 | tt.value.syntax(), | 253 | .into_token()?; |
206 | range + tt.value.syntax().text_range().start(), | 254 | Some((tt.with_value(token), origin)) |
207 | ) | ||
208 | .into_token()?; | ||
209 | Some(tt.with_value(token)) | ||
210 | } | 255 | } |
211 | } | 256 | } |
212 | 257 | ||
@@ -214,76 +259,66 @@ impl ExpansionInfo { | |||
214 | /// | 259 | /// |
215 | /// It is stable across reparses, and can be used as salsa key/value. | 260 | /// It is stable across reparses, and can be used as salsa key/value. |
216 | // FIXME: isn't this just a `Source<FileAstId<N>>` ? | 261 | // FIXME: isn't this just a `Source<FileAstId<N>>` ? |
217 | #[derive(Debug)] | 262 | pub type AstId<N> = InFile<FileAstId<N>>; |
218 | pub struct AstId<N: AstNode> { | ||
219 | file_id: HirFileId, | ||
220 | file_ast_id: FileAstId<N>, | ||
221 | } | ||
222 | |||
223 | impl<N: AstNode> Clone for AstId<N> { | ||
224 | fn clone(&self) -> AstId<N> { | ||
225 | *self | ||
226 | } | ||
227 | } | ||
228 | impl<N: AstNode> Copy for AstId<N> {} | ||
229 | |||
230 | impl<N: AstNode> PartialEq for AstId<N> { | ||
231 | fn eq(&self, other: &Self) -> bool { | ||
232 | (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) | ||
233 | } | ||
234 | } | ||
235 | impl<N: AstNode> Eq for AstId<N> {} | ||
236 | impl<N: AstNode> Hash for AstId<N> { | ||
237 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
238 | (self.file_id, self.file_ast_id).hash(hasher); | ||
239 | } | ||
240 | } | ||
241 | 263 | ||
242 | impl<N: AstNode> AstId<N> { | 264 | impl<N: AstNode> AstId<N> { |
243 | pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> { | ||
244 | AstId { file_id, file_ast_id } | ||
245 | } | ||
246 | |||
247 | pub fn file_id(&self) -> HirFileId { | ||
248 | self.file_id | ||
249 | } | ||
250 | |||
251 | pub fn to_node(&self, db: &dyn db::AstDatabase) -> N { | 265 | pub fn to_node(&self, db: &dyn db::AstDatabase) -> N { |
252 | let root = db.parse_or_expand(self.file_id).unwrap(); | 266 | let root = db.parse_or_expand(self.file_id).unwrap(); |
253 | db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root) | 267 | db.ast_id_map(self.file_id).get(self.value).to_node(&root) |
254 | } | 268 | } |
255 | } | 269 | } |
256 | 270 | ||
257 | /// `Source<T>` stores a value of `T` inside a particular file/syntax tree. | 271 | /// `InFile<T>` stores a value of `T` inside a particular file/syntax tree. |
258 | /// | 272 | /// |
259 | /// Typical usages are: | 273 | /// Typical usages are: |
260 | /// | 274 | /// |
261 | /// * `Source<SyntaxNode>` -- syntax node in a file | 275 | /// * `InFile<SyntaxNode>` -- syntax node in a file |
262 | /// * `Source<ast::FnDef>` -- ast node in a file | 276 | /// * `InFile<ast::FnDef>` -- ast node in a file |
263 | /// * `Source<TextUnit>` -- offset in a file | 277 | /// * `InFile<TextUnit>` -- offset in a file |
264 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | 278 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] |
265 | pub struct Source<T> { | 279 | pub struct InFile<T> { |
266 | pub file_id: HirFileId, | 280 | pub file_id: HirFileId, |
267 | pub value: T, | 281 | pub value: T, |
268 | } | 282 | } |
269 | 283 | ||
270 | impl<T> Source<T> { | 284 | impl<T> InFile<T> { |
271 | pub fn new(file_id: HirFileId, value: T) -> Source<T> { | 285 | pub fn new(file_id: HirFileId, value: T) -> InFile<T> { |
272 | Source { file_id, value } | 286 | InFile { file_id, value } |
273 | } | 287 | } |
274 | 288 | ||
275 | // Similarly, naming here is stupid... | 289 | // Similarly, naming here is stupid... |
276 | pub fn with_value<U>(&self, value: U) -> Source<U> { | 290 | pub fn with_value<U>(&self, value: U) -> InFile<U> { |
277 | Source::new(self.file_id, value) | 291 | InFile::new(self.file_id, value) |
278 | } | 292 | } |
279 | 293 | ||
280 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 294 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> { |
281 | Source::new(self.file_id, f(self.value)) | 295 | InFile::new(self.file_id, f(self.value)) |
282 | } | 296 | } |
283 | pub fn as_ref(&self) -> Source<&T> { | 297 | pub fn as_ref(&self) -> InFile<&T> { |
284 | self.with_value(&self.value) | 298 | self.with_value(&self.value) |
285 | } | 299 | } |
286 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { | 300 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { |
287 | db.parse_or_expand(self.file_id).expect("source created from invalid file") | 301 | db.parse_or_expand(self.file_id).expect("source created from invalid file") |
288 | } | 302 | } |
289 | } | 303 | } |
304 | |||
305 | impl<T: Clone> InFile<&T> { | ||
306 | pub fn cloned(&self) -> InFile<T> { | ||
307 | self.with_value(self.value.clone()) | ||
308 | } | ||
309 | } | ||
310 | |||
311 | impl InFile<SyntaxNode> { | ||
312 | pub fn ancestors_with_macros<'a>( | ||
313 | self, | ||
314 | db: &'a impl crate::db::AstDatabase, | ||
315 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a { | ||
316 | std::iter::successors(Some(self), move |node| match node.value.parent() { | ||
317 | Some(parent) => Some(node.with_value(parent)), | ||
318 | None => { | ||
319 | let parent_node = node.file_id.call_node(db)?; | ||
320 | Some(parent_node) | ||
321 | } | ||
322 | }) | ||
323 | } | ||
324 | } | ||