diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-02 13:05:54 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-02 13:05:54 +0000 |
commit | afa972e78d2d81598c02b742ab84d70c88208300 (patch) | |
tree | 2ec32a586d0ee00e0d35a489efeaf31c91d14a15 /crates/ra_analysis/src | |
parent | e4ffd7b31780b1f2ac6dcb731566b583bf562647 (diff) | |
parent | 1076e82856f353763de8426d378fcd1e371cbed4 (diff) |
Merge #403
403: initial support for macros r=matklad a=matklad
I'll write a more comprehensive description when this is closer to being done. Basically this investigates one question: "how do we represent code which is a result of a macro call". This is an interesting question: currently everything is `FileId` based, but macro expansion does not have a file!
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_scope.rs | 2 | ||||
-rw-r--r-- | crates/ra_analysis/src/db.rs | 16 | ||||
-rw-r--r-- | crates/ra_analysis/src/extend_selection.rs | 10 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 4 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_analysis/src/macros.rs | 75 | ||||
-rw-r--r-- | crates/ra_analysis/src/syntax_highlighting.rs | 30 |
7 files changed, 46 insertions, 92 deletions
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs index daf666505..4dead3689 100644 --- a/crates/ra_analysis/src/completion/complete_scope.rs +++ b/crates/ra_analysis/src/completion/complete_scope.rs | |||
@@ -27,7 +27,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> | |||
27 | match res.import { | 27 | match res.import { |
28 | None => true, | 28 | None => true, |
29 | Some(import) => { | 29 | Some(import) => { |
30 | let range = import.range(ctx.db, module.source().file_id()); | 30 | let range = import.range(ctx.db, module.file_id()); |
31 | !range.is_subrange(&ctx.leaf.range()) | 31 | !range.is_subrange(&ctx.leaf.range()) |
32 | } | 32 | } |
33 | } | 33 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index b072a5eba..d7740f0c4 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::{fmt, sync::Arc}; | 1 | use std::{fmt, sync::Arc}; |
2 | use salsa::{self, Database}; | 2 | use salsa::{self, Database}; |
3 | use ra_db::{LocationIntener, BaseDatabase}; | 3 | use ra_db::{LocationIntener, BaseDatabase}; |
4 | use hir::{self, DefId, DefLoc}; | ||
5 | 4 | ||
6 | use crate::{ | 5 | use crate::{ |
7 | symbol_index, | 6 | symbol_index, |
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase { | |||
15 | 14 | ||
16 | #[derive(Default)] | 15 | #[derive(Default)] |
17 | struct IdMaps { | 16 | struct IdMaps { |
18 | defs: LocationIntener<DefLoc, DefId>, | 17 | defs: LocationIntener<hir::DefLoc, hir::DefId>, |
18 | macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>, | ||
19 | } | 19 | } |
20 | 20 | ||
21 | impl fmt::Debug for IdMaps { | 21 | impl fmt::Debug for IdMaps { |
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase { | |||
59 | 59 | ||
60 | impl BaseDatabase for RootDatabase {} | 60 | impl BaseDatabase for RootDatabase {} |
61 | 61 | ||
62 | impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { | 62 | impl AsRef<LocationIntener<hir::DefLoc, hir::DefId>> for RootDatabase { |
63 | fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { | 63 | fn as_ref(&self) -> &LocationIntener<hir::DefLoc, hir::DefId> { |
64 | &self.id_maps.defs | 64 | &self.id_maps.defs |
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | impl AsRef<LocationIntener<hir::MacroCallLoc, hir::MacroCallId>> for RootDatabase { | ||
69 | fn as_ref(&self) -> &LocationIntener<hir::MacroCallLoc, hir::MacroCallId> { | ||
70 | &self.id_maps.macros | ||
71 | } | ||
72 | } | ||
73 | |||
68 | salsa::database_storage! { | 74 | salsa::database_storage! { |
69 | pub(crate) struct RootDatabaseStorage for RootDatabase { | 75 | pub(crate) struct RootDatabaseStorage for RootDatabase { |
70 | impl ra_db::FilesDatabase { | 76 | impl ra_db::FilesDatabase { |
@@ -85,6 +91,8 @@ salsa::database_storage! { | |||
85 | fn library_symbols() for symbol_index::LibrarySymbolsQuery; | 91 | fn library_symbols() for symbol_index::LibrarySymbolsQuery; |
86 | } | 92 | } |
87 | impl hir::db::HirDatabase { | 93 | impl hir::db::HirDatabase { |
94 | fn hir_source_file() for hir::db::HirSourceFileQuery; | ||
95 | fn expand_macro_invocation() for hir::db::ExpandMacroCallQuery; | ||
88 | fn module_tree() for hir::db::ModuleTreeQuery; | 96 | fn module_tree() for hir::db::ModuleTreeQuery; |
89 | fn fn_scopes() for hir::db::FnScopesQuery; | 97 | fn fn_scopes() for hir::db::FnScopesQuery; |
90 | fn file_items() for hir::db::SourceFileItemsQuery; | 98 | fn file_items() for hir::db::SourceFileItemsQuery; |
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs index 805e9059e..f1b77f981 100644 --- a/crates/ra_analysis/src/extend_selection.rs +++ b/crates/ra_analysis/src/extend_selection.rs | |||
@@ -18,15 +18,15 @@ pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRang | |||
18 | } | 18 | } |
19 | 19 | ||
20 | fn extend_selection_in_macro( | 20 | fn extend_selection_in_macro( |
21 | db: &RootDatabase, | 21 | _db: &RootDatabase, |
22 | source_file: &SourceFileNode, | 22 | source_file: &SourceFileNode, |
23 | frange: FileRange, | 23 | frange: FileRange, |
24 | ) -> Option<TextRange> { | 24 | ) -> Option<TextRange> { |
25 | let macro_call = find_macro_call(source_file.syntax(), frange.range)?; | 25 | let macro_call = find_macro_call(source_file.syntax(), frange.range)?; |
26 | let exp = crate::macros::expand(db, frange.file_id, macro_call)?; | 26 | let (off, exp) = hir::MacroDef::ast_expand(macro_call)?; |
27 | let dst_range = exp.map_range_forward(frange.range)?; | 27 | let dst_range = exp.map_range_forward(frange.range - off)?; |
28 | let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?; | 28 | let dst_range = ra_editor::extend_selection(exp.syntax().borrowed(), dst_range)?; |
29 | let src_range = exp.map_range_back(dst_range)?; | 29 | let src_range = exp.map_range_back(dst_range)? + off; |
30 | Some(src_range) | 30 | Some(src_range) |
31 | } | 31 | } |
32 | 32 | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 5669aa94d..ec7da437a 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -230,7 +230,7 @@ impl AnalysisImpl { | |||
230 | Some(it) => it, | 230 | Some(it) => it, |
231 | }; | 231 | }; |
232 | let root = descr.crate_root(); | 232 | let root = descr.crate_root(); |
233 | let file_id = root.source().file_id(); | 233 | let file_id = root.file_id(); |
234 | 234 | ||
235 | let crate_graph = self.db.crate_graph(); | 235 | let crate_graph = self.db.crate_graph(); |
236 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); | 236 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); |
@@ -283,7 +283,7 @@ impl AnalysisImpl { | |||
283 | if let Some(child_module) = | 283 | if let Some(child_module) = |
284 | source_binder::module_from_declaration(&*self.db, position.file_id, module)? | 284 | source_binder::module_from_declaration(&*self.db, position.file_id, module)? |
285 | { | 285 | { |
286 | let file_id = child_module.source().file_id(); | 286 | let file_id = child_module.file_id(); |
287 | let name = match child_module.name() { | 287 | let name = match child_module.name() { |
288 | Some(name) => name.to_string().into(), | 288 | Some(name) => name.to_string().into(), |
289 | None => "".into(), | 289 | None => "".into(), |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index e6cfaecc3..08ecb125a 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -19,7 +19,6 @@ mod runnables; | |||
19 | 19 | ||
20 | mod extend_selection; | 20 | mod extend_selection; |
21 | mod syntax_highlighting; | 21 | mod syntax_highlighting; |
22 | mod macros; | ||
23 | 22 | ||
24 | use std::{fmt, sync::Arc}; | 23 | use std::{fmt, sync::Arc}; |
25 | 24 | ||
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs deleted file mode 100644 index b9feb7fad..000000000 --- a/crates/ra_analysis/src/macros.rs +++ /dev/null | |||
@@ -1,75 +0,0 @@ | |||
1 | /// Begining of macro expansion. | ||
2 | /// | ||
3 | /// This code should be moved out of ra_analysis into hir (?) ideally. | ||
4 | use ra_syntax::{ast, AstNode, SourceFileNode, TextRange}; | ||
5 | |||
6 | use crate::{db::RootDatabase, FileId}; | ||
7 | |||
8 | pub(crate) fn expand( | ||
9 | _db: &RootDatabase, | ||
10 | _file_id: FileId, | ||
11 | macro_call: ast::MacroCall, | ||
12 | ) -> Option<MacroExpansion> { | ||
13 | let path = macro_call.path()?; | ||
14 | if path.qualifier().is_some() { | ||
15 | return None; | ||
16 | } | ||
17 | let name_ref = path.segment()?.name_ref()?; | ||
18 | if name_ref.text() != "ctry" { | ||
19 | return None; | ||
20 | } | ||
21 | |||
22 | let arg = macro_call.token_tree()?; | ||
23 | let text = format!( | ||
24 | r" | ||
25 | fn dummy() {{ | ||
26 | match {} {{ | ||
27 | None => return Ok(None), | ||
28 | Some(it) => it, | ||
29 | }} | ||
30 | }}", | ||
31 | arg.syntax().text() | ||
32 | ); | ||
33 | let file = SourceFileNode::parse(&text); | ||
34 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
35 | let match_arg = match_expr.expr()?; | ||
36 | let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())]; | ||
37 | let res = MacroExpansion { | ||
38 | source_file: file, | ||
39 | ranges_map, | ||
40 | }; | ||
41 | Some(res) | ||
42 | } | ||
43 | |||
44 | pub(crate) struct MacroExpansion { | ||
45 | pub(crate) source_file: SourceFileNode, | ||
46 | pub(crate) ranges_map: Vec<(TextRange, TextRange)>, | ||
47 | } | ||
48 | |||
49 | impl MacroExpansion { | ||
50 | pub(crate) fn source_file(&self) -> &SourceFileNode { | ||
51 | &self.source_file | ||
52 | } | ||
53 | pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
54 | for (s_range, t_range) in self.ranges_map.iter() { | ||
55 | if tgt_range.is_subrange(&t_range) { | ||
56 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
57 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
58 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
59 | return Some(src_range); | ||
60 | } | ||
61 | } | ||
62 | None | ||
63 | } | ||
64 | pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
65 | for (s_range, t_range) in self.ranges_map.iter() { | ||
66 | if src_range.is_subrange(&s_range) { | ||
67 | let src_at_zero_range = src_range - src_range.start(); | ||
68 | let src_range_offset = src_range.start() - s_range.start(); | ||
69 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
70 | return Some(src_range); | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
75 | } | ||
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs index 7e9139a74..ccea4aee3 100644 --- a/crates/ra_analysis/src/syntax_highlighting.rs +++ b/crates/ra_analysis/src/syntax_highlighting.rs | |||
@@ -15,13 +15,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Hi | |||
15 | .descendants() | 15 | .descendants() |
16 | .filter_map(ast::MacroCall::cast) | 16 | .filter_map(ast::MacroCall::cast) |
17 | { | 17 | { |
18 | if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { | 18 | if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) { |
19 | let mapped_ranges = ra_editor::highlight(exp.source_file().syntax()) | 19 | let mapped_ranges = ra_editor::highlight(exp.syntax().borrowed()) |
20 | .into_iter() | 20 | .into_iter() |
21 | .filter_map(|r| { | 21 | .filter_map(|r| { |
22 | let mapped_range = exp.map_range_back(r.range)?; | 22 | let mapped_range = exp.map_range_back(r.range)?; |
23 | let res = HighlightedRange { | 23 | let res = HighlightedRange { |
24 | range: mapped_range, | 24 | range: mapped_range + off, |
25 | tag: r.tag, | 25 | tag: r.tag, |
26 | }; | 26 | }; |
27 | Some(res) | 27 | Some(res) |
@@ -44,7 +44,7 @@ mod tests { | |||
44 | fn main() { | 44 | fn main() { |
45 | ctry!({ let x = 92; x}); | 45 | ctry!({ let x = 92; x}); |
46 | } | 46 | } |
47 | ", | 47 | ", |
48 | ); | 48 | ); |
49 | let highlights = analysis.highlight(file_id).unwrap(); | 49 | let highlights = analysis.highlight(file_id).unwrap(); |
50 | assert_eq_dbg( | 50 | assert_eq_dbg( |
@@ -60,4 +60,26 @@ mod tests { | |||
60 | &highlights, | 60 | &highlights, |
61 | ) | 61 | ) |
62 | } | 62 | } |
63 | |||
64 | // FIXME: this test is not really necessary: artifact of the inital hacky | ||
65 | // macros implementation. | ||
66 | #[test] | ||
67 | fn highlight_query_group_macro() { | ||
68 | let (analysis, file_id) = single_file( | ||
69 | " | ||
70 | salsa::query_group! { | ||
71 | pub trait HirDatabase: SyntaxDatabase {} | ||
72 | } | ||
73 | ", | ||
74 | ); | ||
75 | let highlights = analysis.highlight(file_id).unwrap(); | ||
76 | assert_eq_dbg( | ||
77 | r#"[HighlightedRange { range: [20; 32), tag: "macro" }, | ||
78 | HighlightedRange { range: [13; 18), tag: "text" }, | ||
79 | HighlightedRange { range: [51; 54), tag: "keyword" }, | ||
80 | HighlightedRange { range: [55; 60), tag: "keyword" }, | ||
81 | HighlightedRange { range: [61; 72), tag: "function" }]"#, | ||
82 | &highlights, | ||
83 | ) | ||
84 | } | ||
63 | } | 85 | } |