aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/lib.rs')
-rw-r--r--crates/ra_hir_expand/src/lib.rs217
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
7pub mod db; 7pub mod db;
8pub mod ast_id_map; 8pub mod ast_id_map;
9pub mod either;
10pub mod name; 9pub mod name;
11pub mod hygiene; 10pub mod hygiene;
12pub mod diagnostics; 11pub mod diagnostics;
12pub mod builtin_derive;
13pub mod builtin_macro; 13pub mod builtin_macro;
14pub mod quote; 14pub mod quote;
15 15
16use std::hash::{Hash, Hasher}; 16use std::hash::Hash;
17use std::sync::Arc; 17use std::sync::Arc;
18 18
19use ra_db::{salsa, CrateId, FileId}; 19use ra_db::{salsa, CrateId, FileId};
@@ -24,6 +24,7 @@ use ra_syntax::{
24}; 24};
25 25
26use crate::ast_id_map::FileAstId; 26use crate::ast_id_map::FileAstId;
27use crate::builtin_derive::BuiltinDeriveExpander;
27use crate::builtin_macro::BuiltinFnLikeExpander; 28use 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)]
106pub struct MacroFile { 118pub 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)]
112pub 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)]
132pub struct MacroDefId { 136pub 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
138impl MacroDefId { 148impl 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 {
149pub enum MacroDefKind { 155pub 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)]
155pub struct MacroCallLoc { 163pub 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)]
169pub enum MacroCallKind {
170 FnLike(AstId<ast::MacroCall>),
171 Attr(AstId<ast::ModuleItem>),
172}
173
174impl 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
160impl MacroCallId { 199impl 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)]
169pub struct ExpansionInfo { 207pub 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
217pub use mbe::Origin;
218
179impl ExpansionInfo { 219impl 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)] 262pub type AstId<N> = InFile<FileAstId<N>>;
218pub struct AstId<N: AstNode> {
219 file_id: HirFileId,
220 file_ast_id: FileAstId<N>,
221}
222
223impl<N: AstNode> Clone for AstId<N> {
224 fn clone(&self) -> AstId<N> {
225 *self
226 }
227}
228impl<N: AstNode> Copy for AstId<N> {}
229
230impl<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}
235impl<N: AstNode> Eq for AstId<N> {}
236impl<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
242impl<N: AstNode> AstId<N> { 264impl<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)]
265pub struct Source<T> { 279pub 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
270impl<T> Source<T> { 284impl<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
305impl<T: Clone> InFile<&T> {
306 pub fn cloned(&self) -> InFile<T> {
307 self.with_value(self.value.clone())
308 }
309}
310
311impl 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}