From de92884c1d2b65bc8f9cb6e47774ce8db64f7b07 Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Thu, 13 Jun 2024 17:20:32 -0700 Subject: [PATCH 1/6] Single-compile testing framework --- crates/paralegal-flow/src/test_utils.rs | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/crates/paralegal-flow/src/test_utils.rs b/crates/paralegal-flow/src/test_utils.rs index 77069981a9..239a318b44 100644 --- a/crates/paralegal-flow/src/test_utils.rs +++ b/crates/paralegal-flow/src/test_utils.rs @@ -167,6 +167,59 @@ macro_rules! define_flow_test_template { }; } +pub struct InlineTestBuilder { + ctrl_name: String, + input: String, +} + +impl InlineTestBuilder { + pub fn new(input: impl Into) -> Self { + Self { + input: input.into(), + ctrl_name: "main".into(), + } + } + + pub fn check(&self, check: impl FnOnce(CtrlRef) + Send) { + #[derive(clap::Parser)] + struct TopLevelArgs { + #[clap(flatten)] + args: ClapArgs, + } + + // TODO make this --analyze work + let args = Args::try_from( + TopLevelArgs::parse_from([ + "".into(), + "--analyze".into(), + format!("{}::{}", DUMMY_MOD_NAME, self.ctrl_name), + ]) + .args, + ) + .unwrap(); + + args.setup_logging(); + + rustc_utils::test_utils::compile_with_args( + &self.input, + [ + "--cfg", + "paralegal", + "-Zcrate-attr=feature(register_tool)", + "-Zcrate-attr=register_tool(paralegal_flow)", + ], + move |tcx| { + let mut memo = Callbacks::new(Box::leak(Box::new(args))); + memo.persist_metadata = false; + let pdg = memo.run_compilation(tcx).unwrap().unwrap(); + let graph = PreFrg::from_description(pdg); + let cref = graph.ctrl(&self.ctrl_name); + check(cref) + }, + ) + } +} + pub trait HasGraph<'g>: Sized + Copy { fn graph(self) -> &'g PreFrg; From 97d58218cafa4031592d09acbb8ba525b79376eb Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Fri, 14 Jun 2024 14:21:27 -0700 Subject: [PATCH 2/6] Documentation and rustc_utils update --- Cargo.lock | 8 +++--- Cargo.toml | 4 +-- crates/paralegal-flow/src/test_utils.rs | 36 ++++++++++++++++++++----- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c474f58a9..53acc2a8c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1110,12 +1110,12 @@ name = "rustc_plugin" version = "0.7.4-nightly-2023-08-25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1348edfa020dbe4807a4d99272332dadcbbedff6b587accb95faefe20d2c7129" -replace = "rustc_plugin 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864)" +replace = "rustc_plugin 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=d4fefb5c0344cdf4812b4877d5b03cb19a2c4672)" [[package]] name = "rustc_plugin" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864#aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=d4fefb5c0344cdf4812b4877d5b03cb19a2c4672#d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" dependencies = [ "cargo_metadata", "log", @@ -1136,12 +1136,12 @@ name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09428c7086894369685cca54a516acc0f0ab6d0e5a628c094ba83bfddaf1aedf" -replace = "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864)" +replace = "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=d4fefb5c0344cdf4812b4877d5b03cb19a2c4672)" [[package]] name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864#aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=d4fefb5c0344cdf4812b4877d5b03cb19a2c4672#d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 2c392735d4..08a3979ed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ debug = true # "rustc_utils:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_utils" } # "rustc_plugin:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_plugin" } -"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864", features = [ +"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672", features = [ "indexical", ] } -"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" } +"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" } diff --git a/crates/paralegal-flow/src/test_utils.rs b/crates/paralegal-flow/src/test_utils.rs index 239a318b44..dd2ffc3ffb 100644 --- a/crates/paralegal-flow/src/test_utils.rs +++ b/crates/paralegal-flow/src/test_utils.rs @@ -167,12 +167,25 @@ macro_rules! define_flow_test_template { }; } +/// Builder for running test cases against a string of source code. +/// +/// Start with [`InlineTestBuilder::new`], compile and run the test case with +/// [`InlineTestBuilder::check`]. pub struct InlineTestBuilder { ctrl_name: String, input: String, } impl InlineTestBuilder { + /// Constructor. + /// + /// Note that this test builder does not support specifying dependencies, + /// including the `paralegal` library. As such use raw annotations like + /// `#[paralegal_flow::marker(...)]`. + /// + /// By default a `main` function is used as the analysis target (even + /// without an `analyze` annotation). Use + /// [`InlineTestBuilder::with_entrypoint`] to use a different function. pub fn new(input: impl Into) -> Self { Self { input: input.into(), @@ -180,6 +193,16 @@ impl InlineTestBuilder { } } + /// Chose a function as analysis entrypoint. Overwrites any previous choice + /// without warning. + pub fn with_entrypoint(&mut self, name: impl into) -> &mut Self { + self.ctrl_name = name.into(); + self + } + + /// Compile the code, select the [`CtrlRef`] corresponding to the configured + /// entrypoint and hand it to the `check` function which should contain the + /// test predicate. pub fn check(&self, check: impl FnOnce(CtrlRef) + Send) { #[derive(clap::Parser)] struct TopLevelArgs { @@ -200,23 +223,22 @@ impl InlineTestBuilder { args.setup_logging(); - rustc_utils::test_utils::compile_with_args( - &self.input, - [ + rustc_utils::test_utils::CompileBuilder::new(&self.input) + .with_args([ "--cfg", "paralegal", "-Zcrate-attr=feature(register_tool)", "-Zcrate-attr=register_tool(paralegal_flow)", - ], - move |tcx| { + ]) + .compile(move |result| { + let tcx = result.tcx; let mut memo = Callbacks::new(Box::leak(Box::new(args))); memo.persist_metadata = false; let pdg = memo.run_compilation(tcx).unwrap().unwrap(); let graph = PreFrg::from_description(pdg); let cref = graph.ctrl(&self.ctrl_name); check(cref) - }, - ) + }); } } From a7b96163207547f6ef6dc6a26274fba8d917a7b7 Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Sat, 15 Jun 2024 15:55:31 -0700 Subject: [PATCH 3/6] Minor fixes --- .../flowistry_pdg_construction/tests/pdg.rs | 6 ++- crates/paralegal-flow/Cargo.toml | 1 + crates/paralegal-flow/src/args.rs | 17 ++++++ crates/paralegal-flow/src/lib.rs | 13 +---- crates/paralegal-flow/src/test_utils.rs | 52 ++++++++++++------- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/crates/flowistry_pdg_construction/tests/pdg.rs b/crates/flowistry_pdg_construction/tests/pdg.rs index 5cbc603dc1..f9b0bf6dd1 100644 --- a/crates/flowistry_pdg_construction/tests/pdg.rs +++ b/crates/flowistry_pdg_construction/tests/pdg.rs @@ -17,7 +17,9 @@ use rustc_middle::{ mir::{Terminator, TerminatorKind}, ty::TyCtxt, }; -use rustc_utils::{mir::borrowck_facts, source_map::find_bodies::find_bodies}; +use rustc_utils::{ + mir::borrowck_facts, source_map::find_bodies::find_bodies, test_utils::CompileResult, +}; fn get_main(tcx: TyCtxt<'_>) -> LocalDefId { find_bodies(tcx) @@ -36,7 +38,7 @@ fn pdg( tests: impl for<'tcx> FnOnce(TyCtxt<'tcx>, DepGraph<'tcx>) + Send, ) { let _ = env_logger::try_init(); - rustc_utils::test_utils::compile(input, move |tcx| { + rustc_utils::test_utils::CompileBuilder::new(input).compile(move |CompileResult { tcx }| { let def_id = get_main(tcx); let params = configure(tcx, PdgParams::new(tcx, def_id).unwrap()); let pdg = flowistry_pdg_construction::compute_pdg(params); diff --git a/crates/paralegal-flow/Cargo.toml b/crates/paralegal-flow/Cargo.toml index 85edf0cb5c..3442dd77cf 100644 --- a/crates/paralegal-flow/Cargo.toml +++ b/crates/paralegal-flow/Cargo.toml @@ -59,6 +59,7 @@ chrono = "0.4" [dev-dependencies] paralegal-flow = { path = ".", features = ["test"] } +rustc_utils = { workspace = true, features = ["test"] } [[bin]] name = "cargo-paralegal-flow" diff --git a/crates/paralegal-flow/src/args.rs b/crates/paralegal-flow/src/args.rs index ddbe7553c8..ebb6d30d0d 100644 --- a/crates/paralegal-flow/src/args.rs +++ b/crates/paralegal-flow/src/args.rs @@ -374,6 +374,23 @@ impl Args { pub fn attach_to_debugger(&self) -> Option { self.attach_to_debugger } + + pub fn setup_logging(&self) { + let lvl = self.verbosity(); + // //let lvl = log::LevelFilter::Debug; + if simple_logger::SimpleLogger::new() + .with_level(lvl) + .with_module_level("flowistry", lvl) + .with_module_level("rustc_utils", log::LevelFilter::Error) + .without_timestamps() + .init() + .is_ok() + { + if matches!(*self.direct_debug(), LogLevelConfig::Targeted(..)) { + log::set_max_level(log::LevelFilter::Warn); + } + } + } } #[derive(serde::Serialize, serde::Deserialize, clap::Args, Default)] diff --git a/crates/paralegal-flow/src/lib.rs b/crates/paralegal-flow/src/lib.rs index ddae56114e..1ff4768433 100644 --- a/crates/paralegal-flow/src/lib.rs +++ b/crates/paralegal-flow/src/lib.rs @@ -350,17 +350,8 @@ impl rustc_plugin::RustcPlugin for DfppPlugin { return rustc_driver::RunCompiler::new(&compiler_args, &mut NoopCallbacks {}).run(); } - let lvl = plugin_args.verbosity(); - // //let lvl = log::LevelFilter::Debug; - simple_logger::SimpleLogger::new() - .with_level(lvl) - //.with_module_level("flowistry", lvl) - .with_module_level("rustc_utils", log::LevelFilter::Error) - .init() - .unwrap(); - if matches!(*plugin_args.direct_debug(), LogLevelConfig::Targeted(..)) { - log::set_max_level(log::LevelFilter::Warn); - } + plugin_args.setup_logging(); + let opts = Box::leak(Box::new(plugin_args)); compiler_args.extend([ diff --git a/crates/paralegal-flow/src/test_utils.rs b/crates/paralegal-flow/src/test_utils.rs index dd2ffc3ffb..9fa1d4e6b1 100644 --- a/crates/paralegal-flow/src/test_utils.rs +++ b/crates/paralegal-flow/src/test_utils.rs @@ -195,7 +195,7 @@ impl InlineTestBuilder { /// Chose a function as analysis entrypoint. Overwrites any previous choice /// without warning. - pub fn with_entrypoint(&mut self, name: impl into) -> &mut Self { + pub fn with_entrypoint(&mut self, name: impl Into) -> &mut Self { self.ctrl_name = name.into(); self } @@ -204,18 +204,23 @@ impl InlineTestBuilder { /// entrypoint and hand it to the `check` function which should contain the /// test predicate. pub fn check(&self, check: impl FnOnce(CtrlRef) + Send) { + use clap::Parser; + #[derive(clap::Parser)] struct TopLevelArgs { #[clap(flatten)] - args: ClapArgs, + args: crate::ClapArgs, } - // TODO make this --analyze work - let args = Args::try_from( + let args = crate::Args::try_from( TopLevelArgs::parse_from([ "".into(), "--analyze".into(), - format!("{}::{}", DUMMY_MOD_NAME, self.ctrl_name), + format!( + "{}::{}", + rustc_utils::test_utils::DUMMY_MOD_NAME, + self.ctrl_name + ), ]) .args, ) @@ -224,17 +229,20 @@ impl InlineTestBuilder { args.setup_logging(); rustc_utils::test_utils::CompileBuilder::new(&self.input) - .with_args([ - "--cfg", - "paralegal", - "-Zcrate-attr=feature(register_tool)", - "-Zcrate-attr=register_tool(paralegal_flow)", - ]) + .with_args( + [ + "--cfg", + "paralegal", + "-Zcrate-attr=feature(register_tool)", + "-Zcrate-attr=register_tool(paralegal_flow)", + ] + .into_iter() + .map(ToOwned::to_owned), + ) .compile(move |result| { let tcx = result.tcx; - let mut memo = Callbacks::new(Box::leak(Box::new(args))); - memo.persist_metadata = false; - let pdg = memo.run_compilation(tcx).unwrap().unwrap(); + let memo = crate::Callbacks::new(Box::leak(Box::new(args))); + let pdg = memo.run(tcx).unwrap(); let graph = PreFrg::from_description(pdg); let cref = graph.ctrl(&self.ctrl_name); check(cref) @@ -339,14 +347,18 @@ impl PreFrg { crate::consts::FLOW_GRAPH_OUT_NAME )) .unwrap(); - let name_map = desc - .def_info - .iter() - .map(|(def_id, info)| (info.name, *def_id)) - .into_group_map(); - Self { desc, name_map } + Self::from_description(desc) }) } + + pub fn from_description(desc: ProgramDescription) -> Self { + let name_map = desc + .def_info + .iter() + .map(|(def_id, info)| (info.name, *def_id)) + .into_group_map(); + Self { desc, name_map } + } } #[derive(Clone)] From 49c86709554364fa1206a02f5a86821089190f41 Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Sat, 15 Jun 2024 16:27:36 -0700 Subject: [PATCH 4/6] Replace needs this --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 08a3979ed3..b0992e6439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,5 +31,6 @@ debug = true "rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672", features = [ "indexical", + "test", ] } "rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" } From b6aa87f3cbbf3b1fa3cad06a59da9ab10f3b384a Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Sat, 15 Jun 2024 17:22:14 -0700 Subject: [PATCH 5/6] Mucking about with features --- Cargo.toml | 5 +---- crates/paralegal-flow/Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b0992e6439..1f8e38ed1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,5 @@ debug = true # "rustc_utils:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_utils" } # "rustc_plugin:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_plugin" } -"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672", features = [ - "indexical", - "test", -] } +"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" } "rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" } diff --git a/crates/paralegal-flow/Cargo.toml b/crates/paralegal-flow/Cargo.toml index 3442dd77cf..255f6079f9 100644 --- a/crates/paralegal-flow/Cargo.toml +++ b/crates/paralegal-flow/Cargo.toml @@ -9,7 +9,7 @@ description = "Extractor for precise semantic PDGs from rust code." rustc_private = true [features] -test = [] +test = ["rustc_utils/test"] [dependencies] paralegal-spdg = { path = "../paralegal-spdg", features = ["rustc"] } @@ -59,7 +59,6 @@ chrono = "0.4" [dev-dependencies] paralegal-flow = { path = ".", features = ["test"] } -rustc_utils = { workspace = true, features = ["test"] } [[bin]] name = "cargo-paralegal-flow" From e95b57bebbfa264090a2075112e2e5ac56ed6b93 Mon Sep 17 00:00:00 2001 From: Justus Adam Date: Mon, 24 Jun 2024 20:42:17 -0400 Subject: [PATCH 6/6] Clippy --- crates/paralegal-flow/src/args.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/paralegal-flow/src/args.rs b/crates/paralegal-flow/src/args.rs index ebb6d30d0d..bbe3a9b2b4 100644 --- a/crates/paralegal-flow/src/args.rs +++ b/crates/paralegal-flow/src/args.rs @@ -385,10 +385,9 @@ impl Args { .without_timestamps() .init() .is_ok() + && matches!(*self.direct_debug(), LogLevelConfig::Targeted(..)) { - if matches!(*self.direct_debug(), LogLevelConfig::Targeted(..)) { - log::set_max_level(log::LevelFilter::Warn); - } + log::set_max_level(log::LevelFilter::Warn); } } }