From d9f3f347e90a6590cea6b07658604d91fce7122c Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 17 Dec 2022 18:18:21 +0100 Subject: [PATCH] make things cool and good and wow --- Cargo.lock | 232 ++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 2 + src/build.rs | 54 ++++++++-- src/everybody_loops.rs | 46 ++++++++ src/lib.rs | 45 +++++++- src/main.rs | 10 +- src/privatize.rs | 41 ++++++++ src/processor.rs | 97 +++++++++++++++++ 8 files changed, 486 insertions(+), 41 deletions(-) create mode 100644 src/everybody_loops.rs create mode 100644 src/privatize.rs create mode 100644 src/processor.rs diff --git a/Cargo.lock b/Cargo.lock index 946d256..34ce842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -95,7 +95,7 @@ dependencies = [ "bytesize", "cargo-platform", "cargo-util", - "clap", + "clap 3.2.22", "crates-io", "crossbeam-utils", "curl", @@ -151,10 +151,12 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo", + "clap 4.0.29", "prettyplease", "proc-macro2", "quote", "syn", + "walkdir", ] [[package]] @@ -211,13 +213,41 @@ checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", - "clap_lex", + "clap_lex 0.2.4", "indexmap", "strsim", "termcolor", "textwrap", ] +[[package]] +name = "clap" +version = "4.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex 0.3.0", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -227,6 +257,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "combine" version = "4.6.6" @@ -365,6 +404,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -383,7 +443,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -489,6 +549,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -498,6 +564,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.3.2" @@ -586,6 +661,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -597,9 +694,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "jobserver" @@ -687,6 +784,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "log" version = "0.4.17" @@ -726,7 +829,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -828,14 +931,38 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "prettyplease" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49e86d2c26a24059894a3afa13fd17d063419b05dfb83f06d9c3566060c3f5a" +checksum = "83fead41e178796ef8274dc612a7d8ce4c7e10ca35cd2c5b5ad24cac63aeb6c0" dependencies = [ "proc-macro2", "syn", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.46" @@ -928,6 +1055,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustix" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "ryu" version = "1.0.11" @@ -950,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1051,9 +1192,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -1273,39 +1414,96 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml index 923f312..1a53a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,9 @@ edition = "2021" [dependencies] anyhow = "1.0.65" cargo = "0.65.0" +clap = { version = "4.0.29", features = ["derive"] } prettyplease = "0.1.19" proc-macro2 = "1.0.46" quote = "1.0.21" syn = { version = "1.0.101", features = ["full", "visit", "visit-mut"] } +walkdir = "2.3.2" diff --git a/src/build.rs b/src/build.rs index e15e8dd..d9f19eb 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,28 +1,60 @@ use anyhow::{Context, Result}; -use std::path::PathBuf; +use std::{fmt::Display, path::PathBuf}; +#[derive(Debug)] pub struct Build { - path: PathBuf, + cargo: bool, + script_path: Option, + input_path: PathBuf, } impl Build { - pub fn new(path: impl Into) -> Self { - Self { path: path.into() } + pub fn new(cargo: bool, script_path: Option, input_path: PathBuf) -> Self { + Self { + cargo, + script_path, + input_path, + } } pub fn build(&self) -> Result { - let mut cmd = std::process::Command::new("cargo"); + let reproduces_issue = if self.cargo { + let mut cmd = std::process::Command::new("cargo"); + cmd.arg("build"); - cmd.current_dir(&self.path).arg("build"); + let output = + String::from_utf8(cmd.output().context("spawning rustc process")?.stderr).unwrap(); - let output = cmd.output().context("spawning cargo")?; + output.contains("internal compiler error") + } else if let Some(script_path) = &self.script_path { + let mut cmd = std::process::Command::new(script_path); - Ok(BuildResult { - success: output.status.success(), - }) + cmd.output().context("spawning script")?.status.success() + } else { + let mut cmd = std::process::Command::new("rustc"); + cmd.args(["--edition", "2018"]); + cmd.arg(&self.input_path); + + cmd.output() + .context("spawning rustc process")? + .status + .code() + == Some(101) + }; + + Ok(BuildResult { reproduces_issue }) } } pub struct BuildResult { - pub success: bool, + pub reproduces_issue: bool, +} + +impl Display for BuildResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.reproduces_issue { + true => f.write_str("yes"), + false => f.write_str("no"), + } + } } diff --git a/src/everybody_loops.rs b/src/everybody_loops.rs new file mode 100644 index 0000000..35b2573 --- /dev/null +++ b/src/everybody_loops.rs @@ -0,0 +1,46 @@ +use syn::{parse_quote, visit_mut::VisitMut}; + +use crate::processor::Processor; + +struct Visitor { + loop_expr: syn::Block, + has_made_change: bool, +} + +impl Visitor { + fn new() -> Self { + Self { + has_made_change: false, + loop_expr: parse_quote! { { loop {} } }, + } + } +} + +impl VisitMut for Visitor { + fn visit_block_mut(&mut self, block: &mut syn::Block) { + match block.stmts.as_slice() { + [syn::Stmt::Expr(syn::Expr::Loop(syn::ExprLoop { + body: loop_body, .. + }))] if loop_body.stmts.is_empty() => {} + _ => { + *block = self.loop_expr.clone(); + self.has_made_change = true; + } + } + } +} + +#[derive(Default)] +pub struct EverybodyLoops; + +impl Processor for EverybodyLoops { + fn process_file(&mut self, krate: &mut syn::File) -> bool { + let mut visitor = Visitor::new(); + visitor.visit_file_mut(krate); + visitor.has_made_change + } + + fn name(&self) -> &'static str { + "everybody-loops" + } +} diff --git a/src/lib.rs b/src/lib.rs index c29fc56..9dfa66b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,48 @@ #![allow(dead_code)] -use std::path::Path; +use std::path::PathBuf; mod build; +mod everybody_loops; mod expand; +mod privatize; +mod processor; -use anyhow::{Context, Result}; +use anyhow::Result; +use clap::Parser; +use processor::Minimizer; -pub fn minimize(cargo_dir: &Path) -> Result<()> { - let file = expand::expand(cargo_dir).context("during expansion")?; +use crate::{everybody_loops::EverybodyLoops, privatize::Privarize, processor::Processor}; +#[derive(clap::Parser)] +pub struct Options { + #[arg(short, long)] + verify_error_path: Option, + #[arg(long)] + cargo: bool, + path: PathBuf, +} + +pub fn minimize() -> Result<()> { + let options = Options::parse(); + + let dir = options.path; + + let build = build::Build::new(options.cargo, options.verify_error_path, dir.clone()); + + let mut minimizer = Minimizer::new_glob_dir(&dir, build); + + println!("{minimizer:?}"); + + minimizer.run_passes([ + //Box::new(Privarize::default()) as Box, + Box::new(EverybodyLoops::default()) as Box, + ])?; + + /* + 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"); @@ -17,7 +50,7 @@ pub fn minimize(cargo_dir: &Path) -> Result<()> { std::fs::write("expanded.rs", file)?; println!("wow, expanded"); - Ok(()) + */ /* let build = Build::new(cargo_dir); @@ -26,4 +59,6 @@ pub fn minimize(cargo_dir: &Path) -> Result<()> { bail!("build must initially fail!"); } */ + + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 19faf2c..3e7d641 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,7 @@ -use std::path::Path; - -use anyhow::{Context, Result}; +use anyhow::Result; fn main() -> Result<()> { - let dir = std::env::args().nth(1).context("expected an argument")?; - - cargo_minimize::minimize(&Path::new(&dir))?; - - println!("Exit"); + cargo_minimize::minimize()?; Ok(()) } diff --git a/src/privatize.rs b/src/privatize.rs new file mode 100644 index 0000000..49f5fcc --- /dev/null +++ b/src/privatize.rs @@ -0,0 +1,41 @@ +use syn::{parse_quote, visit_mut::VisitMut, Visibility}; + +use crate::processor::Processor; + +struct Visitor { + pub_crate: Visibility, + has_made_change: bool, +} + +impl Visitor { + fn new() -> Self { + Self { + has_made_change: false, + pub_crate: parse_quote! { pub(crate) }, + } + } +} + +impl VisitMut for Visitor { + fn visit_visibility_mut(&mut self, vis: &mut Visibility) { + if let Visibility::Public(_) = vis { + self.has_made_change = true; + *vis = self.pub_crate.clone(); + } + } +} + +#[derive(Default)] +pub struct Privarize {} + +impl Processor for Privarize { + fn process_file(&mut self, krate: &mut syn::File) -> bool { + let mut visitor = Visitor::new(); + visitor.visit_file_mut(krate); + visitor.has_made_change + } + + fn name(&self) -> &'static str { + "privatize" + } +} diff --git a/src/processor.rs b/src/processor.rs new file mode 100644 index 0000000..f6480b1 --- /dev/null +++ b/src/processor.rs @@ -0,0 +1,97 @@ +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; + +use anyhow::{ensure, Context, Result}; + +use crate::build::Build; + +pub trait Processor { + fn process_file(&mut self, krate: &mut syn::File) -> bool; + + fn name(&self) -> &'static str; +} + +#[derive(Debug)] +pub struct Minimizer { + files: Vec, + build: Build, +} + +impl Minimizer { + pub fn new_glob_dir(path: &Path, build: Build) -> Self { + let walk = walkdir::WalkDir::new(path); + + let files = walk + .into_iter() + .filter_map(|entry| match entry { + Ok(entry) => Some(entry), + Err(err) => { + eprintln!("WARN: Error in walkdir: {err}"); + None + } + }) + .filter(|entry| entry.path().extension() == Some(OsStr::new("rs"))) + .map(|entry| entry.into_path()) + .collect(); + + Self { files, build } + } + + pub fn run_passes<'a>( + &mut self, + passes: impl IntoIterator>, + ) -> Result<()> { + let inital_build = self.build.build()?; + println!("Initial build: {}", inital_build); + ensure!( + inital_build.reproduces_issue, + "Initial build must reproduce issue" + ); + + for mut pass in passes { + 'pass: loop { + println!("Starting a round of {}", pass.name()); + let mut any_change = false; + + for file in &self.files { + let file_display = file.display(); + + let before_string = std::fs::read_to_string(file) + .with_context(|| format!("opening file {file_display}"))?; + + let mut krate = syn::parse_file(&before_string) + .with_context(|| format!("parsing file {file_display}"))?; + + let has_made_change = pass.process_file(&mut krate); + + if has_made_change { + let result = prettyplease::unparse(&krate); + + std::fs::write(file, &result)?; + + let after = self.build.build()?; + + println!("{file_display}: After {}: {after}", pass.name()); + + if after.reproduces_issue { + any_change = true; + } else { + std::fs::write(file, before_string)?; + } + } else { + println!("{file_display}: After {}: no change", pass.name()); + } + } + + if !any_change { + println!("Finished {}", pass.name()); + break 'pass; + } + } + } + + Ok(()) + } +}