HAHAHAHAHAHAHA

This commit is contained in:
nora 2022-12-19 15:52:04 +01:00
parent 106be93473
commit 38d126a4b6
No known key found for this signature in database
7 changed files with 226 additions and 145 deletions

6
Cargo.lock generated
View file

@ -152,6 +152,7 @@ dependencies = [
"anyhow", "anyhow",
"cargo", "cargo",
"clap 4.0.29", "clap 4.0.29",
"libc",
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -159,6 +160,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"syn", "syn",
"tempfile",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"tracing-tree", "tracing-tree",
@ -736,9 +738,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.134" version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]] [[package]]
name = "libgit2-sys" name = "libgit2-sys"

View file

@ -20,7 +20,11 @@ rustfix = "0.6.1"
serde = { version = "1.0.151", features = ["derive"] } serde = { version = "1.0.151", features = ["derive"] }
serde_json = "1.0.90" serde_json = "1.0.90"
syn = { version = "1.0.101", features = ["full", "visit", "visit-mut"] } syn = { version = "1.0.101", features = ["full", "visit", "visit-mut"] }
tempfile = "3.3.0"
tracing = "0.1.37" tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
walkdir = "2.3.2" walkdir = "2.3.2"
[target."cfg(unix)".dependencies]
libc = "0.2.138"

View file

@ -1,20 +1,42 @@
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use rustfix::diagnostics::Diagnostic; use rustfix::diagnostics::Diagnostic;
use serde::Deserialize; use serde::Deserialize;
use std::{collections::HashSet, fmt::Display, path::PathBuf, process::Command, rc::Rc}; use std::{
collections::HashSet,
fmt::{Debug, Display},
path::PathBuf,
process::Command,
rc::Rc,
};
use crate::{EnvVar, Options}; use crate::{dylib_flag::RustFunction, EnvVar, Options};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Build { pub struct Build {
inner: Rc<BuildInner>, inner: Rc<BuildInner>,
} }
pub enum Verify {
Ice,
Custom(RustFunction),
None,
}
impl Debug for Verify {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ice => write!(f, "Ice"),
Self::Custom(_) => f.debug_tuple("Custom").finish(),
Self::None => write!(f, "None"),
}
}
}
#[derive(Debug)] #[derive(Debug)]
struct BuildInner { struct BuildInner {
mode: BuildMode, mode: BuildMode,
input_path: PathBuf, input_path: PathBuf,
no_verify: bool, verify: Verify,
env: Vec<EnvVar>, env: Vec<EnvVar>,
} }
@ -40,18 +62,26 @@ impl Build {
} }
}; };
let verify = if options.no_verify {
Verify::None
} else if let Some(func) = options.verify_fn {
Verify::Custom(func)
} else {
Verify::Ice
};
Self { Self {
inner: Rc::new(BuildInner { inner: Rc::new(BuildInner {
mode, mode,
input_path: options.path.clone(), input_path: options.path.clone(),
no_verify: options.no_verify, verify,
env: options.env.clone(), env: options.env.clone(),
}), }),
} }
} }
pub fn build(&self) -> Result<BuildResult> { pub fn build(&self) -> Result<BuildResult> {
if self.inner.no_verify { if let Verify::None = self.inner.verify {
return Ok(BuildResult { return Ok(BuildResult {
reproduces_issue: false, reproduces_issue: false,
no_verify: true, no_verify: true,
@ -59,7 +89,7 @@ impl Build {
}); });
} }
let (reproduces_issue, output) = match &self.inner.mode { let (is_ice, output) = match &self.inner.mode {
BuildMode::Cargo { args } => { BuildMode::Cargo { args } => {
let mut cmd = Command::new("cargo"); let mut cmd = Command::new("cargo");
cmd.arg("build"); cmd.arg("build");
@ -116,9 +146,15 @@ impl Build {
} }
}; };
let reproduces_issue = match self.inner.verify {
Verify::None => unreachable!("handled ealier"),
Verify::Ice => is_ice,
Verify::Custom(func) => func.call(&output),
};
Ok(BuildResult { Ok(BuildResult {
reproduces_issue, reproduces_issue,
no_verify: self.inner.no_verify, no_verify: false,
output, output,
}) })
} }
@ -144,8 +180,6 @@ impl Build {
.into_iter::<CargoJsonCompileMessage>() .into_iter::<CargoJsonCompileMessage>()
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
messages messages
.into_iter() .into_iter()
.filter(|msg| msg.reason == "compiler-message") .filter(|msg| msg.reason == "compiler-message")

116
src/dylib_flag.rs Normal file
View file

@ -0,0 +1,116 @@
//! Handles the --verify-fn flag.
//! It takes in a Rust closure like `|str| true` that takes in a `&str` and returns a bool.
use std::{fmt::Debug, str::FromStr};
use anyhow::{Context, Result};
use quote::quote;
type Entrypoint = unsafe extern "C" fn(*const u8, usize) -> bool;
#[derive(Clone, Copy)]
pub struct RustFunction {
func: Entrypoint,
}
impl FromStr for RustFunction {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::compile(s)
}
}
fn wrap_func_body(func: &str) -> Result<String> {
let closure = syn::parse_str::<syn::ExprClosure>(func).context("invalid rust syntax")?;
let tokenstream = quote! {
#[no_mangle]
pub extern "C" fn cargo_minimize_ffi_function(ptr: *const u8, len: usize) -> bool {
match ::std::panic::catch_unwind(|| __cargo_minimize_inner(ptr, len)) {
Ok(bool) => bool,
Err(_) => ::std::process::abort(),
}
}
fn __cargo_minimize_inner(__ptr: *const u8, __len: usize) -> bool {
let __slice = unsafe { ::std::slice::from_raw_parts(__ptr, __len) };
let __str = ::std::str::from_utf8(__slice).unwrap();
(#closure)(__str)
}
};
Ok(tokenstream.to_string())
}
impl RustFunction {
#[cfg(not(unix))]
pub fn compile(body: &str) -> Result<Self> {
Err(anyhow::anyhow!("--verify-fn only works on unix"));
}
#[cfg(unix)]
pub fn compile(body: &str) -> Result<Self> {
use anyhow::bail;
use std::io;
use std::process::Command;
use std::{ffi::CString, os::unix::prelude::OsStringExt};
let file = tempfile::tempdir()?;
let full_file = wrap_func_body(body)?;
let source_path = file.path().join("source.rs");
std::fs::write(&source_path, &full_file).context("writing source")?;
let mut rustc = Command::new("rustc");
rustc.arg(source_path);
rustc.args(["--crate-type=cdylib", "--crate-name=helper", "--emit=link"]);
rustc.current_dir(file.path());
let output = rustc.output().context("running rustc")?;
if !output.status.success() {
let stderr = String::from_utf8(output.stderr)?;
bail!("Failed to compile code: {stderr}");
}
let dylib_path = file.path().join("libhelper.so");
let os_str = dylib_path.into_os_string();
let vec = os_str.into_vec();
let cstr = CString::new(vec)?;
let dylib = unsafe { libc::dlopen(cstr.as_ptr(), libc::RTLD_LAZY) };
if dylib.is_null() {
bail!("failed to open dylib: {}", io::Error::last_os_error());
}
let symbol = b"cargo_minimize_ffi_function\0";
let func = unsafe { libc::dlsym(dylib, symbol.as_ptr().cast()) };
if func.is_null() {
bail!("didn't find entrypoint symbol");
}
let func = unsafe { std::mem::transmute::<*mut _, Entrypoint>(func) };
Ok(Self { func })
}
pub fn call(&self, output: &str) -> bool {
let ptr = output.as_ptr();
let len = output.len();
unsafe { (self.func)(ptr, len) }
}
}
impl Debug for RustFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RustFunction").finish_non_exhaustive()
}
}

View file

@ -4,6 +4,7 @@ extern crate tracing;
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
mod build; mod build;
mod dylib_flag;
mod everybody_loops; mod everybody_loops;
mod expand; mod expand;
mod privatize; mod privatize;
@ -11,9 +12,10 @@ mod processor;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
use dylib_flag::RustFunction;
use processor::Minimizer; use processor::Minimizer;
use crate::{processor::Processor}; use crate::processor::Processor;
#[derive(clap::Parser)] #[derive(clap::Parser)]
#[command(version, about, name = "cargo", bin_name = "cargo")] #[command(version, about, name = "cargo", bin_name = "cargo")]
@ -33,6 +35,8 @@ pub struct Options {
rustc: bool, rustc: bool,
#[arg(long)] #[arg(long)]
no_verify: bool, no_verify: bool,
#[arg(long)]
verify_fn: Option<RustFunction>,
#[arg(long)] #[arg(long)]
env: Vec<EnvVar>, env: Vec<EnvVar>,

View file

@ -1,50 +1,40 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::{ use std::{fs, path::{Path, PathBuf}};
fs,
path::{Path, PathBuf},
};
#[derive(Debug, PartialEq, Eq, Clone, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct SourceFile { pub(crate) struct SourceFile {
pub path: PathBuf, pub(crate) path: PathBuf,
} }
#[derive(Default)] #[derive(Default)]
pub struct Changes { pub(crate) struct Changes {
any_change: bool, any_change: bool,
} }
pub(crate) struct FileChange<'a, 'b> {
pub struct FileChange<'a, 'b> { pub(crate) path: &'a Path,
pub path: &'a Path,
content: String, content: String,
changes: &'b mut Changes, changes: &'b mut Changes,
has_written_change: bool, has_written_change: bool,
} }
impl FileChange<'_, '_> { impl FileChange<'_, '_> {
pub fn before_content(&self) -> &str { pub(crate) fn before_content(&self) -> &str {
&self.content &self.content
} }
pub(crate) fn write(&mut self, new: &str) -> Result<()> {
pub fn write(&mut self, new: &str) -> Result<()> {
self.has_written_change = true; self.has_written_change = true;
fs::write(self.path, new).with_context(|| format!("writing file {}", self.path.display())) fs::write(self.path, new)
.with_context(|| format!("writing file {}", self.path.display()))
} }
pub(crate) fn rollback(mut self) -> Result<()> {
pub fn rollback(mut self) -> Result<()> {
assert!(self.has_written_change); assert!(self.has_written_change);
self.has_written_change = false; self.has_written_change = false;
fs::write(self.path, &self.content) fs::write(self.path, &self.content)
.with_context(|| format!("writing file {}", self.path.display())) .with_context(|| format!("writing file {}", self.path.display()))
} }
pub(crate) fn commit(mut self) {
pub fn commit(mut self) {
assert!(self.has_written_change); assert!(self.has_written_change);
self.has_written_change = false; self.has_written_change = false;
self.changes.any_change = true; self.changes.any_change = true;
} }
} }
impl Drop for FileChange<'_, '_> { impl Drop for FileChange<'_, '_> {
fn drop(&mut self) { fn drop(&mut self) {
if self.has_written_change { if self.has_written_change {
@ -55,9 +45,8 @@ impl Drop for FileChange<'_, '_> {
} }
} }
} }
impl SourceFile { impl SourceFile {
pub fn try_change<'file, 'change>( pub(crate) fn try_change<'file, 'change>(
&'file self, &'file self,
changes: &'change mut Changes, changes: &'change mut Changes,
) -> Result<FileChange<'file, 'change>> { ) -> Result<FileChange<'file, 'change>> {
@ -71,9 +60,8 @@ impl SourceFile {
}) })
} }
} }
impl Changes { impl Changes {
pub fn had_changes(&self) -> bool { pub(crate) fn had_changes(&self) -> bool {
self.any_change self.any_change
} }
} }

