diff options
author | Edwin Cheng <[email protected]> | 2021-01-04 02:53:31 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2021-01-07 05:08:32 +0000 |
commit | 76f2b9d2ef797fb995f1bd2706a7e609d014a67a (patch) | |
tree | f8f81c7afd64167746a40a576e63fcd234c14b5b | |
parent | b47c63a4bcefe93be3e5fe97b2a57489f13da493 (diff) |
Proper handling $crate Take 2
-rw-r--r-- | crates/hir/src/db.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/path/lower.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 12 | ||||
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 210 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/macros.rs | 31 | ||||
-rw-r--r-- | crates/ide_db/src/apply_change.rs | 1 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/matcher.rs | 2 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/transcriber.rs | 20 | ||||
-rw-r--r-- | crates/mbe/src/parser.rs | 11 |
9 files changed, 235 insertions, 58 deletions
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index d01e1b33d..d5d4cf5b6 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -10,8 +10,8 @@ pub use hir_def::db::{ | |||
10 | TypeAliasDataQuery, UnionDataQuery, | 10 | TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery, |
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, | 14 | InternMacroQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, |
15 | }; | 15 | }; |
16 | pub use hir_ty::db::*; | 16 | pub use hir_ty::db::*; |
17 | 17 | ||
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 8a01e6eea..9518ac109 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -123,7 +123,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
123 | // We follow what it did anyway :) | 123 | // We follow what it did anyway :) |
124 | if segments.len() == 1 && kind == PathKind::Plain { | 124 | if segments.len() == 1 && kind == PathKind::Plain { |
125 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { | 125 | if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { |
126 | if let Some(crate_id) = hygiene.local_inner_macros() { | 126 | if let Some(crate_id) = hygiene.local_inner_macros(path) { |
127 | kind = PathKind::DollarCrate(crate_id); | 127 | kind = PathKind::DollarCrate(crate_id); |
128 | } | 128 | } |
129 | } | 129 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 0a0d021e0..ab2637b8c 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -8,9 +8,9 @@ use parser::FragmentKind; | |||
8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, | 11 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, |
12 | HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, | 12 | EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, |
13 | MacroFile, ProcMacroExpander, | 13 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | /// Total limit on the number of tokens produced by any macro invocation. | 16 | /// Total limit on the number of tokens produced by any macro invocation. |
@@ -94,6 +94,8 @@ pub trait AstDatabase: SourceDatabase { | |||
94 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 94 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
95 | 95 | ||
96 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 96 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
97 | |||
98 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; | ||
97 | } | 99 | } |
98 | 100 | ||
99 | /// This expands the given macro call, but with different arguments. This is | 101 | /// This expands the given macro call, but with different arguments. This is |
@@ -369,6 +371,10 @@ fn parse_macro_with_arg( | |||
369 | } | 371 | } |
370 | } | 372 | } |
371 | 373 | ||
374 | fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> { | ||
375 | Arc::new(HygieneFrame::new(db, file_id)) | ||
376 | } | ||
377 | |||
372 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | 378 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. |
373 | /// FIXME: Not completed | 379 | /// FIXME: Not completed |
374 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | 380 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 7ab0a5e52..8db581b77 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -2,65 +2,209 @@ | |||
2 | //! | 2 | //! |
3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at | 3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at |
4 | //! this moment, this is horribly incomplete and handles only `$crate`. | 4 | //! this moment, this is horribly incomplete and handles only `$crate`. |
5 | use std::sync::Arc; | ||
6 | |||
5 | use base_db::CrateId; | 7 | use base_db::CrateId; |
6 | use either::Either; | 8 | use either::Either; |
7 | use syntax::ast; | 9 | use mbe::Origin; |
10 | use parser::SyntaxKind; | ||
11 | use syntax::{ast, AstNode, SyntaxNode, TextRange, TextSize}; | ||
8 | 12 | ||
9 | use crate::{ | 13 | use crate::{ |
10 | db::AstDatabase, | 14 | db::{self, AstDatabase}, |
11 | name::{AsName, Name}, | 15 | name::{AsName, Name}, |
12 | HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, | 16 | HirFileId, HirFileIdRepr, InFile, MacroCallId, MacroCallLoc, MacroDefKind, MacroFile, |
13 | }; | 17 | }; |
14 | 18 | ||
15 | #[derive(Clone, Debug)] | 19 | #[derive(Clone, Debug)] |
16 | pub struct Hygiene { | 20 | pub struct Hygiene { |
17 | // This is what `$crate` expands to | 21 | frames: Option<HygieneFrames>, |
18 | def_crate: Option<CrateId>, | ||
19 | |||
20 | // Indicate this is a local inner macro | ||
21 | local_inner: bool, | ||
22 | } | 22 | } |
23 | 23 | ||
24 | impl Hygiene { | 24 | impl Hygiene { |
25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | 25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { |
26 | let (def_crate, local_inner) = match file_id.0 { | 26 | Hygiene { frames: Some(HygieneFrames::new(db, file_id.clone())) } |
27 | HirFileIdRepr::FileId(_) => (None, false), | ||
28 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | ||
29 | MacroCallId::LazyMacro(id) => { | ||
30 | let loc = db.lookup_intern_macro(id); | ||
31 | match loc.def.kind { | ||
32 | MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner), | ||
33 | MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false), | ||
34 | MacroDefKind::BuiltInDerive(_) => (None, false), | ||
35 | MacroDefKind::BuiltInEager(_) => (None, false), | ||
36 | MacroDefKind::ProcMacro(_) => (None, false), | ||
37 | } | ||
38 | } | ||
39 | MacroCallId::EagerMacro(_id) => (None, false), | ||
40 | }, | ||
41 | }; | ||
42 | Hygiene { def_crate, local_inner } | ||
43 | } | 27 | } |
44 | 28 | ||
45 | pub fn new_unhygienic() -> Hygiene { | 29 | pub fn new_unhygienic() -> Hygiene { |
46 | Hygiene { def_crate: None, local_inner: false } | 30 | Hygiene { frames: None } |
47 | } | 31 | } |
48 | 32 | ||
49 | // FIXME: this should just return name | 33 | // FIXME: this should just return name |
50 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 34 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { |
51 | if let Some(def_crate) = self.def_crate { | 35 | if let Some(frames) = &self.frames { |
52 | if name_ref.text() == "$crate" { | 36 | if name_ref.text() == "$crate" { |
53 | return Either::Right(def_crate); | 37 | if let Some(krate) = frames.root_crate(name_ref.syntax()) { |
38 | return Either::Right(krate); | ||
39 | } | ||
54 | } | 40 | } |
55 | } | 41 | } |
42 | |||
56 | Either::Left(name_ref.as_name()) | 43 | Either::Left(name_ref.as_name()) |
57 | } | 44 | } |
58 | 45 | ||
59 | pub fn local_inner_macros(&self) -> Option<CrateId> { | 46 | pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { |
60 | if self.local_inner { | 47 | let mut token = path.syntax().first_token()?.text_range(); |
61 | self.def_crate | 48 | let frames = self.frames.as_ref()?; |
62 | } else { | 49 | let mut current = frames.0.clone(); |
63 | None | 50 | |
51 | loop { | ||
52 | let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?; | ||
53 | if origin == Origin::Def { | ||
54 | return if current.local_inner { frames.root_crate(path.syntax()) } else { None }; | ||
55 | } | ||
56 | current = current.call_site.as_ref()?.clone(); | ||
57 | token = mapped.value; | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | #[derive(Clone, Debug)] | ||
63 | struct HygieneFrames(Arc<HygieneFrame>); | ||
64 | |||
65 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
66 | pub struct HygieneFrame { | ||
67 | expansion: Option<HygieneInfo>, | ||
68 | |||
69 | // Indicate this is a local inner macro | ||
70 | local_inner: bool, | ||
71 | krate: Option<CrateId>, | ||
72 | |||
73 | call_site: Option<Arc<HygieneFrame>>, | ||
74 | def_site: Option<Arc<HygieneFrame>>, | ||
75 | } | ||
76 | |||
77 | impl HygieneFrames { | ||
78 | fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { | ||
79 | HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) | ||
80 | } | ||
81 | |||
82 | fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> { | ||
83 | let mut token = node.first_token()?.text_range(); | ||
84 | let mut result = self.0.krate; | ||
85 | let mut current = self.0.clone(); | ||
86 | |||
87 | while let Some((mapped, origin)) = | ||
88 | current.expansion.as_ref().and_then(|it| it.map_ident_up(token)) | ||
89 | { | ||
90 | result = current.krate; | ||
91 | |||
92 | let site = match origin { | ||
93 | Origin::Def => ¤t.def_site, | ||
94 | Origin::Call => ¤t.call_site, | ||
95 | }; | ||
96 | |||
97 | let site = match site { | ||
98 | None => break, | ||
99 | Some(it) => it, | ||
100 | }; | ||
101 | |||
102 | current = site.clone(); | ||
103 | token = mapped.value; | ||
64 | } | 104 | } |
105 | |||
106 | result | ||
107 | } | ||
108 | } | ||
109 | |||
110 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
111 | struct HygieneInfo { | ||
112 | arg_start: InFile<TextSize>, | ||
113 | /// The `macro_rules!` arguments. | ||
114 | def_start: Option<InFile<TextSize>>, | ||
115 | |||
116 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | ||
117 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | ||
118 | exp_map: Arc<mbe::TokenMap>, | ||
119 | } | ||
120 | |||
121 | impl HygieneInfo { | ||
122 | fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { | ||
123 | let token_id = self.exp_map.token_by_range(token)?; | ||
124 | |||
125 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | ||
126 | let (token_map, tt) = match origin { | ||
127 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), | ||
128 | mbe::Origin::Def => ( | ||
129 | &self.macro_def.1, | ||
130 | self.def_start | ||
131 | .as_ref() | ||
132 | .expect("`Origin::Def` used with non-`macro_rules!` macro") | ||
133 | .clone(), | ||
134 | ), | ||
135 | }; | ||
136 | |||
137 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; | ||
138 | Some((tt.with_value(range + tt.value), origin)) | ||
139 | } | ||
140 | } | ||
141 | |||
142 | fn make_hygiene_info( | ||
143 | db: &dyn AstDatabase, | ||
144 | macro_file: MacroFile, | ||
145 | loc: &MacroCallLoc, | ||
146 | ) -> Option<HygieneInfo> { | ||
147 | let arg_tt = loc.kind.arg(db)?; | ||
148 | |||
149 | let def_offset = loc.def.ast_id.and_then(|id| { | ||
150 | let def_tt = match id.to_node(db) { | ||
151 | ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), | ||
152 | ast::Macro::MacroDef(_) => return None, | ||
153 | }; | ||
154 | Some(InFile::new(id.file_id, def_tt)) | ||
155 | }); | ||
156 | |||
157 | let macro_def = db.macro_def(loc.def)?; | ||
158 | let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; | ||
159 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | ||
160 | |||
161 | Some(HygieneInfo { | ||
162 | arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()), | ||
163 | def_start: def_offset, | ||
164 | macro_arg, | ||
165 | macro_def, | ||
166 | exp_map, | ||
167 | }) | ||
168 | } | ||
169 | |||
170 | impl HygieneFrame { | ||
171 | pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame { | ||
172 | let (info, krate, local_inner) = match file_id.0 { | ||
173 | HirFileIdRepr::FileId(_) => (None, None, false), | ||
174 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | ||
175 | MacroCallId::EagerMacro(_id) => (None, None, false), | ||
176 | MacroCallId::LazyMacro(id) => { | ||
177 | let loc = db.lookup_intern_macro(id); | ||
178 | let info = make_hygiene_info(db, macro_file, &loc); | ||
179 | match loc.def.kind { | ||
180 | MacroDefKind::Declarative => { | ||
181 | (info, Some(loc.def.krate), loc.def.local_inner) | ||
182 | } | ||
183 | MacroDefKind::BuiltIn(_) => (info, Some(loc.def.krate), false), | ||
184 | MacroDefKind::BuiltInDerive(_) => (info, None, false), | ||
185 | MacroDefKind::BuiltInEager(_) => (info, None, false), | ||
186 | MacroDefKind::ProcMacro(_) => (info, None, false), | ||
187 | } | ||
188 | } | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | let info = match info { | ||
193 | None => { | ||
194 | return HygieneFrame { | ||
195 | expansion: None, | ||
196 | local_inner, | ||
197 | krate, | ||
198 | call_site: None, | ||
199 | def_site: None, | ||
200 | }; | ||
201 | } | ||
202 | Some(it) => it, | ||
203 | }; | ||
204 | |||
205 | let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); | ||
206 | let call_site = Some(db.hygiene_frame(info.arg_start.file_id)); | ||
207 | |||
208 | HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } | ||
65 | } | 209 | } |
66 | } | 210 | } |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 1953da7be..c64f0b5b5 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -371,6 +371,37 @@ expand!(); | |||
371 | } | 371 | } |
372 | 372 | ||
373 | #[test] | 373 | #[test] |
374 | fn infer_macro_with_dollar_crate_in_def_site() { | ||
375 | check_types( | ||
376 | r#" | ||
377 | //- /main.rs crate:main deps:foo | ||
378 | use foo::expand; | ||
379 | |||
380 | macro_rules! list { | ||
381 | ($($tt:tt)*) => { $($tt)* } | ||
382 | } | ||
383 | |||
384 | fn test() { | ||
385 | let r = expand!(); | ||
386 | r; | ||
387 | //^ u128 | ||
388 | } | ||
389 | |||
390 | //- /lib.rs crate:foo | ||
391 | #[macro_export] | ||
392 | macro_rules! expand { | ||
393 | () => { list!($crate::m!()) }; | ||
394 | } | ||
395 | |||
396 | #[macro_export] | ||
397 | macro_rules! m { | ||
398 | () => { 0u128 }; | ||
399 | } | ||
400 | "#, | ||
401 | ); | ||
402 | } | ||
403 | |||
404 | #[test] | ||
374 | fn infer_type_value_non_legacy_macro_use_as() { | 405 | fn infer_type_value_non_legacy_macro_use_as() { |
375 | check_infer( | 406 | check_infer( |
376 | r#" | 407 | r#" |
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 71c19ed13..c770a236b 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -145,6 +145,7 @@ impl RootDatabase { | |||
145 | hir::db::MacroDefQuery | 145 | hir::db::MacroDefQuery |
146 | hir::db::ParseMacroExpansionQuery | 146 | hir::db::ParseMacroExpansionQuery |
147 | hir::db::MacroExpandQuery | 147 | hir::db::MacroExpandQuery |
148 | hir::db::HygieneFrameQuery | ||
148 | 149 | ||
149 | // DefDatabase | 150 | // DefDatabase |
150 | hir::db::ItemTreeQuery | 151 | hir::db::ItemTreeQuery |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index ab5f87c48..385b46601 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -150,7 +150,7 @@ fn match_subtree( | |||
150 | res.add_err(err!("leftover tokens")); | 150 | res.add_err(err!("leftover tokens")); |
151 | } | 151 | } |
152 | } | 152 | } |
153 | Op::Var { name, kind } => { | 153 | Op::Var { name, kind, .. } => { |
154 | let kind = match kind { | 154 | let kind = match kind { |
155 | Some(k) => k, | 155 | Some(k) => k, |
156 | None => { | 156 | None => { |
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index 720531237..57f3f104d 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -100,8 +100,8 @@ fn expand_subtree( | |||
100 | err = err.or(e); | 100 | err = err.or(e); |
101 | arena.push(tt.into()); | 101 | arena.push(tt.into()); |
102 | } | 102 | } |
103 | Op::Var { name, .. } => { | 103 | Op::Var { name, id, .. } => { |
104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name); | 104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); |
105 | err = err.or(e); | 105 | err = err.or(e); |
106 | push_fragment(arena, fragment); | 106 | push_fragment(arena, fragment); |
107 | } | 107 | } |
@@ -118,12 +118,10 @@ fn expand_subtree( | |||
118 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } | 118 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } |
119 | } | 119 | } |
120 | 120 | ||
121 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | 121 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> { |
122 | if v == "crate" { | 122 | if v == "crate" { |
123 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. | 123 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. |
124 | let tt = | 124 | let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into(); |
125 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) | ||
126 | .into(); | ||
127 | ExpandResult::ok(Fragment::Tokens(tt)) | 125 | ExpandResult::ok(Fragment::Tokens(tt)) |
128 | } else if !ctx.bindings.contains(v) { | 126 | } else if !ctx.bindings.contains(v) { |
129 | // Note that it is possible to have a `$var` inside a macro which is not bound. | 127 | // Note that it is possible to have a `$var` inside a macro which is not bound. |
@@ -142,14 +140,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
142 | let tt = tt::Subtree { | 140 | let tt = tt::Subtree { |
143 | delimiter: None, | 141 | delimiter: None, |
144 | token_trees: vec![ | 142 | token_trees: vec![ |
145 | tt::Leaf::from(tt::Punct { | 143 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(), |
146 | char: '$', | 144 | tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(), |
147 | spacing: tt::Spacing::Alone, | ||
148 | id: tt::TokenId::unspecified(), | ||
149 | }) | ||
150 | .into(), | ||
151 | tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) | ||
152 | .into(), | ||
153 | ], | 145 | ], |
154 | } | 146 | } |
155 | .into(); | 147 | .into(); |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 2f3ebc831..77cc739b6 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -8,7 +8,7 @@ use crate::{tt_iter::TtIter, ExpandError, MetaTemplate}; | |||
8 | 8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub(crate) enum Op { | 10 | pub(crate) enum Op { |
11 | Var { name: SmolStr, kind: Option<SmolStr> }, | 11 | Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId }, |
12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, | 12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, |
13 | Leaf(tt::Leaf), | 13 | Leaf(tt::Leaf), |
14 | Subtree(MetaTemplate), | 14 | Subtree(MetaTemplate), |
@@ -106,18 +106,21 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
106 | } | 106 | } |
107 | let name = UNDERSCORE.clone(); | 107 | let name = UNDERSCORE.clone(); |
108 | let kind = eat_fragment_kind(src, mode)?; | 108 | let kind = eat_fragment_kind(src, mode)?; |
109 | Op::Var { name, kind } | 109 | let id = punct.id; |
110 | Op::Var { name, kind, id } | ||
110 | } | 111 | } |
111 | tt::Leaf::Ident(ident) => { | 112 | tt::Leaf::Ident(ident) => { |
112 | let name = ident.text.clone(); | 113 | let name = ident.text.clone(); |
113 | let kind = eat_fragment_kind(src, mode)?; | 114 | let kind = eat_fragment_kind(src, mode)?; |
114 | Op::Var { name, kind } | 115 | let id = ident.id; |
116 | Op::Var { name, kind, id } | ||
115 | } | 117 | } |
116 | tt::Leaf::Literal(lit) => { | 118 | tt::Leaf::Literal(lit) => { |
117 | if is_boolean_literal(&lit) { | 119 | if is_boolean_literal(&lit) { |
118 | let name = lit.text.clone(); | 120 | let name = lit.text.clone(); |
119 | let kind = eat_fragment_kind(src, mode)?; | 121 | let kind = eat_fragment_kind(src, mode)?; |
120 | Op::Var { name, kind } | 122 | let id = lit.id; |
123 | Op::Var { name, kind, id } | ||
121 | } else { | 124 | } else { |
122 | bail!("bad var 2"); | 125 | bail!("bad var 2"); |
123 | } | 126 | } |