aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand')
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs12
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs321
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs244
-rw-r--r--crates/ra_hir_expand/src/db.rs66
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs4
-rw-r--r--crates/ra_hir_expand/src/either.rs54
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs9
-rw-r--r--crates/ra_hir_expand/src/lib.rs217
-rw-r--r--crates/ra_hir_expand/src/name.rs158
-rw-r--r--crates/ra_hir_expand/src/quote.rs40
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]
11log = "0.4.5" 11log = "0.4.5"
12either = "1.5"
12 13
13ra_arena = { path = "../ra_arena" } 14ra_arena = { path = "../ra_arena" }
14ra_db = { path = "../ra_db" } 15ra_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
42impl<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)]
43struct ErasedFileAstId(RawId); 53struct ErasedFileAstId(RawId);
44impl_arena_id!(ErasedFileAstId); 54impl_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
3use log::debug;
4
5use ra_parser::FragmentKind;
6use ra_syntax::{
7 ast::{self, AstNode, ModuleItemOwner, NameOwner, TypeParamsOwner},
8 match_ast,
9};
10
11use crate::db::AstDatabase;
12use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind};
13
14macro_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
46register_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
58struct BasicAdtInfo {
59 name: tt::Ident,
60 type_params: usize,
61}
62
63fn 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
98fn 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
139fn 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
156fn 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
164fn 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
172fn 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
180fn 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
188fn 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
196fn 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
204fn 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
212fn 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
220fn 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)]
229mod 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 @@
2use crate::db::AstDatabase; 2use crate::db::AstDatabase;
3use crate::{ 3use 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
9use crate::quote; 8use 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
47register_builtin! { 50register_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
55fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 64fn 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
130fn 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
141fn 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
123fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 152fn 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
166fn file_expand( 198fn 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
234fn 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)]
208mod tests { 277mod 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(&macro_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(&macro_calls[0])), 297 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_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(&macro_calls[1])), 303 kind: MacroCallKind::FnLike(AstId::new(
304 file_id.into(),
305 ast_id_map.ast_id(&macro_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;
6use ra_db::{salsa, SourceDatabase}; 6use ra_db::{salsa, SourceDatabase};
7use ra_parser::FragmentKind; 7use ra_parser::FragmentKind;
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{AstNode, Parse, SyntaxNode}; 9use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
10 10
11use crate::{ 11use 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)]
17pub enum TokenExpander { 17pub enum TokenExpander {
18 MacroRules(mbe::MacroRules), 18 MacroRules(mbe::MacroRules),
19 Builtin(BuiltinFnLikeExpander), 19 Builtin(BuiltinFnLikeExpander),
20 BuiltinDerive(BuiltinDeriveExpander),
20} 21}
21 22
22impl TokenExpander { 23impl 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
165fn 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
19use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; 19use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
20 20
21use crate::{db::AstDatabase, Source}; 21use crate::{db::AstDatabase, InFile};
22 22
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 23pub 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)]
4pub enum Either<A, B> {
5 A(A),
6 B(B),
7}
8
9impl<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`.
5use either::Either;
5use ra_db::CrateId; 6use ra_db::CrateId;
6use ra_syntax::ast; 7use ra_syntax::ast;
7 8
8use crate::{ 9use 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
7pub mod db; 7pub mod db;
8pub mod ast_id_map; 8pub mod ast_id_map;
9pub mod either;
10pub mod name; 9pub mod name;
11pub mod hygiene; 10pub mod hygiene;
12pub mod diagnostics; 11pub mod diagnostics;
12pub mod builtin_derive;
13pub mod builtin_macro; 13pub mod builtin_macro;
14pub mod quote; 14pub mod quote;
15 15
16use std::hash::{Hash, Hasher}; 16use std::hash::Hash;
17use std::sync::Arc; 17use std::sync::Arc;
18 18
19use ra_db::{salsa, CrateId, FileId}; 19use ra_db::{salsa, CrateId, FileId};
@@ -24,6 +24,7 @@ use ra_syntax::{
24}; 24};
25 25
26use crate::ast_id_map::FileAstId; 26use crate::ast_id_map::FileAstId;
27use crate::builtin_derive::BuiltinDeriveExpander;
27use crate::builtin_macro::BuiltinFnLikeExpander; 28use 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)]
106pub struct MacroFile { 118pub 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)]
112pub 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)]
132pub struct MacroDefId { 136pub 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
138impl MacroDefId { 148impl 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 {
149pub enum MacroDefKind { 155pub 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)]
155pub struct MacroCallLoc { 163pub 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)]
169pub enum MacroCallKind {
170 FnLike(AstId<ast::MacroCall>),
171 Attr(AstId<ast::ModuleItem>),
172}
173
174impl 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
160impl MacroCallId { 199impl 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)]
169pub struct ExpansionInfo { 207pub 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
217pub use mbe::Origin;
218
179impl ExpansionInfo { 219impl 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)] 262pub type AstId<N> = InFile<FileAstId<N>>;
218pub struct AstId<N: AstNode> {
219 file_id: HirFileId,
220 file_ast_id: FileAstId<N>,
221}
222
223impl<N: AstNode> Clone for AstId<N> {
224 fn clone(&self) -> AstId<N> {
225 *self
226 }
227}
228impl<N: AstNode> Copy for AstId<N> {}
229
230impl<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}
235impl<N: AstNode> Eq for AstId<N> {}
236impl<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
242impl<N: AstNode> AstId<N> { 264impl<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)]
265pub struct Source<T> { 279pub 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
270impl<T> Source<T> { 284impl<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
305impl<T: Clone> InFile<&T> {
306 pub fn cloned(&self) -> InFile<T> {
307 self.with_value(self.value.clone())
308 }
309}
310
311impl 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
86impl AsName for tt::Ident {
87 fn as_name(&self) -> Name {
88 Name::resolve(&self.text)
89 }
90}
91
86impl AsName for ast::FieldKind { 92impl 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 107pub mod known {
102pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); 108 macro_rules! known_names {
103pub const I8: Name = Name::new_inline_ascii(2, b"i8"); 109 ($($ident:ident),* $(,)?) => {
104pub const I16: Name = Name::new_inline_ascii(3, b"i16"); 110 $(
105pub const I32: Name = Name::new_inline_ascii(3, b"i32"); 111 #[allow(bad_style)]
106pub const I64: Name = Name::new_inline_ascii(3, b"i64"); 112 pub const $ident: super::Name =
107pub const I128: Name = Name::new_inline_ascii(4, b"i128"); 113 super::Name::new_inline_ascii(stringify!($ident).as_bytes());
108pub const USIZE: Name = Name::new_inline_ascii(5, b"usize"); 114 )*
109pub const U8: Name = Name::new_inline_ascii(2, b"u8"); 115 };
110pub const U16: Name = Name::new_inline_ascii(3, b"u16"); 116 }
111pub const U32: Name = Name::new_inline_ascii(3, b"u32"); 117
112pub const U64: Name = Name::new_inline_ascii(3, b"u64"); 118 known_names!(
113pub const U128: Name = Name::new_inline_ascii(4, b"u128"); 119 // Primitives
114pub const F32: Name = Name::new_inline_ascii(3, b"f32"); 120 isize,
115pub const F64: Name = Name::new_inline_ascii(3, b"f64"); 121 i8,
116pub const BOOL: Name = Name::new_inline_ascii(4, b"bool"); 122 i16,
117pub const CHAR: Name = Name::new_inline_ascii(4, b"char"); 123 i32,
118pub const STR: Name = Name::new_inline_ascii(3, b"str"); 124 i64,
119 125 i128,
120// Special names 126 usize,
121pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); 127 u8,
122pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); 128 u16,
123pub 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,
126pub const STD: Name = Name::new_inline_ascii(3, b"std"); 132 f32,
127pub const ITER: Name = Name::new_inline_ascii(4, b"iter"); 133 f64,
128pub const OPS: Name = Name::new_inline_ascii(3, b"ops"); 134 bool,
129pub const FUTURE: Name = Name::new_inline_ascii(6, b"future"); 135 char,
130pub const RESULT: Name = Name::new_inline_ascii(6, b"result"); 136 str,
131pub 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)
134pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); 140 std,
135pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); 141 iter,
136pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); 142 ops,
137pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); 143 future,
138pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); 144 result,
139pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); 145 boxed,
140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); 146 // Components of known path (type name)
141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); 147 IntoIterator,
142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); 148 Item,
143 149 Try,
144// Builtin Macros 150 Ok,
145pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file"); 151 Future,
146pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); 152 Result,
147pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); 153 Output,
148pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); 154 Target,
149pub 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
205pub 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
115impl IntoTt for Vec<tt::TokenTree> { 133impl 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
171impl_to_to_tokentrees! { 189impl_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 {