diff options
author | Edwin Cheng <[email protected]> | 2021-01-02 12:25:05 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2021-01-02 12:39:57 +0000 |
commit | fe5340d970b764175ebf8b8c32c971fa04c49efb (patch) | |
tree | a6409d67ffee8f5c013ca587db39c371cd0c8ffb | |
parent | 51d29fe55456e6e7af69d23982aa57c7fcf91e81 (diff) |
Introduce HygieneFrames for proper token hyginee
-rw-r--r-- | crates/hir_def/src/path/lower.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 157 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/macros.rs | 31 | ||||
-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 |
7 files changed, 182 insertions, 52 deletions
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/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 7ab0a5e52..d49a67195 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -2,30 +2,96 @@ | |||
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 syntax::{ast, AstNode}; | ||
8 | 11 | ||
9 | use crate::{ | 12 | use crate::{ |
10 | db::AstDatabase, | 13 | db::AstDatabase, |
11 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
12 | HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, | 15 | ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, |
13 | }; | 16 | }; |
14 | 17 | ||
15 | #[derive(Clone, Debug)] | 18 | #[derive(Clone, Debug)] |
16 | pub struct Hygiene { | 19 | pub struct Hygiene { |
17 | // This is what `$crate` expands to | 20 | frames: Option<Arc<HygieneFrames>>, |
18 | def_crate: Option<CrateId>, | 21 | } |
22 | |||
23 | impl Hygiene { | ||
24 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | ||
25 | Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) } | ||
26 | } | ||
27 | |||
28 | pub fn new_unhygienic() -> Hygiene { | ||
29 | Hygiene { frames: None } | ||
30 | } | ||
31 | |||
32 | // FIXME: this should just return name | ||
33 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | ||
34 | if let Some(frames) = &self.frames { | ||
35 | if name_ref.text() == "$crate" { | ||
36 | if let Some(krate) = frames.root_crate(&name_ref) { | ||
37 | return Either::Right(krate); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | Either::Left(name_ref.as_name()) | ||
43 | } | ||
44 | |||
45 | pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { | ||
46 | let frames = self.frames.as_ref()?; | ||
47 | |||
48 | let mut token = path.syntax().first_token()?; | ||
49 | let mut current = frames.0.first(); | ||
50 | |||
51 | while let Some((frame, data)) = | ||
52 | current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) | ||
53 | { | ||
54 | let (mapped, origin) = data; | ||
55 | if origin == Origin::Def { | ||
56 | return if frame.local_inner { frame.krate } else { None }; | ||
57 | } | ||
58 | current = frames.get(frame.call_site?); | ||
59 | token = mapped.value; | ||
60 | } | ||
61 | None | ||
62 | } | ||
63 | } | ||
64 | |||
65 | #[derive(Clone, Debug, Copy)] | ||
66 | struct HygieneFrameId(usize); | ||
67 | |||
68 | #[derive(Clone, Debug, Default)] | ||
69 | struct HygieneFrames(Vec<HygieneFrame>); | ||
70 | |||
71 | #[derive(Clone, Debug)] | ||
72 | struct HygieneFrame { | ||
73 | expansion: Option<ExpansionInfo>, | ||
19 | 74 | ||
20 | // Indicate this is a local inner macro | 75 | // Indicate this is a local inner macro |
21 | local_inner: bool, | 76 | local_inner: bool, |
77 | krate: Option<CrateId>, | ||
78 | |||
79 | call_site: Option<HygieneFrameId>, | ||
80 | def_site: Option<HygieneFrameId>, | ||
22 | } | 81 | } |
23 | 82 | ||
24 | impl Hygiene { | 83 | impl HygieneFrames { |
25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | 84 | fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { |
26 | let (def_crate, local_inner) = match file_id.0 { | 85 | let mut frames = HygieneFrames::default(); |
86 | frames.add(db, file_id); | ||
87 | frames | ||
88 | } | ||
89 | |||
90 | fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<HygieneFrameId> { | ||
91 | let (krate, local_inner) = match file_id.0 { | ||
27 | HirFileIdRepr::FileId(_) => (None, false), | 92 | HirFileIdRepr::FileId(_) => (None, false), |
28 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | 93 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { |
94 | MacroCallId::EagerMacro(_id) => (None, false), | ||
29 | MacroCallId::LazyMacro(id) => { | 95 | MacroCallId::LazyMacro(id) => { |
30 | let loc = db.lookup_intern_macro(id); | 96 | let loc = db.lookup_intern_macro(id); |
31 | match loc.def.kind { | 97 | match loc.def.kind { |
@@ -36,31 +102,72 @@ impl Hygiene { | |||
36 | MacroDefKind::ProcMacro(_) => (None, false), | 102 | MacroDefKind::ProcMacro(_) => (None, false), |
37 | } | 103 | } |
38 | } | 104 | } |
39 | MacroCallId::EagerMacro(_id) => (None, false), | ||
40 | }, | 105 | }, |
41 | }; | 106 | }; |
42 | Hygiene { def_crate, local_inner } | ||
43 | } | ||
44 | 107 | ||
45 | pub fn new_unhygienic() -> Hygiene { | 108 | let expansion = file_id.expansion_info(db); |
46 | Hygiene { def_crate: None, local_inner: false } | 109 | let expansion = match expansion { |
110 | None => { | ||
111 | let idx = self.0.len(); | ||
112 | self.0.push(HygieneFrame { | ||
113 | expansion: None, | ||
114 | local_inner, | ||
115 | krate, | ||
116 | call_site: None, | ||
117 | def_site: None, | ||
118 | }); | ||
119 | return Some(HygieneFrameId(idx)); | ||
120 | } | ||
121 | Some(it) => it, | ||
122 | }; | ||
123 | |||
124 | let def_site = expansion.def.clone(); | ||
125 | let call_site = expansion.arg.file_id; | ||
126 | |||
127 | let idx = self.0.len(); | ||
128 | self.0.push(HygieneFrame { | ||
129 | expansion: Some(expansion), | ||
130 | local_inner, | ||
131 | krate, | ||
132 | call_site: None, | ||
133 | def_site: None, | ||
134 | }); | ||
135 | |||
136 | self.0[idx].call_site = self.add(db, call_site); | ||
137 | self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id)); | ||
138 | |||
139 | Some(HygieneFrameId(idx)) | ||
47 | } | 140 | } |
48 | 141 | ||
49 | // FIXME: this should just return name | 142 | fn get(&self, id: HygieneFrameId) -> Option<&HygieneFrame> { |
50 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 143 | self.0.get(id.0) |
51 | if let Some(def_crate) = self.def_crate { | ||
52 | if name_ref.text() == "$crate" { | ||
53 | return Either::Right(def_crate); | ||
54 | } | ||
55 | } | ||
56 | Either::Left(name_ref.as_name()) | ||
57 | } | 144 | } |
58 | 145 | ||
59 | pub fn local_inner_macros(&self) -> Option<CrateId> { | 146 | fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> { |
60 | if self.local_inner { | 147 | let mut token = name_ref.syntax().first_token()?; |
61 | self.def_crate | 148 | let first = self.0.first()?; |
62 | } else { | 149 | let mut result = first.krate; |
63 | None | 150 | let mut current = Some(first); |
151 | |||
152 | while let Some((frame, (mapped, origin))) = | ||
153 | current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) | ||
154 | { | ||
155 | result = frame.krate; | ||
156 | |||
157 | let site = match origin { | ||
158 | Origin::Def => frame.def_site, | ||
159 | Origin::Call => frame.call_site, | ||
160 | }; | ||
161 | |||
162 | let site = match site { | ||
163 | None => break, | ||
164 | Some(it) => it, | ||
165 | }; | ||
166 | |||
167 | current = self.get(site); | ||
168 | token = mapped.value; | ||
64 | } | 169 | } |
170 | |||
171 | result | ||
65 | } | 172 | } |
66 | } | 173 | } |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 3fa1b1d77..5b6734a5f 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -340,11 +340,8 @@ impl ExpansionInfo { | |||
340 | Some(self.expanded.with_value(token)) | 340 | Some(self.expanded.with_value(token)) |
341 | } | 341 | } |
342 | 342 | ||
343 | pub fn map_token_up( | 343 | pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> { |
344 | &self, | 344 | let token_id = self.exp_map.token_by_range(token.text_range())?; |
345 | token: InFile<&SyntaxToken>, | ||
346 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | ||
347 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | ||
348 | 345 | ||
349 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 346 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
350 | let (token_map, tt) = match origin { | 347 | let (token_map, tt) = match origin { |
@@ -359,7 +356,7 @@ impl ExpansionInfo { | |||
359 | ), | 356 | ), |
360 | }; | 357 | }; |
361 | 358 | ||
362 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 359 | let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?; |
363 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) | 360 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
364 | .into_token()?; | 361 | .into_token()?; |
365 | Some((tt.with_value(token), origin)) | 362 | Some((tt.with_value(token), origin)) |
@@ -495,7 +492,7 @@ fn ascend_call_token( | |||
495 | expansion: &ExpansionInfo, | 492 | expansion: &ExpansionInfo, |
496 | token: InFile<SyntaxToken>, | 493 | token: InFile<SyntaxToken>, |
497 | ) -> Option<InFile<SyntaxToken>> { | 494 | ) -> Option<InFile<SyntaxToken>> { |
498 | let (mapped, origin) = expansion.map_token_up(token.as_ref())?; | 495 | let (mapped, origin) = expansion.map_token_up(&token.value)?; |
499 | if origin != Origin::Call { | 496 | if origin != Origin::Call { |
500 | return None; | 497 | return None; |
501 | } | 498 | } |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index a7656b864..23b79abc4 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/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 | } |