From fe5340d970b764175ebf8b8c32c971fa04c49efb Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 2 Jan 2021 20:25:05 +0800 Subject: Introduce HygieneFrames for proper token hyginee --- crates/hir_expand/src/hygiene.rs | 157 ++++++++++++++++++++++++++++++++------- crates/hir_expand/src/lib.rs | 11 +-- 2 files changed, 136 insertions(+), 32 deletions(-) (limited to 'crates/hir_expand') 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 @@ //! //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at //! this moment, this is horribly incomplete and handles only `$crate`. +use std::sync::Arc; + use base_db::CrateId; use either::Either; -use syntax::ast; +use mbe::Origin; +use syntax::{ast, AstNode}; use crate::{ db::AstDatabase, name::{AsName, Name}, - HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, + ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, }; #[derive(Clone, Debug)] pub struct Hygiene { - // This is what `$crate` expands to - def_crate: Option, + frames: Option>, +} + +impl Hygiene { + pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { + Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) } + } + + pub fn new_unhygienic() -> Hygiene { + Hygiene { frames: None } + } + + // FIXME: this should just return name + pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either { + if let Some(frames) = &self.frames { + if name_ref.text() == "$crate" { + if let Some(krate) = frames.root_crate(&name_ref) { + return Either::Right(krate); + } + } + } + + Either::Left(name_ref.as_name()) + } + + pub fn local_inner_macros(&self, path: ast::Path) -> Option { + let frames = self.frames.as_ref()?; + + let mut token = path.syntax().first_token()?; + let mut current = frames.0.first(); + + while let Some((frame, data)) = + current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) + { + let (mapped, origin) = data; + if origin == Origin::Def { + return if frame.local_inner { frame.krate } else { None }; + } + current = frames.get(frame.call_site?); + token = mapped.value; + } + None + } +} + +#[derive(Clone, Debug, Copy)] +struct HygieneFrameId(usize); + +#[derive(Clone, Debug, Default)] +struct HygieneFrames(Vec); + +#[derive(Clone, Debug)] +struct HygieneFrame { + expansion: Option, // Indicate this is a local inner macro local_inner: bool, + krate: Option, + + call_site: Option, + def_site: Option, } -impl Hygiene { - pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { - let (def_crate, local_inner) = match file_id.0 { +impl HygieneFrames { + fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { + let mut frames = HygieneFrames::default(); + frames.add(db, file_id); + frames + } + + fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option { + let (krate, local_inner) = match file_id.0 { HirFileIdRepr::FileId(_) => (None, false), HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { + MacroCallId::EagerMacro(_id) => (None, false), MacroCallId::LazyMacro(id) => { let loc = db.lookup_intern_macro(id); match loc.def.kind { @@ -36,31 +102,72 @@ impl Hygiene { MacroDefKind::ProcMacro(_) => (None, false), } } - MacroCallId::EagerMacro(_id) => (None, false), }, }; - Hygiene { def_crate, local_inner } - } - pub fn new_unhygienic() -> Hygiene { - Hygiene { def_crate: None, local_inner: false } + let expansion = file_id.expansion_info(db); + let expansion = match expansion { + None => { + let idx = self.0.len(); + self.0.push(HygieneFrame { + expansion: None, + local_inner, + krate, + call_site: None, + def_site: None, + }); + return Some(HygieneFrameId(idx)); + } + Some(it) => it, + }; + + let def_site = expansion.def.clone(); + let call_site = expansion.arg.file_id; + + let idx = self.0.len(); + self.0.push(HygieneFrame { + expansion: Some(expansion), + local_inner, + krate, + call_site: None, + def_site: None, + }); + + self.0[idx].call_site = self.add(db, call_site); + self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id)); + + Some(HygieneFrameId(idx)) } - // FIXME: this should just return name - pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either { - if let Some(def_crate) = self.def_crate { - if name_ref.text() == "$crate" { - return Either::Right(def_crate); - } - } - Either::Left(name_ref.as_name()) + fn get(&self, id: HygieneFrameId) -> Option<&HygieneFrame> { + self.0.get(id.0) } - pub fn local_inner_macros(&self) -> Option { - if self.local_inner { - self.def_crate - } else { - None + fn root_crate(&self, name_ref: &ast::NameRef) -> Option { + let mut token = name_ref.syntax().first_token()?; + let first = self.0.first()?; + let mut result = first.krate; + let mut current = Some(first); + + while let Some((frame, (mapped, origin))) = + current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) + { + result = frame.krate; + + let site = match origin { + Origin::Def => frame.def_site, + Origin::Call => frame.call_site, + }; + + let site = match site { + None => break, + Some(it) => it, + }; + + current = self.get(site); + token = mapped.value; } + + result } } 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 { Some(self.expanded.with_value(token)) } - pub fn map_token_up( - &self, - token: InFile<&SyntaxToken>, - ) -> Option<(InFile, Origin)> { - let token_id = self.exp_map.token_by_range(token.value.text_range())?; + pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile, Origin)> { + let token_id = self.exp_map.token_by_range(token.text_range())?; let (token_id, origin) = self.macro_def.0.map_id_up(token_id); let (token_map, tt) = match origin { @@ -359,7 +356,7 @@ impl ExpansionInfo { ), }; - let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; + let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?; let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) .into_token()?; Some((tt.with_value(token), origin)) @@ -495,7 +492,7 @@ fn ascend_call_token( expansion: &ExpansionInfo, token: InFile, ) -> Option> { - let (mapped, origin) = expansion.map_token_up(token.as_ref())?; + let (mapped, origin) = expansion.map_token_up(&token.value)?; if origin != Origin::Call { return None; } -- cgit v1.2.3 From 3545289603dae852fb2b99181e9be5e3117b0a2e Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 2 Jan 2021 22:48:22 +0800 Subject: Use arena instead of vec --- crates/hir_expand/src/hygiene.rs | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'crates/hir_expand') diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index d49a67195..6042e15b2 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -4,6 +4,7 @@ //! this moment, this is horribly incomplete and handles only `$crate`. use std::sync::Arc; +use arena::{Arena, Idx}; use base_db::CrateId; use either::Either; use mbe::Origin; @@ -46,7 +47,7 @@ impl Hygiene { let frames = self.frames.as_ref()?; let mut token = path.syntax().first_token()?; - let mut current = frames.0.first(); + let mut current = frames.first(); while let Some((frame, data)) = current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) @@ -55,18 +56,15 @@ impl Hygiene { if origin == Origin::Def { return if frame.local_inner { frame.krate } else { None }; } - current = frames.get(frame.call_site?); + current = Some(&frames.0[frame.call_site?]); token = mapped.value; } None } } -#[derive(Clone, Debug, Copy)] -struct HygieneFrameId(usize); - -#[derive(Clone, Debug, Default)] -struct HygieneFrames(Vec); +#[derive(Default, Debug)] +struct HygieneFrames(Arena); #[derive(Clone, Debug)] struct HygieneFrame { @@ -76,8 +74,8 @@ struct HygieneFrame { local_inner: bool, krate: Option, - call_site: Option, - def_site: Option, + call_site: Option>, + def_site: Option>, } impl HygieneFrames { @@ -87,7 +85,7 @@ impl HygieneFrames { frames } - fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option { + fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option> { let (krate, local_inner) = match file_id.0 { HirFileIdRepr::FileId(_) => (None, false), HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { @@ -108,24 +106,20 @@ impl HygieneFrames { let expansion = file_id.expansion_info(db); let expansion = match expansion { None => { - let idx = self.0.len(); - self.0.push(HygieneFrame { + return Some(self.0.alloc(HygieneFrame { expansion: None, local_inner, krate, call_site: None, def_site: None, - }); - return Some(HygieneFrameId(idx)); + })); } Some(it) => it, }; let def_site = expansion.def.clone(); let call_site = expansion.arg.file_id; - - let idx = self.0.len(); - self.0.push(HygieneFrame { + let idx = self.0.alloc(HygieneFrame { expansion: Some(expansion), local_inner, krate, @@ -136,16 +130,16 @@ impl HygieneFrames { self.0[idx].call_site = self.add(db, call_site); self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id)); - Some(HygieneFrameId(idx)) + Some(idx) } - fn get(&self, id: HygieneFrameId) -> Option<&HygieneFrame> { - self.0.get(id.0) + fn first(&self) -> Option<&HygieneFrame> { + self.0.iter().next().map(|it| it.1) } fn root_crate(&self, name_ref: &ast::NameRef) -> Option { let mut token = name_ref.syntax().first_token()?; - let first = self.0.first()?; + let first = self.first()?; let mut result = first.krate; let mut current = Some(first); @@ -164,7 +158,7 @@ impl HygieneFrames { Some(it) => it, }; - current = self.get(site); + current = Some(&self.0[site]); token = mapped.value; } -- cgit v1.2.3