use syn::{
parse2,
visit_mut::{self, VisitMut},
Attribute, Expr, ExprMacro, Item, Macro, Stmt,
};
use crate::{
into_wir::{Error, ErrorType},
util::path_matches_global_names,
wir::WSpan,
};
pub fn expand_in_items(items: &mut [Item]) -> Result<bool, Error> {
let mut visitor = Visitor {
result: Ok(()),
expanded_some_macro: false,
};
for item in items.iter_mut() {
visitor.visit_item_mut(item);
}
visitor.result?;
Ok(visitor.expanded_some_macro)
}
pub fn expand_in_expr(expr: &mut Expr) -> Result<bool, Error> {
let mut visitor = Visitor {
result: Ok(()),
expanded_some_macro: false,
};
visitor.visit_expr_mut(expr);
visitor.result?;
Ok(visitor.expanded_some_macro)
}
struct Visitor {
result: Result<(), Error>,
expanded_some_macro: bool,
}
impl VisitMut for Visitor {
fn visit_stmt_mut(&mut self, stmt: &mut Stmt) {
if let Stmt::Macro(stmt_macro) = stmt {
match self.process_macro(stmt_macro.mac.clone(), stmt_macro.attrs.clone()) {
Ok(macro_result) => *stmt = Stmt::Expr(macro_result, stmt_macro.semi_token),
Err(err) => self.push_error(err),
}
} else {
visit_mut::visit_stmt_mut(self, stmt);
}
}
fn visit_expr_mut(&mut self, expr: &mut Expr) {
if let Expr::Macro(expr_macro) = expr {
match self.process_macro(expr_macro.mac.clone(), expr_macro.attrs.clone()) {
Ok(macro_result) => *expr = macro_result,
Err(err) => self.push_error(err),
}
} else {
visit_mut::visit_expr_mut(self, expr);
}
}
}
impl Visitor {
fn process_macro(&mut self, mac: Macro, attrs: Vec<Attribute>) -> Result<Expr, Error> {
if path_matches_global_names(&mac.path, &["machine_check", "bitmask_switch"]) {
self.expanded_some_macro = true;
return self.process_bitmask_switch(mac);
}
Ok(Expr::Macro(ExprMacro { attrs, mac }))
}
fn process_bitmask_switch(&self, mut mac: Macro) -> Result<Expr, Error> {
let macro_result =
match machine_check_bitmask_switch::process(::std::mem::take(&mut mac.tokens)) {
Ok(ok) => ok,
Err(err) => {
return Err(Error::new(
ErrorType::MacroError(err.msg()),
WSpan::from_syn(&mac),
));
}
};
parse2(macro_result)
.map_err(|err| Error::new(ErrorType::MacroParseError(err), WSpan::from_syn(&mac)))
}
fn push_error(&mut self, err: Error) {
if self.result.is_ok() {
self.result = Err(err);
}
}
}