prefix-stmts 1.0.0

Macro to prefix several statements with the given tokens.
Documentation
//! A utility macro to prefix each of several statements with the given tokens.
//!
//! Doing this with items is straightforward: the `item`
//! [fragment specifier](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.meta.specifier)
//! in [`macro_rules!`](https://doc.rust-lang.org/reference/macros-by-example.html) works as expected.
//! With statements however, things get tricky. Since the `stmt`
//! [fragment specifier](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.meta.specifier)
//! also matches lone semicolons (which are no-op statements), and doesn't match trailing
//! semicolons, the solution isn't trivial.
//!
//! After getting around the limitation, you'll be surprised to find the Rust compiler gets really
//! confused with cryptic errors despite everything seeming right. After much trial and error, I
//! found a procedural macro with special logic for
//! [`Delimiter::None`](proc_macro::Delimiter::None) seems to work right.
//!
//! This macro is implemented as [`prefix_stmts!`]. It's a wrapper around the actual implementation,
//! which first separates input statements through the `stmt`
//! [fragment specifier](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.meta.specifier),
//! and then forwards them to a procedural macro
//! ([`emit_prefixed_tokens_except_starts_with_semicolon`](prefix_stmts_proc_macro::emit_prefixed_tokens_except_starts_with_semicolon)),
//! which does exactly what the name says (with the [`Delimiter::None`](proc_macro::Delimiter::None)
//! trick).
//!
//! If you find a way to do this without a procedural macro, please do let me know!
//!
//! ```rust
//! use prefix_stmts::prefix_stmts;
//!
//! fn void() {}
//!
//! let mut count: usize = 0;
//!
//! prefix_stmts! {
//! 	[ count += 1; ]
//!
//! 	;;;;;;;;;;;;;;;;; // Lone semicolons (noop, don't prefix)
//!
//! 	pub(crate) fn foo() {}
//!
//! 	let _: usize = 42;
//!
//! 	void();
//!
//! 	{ foo();; void() };;;
//!
//! 	{ ( { void() } );; foo() }
//! }
//!
//! assert_eq!(count, 5);
//! ```
//!
//! My whole motivation for making this was allowing several statements to be prefixed with
//! [`cfg`](https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index)
//! attributes without repeating the attribute for each statement.
//!
//! ```rust
//! use prefix_stmts::prefix_stmts;
//!
//! prefix_stmts! {
//! 	[ #[cfg( all() )] ]
//!
//! 	const FOO: bool = true;
//! }
//!
//! prefix_stmts! {
//! 	[ #[cfg( not( all() ) )] ]
//!
//! 	const FOO: bool = false;
//! }
//!
//! assert_eq!(FOO, true);
//! ```
//!
//! The Minimum Supported Rust Version (MSRV) for this crate is
//! [Rust 1.45](https://blog.rust-lang.org/2020/07/16/Rust-1.45.0/), which added support for
//! [statements emitted by procedural macros](https://blog.rust-lang.org/2020/07/16/Rust-1.45.0/#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements).
//! Do mind that Rust <= [1.47](https://blog.rust-lang.org/2020/10/08/Rust-1.47/) has issues with
//! hygiene from the procedural macro, so using at least
//! [Rust 1.48](https://blog.rust-lang.org/2020/11/19/Rust-1.48/) is recommended.

#![no_std]

#[cfg(doc)]
extern crate proc_macro;

#[doc(hidden)]
pub mod __ {
	pub use ::prefix_stmts_proc_macro::emit_prefixed_tokens_except_starts_with_semicolon;

	pub use super::prefix_stmts;
}

/// Prefixes each statement with the given prefix tokens. First provide the tokens to prefix wrapped
/// in `[ brackets ]`, then follow with the statements.
///
/// ```rust
/// use prefix_stmts::prefix_stmts;
///
/// prefix_stmts! {
/// 	[ #[cfg( all() )] ]
///
/// 	const FOO: bool = true;
/// }
///
/// prefix_stmts! {
/// 	[ #[cfg( not( all() ) )] ]
///
/// 	const FOO: bool = false;
/// }
///
/// assert_eq!(FOO, true);
/// ```
///
/// See the [crate documentation](crate) for more details.
#[macro_export]
macro_rules! prefix_stmts {
	(
		[ $($prefix:tt)* ]
		$($statements:stmt)*
	) => {
		$crate::__::emit_prefixed_tokens_except_starts_with_semicolon! (
			[ $($prefix)* ]
			$( ( $statements ) )*
		);
	};
}

#[cfg(test)]
mod tests {
	#[test]
	fn prefixes_for_each_stmt() -> Result<(), ()> {
		let mut count: usize = 0;

		let mut raise_count = || {
			count += 1;
		};

		macro_rules! dummy_macro {
			() => {};
		}

		// https://doc.rust-lang.org/reference/statements.html
		#[allow(unused)]
		loop {
			crate::prefix_stmts! (
				[ raise_count(); ]

				// Lone semicolons (noop, don't prefix)
				;;;;;;;;;;;;;;;;;

				// Items (20)
				#[cfg(all())]
				#[cfg(all())]
				pub(crate) mod foo {}

				pub(crate) mod bar {};

				extern crate core;

				use core::option;;;

				#[inline(always)]
				fn qux<T: ?Sized>(v: &T) -> &T { { (v) } };

				pub(crate) fn void() {}

				type Alias = usize;;

				struct Quz;

				#[repr(C)]
				struct Quux<T>(T);

				enum Corge {
					A = 60,
					B,
				}

				union Grault {
					a: u32,
					b: f32,
				};

				const _: Alias = 2 + 2;;;

				static DAKOTA: Alias = (3 + 3);

				trait Garply: Sized {
					fn foo(&self);
				}

				impl Garply for Quz {
					fn foo(&self) {
						void();
					}
				}

				extern "C" {}

				dummy_macro!();

				dummy_macro![];

				dummy_macro! {}

				dummy_macro! {};

				// Some more unnecessary semicolons for good measure
				;;;;;;;;;;

				// Let statement (3)
				let hi_dakota: Alias = DAKOTA;

				let (a, b, c) = (1, 2, 3);

				let _ = ((()));

				// Expression statement (17)

				hi_dakota + 1;

				(qux(&hi_dakota));

				{ qux(&hi_dakota); qux(&hi_dakota) };

				{ qux(&hi_dakota); qux(&hi_dakota); }

				{ void() }

				[void(),];

				(void());

				(void(),);

				"The real victor of WW2 was the Soviet Union.";

				Some::<()>;

				'thingmabob: loop {
					break;
				}

				loop {
					break 42
				};

				unsafe {
					void()
				}

				unsafe {
					qux(&hi_dakota)
				};

				unsafe {
					void();
				}

				23..0;

				break
			);
		}

		assert_eq!(count, 40);

		Ok(())
	}
}