diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-05 20:00:20 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-05 20:00:20 +0000 |
commit | 6e10a9f57815ad865a570816436adfdf0de1cdf0 (patch) | |
tree | 410c416bfe9daa05743ce8c49418c20df28dd625 /crates/ra_hir_expand | |
parent | 217a6fa4a387dbfe6ac725b6dba2f15d6532679f (diff) | |
parent | 10697041c1c72ddbe27c41912e691656be6ccce4 (diff) |
Merge #2479
2479: Add expansion infrastructure for derive macros r=matklad a=flodiebold
I thought I'd experiment a bit with attribute macro/derive expansion, and here's what I've got so far. It has dummy implementations of the Copy / Clone derives, to show that the approach works; it doesn't add any attribute macro support, but I think that fits into the architecture.
Basically, during raw item collection, we look at the attributes and generate macro calls for them if necessary. Currently I only do this for derives, and just add the derive macro calls as separate calls next to the item. I think for derives, it's important that they don't obscure the actual item, since they can't actually change it (e.g. sending the item token tree through macro expansion unnecessarily might make completion within it more complicated).
Attribute macros would have to be recognized at that stage and replace the item (i.e., the raw item collector will just emit an attribute macro call, and not the item). I think when we implement this, we should try to recognize known inert attributes, so that we don't do macro expansion unnecessarily; anything that isn't known needs to be treated as a possible attribute macro call (since the raw item collector can't resolve the macro yet).
There's basically no name resolution for attribute macros implemented, I just hardcoded the built-in derives. In the future, the built-ins should work within the normal name resolution infrastructure; the problem there is that the builtin stubs in `std` use macros 2.0, which we don't support yet (and adding support is outside the scope of this).
One aspect that I don't really have a solution for, but I don't know how important it is, is removing the attribute itself from its input. I'm pretty sure rustc leaves out the attribute macro from the input, but to do that, we'd have to create a completely new syntax node. I guess we could do it when / after converting to a token tree.
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_expand')
-rw-r--r-- | crates/ra_hir_expand/src/ast_id_map.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_derive.rs | 301 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 37 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 19 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/hygiene.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 76 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/quote.rs | 10 |
8 files changed, 424 insertions, 51 deletions
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..78fa9b09a --- /dev/null +++ b/crates/ra_hir_expand/src/builtin_derive.rs | |||
@@ -0,0 +1,301 @@ | |||
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 | ( $(($name:ident, $kind: ident) => $expand:ident),* ) => { | ||
16 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
17 | pub enum BuiltinDeriveExpander { | ||
18 | $($kind),* | ||
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::$kind => $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 => BuiltinDeriveExpander::$kind, )* | ||
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_TRAIT, Copy) => copy_expand, | ||
48 | (CLONE_TRAIT, Clone) => clone_expand, | ||
49 | (DEFAULT_TRAIT, Default) => default_expand, | ||
50 | (DEBUG_TRAIT, Debug) => debug_expand, | ||
51 | (HASH_TRAIT, Hash) => hash_expand, | ||
52 | (ORD_TRAIT, Ord) => ord_expand, | ||
53 | (PARTIAL_ORD_TRAIT, PartialOrd) => partial_ord_expand, | ||
54 | (EQ_TRAIT, Eq) => eq_expand, | ||
55 | (PARTIAL_EQ_TRAIT, 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(tt::Leaf::Punct(tt::Punct { char: '<', spacing: tt::Spacing::Alone }).into()); | ||
101 | for i in 0..n { | ||
102 | if i > 0 { | ||
103 | result | ||
104 | .push(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone }).into()); | ||
105 | } | ||
106 | result.push( | ||
107 | tt::Leaf::Ident(tt::Ident { | ||
108 | id: tt::TokenId::unspecified(), | ||
109 | text: format!("T{}", i).into(), | ||
110 | }) | ||
111 | .into(), | ||
112 | ); | ||
113 | result.extend(bound.iter().cloned()); | ||
114 | } | ||
115 | result.push(tt::Leaf::Punct(tt::Punct { char: '>', spacing: tt::Spacing::Alone }).into()); | ||
116 | result | ||
117 | } | ||
118 | |||
119 | fn expand_simple_derive( | ||
120 | tt: &tt::Subtree, | ||
121 | trait_path: tt::Subtree, | ||
122 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
123 | let info = parse_adt(tt)?; | ||
124 | let name = info.name; | ||
125 | let trait_path_clone = trait_path.token_trees.clone(); | ||
126 | let bound = (quote! { : ##trait_path_clone }).token_trees; | ||
127 | let type_params = make_type_args(info.type_params, bound); | ||
128 | let type_args = make_type_args(info.type_params, Vec::new()); | ||
129 | let trait_path = trait_path.token_trees; | ||
130 | let expanded = quote! { | ||
131 | impl ##type_params ##trait_path for #name ##type_args {} | ||
132 | }; | ||
133 | Ok(expanded) | ||
134 | } | ||
135 | |||
136 | fn copy_expand( | ||
137 | _db: &dyn AstDatabase, | ||
138 | _id: MacroCallId, | ||
139 | tt: &tt::Subtree, | ||
140 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
141 | expand_simple_derive(tt, quote! { std::marker::Copy }) | ||
142 | } | ||
143 | |||
144 | fn clone_expand( | ||
145 | _db: &dyn AstDatabase, | ||
146 | _id: MacroCallId, | ||
147 | tt: &tt::Subtree, | ||
148 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
149 | expand_simple_derive(tt, quote! { std::clone::Clone }) | ||
150 | } | ||
151 | |||
152 | fn default_expand( | ||
153 | _db: &dyn AstDatabase, | ||
154 | _id: MacroCallId, | ||
155 | tt: &tt::Subtree, | ||
156 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
157 | expand_simple_derive(tt, quote! { std::default::Default }) | ||
158 | } | ||
159 | |||
160 | fn debug_expand( | ||
161 | _db: &dyn AstDatabase, | ||
162 | _id: MacroCallId, | ||
163 | tt: &tt::Subtree, | ||
164 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
165 | expand_simple_derive(tt, quote! { std::fmt::Debug }) | ||
166 | } | ||
167 | |||
168 | fn hash_expand( | ||
169 | _db: &dyn AstDatabase, | ||
170 | _id: MacroCallId, | ||
171 | tt: &tt::Subtree, | ||
172 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
173 | expand_simple_derive(tt, quote! { std::hash::Hash }) | ||
174 | } | ||
175 | |||
176 | fn eq_expand( | ||
177 | _db: &dyn AstDatabase, | ||
178 | _id: MacroCallId, | ||
179 | tt: &tt::Subtree, | ||
180 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
181 | expand_simple_derive(tt, quote! { std::cmp::Eq }) | ||
182 | } | ||
183 | |||
184 | fn partial_eq_expand( | ||
185 | _db: &dyn AstDatabase, | ||
186 | _id: MacroCallId, | ||
187 | tt: &tt::Subtree, | ||
188 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
189 | expand_simple_derive(tt, quote! { std::cmp::PartialEq }) | ||
190 | } | ||
191 | |||
192 | fn ord_expand( | ||
193 | _db: &dyn AstDatabase, | ||
194 | _id: MacroCallId, | ||
195 | tt: &tt::Subtree, | ||
196 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
197 | expand_simple_derive(tt, quote! { std::cmp::Ord }) | ||
198 | } | ||
199 | |||
200 | fn partial_ord_expand( | ||
201 | _db: &dyn AstDatabase, | ||
202 | _id: MacroCallId, | ||
203 | tt: &tt::Subtree, | ||
204 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
205 | expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) | ||
206 | } | ||
207 | |||
208 | #[cfg(test)] | ||
209 | mod tests { | ||
210 | use super::*; | ||
211 | use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc, MacroFileKind}; | ||
212 | use ra_db::{fixture::WithFixture, SourceDatabase}; | ||
213 | |||
214 | fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { | ||
215 | let (db, file_id) = TestDB::with_single_file(&s); | ||
216 | let parsed = db.parse(file_id); | ||
217 | let items: Vec<_> = | ||
218 | parsed.syntax_node().descendants().filter_map(|it| ast::ModuleItem::cast(it)).collect(); | ||
219 | |||
220 | let ast_id_map = db.ast_id_map(file_id.into()); | ||
221 | |||
222 | // the first one should be a macro_rules | ||
223 | let def = | ||
224 | MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) }; | ||
225 | |||
226 | let loc = MacroCallLoc { | ||
227 | def, | ||
228 | kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))), | ||
229 | }; | ||
230 | |||
231 | let id = db.intern_macro(loc); | ||
232 | let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Items)).unwrap(); | ||
233 | |||
234 | // FIXME text() for syntax nodes parsed from token tree looks weird | ||
235 | // because there's no whitespace, see below | ||
236 | parsed.text().to_string() | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn test_copy_expand_simple() { | ||
241 | let expanded = expand_builtin_derive( | ||
242 | r#" | ||
243 | #[derive(Copy)] | ||
244 | struct Foo; | ||
245 | "#, | ||
246 | BuiltinDeriveExpander::Copy, | ||
247 | ); | ||
248 | |||
249 | assert_eq!(expanded, "impl <>std::marker::CopyforFoo <>{}"); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn test_copy_expand_with_type_params() { | ||
254 | let expanded = expand_builtin_derive( | ||
255 | r#" | ||
256 | #[derive(Copy)] | ||
257 | struct Foo<A, B>; | ||
258 | "#, | ||
259 | BuiltinDeriveExpander::Copy, | ||
260 | ); | ||
261 | |||
262 | assert_eq!( | ||
263 | expanded, | ||
264 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | ||
265 | ); | ||
266 | } | ||
267 | |||
268 | #[test] | ||
269 | fn test_copy_expand_with_lifetimes() { | ||
270 | let expanded = expand_builtin_derive( | ||
271 | r#" | ||
272 | #[derive(Copy)] | ||
273 | struct Foo<A, B, 'a, 'b>; | ||
274 | "#, | ||
275 | BuiltinDeriveExpander::Copy, | ||
276 | ); | ||
277 | |||
278 | // We currently just ignore lifetimes | ||
279 | |||
280 | assert_eq!( | ||
281 | expanded, | ||
282 | "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" | ||
283 | ); | ||
284 | } | ||
285 | |||
286 | #[test] | ||
287 | fn test_clone_expand() { | ||
288 | let expanded = expand_builtin_derive( | ||
289 | r#" | ||
290 | #[derive(Clone)] | ||
291 | struct Foo<A, B>; | ||
292 | "#, | ||
293 | BuiltinDeriveExpander::Clone, | ||
294 | ); | ||
295 | |||
296 | assert_eq!( | ||
297 | expanded, | ||
298 | "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}" | ||
299 | ); | ||
300 | } | ||
301 | } | ||
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index d370dfb34..35f99b2bc 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -39,7 +39,7 @@ macro_rules! register_builtin { | |||
39 | _ => return None, | 39 | _ => return None, |
40 | }; | 40 | }; |
41 | 41 | ||
42 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) | 42 | Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) }) |
43 | } | 43 | } |
44 | }; | 44 | }; |
45 | } | 45 | } |
@@ -82,10 +82,9 @@ fn line_expand( | |||
82 | _tt: &tt::Subtree, | 82 | _tt: &tt::Subtree, |
83 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 83 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
84 | let loc = db.lookup_intern_macro(id); | 84 | let loc = db.lookup_intern_macro(id); |
85 | let macro_call = loc.ast_id.to_node(db); | ||
86 | 85 | ||
87 | let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 86 | let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
88 | let arg_start = arg.syntax().text_range().start(); | 87 | let arg_start = arg.text_range().start(); |
89 | 88 | ||
90 | let file = id.as_file(MacroFileKind::Expr); | 89 | let file = id.as_file(MacroFileKind::Expr); |
91 | let line_num = to_line_number(db, file, arg_start); | 90 | let line_num = to_line_number(db, file, arg_start); |
@@ -103,11 +102,10 @@ fn stringify_expand( | |||
103 | _tt: &tt::Subtree, | 102 | _tt: &tt::Subtree, |
104 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 103 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
105 | let loc = db.lookup_intern_macro(id); | 104 | let loc = db.lookup_intern_macro(id); |
106 | let macro_call = loc.ast_id.to_node(db); | ||
107 | 105 | ||
108 | let macro_content = { | 106 | let macro_content = { |
109 | let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 107 | let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
110 | let macro_args = arg.syntax().clone(); | 108 | let macro_args = arg.clone(); |
111 | let text = macro_args.text(); | 109 | let text = macro_args.text(); |
112 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); | 110 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); |
113 | text.slice(without_parens).to_string() | 111 | text.slice(without_parens).to_string() |
@@ -148,7 +146,10 @@ fn column_expand( | |||
148 | _tt: &tt::Subtree, | 146 | _tt: &tt::Subtree, |
149 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 147 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
150 | let loc = db.lookup_intern_macro(id); | 148 | let loc = db.lookup_intern_macro(id); |
151 | let macro_call = loc.ast_id.to_node(db); | 149 | let macro_call = match loc.kind { |
150 | crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db), | ||
151 | _ => panic!("column macro called as attr"), | ||
152 | }; | ||
152 | 153 | ||
153 | let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 154 | let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; |
154 | let col_start = macro_call.syntax().text_range().start(); | 155 | let col_start = macro_call.syntax().text_range().start(); |
@@ -164,15 +165,10 @@ fn column_expand( | |||
164 | } | 165 | } |
165 | 166 | ||
166 | fn file_expand( | 167 | fn file_expand( |
167 | db: &dyn AstDatabase, | 168 | _db: &dyn AstDatabase, |
168 | id: MacroCallId, | 169 | _id: MacroCallId, |
169 | _tt: &tt::Subtree, | 170 | _tt: &tt::Subtree, |
170 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 171 | ) -> 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 | 172 | // FIXME: RA purposefully lacks knowledge of absolute file names |
177 | // so just return "". | 173 | // so just return "". |
178 | let file_name = ""; | 174 | let file_name = ""; |
@@ -207,7 +203,7 @@ fn compile_error_expand( | |||
207 | #[cfg(test)] | 203 | #[cfg(test)] |
208 | mod tests { | 204 | mod tests { |
209 | use super::*; | 205 | use super::*; |
210 | use crate::{test_db::TestDB, MacroCallLoc}; | 206 | use crate::{test_db::TestDB, MacroCallKind, MacroCallLoc}; |
211 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 207 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
212 | 208 | ||
213 | fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { | 209 | fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { |
@@ -220,14 +216,17 @@ mod tests { | |||
220 | 216 | ||
221 | // the first one should be a macro_rules | 217 | // the first one should be a macro_rules |
222 | let def = MacroDefId { | 218 | let def = MacroDefId { |
223 | krate: CrateId(0), | 219 | krate: Some(CrateId(0)), |
224 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])), | 220 | ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))), |
225 | kind: MacroDefKind::BuiltIn(expander), | 221 | kind: MacroDefKind::BuiltIn(expander), |
226 | }; | 222 | }; |
227 | 223 | ||
228 | let loc = MacroCallLoc { | 224 | let loc = MacroCallLoc { |
229 | def, | 225 | def, |
230 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])), | 226 | kind: MacroCallKind::FnLike(AstId::new( |
227 | file_id.into(), | ||
228 | ast_id_map.ast_id(¯o_calls[1]), | ||
229 | )), | ||
231 | }; | 230 | }; |
232 | 231 | ||
233 | let id = db.intern_macro(loc); | 232 | let id = db.intern_macro(loc); |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 8e46fa177..99dabf3fb 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -9,14 +9,15 @@ use ra_prof::profile; | |||
9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; | 9 | use ra_syntax::{AstNode, Parse, 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, MacroFileKind, |
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,6 +38,7 @@ 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 | ||
@@ -43,6 +46,7 @@ impl TokenExpander { | |||
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::Def), |
49 | TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Def), | ||
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); |
@@ -91,6 +95,10 @@ pub(crate) fn macro_def( | |||
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.clone()), mbe::TokenMap::default()))) |
93 | } | 97 | } |
98 | MacroDefKind::BuiltInDerive(expander) => Some(Arc::new(( | ||
99 | TokenExpander::BuiltinDerive(expander.clone()), | ||
100 | mbe::TokenMap::default(), | ||
101 | ))), | ||
94 | } | 102 | } |
95 | } | 103 | } |
96 | 104 | ||
@@ -99,9 +107,8 @@ pub(crate) fn macro_arg( | |||
99 | id: MacroCallId, | 107 | id: MacroCallId, |
100 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | 108 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { |
101 | let loc = db.lookup_intern_macro(id); | 109 | let loc = db.lookup_intern_macro(id); |
102 | let macro_call = loc.ast_id.to_node(db); | 110 | let arg = loc.kind.arg(db)?; |
103 | let arg = macro_call.token_tree()?; | 111 | 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))) | 112 | Some(Arc::new((tt, tmap))) |
106 | } | 113 | } |
107 | 114 | ||
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index 64c8b06c6..2e8a533f7 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs | |||
@@ -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 | }; |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 3be9bdf86..59c69b91b 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -9,6 +9,7 @@ pub mod ast_id_map; | |||
9 | pub mod name; | 9 | pub mod name; |
10 | pub mod hygiene; | 10 | pub mod hygiene; |
11 | pub mod diagnostics; | 11 | pub mod diagnostics; |
12 | pub mod builtin_derive; | ||
12 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
13 | pub mod quote; | 14 | pub mod quote; |
14 | 15 | ||
@@ -23,6 +24,7 @@ use ra_syntax::{ | |||
23 | }; | 24 | }; |
24 | 25 | ||
25 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_derive::BuiltinDeriveExpander; | ||
26 | use crate::builtin_macro::BuiltinFnLikeExpander; | 28 | use crate::builtin_macro::BuiltinFnLikeExpander; |
27 | 29 | ||
28 | #[cfg(test)] | 30 | #[cfg(test)] |
@@ -69,7 +71,7 @@ impl HirFileId { | |||
69 | HirFileIdRepr::FileId(file_id) => file_id, | 71 | HirFileIdRepr::FileId(file_id) => file_id, |
70 | HirFileIdRepr::MacroFile(macro_file) => { | 72 | HirFileIdRepr::MacroFile(macro_file) => { |
71 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); | 73 | let loc = db.lookup_intern_macro(macro_file.macro_call_id); |
72 | loc.ast_id.file_id.original_file(db) | 74 | loc.kind.file_id().original_file(db) |
73 | } | 75 | } |
74 | } | 76 | } |
75 | } | 77 | } |
@@ -81,8 +83,8 @@ impl HirFileId { | |||
81 | HirFileIdRepr::MacroFile(macro_file) => { | 83 | HirFileIdRepr::MacroFile(macro_file) => { |
82 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 84 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
83 | 85 | ||
84 | let arg_tt = loc.ast_id.to_node(db).token_tree()?; | 86 | let arg_tt = loc.kind.arg(db)?; |
85 | let def_tt = loc.def.ast_id.to_node(db).token_tree()?; | 87 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
86 | 88 | ||
87 | let macro_def = db.macro_def(loc.def)?; | 89 | let macro_def = db.macro_def(loc.def)?; |
88 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 90 | let (parse, exp_map) = db.parse_macro(macro_file)?; |
@@ -90,8 +92,8 @@ impl HirFileId { | |||
90 | 92 | ||
91 | Some(ExpansionInfo { | 93 | Some(ExpansionInfo { |
92 | expanded: InFile::new(self, parse.syntax_node()), | 94 | expanded: InFile::new(self, parse.syntax_node()), |
93 | arg: InFile::new(loc.ast_id.file_id, arg_tt), | 95 | arg: InFile::new(loc.kind.file_id(), arg_tt), |
94 | def: InFile::new(loc.ast_id.file_id, def_tt), | 96 | def: InFile::new(loc.def.ast_id?.file_id, def_tt), |
95 | macro_arg, | 97 | macro_arg, |
96 | macro_def, | 98 | macro_def, |
97 | exp_map, | 99 | exp_map, |
@@ -129,18 +131,20 @@ impl salsa::InternKey for MacroCallId { | |||
129 | 131 | ||
130 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 132 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
131 | pub struct MacroDefId { | 133 | pub struct MacroDefId { |
132 | pub krate: CrateId, | 134 | // FIXME: krate and ast_id are currently optional because we don't have a |
133 | pub ast_id: AstId<ast::MacroCall>, | 135 | // definition location for built-in derives. There is one, though: the |
136 | // standard library defines them. The problem is that it uses the new | ||
137 | // `macro` syntax for this, which we don't support yet. As soon as we do | ||
138 | // (which will probably require touching this code), we can instead use | ||
139 | // that (and also remove the hacks for resolving built-in derives). | ||
140 | pub krate: Option<CrateId>, | ||
141 | pub ast_id: Option<AstId<ast::MacroCall>>, | ||
134 | pub kind: MacroDefKind, | 142 | pub kind: MacroDefKind, |
135 | } | 143 | } |
136 | 144 | ||
137 | impl MacroDefId { | 145 | impl MacroDefId { |
138 | pub fn as_call_id( | 146 | pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId { |
139 | self, | 147 | db.intern_macro(MacroCallLoc { def: self, kind }) |
140 | db: &dyn db::AstDatabase, | ||
141 | ast_id: AstId<ast::MacroCall>, | ||
142 | ) -> MacroCallId { | ||
143 | db.intern_macro(MacroCallLoc { def: self, ast_id }) | ||
144 | } | 148 | } |
145 | } | 149 | } |
146 | 150 | ||
@@ -148,12 +152,38 @@ impl MacroDefId { | |||
148 | pub enum MacroDefKind { | 152 | pub enum MacroDefKind { |
149 | Declarative, | 153 | Declarative, |
150 | BuiltIn(BuiltinFnLikeExpander), | 154 | BuiltIn(BuiltinFnLikeExpander), |
155 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | ||
156 | BuiltInDerive(BuiltinDeriveExpander), | ||
151 | } | 157 | } |
152 | 158 | ||
153 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 159 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
154 | pub struct MacroCallLoc { | 160 | pub struct MacroCallLoc { |
155 | pub(crate) def: MacroDefId, | 161 | pub(crate) def: MacroDefId, |
156 | pub(crate) ast_id: AstId<ast::MacroCall>, | 162 | pub(crate) kind: MacroCallKind, |
163 | } | ||
164 | |||
165 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
166 | pub enum MacroCallKind { | ||
167 | FnLike(AstId<ast::MacroCall>), | ||
168 | Attr(AstId<ast::ModuleItem>), | ||
169 | } | ||
170 | |||
171 | impl MacroCallKind { | ||
172 | pub fn file_id(&self) -> HirFileId { | ||
173 | match self { | ||
174 | MacroCallKind::FnLike(ast_id) => ast_id.file_id, | ||
175 | MacroCallKind::Attr(ast_id) => ast_id.file_id, | ||
176 | } | ||
177 | } | ||
178 | |||
179 | pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { | ||
180 | match self { | ||
181 | MacroCallKind::FnLike(ast_id) => { | ||
182 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | ||
183 | } | ||
184 | MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()), | ||
185 | } | ||
186 | } | ||
157 | } | 187 | } |
158 | 188 | ||
159 | impl MacroCallId { | 189 | impl MacroCallId { |
@@ -167,7 +197,7 @@ impl MacroCallId { | |||
167 | #[derive(Debug, Clone, PartialEq, Eq)] | 197 | #[derive(Debug, Clone, PartialEq, Eq)] |
168 | pub struct ExpansionInfo { | 198 | pub struct ExpansionInfo { |
169 | expanded: InFile<SyntaxNode>, | 199 | expanded: InFile<SyntaxNode>, |
170 | arg: InFile<ast::TokenTree>, | 200 | arg: InFile<SyntaxNode>, |
171 | def: InFile<ast::TokenTree>, | 201 | def: InFile<ast::TokenTree>, |
172 | 202 | ||
173 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 203 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, |
@@ -178,8 +208,7 @@ pub struct ExpansionInfo { | |||
178 | impl ExpansionInfo { | 208 | impl ExpansionInfo { |
179 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { | 209 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { |
180 | assert_eq!(token.file_id, self.arg.file_id); | 210 | assert_eq!(token.file_id, self.arg.file_id); |
181 | let range = | 211 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
182 | token.value.text_range().checked_sub(self.arg.value.syntax().text_range().start())?; | ||
183 | let token_id = self.macro_arg.1.token_by_range(range)?; | 212 | let token_id = self.macro_arg.1.token_by_range(range)?; |
184 | let token_id = self.macro_def.0.map_id_down(token_id); | 213 | let token_id = self.macro_def.0.map_id_down(token_id); |
185 | 214 | ||
@@ -195,16 +224,15 @@ impl ExpansionInfo { | |||
195 | 224 | ||
196 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 225 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
197 | let (token_map, tt) = match origin { | 226 | let (token_map, tt) = match origin { |
198 | mbe::Origin::Call => (&self.macro_arg.1, &self.arg), | 227 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), |
199 | mbe::Origin::Def => (&self.macro_def.1, &self.def), | 228 | mbe::Origin::Def => { |
229 | (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone())) | ||
230 | } | ||
200 | }; | 231 | }; |
201 | 232 | ||
202 | let range = token_map.range_by_token(token_id)?; | 233 | let range = token_map.range_by_token(token_id)?; |
203 | let token = algo::find_covering_element( | 234 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
204 | tt.value.syntax(), | 235 | .into_token()?; |
205 | range + tt.value.syntax().text_range().start(), | ||
206 | ) | ||
207 | .into_token()?; | ||
208 | Some(tt.with_value(token)) | 236 | Some(tt.with_value(token)) |
209 | } | 237 | } |
210 | } | 238 | } |
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 05ba37070..c5a191160 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -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 { |
@@ -153,3 +159,14 @@ pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); | |||
153 | pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); | 159 | pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); |
154 | pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); | 160 | pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); |
155 | pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); | 161 | pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); |
162 | |||
163 | // Builtin derives | ||
164 | pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy"); | ||
165 | pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone"); | ||
166 | pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default"); | ||
167 | pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug"); | ||
168 | pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash"); | ||
169 | pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord"); | ||
170 | pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd"); | ||
171 | pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq"); | ||
172 | pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(9, b"PartialEq"); | ||
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs index 65a35e52f..4f698ff13 100644 --- a/crates/ra_hir_expand/src/quote.rs +++ b/crates/ra_hir_expand/src/quote.rs | |||
@@ -60,6 +60,15 @@ macro_rules! __quote { | |||
60 | } | 60 | } |
61 | }; | 61 | }; |
62 | 62 | ||
63 | ( ## $first:ident $($tail:tt)* ) => { | ||
64 | { | ||
65 | let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>(); | ||
66 | let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*)); | ||
67 | tokens.append(&mut tail_tokens); | ||
68 | tokens | ||
69 | } | ||
70 | }; | ||
71 | |||
63 | // Brace | 72 | // Brace |
64 | ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) }; | 73 | ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) }; |
65 | // Bracket | 74 | // Bracket |
@@ -85,6 +94,7 @@ macro_rules! __quote { | |||
85 | ( & ) => {$crate::__quote!(@PUNCT '&')}; | 94 | ( & ) => {$crate::__quote!(@PUNCT '&')}; |
86 | ( , ) => {$crate::__quote!(@PUNCT ',')}; | 95 | ( , ) => {$crate::__quote!(@PUNCT ',')}; |
87 | ( : ) => {$crate::__quote!(@PUNCT ':')}; | 96 | ( : ) => {$crate::__quote!(@PUNCT ':')}; |
97 | ( :: ) => {$crate::__quote!(@PUNCT ':', ':')}; | ||
88 | ( . ) => {$crate::__quote!(@PUNCT '.')}; | 98 | ( . ) => {$crate::__quote!(@PUNCT '.')}; |
89 | 99 | ||
90 | ( $first:tt $($tail:tt)+ ) => { | 100 | ( $first:tt $($tail:tt)+ ) => { |