diff options
author | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
commit | b21d9337d9200e2cfdc90b386591c72c302dc03e (patch) | |
tree | f81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_hir_expand | |
parent | 18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff) | |
parent | ce07a2daa9e53aa86a769f8641b14c2878444fbc (diff) |
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_hir_expand')
-rw-r--r-- | crates/ra_hir_expand/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/ast_id_map.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_derive.rs | 321 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 244 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 66 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/diagnostics.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/either.rs | 54 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/hygiene.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 217 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 158 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/quote.rs | 40 |
11 files changed, 837 insertions, 289 deletions
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml index c60152a79..3ae4376dc 100644 --- a/crates/ra_hir_expand/Cargo.toml +++ b/crates/ra_hir_expand/Cargo.toml | |||
@@ -9,6 +9,7 @@ doctest = false | |||
9 | 9 | ||
10 | [dependencies] | 10 | [dependencies] |
11 | log = "0.4.5" | 11 | log = "0.4.5" |
12 | either = "1.5" | ||
12 | 13 | ||
13 | ra_arena = { path = "../ra_arena" } | 14 | ra_arena = { path = "../ra_arena" } |
14 | ra_db = { path = "../ra_db" } | 15 | ra_db = { path = "../ra_db" } |
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs index cb464c3ff..a764bdf24 100644 --- a/crates/ra_hir_expand/src/ast_id_map.rs +++ b/crates/ra_hir_expand/src/ast_id_map.rs | |||
@@ -39,6 +39,16 @@ impl<N: AstNode> Hash for FileAstId<N> { | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | impl<N: AstNode> FileAstId<N> { | ||
43 | // Can't make this a From implementation because of coherence | ||
44 | pub fn upcast<M: AstNode>(self) -> FileAstId<M> | ||
45 | where | ||
46 | M: From<N>, | ||
47 | { | ||
48 | FileAstId { raw: self.raw, _ty: PhantomData } | ||
49 | } | ||
50 | } | ||
51 | |||
42 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 52 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
43 | struct ErasedFileAstId(RawId); | 53 | struct ErasedFileAstId(RawId); |
44 | impl_arena_id!(ErasedFileAstId); | 54 | impl_arena_id!(ErasedFileAstId); |
@@ -53,7 +63,7 @@ impl AstIdMap { | |||
53 | pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { | 63 | pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { |
54 | assert!(node.parent().is_none()); | 64 | assert!(node.parent().is_none()); |
55 | let mut res = AstIdMap { arena: Arena::default() }; | 65 | let mut res = AstIdMap { arena: Arena::default() }; |
56 | // By walking the tree in bread-first order we make sure that parents | 66 | // By walking the tree in breadth-first order we make sure that parents |
57 | // get lower ids then children. That is, adding a new child does not | 67 | // get lower ids then children. That is, adding a new child does not |
58 | // change parent's id. This means that, say, adding a new function to a | 68 | // change parent's id. This means that, say, adding a new function to a |
59 | // trait does not change ids of top-level items, which helps caching. | 69 | // trait does not change ids of top-level items, which helps caching. |
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs new file mode 100644 index 000000000..62c60e336 --- /dev/null +++ b/crates/ra_hir_expand/src/builtin_derive.rs | |||
@@ -0,0 +1,321 @@ | |||
1 | //! Builtin derives. | ||
2 | |||
3 | use log::debug; | ||
4 | |||
5 | use ra_parser::FragmentKind; | ||
6 | use ra_syntax::{ | ||
7 | ast::{self, AstNode, ModuleItemOwner, NameOwner, TypeParamsOwner}, | ||
8 | match_ast, | ||
9 | }; | ||
10 | |||
11 | use crate::db::AstDatabase; | ||
12 | use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind}; | ||
13 | |||
14 | macro_rules! register_builtin { | ||
15 | ( $($trait:ident => $expand:ident),* ) => { | ||
16 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
17 | pub enum BuiltinDeriveExpander { | ||
18 | $($trait),* | ||
19 | } | ||
20 | |||
21 | impl BuiltinDeriveExpander { | ||
22 | pub fn expand( | ||
23 | &self, | ||
24 | db: &dyn AstDatabase, | ||
25 | id: MacroCallId, | ||
26 | tt: &tt::Subtree, | ||
27 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
28 | let expander = match *self { | ||
29 | $( BuiltinDeriveExpander::$trait => $expand, )* | ||
30 | }; | ||
31 | expander(db, id, tt) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | pub fn find_builtin_derive(ident: &name::Name) -> Option<MacroDefId> { | ||
36 | let kind = match ident { | ||
37 | $( id if id == &name::name![$trait] => BuiltinDeriveExpander::$trait, )* | ||
38 | _ => return None, | ||
39 | }; | ||
40 | |||
41 | Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind) }) | ||
42 | } | ||
43 | }; | ||
44 | } | ||
45 | |||
46 | register_builtin! { | ||
47 | Copy => copy_expand, | ||
48 | Clone => clone_expand, | ||
49 | Default => default_expand, | ||
50 | Debug => debug_expand, | ||
51 | Hash => hash_expand, | ||
52 | Ord => ord_expand, | ||
53 | PartialOrd => partial_ord_expand, | ||
54 | Eq => eq_expand, | ||
55 | PartialEq => partial_eq_expand | ||
56 | } | ||
57 | |||
58 | struct BasicAdtInfo { | ||
59 | name: tt::Ident, | ||
60 | type_params: usize, | ||
61 | } | ||
62 | |||
63 | fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> { | ||
64 | let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, FragmentKind::Items)?; // FragmentKind::Items doesn't parse attrs? | ||
65 | let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| { | ||
66 | debug!("derive node didn't parse"); | ||
67 | mbe::ExpandError::UnexpectedToken | ||
68 | })?; | ||
69 | let item = macro_items.items().next().ok_or_else(|| { | ||
70 | debug!("no module item parsed"); | ||
71 | mbe::ExpandError::NoMatchingRule | ||
72 | })?; | ||
73 | let node = item.syntax(); | ||
74 | let (name, params) = match_ast! { | ||
75 | match node { | ||
76 | ast::StructDef(it) => { (it.name(), it.type_param_list()) }, | ||
77 | ast::EnumDef(it) => { (it.name(), it.type_param_list()) }, | ||
78 | ast::UnionDef(it) => { (it.name(), it.type_param_list()) }, | ||
79 | _ => { | ||
80 | debug!("unexpected node is {:?}", node); | ||
81 | return Err(mbe::ExpandError::ConversionError) | ||
82 | }, | ||
83 | } | ||
84 | }; | ||
85 | let name = name.ok_or_else(|| { | ||
86 | debug!("parsed item has no name"); | ||
87 | mbe::ExpandError::NoMatchingRule | ||
88 | })?; | ||
89 | let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| { | ||
90 | debug!("name token not found"); | ||
91 | mbe::ExpandError::ConversionError | ||
92 | })?; | ||
93 | let name_token = tt::Ident { id: name_token_id, text: name.text().clone() }; | ||
94 | let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count()); | ||
95 | Ok(BasicAdtInfo { name: name_token, type_params }) | ||
96 | } | ||
97 | |||
98 | fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> { | ||
99 | let mut result = Vec::<tt::TokenTree>::new(); | ||
100 | result.push( | ||
101 | tt::Leaf::Punct(tt::Punct { | ||
102 | char: '<', | ||
103 | spacing: tt::Spacing::Alone, | ||
104 | id: tt::TokenId::unspecified(), | ||
105 | }) | ||
106 | .into(), | ||
107 | ); | ||
108 | for i in 0..n { | ||
109 | if i > 0 { | ||
110 | result.push( | ||
111 | tt::Leaf::Punct(tt::Punct { | ||
112 | char: ',', | ||
113 | spacing: tt::Spacing::Alone, | ||
114 | id: tt::TokenId::unspecified(), | ||
115 | }) | ||
116 | .into(), | ||
117 | ); | ||
118 | } | ||
119 | result.push( | ||
120 | tt::Leaf::Ident(tt::Ident { | ||
121 | id: tt::TokenId::unspecified(), | ||
122 | text: format!("T{}", i).into(), | ||
123 | }) | ||
124 | .into(), | ||
125 | ); | ||
126 | result.extend(bound.iter().cloned()); | ||
127 | } | ||
128 | result.push( | ||
129 | tt::Leaf::Punct(tt::Punct { | ||
130 | char: '>', | ||
131 | spacing: tt::Spacing::Alone, | ||
132 | id: tt::TokenId::unspecified(), | ||
133 | }) | ||
134 | .into(), | ||
135 | ); | ||
136 | result | ||
137 | } | ||
138 | |||
139 | fn expand_simple_derive( | ||
140 | tt: &tt::Subtree, | ||
141 | trait_path: tt::Subtree, | ||
142 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
143 | let info = parse_adt(tt)?; | ||
144 | let name = info.name; | ||
145 | let trait_path_clone = trait_path.token_trees.clone(); | ||
146 | let bound = (quote! { : ##trait_path_clone }).token_trees; | ||
147 | let type_params = make_type_args(info.type_params, bound); | ||
148 | let type_args = make_type_args(info.type_params, Vec::new()); | ||
149 | let trait_path = trait_path.token_trees; | ||
150 | let expanded = quote! { | ||
151 | impl ##type_params ##trait_path for #name ##type_args {} | ||
152 | }; | ||
153 | Ok(expanded) | ||
154 | } | ||
155 | |||
156 | fn copy_expand( | ||
157 | _db: &dyn AstDatabase, | ||
158 | _id: MacroCallId, | ||
159 | tt: &tt::Subtree, | ||
160 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
161 | expand_simple_derive(tt, quote! { std::marker::Copy }) | ||
162 | } | ||
163 | |||
164 | fn clone_expand( | ||
165 | _db: &dyn AstDatabase, | ||
166 | _id: MacroCallId, | ||
167 | tt: &tt::Subtree, | ||
168 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
169 | expand_simple_derive(tt, quote! { std::clone::Clone }) | ||
170 | } | ||
171 | |||
172 | fn default_expand( | ||
173 | _db: &dyn AstDatabase, | ||
174 | _id: MacroCallId, | ||
175 | tt: &tt::Subtree, | ||
176 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
177 | expand_simple_derive(tt, quote! { std::default::Default }) | ||
178 | } | ||
179 | |||
180 | fn debug_expand( | ||
181 | _db: &dyn AstDatabase, | ||
182 | _id: MacroCallId, | ||
183 | tt: &tt::Subtree, | ||
184 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
185 | expand_simple_derive(tt, quote! { std::fmt::Debug }) | ||
186 | } | ||
187 | |||
188 | fn hash_expand( | ||
189 | _db: &dyn AstDatabase, | ||
190 | _id: MacroCallId, | ||
191 | tt: &tt::Subtree, | ||
192 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
193 | expand_simple_derive(tt, quote! { std::hash::Hash }) | ||
194 | } | ||
195 | |||
196 | fn eq_expand( | ||
197 | _db: &dyn AstDatabase, | ||
198 | _id: MacroCallId, | ||
199 | tt: &tt::Subtree, | ||
200 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
201 | expand_simple_derive(tt, quote! { std::cmp::Eq }) | ||
202 | } | ||
203 | |||
204 | fn partial_eq_expand( | ||
205 | _db: &dyn AstDatabase, | ||
206 | _id: MacroCallId, | ||
207 | tt: &tt::Subtree, | ||
208 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
209 | expand_simple_derive(tt, quote! { std::cmp::PartialEq }) | ||
210 | } | ||
211 | |||
212 | fn ord_expand( | ||
213 | _db: &dyn AstDatabase, | ||
214 | _id: MacroCallId, | ||
215 | tt: &tt::Subtree, | ||
216 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
217 | expand_simple_derive(tt, quote! { std::cmp::Ord }) | ||
218 | } | ||
219 | |||
220 | fn partial_ord_expand( | ||
221 | _db: &dyn AstDatabase, | ||
222 | _id: MacroCallId, | ||
223 | tt: &tt::Subtree, | ||
224 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
225 | expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) | ||
226 | } | ||
227 | |||
228 | #[cfg(test)] | ||
229 | mod tests { | ||
230 | use super::*; | ||
231 | use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc}; | ||
232 | use ra_db::{fixture::WithFixture, SourceDatabase}; | ||
233 | |||
234 | fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { | ||
235 | let (db, file_id) = TestDB::with_single_file(&s); | ||
236 | let parsed = db.parse(file_id); | ||
237 | let items: Vec<_> = | ||
238 | parsed.syntax_node().descendants().filter_map(|it| ast::ModuleItem::cast(it)).collect(); | ||
239 | |||
240 | let ast_id_map = db.ast_id_map(file_id.into()); | ||
241 | |||
242 | // the first one should be a macro_rules | ||
243 | let def = | ||
244 | MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) }; | ||
245 | |||
246 | let loc = MacroCallLoc { | ||
247 | def, | ||
248 | kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))), | ||
249 | }; | ||
250 | |||
251 | let id = db.intern_macro(loc); | ||
252 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); | ||
253 | |||
254 | // FIXME text() for syntax nodes parsed from token tree looks weird | ||
255 | // because there's no whitespace, see below | ||
256 | parsed.text().to_string() | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn test_copy_expand_simple() { | ||
261 | let expanded = expand_builtin_derive( | ||
262 | r#" | ||
263 | #[derive(Copy)] | ||
264 | struct Foo; | ||
265 | "#, | ||
266 | BuiltinDeriveExpander::Copy, | ||
267 | ); | ||
268 | |||
269 | assert_eq!(expanded, "impl <>std::marker::CopyforFoo <>{}"); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn test_copy_expand_with_type_params() { | ||
274 | let expanded = expand_builtin_derive( | ||
275 | r#" | ||
276 | #[derive(Copy)] | ||
277 | struct Foo<A, B>; | ||
278 | "#, | ||
279 | BuiltinDeriveExpander::Copy, | ||
280 | ); | ||
281 | |||
282 | assert_eq!( | ||
283 | expanded, | ||
284 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | ||
285 | ); | ||
286 | } | ||
287 | |||
288 | #[test] | ||
289 | fn test_copy_expand_with_lifetimes() { | ||
290 | let expanded = expand_builtin_derive( | ||
291 | r#" | ||
292 | #[derive(Copy)] | ||
293 | struct Foo<A, B, 'a, 'b>; | ||
294 | "#, | ||
295 | BuiltinDeriveExpander::Copy, | ||
296 | ); | ||
297 | |||
298 | // We currently just ignore lifetimes | ||
299 | |||
300 | assert_eq!( | ||
301 | expanded, | ||
302 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn test_clone_expand() { | ||
308 | let expanded = expand_builtin_derive( | ||
309 | r#" | ||
310 | #[derive(Clone)] | ||
311 | struct Foo<A, B>; | ||
312 | "#, | ||
313 | BuiltinDeriveExpander::Clone, | ||
314 | ); | ||
315 | |||
316 | assert_eq!( | ||
317 | expanded, | ||
318 | "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}" | ||
319 | ); | ||
320 | } | ||
321 | } | ||
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index d370dfb34..2c119269c 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -2,8 +2,7 @@ | |||
2 | use crate::db::AstDatabase; | 2 | use crate::db::AstDatabase; |
3 | use crate::{ | 3 | use crate::{ |
4 | ast::{self, AstNode}, | 4 | ast::{self, AstNode}, |
5 | name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind, | 5 | name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, TextUnit, |
6 | TextUnit, | ||
7 | }; | 6 | }; |
8 | 7 | ||
9 | use crate::quote; | 8 | use crate::quote; |
@@ -27,6 +26,13 @@ macro_rules! register_builtin { | |||
27 | }; | 26 | }; |
28 | expander(db, id, tt) | 27 | expander(db, id, tt) |
29 | } | 28 | } |
29 | |||
30 | fn by_name(ident: &name::Name) -> Option<BuiltinFnLikeExpander> { | ||
31 | match ident { | ||
32 | $( id if id == &name::name![$name] => Some(BuiltinFnLikeExpander::$kind), )* | ||
33 | _ => return None, | ||
34 | } | ||
35 | } | ||
30 | } | 36 | } |
31 | 37 | ||
32 | pub fn find_builtin_macro( | 38 | pub fn find_builtin_macro( |
@@ -34,22 +40,25 @@ macro_rules! register_builtin { | |||
34 | krate: CrateId, | 40 | krate: CrateId, |
35 | ast_id: AstId<ast::MacroCall>, | 41 | ast_id: AstId<ast::MacroCall>, |
36 | ) -> Option<MacroDefId> { | 42 | ) -> Option<MacroDefId> { |
37 | let kind = match ident { | 43 | let kind = BuiltinFnLikeExpander::by_name(ident)?; |
38 | $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )* | ||
39 | _ => return None, | ||
40 | }; | ||
41 | 44 | ||
42 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) | 45 | Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) }) |
43 | } | 46 | } |
44 | }; | 47 | }; |
45 | } | 48 | } |
46 | 49 | ||
47 | register_builtin! { | 50 | register_builtin! { |
48 | (COLUMN_MACRO, Column) => column_expand, | 51 | (column, Column) => column_expand, |
49 | (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand, | 52 | (compile_error, CompileError) => compile_error_expand, |
50 | (FILE_MACRO, File) => file_expand, | 53 | (file, File) => file_expand, |
51 | (LINE_MACRO, Line) => line_expand, | 54 | (line, Line) => line_expand, |
52 | (STRINGIFY_MACRO, Stringify) => stringify_expand | 55 | (stringify, Stringify) => stringify_expand, |
56 | (format_args, FormatArgs) => format_args_expand, | ||
57 | (env, Env) => env_expand, | ||
58 | (option_env, OptionEnv) => option_env_expand, | ||
59 | // format_args_nl only differs in that it adds a newline in the end, | ||
60 | // so we use the same stub expansion for now | ||
61 | (format_args_nl, FormatArgsNl) => format_args_expand | ||
53 | } | 62 | } |
54 | 63 | ||
55 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 64 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
@@ -82,12 +91,11 @@ fn line_expand( | |||
82 | _tt: &tt::Subtree, | 91 | _tt: &tt::Subtree, |
83 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 92 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
84 | let loc = db.lookup_intern_macro(id); | 93 | let loc = db.lookup_intern_macro(id); |
85 | let macro_call = loc.ast_id.to_node(db); | ||
86 | 94 | ||
87 | let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 95 | let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
88 | let arg_start = arg.syntax().text_range().start(); | 96 | let arg_start = arg.text_range().start(); |
89 | 97 | ||
90 | let file = id.as_file(MacroFileKind::Expr); | 98 | let file = id.as_file(); |
91 | let line_num = to_line_number(db, file, arg_start); | 99 | let line_num = to_line_number(db, file, arg_start); |
92 | 100 | ||
93 | let expanded = quote! { | 101 | let expanded = quote! { |
@@ -103,11 +111,10 @@ fn stringify_expand( | |||
103 | _tt: &tt::Subtree, | 111 | _tt: &tt::Subtree, |
104 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 112 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
105 | let loc = db.lookup_intern_macro(id); | 113 | let loc = db.lookup_intern_macro(id); |
106 | let macro_call = loc.ast_id.to_node(db); | ||
107 | 114 | ||
108 | let macro_content = { | 115 | let macro_content = { |
109 | let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 116 | let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
110 | let macro_args = arg.syntax().clone(); | 117 | let macro_args = arg; |
111 | let text = macro_args.text(); | 118 | let text = macro_args.text(); |
112 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); | 119 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); |
113 | text.slice(without_parens).to_string() | 120 | text.slice(without_parens).to_string() |
@@ -120,6 +127,28 @@ fn stringify_expand( | |||
120 | Ok(expanded) | 127 | Ok(expanded) |
121 | } | 128 | } |
122 | 129 | ||
130 | fn env_expand( | ||
131 | _db: &dyn AstDatabase, | ||
132 | _id: MacroCallId, | ||
133 | _tt: &tt::Subtree, | ||
134 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
135 | // dummy implementation for type-checking purposes | ||
136 | let expanded = quote! { "" }; | ||
137 | |||
138 | Ok(expanded) | ||
139 | } | ||
140 | |||
141 | fn option_env_expand( | ||
142 | _db: &dyn AstDatabase, | ||
143 | _id: MacroCallId, | ||
144 | _tt: &tt::Subtree, | ||
145 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
146 | // dummy implementation for type-checking purposes | ||
147 | let expanded = quote! { std::option::Option::None::<&str> }; | ||
148 | |||
149 | Ok(expanded) | ||
150 | } | ||
151 | |||
123 | fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 152 | fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
124 | // FIXME: Use expansion info | 153 | // FIXME: Use expansion info |
125 | let file_id = file.original_file(db); | 154 | let file_id = file.original_file(db); |
@@ -137,7 +166,7 @@ fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize | |||
137 | if c == '\n' { | 166 | if c == '\n' { |
138 | break; | 167 | break; |
139 | } | 168 | } |
140 | col_num = col_num + 1; | 169 | col_num += 1; |
141 | } | 170 | } |
142 | col_num | 171 | col_num |
143 | } | 172 | } |
@@ -148,12 +177,15 @@ fn column_expand( | |||
148 | _tt: &tt::Subtree, | 177 | _tt: &tt::Subtree, |
149 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 178 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
150 | let loc = db.lookup_intern_macro(id); | 179 | let loc = db.lookup_intern_macro(id); |
151 | let macro_call = loc.ast_id.to_node(db); | 180 | let macro_call = match loc.kind { |
181 | crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db), | ||
182 | _ => panic!("column macro called as attr"), | ||
183 | }; | ||
152 | 184 | ||
153 | let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 185 | let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
154 | let col_start = macro_call.syntax().text_range().start(); | 186 | let col_start = macro_call.syntax().text_range().start(); |
155 | 187 | ||
156 | let file = id.as_file(MacroFileKind::Expr); | 188 | let file = id.as_file(); |
157 | let col_num = to_col_number(db, file, col_start); | 189 | let col_num = to_col_number(db, file, col_start); |
158 | 190 | ||
159 | let expanded = quote! { | 191 | let expanded = quote! { |
@@ -164,15 +196,10 @@ fn column_expand( | |||
164 | } | 196 | } |
165 | 197 | ||
166 | fn file_expand( | 198 | fn file_expand( |
167 | db: &dyn AstDatabase, | 199 | _db: &dyn AstDatabase, |
168 | id: MacroCallId, | 200 | _id: MacroCallId, |
169 | _tt: &tt::Subtree, | 201 | _tt: &tt::Subtree, |
170 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 202 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
171 | let loc = db.lookup_intern_macro(id); | ||
172 | let macro_call = loc.ast_id.to_node(db); | ||
173 | |||
174 | let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | ||
175 | |||
176 | // FIXME: RA purposefully lacks knowledge of absolute file names | 203 | // FIXME: RA purposefully lacks knowledge of absolute file names |
177 | // so just return "". | 204 | // so just return "". |
178 | let file_name = ""; | 205 | let file_name = ""; |
@@ -204,13 +231,56 @@ fn compile_error_expand( | |||
204 | Err(mbe::ExpandError::BindingError("Must be a string".into())) | 231 | Err(mbe::ExpandError::BindingError("Must be a string".into())) |
205 | } | 232 | } |
206 | 233 | ||
234 | fn format_args_expand( | ||
235 | _db: &dyn AstDatabase, | ||
236 | _id: MacroCallId, | ||
237 | tt: &tt::Subtree, | ||
238 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
239 | // We expand `format_args!("", a1, a2)` to | ||
240 | // ``` | ||
241 | // std::fmt::Arguments::new_v1(&[], &[ | ||
242 | // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt), | ||
243 | // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt), | ||
244 | // ]) | ||
245 | // ```, | ||
246 | // which is still not really correct, but close enough for now | ||
247 | let mut args = Vec::new(); | ||
248 | let mut current = Vec::new(); | ||
249 | for tt in tt.token_trees.iter().cloned() { | ||
250 | match tt { | ||
251 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
252 | args.push(current); | ||
253 | current = Vec::new(); | ||
254 | } | ||
255 | _ => { | ||
256 | current.push(tt); | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | if !current.is_empty() { | ||
261 | args.push(current); | ||
262 | } | ||
263 | if args.is_empty() { | ||
264 | return Err(mbe::ExpandError::NoMatchingRule); | ||
265 | } | ||
266 | let _format_string = args.remove(0); | ||
267 | let arg_tts = args.into_iter().flat_map(|arg| { | ||
268 | quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), } | ||
269 | }.token_trees).collect::<Vec<_>>(); | ||
270 | let expanded = quote! { | ||
271 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) | ||
272 | }; | ||
273 | Ok(expanded) | ||
274 | } | ||
275 | |||
207 | #[cfg(test)] | 276 | #[cfg(test)] |
208 | mod tests { | 277 | mod tests { |
209 | use super::*; | 278 | use super::*; |
210 | use crate::{test_db::TestDB, MacroCallLoc}; | 279 | use crate::{name::AsName, test_db::TestDB, MacroCallKind, MacroCallLoc}; |
211 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 280 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
281 | use ra_syntax::ast::NameOwner; | ||
212 | 282 | ||
213 | fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { | 283 | fn expand_builtin_macro(s: &str) -> String { |
214 | let (db, file_id) = TestDB::with_single_file(&s); | 284 | let (db, file_id) = TestDB::with_single_file(&s); |
215 | let parsed = db.parse(file_id); | 285 | let parsed = db.parse(file_id); |
216 | let macro_calls: Vec<_> = | 286 | let macro_calls: Vec<_> = |
@@ -218,20 +288,26 @@ mod tests { | |||
218 | 288 | ||
219 | let ast_id_map = db.ast_id_map(file_id.into()); | 289 | let ast_id_map = db.ast_id_map(file_id.into()); |
220 | 290 | ||
291 | let expander = | ||
292 | BuiltinFnLikeExpander::by_name(¯o_calls[0].name().unwrap().as_name()).unwrap(); | ||
293 | |||
221 | // the first one should be a macro_rules | 294 | // the first one should be a macro_rules |
222 | let def = MacroDefId { | 295 | let def = MacroDefId { |
223 | krate: CrateId(0), | 296 | krate: Some(CrateId(0)), |
224 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])), | 297 | ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))), |
225 | kind: MacroDefKind::BuiltIn(expander), | 298 | kind: MacroDefKind::BuiltIn(expander), |
226 | }; | 299 | }; |
227 | 300 | ||
228 | let loc = MacroCallLoc { | 301 | let loc = MacroCallLoc { |
229 | def, | 302 | def, |
230 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])), | 303 | kind: MacroCallKind::FnLike(AstId::new( |
304 | file_id.into(), | ||
305 | ast_id_map.ast_id(¯o_calls[1]), | ||
306 | )), | ||
231 | }; | 307 | }; |
232 | 308 | ||
233 | let id = db.intern_macro(loc); | 309 | let id = db.intern_macro(loc); |
234 | let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap(); | 310 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); |
235 | 311 | ||
236 | parsed.text().to_string() | 312 | parsed.text().to_string() |
237 | } | 313 | } |
@@ -240,25 +316,23 @@ mod tests { | |||
240 | fn test_column_expand() { | 316 | fn test_column_expand() { |
241 | let expanded = expand_builtin_macro( | 317 | let expanded = expand_builtin_macro( |
242 | r#" | 318 | r#" |
243 | #[rustc_builtin_macro] | 319 | #[rustc_builtin_macro] |
244 | macro_rules! column {() => {}} | 320 | macro_rules! column {() => {}} |
245 | column!() | 321 | column!() |
246 | "#, | 322 | "#, |
247 | BuiltinFnLikeExpander::Column, | ||
248 | ); | 323 | ); |
249 | 324 | ||
250 | assert_eq!(expanded, "9"); | 325 | assert_eq!(expanded, "13"); |
251 | } | 326 | } |
252 | 327 | ||
253 | #[test] | 328 | #[test] |
254 | fn test_line_expand() { | 329 | fn test_line_expand() { |
255 | let expanded = expand_builtin_macro( | 330 | let expanded = expand_builtin_macro( |
256 | r#" | 331 | r#" |
257 | #[rustc_builtin_macro] | 332 | #[rustc_builtin_macro] |
258 | macro_rules! line {() => {}} | 333 | macro_rules! line {() => {}} |
259 | line!() | 334 | line!() |
260 | "#, | 335 | "#, |
261 | BuiltinFnLikeExpander::Line, | ||
262 | ); | 336 | ); |
263 | 337 | ||
264 | assert_eq!(expanded, "4"); | 338 | assert_eq!(expanded, "4"); |
@@ -268,25 +342,49 @@ mod tests { | |||
268 | fn test_stringify_expand() { | 342 | fn test_stringify_expand() { |
269 | let expanded = expand_builtin_macro( | 343 | let expanded = expand_builtin_macro( |
270 | r#" | 344 | r#" |
271 | #[rustc_builtin_macro] | 345 | #[rustc_builtin_macro] |
272 | macro_rules! stringify {() => {}} | 346 | macro_rules! stringify {() => {}} |
273 | stringify!(a b c) | 347 | stringify!(a b c) |
274 | "#, | 348 | "#, |
275 | BuiltinFnLikeExpander::Stringify, | ||
276 | ); | 349 | ); |
277 | 350 | ||
278 | assert_eq!(expanded, "\"a b c\""); | 351 | assert_eq!(expanded, "\"a b c\""); |
279 | } | 352 | } |
280 | 353 | ||
281 | #[test] | 354 | #[test] |
355 | fn test_env_expand() { | ||
356 | let expanded = expand_builtin_macro( | ||
357 | r#" | ||
358 | #[rustc_builtin_macro] | ||
359 | macro_rules! env {() => {}} | ||
360 | env!("TEST_ENV_VAR") | ||
361 | "#, | ||
362 | ); | ||
363 | |||
364 | assert_eq!(expanded, "\"\""); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn test_option_env_expand() { | ||
369 | let expanded = expand_builtin_macro( | ||
370 | r#" | ||
371 | #[rustc_builtin_macro] | ||
372 | macro_rules! option_env {() => {}} | ||
373 | option_env!("TEST_ENV_VAR") | ||
374 | "#, | ||
375 | ); | ||
376 | |||
377 | assert_eq!(expanded, "std::option::Option::None:: <&str>"); | ||
378 | } | ||
379 | |||
380 | #[test] | ||
282 | fn test_file_expand() { | 381 | fn test_file_expand() { |
283 | let expanded = expand_builtin_macro( | 382 | let expanded = expand_builtin_macro( |
284 | r#" | 383 | r#" |
285 | #[rustc_builtin_macro] | 384 | #[rustc_builtin_macro] |
286 | macro_rules! file {() => {}} | 385 | macro_rules! file {() => {}} |
287 | file!() | 386 | file!() |
288 | "#, | 387 | "#, |
289 | BuiltinFnLikeExpander::File, | ||
290 | ); | 388 | ); |
291 | 389 | ||
292 | assert_eq!(expanded, "\"\""); | 390 | assert_eq!(expanded, "\"\""); |
@@ -296,16 +394,34 @@ mod tests { | |||
296 | fn test_compile_error_expand() { | 394 | fn test_compile_error_expand() { |
297 | let expanded = expand_builtin_macro( | 395 | let expanded = expand_builtin_macro( |
298 | r#" | 396 | r#" |
299 | #[rustc_builtin_macro] | 397 | #[rustc_builtin_macro] |
300 | macro_rules! compile_error { | 398 | macro_rules! compile_error { |
301 | ($msg:expr) => ({ /* compiler built-in */ }); | 399 | ($msg:expr) => ({ /* compiler built-in */ }); |
302 | ($msg:expr,) => ({ /* compiler built-in */ }) | 400 | ($msg:expr,) => ({ /* compiler built-in */ }) |
303 | } | 401 | } |
304 | compile_error!("error!"); | 402 | compile_error!("error!"); |
305 | "#, | 403 | "#, |
306 | BuiltinFnLikeExpander::CompileError, | ||
307 | ); | 404 | ); |
308 | 405 | ||
309 | assert_eq!(expanded, r#"loop{"error!"}"#); | 406 | assert_eq!(expanded, r#"loop{"error!"}"#); |
310 | } | 407 | } |
408 | |||
409 | #[test] | ||
410 | fn test_format_args_expand() { | ||
411 | let expanded = expand_builtin_macro( | ||
412 | r#" | ||
413 | #[rustc_builtin_macro] | ||
414 | macro_rules! format_args { | ||
415 | ($fmt:expr) => ({ /* compiler built-in */ }); | ||
416 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) | ||
417 | } | ||
418 | format_args!("{} {:?}", arg1(a, b, c), arg2); | ||
419 | "#, | ||
420 | ); | ||
421 | |||
422 | assert_eq!( | ||
423 | expanded, | ||
424 | r#"std::fmt::Arguments::new_v1(&[] ,&[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"# | ||
425 | ); | ||
426 | } | ||
311 | } | 427 | } |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 8e46fa177..2e12e126f 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -6,17 +6,18 @@ use mbe::MacroRules; | |||
6 | use ra_db::{salsa, SourceDatabase}; | 6 | use ra_db::{salsa, SourceDatabase}; |
7 | use ra_parser::FragmentKind; | 7 | use ra_parser::FragmentKind; |
8 | use ra_prof::profile; | 8 | use ra_prof::profile; |
9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; | 9 | use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, | 12 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, |
13 | MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind, | 13 | MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #[derive(Debug, Clone, Eq, PartialEq)] | 16 | #[derive(Debug, Clone, Eq, PartialEq)] |
17 | pub enum TokenExpander { | 17 | pub enum TokenExpander { |
18 | MacroRules(mbe::MacroRules), | 18 | MacroRules(mbe::MacroRules), |
19 | Builtin(BuiltinFnLikeExpander), | 19 | Builtin(BuiltinFnLikeExpander), |
20 | BuiltinDerive(BuiltinDeriveExpander), | ||
20 | } | 21 | } |
21 | 22 | ||
22 | impl TokenExpander { | 23 | impl TokenExpander { |
@@ -29,6 +30,7 @@ impl TokenExpander { | |||
29 | match self { | 30 | match self { |
30 | TokenExpander::MacroRules(it) => it.expand(tt), | 31 | TokenExpander::MacroRules(it) => it.expand(tt), |
31 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | 32 | TokenExpander::Builtin(it) => it.expand(db, id, tt), |
33 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt), | ||
32 | } | 34 | } |
33 | } | 35 | } |
34 | 36 | ||
@@ -36,13 +38,15 @@ impl TokenExpander { | |||
36 | match self { | 38 | match self { |
37 | TokenExpander::MacroRules(it) => it.map_id_down(id), | 39 | TokenExpander::MacroRules(it) => it.map_id_down(id), |
38 | TokenExpander::Builtin(..) => id, | 40 | TokenExpander::Builtin(..) => id, |
41 | TokenExpander::BuiltinDerive(..) => id, | ||
39 | } | 42 | } |
40 | } | 43 | } |
41 | 44 | ||
42 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { | 45 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { |
43 | match self { | 46 | match self { |
44 | TokenExpander::MacroRules(it) => it.map_id_up(id), | 47 | TokenExpander::MacroRules(it) => it.map_id_up(id), |
45 | TokenExpander::Builtin(..) => (id, mbe::Origin::Def), | 48 | TokenExpander::Builtin(..) => (id, mbe::Origin::Call), |
49 | TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), | ||
46 | } | 50 | } |
47 | } | 51 | } |
48 | } | 52 | } |
@@ -76,7 +80,7 @@ pub(crate) fn macro_def( | |||
76 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 80 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
77 | match id.kind { | 81 | match id.kind { |
78 | MacroDefKind::Declarative => { | 82 | MacroDefKind::Declarative => { |
79 | let macro_call = id.ast_id.to_node(db); | 83 | let macro_call = id.ast_id?.to_node(db); |
80 | let arg = macro_call.token_tree()?; | 84 | let arg = macro_call.token_tree()?; |
81 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { | 85 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { |
82 | log::warn!("fail on macro_def to token tree: {:#?}", arg); | 86 | log::warn!("fail on macro_def to token tree: {:#?}", arg); |
@@ -89,7 +93,10 @@ pub(crate) fn macro_def( | |||
89 | Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) | 93 | Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) |
90 | } | 94 | } |
91 | MacroDefKind::BuiltIn(expander) => { | 95 | MacroDefKind::BuiltIn(expander) => { |
92 | Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default()))) | 96 | Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) |
97 | } | ||
98 | MacroDefKind::BuiltInDerive(expander) => { | ||
99 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) | ||
93 | } | 100 | } |
94 | } | 101 | } |
95 | } | 102 | } |
@@ -99,9 +106,8 @@ pub(crate) fn macro_arg( | |||
99 | id: MacroCallId, | 106 | id: MacroCallId, |
100 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | 107 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { |
101 | let loc = db.lookup_intern_macro(id); | 108 | let loc = db.lookup_intern_macro(id); |
102 | let macro_call = loc.ast_id.to_node(db); | 109 | let arg = loc.kind.arg(db)?; |
103 | let arg = macro_call.token_tree()?; | 110 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?; |
104 | let (tt, tmap) = mbe::ast_to_token_tree(&arg)?; | ||
105 | Some(Arc::new((tt, tmap))) | 111 | Some(Arc::new((tt, tmap))) |
106 | } | 112 | } |
107 | 113 | ||
@@ -148,11 +154,43 @@ pub(crate) fn parse_macro( | |||
148 | }) | 154 | }) |
149 | .ok()?; | 155 | .ok()?; |
150 | 156 | ||
151 | let fragment_kind = match macro_file.macro_file_kind { | 157 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
152 | MacroFileKind::Items => FragmentKind::Items, | 158 | |
153 | MacroFileKind::Expr => FragmentKind::Expr, | ||
154 | MacroFileKind::Statements => FragmentKind::Statements, | ||
155 | }; | ||
156 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 159 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; |
157 | Some((parse, Arc::new(rev_token_map))) | 160 | Some((parse, Arc::new(rev_token_map))) |
158 | } | 161 | } |
162 | |||
163 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | ||
164 | /// FIXME: Not completed | ||
165 | fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> FragmentKind { | ||
166 | let syn = db.lookup_intern_macro(macro_call_id).kind.node(db).value; | ||
167 | |||
168 | let parent = match syn.parent() { | ||
169 | Some(it) => it, | ||
170 | None => { | ||
171 | // FIXME: | ||
172 | // If it is root, which means the parent HirFile | ||
173 | // MacroKindFile must be non-items | ||
174 | // return expr now. | ||
175 | return FragmentKind::Expr; | ||
176 | } | ||
177 | }; | ||
178 | |||
179 | match parent.kind() { | ||
180 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
181 | LET_STMT => { | ||
182 | // FIXME: Handle Pattern | ||
183 | FragmentKind::Expr | ||
184 | } | ||
185 | // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that | ||
186 | EXPR_STMT | BLOCK => FragmentKind::Expr, | ||
187 | ARG_LIST => FragmentKind::Expr, | ||
188 | TRY_EXPR => FragmentKind::Expr, | ||
189 | TUPLE_EXPR => FragmentKind::Expr, | ||
190 | ITEM_LIST => FragmentKind::Items, | ||
191 | _ => { | ||
192 | // Unknown , Just guess it is `Items` | ||
193 | FragmentKind::Items | ||
194 | } | ||
195 | } | ||
196 | } | ||
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 3d37e9335..108c1e38c 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs | |||
@@ -18,11 +18,11 @@ use std::{any::Any, fmt}; | |||
18 | 18 | ||
19 | use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; | 19 | use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; |
20 | 20 | ||
21 | use crate::{db::AstDatabase, Source}; | 21 | use crate::{db::AstDatabase, InFile}; |
22 | 22 | ||
23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { |
24 | fn message(&self) -> String; | 24 | fn message(&self) -> String; |
25 | fn source(&self) -> Source<SyntaxNodePtr>; | 25 | fn source(&self) -> InFile<SyntaxNodePtr>; |
26 | fn highlight_range(&self) -> TextRange { | 26 | fn highlight_range(&self) -> TextRange { |
27 | self.source().value.range() | 27 | self.source().value.range() |
28 | } | 28 | } |
diff --git a/crates/ra_hir_expand/src/either.rs b/crates/ra_hir_expand/src/either.rs deleted file mode 100644 index 83583ef8b..000000000 --- a/crates/ra_hir_expand/src/either.rs +++ /dev/null | |||
@@ -1,54 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
4 | pub enum Either<A, B> { | ||
5 | A(A), | ||
6 | B(B), | ||
7 | } | ||
8 | |||
9 | impl<A, B> Either<A, B> { | ||
10 | pub fn either<R, F1, F2>(self, f1: F1, f2: F2) -> R | ||
11 | where | ||
12 | F1: FnOnce(A) -> R, | ||
13 | F2: FnOnce(B) -> R, | ||
14 | { | ||
15 | match self { | ||
16 | Either::A(a) => f1(a), | ||
17 | Either::B(b) => f2(b), | ||
18 | } | ||
19 | } | ||
20 | pub fn map<U, V, F1, F2>(self, f1: F1, f2: F2) -> Either<U, V> | ||
21 | where | ||
22 | F1: FnOnce(A) -> U, | ||
23 | F2: FnOnce(B) -> V, | ||
24 | { | ||
25 | match self { | ||
26 | Either::A(a) => Either::A(f1(a)), | ||
27 | Either::B(b) => Either::B(f2(b)), | ||
28 | } | ||
29 | } | ||
30 | pub fn map_a<U, F>(self, f: F) -> Either<U, B> | ||
31 | where | ||
32 | F: FnOnce(A) -> U, | ||
33 | { | ||
34 | self.map(f, |it| it) | ||
35 | } | ||
36 | pub fn a(self) -> Option<A> { | ||
37 | match self { | ||
38 | Either::A(it) => Some(it), | ||
39 | Either::B(_) => None, | ||
40 | } | ||
41 | } | ||
42 | pub fn b(self) -> Option<B> { | ||
43 | match self { | ||
44 | Either::A(_) => None, | ||
45 | Either::B(it) => Some(it), | ||
46 | } | ||
47 | } | ||
48 | pub fn as_ref(&self) -> Either<&A, &B> { | ||
49 | match self { | ||
50 | Either::A(it) => Either::A(it), | ||
51 | Either::B(it) => Either::B(it), | ||
52 | } | ||
53 | } | ||
54 | } | ||
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index 379562a2c..2e8a533f7 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs | |||
@@ -2,12 +2,12 @@ | |||
2 | //! | 2 | //! |
3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at | 3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at |
4 | //! this moment, this is horribly incomplete and handles only `$crate`. | 4 | //! this moment, this is horribly incomplete and handles only `$crate`. |
5 | use either::Either; | ||
5 | use ra_db::CrateId; | 6 | use ra_db::CrateId; |
6 | use ra_syntax::ast; | 7 | use ra_syntax::ast; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
9 | db::AstDatabase, | 10 | db::AstDatabase, |
10 | either::Either, | ||
11 | name::{AsName, Name}, | 11 | name::{AsName, Name}, |
12 | HirFileId, HirFileIdRepr, MacroDefKind, | 12 | HirFileId, HirFileIdRepr, MacroDefKind, |
13 | }; | 13 | }; |
@@ -25,8 +25,9 @@ impl Hygiene { | |||
25 | HirFileIdRepr::MacroFile(macro_file) => { | 25 | HirFileIdRepr::MacroFile(macro_file) => { |
26 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | 26 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); |
27 | match loc.def.kind { | 27 | match loc.def.kind { |
28 | MacroDefKind::Declarative => Some(loc.def.krate), | 28 | MacroDefKind::Declarative => loc.def.krate, |
29 | MacroDefKind::BuiltIn(_) => None, | 29 | MacroDefKind::BuiltIn(_) => None, |
30 | MacroDefKind::BuiltInDerive(_) => None, | ||
30 | } | 31 | } |
31 | } | 32 | } |
32 | }; | 33 | }; |
@@ -41,9 +42,9 @@ impl Hygiene { | |||
41 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 42 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { |
42 | if let Some(def_crate) = self.def_crate { | 43 | if let Some(def_crate) = self.def_crate { |
43 | if name_ref.text() == "$crate" { | 44 | if name_ref.text() == "$crate" { |
44 | return Either::B(def_crate); | 45 | return Either::Right(def_crate); |
45 | } | 46 | } |
46 | } | 47 | } |
47 | Either::A(name_ref.as_name()) | 48 | Either::Left(name_ref.as_name()) |
48 | } | 49 | } |
49 | } | 50 | } |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index b6a739cda..2fa5d5140 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -6,14 +6,14 @@ | |||
6 | 6 | ||
7 | pub mod db; | 7 | pub mod db; |
8 | pub mod ast_id_map; | 8 | pub mod ast_id_map; |
9 | pub mod either; | ||
10 | pub mod name; | 9 | pub mod name; |
11 | pub mod hygiene; | 10 | pub mod hygiene; |
12 | pub mod diagnostics; | 11 | pub mod diagnostics; |
12 | pub mod builtin_derive; | ||
13 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
14 | pub mod quote; | 14 | pub mod quote; |
15 | 15 | ||
16 | use std::hash::{Hash, Hasher}; | 16 | use std::hash::Hash; |
17 | use std::sync::Arc; | 17 | use std::sync::Arc; |
18 | 18 | ||
19 | use ra_db::{salsa, CrateId, FileId}; | 19 | use ra_db::{salsa, CrateId, FileId}; |
@@ -24,6 +24,7 @@ use ra_syntax::{ | |||
24 | }; | 24 | }; |
25 | 25 | ||
26 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_derive::BuiltinDeriveExpander; | ||
27 | use crate::builtin_macro::BuiltinFnLikeExpander; | 28 | use crate::builtin_macro::BuiltinFnLikeExpander; |
28 | 29 | ||
29 | #[cfg(test)] | 30 | #[cfg(test)] |
@@ -70,7 +71,18 @@ impl HirFileId { | |||
70 | HirFileIdRepr::FileId(file_id) => file_id, | 71 | HirFileIdRepr::FileId(file_id) => file_id, |
71 | HirFileIdRepr::MacroFile(macro_file) => { | 72 | HirFileIdRepr::MacroFile(macro_file) => { |
72 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | 73 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); |
73 | loc.ast_id.file_id().original_file(db) | 74 | loc.kind.file_id().original_file(db) |
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /// If this is a macro call, returns the syntax node of the call. | ||
80 | pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { | ||
81 | match self.0 { | ||
82 | HirFileIdRepr::FileId(_) => None, | ||
83 | HirFileIdRepr::MacroFile(macro_file) => { | ||
84 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | ||
85 | Some(loc.kind.node(db)) | ||
74 | } | 86 | } |
75 | } | 87 | } |
76 | } | 88 | } |
@@ -82,17 +94,17 @@ impl HirFileId { | |||
82 | HirFileIdRepr::MacroFile(macro_file) => { | 94 | HirFileIdRepr::MacroFile(macro_file) => { |
83 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 95 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
84 | 96 | ||
85 | let arg_tt = loc.ast_id.to_node(db).token_tree()?; | 97 | let arg_tt = loc.kind.arg(db)?; |
86 | let def_tt = loc.def.ast_id.to_node(db).token_tree()?; | 98 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
87 | 99 | ||
88 | let macro_def = db.macro_def(loc.def)?; | 100 | let macro_def = db.macro_def(loc.def)?; |
89 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 101 | let (parse, exp_map) = db.parse_macro(macro_file)?; |
90 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 102 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
91 | 103 | ||
92 | Some(ExpansionInfo { | 104 | Some(ExpansionInfo { |
93 | expanded: Source::new(self, parse.syntax_node()), | 105 | expanded: InFile::new(self, parse.syntax_node()), |
94 | arg: Source::new(loc.ast_id.file_id, arg_tt), | 106 | arg: InFile::new(loc.kind.file_id(), arg_tt), |
95 | def: Source::new(loc.ast_id.file_id, def_tt), | 107 | def: InFile::new(loc.def.ast_id?.file_id, def_tt), |
96 | macro_arg, | 108 | macro_arg, |
97 | macro_def, | 109 | macro_def, |
98 | exp_map, | 110 | exp_map, |
@@ -105,14 +117,6 @@ impl HirFileId { | |||
105 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
106 | pub struct MacroFile { | 118 | pub struct MacroFile { |
107 | macro_call_id: MacroCallId, | 119 | macro_call_id: MacroCallId, |
108 | macro_file_kind: MacroFileKind, | ||
109 | } | ||
110 | |||
111 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
112 | pub enum MacroFileKind { | ||
113 | Items, | ||
114 | Expr, | ||
115 | Statements, | ||
116 | } | 120 | } |
117 | 121 | ||
118 | /// `MacroCallId` identifies a particular macro invocation, like | 122 | /// `MacroCallId` identifies a particular macro invocation, like |
@@ -130,18 +134,20 @@ impl salsa::InternKey for MacroCallId { | |||
130 | 134 | ||
131 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
132 | pub struct MacroDefId { | 136 | pub struct MacroDefId { |
133 | pub krate: CrateId, | 137 | // FIXME: krate and ast_id are currently optional because we don't have a |
134 | pub ast_id: AstId<ast::MacroCall>, | 138 | // definition location for built-in derives. There is one, though: the |
139 | // standard library defines them. The problem is that it uses the new | ||
140 | // `macro` syntax for this, which we don't support yet. As soon as we do | ||
141 | // (which will probably require touching this code), we can instead use | ||
142 | // that (and also remove the hacks for resolving built-in derives). | ||
143 | pub krate: Option<CrateId>, | ||
144 | pub ast_id: Option<AstId<ast::MacroCall>>, | ||
135 | pub kind: MacroDefKind, | 145 | pub kind: MacroDefKind, |
136 | } | 146 | } |
137 | 147 | ||
138 | impl MacroDefId { | 148 | impl MacroDefId { |
139 | pub fn as_call_id( | 149 | pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId { |
140 | self, | 150 | db.intern_macro(MacroCallLoc { def: self, kind }) |
141 | db: &dyn db::AstDatabase, | ||
142 | ast_id: AstId<ast::MacroCall>, | ||
143 | ) -> MacroCallId { | ||
144 | db.intern_macro(MacroCallLoc { def: self, ast_id }) | ||
145 | } | 151 | } |
146 | } | 152 | } |
147 | 153 | ||
@@ -149,64 +155,103 @@ impl MacroDefId { | |||
149 | pub enum MacroDefKind { | 155 | pub enum MacroDefKind { |
150 | Declarative, | 156 | Declarative, |
151 | BuiltIn(BuiltinFnLikeExpander), | 157 | BuiltIn(BuiltinFnLikeExpander), |
158 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | ||
159 | BuiltInDerive(BuiltinDeriveExpander), | ||
152 | } | 160 | } |
153 | 161 | ||
154 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 162 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
155 | pub struct MacroCallLoc { | 163 | pub struct MacroCallLoc { |
156 | pub(crate) def: MacroDefId, | 164 | pub(crate) def: MacroDefId, |
157 | pub(crate) ast_id: AstId<ast::MacroCall>, | 165 | pub(crate) kind: MacroCallKind, |
166 | } | ||
167 | |||
168 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
169 | pub enum MacroCallKind { | ||
170 | FnLike(AstId<ast::MacroCall>), | ||
171 | Attr(AstId<ast::ModuleItem>), | ||
172 | } | ||
173 | |||
174 | impl MacroCallKind { | ||
175 | pub fn file_id(&self) -> HirFileId { | ||
176 | match self { | ||
177 | MacroCallKind::FnLike(ast_id) => ast_id.file_id, | ||
178 | MacroCallKind::Attr(ast_id) => ast_id.file_id, | ||
179 | } | ||
180 | } | ||
181 | |||
182 | pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { | ||
183 | match self { | ||
184 | MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), | ||
185 | MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), | ||
186 | } | ||
187 | } | ||
188 | |||
189 | pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { | ||
190 | match self { | ||
191 | MacroCallKind::FnLike(ast_id) => { | ||
192 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | ||
193 | } | ||
194 | MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()), | ||
195 | } | ||
196 | } | ||
158 | } | 197 | } |
159 | 198 | ||
160 | impl MacroCallId { | 199 | impl MacroCallId { |
161 | pub fn as_file(self, kind: MacroFileKind) -> HirFileId { | 200 | pub fn as_file(self) -> HirFileId { |
162 | let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind }; | 201 | MacroFile { macro_call_id: self }.into() |
163 | macro_file.into() | ||
164 | } | 202 | } |
165 | } | 203 | } |
166 | 204 | ||
167 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro | 205 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro |
168 | #[derive(Debug, Clone, PartialEq, Eq)] | 206 | #[derive(Debug, Clone, PartialEq, Eq)] |
169 | pub struct ExpansionInfo { | 207 | pub struct ExpansionInfo { |
170 | expanded: Source<SyntaxNode>, | 208 | expanded: InFile<SyntaxNode>, |
171 | arg: Source<ast::TokenTree>, | 209 | arg: InFile<SyntaxNode>, |
172 | def: Source<ast::TokenTree>, | 210 | def: InFile<ast::TokenTree>, |
173 | 211 | ||
174 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 212 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, |
175 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 213 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
176 | exp_map: Arc<mbe::TokenMap>, | 214 | exp_map: Arc<mbe::TokenMap>, |
177 | } | 215 | } |
178 | 216 | ||
217 | pub use mbe::Origin; | ||
218 | |||
179 | impl ExpansionInfo { | 219 | impl ExpansionInfo { |
180 | pub fn map_token_down(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> { | 220 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { |
221 | Some(self.arg.with_value(self.arg.value.parent()?)) | ||
222 | } | ||
223 | |||
224 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { | ||
181 | assert_eq!(token.file_id, self.arg.file_id); | 225 | assert_eq!(token.file_id, self.arg.file_id); |
182 | let range = | 226 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
183 | token.value.text_range().checked_sub(self.arg.value.syntax().text_range().start())?; | ||
184 | let token_id = self.macro_arg.1.token_by_range(range)?; | 227 | let token_id = self.macro_arg.1.token_by_range(range)?; |
185 | let token_id = self.macro_def.0.map_id_down(token_id); | 228 | let token_id = self.macro_def.0.map_id_down(token_id); |
186 | 229 | ||
187 | let range = self.exp_map.range_by_token(token_id)?; | 230 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
188 | 231 | ||
189 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; | 232 | let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; |
190 | 233 | ||
191 | Some(self.expanded.with_value(token)) | 234 | Some(self.expanded.with_value(token)) |
192 | } | 235 | } |
193 | 236 | ||
194 | pub fn map_token_up(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> { | 237 | pub fn map_token_up( |
238 | &self, | ||
239 | token: InFile<&SyntaxToken>, | ||
240 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | ||
195 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | 241 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; |
196 | 242 | ||
197 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 243 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
198 | let (token_map, tt) = match origin { | 244 | let (token_map, tt) = match origin { |
199 | mbe::Origin::Call => (&self.macro_arg.1, &self.arg), | 245 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), |
200 | mbe::Origin::Def => (&self.macro_def.1, &self.def), | 246 | mbe::Origin::Def => { |
247 | (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone())) | ||
248 | } | ||
201 | }; | 249 | }; |
202 | 250 | ||
203 | let range = token_map.range_by_token(token_id)?; | 251 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
204 | let token = algo::find_covering_element( | 252 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
205 | tt.value.syntax(), | 253 | .into_token()?; |
206 | range + tt.value.syntax().text_range().start(), | 254 | Some((tt.with_value(token), origin)) |
207 | ) | ||
208 | .into_token()?; | ||
209 | Some(tt.with_value(token)) | ||
210 | } | 255 | } |
211 | } | 256 | } |
212 | 257 | ||
@@ -214,76 +259,66 @@ impl ExpansionInfo { | |||
214 | /// | 259 | /// |
215 | /// It is stable across reparses, and can be used as salsa key/value. | 260 | /// It is stable across reparses, and can be used as salsa key/value. |
216 | // FIXME: isn't this just a `Source<FileAstId<N>>` ? | 261 | // FIXME: isn't this just a `Source<FileAstId<N>>` ? |
217 | #[derive(Debug)] | 262 | pub type AstId<N> = InFile<FileAstId<N>>; |
218 | pub struct AstId<N: AstNode> { | ||
219 | file_id: HirFileId, | ||
220 | file_ast_id: FileAstId<N>, | ||
221 | } | ||
222 | |||
223 | impl<N: AstNode> Clone for AstId<N> { | ||
224 | fn clone(&self) -> AstId<N> { | ||
225 | *self | ||
226 | } | ||
227 | } | ||
228 | impl<N: AstNode> Copy for AstId<N> {} | ||
229 | |||
230 | impl<N: AstNode> PartialEq for AstId<N> { | ||
231 | fn eq(&self, other: &Self) -> bool { | ||
232 | (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) | ||
233 | } | ||
234 | } | ||
235 | impl<N: AstNode> Eq for AstId<N> {} | ||
236 | impl<N: AstNode> Hash for AstId<N> { | ||
237 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
238 | (self.file_id, self.file_ast_id).hash(hasher); | ||
239 | } | ||
240 | } | ||
241 | 263 | ||
242 | impl<N: AstNode> AstId<N> { | 264 | impl<N: AstNode> AstId<N> { |
243 | pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> { | ||
244 | AstId { file_id, file_ast_id } | ||
245 | } | ||
246 | |||
247 | pub fn file_id(&self) -> HirFileId { | ||
248 | self.file_id | ||
249 | } | ||
250 | |||
251 | pub fn to_node(&self, db: &dyn db::AstDatabase) -> N { | 265 | pub fn to_node(&self, db: &dyn db::AstDatabase) -> N { |
252 | let root = db.parse_or_expand(self.file_id).unwrap(); | 266 | let root = db.parse_or_expand(self.file_id).unwrap(); |
253 | db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root) | 267 | db.ast_id_map(self.file_id).get(self.value).to_node(&root) |
254 | } | 268 | } |
255 | } | 269 | } |
256 | 270 | ||
257 | /// `Source<T>` stores a value of `T` inside a particular file/syntax tree. | 271 | /// `InFile<T>` stores a value of `T` inside a particular file/syntax tree. |
258 | /// | 272 | /// |
259 | /// Typical usages are: | 273 | /// Typical usages are: |
260 | /// | 274 | /// |
261 | /// * `Source<SyntaxNode>` -- syntax node in a file | 275 | /// * `InFile<SyntaxNode>` -- syntax node in a file |
262 | /// * `Source<ast::FnDef>` -- ast node in a file | 276 | /// * `InFile<ast::FnDef>` -- ast node in a file |
263 | /// * `Source<TextUnit>` -- offset in a file | 277 | /// * `InFile<TextUnit>` -- offset in a file |
264 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | 278 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] |
265 | pub struct Source<T> { | 279 | pub struct InFile<T> { |
266 | pub file_id: HirFileId, | 280 | pub file_id: HirFileId, |
267 | pub value: T, | 281 | pub value: T, |
268 | } | 282 | } |
269 | 283 | ||
270 | impl<T> Source<T> { | 284 | impl<T> InFile<T> { |
271 | pub fn new(file_id: HirFileId, value: T) -> Source<T> { | 285 | pub fn new(file_id: HirFileId, value: T) -> InFile<T> { |
272 | Source { file_id, value } | 286 | InFile { file_id, value } |
273 | } | 287 | } |
274 | 288 | ||
275 | // Similarly, naming here is stupid... | 289 | // Similarly, naming here is stupid... |
276 | pub fn with_value<U>(&self, value: U) -> Source<U> { | 290 | pub fn with_value<U>(&self, value: U) -> InFile<U> { |
277 | Source::new(self.file_id, value) | 291 | InFile::new(self.file_id, value) |
278 | } | 292 | } |
279 | 293 | ||
280 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 294 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> { |
281 | Source::new(self.file_id, f(self.value)) | 295 | InFile::new(self.file_id, f(self.value)) |
282 | } | 296 | } |
283 | pub fn as_ref(&self) -> Source<&T> { | 297 | pub fn as_ref(&self) -> InFile<&T> { |
284 | self.with_value(&self.value) | 298 | self.with_value(&self.value) |
285 | } | 299 | } |
286 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { | 300 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { |
287 | db.parse_or_expand(self.file_id).expect("source created from invalid file") | 301 | db.parse_or_expand(self.file_id).expect("source created from invalid file") |
288 | } | 302 | } |
289 | } | 303 | } |
304 | |||
305 | impl<T: Clone> InFile<&T> { | ||
306 | pub fn cloned(&self) -> InFile<T> { | ||
307 | self.with_value(self.value.clone()) | ||
308 | } | ||
309 | } | ||
310 | |||
311 | impl InFile<SyntaxNode> { | ||
312 | pub fn ancestors_with_macros<'a>( | ||
313 | self, | ||
314 | db: &'a impl crate::db::AstDatabase, | ||
315 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a { | ||
316 | std::iter::successors(Some(self), move |node| match node.value.parent() { | ||
317 | Some(parent) => Some(node.with_value(parent)), | ||
318 | None => { | ||
319 | let parent_node = node.file_id.call_node(db)?; | ||
320 | Some(parent_node) | ||
321 | } | ||
322 | }) | ||
323 | } | ||
324 | } | ||
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 7824489d7..e62693b68 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -38,8 +38,8 @@ impl Name { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | /// Shortcut to create inline plain text name | 40 | /// Shortcut to create inline plain text name |
41 | const fn new_inline_ascii(len: usize, text: &[u8]) -> Name { | 41 | const fn new_inline_ascii(text: &[u8]) -> Name { |
42 | Name::new_text(SmolStr::new_inline_from_ascii(len, text)) | 42 | Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) |
43 | } | 43 | } |
44 | 44 | ||
45 | /// Resolve a name from the text of token. | 45 | /// Resolve a name from the text of token. |
@@ -83,6 +83,12 @@ impl AsName for ast::Name { | |||
83 | } | 83 | } |
84 | } | 84 | } |
85 | 85 | ||
86 | impl AsName for tt::Ident { | ||
87 | fn as_name(&self) -> Name { | ||
88 | Name::resolve(&self.text) | ||
89 | } | ||
90 | } | ||
91 | |||
86 | impl AsName for ast::FieldKind { | 92 | impl AsName for ast::FieldKind { |
87 | fn as_name(&self) -> Name { | 93 | fn as_name(&self) -> Name { |
88 | match self { | 94 | match self { |
@@ -98,52 +104,102 @@ impl AsName for ra_db::Dependency { | |||
98 | } | 104 | } |
99 | } | 105 | } |
100 | 106 | ||
101 | // Primitives | 107 | pub mod known { |
102 | pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); | 108 | macro_rules! known_names { |
103 | pub const I8: Name = Name::new_inline_ascii(2, b"i8"); | 109 | ($($ident:ident),* $(,)?) => { |
104 | pub const I16: Name = Name::new_inline_ascii(3, b"i16"); | 110 | $( |
105 | pub const I32: Name = Name::new_inline_ascii(3, b"i32"); | 111 | #[allow(bad_style)] |
106 | pub const I64: Name = Name::new_inline_ascii(3, b"i64"); | 112 | pub const $ident: super::Name = |
107 | pub const I128: Name = Name::new_inline_ascii(4, b"i128"); | 113 | super::Name::new_inline_ascii(stringify!($ident).as_bytes()); |
108 | pub const USIZE: Name = Name::new_inline_ascii(5, b"usize"); | 114 | )* |
109 | pub const U8: Name = Name::new_inline_ascii(2, b"u8"); | 115 | }; |
110 | pub const U16: Name = Name::new_inline_ascii(3, b"u16"); | 116 | } |
111 | pub const U32: Name = Name::new_inline_ascii(3, b"u32"); | 117 | |
112 | pub const U64: Name = Name::new_inline_ascii(3, b"u64"); | 118 | known_names!( |
113 | pub const U128: Name = Name::new_inline_ascii(4, b"u128"); | 119 | // Primitives |
114 | pub const F32: Name = Name::new_inline_ascii(3, b"f32"); | 120 | isize, |
115 | pub const F64: Name = Name::new_inline_ascii(3, b"f64"); | 121 | i8, |
116 | pub const BOOL: Name = Name::new_inline_ascii(4, b"bool"); | 122 | i16, |
117 | pub const CHAR: Name = Name::new_inline_ascii(4, b"char"); | 123 | i32, |
118 | pub const STR: Name = Name::new_inline_ascii(3, b"str"); | 124 | i64, |
119 | 125 | i128, | |
120 | // Special names | 126 | usize, |
121 | pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); | 127 | u8, |
122 | pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); | 128 | u16, |
123 | pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules"); | 129 | u32, |
124 | 130 | u64, | |
125 | // Components of known path (value or mod name) | 131 | u128, |
126 | pub const STD: Name = Name::new_inline_ascii(3, b"std"); | 132 | f32, |
127 | pub const ITER: Name = Name::new_inline_ascii(4, b"iter"); | 133 | f64, |
128 | pub const OPS: Name = Name::new_inline_ascii(3, b"ops"); | 134 | bool, |
129 | pub const FUTURE: Name = Name::new_inline_ascii(6, b"future"); | 135 | char, |
130 | pub const RESULT: Name = Name::new_inline_ascii(6, b"result"); | 136 | str, |
131 | pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed"); | 137 | // Special names |
132 | 138 | macro_rules, | |
133 | // Components of known path (type name) | 139 | // Components of known path (value or mod name) |
134 | pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); | 140 | std, |
135 | pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); | 141 | iter, |
136 | pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); | 142 | ops, |
137 | pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); | 143 | future, |
138 | pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); | 144 | result, |
139 | pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); | 145 | boxed, |
140 | pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); | 146 | // Components of known path (type name) |
141 | pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); | 147 | IntoIterator, |
142 | pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); | 148 | Item, |
143 | 149 | Try, | |
144 | // Builtin Macros | 150 | Ok, |
145 | pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file"); | 151 | Future, |
146 | pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); | 152 | Result, |
147 | pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); | 153 | Output, |
148 | pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); | 154 | Target, |
149 | pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); | 155 | Box, |
156 | RangeFrom, | ||
157 | RangeFull, | ||
158 | RangeInclusive, | ||
159 | RangeToInclusive, | ||
160 | RangeTo, | ||
161 | Range, | ||
162 | Neg, | ||
163 | Not, | ||
164 | Index, | ||
165 | // Builtin macros | ||
166 | file, | ||
167 | column, | ||
168 | compile_error, | ||
169 | line, | ||
170 | stringify, | ||
171 | format_args, | ||
172 | format_args_nl, | ||
173 | env, | ||
174 | option_env, | ||
175 | // Builtin derives | ||
176 | Copy, | ||
177 | Clone, | ||
178 | Default, | ||
179 | Debug, | ||
180 | Hash, | ||
181 | Ord, | ||
182 | PartialOrd, | ||
183 | Eq, | ||
184 | PartialEq, | ||
185 | ); | ||
186 | |||
187 | // self/Self cannot be used as an identifier | ||
188 | pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self"); | ||
189 | pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self"); | ||
190 | |||
191 | #[macro_export] | ||
192 | macro_rules! name { | ||
193 | (self) => { | ||
194 | $crate::name::known::SELF_PARAM | ||
195 | }; | ||
196 | (Self) => { | ||
197 | $crate::name::known::SELF_TYPE | ||
198 | }; | ||
199 | ($ident:ident) => { | ||
200 | $crate::name::known::$ident | ||
201 | }; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | pub use crate::name; | ||
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs index 65a35e52f..4de219ce4 100644 --- a/crates/ra_hir_expand/src/quote.rs +++ b/crates/ra_hir_expand/src/quote.rs | |||
@@ -16,7 +16,10 @@ macro_rules! __quote { | |||
16 | { | 16 | { |
17 | let children = $crate::__quote!($($tt)*); | 17 | let children = $crate::__quote!($($tt)*); |
18 | let subtree = tt::Subtree { | 18 | let subtree = tt::Subtree { |
19 | delimiter: tt::Delimiter::$delim, | 19 | delimiter: Some(tt::Delimiter { |
20 | kind: tt::DelimiterKind::$delim, | ||
21 | id: tt::TokenId::unspecified(), | ||
22 | }), | ||
20 | token_trees: $crate::quote::IntoTt::to_tokens(children), | 23 | token_trees: $crate::quote::IntoTt::to_tokens(children), |
21 | }; | 24 | }; |
22 | subtree | 25 | subtree |
@@ -29,6 +32,7 @@ macro_rules! __quote { | |||
29 | tt::Leaf::Punct(tt::Punct { | 32 | tt::Leaf::Punct(tt::Punct { |
30 | char: $first, | 33 | char: $first, |
31 | spacing: tt::Spacing::Alone, | 34 | spacing: tt::Spacing::Alone, |
35 | id: tt::TokenId::unspecified(), | ||
32 | }).into() | 36 | }).into() |
33 | ] | 37 | ] |
34 | } | 38 | } |
@@ -40,10 +44,12 @@ macro_rules! __quote { | |||
40 | tt::Leaf::Punct(tt::Punct { | 44 | tt::Leaf::Punct(tt::Punct { |
41 | char: $first, | 45 | char: $first, |
42 | spacing: tt::Spacing::Joint, | 46 | spacing: tt::Spacing::Joint, |
47 | id: tt::TokenId::unspecified(), | ||
43 | }).into(), | 48 | }).into(), |
44 | tt::Leaf::Punct(tt::Punct { | 49 | tt::Leaf::Punct(tt::Punct { |
45 | char: $sec, | 50 | char: $sec, |
46 | spacing: tt::Spacing::Alone, | 51 | spacing: tt::Spacing::Alone, |
52 | id: tt::TokenId::unspecified(), | ||
47 | }).into() | 53 | }).into() |
48 | ] | 54 | ] |
49 | } | 55 | } |
@@ -60,6 +66,15 @@ macro_rules! __quote { | |||
60 | } | 66 | } |
61 | }; | 67 | }; |
62 | 68 | ||
69 | ( ## $first:ident $($tail:tt)* ) => { | ||
70 | { | ||
71 | let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>(); | ||
72 | let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*)); | ||
73 | tokens.append(&mut tail_tokens); | ||
74 | tokens | ||
75 | } | ||
76 | }; | ||
77 | |||
63 | // Brace | 78 | // Brace |
64 | ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) }; | 79 | ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) }; |
65 | // Bracket | 80 | // Bracket |
@@ -85,7 +100,10 @@ macro_rules! __quote { | |||
85 | ( & ) => {$crate::__quote!(@PUNCT '&')}; | 100 | ( & ) => {$crate::__quote!(@PUNCT '&')}; |
86 | ( , ) => {$crate::__quote!(@PUNCT ',')}; | 101 | ( , ) => {$crate::__quote!(@PUNCT ',')}; |
87 | ( : ) => {$crate::__quote!(@PUNCT ':')}; | 102 | ( : ) => {$crate::__quote!(@PUNCT ':')}; |
103 | ( :: ) => {$crate::__quote!(@PUNCT ':', ':')}; | ||
88 | ( . ) => {$crate::__quote!(@PUNCT '.')}; | 104 | ( . ) => {$crate::__quote!(@PUNCT '.')}; |
105 | ( < ) => {$crate::__quote!(@PUNCT '<')}; | ||
106 | ( > ) => {$crate::__quote!(@PUNCT '>')}; | ||
89 | 107 | ||
90 | ( $first:tt $($tail:tt)+ ) => { | 108 | ( $first:tt $($tail:tt)+ ) => { |
91 | { | 109 | { |
@@ -114,7 +132,7 @@ pub(crate) trait IntoTt { | |||
114 | 132 | ||
115 | impl IntoTt for Vec<tt::TokenTree> { | 133 | impl IntoTt for Vec<tt::TokenTree> { |
116 | fn to_subtree(self) -> tt::Subtree { | 134 | fn to_subtree(self) -> tt::Subtree { |
117 | tt::Subtree { delimiter: tt::Delimiter::None, token_trees: self } | 135 | tt::Subtree { delimiter: None, token_trees: self } |
118 | } | 136 | } |
119 | 137 | ||
120 | fn to_tokens(self) -> Vec<tt::TokenTree> { | 138 | fn to_tokens(self) -> Vec<tt::TokenTree> { |
@@ -169,15 +187,15 @@ macro_rules! impl_to_to_tokentrees { | |||
169 | } | 187 | } |
170 | 188 | ||
171 | impl_to_to_tokentrees! { | 189 | impl_to_to_tokentrees! { |
172 | u32 => self { tt::Literal{text: self.to_string().into()} }; | 190 | u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; |
173 | usize => self { tt::Literal{text: self.to_string().into()}}; | 191 | usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; |
174 | i32 => self { tt::Literal{text: self.to_string().into()}}; | 192 | i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; |
175 | tt::Leaf => self { self }; | 193 | tt::Leaf => self { self }; |
176 | tt::Literal => self { self }; | 194 | tt::Literal => self { self }; |
177 | tt::Ident => self { self }; | 195 | tt::Ident => self { self }; |
178 | tt::Punct => self { self }; | 196 | tt::Punct => self { self }; |
179 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}}; | 197 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}; |
180 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}} | 198 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}} |
181 | } | 199 | } |
182 | 200 | ||
183 | #[cfg(test)] | 201 | #[cfg(test)] |
@@ -244,7 +262,13 @@ mod tests { | |||
244 | let fields = | 262 | let fields = |
245 | fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone()).flatten(); | 263 | fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone()).flatten(); |
246 | 264 | ||
247 | let list = tt::Subtree { delimiter: tt::Delimiter::Brace, token_trees: fields.collect() }; | 265 | let list = tt::Subtree { |
266 | delimiter: Some(tt::Delimiter { | ||
267 | kind: tt::DelimiterKind::Brace, | ||
268 | id: tt::TokenId::unspecified(), | ||
269 | }), | ||
270 | token_trees: fields.collect(), | ||
271 | }; | ||
248 | 272 | ||
249 | let quoted = quote! { | 273 | let quoted = quote! { |
250 | impl Clone for #struct_name { | 274 | impl Clone for #struct_name { |