aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_expand')
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs3
-rw-r--r--crates/hir_expand/src/db.rs16
-rw-r--r--crates/hir_expand/src/eager.rs6
-rw-r--r--crates/hir_expand/src/hygiene.rs210
-rw-r--r--crates/hir_expand/src/name.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs10
7 files changed, 204 insertions, 45 deletions
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index ad378762a..eb257579f 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -277,7 +277,7 @@ mod tests {
277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); 277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
278 let fixture = format!( 278 let fixture = format!(
279 r#"//- /main.rs crate:main deps:core 279 r#"//- /main.rs crate:main deps:core
280<|> 280$0
281{} 281{}
282//- /lib.rs crate:core 282//- /lib.rs crate:core
283// empty 283// empty
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 6382521fb..80b60d59f 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -259,7 +259,8 @@ fn format_args_expand(
259 } 259 }
260 for arg in &mut args { 260 for arg in &mut args {
261 // Remove `key =`. 261 // Remove `key =`.
262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { 262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
263 {
263 arg.drain(..2); 264 arg.drain(..2);
264 } 265 }
265 } 266 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 06f0a3ed9..c62086390 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -8,9 +8,9 @@ use parser::FragmentKind;
8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
10use crate::{ 10use 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.
@@ -40,7 +40,7 @@ impl TokenExpander {
40 // FIXME switch these to ExpandResult as well 40 // FIXME switch these to ExpandResult as well
41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
42 TokenExpander::ProcMacro(_) => { 42 TokenExpander::ProcMacro(_) => {
43 // We store the result in salsa db to prevent non-determinisc behavior in 43 // We store the result in salsa db to prevent non-deterministic behavior in
44 // some proc-macro implementation 44 // some proc-macro implementation
45 // See #4315 for details 45 // See #4315 for details
46 db.expand_proc_macro(id.into()).into() 46 db.expand_proc_macro(id.into()).into()
@@ -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
374fn 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
374fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { 380fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
@@ -404,7 +410,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
404 TRY_EXPR => FragmentKind::Expr, 410 TRY_EXPR => FragmentKind::Expr,
405 TUPLE_EXPR => FragmentKind::Expr, 411 TUPLE_EXPR => FragmentKind::Expr,
406 PAREN_EXPR => FragmentKind::Expr, 412 PAREN_EXPR => FragmentKind::Expr,
407 413 ARRAY_EXPR => FragmentKind::Expr,
408 FOR_EXPR => FragmentKind::Expr, 414 FOR_EXPR => FragmentKind::Expr,
409 PATH_EXPR => FragmentKind::Expr, 415 PATH_EXPR => FragmentKind::Expr,
410 CLOSURE_EXPR => FragmentKind::Expr, 416 CLOSURE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 6354b090d..ae7b51a08 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -218,6 +218,12 @@ fn eager_macro_recur(
218 } 218 }
219 }; 219 };
220 220
221 // check if the whole original sytnax is replaced
222 // Note that SyntaxRewriter cannot replace the root node itself
223 if child.syntax() == &original {
224 return Ok(insert);
225 }
226
221 rewriter.replace(child.syntax(), &insert); 227 rewriter.replace(child.syntax(), &insert);
222 } 228 }
223 229
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`.
5use std::sync::Arc;
6
5use base_db::CrateId; 7use base_db::CrateId;
6use either::Either; 8use either::Either;
7use syntax::ast; 9use mbe::Origin;
10use parser::SyntaxKind;
11use syntax::{ast, AstNode, SyntaxNode, TextRange, TextSize};
8 12
9use crate::{ 13use 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)]
16pub struct Hygiene { 20pub 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
24impl Hygiene { 24impl 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)]
63struct HygieneFrames(Arc<HygieneFrame>);
64
65#[derive(Clone, Debug, Eq, PartialEq)]
66pub 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
77impl 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 => &current.def_site,
94 Origin::Call => &current.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)]
111struct 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
121impl 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
142fn 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
170impl 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_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 2f44876a8..95d853b6d 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -164,6 +164,7 @@ pub mod known {
164 future, 164 future,
165 result, 165 result,
166 boxed, 166 boxed,
167 option,
167 // Components of known path (type name) 168 // Components of known path (type name)
168 Iterator, 169 Iterator,
169 IntoIterator, 170 IntoIterator,
@@ -172,6 +173,7 @@ pub mod known {
172 Ok, 173 Ok,
173 Future, 174 Future,
174 Result, 175 Result,
176 Option,
175 Output, 177 Output,
176 Target, 178 Target,
177 Box, 179 Box,
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7c77f6ce0..1923daca5 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -58,7 +58,7 @@ impl ProcMacroExpander {
58} 58}
59 59
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool { 60fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
61 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() { 61 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
62 if punct.char == c { 62 if punct.char == c {
63 *cursor = cursor.bump(); 63 *cursor = cursor.bump();
64 return true; 64 return true;
@@ -68,7 +68,7 @@ fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
68} 68}
69 69
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { 70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
71 if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() { 71 if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
72 if Some(kind) == subtree.delimiter_kind() { 72 if Some(kind) == subtree.delimiter_kind() {
73 *cursor = cursor.bump_subtree(); 73 *cursor = cursor.bump_subtree();
74 return true; 74 return true;
@@ -78,7 +78,7 @@ fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
78} 78}
79 79
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { 80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
81 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() { 81 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
82 if t == ident.text.as_str() { 82 if t == ident.text.as_str() {
83 *cursor = cursor.bump(); 83 *cursor = cursor.bump();
84 return true; 84 return true;
@@ -88,7 +88,7 @@ fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
88} 88}
89 89
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { 90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
91 let buffer = TokenBuffer::new(&tt.token_trees); 91 let buffer = TokenBuffer::from_tokens(&tt.token_trees);
92 let mut p = buffer.begin(); 92 let mut p = buffer.begin();
93 let mut result = tt::Subtree::default(); 93 let mut result = tt::Subtree::default();
94 94
@@ -106,7 +106,7 @@ fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
106 } 106 }
107 } 107 }
108 108
109 result.token_trees.push(curr.token_tree()?.clone()); 109 result.token_trees.push(curr.token_tree()?.cloned());
110 p = curr.bump(); 110 p = curr.bump();
111 } 111 }
112 112