aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-02 13:05:54 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-02 13:05:54 +0000
commitafa972e78d2d81598c02b742ab84d70c88208300 (patch)
tree2ec32a586d0ee00e0d35a489efeaf31c91d14a15 /crates
parente4ffd7b31780b1f2ac6dcb731566b583bf562647 (diff)
parent1076e82856f353763de8426d378fcd1e371cbed4 (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')
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_analysis/src/db.rs16
-rw-r--r--crates/ra_analysis/src/extend_selection.rs10
-rw-r--r--crates/ra_analysis/src/imp.rs4
-rw-r--r--crates/ra_analysis/src/lib.rs1
-rw-r--r--crates/ra_analysis/src/macros.rs75
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs30
-rw-r--r--crates/ra_hir/src/adt.rs3
-rw-r--r--crates/ra_hir/src/db.rs19
-rw-r--r--crates/ra_hir/src/ids.rs280
-rw-r--r--crates/ra_hir/src/krate.rs5
-rw-r--r--crates/ra_hir/src/lib.rs169
-rw-r--r--crates/ra_hir/src/macros.rs181
-rw-r--r--crates/ra_hir/src/mock.rs10
-rw-r--r--crates/ra_hir/src/module.rs25
-rw-r--r--crates/ra_hir/src/module/imp.rs6
-rw-r--r--crates/ra_hir/src/module/nameres.rs85
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs84
-rw-r--r--crates/ra_hir/src/query_definitions.rs73
-rw-r--r--crates/ra_hir/src/source_binder.rs12
-rw-r--r--crates/ra_syntax/src/ast.rs30
-rw-r--r--crates/ra_syntax/src/lib.rs2
22 files changed, 778 insertions, 344 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 @@
1use std::{fmt, sync::Arc}; 1use std::{fmt, sync::Arc};
2use salsa::{self, Database}; 2use salsa::{self, Database};
3use ra_db::{LocationIntener, BaseDatabase}; 3use ra_db::{LocationIntener, BaseDatabase};
4use hir::{self, DefId, DefLoc};
5 4
6use crate::{ 5use crate::{
7 symbol_index, 6 symbol_index,
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase {
15 14
16#[derive(Default)] 15#[derive(Default)]
17struct IdMaps { 16struct IdMaps {
18 defs: LocationIntener<DefLoc, DefId>, 17 defs: LocationIntener<hir::DefLoc, hir::DefId>,
18 macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>,
19} 19}
20 20
21impl fmt::Debug for IdMaps { 21impl fmt::Debug for IdMaps {
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase {
59 59
60impl BaseDatabase for RootDatabase {} 60impl BaseDatabase for RootDatabase {}
61 61
62impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { 62impl 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
68impl 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
68salsa::database_storage! { 74salsa::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
20fn extend_selection_in_macro( 20fn 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
20mod extend_selection; 20mod extend_selection;
21mod syntax_highlighting; 21mod syntax_highlighting;
22mod macros;
23 22
24use std::{fmt, sync::Arc}; 23use 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.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(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
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl 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}
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index e839a5a90..c6463235c 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -1,9 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_db::Cancelable;
3use ra_syntax::ast::{self, NameOwner, StructFlavor}; 4use ra_syntax::ast::{self, NameOwner, StructFlavor};
4 5
5use crate::{ 6use crate::{
6 DefId, Cancelable, Name, AsName, 7 DefId, Name, AsName,
7 db::HirDatabase, 8 db::HirDatabase,
8 type_ref::TypeRef, 9 type_ref::TypeRef,
9}; 10};
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 5a8ca3b47..73a4cdc5c 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,13 +1,14 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::SyntaxNode; 3use ra_syntax::{SyntaxNode, SourceFileNode};
4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; 4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable};
5 5
6use crate::{ 6use crate::{
7 DefLoc, DefId, Name, 7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
8 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
9 query_definitions, 9 query_definitions,
10 FnScopes, 10 FnScopes,
11 macros::MacroExpansion,
11 module::{ModuleId, ModuleTree, ModuleSource, 12 module::{ModuleId, ModuleTree, ModuleSource,
12 nameres::{ItemMap, InputModuleItems}}, 13 nameres::{ItemMap, InputModuleItems}},
13 ty::{InferenceResult, Ty}, 14 ty::{InferenceResult, Ty},
@@ -18,7 +19,17 @@ salsa::query_group! {
18 19
19pub trait HirDatabase: SyntaxDatabase 20pub trait HirDatabase: SyntaxDatabase
20 + AsRef<LocationIntener<DefLoc, DefId>> 21 + AsRef<LocationIntener<DefLoc, DefId>>
22 + AsRef<LocationIntener<MacroCallLoc, MacroCallId>>
21{ 23{
24 fn hir_source_file(file_id: HirFileId) -> SourceFileNode {
25 type HirSourceFileQuery;
26 use fn HirFileId::hir_source_file;
27 }
28 fn expand_macro_invocation(invoc: MacroCallId) -> Option<Arc<MacroExpansion>> {
29 type ExpandMacroCallQuery;
30 use fn crate::macros::expand_macro_invocation;
31 }
32
22 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { 33 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> {
23 type FnScopesQuery; 34 type FnScopesQuery;
24 use fn query_definitions::fn_scopes; 35 use fn query_definitions::fn_scopes;
@@ -49,7 +60,7 @@ pub trait HirDatabase: SyntaxDatabase
49 use fn crate::ty::type_for_field; 60 use fn crate::ty::type_for_field;
50 } 61 }
51 62
52 fn file_items(file_id: FileId) -> Arc<SourceFileItems> { 63 fn file_items(file_id: HirFileId) -> Arc<SourceFileItems> {
53 type SourceFileItemsQuery; 64 type SourceFileItemsQuery;
54 use fn query_definitions::file_items; 65 use fn query_definitions::file_items;
55 } 66 }
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
new file mode 100644
index 000000000..a09dee8b1
--- /dev/null
+++ b/crates/ra_hir/src/ids.rs
@@ -0,0 +1,280 @@
1use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3
4use crate::{
5 HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum,
6 arena::{Arena, Id},
7};
8
9/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
10/// can think of id as a pointer (but without a lifetime) or a file descriptor
11/// (but for hir objects).
12///
13/// This module defines a bunch of ids we are using. The most important ones are
14/// probably `HirFileId` and `DefId`.
15
16/// Input to the analyzer is a set of file, where each file is indetified by
17/// `FileId` and contains source code. However, another source of source code in
18/// Rust are macros: each macro can be thought of as producing a "temporary
19/// file". To assign id to such file, we use the id of a macro call that
20/// produced the file. So, a `HirFileId` is either a `FileId` (source code
21/// written by user), or a `MacroCallId` (source code produced by macro).
22///
23/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
24/// the call plus the offset of the macro call in the file. Note that this is a
25/// recursive definition! Nethetheless, size_of of `HirFileId` is finite
26/// (because everything bottoms out at the real `FileId`) and small
27/// (`MacroCallId` uses location interner).
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub struct HirFileId(HirFileIdRepr);
30
31impl HirFileId {
32 /// For macro-expansion files, returns the file original source file the
33 /// expansionoriginated from.
34 pub(crate) fn original_file(self, db: &impl HirDatabase) -> FileId {
35 match self.0 {
36 HirFileIdRepr::File(file_id) => file_id,
37 HirFileIdRepr::Macro(macro_call_id) => {
38 let loc = macro_call_id.loc(db);
39 loc.source_item_id.file_id.original_file(db)
40 }
41 }
42 }
43
44 pub(crate) fn as_original_file(self) -> FileId {
45 match self.0 {
46 HirFileIdRepr::File(file_id) => file_id,
47 HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
48 }
49 }
50
51 pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode {
52 match file_id.0 {
53 HirFileIdRepr::File(file_id) => db.source_file(file_id),
54 HirFileIdRepr::Macro(m) => {
55 if let Some(exp) = db.expand_macro_invocation(m) {
56 return exp.file();
57 }
58 // returning an empty string looks fishy...
59 SourceFileNode::parse("")
60 }
61 }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66enum HirFileIdRepr {
67 File(FileId),
68 Macro(MacroCallId),
69}
70
71impl From<FileId> for HirFileId {
72 fn from(file_id: FileId) -> HirFileId {
73 HirFileId(HirFileIdRepr::File(file_id))
74 }
75}
76
77impl From<MacroCallId> for HirFileId {
78 fn from(macro_call_id: MacroCallId) -> HirFileId {
79 HirFileId(HirFileIdRepr::Macro(macro_call_id))
80 }
81}
82
83/// `MacroCallId` identifies a particular macro invocation, like
84/// `println!("Hello, {}", world)`.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub struct MacroCallId(u32);
87ra_db::impl_numeric_id!(MacroCallId);
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub struct MacroCallLoc {
91 pub(crate) source_root_id: SourceRootId,
92 pub(crate) module_id: ModuleId,
93 pub(crate) source_item_id: SourceItemId,
94}
95
96impl MacroCallId {
97 pub(crate) fn loc(
98 self,
99 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
100 ) -> MacroCallLoc {
101 db.as_ref().id2loc(self)
102 }
103}
104
105impl MacroCallLoc {
106 #[allow(unused)]
107 pub(crate) fn id(
108 &self,
109 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
110 ) -> MacroCallId {
111 db.as_ref().loc2id(&self)
112 }
113}
114
115/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
116/// in a specific module.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct DefId(u32);
119ra_db::impl_numeric_id!(DefId);
120
121#[derive(Clone, Debug, PartialEq, Eq, Hash)]
122pub struct DefLoc {
123 pub(crate) kind: DefKind,
124 pub(crate) source_root_id: SourceRootId,
125 pub(crate) module_id: ModuleId,
126 pub(crate) source_item_id: SourceItemId,
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
130pub(crate) enum DefKind {
131 Module,
132 Function,
133 Struct,
134 Enum,
135 Item,
136
137 StructCtor,
138}
139
140impl DefId {
141 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
142 db.as_ref().id2loc(self)
143 }
144
145 pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
146 let loc = self.loc(db);
147 let res = match loc.kind {
148 DefKind::Module => {
149 let module = Module::new(db, loc.source_root_id, loc.module_id)?;
150 Def::Module(module)
151 }
152 DefKind::Function => {
153 let function = Function::new(self);
154 Def::Function(function)
155 }
156 DefKind::Struct => {
157 let struct_def = Struct::new(self);
158 Def::Struct(struct_def)
159 }
160 DefKind::Enum => {
161 let enum_def = Enum::new(self);
162 Def::Enum(enum_def)
163 }
164 DefKind::StructCtor => Def::Item,
165 DefKind::Item => Def::Item,
166 };
167 Ok(res)
168 }
169
170 /// For a module, returns that module; for any other def, returns the containing module.
171 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
172 let loc = self.loc(db);
173 Module::new(db, loc.source_root_id, loc.module_id)
174 }
175}
176
177impl DefLoc {
178 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
179 db.as_ref().loc2id(&self)
180 }
181}
182
183impl DefKind {
184 pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
185 match kind {
186 SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
187 SyntaxKind::MODULE => PerNs::types(DefKind::Module),
188 SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
189 SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
190 // These define items, but don't have their own DefKinds yet:
191 SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
192 SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
193 SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
194 SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
195 _ => PerNs::none(),
196 }
197 }
198}
199
200/// Identifier of item within a specific file. This is stable over reparses, so
201/// it's OK to use it as a salsa key/value.
202pub(crate) type SourceFileItemId = Id<SyntaxNode>;
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205pub struct SourceItemId {
206 pub(crate) file_id: HirFileId,
207 /// None for the whole file.
208 pub(crate) item_id: Option<SourceFileItemId>,
209}
210
211/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
212#[derive(Debug, PartialEq, Eq)]
213pub struct SourceFileItems {
214 file_id: HirFileId,
215 arena: Arena<SyntaxNode>,
216}
217
218impl SourceFileItems {
219 pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems {
220 let mut res = SourceFileItems {
221 file_id,
222 arena: Arena::default(),
223 };
224 res.init(source_file);
225 res
226 }
227
228 fn init(&mut self, source_file: SourceFile) {
229 source_file.syntax().descendants().for_each(|it| {
230 if let Some(module_item) = ast::ModuleItem::cast(it) {
231 self.alloc(module_item.syntax().owned());
232 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
233 self.alloc(macro_call.syntax().owned());
234 }
235 });
236 }
237
238 fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
239 self.arena.alloc(item)
240 }
241 pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId {
242 assert_eq!(
243 self.file_id, file_id,
244 "SourceFileItems: wrong file, expected {:?}, got {:?}",
245 self.file_id, file_id
246 );
247 self.id_of_unchecked(item)
248 }
249 pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
250 if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
251 return id;
252 }
253 // This should not happen. Let's try to give a sensible diagnostics.
254 if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
255 // FIXME(#288): whyyy are we getting here?
256 log::error!(
257 "unequal syntax nodes with the same range:\n{:?}\n{:?}",
258 item,
259 i
260 );
261 return id;
262 }
263 panic!(
264 "Can't find {:?} in SourceFileItems:\n{:?}",
265 item,
266 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
267 );
268 }
269 pub fn id_of_source_file(&self) -> SourceFileItemId {
270 let (id, _syntax) = self.arena.iter().next().unwrap();
271 id
272 }
273}
274
275impl std::ops::Index<SourceFileItemId> for SourceFileItems {
276 type Output = SyntaxNode;
277 fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
278 &self.arena[idx]
279 }
280}
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs
index 89b1e639e..a0821d15d 100644
--- a/crates/ra_hir/src/krate.rs
+++ b/crates/ra_hir/src/krate.rs
@@ -1,6 +1,6 @@
1pub use ra_db::CrateId; 1pub use ra_db::{CrateId, Cancelable};
2 2
3use crate::{HirDatabase, Module, Cancelable, Name, AsName}; 3use crate::{HirDatabase, Module, Name, AsName, HirFileId};
4 4
5/// hir::Crate describes a single crate. It's the main inteface with which 5/// hir::Crate describes a single crate. It's the main inteface with which
6/// crate's dependencies interact. Mostly, it should be just a proxy for the 6/// crate's dependencies interact. Mostly, it should be just a proxy for the
@@ -35,6 +35,7 @@ impl Crate {
35 let crate_graph = db.crate_graph(); 35 let crate_graph = db.crate_graph();
36 let file_id = crate_graph.crate_root(self.crate_id); 36 let file_id = crate_graph.crate_root(self.crate_id);
37 let source_root_id = db.file_source_root(file_id); 37 let source_root_id = db.file_source_root(file_id);
38 let file_id = HirFileId::from(file_id);
38 let module_tree = db.module_tree(source_root_id)?; 39 let module_tree = db.module_tree(source_root_id)?;
39 // FIXME: teach module tree about crate roots instead of guessing 40 // FIXME: teach module tree about crate roots instead of guessing
40 let (module_id, _) = ctry!(module_tree 41 let (module_id, _) = ctry!(module_tree
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 5bbb09c01..8ee52a466 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -22,7 +22,10 @@ mod path;
22mod arena; 22mod arena;
23pub mod source_binder; 23pub mod source_binder;
24 24
25mod ids;
26mod macros;
25mod name; 27mod name;
28// can't use `crate` or `r#crate` here :(
26mod krate; 29mod krate;
27mod module; 30mod module;
28mod function; 31mod function;
@@ -30,21 +33,18 @@ mod adt;
30mod type_ref; 33mod type_ref;
31mod ty; 34mod ty;
32 35
33use std::ops::Index;
34
35use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
36use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
37
38use crate::{ 36use crate::{
39 db::HirDatabase, 37 db::HirDatabase,
40 arena::{Arena, Id},
41 name::{AsName, KnownName}, 38 name::{AsName, KnownName},
39 ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems},
42}; 40};
43 41
44pub use self::{ 42pub use self::{
45 path::{Path, PathKind}, 43 path::{Path, PathKind},
46 name::Name, 44 name::Name,
47 krate::Crate, 45 krate::Crate,
46 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
47 macros::{MacroDef, MacroInput, MacroExpansion},
48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
49 function::{Function, FnScopes}, 49 function::{Function, FnScopes},
50 adt::{Struct, Enum}, 50 adt::{Struct, Enum},
@@ -53,60 +53,6 @@ pub use self::{
53 53
54pub use self::function::FnSignatureInfo; 54pub use self::function::FnSignatureInfo;
55 55
56/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
57/// in a specific module.
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub struct DefId(u32);
60ra_db::impl_numeric_id!(DefId);
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub(crate) enum DefKind {
64 Module,
65 Function,
66 Struct,
67 Enum,
68 Item,
69
70 StructCtor,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Hash)]
74pub struct DefLoc {
75 pub(crate) kind: DefKind,
76 source_root_id: SourceRootId,
77 module_id: ModuleId,
78 source_item_id: SourceItemId,
79}
80
81impl DefKind {
82 pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
83 match kind {
84 SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
85 SyntaxKind::MODULE => PerNs::types(DefKind::Module),
86 SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
87 SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
88 // These define items, but don't have their own DefKinds yet:
89 SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
90 SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
91 SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
92 SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
93 _ => PerNs::none(),
94 }
95 }
96}
97
98impl DefId {
99 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
100 db.as_ref().id2loc(self)
101 }
102}
103
104impl DefLoc {
105 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
106 db.as_ref().loc2id(&self)
107 }
108}
109
110pub enum Def { 56pub enum Def {
111 Module(Module), 57 Module(Module),
112 Function(Function), 58 Function(Function),
@@ -114,106 +60,3 @@ pub enum Def {
114 Enum(Enum), 60 Enum(Enum),
115 Item, 61 Item,
116} 62}
117
118impl DefId {
119 pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
120 let loc = self.loc(db);
121 let res = match loc.kind {
122 DefKind::Module => {
123 let module = Module::new(db, loc.source_root_id, loc.module_id)?;
124 Def::Module(module)
125 }
126 DefKind::Function => {
127 let function = Function::new(self);
128 Def::Function(function)
129 }
130 DefKind::Struct => {
131 let struct_def = Struct::new(self);
132 Def::Struct(struct_def)
133 }
134 DefKind::Enum => {
135 let enum_def = Enum::new(self);
136 Def::Enum(enum_def)
137 }
138 DefKind::StructCtor => Def::Item,
139 DefKind::Item => Def::Item,
140 };
141 Ok(res)
142 }
143
144 /// For a module, returns that module; for any other def, returns the containing module.
145 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
146 let loc = self.loc(db);
147 Module::new(db, loc.source_root_id, loc.module_id)
148 }
149}
150
151/// Identifier of item within a specific file. This is stable over reparses, so
152/// it's OK to use it as a salsa key/value.
153pub(crate) type SourceFileItemId = Id<SyntaxNode>;
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156pub struct SourceItemId {
157 file_id: FileId,
158 /// None for the whole file.
159 item_id: Option<SourceFileItemId>,
160}
161
162/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
163#[derive(Debug, PartialEq, Eq)]
164pub struct SourceFileItems {
165 file_id: FileId,
166 arena: Arena<SyntaxNode>,
167}
168
169impl SourceFileItems {
170 fn new(file_id: FileId) -> SourceFileItems {
171 SourceFileItems {
172 file_id,
173 arena: Arena::default(),
174 }
175 }
176
177 fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
178 self.arena.alloc(item)
179 }
180 pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId {
181 assert_eq!(
182 self.file_id, file_id,
183 "SourceFileItems: wrong file, expected {:?}, got {:?}",
184 self.file_id, file_id
185 );
186 self.id_of_unchecked(item)
187 }
188 fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
189 if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
190 return id;
191 }
192 // This should not happen. Let's try to give a sensible diagnostics.
193 if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
194 // FIXME(#288): whyyy are we getting here?
195 log::error!(
196 "unequal syntax nodes with the same range:\n{:?}\n{:?}",
197 item,
198 i
199 );
200 return id;
201 }
202 panic!(
203 "Can't find {:?} in SourceFileItems:\n{:?}",
204 item,
205 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
206 );
207 }
208 pub fn id_of_source_file(&self) -> SourceFileItemId {
209 let (id, _syntax) = self.arena.iter().next().unwrap();
210 id
211 }
212}
213
214impl Index<SourceFileItemId> for SourceFileItems {
215 type Output = SyntaxNode;
216 fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
217 &self.arena[idx]
218 }
219}
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs
new file mode 100644
index 000000000..b7b75e702
--- /dev/null
+++ b/crates/ra_hir/src/macros.rs
@@ -0,0 +1,181 @@
1/// Machinery for macro expansion.
2///
3/// One of the more complicated things about macros is managing the source code
4/// that is produced after expansion. See `HirFileId` and `MacroCallId` for how
5/// do we do that.
6///
7/// When file-management question is resolved, all that is left is a token tree
8/// to token tree transformation plus hygent. We don't have either of thouse
9/// yet, so all macros are string based at the moment!
10use std::sync::Arc;
11
12use ra_db::LocalSyntaxPtr;
13use ra_syntax::{
14 TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode,
15 ast::{self, NameOwner},
16};
17
18use crate::{HirDatabase, MacroCallId};
19
20// Hard-coded defs for now :-(
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub enum MacroDef {
23 CTry,
24 QueryGroup,
25}
26
27impl MacroDef {
28 /// Expands macro call, returning the expansion and offset to be used to
29 /// convert ranges between expansion and original source.
30 pub fn ast_expand(macro_call: ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> {
31 let (def, input) = MacroDef::from_call(macro_call)?;
32 let exp = def.expand(input)?;
33 let off = macro_call.token_tree()?.syntax().range().start();
34 Some((off, exp))
35 }
36
37 fn from_call(macro_call: ast::MacroCall) -> Option<(MacroDef, MacroInput)> {
38 let def = {
39 let path = macro_call.path()?;
40 let name_ref = path.segment()?.name_ref()?;
41 if name_ref.text() == "ctry" {
42 MacroDef::CTry
43 } else if name_ref.text() == "query_group" {
44 MacroDef::QueryGroup
45 } else {
46 return None;
47 }
48 };
49
50 let input = {
51 let arg = macro_call.token_tree()?.syntax();
52 MacroInput {
53 text: arg.text().to_string(),
54 }
55 };
56 Some((def, input))
57 }
58
59 fn expand(self, input: MacroInput) -> Option<MacroExpansion> {
60 match self {
61 MacroDef::CTry => self.expand_ctry(input),
62 MacroDef::QueryGroup => self.expand_query_group(input),
63 }
64 }
65 fn expand_ctry(self, input: MacroInput) -> Option<MacroExpansion> {
66 let text = format!(
67 r"
68 fn dummy() {{
69 match {} {{
70 None => return Ok(None),
71 Some(it) => it,
72 }}
73 }}",
74 input.text
75 );
76 let file = SourceFileNode::parse(&text);
77 let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
78 let match_arg = match_expr.expr()?;
79 let ptr = LocalSyntaxPtr::new(match_arg.syntax());
80 let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text));
81 let ranges_map = vec![(src_range, match_arg.syntax().range())];
82 let res = MacroExpansion {
83 text,
84 ranges_map,
85 ptr,
86 };
87 Some(res)
88 }
89 fn expand_query_group(self, input: MacroInput) -> Option<MacroExpansion> {
90 let anchor = "trait ";
91 let pos = input.text.find(anchor)? + anchor.len();
92 let trait_name = input.text[pos..]
93 .chars()
94 .take_while(|c| c.is_alphabetic())
95 .collect::<String>();
96 if trait_name.is_empty() {
97 return None;
98 }
99 let src_range = TextRange::offset_len((pos as u32).into(), TextUnit::of_str(&trait_name));
100 let text = format!(r"trait {} {{ }}", trait_name);
101 let file = SourceFileNode::parse(&text);
102 let trait_def = file.syntax().descendants().find_map(ast::TraitDef::cast)?;
103 let name = trait_def.name()?;
104 let ptr = LocalSyntaxPtr::new(trait_def.syntax());
105 let ranges_map = vec![(src_range, name.syntax().range())];
106 let res = MacroExpansion {
107 text,
108 ranges_map,
109 ptr,
110 };
111 Some(res)
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Hash)]
116pub struct MacroInput {
117 // Should be token trees
118 pub text: String,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct MacroExpansion {
123 /// The result of macro expansion. Should be token tree as well.
124 text: String,
125 /// Correspondence between ranges in the original source code and ranges in
126 /// the macro.
127 ranges_map: Vec<(TextRange, TextRange)>,
128 /// Implementation detail: internally, a macro is expanded to the whole file,
129 /// even if it is an expression. This `ptr` selects the actual expansion from
130 /// the expanded file.
131 ptr: LocalSyntaxPtr,
132}
133
134impl MacroExpansion {
135 // FIXME: does not really make sense, macro expansion is not neccessary a
136 // whole file. See `MacroExpansion::ptr` as well.
137 pub(crate) fn file(&self) -> SourceFileNode {
138 SourceFileNode::parse(&self.text)
139 }
140
141 pub fn syntax(&self) -> SyntaxNode {
142 self.ptr.resolve(&self.file())
143 }
144 /// Maps range in the source code to the range in the expanded code.
145 pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
146 for (s_range, t_range) in self.ranges_map.iter() {
147 if src_range.is_subrange(&s_range) {
148 let src_at_zero_range = src_range - src_range.start();
149 let src_range_offset = src_range.start() - s_range.start();
150 let src_range = src_at_zero_range + src_range_offset + t_range.start();
151 return Some(src_range);
152 }
153 }
154 None
155 }
156 /// Maps range in the expanded code to the range in the source code.
157 pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
158 for (s_range, t_range) in self.ranges_map.iter() {
159 if tgt_range.is_subrange(&t_range) {
160 let tgt_at_zero_range = tgt_range - tgt_range.start();
161 let tgt_range_offset = tgt_range.start() - t_range.start();
162 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
163 return Some(src_range);
164 }
165 }
166 None
167 }
168}
169
170pub(crate) fn expand_macro_invocation(
171 db: &impl HirDatabase,
172 invoc: MacroCallId,
173) -> Option<Arc<MacroExpansion>> {
174 let loc = invoc.loc(db);
175 let syntax = db.file_item(loc.source_item_id);
176 let syntax = syntax.borrowed();
177 let macro_call = ast::MacroCall::cast(syntax).unwrap();
178
179 let (def, input) = MacroDef::from_call(macro_call)?;
180 def.expand(input).map(Arc::new)
181}
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a2507c9b5..89b18194a 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -6,7 +6,7 @@ use ra_db::{LocationIntener, BaseDatabase, FilePosition, FileId, CrateGraph, Sou
6use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; 7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8 8
9use crate::{db, DefId, DefLoc}; 9use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc};
10 10
11pub const WORKSPACE: SourceRootId = SourceRootId(0); 11pub const WORKSPACE: SourceRootId = SourceRootId(0);
12 12
@@ -95,6 +95,7 @@ impl MockDatabase {
95#[derive(Debug, Default)] 95#[derive(Debug, Default)]
96struct IdMaps { 96struct IdMaps {
97 defs: LocationIntener<DefLoc, DefId>, 97 defs: LocationIntener<DefLoc, DefId>,
98 macros: LocationIntener<MacroCallLoc, MacroCallId>,
98} 99}
99 100
100impl salsa::Database for MockDatabase { 101impl salsa::Database for MockDatabase {
@@ -144,6 +145,11 @@ impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase {
144 &self.id_maps.defs 145 &self.id_maps.defs
145 } 146 }
146} 147}
148impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase {
149 fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> {
150 &self.id_maps.macros
151 }
152}
147 153
148impl MockDatabase { 154impl MockDatabase {
149 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { 155 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
@@ -183,6 +189,8 @@ salsa::database_storage! {
183 fn file_lines() for ra_db::FileLinesQuery; 189 fn file_lines() for ra_db::FileLinesQuery;
184 } 190 }
185 impl db::HirDatabase { 191 impl db::HirDatabase {
192 fn hir_source_file() for db::HirSourceFileQuery;
193 fn expand_macro_invocation() for db::ExpandMacroCallQuery;
186 fn module_tree() for db::ModuleTreeQuery; 194 fn module_tree() for db::ModuleTreeQuery;
187 fn fn_scopes() for db::FnScopesQuery; 195 fn fn_scopes() for db::FnScopesQuery;
188 fn file_items() for db::SourceFileItemsQuery; 196 fn file_items() for db::SourceFileItemsQuery;
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index 87e30191f..26171d27c 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -15,6 +15,7 @@ use relative_path::RelativePathBuf;
15use crate::{ 15use crate::{
16 Def, DefKind, DefLoc, DefId, 16 Def, DefKind, DefLoc, DefId,
17 Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, 17 Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate,
18 HirFileId,
18 arena::{Arena, Id}, 19 arena::{Arena, Id},
19}; 20};
20 21
@@ -48,13 +49,17 @@ impl Module {
48 /// Returns `None` for the root module 49 /// Returns `None` for the root module
49 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { 50 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
50 let link = self.module_id.parent_link(&self.tree)?; 51 let link = self.module_id.parent_link(&self.tree)?;
51 let file_id = link.owner(&self.tree).source(&self.tree).file_id(); 52 let file_id = link
53 .owner(&self.tree)
54 .source(&self.tree)
55 .file_id()
56 .as_original_file();
52 let src = link.bind_source(&self.tree, db); 57 let src = link.bind_source(&self.tree, db);
53 Some((file_id, src)) 58 Some((file_id, src))
54 } 59 }
55 60
56 pub fn source(&self) -> ModuleSource { 61 pub fn file_id(&self) -> FileId {
57 self.module_id.source(&self.tree) 62 self.source().file_id().as_original_file()
58 } 63 }
59 64
60 /// Parent module. Returns `None` if this is a root module. 65 /// Parent module. Returns `None` if this is a root module.
@@ -69,7 +74,7 @@ impl Module {
69 /// Returns the crate this module is part of. 74 /// Returns the crate this module is part of.
70 pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { 75 pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
71 let root_id = self.module_id.crate_root(&self.tree); 76 let root_id = self.module_id.crate_root(&self.tree);
72 let file_id = root_id.source(&self.tree).file_id(); 77 let file_id = root_id.source(&self.tree).file_id().as_original_file();
73 let crate_graph = db.crate_graph(); 78 let crate_graph = db.crate_graph();
74 let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; 79 let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
75 Some(Crate::new(crate_id)) 80 Some(Crate::new(crate_id))
@@ -162,6 +167,10 @@ impl Module {
162 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { 167 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
163 self.module_id.problems(&self.tree, db) 168 self.module_id.problems(&self.tree, db)
164 } 169 }
170
171 pub(crate) fn source(&self) -> ModuleSource {
172 self.module_id.source(&self.tree)
173 }
165} 174}
166 175
167/// Physically, rust source is organized as a set of files, but logically it is 176/// Physically, rust source is organized as a set of files, but logically it is
@@ -291,18 +300,18 @@ pub struct ModuleData {
291 300
292impl ModuleSource { 301impl ModuleSource {
293 // precondition: item_id **must** point to module 302 // precondition: item_id **must** point to module
294 fn new(file_id: FileId, item_id: Option<SourceFileItemId>) -> ModuleSource { 303 fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
295 let source_item_id = SourceItemId { file_id, item_id }; 304 let source_item_id = SourceItemId { file_id, item_id };
296 ModuleSource(source_item_id) 305 ModuleSource(source_item_id)
297 } 306 }
298 307
299 pub(crate) fn new_file(file_id: FileId) -> ModuleSource { 308 pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource {
300 ModuleSource::new(file_id, None) 309 ModuleSource::new(file_id, None)
301 } 310 }
302 311
303 pub(crate) fn new_inline( 312 pub(crate) fn new_inline(
304 db: &impl HirDatabase, 313 db: &impl HirDatabase,
305 file_id: FileId, 314 file_id: HirFileId,
306 m: ast::Module, 315 m: ast::Module,
307 ) -> ModuleSource { 316 ) -> ModuleSource {
308 assert!(!m.has_semi()); 317 assert!(!m.has_semi());
@@ -311,7 +320,7 @@ impl ModuleSource {
311 ModuleSource::new(file_id, Some(item_id)) 320 ModuleSource::new(file_id, Some(item_id))
312 } 321 }
313 322
314 pub fn file_id(self) -> FileId { 323 pub(crate) fn file_id(self) -> HirFileId {
315 self.0.file_id 324 self.0.file_id
316 } 325 }
317 326
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
index eded85a63..3849026db 100644
--- a/crates/ra_hir/src/module/imp.rs
+++ b/crates/ra_hir/src/module/imp.rs
@@ -64,7 +64,7 @@ fn create_module_tree<'a>(
64 64
65 let source_root = db.source_root(source_root); 65 let source_root = db.source_root(source_root);
66 for &file_id in source_root.files.values() { 66 for &file_id in source_root.files.values() {
67 let source = ModuleSource::new_file(file_id); 67 let source = ModuleSource::new_file(file_id.into());
68 if visited.contains(&source) { 68 if visited.contains(&source) {
69 continue; // TODO: use explicit crate_roots here 69 continue; // TODO: use explicit crate_roots here
70 } 70 }
@@ -123,7 +123,7 @@ fn build_subtree(
123 visited, 123 visited,
124 roots, 124 roots,
125 Some(link), 125 Some(link),
126 ModuleSource::new_file(file_id), 126 ModuleSource::new_file(file_id.into()),
127 ), 127 ),
128 }) 128 })
129 .collect::<Cancelable<Vec<_>>>()?; 129 .collect::<Cancelable<Vec<_>>>()?;
@@ -155,7 +155,7 @@ fn resolve_submodule(
155 name: &Name, 155 name: &Name,
156) -> (Vec<FileId>, Option<Problem>) { 156) -> (Vec<FileId>, Option<Problem>) {
157 // FIXME: handle submodules of inline modules properly 157 // FIXME: handle submodules of inline modules properly
158 let file_id = source.file_id(); 158 let file_id = source.file_id().original_file(db);
159 let source_root_id = db.file_source_root(file_id); 159 let source_root_id = db.file_source_root(file_id);
160 let path = db.file_relative_path(file_id); 160 let path = db.file_relative_path(file_id);
161 let root = RelativePathBuf::default(); 161 let root = RelativePathBuf::default();
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 68eb02a98..40aa33ffa 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -22,10 +22,10 @@ use ra_syntax::{
22 SyntaxKind::{self, *}, 22 SyntaxKind::{self, *},
23 ast::{self, AstNode} 23 ast::{self, AstNode}
24}; 24};
25use ra_db::SourceRootId; 25use ra_db::{SourceRootId, Cancelable, FileId};
26 26
27use crate::{ 27use crate::{
28 Cancelable, FileId, 28 HirFileId,
29 DefId, DefLoc, DefKind, 29 DefId, DefLoc, DefKind,
30 SourceItemId, SourceFileItemId, SourceFileItems, 30 SourceItemId, SourceFileItemId, SourceFileItems,
31 Path, PathKind, 31 Path, PathKind,
@@ -70,7 +70,7 @@ pub struct InputModuleItems {
70 70
71#[derive(Debug, PartialEq, Eq)] 71#[derive(Debug, PartialEq, Eq)]
72struct ModuleItem { 72struct ModuleItem {
73 id: SourceFileItemId, 73 id: SourceItemId,
74 name: Name, 74 name: Name,
75 kind: SyntaxKind, 75 kind: SyntaxKind,
76 vis: Vis, 76 vis: Vis,
@@ -95,9 +95,11 @@ pub struct NamedImport {
95} 95}
96 96
97impl NamedImport { 97impl NamedImport {
98 // FIXME: this is only here for one use-case in completion. Seems like a
99 // pretty gross special case.
98 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { 100 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
99 let source_item_id = SourceItemId { 101 let source_item_id = SourceItemId {
100 file_id, 102 file_id: file_id.into(),
101 item_id: Some(self.file_item_id), 103 item_id: Some(self.file_item_id),
102 }; 104 };
103 let syntax = db.file_item(source_item_id); 105 let syntax = db.file_item(source_item_id);
@@ -209,24 +211,28 @@ impl<T> PerNs<T> {
209} 211}
210 212
211impl InputModuleItems { 213impl InputModuleItems {
212 pub(crate) fn new<'a>( 214 pub(crate) fn add_item(
215 &mut self,
216 file_id: HirFileId,
213 file_items: &SourceFileItems, 217 file_items: &SourceFileItems,
214 items: impl Iterator<Item = ast::ModuleItem<'a>>, 218 item: ast::ModuleItem,
215 ) -> InputModuleItems { 219 ) -> Option<()> {
216 let mut res = InputModuleItems::default();
217 for item in items {
218 res.add_item(file_items, item);
219 }
220 res
221 }
222
223 fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> {
224 match item { 220 match item {
225 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 221 ast::ModuleItem::StructDef(it) => {
226 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 222 self.items.push(ModuleItem::new(file_id, file_items, it)?)
227 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 223 }
228 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 224 ast::ModuleItem::EnumDef(it) => {
229 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 225 self.items.push(ModuleItem::new(file_id, file_items, it)?)
226 }
227 ast::ModuleItem::FnDef(it) => {
228 self.items.push(ModuleItem::new(file_id, file_items, it)?)
229 }
230 ast::ModuleItem::TraitDef(it) => {
231 self.items.push(ModuleItem::new(file_id, file_items, it)?)
232 }
233 ast::ModuleItem::TypeDef(it) => {
234 self.items.push(ModuleItem::new(file_id, file_items, it)?)
235 }
230 ast::ModuleItem::ImplItem(_) => { 236 ast::ModuleItem::ImplItem(_) => {
231 // impls don't define items 237 // impls don't define items
232 } 238 }
@@ -234,9 +240,15 @@ impl InputModuleItems {
234 ast::ModuleItem::ExternCrateItem(_) => { 240 ast::ModuleItem::ExternCrateItem(_) => {
235 // TODO 241 // TODO
236 } 242 }
237 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 243 ast::ModuleItem::ConstDef(it) => {
238 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?), 244 self.items.push(ModuleItem::new(file_id, file_items, it)?)
239 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?), 245 }
246 ast::ModuleItem::StaticDef(it) => {
247 self.items.push(ModuleItem::new(file_id, file_items, it)?)
248 }
249 ast::ModuleItem::Module(it) => {
250 self.items.push(ModuleItem::new(file_id, file_items, it)?)
251 }
240 } 252 }
241 Some(()) 253 Some(())
242 } 254 }
@@ -258,11 +270,16 @@ impl InputModuleItems {
258} 270}
259 271
260impl ModuleItem { 272impl ModuleItem {
261 fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { 273 fn new<'a>(
274 file_id: HirFileId,
275 file_items: &SourceFileItems,
276 item: impl ast::NameOwner<'a>,
277 ) -> Option<ModuleItem> {
262 let name = item.name()?.as_name(); 278 let name = item.name()?.as_name();
263 let kind = item.syntax().kind(); 279 let kind = item.syntax().kind();
264 let vis = Vis::Other; 280 let vis = Vis::Other;
265 let id = file_items.id_of_unchecked(item.syntax()); 281 let item_id = Some(file_items.id_of_unchecked(item.syntax()));
282 let id = SourceItemId { file_id, item_id };
266 let res = ModuleItem { 283 let res = ModuleItem {
267 id, 284 id,
268 name, 285 name,
@@ -302,7 +319,7 @@ where
302 319
303 pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { 320 pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
304 for (&module_id, items) in self.input.iter() { 321 for (&module_id, items) in self.input.iter() {
305 self.populate_module(module_id, items)?; 322 self.populate_module(module_id, Arc::clone(items))?;
306 } 323 }
307 324
308 for &module_id in self.input.keys() { 325 for &module_id in self.input.keys() {
@@ -312,9 +329,11 @@ where
312 Ok(self.result) 329 Ok(self.result)
313 } 330 }
314 331
315 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> { 332 fn populate_module(
316 let file_id = module_id.source(&self.module_tree).file_id(); 333 &mut self,
317 334 module_id: ModuleId,
335 input: Arc<InputModuleItems>,
336 ) -> Cancelable<()> {
318 let mut module_items = ModuleScope::default(); 337 let mut module_items = ModuleScope::default();
319 338
320 // Populate extern crates prelude 339 // Populate extern crates prelude
@@ -322,7 +341,8 @@ where
322 let root_id = module_id.crate_root(&self.module_tree); 341 let root_id = module_id.crate_root(&self.module_tree);
323 let file_id = root_id.source(&self.module_tree).file_id(); 342 let file_id = root_id.source(&self.module_tree).file_id();
324 let crate_graph = self.db.crate_graph(); 343 let crate_graph = self.db.crate_graph();
325 if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) { 344 if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
345 {
326 let krate = Crate::new(crate_id); 346 let krate = Crate::new(crate_id);
327 for dep in krate.dependencies(self.db) { 347 for dep in krate.dependencies(self.db) {
328 if let Some(module) = dep.krate.root_module(self.db)? { 348 if let Some(module) = dep.krate.root_module(self.db)? {
@@ -362,10 +382,7 @@ where
362 kind: k, 382 kind: k,
363 source_root_id: self.source_root, 383 source_root_id: self.source_root,
364 module_id, 384 module_id,
365 source_item_id: SourceItemId { 385 source_item_id: item.id,
366 file_id,
367 item_id: Some(item.id),
368 },
369 }; 386 };
370 def_loc.id(self.db) 387 def_loc.id(self.db)
371 }); 388 });
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs
index ca20f064f..a6a0bea31 100644
--- a/crates/ra_hir/src/module/nameres/tests.rs
+++ b/crates/ra_hir/src/module/nameres/tests.rs
@@ -78,6 +78,35 @@ fn item_map_smoke_test() {
78} 78}
79 79
80#[test] 80#[test]
81fn item_map_contains_items_from_expansions() {
82 let (item_map, module_id) = item_map(
83 "
84 //- /lib.rs
85 mod foo;
86
87 use crate::foo::bar::Baz;
88 <|>
89
90 //- /foo/mod.rs
91 pub mod bar;
92
93 //- /foo/bar.rs
94 salsa::query_group! {
95 trait Baz {}
96 }
97 ",
98 );
99 check_module_item_map(
100 &item_map,
101 module_id,
102 "
103 Baz: t
104 foo: t
105 ",
106 );
107}
108
109#[test]
81fn item_map_using_self() { 110fn item_map_using_self() {
82 let (item_map, module_id) = item_map( 111 let (item_map, module_id) = item_map(
83 " 112 "
@@ -144,6 +173,59 @@ fn typing_inside_a_function_should_not_invalidate_item_map() {
144 let (mut db, pos) = MockDatabase::with_position( 173 let (mut db, pos) = MockDatabase::with_position(
145 " 174 "
146 //- /lib.rs 175 //- /lib.rs
176 mod foo;
177
178 use crate::foo::bar::Baz;
179
180 //- /foo/mod.rs
181 pub mod bar;
182
183 //- /foo/bar.rs
184 <|>
185 salsa::query_group! {
186 trait Baz {
187 fn foo() -> i32 { 1 + 1 }
188 }
189 }
190 ",
191 );
192 let source_root = db.file_source_root(pos.file_id);
193 {
194 let events = db.log_executed(|| {
195 db.item_map(source_root).unwrap();
196 });
197 assert!(format!("{:?}", events).contains("item_map"))
198 }
199
200 let new_text = "
201 salsa::query_group! {
202 trait Baz {
203 fn foo() -> i32 { 92 }
204 }
205 }
206 "
207 .to_string();
208
209 db.query_mut(ra_db::FileTextQuery)
210 .set(pos.file_id, Arc::new(new_text));
211
212 {
213 let events = db.log_executed(|| {
214 db.item_map(source_root).unwrap();
215 });
216 assert!(
217 !format!("{:?}", events).contains("item_map"),
218 "{:#?}",
219 events
220 )
221 }
222}
223
224#[test]
225fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
226 let (mut db, pos) = MockDatabase::with_position(
227 "
228 //- /lib.rs
147 mod foo;<|> 229 mod foo;<|>
148 230
149 use crate::foo::bar::Baz; 231 use crate::foo::bar::Baz;
@@ -183,7 +265,7 @@ fn typing_inside_a_function_should_not_invalidate_item_map() {
183 db.item_map(source_root).unwrap(); 265 db.item_map(source_root).unwrap();
184 }); 266 });
185 assert!( 267 assert!(
186 !format!("{:?}", events).contains("_item_map"), 268 !format!("{:?}", events).contains("item_map"),
187 "{:#?}", 269 "{:#?}",
188 events 270 events
189 ) 271 )
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index 721bd4195..a5d99beda 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -8,10 +8,11 @@ use ra_syntax::{
8 AstNode, SyntaxNode, 8 AstNode, SyntaxNode,
9 ast::{self, NameOwner, ModuleItemOwner} 9 ast::{self, NameOwner, ModuleItemOwner}
10}; 10};
11use ra_db::{SourceRootId, FileId, Cancelable,}; 11use ra_db::{SourceRootId, Cancelable,};
12 12
13use crate::{ 13use crate::{
14 SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, 14 SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId,
15 MacroCallLoc,
15 db::HirDatabase, 16 db::HirDatabase,
16 function::FnScopes, 17 function::FnScopes,
17 module::{ 18 module::{
@@ -47,25 +48,17 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<
47 Ok(Arc::new(EnumData::new(enum_def.borrowed()))) 48 Ok(Arc::new(EnumData::new(enum_def.borrowed())))
48} 49}
49 50
50pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 51pub(super) fn file_items(db: &impl HirDatabase, file_id: HirFileId) -> Arc<SourceFileItems> {
51 let mut res = SourceFileItems::new(file_id); 52 let source_file = db.hir_source_file(file_id);
52 let source_file = db.source_file(file_id);
53 let source_file = source_file.borrowed(); 53 let source_file = source_file.borrowed();
54 source_file 54 let res = SourceFileItems::new(file_id, source_file);
55 .syntax()
56 .descendants()
57 .filter_map(ast::ModuleItem::cast)
58 .map(|it| it.syntax().owned())
59 .for_each(|it| {
60 res.alloc(it);
61 });
62 Arc::new(res) 55 Arc::new(res)
63} 56}
64 57
65pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { 58pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode {
66 match source_item_id.item_id { 59 match source_item_id.item_id {
67 Some(id) => db.file_items(source_item_id.file_id)[id].clone(), 60 Some(id) => db.file_items(source_item_id.file_id)[id].clone(),
68 None => db.source_file(source_item_id.file_id).syntax().owned(), 61 None => db.hir_source_file(source_item_id.file_id).syntax().owned(),
69 } 62 }
70} 63}
71 64
@@ -87,7 +80,7 @@ pub(crate) fn submodules(
87 80
88 fn collect_submodules<'a>( 81 fn collect_submodules<'a>(
89 db: &impl HirDatabase, 82 db: &impl HirDatabase,
90 file_id: FileId, 83 file_id: HirFileId,
91 root: impl ast::ModuleItemOwner<'a>, 84 root: impl ast::ModuleItemOwner<'a>,
92 ) -> Vec<Submodule> { 85 ) -> Vec<Submodule> {
93 modules(root) 86 modules(root)
@@ -119,24 +112,48 @@ pub(crate) fn modules<'a>(
119 112
120pub(super) fn input_module_items( 113pub(super) fn input_module_items(
121 db: &impl HirDatabase, 114 db: &impl HirDatabase,
122 source_root: SourceRootId, 115 source_root_id: SourceRootId,
123 module_id: ModuleId, 116 module_id: ModuleId,
124) -> Cancelable<Arc<InputModuleItems>> { 117) -> Cancelable<Arc<InputModuleItems>> {
125 let module_tree = db.module_tree(source_root)?; 118 let module_tree = db.module_tree(source_root_id)?;
126 let source = module_id.source(&module_tree); 119 let source = module_id.source(&module_tree);
127 let file_items = db.file_items(source.file_id()); 120 let file_id = source.file_id();
128 let res = match source.resolve(db) { 121 let file_items = db.file_items(file_id);
129 ModuleSourceNode::SourceFile(it) => { 122 let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| {
130 let items = it.borrowed().items(); 123 for item in items {
131 InputModuleItems::new(&file_items, items) 124 match item {
125 ast::ItemOrMacro::Item(it) => {
126 acc.add_item(file_id, &file_items, it);
127 }
128 ast::ItemOrMacro::Macro(macro_call) => {
129 let item_id = file_items.id_of_unchecked(macro_call.syntax());
130 let loc = MacroCallLoc {
131 source_root_id,
132 module_id,
133 source_item_id: SourceItemId {
134 file_id,
135 item_id: Some(item_id),
136 },
137 };
138 let id = loc.id(db);
139 let file_id = HirFileId::from(id);
140 let file_items = db.file_items(file_id);
141 //FIXME: expand recursively
142 for item in db.hir_source_file(file_id).borrowed().items() {
143 acc.add_item(file_id, &file_items, item);
144 }
145 }
146 }
132 } 147 }
148 };
149
150 let mut res = InputModuleItems::default();
151 match source.resolve(db) {
152 ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()),
133 ModuleSourceNode::Module(it) => { 153 ModuleSourceNode::Module(it) => {
134 let items = it 154 if let Some(item_list) = it.borrowed().item_list() {
135 .borrowed() 155 fill(&mut res, &mut item_list.items_with_macros())
136 .item_list() 156 }
137 .into_iter()
138 .flat_map(|it| it.items());
139 InputModuleItems::new(&file_items, items)
140 } 157 }
141 }; 158 };
142 Ok(Arc::new(res)) 159 Ok(Arc::new(res))
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index a0d1daf71..24490d119 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -20,7 +20,7 @@ use crate::{
20 20
21/// Locates the module by `FileId`. Picks topmost module in the file. 21/// Locates the module by `FileId`. Picks topmost module in the file.
22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { 22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> {
23 let module_source = ModuleSource::new_file(file_id); 23 let module_source = ModuleSource::new_file(file_id.into());
24 module_from_source(db, module_source) 24 module_from_source(db, module_source)
25} 25}
26 26
@@ -50,8 +50,8 @@ pub fn module_from_position(
50) -> Cancelable<Option<Module>> { 50) -> Cancelable<Option<Module>> {
51 let file = db.source_file(position.file_id); 51 let file = db.source_file(position.file_id);
52 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { 52 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
53 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), 53 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m),
54 _ => ModuleSource::new_file(position.file_id), 54 _ => ModuleSource::new_file(position.file_id.into()),
55 }; 55 };
56 module_from_source(db, module_source) 56 module_from_source(db, module_source)
57} 57}
@@ -67,9 +67,9 @@ pub fn module_from_child_node(
67 .filter_map(ast::Module::cast) 67 .filter_map(ast::Module::cast)
68 .find(|it| !it.has_semi()) 68 .find(|it| !it.has_semi())
69 { 69 {
70 ModuleSource::new_inline(db, file_id, m) 70 ModuleSource::new_inline(db, file_id.into(), m)
71 } else { 71 } else {
72 ModuleSource::new_file(file_id) 72 ModuleSource::new_file(file_id.into())
73 }; 73 };
74 module_from_source(db, module_source) 74 module_from_source(db, module_source)
75} 75}
@@ -78,7 +78,7 @@ fn module_from_source(
78 db: &impl HirDatabase, 78 db: &impl HirDatabase,
79 module_source: ModuleSource, 79 module_source: ModuleSource,
80) -> Cancelable<Option<Module>> { 80) -> Cancelable<Option<Module>> {
81 let source_root_id = db.file_source_root(module_source.file_id()); 81 let source_root_id = db.file_source_root(module_source.file_id().as_original_file());
82 let module_tree = db.module_tree(source_root_id)?; 82 let module_tree = db.module_tree(source_root_id)?;
83 let m = module_tree 83 let m = module_tree
84 .modules_with_sources() 84 .modules_with_sources()
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 8fb6b6408..3e948800e 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -48,10 +48,40 @@ pub trait FnDefOwner<'a>: AstNode<'a> {
48 } 48 }
49} 49}
50 50
51// ModuleItem
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum ItemOrMacro<'a> {
54 Item(ModuleItem<'a>),
55 Macro(MacroCall<'a>),
56}
57
58impl<'a> AstNode<'a> for ItemOrMacro<'a> {
59 fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
60 let res = if let Some(item) = ModuleItem::cast(syntax) {
61 ItemOrMacro::Item(item)
62 } else if let Some(macro_call) = MacroCall::cast(syntax) {
63 ItemOrMacro::Macro(macro_call)
64 } else {
65 return None;
66 };
67 Some(res)
68 }
69 fn syntax(self) -> SyntaxNodeRef<'a> {
70 match self {
71 ItemOrMacro::Item(it) => it.syntax(),
72 ItemOrMacro::Macro(it) => it.syntax(),
73 }
74 }
75}
76
51pub trait ModuleItemOwner<'a>: AstNode<'a> { 77pub trait ModuleItemOwner<'a>: AstNode<'a> {
52 fn items(self) -> AstChildren<'a, ModuleItem<'a>> { 78 fn items(self) -> AstChildren<'a, ModuleItem<'a>> {
53 children(self) 79 children(self)
54 } 80 }
81
82 fn items_with_macros(self) -> AstChildren<'a, ItemOrMacro<'a>> {
83 children(self)
84 }
55} 85}
56 86
57pub trait TypeParamsOwner<'a>: AstNode<'a> { 87pub trait TypeParamsOwner<'a>: AstNode<'a> {
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 34a3aabef..6753c513f 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -51,7 +51,7 @@ use ra_text_edit::AtomTextEdit;
51use crate::yellow::GreenNode; 51use crate::yellow::GreenNode;
52 52
53/// `SourceFileNode` represents a parse tree for a single Rust file. 53/// `SourceFileNode` represents a parse tree for a single Rust file.
54pub use crate::ast::SourceFileNode; 54pub use crate::ast::{SourceFile, SourceFileNode};
55 55
56impl SourceFileNode { 56impl SourceFileNode {
57 fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode { 57 fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode {