rust/crates/hir_expand/src/proc_macro.rs

155 lines
3.9 KiB
Rust
Raw Normal View History

2020-03-18 04:47:59 -05:00
//! Proc Macro Expander stub
2020-03-18 07:56:46 -05:00
use crate::{db::AstDatabase, LazyMacroId};
2020-08-13 09:25:38 -05:00
use base_db::{CrateId, ProcMacroId};
2020-04-21 12:44:21 -05:00
use tt::buffer::{Cursor, TokenBuffer};
2020-03-18 04:47:59 -05:00
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander {
krate: CrateId,
2020-09-18 09:43:50 -05:00
proc_macro_id: Option<ProcMacroId>,
2020-03-18 04:47:59 -05:00
}
2020-03-26 15:26:34 -05:00
macro_rules! err {
($fmt:literal, $($tt:tt),*) => {
mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
};
($fmt:literal) => {
mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
}
}
2020-03-18 04:47:59 -05:00
impl ProcMacroExpander {
2020-09-18 09:43:50 -05:00
pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> Self {
Self { krate, proc_macro_id: Some(proc_macro_id) }
}
pub fn dummy(krate: CrateId) -> Self {
// FIXME: Should store the name for better errors
// FIXME: I think this is the second layer of "dummy" expansion, we should reduce that
Self { krate, proc_macro_id: None }
2020-03-18 04:47:59 -05:00
}
pub fn expand(
2020-05-26 13:12:13 -05:00
self,
2020-03-18 04:47:59 -05:00
db: &dyn AstDatabase,
2020-03-18 07:56:46 -05:00
_id: LazyMacroId,
tt: &tt::Subtree,
2020-03-18 04:47:59 -05:00
) -> Result<tt::Subtree, mbe::ExpandError> {
2020-09-18 09:43:50 -05:00
match self.proc_macro_id {
Some(id) => {
let krate_graph = db.crate_graph();
let proc_macro = krate_graph[self.krate]
.proc_macro
.get(id.0 as usize)
.clone()
.ok_or_else(|| err!("No derive macro found."))?;
let tt = remove_derive_attrs(tt)
.ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
}
None => Err(err!("Unresolved proc macro")),
}
2020-03-18 04:47:59 -05:00
}
}
2020-03-26 15:26:34 -05:00
2020-04-21 12:44:21 -05:00
fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() {
if punct.char == c {
*cursor = cursor.bump();
return true;
}
2020-03-26 15:26:34 -05:00
}
2020-04-21 12:44:21 -05:00
false
}
fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() {
if Some(kind) == subtree.delimiter_kind() {
*cursor = cursor.bump_subtree();
return true;
}
}
false
}
fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() {
if t == ident.text.as_str() {
*cursor = cursor.bump();
return true;
}
}
false
}
fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
let buffer = TokenBuffer::new(&tt.token_trees);
let mut p = buffer.begin();
let mut result = tt::Subtree::default();
while !p.eof() {
let curr = p;
2020-03-26 15:26:34 -05:00
2020-04-21 12:44:21 -05:00
if eat_punct(&mut p, '#') {
eat_punct(&mut p, '!');
let parent = p;
if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
if eat_ident(&mut p, "derive") {
p = parent.bump();
continue;
}
}
}
result.token_trees.push(curr.token_tree()?.clone());
p = curr.bump();
}
Some(result)
}
#[cfg(test)]
2020-07-31 11:30:37 -05:00
mod tests {
2020-04-21 12:44:21 -05:00
use super::*;
use test_utils::assert_eq_text;
#[test]
fn test_remove_derive_attrs() {
let tt = mbe::parse_to_token_tree(
r#"
#[allow(unused)]
#[derive(Copy)]
#[derive(Hello)]
struct A {
bar: u32
}
"#,
)
.unwrap()
.0;
let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
assert_eq_text!(
&result,
r#"
SUBTREE $
PUNCH # [alone] 0
SUBTREE [] 1
IDENT allow 2
SUBTREE () 3
IDENT unused 4
IDENT struct 15
IDENT A 16
SUBTREE {} 17
IDENT bar 18
PUNCH : [alone] 19
IDENT u32 20
"#
.trim()
);
}
2020-03-26 15:26:34 -05:00
}