aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/expand.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/expand.rs')
-rw-r--r--crates/ra_ide_api/src/expand.rs59
1 files changed, 59 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/expand.rs b/crates/ra_ide_api/src/expand.rs
new file mode 100644
index 000000000..7f59e46d2
--- /dev/null
+++ b/crates/ra_ide_api/src/expand.rs
@@ -0,0 +1,59 @@
1//! Utilities to work with files, produced by macros.
2use std::iter::successors;
3
4use hir::Source;
5use ra_db::FileId;
6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken};
7
8use crate::{db::RootDatabase, FileRange};
9
10pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange {
11 let expansion = match node.file_id.expansion_info(db) {
12 None => {
13 return FileRange {
14 file_id: node.file_id.original_file(db),
15 range: node.ast.text_range(),
16 }
17 }
18 Some(it) => it,
19 };
20 // FIXME: the following completely wrong.
21 //
22 // *First*, we should try to map first and last tokens of node, and, if that
23 // fails, return the range of the overall macro expansions.
24 //
25 // *Second*, we should handle recurside macro expansions
26
27 let token = node
28 .ast
29 .descendants_with_tokens()
30 .filter_map(|it| it.into_token())
31 .find_map(|it| expansion.map_token_up(node.with_ast(&it)));
32
33 match token {
34 Some(it) => FileRange { file_id: it.file_id.original_file(db), range: it.ast.text_range() },
35 None => FileRange { file_id: node.file_id.original_file(db), range: node.ast.text_range() },
36 }
37}
38
39pub(crate) fn descend_into_macros(
40 db: &RootDatabase,
41 file_id: FileId,
42 token: SyntaxToken,
43) -> Source<SyntaxToken> {
44 let src = Source::new(file_id.into(), token);
45
46 successors(Some(src), |token| {
47 let macro_call = token.ast.ancestors().find_map(ast::MacroCall::cast)?;
48 let tt = macro_call.token_tree()?;
49 if !token.ast.text_range().is_subrange(&tt.syntax().text_range()) {
50 return None;
51 }
52 let source_analyzer =
53 hir::SourceAnalyzer::new(db, token.with_ast(token.ast.parent()).as_ref(), None);
54 let exp = source_analyzer.expand(db, &macro_call)?;
55 exp.map_token_down(db, token.as_ref())
56 })
57 .last()
58 .unwrap()
59}