diff --git a/Cargo.lock b/Cargo.lock index 949af84..5b36271 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1180,6 +1180,7 @@ name = "syntest" version = "0.1.0" dependencies = [ "anyhow", + "proc-macro2", "syn", ] diff --git a/crates/syntest/Cargo.toml b/crates/syntest/Cargo.toml index 5df0413..93ce589 100644 --- a/crates/syntest/Cargo.toml +++ b/crates/syntest/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] anyhow = "1.0.86" +proc-macro2 = "1.0.85" syn = { version = "2.0.66", features = ["full", "extra-traits"] } diff --git a/crates/syntest/src/mac.rs b/crates/syntest/src/mac.rs index 8ae0d29..5295160 100644 --- a/crates/syntest/src/mac.rs +++ b/crates/syntest/src/mac.rs @@ -9,24 +9,42 @@ pub struct Mac { file: Rc, } +#[derive(Debug)] +pub struct MacroDetails { + pub name: String, + pub tokens: Vec, +} + impl Mac { pub fn new(file: Rc) -> Self { Self { file } } - pub fn check_println_usage(&self, fn_name: &str) -> bool { - let mut found = false; + pub fn macros(&self, fn_name: &str) -> Vec { + let mut macros = Vec::new(); + self.func_stmts(fn_name, |_, stmt| { if let Stmt::Macro(macro_stmt) = stmt { let mac = ¯o_stmt.mac; - if mac.path.segments.iter().any(|seg| seg.ident == "println") { - found = true; - } + mac.path.segments.iter().for_each(|seg| { + let macro_name = seg.ident.to_string(); + let tokens = mac + .tokens + .clone() + .into_iter() + .map(|token| token.to_string()) + .collect(); + + macros.push(MacroDetails { + name: macro_name, + tokens, + }); + }); } }); - found + macros } } @@ -41,7 +59,7 @@ mod tests { use crate::Syntest; #[test] - fn test_macro_usage() { + fn test_list_macros() { let content = r#" pub fn test_fn() { let my_local_int = 42; @@ -50,6 +68,7 @@ mod tests { let re_assigned = my_local_int + another_local_int * 2 / 2 - 2; println!("Result: {}", re_assigned); + println!("Second macro"); return re_assigned; } @@ -57,6 +76,37 @@ mod tests { let mac = Syntest::from_code(content).unwrap().mac; - assert!(mac.check_println_usage("test_fn")); + let macros = mac.macros("test_fn"); + + assert_eq!(macros.len(), 2); + + match ¯os[..] { + [first, second] => { + // Testing the first one + assert_eq!(first.name, "println"); + first + .tokens + .iter() + .enumerate() + .for_each(|(i, token)| match i { + 0 => assert_eq!(token, "\"Result: {}\""), + 1 => assert_eq!(token, ","), + 2 => assert_eq!(token, "re_assigned"), + _ => panic!("Unexpected token"), + }); + + // Testing the second one + assert_eq!(second.name, "println"); + second + .tokens + .iter() + .enumerate() + .for_each(|(i, token)| match i { + 0 => assert_eq!(token, "\"Second macro\""), + _ => panic!("Unexpected token"), + }); + } + _ => panic!("Expected two macros"), + } } } diff --git a/crates/syntest/src/var.rs b/crates/syntest/src/var.rs index 16f3d38..be59b0b 100644 --- a/crates/syntest/src/var.rs +++ b/crates/syntest/src/var.rs @@ -1,3 +1,4 @@ +use proc_macro2::TokenTree; use std::fmt::Display; #[derive(Debug, PartialEq)] @@ -44,10 +45,30 @@ pub enum Value { Float(f64), Char(char), Bool(bool), + Vec(Vec), Closure, Other, } +impl From for Value { + fn from(value: TokenTree) -> Self { + match value { + TokenTree::Group(group) => { + let mut tokens = Vec::new(); + + for token in group.stream() { + tokens.push(Value::from(token)); + } + + Value::Vec(tokens) + } + TokenTree::Ident(ident) => Value::Str(ident.to_string()), + TokenTree::Literal(lit) => Value::Str(lit.to_string()), + TokenTree::Punct(punct) => Value::Char(punct.as_char()), + } + } +} + impl LocalVariable { pub fn is_used(&self) -> bool { match self {