//!
//! 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 arena::{Arena, Idx};
use base_db::CrateId;
use either::Either;
-use mbe::Origin;
-use syntax::{ast, AstNode};
+use syntax::ast;
use crate::{
db::AstDatabase,
name::{AsName, Name},
- ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
+ HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
};
#[derive(Clone, Debug)]
pub struct Hygiene {
- frames: Option<Arc<HygieneFrames>>,
-}
-
-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<Name, CrateId> {
- 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<CrateId> {
- let frames = self.frames.as_ref()?;
-
- let mut token = path.syntax().first_token()?;
- let mut current = frames.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 = Some(&frames.0[frame.call_site?]);
- token = mapped.value;
- }
- None
- }
-}
-
-#[derive(Default, Debug)]
-struct HygieneFrames(Arena<HygieneFrame>);
-
-#[derive(Clone, Debug)]
-struct HygieneFrame {
- expansion: Option<ExpansionInfo>,
+ // This is what `$crate` expands to
+ def_crate: Option<CrateId>,
// Indicate this is a local inner macro
local_inner: bool,
- krate: Option<CrateId>,
-
- call_site: Option<Idx<HygieneFrame>>,
- def_site: Option<Idx<HygieneFrame>>,
}
-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<Idx<HygieneFrame>> {
- let (krate, local_inner) = match file_id.0 {
+impl Hygiene {
+ pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
+ let (def_crate, 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 {
MacroDefKind::ProcMacro(_) => (None, false),
}
}
+ MacroCallId::EagerMacro(_id) => (None, false),
},
};
-
- let expansion = file_id.expansion_info(db);
- let expansion = match expansion {
- None => {
- return Some(self.0.alloc(HygieneFrame {
- expansion: None,
- local_inner,
- krate,
- call_site: None,
- def_site: None,
- }));
- }
- Some(it) => it,
- };
-
- let def_site = expansion.def.clone();
- let call_site = expansion.arg.file_id;
- let idx = self.0.alloc(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(idx)
+ Hygiene { def_crate, local_inner }
}
- fn first(&self) -> Option<&HygieneFrame> {
- self.0.iter().next().map(|it| it.1)
+ pub fn new_unhygienic() -> Hygiene {
+ Hygiene { def_crate: None, local_inner: false }
}
- fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
- let mut token = name_ref.syntax().first_token()?;
- let first = self.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 = Some(&self.0[site]);
- token = mapped.value;
+ // FIXME: this should just return name
+ pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
+ if let Some(def_crate) = self.def_crate {
+ if name_ref.text() == "$crate" {
+ return Either::Right(def_crate);
+ }
}
+ Either::Left(name_ref.as_name())
+ }
- result
+ pub fn local_inner_macros(&self) -> Option<CrateId> {
+ if self.local_inner {
+ self.def_crate
+ } else {
+ None
+ }
}
}
Some(self.expanded.with_value(token))
}
- pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> {
- let token_id = self.exp_map.token_by_range(token.text_range())?;
+ pub fn map_token_up(
+ &self,
+ token: InFile<&SyntaxToken>,
+ ) -> Option<(InFile<SyntaxToken>, Origin)> {
+ let token_id = self.exp_map.token_by_range(token.value.text_range())?;
let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
let (token_map, tt) = match origin {
),
};
- let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?;
+ let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
.into_token()?;
Some((tt.with_value(token), origin))
expansion: &ExpansionInfo,
token: InFile<SyntaxToken>,
) -> Option<InFile<SyntaxToken>> {
- let (mapped, origin) = expansion.map_token_up(&token.value)?;
+ let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
if origin != Origin::Call {
return None;
}
err = err.or(e);
arena.push(tt.into());
}
- Op::Var { name, id, .. } => {
- let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id);
+ Op::Var { name, .. } => {
+ let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name);
err = err.or(e);
push_fragment(arena, fragment);
}
ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
}
-fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> {
+fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
if v == "crate" {
// We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
- let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into();
+ let tt =
+ tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
+ .into();
ExpandResult::ok(Fragment::Tokens(tt))
} else if !ctx.bindings.contains(v) {
// Note that it is possible to have a `$var` inside a macro which is not bound.
let tt = tt::Subtree {
delimiter: None,
token_trees: vec![
- tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(),
- tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(),
+ tt::Leaf::from(tt::Punct {
+ char: '$',
+ spacing: tt::Spacing::Alone,
+ id: tt::TokenId::unspecified(),
+ })
+ .into(),
+ tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
+ .into(),
],
}
.into();
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Op {
- Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
+ Var { name: SmolStr, kind: Option<SmolStr> },
Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
Leaf(tt::Leaf),
Subtree(MetaTemplate),
}
let name = UNDERSCORE.clone();
let kind = eat_fragment_kind(src, mode)?;
- let id = punct.id;
- Op::Var { name, kind, id }
+ Op::Var { name, kind }
}
tt::Leaf::Ident(ident) => {
let name = ident.text.clone();
let kind = eat_fragment_kind(src, mode)?;
- let id = ident.id;
- Op::Var { name, kind, id }
+ Op::Var { name, kind }
}
tt::Leaf::Literal(lit) => {
if is_boolean_literal(&lit) {
let name = lit.text.clone();
let kind = eat_fragment_kind(src, mode)?;
- let id = lit.id;
- Op::Var { name, kind, id }
+ Op::Var { name, kind }
} else {
bail!("bad var 2");
}