mirror of
https://github.com/Noratrieb/cargo-minimize.git
synced 2026-01-16 17:25:03 +01:00
make better
This commit is contained in:
parent
f7395cd817
commit
79b69fafb9
7 changed files with 182 additions and 82 deletions
|
|
@ -92,7 +92,7 @@ impl Build {
|
||||||
let (is_ice, 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.args(["build", "--color=always"]);
|
||||||
|
|
||||||
for arg in args.iter().flatten() {
|
for arg in args.iter().flatten() {
|
||||||
cmd.arg(arg);
|
cmd.arg(arg);
|
||||||
|
|
@ -107,8 +107,8 @@ impl Build {
|
||||||
let output = String::from_utf8(outputs.stderr)?;
|
let output = String::from_utf8(outputs.stderr)?;
|
||||||
|
|
||||||
(
|
(
|
||||||
outputs.status.code() == Some(101)
|
// Cargo always exits with 101 when rustc has an error.
|
||||||
|| output.contains("internal compiler error"),
|
output.contains("internal compiler error") || output.contains("' panicked at"),
|
||||||
output,
|
output,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/lib.rs
21
src/lib.rs
|
|
@ -83,26 +83,5 @@ pub fn minimize() -> Result<()> {
|
||||||
|
|
||||||
minimizer.delete_dead_code().context("deleting dead code")?;
|
minimizer.delete_dead_code().context("deleting dead code")?;
|
||||||
|
|
||||||
/*
|
|
||||||
let file = expand::expand(&dir).context("during expansion")?;
|
|
||||||
|
|
||||||
//let file = syn::parse_str("extern { pub fn printf(format: *const ::c_char, ...) -> ::c_int; }",).unwrap();
|
|
||||||
let file = prettyplease::unparse(&file);
|
|
||||||
|
|
||||||
println!("// EXPANDED-START\n\n{file}\n\n// EXPANDED-END");
|
|
||||||
|
|
||||||
std::fs::write("expanded.rs", file)?;
|
|
||||||
|
|
||||||
println!("wow, expanded");
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
let build = Build::new(cargo_dir);
|
|
||||||
|
|
||||||
if build.build()?.success {
|
|
||||||
bail!("build must initially fail!");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/main.rs
13
src/main.rs
|
|
@ -1,9 +1,14 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tracing::info;
|
use tracing::{error, info, Level};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let registry = Registry::default().with(EnvFilter::from_default_env());
|
let registry = Registry::default().with(
|
||||||
|
EnvFilter::builder()
|
||||||
|
.with_default_directive(Level::INFO.into())
|
||||||
|
.from_env()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
info!("Starting cargo-minimize");
|
info!("Starting cargo-minimize");
|
||||||
|
|
||||||
|
|
@ -13,7 +18,9 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
registry.with(tree_layer).init();
|
registry.with(tree_layer).init();
|
||||||
|
|
||||||
cargo_minimize::minimize()?;
|
if let Err(err) = cargo_minimize::minimize() {
|
||||||
|
error!("An error occured:\n{err}");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,50 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::{fs, path::{Path, PathBuf}};
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
pub(crate) struct SourceFile {
|
pub(crate) struct SourceFile {
|
||||||
pub(crate) path: PathBuf,
|
pub(crate) path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Changes {
|
pub(crate) struct Changes {
|
||||||
any_change: bool,
|
any_change: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FileChange<'a, 'b> {
|
pub(crate) struct FileChange<'a, 'b> {
|
||||||
pub(crate) path: &'a Path,
|
pub(crate) 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(crate) 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(crate) fn write(&mut self, new: &str) -> Result<()> {
|
||||||
self.has_written_change = true;
|
self.has_written_change = true;
|
||||||
fs::write(self.path, new)
|
fs::write(self.path, new).with_context(|| format!("writing file {}", self.path.display()))
|
||||||
.with_context(|| format!("writing file {}", self.path.display()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn rollback(mut self) -> Result<()> {
|
pub(crate) 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(crate) 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 {
|
||||||
|
|
@ -45,6 +55,7 @@ impl Drop for FileChange<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceFile {
|
impl SourceFile {
|
||||||
pub(crate) fn try_change<'file, 'change>(
|
pub(crate) fn try_change<'file, 'change>(
|
||||||
&'file self,
|
&'file self,
|
||||||
|
|
@ -60,6 +71,7 @@ impl SourceFile {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Changes {
|
impl Changes {
|
||||||
pub(crate) fn had_changes(&self) -> bool {
|
pub(crate) fn had_changes(&self) -> bool {
|
||||||
self.any_change
|
self.any_change
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,22 @@
|
||||||
mod files;
|
mod files;
|
||||||
mod reaper;
|
mod reaper;
|
||||||
use std::{borrow::Borrow, collections::HashSet, ffi::OsStr, fmt::Debug, mem, path::Path};
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use crate::{build::Build, processor::files::Changes};
|
|
||||||
pub(crate) use self::files::SourceFile;
|
pub(crate) use self::files::SourceFile;
|
||||||
|
use crate::{build::Build, processor::files::Changes};
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
collections::{BTreeSet, HashSet},
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::Debug,
|
||||||
|
mem,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) trait Processor {
|
pub(crate) 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.
|
||||||
|
|
@ -17,24 +26,29 @@ pub(crate) 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(crate) enum ProcessState {
|
pub(crate) enum ProcessState {
|
||||||
NoChange,
|
NoChange,
|
||||||
Changed,
|
Changed,
|
||||||
FileInvalidated,
|
FileInvalidated,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Minimizer {
|
pub(crate) struct Minimizer {
|
||||||
files: Vec<SourceFile>,
|
files: Vec<SourceFile>,
|
||||||
build: Build,
|
build: Build,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Minimizer {
|
impl Minimizer {
|
||||||
pub(crate) 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);
|
||||||
|
|
@ -43,7 +57,7 @@ impl Minimizer {
|
||||||
.filter_map(|entry| match entry {
|
.filter_map(|entry| match entry {
|
||||||
Ok(entry) => Some(entry),
|
Ok(entry) => Some(entry),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("WARN: Error in walkdir: {err}");
|
warn!("Error during walkdir: {err}");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -52,23 +66,28 @@ impl Minimizer {
|
||||||
path: entry.into_path(),
|
path: entry.into_path(),
|
||||||
})
|
})
|
||||||
.inspect(|file| {
|
.inspect(|file| {
|
||||||
println!("- {}", file.path.display());
|
info!("Collecting file: {}", file.path.display());
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Self { files, build }
|
Self { files, build }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn run_passes<'a>(
|
pub(crate) 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}");
|
info!("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;
|
||||||
|
|
@ -76,27 +95,32 @@ impl Minimizer {
|
||||||
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() {
|
||||||
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());
|
info!("Refreshing files for {}", pass.name());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
println!("Finished {}", pass.name());
|
|
||||||
|
info!("Finished {}", pass.name());
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
refresh_and_try_again = false;
|
refresh_and_try_again = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_file<'file>(
|
fn process_file<'file>(
|
||||||
&self,
|
&self,
|
||||||
pass: &mut dyn Processor,
|
pass: &mut dyn Processor,
|
||||||
|
|
@ -106,18 +130,19 @@ impl Minimizer {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut checker = PassController::new();
|
let mut checker = PassController::new();
|
||||||
loop {
|
loop {
|
||||||
dbg!(& checker);
|
debug!(?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());
|
info!("{file_display}: After {}: {after}", pass.name());
|
||||||
if after.reproduces_issue() {
|
if after.reproduces_issue() {
|
||||||
change.commit();
|
change.commit();
|
||||||
checker.reproduces();
|
checker.reproduces();
|
||||||
|
|
@ -130,10 +155,11 @@ impl Minimizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProcessState::NoChange => {
|
ProcessState::NoChange => {
|
||||||
println!("{file_display}: After {}: no changes", pass.name());
|
info!("{file_display}: After {}: no changes", pass.name());
|
||||||
checker.no_change();
|
checker.no_change();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker.is_finished() {
|
if checker.is_finished() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -141,28 +167,55 @@ impl Minimizer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
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(crate) struct PassController {
|
pub(crate) struct PassController {
|
||||||
state: PassControllerState,
|
state: PassControllerState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PassControllerState {
|
enum PassControllerState {
|
||||||
InitialCollection { candidates: Vec<AstPath> },
|
InitialCollection {
|
||||||
Bisecting { current: HashSet<AstPath>, worklist: Vec<Vec<AstPath>> },
|
candidates: Vec<AstPath>,
|
||||||
|
},
|
||||||
|
Bisecting {
|
||||||
|
committed: BTreeSet<AstPath>,
|
||||||
|
failed: BTreeSet<AstPath>,
|
||||||
|
current: BTreeSet<AstPath>,
|
||||||
|
worklist: Vec<Vec<AstPath>>,
|
||||||
|
},
|
||||||
Success,
|
Success,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_owned<T, From: IntoIterator<Item = T>, A: FromIterator<T>, B: FromIterator<T>>(
|
||||||
|
vec: From,
|
||||||
|
) -> (A, B) {
|
||||||
|
let candidates = vec.into_iter().collect::<Vec<_>>();
|
||||||
|
let half = candidates.len() / 2;
|
||||||
|
|
||||||
|
let mut candidates = candidates.into_iter();
|
||||||
|
|
||||||
|
let first_half = candidates.by_ref().take(half).collect();
|
||||||
|
let second_half = candidates.collect();
|
||||||
|
|
||||||
|
(first_half, second_half)
|
||||||
|
}
|
||||||
|
|
||||||
impl PassController {
|
impl PassController {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -171,41 +224,80 @@ impl PassController {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_in_worklist(&mut self) {
|
||||||
|
match &mut self.state {
|
||||||
|
PassControllerState::Bisecting {
|
||||||
|
current, worklist, ..
|
||||||
|
} => match worklist.pop() {
|
||||||
|
Some(next) => {
|
||||||
|
*current = next.into_iter().collect();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.state = PassControllerState::Success;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => unreachable!("next_in_worklist called on non-bisecting state"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 { current, worklist, .. } => {
|
PassControllerState::Bisecting {
|
||||||
match worklist.pop() {
|
committed,
|
||||||
Some(next) => *current = next.into_iter().collect(),
|
failed: _,
|
||||||
None => {
|
current,
|
||||||
self.state = PassControllerState::Success;
|
worklist: _,
|
||||||
}
|
} => {
|
||||||
}
|
committed.extend(mem::take(current));
|
||||||
|
|
||||||
|
self.next_in_worklist();
|
||||||
}
|
}
|
||||||
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 (current, first_worklist_item) = split_owned(mem::take(candidates));
|
||||||
let half = candidates.len() / 2;
|
|
||||||
let (first_half, second_half) = candidates.split_at(half);
|
self.state = PassControllerState::Bisecting {
|
||||||
self
|
committed: BTreeSet::new(),
|
||||||
.state = PassControllerState::Bisecting {
|
failed: BTreeSet::new(),
|
||||||
current: first_half.iter().cloned().collect(),
|
current,
|
||||||
worklist: vec![second_half.to_owned()],
|
worklist: vec![first_worklist_item],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
PassControllerState::Bisecting { current, worklist } => {
|
PassControllerState::Bisecting {
|
||||||
dbg!(& current, & worklist);
|
committed,
|
||||||
todo!();
|
failed,
|
||||||
|
current,
|
||||||
|
worklist,
|
||||||
|
} => {
|
||||||
|
debug!(?committed, ?current, ?worklist);
|
||||||
|
|
||||||
|
if current.len() == 1 {
|
||||||
|
// We are at a leaf. This is a failure.
|
||||||
|
// FIXME: We should retry the failed ones until a fixpoint is reached.
|
||||||
|
failed.extend(mem::take(current));
|
||||||
|
} else {
|
||||||
|
// Split it further and add it to the worklist.
|
||||||
|
let (first_half, second_half) = split_owned(mem::take(current));
|
||||||
|
|
||||||
|
worklist.push(first_half);
|
||||||
|
worklist.push(second_half);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next_in_worklist()
|
||||||
}
|
}
|
||||||
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 } => {
|
||||||
|
|
@ -216,13 +308,12 @@ impl PassController {
|
||||||
self.state = PassControllerState::Success;
|
self.state = PassControllerState::Success;
|
||||||
}
|
}
|
||||||
PassControllerState::Bisecting { current, .. } => {
|
PassControllerState::Bisecting { current, .. } => {
|
||||||
unreachable!(
|
unreachable!("No change while bisecting, current was empty somehow: {current:?}");
|
||||||
"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,
|
||||||
|
|
@ -230,6 +321,7 @@ impl PassController {
|
||||||
PassControllerState::Success => true,
|
PassControllerState::Success => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn can_process(&mut self, path: &[String]) -> bool {
|
pub(crate) fn can_process(&mut self, path: &[String]) -> bool {
|
||||||
match &mut self.state {
|
match &mut self.state {
|
||||||
PassControllerState::InitialCollection { candidates } => {
|
PassControllerState::InitialCollection { candidates } => {
|
||||||
|
|
@ -243,31 +335,42 @@ impl PassController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! tracking {
|
macro_rules! tracking {
|
||||||
() => {
|
() => {
|
||||||
tracking!(visit_item_fn_mut); tracking!(visit_impl_item_method_mut);
|
tracking!(visit_item_fn_mut);
|
||||||
tracking!(visit_item_impl_mut); tracking!(visit_item_mod_mut);
|
tracking!(visit_impl_item_method_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) { self.current_path
|
fn visit_item_fn_mut(&mut self, func: &mut syn::ItemFn) {
|
||||||
.push(func.sig.ident.to_string()); syn::visit_mut::visit_item_fn_mut(self, func);
|
self.current_path.push(func.sig.ident.to_string());
|
||||||
self.current_path.pop(); }
|
syn::visit_mut::visit_item_fn_mut(self, func);
|
||||||
|
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); self.current_path
|
syn::visit_mut::visit_impl_item_method_mut(self, method);
|
||||||
.pop(); }
|
self.current_path.pop();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
(visit_item_impl_mut) => {
|
(visit_item_impl_mut) => {
|
||||||
fn visit_item_impl_mut(& mut self, item : & mut syn::ItemImpl) { self
|
fn visit_item_impl_mut(&mut self, item: &mut syn::ItemImpl) {
|
||||||
.current_path.push(item.self_ty.clone().into_token_stream().to_string());
|
self.current_path
|
||||||
syn::visit_mut::visit_item_impl_mut(self, item); self.current_path.pop(); }
|
.push(item.self_ty.clone().into_token_stream().to_string());
|
||||||
|
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) { self
|
fn visit_item_mod_mut(&mut self, module: &mut syn::ItemMod) {
|
||||||
.current_path.push(module.ident.to_string());
|
self.current_path.push(module.ident.to_string());
|
||||||
syn::visit_mut::visit_item_mod_mut(self, module); self.current_path.pop(); }
|
syn::visit_mut::visit_item_mod_mut(self, module);
|
||||||
|
self.current_path.pop();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use tracking;
|
pub(crate) use tracking;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ fn file_for_suggestion(suggestion: &Suggestion) -> &str {
|
||||||
impl Minimizer {
|
impl Minimizer {
|
||||||
pub fn delete_dead_code(&mut self) -> Result<()> {
|
pub fn delete_dead_code(&mut self) -> Result<()> {
|
||||||
let inital_build = self.build.build()?;
|
let inital_build = self.build.build()?;
|
||||||
println!("Before reaper: {inital_build}");
|
info!("Before reaper: {inital_build}");
|
||||||
|
|
||||||
inital_build.require_reproduction("Initial")?;
|
inital_build.require_reproduction("Initial")?;
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ impl Minimizer {
|
||||||
|
|
||||||
let after = self.build.build()?;
|
let after = self.build.build()?;
|
||||||
|
|
||||||
println!("{}: After reaper: {after}", file.path.display());
|
info!("{}: After reaper: {after}", file.path.display());
|
||||||
|
|
||||||
if after.reproduces_issue() {
|
if after.reproduces_issue() {
|
||||||
change.commit();
|
change.commit();
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,4 @@ fn unused() {
|
||||||
fn main() {
|
fn main() {
|
||||||
other::unused();
|
other::unused();
|
||||||
}
|
}
|
||||||
|
|
||||||
mod other;
|
mod other;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue