Implement concat macro

This commit is contained in:
Edwin Cheng 2020-03-02 14:05:15 +08:00
parent 0d55454073
commit 1465cc0c4f
10 changed files with 305 additions and 77 deletions

View file

@ -47,8 +47,8 @@ mod marks;
use std::hash::Hash; use std::hash::Hash;
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile, ast_id_map::FileAstId, db::AstDatabase, eager::expand_eager_macro, hygiene::Hygiene, AstId,
MacroCallId, MacroCallKind, MacroDefId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
}; };
use ra_arena::{impl_arena_id, RawId}; use ra_arena::{impl_arena_id, RawId};
use ra_db::{impl_intern_key, salsa, CrateId}; use ra_db::{impl_intern_key, salsa, CrateId};
@ -459,8 +459,21 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
db: &impl AstDatabase, db: &impl AstDatabase,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> { ) -> Option<MacroCallId> {
let def = resolver(self.path.clone())?; let def: MacroDefId = resolver(self.path.clone())?;
Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id)))
if let MacroDefKind::BuiltInEager(_) = def.kind {
let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db));
let hygiene = Hygiene::new(db, self.ast_id.file_id);
Some(
expand_eager_macro(db, macro_call, def, &|path: ast::Path| {
resolver(path::ModPath::from_src(path, &hygiene)?)
})?
.into(),
)
} else {
Some(def.as_lazy_macro(db, MacroCallKind::FnLike(self.ast_id)).into())
}
} }
} }
@ -471,6 +484,6 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> { ) -> Option<MacroCallId> {
let def = resolver(self.path.clone())?; let def = resolver(self.path.clone())?;
Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id))) Some(def.as_lazy_macro(db, MacroCallKind::Attr(self.ast_id)).into())
} }
} }

View file

