aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-10-29 11:55:39 +0000
committerAleksey Kladov <[email protected]>2019-10-29 11:55:39 +0000
commit541387564483ee3a42a1969fd048f94e57599ca4 (patch)
tree65575cdef1622e15d2fea79e886951a8b38c3406
parent4f22d2f3b0852f32c0ba5e4545ec8cc2d986cfcc (diff)
move expansion-related code to a separate crate
-rw-r--r--Cargo.lock4
-rw-r--r--crates/ra_hir/src/db.rs44
-rw-r--r--crates/ra_hir/src/debug.rs10
-rw-r--r--crates/ra_hir/src/ids.rs206
-rw-r--r--crates/ra_hir/src/nameres/collector.rs3
-rw-r--r--crates/ra_hir/src/path.rs9
-rw-r--r--crates/ra_hir/src/source_id.rs73
-rw-r--r--crates/ra_hir_def/Cargo.toml5
-rw-r--r--crates/ra_hir_def/src/db.rs46
-rw-r--r--crates/ra_hir_def/src/expand.rs243
-rw-r--r--crates/ra_hir_def/src/lib.rs4
11 files changed, 344 insertions, 303 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 260be7289..c0b060aea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1005,9 +1005,13 @@ dependencies = [
1005name = "ra_hir_def" 1005name = "ra_hir_def"
1006version = "0.1.0" 1006version = "0.1.0"
1007dependencies = [ 1007dependencies = [
1008 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
1008 "ra_arena 0.1.0", 1009 "ra_arena 0.1.0",
1009 "ra_db 0.1.0", 1010 "ra_db 0.1.0",
1011 "ra_mbe 0.1.0",
1012 "ra_prof 0.1.0",
1010 "ra_syntax 0.1.0", 1013 "ra_syntax 0.1.0",
1014 "ra_tt 0.1.0",
1011] 1015]
1012 1016
1013[[package]] 1017[[package]]
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 7abbf8dca..a8fd695c0 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -3,7 +3,7 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use ra_db::{salsa, SourceDatabase}; 5use ra_db::{salsa, SourceDatabase};
6use ra_syntax::{ast, Parse, SmolStr, SyntaxNode}; 6use ra_syntax::{ast, SmolStr};
7 7
8use crate::{ 8use crate::{
9 adt::{EnumData, StructData}, 9 adt::{EnumData, StructData},
@@ -19,9 +19,13 @@ use crate::{
19 InferenceResult, Substs, Ty, TypableDef, TypeCtor, 19 InferenceResult, Substs, Ty, TypableDef, TypeCtor,
20 }, 20 },
21 type_alias::TypeAliasData, 21 type_alias::TypeAliasData,
22 AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData, 22 Const, ConstData, Crate, DefWithBody, Enum, ExprScopes, FnData, Function, HirFileId, Module,
23 Function, HirFileId, MacroCallLoc, MacroDefId, Module, Static, Struct, StructField, Trait, 23 Static, Struct, StructField, Trait, TypeAlias,
24 TypeAlias, 24};
25
26pub use hir_def::db::{
27 AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
28 ParseMacroQuery,
25}; 29};
26 30
27/// We store all interned things in the single QueryGroup. 31/// We store all interned things in the single QueryGroup.
@@ -32,8 +36,6 @@ use crate::{
32#[salsa::query_group(InternDatabaseStorage)] 36#[salsa::query_group(InternDatabaseStorage)]
33pub trait InternDatabase: SourceDatabase { 37pub trait InternDatabase: SourceDatabase {
34 #[salsa::interned] 38 #[salsa::interned]
35 fn intern_macro(&self, macro_call: MacroCallLoc) -> ids::MacroCallId;
36 #[salsa::interned]
37 fn intern_function(&self, loc: ids::ItemLoc<ast::FnDef>) -> ids::FunctionId; 39 fn intern_function(&self, loc: ids::ItemLoc<ast::FnDef>) -> ids::FunctionId;
38 #[salsa::interned] 40 #[salsa::interned]
39 fn intern_struct(&self, loc: ids::ItemLoc<ast::StructDef>) -> ids::StructId; 41 fn intern_struct(&self, loc: ids::ItemLoc<ast::StructDef>) -> ids::StructId;
@@ -55,38 +57,10 @@ pub trait InternDatabase: SourceDatabase {
55 fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId; 57 fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId;
56} 58}
57 59
58/// This database has access to source code, so queries here are not really
59/// incremental.
60#[salsa::query_group(AstDatabaseStorage)]
61pub trait AstDatabase: InternDatabase {
62 #[salsa::invoke(crate::source_id::ast_id_map_query)]
63 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
64
65 #[salsa::transparent]
66 #[salsa::invoke(crate::source_id::file_item_query)]
67 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode;
68
69 #[salsa::transparent]
70 #[salsa::invoke(crate::ids::HirFileId::parse_or_expand_query)]
71 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
72
73 #[salsa::invoke(crate::ids::HirFileId::parse_macro_query)]
74 fn parse_macro(&self, macro_file: ids::MacroFile) -> Option<Parse<SyntaxNode>>;
75
76 #[salsa::invoke(crate::ids::macro_def_query)]
77 fn macro_def(&self, macro_id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
78
79 #[salsa::invoke(crate::ids::macro_arg_query)]
80 fn macro_arg(&self, macro_call: ids::MacroCallId) -> Option<Arc<tt::Subtree>>;
81
82 #[salsa::invoke(crate::ids::macro_expand_query)]
83 fn macro_expand(&self, macro_call: ids::MacroCallId) -> Result<Arc<tt::Subtree>, String>;
84}
85
86// This database uses `AstDatabase` internally, 60// This database uses `AstDatabase` internally,
87#[salsa::query_group(DefDatabaseStorage)] 61#[salsa::query_group(DefDatabaseStorage)]
88#[salsa::requires(AstDatabase)] 62#[salsa::requires(AstDatabase)]
89pub trait DefDatabase: InternDatabase + HirDebugDatabase { 63pub trait DefDatabase: InternDatabase + HirDebugDatabase + AstDatabase {
90 #[salsa::invoke(crate::adt::StructData::struct_data_query)] 64 #[salsa::invoke(crate::adt::StructData::struct_data_query)]
91 fn struct_data(&self, s: Struct) -> Arc<StructData>; 65 fn struct_data(&self, s: Struct) -> Arc<StructData>;
92 66
diff --git a/crates/ra_hir/src/debug.rs b/crates/ra_hir/src/debug.rs
index 48b69000b..c3f890ed4 100644
--- a/crates/ra_hir/src/debug.rs
+++ b/crates/ra_hir/src/debug.rs
@@ -36,11 +36,11 @@ impl Module {
36 } 36 }
37} 37}
38 38
39impl HirFileId { 39// impl HirFileId {
40 pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ { 40// pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
41 debug_fn(move |fmt| db.debug_hir_file_id(self, fmt)) 41// debug_fn(move |fmt| db.debug_hir_file_id(self, fmt))
42 } 42// }
43} 43// }
44 44
45pub trait HirDebugHelper: HirDatabase { 45pub trait HirDebugHelper: HirDatabase {
46 fn crate_name(&self, _krate: CrateId) -> Option<String> { 46 fn crate_name(&self, _krate: CrateId) -> Option<String> {
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index f141206c6..9f85bb30d 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -1,168 +1,23 @@
1//! FIXME: write short doc here 1//! hir makes heavy use of ids: integer (u32) handlers to various things. You
2//! can think of id as a pointer (but without a lifetime) or a file descriptor
3//! (but for hir objects).
4//!
5//! This module defines a bunch of ids we are using. The most important ones are
6//! probably `HirFileId` and `DefId`.
2 7
3use std::{ 8use std::hash::{Hash, Hasher};
4 hash::{Hash, Hasher},
5 sync::Arc,
6};
7 9
8use mbe::MacroRules; 10use ra_db::salsa;
9use ra_db::{salsa, FileId}; 11use ra_syntax::{ast, AstNode};
10use ra_prof::profile;
11use ra_syntax::{ast, AstNode, Parse, SyntaxNode};
12 12
13use crate::{ 13use crate::{
14 db::{AstDatabase, InternDatabase}, 14 db::{AstDatabase, InternDatabase},
15 AstId, Crate, FileAstId, Module, Source, 15 AstId, FileAstId, Module, Source,
16}; 16};
17 17
18/// hir makes heavy use of ids: integer (u32) handlers to various things. You 18pub use hir_def::expand::{
19/// can think of id as a pointer (but without a lifetime) or a file descriptor 19 HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, MacroFileKind,
20/// (but for hir objects). 20};
21///
22/// This module defines a bunch of ids we are using. The most important ones are
23/// probably `HirFileId` and `DefId`.
24
25/// Input to the analyzer is a set of files, where each file is identified by
26/// `FileId` and contains source code. However, another source of source code in
27/// Rust are macros: each macro can be thought of as producing a "temporary
28/// file". To assign an id to such a file, we use the id of the macro call that
29/// produced the file. So, a `HirFileId` is either a `FileId` (source code
30/// written by user), or a `MacroCallId` (source code produced by macro).
31///
32/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
33/// containing the call plus the offset of the macro call in the file. Note that
34/// this is a recursive definition! However, the size_of of `HirFileId` is
35/// finite (because everything bottoms out at the real `FileId`) and small
36/// (`MacroCallId` uses the location interner).
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub struct HirFileId(HirFileIdRepr);
39
40impl HirFileId {
41 /// For macro-expansion files, returns the file original source file the
42 /// expansion originated from.
43 pub fn original_file(self, db: &impl InternDatabase) -> FileId {
44 match self.0 {
45 HirFileIdRepr::File(file_id) => file_id,
46 HirFileIdRepr::Macro(macro_file) => {
47 let loc = macro_file.macro_call_id.loc(db);
48 loc.ast_id.file_id().original_file(db)
49 }
50 }
51 }
52
53 /// Get the crate which the macro lives in, if it is a macro file.
54 pub(crate) fn macro_crate(self, db: &impl AstDatabase) -> Option<Crate> {
55 match self.0 {
56 HirFileIdRepr::File(_) => None,
57 HirFileIdRepr::Macro(macro_file) => {
58 let loc = macro_file.macro_call_id.loc(db);
59 Some(loc.def.krate)
60 }
61 }
62 }
63
64 pub(crate) fn parse_or_expand_query(
65 db: &impl AstDatabase,
66 file_id: HirFileId,
67 ) -> Option<SyntaxNode> {
68 match file_id.0 {
69 HirFileIdRepr::File(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
70 HirFileIdRepr::Macro(macro_file) => {
71 db.parse_macro(macro_file).map(|it| it.syntax_node())
72 }
73 }
74 }
75
76 pub(crate) fn parse_macro_query(
77 db: &impl AstDatabase,
78 macro_file: MacroFile,
79 ) -> Option<Parse<SyntaxNode>> {
80 let _p = profile("parse_macro_query");
81 let macro_call_id = macro_file.macro_call_id;
82 let tt = db
83 .macro_expand(macro_call_id)
84 .map_err(|err| {
85 // Note:
86 // The final goal we would like to make all parse_macro success,
87 // such that the following log will not call anyway.
88 log::warn!("fail on macro_parse: (reason: {})", err,);
89 })
90 .ok()?;
91 match macro_file.macro_file_kind {
92 MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
93 MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99enum HirFileIdRepr {
100 File(FileId),
101 Macro(MacroFile),
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub struct MacroFile {
106 macro_call_id: MacroCallId,
107 macro_file_kind: MacroFileKind,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub(crate) enum MacroFileKind {
112 Items,
113 Expr,
114}
115
116impl From<FileId> for HirFileId {
117 fn from(file_id: FileId) -> HirFileId {
118 HirFileId(HirFileIdRepr::File(file_id))
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
123pub struct MacroDefId {
124 pub(crate) ast_id: AstId<ast::MacroCall>,
125 pub(crate) krate: Crate,
126}
127
128pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
129 let macro_call = id.ast_id.to_node(db);
130 let arg = macro_call.token_tree()?;
131 let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
132 log::warn!("fail on macro_def to token tree: {:#?}", arg);
133 None
134 })?;
135 let rules = MacroRules::parse(&tt).ok().or_else(|| {
136 log::warn!("fail on macro_def parse: {:#?}", tt);
137 None
138 })?;
139 Some(Arc::new(rules))
140}
141
142pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
143 let loc = id.loc(db);
144 let macro_call = loc.ast_id.to_node(db);
145 let arg = macro_call.token_tree()?;
146 let (tt, _) = mbe::ast_to_token_tree(&arg)?;
147 Some(Arc::new(tt))
148}
149
150pub(crate) fn macro_expand_query(
151 db: &impl AstDatabase,
152 id: MacroCallId,
153) -> Result<Arc<tt::Subtree>, String> {
154 let loc = id.loc(db);
155 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
156
157 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
158 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
159 // Set a hard limit for the expanded tt
160 let count = tt.count();
161 if count > 65536 {
162 return Err(format!("Total tokens count exceed limit : count = {}", count));
163 }
164 Ok(Arc::new(tt))
165}
166 21
167macro_rules! impl_intern_key { 22macro_rules! impl_intern_key {
168 ($name:ident) => { 23 ($name:ident) => {
@@ -177,35 +32,6 @@ macro_rules! impl_intern_key {
177 }; 32 };
178} 33}
179 34
180/// `MacroCallId` identifies a particular macro invocation, like
181/// `println!("Hello, {}", world)`.
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
183pub struct MacroCallId(salsa::InternId);
184impl_intern_key!(MacroCallId);
185
186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
187pub struct MacroCallLoc {
188 pub(crate) def: MacroDefId,
189 pub(crate) ast_id: AstId<ast::MacroCall>,
190}
191
192impl MacroCallId {
193 pub(crate) fn loc(self, db: &impl InternDatabase) -> MacroCallLoc {
194 db.lookup_intern_macro(self)
195 }
196
197 pub(crate) fn as_file(self, kind: MacroFileKind) -> HirFileId {
198 let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
199 HirFileId(HirFileIdRepr::Macro(macro_file))
200 }
201}
202
203impl MacroCallLoc {
204 pub(crate) fn id(self, db: &impl InternDatabase) -> MacroCallId {
205 db.intern_macro(self)
206 }
207}
208
209#[derive(Debug)] 35#[derive(Debug)]
210pub struct ItemLoc<N: AstNode> { 36pub struct ItemLoc<N: AstNode> {
211 pub(crate) module: Module, 37 pub(crate) module: Module,
@@ -244,7 +70,7 @@ impl<'a, DB> LocationCtx<&'a DB> {
244 } 70 }
245} 71}
246 72
247impl<'a, DB: AstDatabase> LocationCtx<&'a DB> { 73impl<'a, DB: AstDatabase + InternDatabase> LocationCtx<&'a DB> {
248 pub(crate) fn to_def<N, DEF>(self, ast: &N) -> DEF 74 pub(crate) fn to_def<N, DEF>(self, ast: &N) -> DEF
249 where 75 where
250 N: AstNode, 76 N: AstNode,
@@ -258,7 +84,7 @@ pub(crate) trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
258 fn intern(db: &impl InternDatabase, loc: ItemLoc<N>) -> Self; 84 fn intern(db: &impl InternDatabase, loc: ItemLoc<N>) -> Self;
259 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<N>; 85 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<N>;
260 86
261 fn from_ast(ctx: LocationCtx<&impl AstDatabase>, ast: &N) -> Self { 87 fn from_ast(ctx: LocationCtx<&(impl AstDatabase + InternDatabase)>, ast: &N) -> Self {
262 let items = ctx.db.ast_id_map(ctx.file_id); 88 let items = ctx.db.ast_id_map(ctx.file_id);
263 let item_id = items.ast_id(ast); 89 let item_id = items.ast_id(ast);
264 Self::from_ast_id(ctx, item_id) 90 Self::from_ast_id(ctx, item_id)
@@ -267,7 +93,7 @@ pub(crate) trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
267 let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) }; 93 let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) };
268 Self::intern(ctx.db, loc) 94 Self::intern(ctx.db, loc)
269 } 95 }
270 fn source(self, db: &impl AstDatabase) -> Source<N> { 96 fn source(self, db: &(impl AstDatabase + InternDatabase)) -> Source<N> {
271 let loc = self.lookup_intern(db); 97 let loc = self.lookup_intern(db);
272 let ast = loc.ast_id.to_node(db); 98 let ast = loc.ast_id.to_node(db);
273 Source { file_id: loc.ast_id.file_id(), ast } 99 Source { file_id: loc.ast_id.file_id(), ast }
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 4f363df36..885ea57a1 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -676,7 +676,8 @@ where
676 // Case 1: macro rules, define a macro in crate-global mutable scope 676 // Case 1: macro rules, define a macro in crate-global mutable scope
677 if is_macro_rules(&mac.path) { 677 if is_macro_rules(&mac.path) {
678 if let Some(name) = &mac.name { 678 if let Some(name) = &mac.name {
679 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate }; 679 let macro_id =
680 MacroDefId { ast_id, krate: self.def_collector.def_map.krate.crate_id };
680 let macro_ = MacroDef { id: macro_id }; 681 let macro_ = MacroDef { id: macro_id };
681 self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); 682 self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
682 } 683 }
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 394617e1a..bbe536bcb 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -66,7 +66,12 @@ impl Path {
66 mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>), 66 mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
67 ) { 67 ) {
68 if let Some(tree) = item_src.ast.use_tree() { 68 if let Some(tree) = item_src.ast.use_tree() {
69 expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb); 69 expand_use_tree(
70 None,
71 tree,
72 &|| item_src.file_id.macro_crate(db).map(|crate_id| Crate { crate_id }),
73 &mut cb,
74 );
70 } 75 }
71 } 76 }
72 77
@@ -90,7 +95,7 @@ impl Path {
90 /// It correctly handles `$crate` based path from macro call. 95 /// It correctly handles `$crate` based path from macro call.
91 pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> { 96 pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> {
92 let file_id = source.file_id; 97 let file_id = source.file_id;
93 Path::parse(source.ast, &|| file_id.macro_crate(db)) 98 Path::parse(source.ast, &|| file_id.macro_crate(db).map(|crate_id| Crate { crate_id }))
94 } 99 }
95 100
96 fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Option<Path> { 101 fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Option<Path> {
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs
index 260b79661..c70245949 100644
--- a/crates/ra_hir/src/source_id.rs
+++ b/crates/ra_hir/src/source_id.rs
@@ -1,73 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3pub use hir_def::{
4 hash::{Hash, Hasher}, 4 ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId},
5 sync::Arc, 5 expand::AstId,
6}; 6};
7
8pub use hir_def::ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId};
9use ra_syntax::{AstNode, SyntaxNode};
10
11use crate::{db::AstDatabase, HirFileId};
12
13/// `AstId` points to an AST node in any file.
14///
15/// It is stable across reparses, and can be used as salsa key/value.
16// FIXME: isn't this just a `Source<FileAstId<N>>` ?
17#[derive(Debug)]
18pub(crate) struct AstId<N: AstNode> {
19 file_id: HirFileId,
20 file_ast_id: FileAstId<N>,
21}
22
23impl<N: AstNode> Clone for AstId<N> {
24 fn clone(&self) -> AstId<N> {
25 *self
26 }
27}
28impl<N: AstNode> Copy for AstId<N> {}
29
30impl<N: AstNode> PartialEq for AstId<N> {
31 fn eq(&self, other: &Self) -> bool {
32 (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
33 }
34}
35impl<N: AstNode> Eq for AstId<N> {}
36impl<N: AstNode> Hash for AstId<N> {
37 fn hash<H: Hasher>(&self, hasher: &mut H) {
38 (self.file_id, self.file_ast_id).hash(hasher);
39 }
40}
41
42impl<N: AstNode> AstId<N> {
43 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
44 AstId { file_id, file_ast_id }
45 }
46
47 pub(crate) fn file_id(&self) -> HirFileId {
48 self.file_id
49 }
50
51 pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N {
52 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into());
53 N::cast(syntax_node).unwrap()
54 }
55}
56
57pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
58 let map = if let Some(node) = db.parse_or_expand(file_id) {
59 AstIdMap::from_source(&node)
60 } else {
61 AstIdMap::default()
62 };
63 Arc::new(map)
64}
65
66pub(crate) fn file_item_query(
67 db: &impl AstDatabase,
68 file_id: HirFileId,
69 ast_id: ErasedFileAstId,
70) -> SyntaxNode {
71 let node = db.parse_or_expand(file_id).unwrap();
72 db.ast_id_map(file_id)[ast_id].to_node(&node)
73}
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 7c57d56bd..049f8a4fc 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -5,6 +5,11 @@ version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6 6
7[dependencies] 7[dependencies]
8log = "0.4.5"
9
8ra_arena = { path = "../ra_arena" } 10ra_arena = { path = "../ra_arena" }
9ra_db = { path = "../ra_db" } 11ra_db = { path = "../ra_db" }
10ra_syntax = { path = "../ra_syntax" } 12ra_syntax = { path = "../ra_syntax" }
13ra_prof = { path = "../ra_prof" }
14tt = { path = "../ra_tt", package = "ra_tt" }
15mbe = { path = "../ra_mbe", package = "ra_mbe" }
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
new file mode 100644
index 000000000..7133b61db
--- /dev/null
+++ b/crates/ra_hir_def/src/db.rs
@@ -0,0 +1,46 @@
1use std::sync::Arc;
2
3use ra_db::{salsa, SourceDatabase};
4use ra_syntax::{Parse, SyntaxNode};
5
6use crate::{
7 ast_id_map::{AstIdMap, ErasedFileAstId},
8 expand::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
9};
10
11#[salsa::query_group(AstDatabaseStorage)]
12pub trait AstDatabase: SourceDatabase {
13 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
14 #[salsa::transparent]
15 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode;
16
17 #[salsa::transparent]
18 #[salsa::invoke(crate::expand::parse_or_expand_query)]
19 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
20
21 #[salsa::interned]
22 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
23 #[salsa::invoke(crate::expand::macro_arg_query)]
24 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>;
25 #[salsa::invoke(crate::expand::macro_def_query)]
26 fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
27 #[salsa::invoke(crate::expand::parse_macro_query)]
28 fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>;
29 #[salsa::invoke(crate::expand::macro_expand_query)]
30 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
31}
32
33pub(crate) fn ast_id_map(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
34 let map =
35 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
36 Arc::new(map)
37}
38
39pub(crate) fn ast_id_to_node(
40 db: &impl AstDatabase,
41 file_id: HirFileId,
42 ast_id: ErasedFileAstId,
43) -> SyntaxNode {
44 let node = db.parse_or_expand(file_id).unwrap();
45 db.ast_id_map(file_id)[ast_id].to_node(&node)
46}
diff --git a/crates/ra_hir_def/src/expand.rs b/crates/ra_hir_def/src/expand.rs
new file mode 100644
index 000000000..6517ea84d
--- /dev/null
+++ b/crates/ra_hir_def/src/expand.rs
@@ -0,0 +1,243 @@
1use std::{
2 hash::{Hash, Hasher},
3 sync::Arc,
4};
5
6use mbe::MacroRules;
7use ra_db::{salsa, CrateId, FileId};
8use ra_prof::profile;
9use ra_syntax::{
10 ast::{self, AstNode},
11 Parse, SyntaxNode,
12};
13
14use crate::{ast_id_map::FileAstId, db::AstDatabase};
15
16macro_rules! impl_intern_key {
17 ($name:ident) => {
18 impl salsa::InternKey for $name {
19 fn from_intern_id(v: salsa::InternId) -> Self {
20 $name(v)
21 }
22 fn as_intern_id(&self) -> salsa::InternId {
23 self.0
24 }
25 }
26 };
27}
28
29/// Input to the analyzer is a set of files, where each file is identified by
30/// `FileId` and contains source code. However, another source of source code in
31/// Rust are macros: each macro can be thought of as producing a "temporary
32/// file". To assign an id to such a file, we use the id of the macro call that
33/// produced the file. So, a `HirFileId` is either a `FileId` (source code
34/// written by user), or a `MacroCallId` (source code produced by macro).
35///
36/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
37/// containing the call plus the offset of the macro call in the file. Note that
38/// this is a recursive definition! However, the size_of of `HirFileId` is
39/// finite (because everything bottoms out at the real `FileId`) and small
40/// (`MacroCallId` uses the location interner).
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub enum HirFileId {
43 FileId(FileId),
44 MacroFile(MacroFile),
45}
46
47impl From<FileId> for HirFileId {
48 fn from(id: FileId) -> Self {
49 HirFileId::FileId(id)
50 }
51}
52
53impl From<MacroFile> for HirFileId {
54 fn from(id: MacroFile) -> Self {
55 HirFileId::MacroFile(id)
56 }
57}
58
59impl HirFileId {
60 /// For macro-expansion files, returns the file original source file the
61 /// expansion originated from.
62 pub fn original_file(self, db: &impl AstDatabase) -> FileId {
63 match self {
64 HirFileId::FileId(file_id) => file_id,
65 HirFileId::MacroFile(macro_file) => {
66 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
67 loc.ast_id.file_id().original_file(db)
68 }
69 }
70 }
71
72 /// Get the crate which the macro lives in, if it is a macro file.
73 pub fn macro_crate(self, db: &impl AstDatabase) -> Option<CrateId> {
74 match self {
75 HirFileId::FileId(_) => None,
76 HirFileId::MacroFile(macro_file) => {
77 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
78 Some(loc.def.krate)
79 }
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85pub struct MacroFile {
86 macro_call_id: MacroCallId,
87 macro_file_kind: MacroFileKind,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub enum MacroFileKind {
92 Items,
93 Expr,
94}
95
96/// `MacroCallId` identifies a particular macro invocation, like
97/// `println!("Hello, {}", world)`.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99pub struct MacroCallId(salsa::InternId);
100impl_intern_key!(MacroCallId);
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub struct MacroDefId {
104 pub krate: CrateId,
105 pub ast_id: AstId<ast::MacroCall>,
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Hash)]
109pub struct MacroCallLoc {
110 pub def: MacroDefId,
111 pub ast_id: AstId<ast::MacroCall>,
112}
113
114impl MacroCallId {
115 pub fn loc(self, db: &impl AstDatabase) -> MacroCallLoc {
116 db.lookup_intern_macro(self)
117 }
118
119 pub fn as_file(self, kind: MacroFileKind) -> HirFileId {
120 let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
121 macro_file.into()
122 }
123}
124
125impl MacroCallLoc {
126 pub fn id(self, db: &impl AstDatabase) -> MacroCallId {
127 db.intern_macro(self)
128 }
129}
130
131/// `AstId` points to an AST node in any file.
132///
133/// It is stable across reparses, and can be used as salsa key/value.
134// FIXME: isn't this just a `Source<FileAstId<N>>` ?
135#[derive(Debug)]
136pub struct AstId<N: AstNode> {
137 file_id: HirFileId,
138 file_ast_id: FileAstId<N>,
139}
140
141impl<N: AstNode> Clone for AstId<N> {
142 fn clone(&self) -> AstId<N> {
143 *self
144 }
145}
146impl<N: AstNode> Copy for AstId<N> {}
147
148impl<N: AstNode> PartialEq for AstId<N> {
149 fn eq(&self, other: &Self) -> bool {
150 (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
151 }
152}
153impl<N: AstNode> Eq for AstId<N> {}
154impl<N: AstNode> Hash for AstId<N> {
155 fn hash<H: Hasher>(&self, hasher: &mut H) {
156 (self.file_id, self.file_ast_id).hash(hasher);
157 }
158}
159
160impl<N: AstNode> AstId<N> {
161 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
162 AstId { file_id, file_ast_id }
163 }
164
165 pub fn file_id(&self) -> HirFileId {
166 self.file_id
167 }
168
169 pub fn to_node(&self, db: &impl AstDatabase) -> N {
170 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into());
171 N::cast(syntax_node).unwrap()
172 }
173}
174
175pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
176 let macro_call = id.ast_id.to_node(db);
177 let arg = macro_call.token_tree()?;
178 let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
179 log::warn!("fail on macro_def to token tree: {:#?}", arg);
180 None
181 })?;
182 let rules = MacroRules::parse(&tt).ok().or_else(|| {
183 log::warn!("fail on macro_def parse: {:#?}", tt);
184 None
185 })?;
186 Some(Arc::new(rules))
187}
188
189pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
190 let loc = db.lookup_intern_macro(id);
191 let macro_call = loc.ast_id.to_node(db);
192 let arg = macro_call.token_tree()?;
193 let (tt, _) = mbe::ast_to_token_tree(&arg)?;
194 Some(Arc::new(tt))
195}
196
197pub(crate) fn macro_expand_query(
198 db: &impl AstDatabase,
199 id: MacroCallId,
200) -> Result<Arc<tt::Subtree>, String> {
201 let loc = db.lookup_intern_macro(id);
202 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
203
204 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
205 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
206 // Set a hard limit for the expanded tt
207 let count = tt.count();
208 if count > 65536 {
209 return Err(format!("Total tokens count exceed limit : count = {}", count));
210 }
211 Ok(Arc::new(tt))
212}
213
214pub(crate) fn parse_or_expand_query(
215 db: &impl AstDatabase,
216 file_id: HirFileId,
217) -> Option<SyntaxNode> {
218 match file_id {
219 HirFileId::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
220 HirFileId::MacroFile(macro_file) => db.parse_macro(macro_file).map(|it| it.syntax_node()),
221 }
222}
223
224pub(crate) fn parse_macro_query(
225 db: &impl AstDatabase,
226 macro_file: MacroFile,
227) -> Option<Parse<SyntaxNode>> {
228 let _p = profile("parse_macro_query");
229 let macro_call_id = macro_file.macro_call_id;
230 let tt = db
231 .macro_expand(macro_call_id)
232 .map_err(|err| {
233 // Note:
234 // The final goal we would like to make all parse_macro success,
235 // such that the following log will not call anyway.
236 log::warn!("fail on macro_parse: (reason: {})", err,);
237 })
238 .ok()?;
239 match macro_file.macro_file_kind {
240 MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
241 MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
242 }
243}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 4d4d2cb19..6ccb11068 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -4,4 +4,8 @@
4//! Note that we are in the process of moving parts of `ra_hir` into 4//! Note that we are in the process of moving parts of `ra_hir` into
5//! `ra_hir_def`, so this crates doesn't contain a lot at the moment. 5//! `ra_hir_def`, so this crates doesn't contain a lot at the moment.
6 6
7pub mod db;
8
7pub mod ast_id_map; 9pub mod ast_id_map;
10
11pub mod expand;