From ad7173085a74449555780f3c4a6f016fce34385e Mon Sep 17 00:00:00 2001 From: Lennart Kloock <lennart@scuffle.cloud> Date: Wed, 15 Jan 2025 23:20:17 +0100 Subject: [PATCH 1/4] docs(postcompile): root docs --- crates/postcompile/README.md | 2 -- crates/postcompile/src/lib.rs | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/crates/postcompile/README.md b/crates/postcompile/README.md index a673309be..baad501fc 100644 --- a/crates/postcompile/README.md +++ b/crates/postcompile/README.md @@ -19,8 +19,6 @@ This is particularly useful when making snapshot tests of proc-macros, look belo #[test] fn some_cool_test() { insta::assert_snapshot!(postcompile::compile! { - #![allow(unused)] - #[derive(Debug, Clone)] struct Test { a: u32, diff --git a/crates/postcompile/src/lib.rs b/crates/postcompile/src/lib.rs index 9143b3d72..ab37879a2 100644 --- a/crates/postcompile/src/lib.rs +++ b/crates/postcompile/src/lib.rs @@ -1,3 +1,71 @@ +//! # postcompile +//! +//! A crate which allows you to compile Rust code at runtime (hence the name `postcompile`). +//! +//! What that means is that you can provide the input to `rustc` and then get back the expanded output, compiler errors, warnings, etc. +//! +//! This is particularly useful when making snapshot tests of proc-macros, look below for an example with the `insta` crate. +//! +//! ## Usage +//! +//! ```rs +//! #[test] +//! fn some_cool_test() { +//! insta::assert_snapshot!(postcompile::compile! { +//! #![allow(unused)] +//! +//! #[derive(Debug, Clone)] +//! struct Test { +//! a: u32, +//! b: i32, +//! } +//! +//! const TEST: Test = Test { a: 1, b: 3 }; +//! }); +//! } +//! +//! #[test] +//! fn some_cool_test_extern() { +//! insta::assert_snapshot!(postcompile::compile_str!(include_str!("some_file.rs"))); +//! } +//! ``` +//! +//! ## Features +//! +//! - Cached builds: This crate reuses the cargo build cache of the original crate so that only the contents of the macro are compiled & not any additional dependencies. +//! - Coverage: This crate works with [`cargo-llvm-cov`](https://crates.io/crates/cargo-llvm-cov) out of the box, which allows you to instrument the proc-macro expansion. +//! +//! ## Alternatives +//! +//! - [`compiletest_rs`](https://crates.io/crates/compiletest_rs): This crate is used by the Rust compiler team to test the compiler itself. Not really useful for proc-macros. +//! - [`trybuild`](https://crates.io/crates/trybuild): This crate is an all-in-one solution for testing proc-macros, with built in snapshot testing. +//! - [`ui_test`](https://crates.io/crates/ui_test): Similar to `trybuild` with a slightly different API & used by the Rust compiler team to test the compiler itself. +//! +//! ### Differences +//! +//! The other libraries are focused on testing & have built in test harnesses. This crate takes a step back and allows you to compile without a testing harness. This has the advantage of being more flexible, and allows you to use whatever testing framework you want. +//! +//! In the examples above I showcase how to use this crate with the `insta` crate for snapshot testing. +//! +//! ## Status +//! +//! This crate is currently under development and is not yet stable. +//! +//! Unit tests are not yet fully implemented. Use at your own risk. +//! +//! ## Limitations +//! +//! Please note that this crate does not work inside a running compiler process (inside a proc-macro) without hacky workarounds and complete build-cache invalidation. +//! +//! This is because `cargo` holds a lock on the build directory and that if we were to compile inside a proc-macro we would recursively invoke the compiler. +//! +//! ## License +//! +//! This project is licensed under the [MIT](./LICENSE.MIT) or [Apache-2.0](./LICENSE.Apache-2.0) license. +//! You can choose between one of them if you use this work. +//! +//! `SPDX-License-Identifier: MIT OR Apache-2.0` + use std::borrow::Cow; use std::ffi::{OsStr, OsString}; use std::os::unix::ffi::OsStrExt; From 2636401e53b7a3540a185025fd7497ca19dbcb38 Mon Sep 17 00:00:00 2001 From: Lennart Kloock <lennart@scuffle.cloud> Date: Wed, 15 Jan 2025 23:23:52 +0100 Subject: [PATCH 2/4] fix(postcompile): readme --- crates/postcompile/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/postcompile/README.md b/crates/postcompile/README.md index baad501fc..a673309be 100644 --- a/crates/postcompile/README.md +++ b/crates/postcompile/README.md @@ -19,6 +19,8 @@ This is particularly useful when making snapshot tests of proc-macros, look belo #[test] fn some_cool_test() { insta::assert_snapshot!(postcompile::compile! { + #![allow(unused)] + #[derive(Debug, Clone)] struct Test { a: u32, From 3bed8584855285e33d675905055acd43b43d9480 Mon Sep 17 00:00:00 2001 From: Lennart Kloock <lennart@scuffle.cloud> Date: Wed, 15 Jan 2025 23:24:25 +0100 Subject: [PATCH 3/4] chore(postcompile): fmt --- crates/postcompile/src/lib.rs | 49 +++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/postcompile/src/lib.rs b/crates/postcompile/src/lib.rs index ab37879a2..3c324319d 100644 --- a/crates/postcompile/src/lib.rs +++ b/crates/postcompile/src/lib.rs @@ -1,10 +1,13 @@ //! # postcompile //! -//! A crate which allows you to compile Rust code at runtime (hence the name `postcompile`). +//! A crate which allows you to compile Rust code at runtime (hence the name +//! `postcompile`). //! -//! What that means is that you can provide the input to `rustc` and then get back the expanded output, compiler errors, warnings, etc. +//! What that means is that you can provide the input to `rustc` and then get +//! back the expanded output, compiler errors, warnings, etc. //! -//! This is particularly useful when making snapshot tests of proc-macros, look below for an example with the `insta` crate. +//! This is particularly useful when making snapshot tests of proc-macros, look +//! below for an example with the `insta` crate. //! //! ## Usage //! @@ -32,20 +35,33 @@ //! //! ## Features //! -//! - Cached builds: This crate reuses the cargo build cache of the original crate so that only the contents of the macro are compiled & not any additional dependencies. -//! - Coverage: This crate works with [`cargo-llvm-cov`](https://crates.io/crates/cargo-llvm-cov) out of the box, which allows you to instrument the proc-macro expansion. +//! - Cached builds: This crate reuses the cargo build cache of the original +//! crate so that only the contents of the macro are compiled & not any +//! additional dependencies. +//! - Coverage: This crate works with [`cargo-llvm-cov`](https://crates.io/crates/cargo-llvm-cov) +//! out of the box, which allows you to instrument the proc-macro expansion. //! //! ## Alternatives //! -//! - [`compiletest_rs`](https://crates.io/crates/compiletest_rs): This crate is used by the Rust compiler team to test the compiler itself. Not really useful for proc-macros. -//! - [`trybuild`](https://crates.io/crates/trybuild): This crate is an all-in-one solution for testing proc-macros, with built in snapshot testing. -//! - [`ui_test`](https://crates.io/crates/ui_test): Similar to `trybuild` with a slightly different API & used by the Rust compiler team to test the compiler itself. +//! - [`compiletest_rs`](https://crates.io/crates/compiletest_rs): This crate is +//! used by the Rust compiler team to test the compiler itself. Not really +//! useful for proc-macros. +//! - [`trybuild`](https://crates.io/crates/trybuild): This crate is an +//! all-in-one solution for testing proc-macros, with built in snapshot +//! testing. +//! - [`ui_test`](https://crates.io/crates/ui_test): Similar to `trybuild` with +//! a slightly different API & used by the Rust compiler team to test the +//! compiler itself. //! //! ### Differences //! -//! The other libraries are focused on testing & have built in test harnesses. This crate takes a step back and allows you to compile without a testing harness. This has the advantage of being more flexible, and allows you to use whatever testing framework you want. +//! The other libraries are focused on testing & have built in test harnesses. +//! This crate takes a step back and allows you to compile without a testing +//! harness. This has the advantage of being more flexible, and allows you to +//! use whatever testing framework you want. //! -//! In the examples above I showcase how to use this crate with the `insta` crate for snapshot testing. +//! In the examples above I showcase how to use this crate with the `insta` +//! crate for snapshot testing. //! //! ## Status //! @@ -55,14 +71,19 @@ //! //! ## Limitations //! -//! Please note that this crate does not work inside a running compiler process (inside a proc-macro) without hacky workarounds and complete build-cache invalidation. +//! Please note that this crate does not work inside a running compiler process +//! (inside a proc-macro) without hacky workarounds and complete build-cache +//! invalidation. //! -//! This is because `cargo` holds a lock on the build directory and that if we were to compile inside a proc-macro we would recursively invoke the compiler. +//! This is because `cargo` holds a lock on the build directory and that if we +//! were to compile inside a proc-macro we would recursively invoke the +//! compiler. //! //! ## License //! -//! This project is licensed under the [MIT](./LICENSE.MIT) or [Apache-2.0](./LICENSE.Apache-2.0) license. -//! You can choose between one of them if you use this work. +//! This project is licensed under the [MIT](./LICENSE.MIT) or +//! [Apache-2.0](./LICENSE.Apache-2.0) license. You can choose between one of +//! them if you use this work. //! //! `SPDX-License-Identifier: MIT OR Apache-2.0` From 27d93a9fc70d48e285d1b01c639a9ddfdf652e07 Mon Sep 17 00:00:00 2001 From: Lennart Kloock <lennart@scuffle.cloud> Date: Mon, 20 Jan 2025 21:08:09 +0100 Subject: [PATCH 4/4] test(postcompile): add tests --- Cargo.lock | 1 + crates/postcompile/Cargo.toml | 7 +- crates/postcompile/src/lib.rs | 70 ++++++++++++++++++- .../postcompile__tests__compile_failure.snap | 14 ++++ .../postcompile__tests__compile_success.snap | 18 +++++ 5 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 crates/postcompile/src/snapshots/postcompile__tests__compile_failure.snap create mode 100644 crates/postcompile/src/snapshots/postcompile__tests__compile_success.snap diff --git a/Cargo.lock b/Cargo.lock index ddd4dbc8b..98fcd7ecc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2135,6 +2135,7 @@ version = "0.0.5" dependencies = [ "cargo-platform", "cargo_metadata", + "insta", "prettyplease", "scuffle-workspace-hack", "serde", diff --git a/crates/postcompile/Cargo.toml b/crates/postcompile/Cargo.toml index e3541798b..88375bffd 100644 --- a/crates/postcompile/Cargo.toml +++ b/crates/postcompile/Cargo.toml @@ -11,6 +11,9 @@ license = "MIT OR Apache-2.0" description = "Helper crate for post-compiling Rust code." keywords = ["postcompile", "snapshot", "test", "proc-macro"] +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(trybuild_no_target)', 'cfg(postcompile_no_target)', 'cfg(coverage_nightly)'] } + [dependencies] serde_json = "1.0" cargo_metadata = "0.19.1" @@ -22,8 +25,8 @@ prettyplease = { version = "0.2", optional = true } syn = { version = "2", features = ["full"] } scuffle-workspace-hack.workspace = true -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(trybuild_no_target)', 'cfg(postcompile_no_target)'] } +[dev-dependencies] +insta = "1.42.0" [features] prettyplease = ["dep:prettyplease"] diff --git a/crates/postcompile/src/lib.rs b/crates/postcompile/src/lib.rs index 3c324319d..4d156d973 100644 --- a/crates/postcompile/src/lib.rs +++ b/crates/postcompile/src/lib.rs @@ -1,5 +1,3 @@ -//! # postcompile -//! //! A crate which allows you to compile Rust code at runtime (hence the name //! `postcompile`). //! @@ -86,6 +84,7 @@ //! them if you use this work. //! //! `SPDX-License-Identifier: MIT OR Apache-2.0` +#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))] use std::borrow::Cow; use std::ffi::{OsStr, OsString}; @@ -383,3 +382,70 @@ macro_rules! try_compile_str { $crate::compile_custom($expr, &$crate::_config!()) }; } + +#[cfg(test)] +#[cfg_attr(all(test, coverage_nightly), coverage(off))] +mod tests { + use insta::assert_snapshot; + + use crate::ExitStatus; + + #[test] + fn compile_success() { + let out = compile! { + #[allow(unused)] + fn main() { + let a = 1; + let b = 2; + let c = a + b; + } + }; + + assert_eq!(out.status, ExitStatus::Success); + assert!(out.stderr.is_empty()); + assert_snapshot!(out); + } + + #[test] + fn try_compile_success() { + let out = try_compile! { + #[allow(unused)] + fn main() { + let xd = 0xd; + let xdd = 0xdd; + let xddd = xd + xdd; + println!("{}", xddd); + } + }; + + assert!(out.is_ok()); + let out = out.unwrap(); + assert_eq!(out.status, ExitStatus::Success); + assert!(out.stderr.is_empty()); + assert!(!out.stdout.is_empty()); + } + + #[test] + fn compile_failure() { + let out = compile! { + invalid_rust_code + }; + + assert_eq!(out.status, ExitStatus::Failure(1)); + assert!(out.stdout.is_empty()); + assert_snapshot!(out); + } + + #[test] + fn try_compile_failure() { + let out = try_compile! { + invalid rust code + }; + + assert!(out.is_ok()); + let out = out.unwrap(); + assert_eq!(out.status, ExitStatus::Failure(1)); + assert!(out.stdout.is_empty()); + assert!(!out.stderr.is_empty()); + } +} diff --git a/crates/postcompile/src/snapshots/postcompile__tests__compile_failure.snap b/crates/postcompile/src/snapshots/postcompile__tests__compile_failure.snap new file mode 100644 index 000000000..1fa9ab347 --- /dev/null +++ b/crates/postcompile/src/snapshots/postcompile__tests__compile_failure.snap @@ -0,0 +1,14 @@ +--- +source: crates/postcompile/src/lib.rs +expression: out +snapshot_kind: text +--- +exit status: 1 +--- stderr +error: expected one of `!` or `::`, found `<eof>` + --> <postcompile>:1:1 + | +1 | invalid_rust_code + | ^^^^^^^^^^^^^^^^^ expected one of `!` or `::` + +error: aborting due to 1 previous error diff --git a/crates/postcompile/src/snapshots/postcompile__tests__compile_success.snap b/crates/postcompile/src/snapshots/postcompile__tests__compile_success.snap new file mode 100644 index 000000000..b71eceaf4 --- /dev/null +++ b/crates/postcompile/src/snapshots/postcompile__tests__compile_success.snap @@ -0,0 +1,18 @@ +--- +source: crates/postcompile/src/lib.rs +expression: out +snapshot_kind: text +--- +exit status: 0 +--- stdout +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +#[allow(unused)] +fn main() { + let a = 1; + let b = 2; + let c = a + b; +}