View file

@ -1,19 +1,13 @@
mod files; mod files;
mod reaper; mod reaper;
use std::{borrow::Borrow, collections::HashSet, ffi::OsStr, fmt::Debug, mem, path::Path}; use std::{borrow::Borrow, collections::HashSet, ffi::OsStr, fmt::Debug, mem, path::Path};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crate::{build::Build, processor::files::Changes}; use crate::{build::Build, processor::files::Changes};
pub(crate) use self::files::SourceFile;
pub use self::files::SourceFile; pub(crate) trait Processor {
pub trait Processor {
fn refresh_state(&mut self) -> Result<()> { fn refresh_state(&mut self) -> Result<()> {
Ok(()) Ok(())
} }
/// Process a file. The state of the processor might get invalidated in the process as signaled with /// Process a file. The state of the processor might get invalidated in the process as signaled with
/// `ProcessState::FileInvalidated`. When a file is invalidated, the minimizer will call `Processor::refersh_state` /// `ProcessState::FileInvalidated`. When a file is invalidated, the minimizer will call `Processor::refersh_state`
/// before calling the this function on the same file again. /// before calling the this function on the same file again.
@ -23,33 +17,27 @@ pub trait Processor {
file: &SourceFile, file: &SourceFile,
checker: &mut PassController, checker: &mut PassController,
) -> ProcessState; ) -> ProcessState;
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
} }
impl Debug for dyn Processor { impl Debug for dyn Processor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name()) f.write_str(self.name())
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum ProcessState { pub(crate) enum ProcessState {
NoChange, NoChange,
Changed, Changed,
FileInvalidated, FileInvalidated,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Minimizer { pub(crate) struct Minimizer {
files: Vec<SourceFile>, files: Vec<SourceFile>,
build: Build, build: Build,
} }
impl Minimizer { impl Minimizer {
pub fn new_glob_dir(path: &Path, build: Build) -> Self { pub(crate) fn new_glob_dir(path: &Path, build: Build) -> Self {
let walk = walkdir::WalkDir::new(path); let walk = walkdir::WalkDir::new(path);
let files = walk let files = walk
.into_iter() .into_iter()
.filter_map(|entry| match entry { .filter_map(|entry| match entry {
@ -67,54 +55,41 @@ impl Minimizer {
println!("- {}", file.path.display()); println!("- {}", file.path.display());
}) })
.collect(); .collect();
Self { files, build } Self { files, build }
} }
pub(crate) fn run_passes<'a>(
pub fn run_passes<'a>(
&self, &self,
passes: impl IntoIterator<Item = Box<dyn Processor + 'a>>, passes: impl IntoIterator<Item = Box<dyn Processor + 'a>>,
) -> Result<()> { ) -> Result<()> {
let inital_build = self.build.build()?; let inital_build = self.build.build()?;
println!("Initial build: {inital_build}"); println!("Initial build: {inital_build}");
inital_build.require_reproduction("Initial")?; inital_build.require_reproduction("Initial")?;
for mut pass in passes { for mut pass in passes {
self.run_pass(&mut *pass)?; self.run_pass(&mut *pass)?;
} }
Ok(()) Ok(())
} }
fn run_pass(&self, pass: &mut dyn Processor) -> Result<()> { fn run_pass(&self, pass: &mut dyn Processor) -> Result<()> {
let mut invalidated_files = HashSet::new(); let mut invalidated_files = HashSet::new();
let mut refresh_and_try_again = false; let mut refresh_and_try_again = false;
loop { loop {
let span = info_span!("Starting round of pass", name = pass.name()); let span = info_span!("Starting round of pass", name = pass.name());
let _enter = span.enter(); let _enter = span.enter();
let mut changes = Changes::default(); let mut changes = Changes::default();
for file in &self.files { for file in &self.files {
if invalidated_files.contains(file) { if invalidated_files.contains(file) {
continue; continue;
} }
self.process_file(pass, file, &mut invalidated_files, &mut changes)?; self.process_file(pass, file, &mut invalidated_files, &mut changes)?;
} }
if !changes.had_changes() { if !changes.had_changes() {
if !refresh_and_try_again && !invalidated_files.is_empty() { if !refresh_and_try_again && !invalidated_files.is_empty() {
// A few files have been invalidated, let's refresh and try these again.
pass.refresh_state().context("refreshing state for pass")?; pass.refresh_state().context("refreshing state for pass")?;
invalidated_files.clear(); invalidated_files.clear();
refresh_and_try_again = true; refresh_and_try_again = true;
println!("Refreshing files for {}", pass.name()); println!("Refreshing files for {}", pass.name());
continue; continue;
} }
println!("Finished {}", pass.name()); println!("Finished {}", pass.name());
return Ok(()); return Ok(());
} else { } else {
@ -122,7 +97,6 @@ impl Minimizer {
} }
} }
} }
fn process_file<'file>( fn process_file<'file>(
&self, &self,
pass: &mut dyn Processor, pass: &mut dyn Processor,
@ -131,29 +105,19 @@ impl Minimizer {
changes: &mut Changes, changes: &mut Changes,
) -> Result<()> { ) -> Result<()> {
let mut checker = PassController::new(); let mut checker = PassController::new();
loop { loop {
dbg!(&checker); dbg!(& checker);
let file_display = file.path.display(); let file_display = file.path.display();
let mut change = file.try_change(changes)?; let mut change = file.try_change(changes)?;
let mut krate = syn::parse_file(change.before_content()) let mut krate = syn::parse_file(change.before_content())
.with_context(|| format!("parsing file {file_display}"))?; .with_context(|| format!("parsing file {file_display}"))?;
let has_made_change = pass.process_file(&mut krate, file, &mut checker); let has_made_change = pass.process_file(&mut krate, file, &mut checker);
match has_made_change { match has_made_change {
ProcessState::Changed | ProcessState::FileInvalidated => { ProcessState::Changed | ProcessState::FileInvalidated => {
let result = prettyplease::unparse(&krate); let result = prettyplease::unparse(&krate);
change.write(&result)?; change.write(&result)?;
let after = self.build.build()?; let after = self.build.build()?;
println!("{file_display}: After {}: {after}", pass.name()); println!("{file_display}: After {}: {after}", pass.name());
if after.reproduces_issue() { if after.reproduces_issue() {
change.commit(); change.commit();
checker.reproduces(); checker.reproduces();
@ -161,7 +125,6 @@ impl Minimizer {
change.rollback()?; change.rollback()?;
checker.does_not_reproduce(); checker.does_not_reproduce();
} }
if has_made_change == ProcessState::FileInvalidated { if has_made_change == ProcessState::FileInvalidated {
invalidated_files.insert(file); invalidated_files.insert(file);
} }
@ -171,50 +134,35 @@ impl Minimizer {
checker.no_change(); checker.no_change();
} }
} }
if checker.is_finished() { if checker.is_finished() {
break; break;
} }
} }
Ok(()) Ok(())
} }
} }
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
struct AstPath(Vec<String>); struct AstPath(Vec<String>);
impl Borrow<[String]> for AstPath { impl Borrow<[String]> for AstPath {
fn borrow(&self) -> &[String] { fn borrow(&self) -> &[String] {
&self.0 &self.0
} }
} }
impl Debug for AstPath { impl Debug for AstPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AstPath({:?})", self.0) write!(f, "AstPath({:?})", self.0)
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct PassController { pub(crate) struct PassController {
state: PassControllerState, state: PassControllerState,
} }
#[derive(Debug)] #[derive(Debug)]
enum PassControllerState { enum PassControllerState {
InitialCollection { InitialCollection { candidates: Vec<AstPath> },
candidates: Vec<AstPath>, Bisecting { current: HashSet<AstPath>, worklist: Vec<Vec<AstPath>> },
},
Bisecting {
current: HashSet<AstPath>,
worklist: Vec<Vec<AstPath>>,
},
Success, Success,
} }
impl PassController { impl PassController {
fn new() -> Self { fn new() -> Self {
Self { Self {
@ -223,44 +171,41 @@ impl PassController {
}, },
} }
} }
fn reproduces(&mut self) { fn reproduces(&mut self) {
match &mut self.state { match &mut self.state {
PassControllerState::InitialCollection { .. } => { PassControllerState::InitialCollection { .. } => {
self.state = PassControllerState::Success self.state = PassControllerState::Success;
} }
PassControllerState::Bisecting { PassControllerState::Bisecting { current, worklist, .. } => {
current, worklist, .. match worklist.pop() {
} => match worklist.pop() { Some(next) => *current = next.into_iter().collect(),
Some(next) => *current = next.into_iter().collect(), None => {
None => { self.state = PassControllerState::Success;
self.state = PassControllerState::Success; }
} }
}, }
PassControllerState::Success => unreachable!("Processed after success"), PassControllerState::Success => unreachable!("Processed after success"),
} }
} }
fn does_not_reproduce(&mut self) { fn does_not_reproduce(&mut self) {
match &mut self.state { match &mut self.state {
PassControllerState::InitialCollection { candidates } => { PassControllerState::InitialCollection { candidates } => {
let candidates = mem::take(candidates); let candidates = mem::take(candidates);
let half = candidates.len() / 2; let half = candidates.len() / 2;
let (first_half, second_half) = candidates.split_at(half); let (first_half, second_half) = candidates.split_at(half);
self
self.state = PassControllerState::Bisecting { .state = PassControllerState::Bisecting {
current: first_half.iter().cloned().collect(), current: first_half.iter().cloned().collect(),
worklist: vec![second_half.to_owned()], worklist: vec![second_half.to_owned()],
}; };
} }
PassControllerState::Bisecting { current, worklist } => { PassControllerState::Bisecting { current, worklist } => {
dbg!(&current, &worklist); dbg!(& current, & worklist);
todo!(); todo!();
} }
PassControllerState::Success => unreachable!("Processed after success"), PassControllerState::Success => unreachable!("Processed after success"),
} }
} }
fn no_change(&mut self) { fn no_change(&mut self) {
match &self.state { match &self.state {
PassControllerState::InitialCollection { candidates } => { PassControllerState::InitialCollection { candidates } => {
@ -271,12 +216,13 @@ impl PassController {
self.state = PassControllerState::Success; self.state = PassControllerState::Success;
} }
PassControllerState::Bisecting { current, .. } => { PassControllerState::Bisecting { current, .. } => {
unreachable!("No change while bisecting, current was empty somehow: {current:?}"); unreachable!(
"No change while bisecting, current was empty somehow: {current:?}"
);
} }
PassControllerState::Success => {} PassControllerState::Success => {}
} }
} }
fn is_finished(&mut self) -> bool { fn is_finished(&mut self) -> bool {
match &mut self.state { match &mut self.state {
PassControllerState::InitialCollection { .. } => false, PassControllerState::InitialCollection { .. } => false,
@ -284,8 +230,7 @@ impl PassController {
PassControllerState::Success => true, PassControllerState::Success => true,
} }
} }
pub(crate) fn can_process(&mut self, path: &[String]) -> bool {
pub fn can_process(&mut self, path: &[String]) -> bool {
match &mut self.state { match &mut self.state {
PassControllerState::InitialCollection { candidates } => { PassControllerState::InitialCollection { candidates } => {
candidates.push(AstPath(path.to_owned())); candidates.push(AstPath(path.to_owned()));
@ -298,43 +243,31 @@ impl PassController {
} }
} }
} }
macro_rules! tracking { macro_rules! tracking {
() => { () => {
tracking!(visit_item_fn_mut); tracking!(visit_item_fn_mut); tracking!(visit_impl_item_method_mut);
tracking!(visit_impl_item_method_mut); tracking!(visit_item_impl_mut); tracking!(visit_item_mod_mut);
tracking!(visit_item_impl_mut);
tracking!(visit_item_mod_mut);
}; };
(visit_item_fn_mut) => { (visit_item_fn_mut) => {
fn visit_item_fn_mut(&mut self, func: &mut syn::ItemFn) { fn visit_item_fn_mut(& mut self, func : & mut syn::ItemFn) { self.current_path
self.current_path.push(func.sig.ident.to_string()); .push(func.sig.ident.to_string()); syn::visit_mut::visit_item_fn_mut(self, func);
syn::visit_mut::visit_item_fn_mut(self, func); self.current_path.pop(); }
self.current_path.pop();
}
}; };
(visit_impl_item_method_mut) => { (visit_impl_item_method_mut) => {
fn visit_impl_item_method_mut(&mut self, method: &mut syn::ImplItemMethod) { fn visit_impl_item_method_mut(& mut self, method : & mut syn::ImplItemMethod) {
self.current_path.push(method.sig.ident.to_string()); self.current_path.push(method.sig.ident.to_string());
syn::visit_mut::visit_impl_item_method_mut(self, method); syn::visit_mut::visit_impl_item_method_mut(self, method); self.current_path
self.current_path.pop(); .pop(); }
}
}; };
(visit_item_impl_mut) => { (visit_item_impl_mut) => {
fn visit_item_impl_mut(&mut self, item: &mut syn::ItemImpl) { fn visit_item_impl_mut(& mut self, item : & mut syn::ItemImpl) { self
self.current_path .current_path.push(item.self_ty.clone().into_token_stream().to_string());
.push(item.self_ty.clone().into_token_stream().to_string()); syn::visit_mut::visit_item_impl_mut(self, item); self.current_path.pop(); }
syn::visit_mut::visit_item_impl_mut(self, item);
self.current_path.pop();
}
}; };
(visit_item_mod_mut) => { (visit_item_mod_mut) => {
fn visit_item_mod_mut(&mut self, module: &mut syn::ItemMod) { fn visit_item_mod_mut(& mut self, module : & mut syn::ItemMod) { self
self.current_path.push(module.ident.to_string()); .current_path.push(module.ident.to_string());
syn::visit_mut::visit_item_mod_mut(self, module); syn::visit_mut::visit_item_mod_mut(self, module); self.current_path.pop(); }
self.current_path.pop();
}
}; };
} }
pub(crate) use tracking; pub(crate) use tracking;