aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_def/src/path/lower.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs151
-rw-r--r--crates/hir_expand/src/lib.rs11
-rw-r--r--crates/hir_ty/src/tests/macros.rs31
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs2
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs20
-rw-r--r--crates/mbe/src/parser.rs11
7 files changed, 52 insertions, 176 deletions
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 9518ac109..8a01e6eea 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(path) { 126 if let Some(crate_id) = hygiene.local_inner_macros() {
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 6042e15b2..7ab0a5e52 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,94 +2,30 @@
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 std::sync::Arc;
6
7use arena::{Arena, Idx};
8use base_db::CrateId; 5use base_db::CrateId;
9use either::Either; 6use either::Either;
10use mbe::Origin; 7use syntax::ast;
11use syntax::{ast, AstNode};
12 8
13use crate::{ 9use crate::{
14 db::AstDatabase, 10 db::AstDatabase,
15 name::{AsName, Name}, 11 name::{AsName, Name},
16 ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
17}; 13};
18 14
19#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
20pub struct Hygiene { 16pub struct Hygiene {
21 frames: Option<Arc<HygieneFrames>>, 17 // This is what `$crate` expands to
22} 18 def_crate: Option<CrateId>,
23
24impl Hygiene {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
26 Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
27 }
28
29 pub fn new_unhygienic() -> Hygiene {
30 Hygiene { frames: None }
31 }
32
33 // FIXME: this should just return name
34 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
35 if let Some(frames) = &self.frames {
36 if name_ref.text() == "$crate" {
37 if let Some(krate) = frames.root_crate(&name_ref) {
38 return Either::Right(krate);
39 }
40 }
41 }
42
43 Either::Left(name_ref.as_name())
44 }
45
46 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
47 let frames = self.frames.as_ref()?;
48
49 let mut token = path.syntax().first_token()?;
50 let mut current = frames.first();
51
52 while let Some((frame, data)) =
53 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
54 {
55 let (mapped, origin) = data;
56 if origin == Origin::Def {
57 return if frame.local_inner { frame.krate } else { None };
58 }
59 current = Some(&frames.0[frame.call_site?]);
60 token = mapped.value;
61 }
62 None
63 }
64}
65
66#[derive(Default, Debug)]
67struct HygieneFrames(Arena<HygieneFrame>);
68
69#[derive(Clone, Debug)]
70struct HygieneFrame {
71 expansion: Option<ExpansionInfo>,
72 19
73 // Indicate this is a local inner macro 20 // Indicate this is a local inner macro
74 local_inner: bool, 21 local_inner: bool,
75 krate: Option<CrateId>,
76
77 call_site: Option<Idx<HygieneFrame>>,
78 def_site: Option<Idx<HygieneFrame>>,
79} 22}
80 23
81impl HygieneFrames { 24impl Hygiene {
82 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { 25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
83 let mut frames = HygieneFrames::default(); 26 let (def_crate, local_inner) = match file_id.0 {
84 frames.add(db, file_id);
85 frames
86 }
87
88 fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<Idx<HygieneFrame>> {
89 let (krate, local_inner) = match file_id.0 {
90 HirFileIdRepr::FileId(_) => (None, false), 27 HirFileIdRepr::FileId(_) => (None, false),
91 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { 28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
92 MacroCallId::EagerMacro(_id) => (None, false),
93 MacroCallId::LazyMacro(id) => { 29 MacroCallId::LazyMacro(id) => {
94 let loc = db.lookup_intern_macro(id); 30 let loc = db.lookup_intern_macro(id);
95 match loc.def.kind { 31 match loc.def.kind {
@@ -100,68 +36,31 @@ impl HygieneFrames {
100 MacroDefKind::ProcMacro(_) => (None, false), 36 MacroDefKind::ProcMacro(_) => (None, false),
101 } 37 }
102 } 38 }
39 MacroCallId::EagerMacro(_id) => (None, false),
103 }, 40 },
104 }; 41 };
105 42 Hygiene { def_crate, local_inner }
106 let expansion = file_id.expansion_info(db);
107 let expansion = match expansion {
108 None => {
109 return Some(self.0.alloc(HygieneFrame {
110 expansion: None,
111 local_inner,
112 krate,
113 call_site: None,
114 def_site: None,
115 }));
116 }
117 Some(it) => it,
118 };
119
120 let def_site = expansion.def.clone();
121 let call_site = expansion.arg.file_id;
122 let idx = self.0.alloc(HygieneFrame {
123 expansion: Some(expansion),
124 local_inner,
125 krate,
126 call_site: None,
127 def_site: None,
128 });
129
130 self.0[idx].call_site = self.add(db, call_site);
131 self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));
132
133 Some(idx)
134 } 43 }
135 44
136 fn first(&self) -> Option<&HygieneFrame> { 45 pub fn new_unhygienic() -> Hygiene {
137 self.0.iter().next().map(|it| it.1) 46 Hygiene { def_crate: None, local_inner: false }
138 } 47 }
139 48
140 fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> { 49 // FIXME: this should just return name
141 let mut token = name_ref.syntax().first_token()?; 50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
142 let first = self.first()?; 51 if let Some(def_crate) = self.def_crate {
143 let mut result = first.krate; 52 if name_ref.text() == "$crate" {
144 let mut current = Some(first); 53 return Either::Right(def_crate);
145 54 }
146 while let Some((frame, (mapped, origin))) =
147 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
148 {
149 result = frame.krate;
150
151 let site = match origin {
152 Origin::Def => frame.def_site,
153 Origin::Call => frame.call_site,
154 };
155
156 let site = match site {
157 None => break,
158 Some(it) => it,
159 };
160
161 current = Some(&self.0[site]);
162 token = mapped.value;
163 } 55 }
56 Either::Left(name_ref.as_name())
57 }
164 58
165 result 59 pub fn local_inner_macros(&self) -> Option<CrateId> {
60 if self.local_inner {
61 self.def_crate
62 } else {
63 None
64 }
166 } 65 }
167} 66}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 5b6734a5f..3fa1b1d77 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -340,8 +340,11 @@ 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(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> { 343 pub fn map_token_up(
344 let token_id = self.exp_map.token_by_range(token.text_range())?; 344 &self,
345 token: InFile<&SyntaxToken>,
346 ) -> Option<(InFile<SyntaxToken>, Origin)> {
347 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
345 348
346 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 349 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
347 let (token_map, tt) = match origin { 350 let (token_map, tt) = match origin {
@@ -356,7 +359,7 @@ impl ExpansionInfo {
356 ), 359 ),
357 }; 360 };
358 361
359 let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?; 362 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
360 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) 363 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
361 .into_token()?; 364 .into_token()?;
362 Some((tt.with_value(token), origin)) 365 Some((tt.with_value(token), origin))
@@ -492,7 +495,7 @@ fn ascend_call_token(
492 expansion: &ExpansionInfo, 495 expansion: &ExpansionInfo,
493 token: InFile<SyntaxToken>, 496 token: InFile<SyntaxToken>,
494) -> Option<InFile<SyntaxToken>> { 497) -> Option<InFile<SyntaxToken>> {
495 let (mapped, origin) = expansion.map_token_up(&token.value)?; 498 let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
496 if origin != Origin::Call { 499 if origin != Origin::Call {
497 return None; 500 return None;
498 } 501 }
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index c64f0b5b5..1953da7be 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -371,37 +371,6 @@ expand!();
371} 371}
372 372
373#[test] 373#[test]
374fn infer_macro_with_dollar_crate_in_def_site() {
375 check_types(
376 r#"
377//- /main.rs crate:main deps:foo
378use foo::expand;
379
380macro_rules! list {
381 ($($tt:tt)*) => { $($tt)* }
382}
383
384fn test() {
385 let r = expand!();
386 r;
387 //^ u128
388}
389
390//- /lib.rs crate:foo
391#[macro_export]
392macro_rules! expand {
393 () => { list!($crate::m!()) };
394}
395
396#[macro_export]
397macro_rules! m {
398 () => { 0u128 };
399}
400"#,
401 );
402}
403
404#[test]
405fn infer_type_value_non_legacy_macro_use_as() { 374fn infer_type_value_non_legacy_macro_use_as() {
406 check_infer( 375 check_infer(
407 r#" 376 r#"
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 385b46601..ab5f87c48 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 57f3f104d..720531237 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, id, .. } => { 103 Op::Var { name, .. } => {
104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); 104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name);
105 err = err.or(e); 105 err = err.or(e);
106 push_fragment(arena, fragment); 106 push_fragment(arena, fragment);
107 } 107 }
@@ -118,10 +118,12 @@ 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
121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> { 121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> 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 = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into(); 124 let tt =
125 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
126 .into();
125 ExpandResult::ok(Fragment::Tokens(tt)) 127 ExpandResult::ok(Fragment::Tokens(tt))
126 } else if !ctx.bindings.contains(v) { 128 } else if !ctx.bindings.contains(v) {
127 // Note that it is possible to have a `$var` inside a macro which is not bound. 129 // Note that it is possible to have a `$var` inside a macro which is not bound.
@@ -140,8 +142,14 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult
140 let tt = tt::Subtree { 142 let tt = tt::Subtree {
141 delimiter: None, 143 delimiter: None,
142 token_trees: vec![ 144 token_trees: vec![
143 tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(), 145 tt::Leaf::from(tt::Punct {
144 tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(), 146 char: '$',
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(),
145 ], 153 ],
146 } 154 }
147 .into(); 155 .into();
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 77cc739b6..2f3ebc831 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)]
10pub(crate) enum Op { 10pub(crate) enum Op {
11 Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId }, 11 Var { name: SmolStr, kind: Option<SmolStr> },
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,21 +106,18 @@ 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 let id = punct.id; 109 Op::Var { name, kind }
110 Op::Var { name, kind, id }
111 } 110 }
112 tt::Leaf::Ident(ident) => { 111 tt::Leaf::Ident(ident) => {
113 let name = ident.text.clone(); 112 let name = ident.text.clone();
114 let kind = eat_fragment_kind(src, mode)?; 113 let kind = eat_fragment_kind(src, mode)?;
115 let id = ident.id; 114 Op::Var { name, kind }
116 Op::Var { name, kind, id }
117 } 115 }
118 tt::Leaf::Literal(lit) => { 116 tt::Leaf::Literal(lit) => {
119 if is_boolean_literal(&lit) { 117 if is_boolean_literal(&lit) {
120 let name = lit.text.clone(); 118 let name = lit.text.clone();
121 let kind = eat_fragment_kind(src, mode)?; 119 let kind = eat_fragment_kind(src, mode)?;
122 let id = lit.id; 120 Op::Var { name, kind }
123 Op::Var { name, kind, id }
124 } else { 121 } else {
125 bail!("bad var 2"); 122 bail!("bad var 2");
126 } 123 }