@ -9,7 +9,7 @@ use ra_syntax::{
}; };
use crate::db::AstDatabase; use crate::db::AstDatabase;
use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind}; use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind};
macro_rules! register_builtin { macro_rules! register_builtin {
( $($trait:ident => $expand:ident),* ) => { ( $($trait:ident => $expand:ident),* ) => {
@ -22,7 +22,7 @@ macro_rules! register_builtin {
pub fn expand( pub fn expand(
&self, &self,
db: &dyn AstDatabase, db: &dyn AstDatabase,
id: MacroCallId, id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
let expander = match *self { let expander = match *self {
@ -155,7 +155,7 @@ fn expand_simple_derive(
fn copy_expand( fn copy_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::marker::Copy }) expand_simple_derive(tt, quote! { std::marker::Copy })
@ -163,7 +163,7 @@ fn copy_expand(
fn clone_expand( fn clone_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::clone::Clone }) expand_simple_derive(tt, quote! { std::clone::Clone })
@ -171,7 +171,7 @@ fn clone_expand(
fn default_expand( fn default_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::default::Default }) expand_simple_derive(tt, quote! { std::default::Default })
@ -179,7 +179,7 @@ fn default_expand(
fn debug_expand( fn debug_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::fmt::Debug }) expand_simple_derive(tt, quote! { std::fmt::Debug })
@ -187,7 +187,7 @@ fn debug_expand(
fn hash_expand( fn hash_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::hash::Hash }) expand_simple_derive(tt, quote! { std::hash::Hash })
@ -195,7 +195,7 @@ fn hash_expand(
fn eq_expand( fn eq_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::cmp::Eq }) expand_simple_derive(tt, quote! { std::cmp::Eq })
@ -203,7 +203,7 @@ fn eq_expand(
fn partial_eq_expand( fn partial_eq_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::cmp::PartialEq }) expand_simple_derive(tt, quote! { std::cmp::PartialEq })
@ -211,7 +211,7 @@ fn partial_eq_expand(
fn ord_expand( fn ord_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::cmp::Ord }) expand_simple_derive(tt, quote! { std::cmp::Ord })
@ -219,7 +219,7 @@ fn ord_expand(
fn partial_ord_expand( fn partial_ord_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
@ -228,7 +228,7 @@ fn partial_ord_expand(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc}; use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
use ra_db::{fixture::WithFixture, SourceDatabase}; use ra_db::{fixture::WithFixture, SourceDatabase};
fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String {

View file

@ -1,24 +1,31 @@
//! Builtin macro //! Builtin macro
use crate::db::AstDatabase; use crate::db::AstDatabase;
use crate::{ use crate::{
ast::{self}, ast::{self, AstToken, HasStringValue},
name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind, TextUnit, name, AstId, CrateId, MacroDefId, MacroDefKind, TextUnit,
}; };
use crate::quote; use crate::{quote, LazyMacroId};
use either::Either;
use ra_parser::FragmentKind;
macro_rules! register_builtin { macro_rules! register_builtin {
( $(($name:ident, $kind: ident) => $expand:ident),* ) => { ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinFnLikeExpander { pub enum BuiltinFnLikeExpander {
$($kind),* $($kind),*
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EagerExpander {
$($e_kind),*
}
impl BuiltinFnLikeExpander { impl BuiltinFnLikeExpander {
pub fn expand( pub fn expand(
&self, &self,
db: &dyn AstDatabase, db: &dyn AstDatabase,
id: MacroCallId, id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
let expander = match *self { let expander = match *self {
@ -26,28 +33,54 @@ macro_rules! register_builtin {
}; };
expander(db, id, tt) expander(db, id, tt)
} }
}
fn by_name(ident: &name::Name) -> Option<BuiltinFnLikeExpander> { impl EagerExpander {
pub fn expand(
&self,
tt: &tt::Subtree,
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
let expander = match *self {
$( EagerExpander::$e_kind => $e_expand, )*
};
expander(tt)
}
}
fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> {
match ident { match ident {
$( id if id == &name::name![$name] => Some(BuiltinFnLikeExpander::$kind), )* $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
$( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )*
_ => return None, _ => return None,
} }
} }
}
pub fn find_builtin_macro(
ident: &name::Name,
krate: CrateId,
ast_id: AstId<ast::MacroCall>,
) -> Option<MacroDefId> {
let kind = BuiltinFnLikeExpander::by_name(ident)?;
Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) })
}
}; };
} }
pub fn find_builtin_macro(
ident: &name::Name,
krate: CrateId,
ast_id: AstId<ast::MacroCall>,
) -> Option<MacroDefId> {
let kind = find_by_name(ident)?;
match kind {
Either::Left(kind) => Some(MacroDefId {
krate: Some(krate),
ast_id: Some(ast_id),
kind: MacroDefKind::BuiltIn(kind),
}),
Either::Right(kind) => Some(MacroDefId {
krate: Some(krate),
ast_id: Some(ast_id),
kind: MacroDefKind::BuiltInEager(kind),
}),
}
}
register_builtin! { register_builtin! {
LAZY:
(column, Column) => column_expand, (column, Column) => column_expand,
(compile_error, CompileError) => compile_error_expand, (compile_error, CompileError) => compile_error_expand,
(file, File) => file_expand, (file, File) => file_expand,
@ -58,12 +91,16 @@ register_builtin! {
(option_env, OptionEnv) => option_env_expand, (option_env, OptionEnv) => option_env_expand,
// format_args_nl only differs in that it adds a newline in the end, // format_args_nl only differs in that it adds a newline in the end,
// so we use the same stub expansion for now // so we use the same stub expansion for now
(format_args_nl, FormatArgsNl) => format_args_expand (format_args_nl, FormatArgsNl) => format_args_expand,
EAGER:
// eagers
(concat, Concat) => concat_expand
} }
fn line_expand( fn line_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
@ -77,13 +114,9 @@ fn line_expand(
fn stringify_expand( fn stringify_expand(
db: &dyn AstDatabase, db: &dyn AstDatabase,
id: MacroCallId, id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
let id = match id {
MacroCallId::LazyMacro(id) => id,
};
let loc = db.lookup_intern_macro(id); let loc = db.lookup_intern_macro(id);
let macro_content = { let macro_content = {
@ -103,7 +136,7 @@ fn stringify_expand(
fn env_expand( fn env_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
@ -114,7 +147,7 @@ fn env_expand(
fn option_env_expand( fn option_env_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
@ -125,7 +158,7 @@ fn option_env_expand(
fn column_expand( fn column_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
@ -139,7 +172,7 @@ fn column_expand(
fn file_expand( fn file_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// FIXME: RA purposefully lacks knowledge of absolute file names // FIXME: RA purposefully lacks knowledge of absolute file names
@ -155,7 +188,7 @@ fn file_expand(
fn compile_error_expand( fn compile_error_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
if tt.count() == 1 { if tt.count() == 1 {
@ -172,7 +205,7 @@ fn compile_error_expand(
fn format_args_expand( fn format_args_expand(
_db: &dyn AstDatabase, _db: &dyn AstDatabase,
_id: MacroCallId, _id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
// We expand `format_args!("", a1, a2)` to // We expand `format_args!("", a1, a2)` to
@ -212,23 +245,44 @@ fn format_args_expand(
Ok(expanded) Ok(expanded)
} }
fn unquote_str(lit: &tt::Literal) -> Option<String> {
let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::String::cast(lit)?;
token.value()
}
fn concat_expand(tt: &tt::Subtree) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
let mut text = String::new();
for (i, t) in tt.token_trees.iter().enumerate() {
match t {
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?;
}
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
_ => return Err(mbe::ExpandError::UnexpectedToken),
}
}
Ok((quote!(#text), FragmentKind::Expr))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallKind, MacroCallLoc}; use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallId, MacroCallKind, MacroCallLoc};
use ra_db::{fixture::WithFixture, SourceDatabase}; use ra_db::{fixture::WithFixture, SourceDatabase};
use ra_syntax::ast::NameOwner; use ra_syntax::ast::NameOwner;
fn expand_builtin_macro(s: &str) -> String { fn expand_builtin_macro(ra_fixture: &str) -> String {
let (db, file_id) = TestDB::with_single_file(&s); let (db, file_id) = TestDB::with_single_file(&ra_fixture);
let parsed = db.parse(file_id); let parsed = db.parse(file_id);
let macro_calls: Vec<_> = let macro_calls: Vec<_> =
parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect();
let ast_id_map = db.ast_id_map(file_id.into()); let ast_id_map = db.ast_id_map(file_id.into());
let expander = let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
BuiltinFnLikeExpander::by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); let expander = expander.left().unwrap();
// the first one should be a macro_rules // the first one should be a macro_rules
let def = MacroDefId { let def = MacroDefId {

View file

@ -9,8 +9,9 @@ use ra_prof::profile;
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
use crate::{ use crate::{
ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
MacroFile,
}; };
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -24,7 +25,7 @@ impl TokenExpander {
pub fn expand( pub fn expand(
&self, &self,
db: &dyn AstDatabase, db: &dyn AstDatabase,
id: MacroCallId, id: LazyMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> { ) -> Result<tt::Subtree, mbe::ExpandError> {
match self { match self {
@ -66,6 +67,9 @@ pub trait AstDatabase: SourceDatabase {
fn parse_macro(&self, macro_file: MacroFile) fn parse_macro(&self, macro_file: MacroFile)
-> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
#[salsa::interned]
fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
} }
pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
@ -101,6 +105,7 @@ pub(crate) fn macro_def(
MacroDefKind::BuiltInDerive(expander) => { MacroDefKind::BuiltInDerive(expander) => {
Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
} }
MacroDefKind::BuiltInEager(_expander) => None,
} }
} }
@ -110,6 +115,10 @@ pub(crate) fn macro_arg(
) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
let id = match id { let id = match id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(_id) => {
// FIXME: support macro_arg for eager macro
return None;
}
}; };
let loc = db.lookup_intern_macro(id); let loc = db.lookup_intern_macro(id);
let arg = loc.kind.arg(db)?; let arg = loc.kind.arg(db)?;
@ -123,13 +132,16 @@ pub(crate) fn macro_expand(
) -> Result<Arc<tt::Subtree>, String> { ) -> Result<Arc<tt::Subtree>, String> {
let lazy_id = match id { let lazy_id = match id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(id) => {
return Ok(db.lookup_intern_eager_expansion(id).subtree);
}
}; };
let loc = db.lookup_intern_macro(lazy_id); let loc = db.lookup_intern_macro(lazy_id);
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
let tt = macro_rules.0.expand(db, id, &macro_arg.0).map_err(|err| format!("{:?}", err))?; let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
// Set a hard limit for the expanded tt // Set a hard limit for the expanded tt
let count = tt.count(); let count = tt.count();
if count > 65536 { if count > 65536 {
@ -177,6 +189,9 @@ pub(crate) fn parse_macro(
fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
let lazy_id = match id { let lazy_id = match id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(id) => {
return db.lookup_intern_eager_expansion(id).fragment;
}
}; };
let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value; let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;

View file

@ -0,0 +1,93 @@
//! Eager expansion related utils
use crate::{
ast::{self, AstNode},
db::AstDatabase,
EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
};
use ra_parser::FragmentKind;
use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode};
use std::{collections::HashMap, sync::Arc};
fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> {
let mut subtree = mbe::syntax_node_to_token_tree(node)?.0;
subtree.delimiter = None;
Some(subtree)
}
fn lazy_expand(
db: &impl AstDatabase,
def: &MacroDefId,
macro_call: InFile<ast::MacroCall>,
) -> Option<InFile<SyntaxNode>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let id: MacroCallId =
def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
}
fn eager_macro_recur(
db: &impl AstDatabase,
curr: InFile<SyntaxNode>,
macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
) -> Option<SyntaxNode> {
let mut original = curr.value.clone();
let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default();
// Collect replacement
for child in children {
let def: MacroDefId = macro_resolver(child.path()?)?;
let insert = match def.kind {
MacroDefKind::BuiltInEager(_) => {
let id: MacroCallId =
expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)?
.into();
db.parse_or_expand(id.as_file())?
}
MacroDefKind::Declarative
| MacroDefKind::BuiltIn(_)
| MacroDefKind::BuiltInDerive(_) => {
let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?;
// replace macro inside
eager_macro_recur(db, expanded, macro_resolver)?
}
};
replaces.insert(child.syntax().clone().into(), insert.into());
}
if !replaces.is_empty() {
original = replace_descendants(&original, |n| replaces.get(n).cloned());
}
Some(original)
}
pub fn expand_eager_macro(
db: &impl AstDatabase,
macro_call: InFile<ast::MacroCall>,
def: MacroDefId,
resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
) -> Option<EagerMacroId> {
let args = macro_call.value.token_tree()?;
let parsed_args = mbe::ast_to_token_tree(&args)?.0;
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?;
let subtree = to_subtree(&result)?;
if let MacroDefKind::BuiltInEager(eager) = def.kind {
let (subtree, fragment) = eager.expand(&subtree).ok()?;
let eager =
EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id };
Some(db.intern_eager_expansion(eager))
} else {
None
}
}

View file

@ -9,7 +9,7 @@ use ra_syntax::ast;
use crate::{ use crate::{
db::AstDatabase, db::AstDatabase,
name::{AsName, Name}, name::{AsName, Name},
HirFileId, HirFileIdRepr, MacroDefKind, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -22,17 +22,18 @@ impl Hygiene {
pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene { pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene {
let def_crate = match file_id.0 { let def_crate = match file_id.0 {
HirFileIdRepr::FileId(_) => None, HirFileIdRepr::FileId(_) => None,
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
let lazy_id = match macro_file.macro_call_id { MacroCallId::LazyMacro(id) => {
crate::MacroCallId::LazyMacro(id) => id, let loc = db.lookup_intern_macro(id);
};
let loc = db.lookup_intern_macro(lazy_id);
match loc.def.kind { match loc.def.kind {
MacroDefKind::Declarative => loc.def.krate, MacroDefKind::Declarative => loc.def.krate,
MacroDefKind::BuiltIn(_) => None, MacroDefKind::BuiltIn(_) => None,
MacroDefKind::BuiltInDerive(_) => None, MacroDefKind::BuiltInDerive(_) => None,
MacroDefKind::BuiltInEager(_) => None,
} }
} }
MacroCallId::EagerMacro(_id) => None,
},
}; };
Hygiene { def_crate } Hygiene { def_crate }
} }

View file

@ -12,6 +12,7 @@ pub mod diagnostics;
pub mod builtin_derive; pub mod builtin_derive;
pub mod builtin_macro; pub mod builtin_macro;
pub mod quote; pub mod quote;
pub mod eager;
use std::hash::Hash; use std::hash::Hash;
use std::sync::Arc; use std::sync::Arc;
@ -25,7 +26,7 @@ use ra_syntax::{
use crate::ast_id_map::FileAstId; use crate::ast_id_map::FileAstId;
use crate::builtin_derive::BuiltinDeriveExpander; use crate::builtin_derive::BuiltinDeriveExpander;
use crate::builtin_macro::BuiltinFnLikeExpander; use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
#[cfg(test)] #[cfg(test)]
mod test_db; mod test_db;
@ -70,11 +71,17 @@ impl HirFileId {
match self.0 { match self.0 {
HirFileIdRepr::FileId(file_id) => file_id, HirFileIdRepr::FileId(file_id) => file_id,
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
let lazy_id = match macro_file.macro_call_id { let file_id = match macro_file.macro_call_id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => {
let loc = db.lookup_intern_macro(id);
loc.kind.file_id()
}
MacroCallId::EagerMacro(id) => {
let loc = db.lookup_intern_eager_expansion(id);
loc.file_id
}
}; };
let loc = db.lookup_intern_macro(lazy_id); file_id.original_file(db)
loc.kind.file_id().original_file(db)
} }
} }
} }
@ -86,6 +93,10 @@ impl HirFileId {
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
let lazy_id = match macro_file.macro_call_id { let lazy_id = match macro_file.macro_call_id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(_id) => {
// FIXME: handle call node for eager macro
return None;
}
}; };
let loc = db.lookup_intern_macro(lazy_id); let loc = db.lookup_intern_macro(lazy_id);
Some(loc.kind.node(db)) Some(loc.kind.node(db))
@ -100,6 +111,10 @@ impl HirFileId {
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
let lazy_id = match macro_file.macro_call_id { let lazy_id = match macro_file.macro_call_id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(_id) => {
// FIXME: handle expansion_info for eager macro
return None;
}
}; };
let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
@ -129,6 +144,9 @@ impl HirFileId {
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
let lazy_id = match macro_file.macro_call_id { let lazy_id = match macro_file.macro_call_id {
MacroCallId::LazyMacro(id) => id, MacroCallId::LazyMacro(id) => id,
MacroCallId::EagerMacro(_id) => {
return None;
}
}; };
let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
let item = match loc.def.kind { let item = match loc.def.kind {
@ -151,6 +169,7 @@ pub struct MacroFile {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MacroCallId { pub enum MacroCallId {
LazyMacro(LazyMacroId), LazyMacro(LazyMacroId),
EagerMacro(EagerMacroId),
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -164,11 +183,27 @@ impl salsa::InternKey for LazyMacroId {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EagerMacroId(salsa::InternId);
impl salsa::InternKey for EagerMacroId {
fn from_intern_id(v: salsa::InternId) -> Self {
EagerMacroId(v)
}
fn as_intern_id(&self) -> salsa::InternId {
self.0
}
}
impl From<LazyMacroId> for MacroCallId { impl From<LazyMacroId> for MacroCallId {
fn from(it: LazyMacroId) -> Self { fn from(it: LazyMacroId) -> Self {
MacroCallId::LazyMacro(it) MacroCallId::LazyMacro(it)
} }
} }
impl From<EagerMacroId> for MacroCallId {
fn from(it: EagerMacroId) -> Self {
MacroCallId::EagerMacro(it)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroDefId { pub struct MacroDefId {
@ -184,8 +219,8 @@ pub struct MacroDefId {
} }
impl MacroDefId { impl MacroDefId {
pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId { pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId {
db.intern_macro(MacroCallLoc { def: self, kind }).into() db.intern_macro(MacroCallLoc { def: self, kind })
} }
} }
@ -195,6 +230,7 @@ pub enum MacroDefKind {
BuiltIn(BuiltinFnLikeExpander), BuiltIn(BuiltinFnLikeExpander),
// FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
BuiltInDerive(BuiltinDeriveExpander), BuiltInDerive(BuiltinDeriveExpander),
BuiltInEager(EagerExpander),
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -240,6 +276,14 @@ impl MacroCallId {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EagerCallLoc {
pub(crate) def: MacroDefId,
pub(crate) fragment: FragmentKind,
pub(crate) subtree: Arc<tt::Subtree>,
pub(crate) file_id: HirFileId,
}
/// ExpansionInfo mainly describes how to map text range between src and expanded macro /// ExpansionInfo mainly describes how to map text range between src and expanded macro
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpansionInfo { pub struct ExpansionInfo {
@ -253,6 +297,7 @@ pub struct ExpansionInfo {
} }
pub use mbe::Origin; pub use mbe::Origin;
use ra_parser::FragmentKind;
impl ExpansionInfo { impl ExpansionInfo {
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {

View file

@ -173,6 +173,7 @@ pub mod known {
compile_error, compile_error,
line, line,
stringify, stringify,
concat,
format_args, format_args,
format_args_nl, format_args_nl,
env, env,

View file

@ -83,7 +83,7 @@ pub fn parse(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
parse_from_tokens(token_source, tree_sink, grammar::root); parse_from_tokens(token_source, tree_sink, grammar::root);
} }
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FragmentKind { pub enum FragmentKind {
Path, Path,
Expr, Expr,

View file

@ -219,7 +219,7 @@ fn unroot(n: SyntaxNode) -> SyntaxNode {
} }
pub mod tokens { pub mod tokens {
use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T}; use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> =
@ -251,6 +251,12 @@ pub mod tokens {
sf.syntax().first_child_or_token().unwrap().into_token().unwrap() sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
} }
pub fn literal(text: &str) -> SyntaxToken {
assert_eq!(text.trim(), text);
let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text));
lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
}
pub fn single_newline() -> SyntaxToken { pub fn single_newline() -> SyntaxToken {
SOURCE_FILE SOURCE_FILE
.tree() .tree()