1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
//! This crate provides the ability to write procedural macros directly in your code, instead of
//! having to use another crate.
//!
//! [Repo](https://github.com/KaiJewson/inline-proc) - [crates.io](https://crates.io/crates/inline-proc) - [documentation](https://docs.rs/inline-proc)
//!
//! # Example
//! ```
//! use inline_proc::inline_proc;
//!
//! #[inline_proc]
//! mod example {
//! metadata::ron!(
//! edition: "2021",
//! clippy: true,
//! dependencies: {
//! "quote": "1",
//! },
//! exports: (
//! bang_macros: {
//! "def_func": "define_function",
//! },
//! ),
//! );
//!
//! pub fn define_function(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
//! quote::quote!(
//! fn macro_function() {
//! println!("Hello from a proc macro!");
//! }
//! ).into()
//! }
//! }
//!
//! def_func!();
//!
//! macro_function();
//! // => Hello from a proc macro!
//! ```
//!
//! # How It Works
//!
//! `inline-proc` takes your code and puts it in a crate with the path
//! `{temporary directory}/inline-proc-crates/{package name}-{significant package version}-{module name}`.
//! For example, if you call `inline-proc` on Linux from the module `my_module` in `my-nice-crate`
//! which has version `0.7.3`, a temporary crate will be created in
//! `/tmp/inline-proc-crates/my-nice-crate-0.7-my_module`.
//!
//! It then compiles this crate as a `dylib` with Cargo and translates all the outputted errors into
//! errors from the proc macro, so it appears identical to writing the code inline. Note that proc
//! macros cannot currently emit warnings on stable, so you will have to use nightly if you want
//! that.
//!
//! It outputs `macro_rules!` macros that expand to invocations of the private
//! `inline_proc::invoke_inline_macro!` macro. This macro takes in the path of a dylib generated by
//! the `inline_proc` attribute macro, the name of the macro that is inside that dylib, the type of
//! macro that it is (bang/derive/attribute) and the input to the macro. It opens up the dylib and
//! calls the macro, returning its result.
//!
//! # Using the generated macros
//!
//! The macros generated by `#[inline_proc]` can be used directly:
//!
//! ```
//! use inline_proc::inline_proc;
//!
//! #[inline_proc]
//! mod direct_usage {
//! metadata::ron!(
//! edition: "2021",
//! dependencies: {},
//! exports: (
//! bang_macros: { "my_bang_macro": "my_bang_macro" },
//! derives: { "MyDeriveMacro": "my_derive_macro" },
//! attributes: { "my_attribute_macro": "my_attribute_macro" },
//! ),
//! );
//! use proc_macro::TokenStream;
//!
//! pub fn my_bang_macro(_input: TokenStream) -> TokenStream {
//! # return TokenStream::new();
//! todo!()
//! }
//! pub fn my_derive_macro(_item: TokenStream) -> TokenStream {
//! # return TokenStream::new();
//! todo!()
//! }
//! pub fn my_attribute_macro(_attr: TokenStream, _item: TokenStream) -> TokenStream {
//! # return TokenStream::new();
//! todo!()
//! }
//! }
//!
//! my_bang_macro!(input tokens);
//! MyDeriveMacro!(struct InnerItem;);
//! my_attribute_macro!((attribute tokens) struct InnerItem;);
//! ```
//!
//! This works fine for bang macros, but is not so good for attribute or derive macros. So this
//! crate provides the attribute and derive macros `#[inline_attr]` and `InlineDerive`; they can be
//! used like this:
//!
//! ```
//! # use inline_proc::inline_proc;
//! # #[inline_proc]
//! # mod indirect_usage {
//! # metadata::ron!(
//! # edition: "2021",
//! # dependencies: {},
//! # exports: (
//! # derives: { "MyDeriveMacro": "my_derive_macro" },
//! # attributes: { "my_attribute_macro": "my_attribute_macro" },
//! # ),
//! # );
//! # use proc_macro::TokenStream;
//! #
//! # pub fn my_derive_macro(_item: TokenStream) -> TokenStream {
//! # TokenStream::new()
//! # }
//! # pub fn my_attribute_macro(_attr: TokenStream, _item: TokenStream) -> TokenStream {
//! # TokenStream::new()
//! # }
//! # }
//! use inline_proc::{InlineDerive, inline_attr};
//!
//! #[derive(InlineDerive)]
//! #[inline_derive(MyDeriveMacro)]
//! struct InnerItem;
//!
//! #[inline_attr[my_attribute_macro(attribute tokens)]]
//! struct InnerItem;
//! ```
//!
//! They expand to the same code as above.
//!
//! # Exporting the macros
//!
//! In order to export your macro, you will first have to change your macro definition to:
//! `"macro_name": ( function: "macro_function", export: true )`. This will do three things:
//!
//! 1. Label the generated `macro_rules!` with `#[macro_export]` and `#[doc(hidden)]`.
//! 1. Have the macro take a path to `inline_proc::invoke_inline_macro`.
//! 1. Suffix the macro's name with `_inner`.
//!
//! You then create a wrapper around it like so:
//!
//! ```
//! # #[inline_proc::inline_proc]
//! # mod macro_export {
//! # metadata::ron!(
//! # edition: "2021",
//! # dependencies: {},
//! # exports: (
//! # bang_macros: { "my_macro": ( function: "my_macro", export: true ) },
//! # ),
//! # );
//! # pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
//! # input
//! # }
//! # }
//! // At the crate root
//!
//! #[doc(hidden)]
//! pub use inline_proc::invoke_inline_macro;
//!
//! // Where your #[inline_proc] is
//!
//! /// This macro does XYZ.
//! #[macro_export]
//! macro_rules! my_macro {
//! ($($tt:tt)*) => {
//! $crate::my_macro_inner!($crate::invoke_inline_macro, $($tt)*);
//! }
//! }
//! ```
//!
//! This level of indirection is necessary as proc macros don't have a way of getting the current
//! crate like MBEs do (`$crate`), so you have to supply it via the MBE method.
//!
//! # Crate attributes
//!
//! Inline procedural macros support inner crate attributes.
//!
//! ```
//! use inline_proc::inline_proc;
//!
//! #[inline_proc]
//! mod crate_attributes {
//! #![feature(box_syntax)]
//!
//! metadata::ron!(
//! edition: "2021",
//! dependencies: {},
//! exports: (bang_macros: { "my_bang_macro": "my_bang_macro" }),
//! );
//!
//! use proc_macro::TokenStream;
//!
//! pub fn my_bang_macro(_input: TokenStream) -> TokenStream {
//! *box TokenStream::new()
//! }
//! }
//! my_bang_macro!(input tokens);
//! ```
//!
//! # Caveats
//!
//! This approach comes with several caveats over regular proc macros:
//! - Slower compilation speeds as a second Cargo instance has to be invoked.
//! - Not able to use TOML to define dependencies.
//! - Exporting macros is a pain.
//! - The macros can only be defined in one file.
//! - Errors are a lot less helpful. This is improved a bit by Nightly, but still isn't is good as
//! native proc macro errors.
//! - Derive helper attributes are not supported. The `InlineDerive` macro does reserve the `helper`
//! helper attribute, so you can for example replace `#[my_helper]` with `#[helper[my_helper]]`.
use proc_macro::TokenStream as TokenStream1;
use proc_macro2::{Group, TokenStream};
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{Item, Path, Token};
mod inline_proc;
mod invoke;
/// Write an inline procedural macro.
///
/// This attribute must go on a module, and that module's name must be unique in the entire crate.
///
/// # Metadata
///
/// The module's first item must be an invocation of a `metadata::{format}!` macro, where
/// `{format}` is the chosen format to write the metadata in. Currently we support JSON and
/// [RON](https://github.com/ron-rs/ron), feature-gated with the `json` and `ron` features
/// respectively. In these examples we will use RON because it is shorter and clearer.
///
/// Unfortunately TOML can't be used for this since it is whitespace-sensitive and Rust's lexer
/// strips out all whitespace. Additionally, the `proc_macro_span` feature is unstable so we can't
/// even reconstruct the whitespace.
///
/// ## Metadata Options
///
/// ```
/// use inline_proc::inline_proc;
///
/// #[inline_proc]
/// mod metadata_options {
/// metadata::ron!(
/// // The path to your cargo executable. By default it uses the same one as the one used
/// // to compile the proc macro (the $CARGO env variable).
/// cargo: "cargo",
///
/// // Whether to pass `--color=always` to Cargo; otherwise the lines printed by Cargo will
/// // not appear in color. Default is true.
/// color: true,
///
/// // Whether to check the code with Clippy. Default is false.
/// clippy: true,
///
/// // The edition to use. Default is 2015 edition.
/// edition: "2021",
///
/// // The dependencies of the proc macro. This is in the same format as Cargo.toml's
/// // `[dependencies]` section.
/// dependencies: {
/// "proc-macro2": "1",
/// "syn": ( version: "1", features: ["full"] ),
/// },
///
/// // The path to use for the `inline_proc` crate inside non-exported macros. Defaults to
/// // `::inline_proc`. Use this if you have renamed the crate.
/// inline_proc_path: "::inline_proc",
///
/// // The macros exported by this module.
/// exports: (
/// // The bang macros exported by this module.
/// bang_macros: {
/// // This is a map of the external macro names to paths to the macro functions.
/// "my_nice_macro": "my_nice_macro",
/// // You can use this form to export the macros. See the crate root for an
/// // explanation of how this works.
/// "my_public_macro": ( function: "my_nice_macro", export: true ),
/// },
/// // The derive macros exported by this module.
/// derives: {
/// "MyDeriveMacro": "my_derive_macro",
/// },
/// // The attribute macros exported by this module.
/// attributes: {
/// "my_attribute_macro": "my_attribute_macro",
/// },
/// )
/// );
///
/// use proc_macro::TokenStream;
///
/// // These functions must have a visibility of pub(super) or higher.
///
/// // Bang macros take one token stream and return one token stream.
/// pub fn my_nice_macro(input: TokenStream) -> TokenStream {
/// input
/// }
///
/// // Derive macros take one token stream and return one token stream. Unlike other macros
/// // the original token stream is not destroyed.
/// pub fn my_derive_macro(_item: TokenStream) -> TokenStream {
/// TokenStream::new()
/// }
///
/// // Attribute macros take two token streams; the attribute parameters and the item. On a
/// // regular attribute macro, it looks like this:
/// //
/// // #[my_attribute_macro(/* attribute parameters */)]
/// // struct Whatever; // <-- item
/// //
/// // And on an inline attribute macro it looks like this:
/// //
/// // #[inline_attr[my_attribute_macro(/* attribute parameters */)]]
/// // struct Whatever; // <-- item
/// pub fn my_attribute_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// item
/// }
/// }
/// ```
///
/// # Output
///
/// This macro generates a `macro_rules!` macro for each macro listed in `exports`. This macro can
/// be used like so:
///
/// ```ignore
/// // A bang macro
/// my_bang_macro!(input tokens);
/// // A derive macro
/// MyDeriveMacro!(item tokens);
/// // An attribute macro
/// my_attribute_macro!((attribute parameters) item tokens);
/// ```
///
/// However for derive macros and attribute macros it is recommended to use the
/// [`InlineDerive`](derive.InlineDerive.html) and [`#[inline_attr]`](attr.inline_attr.html) macros
/// instead.
#[proc_macro_error]
#[proc_macro_attribute]
pub fn inline_proc(_: TokenStream1, input: TokenStream1) -> TokenStream1 {
inline_proc::inline_proc(input)
}
#[proc_macro_error]
#[proc_macro]
#[doc(hidden)]
pub fn invoke_inline_macro(input: TokenStream1) -> TokenStream1 {
invoke::invoke_inline_macro(input)
}
/// Use an inline procedural macro attribute.
///
/// Simply replace where you would usually write `#[my_attr]` or `#[my_attr(params)]` with
/// `#[inline_attr[my_attr]]` and `#[inline_attr[my_attr(params)]]`. Everything else works the
/// same.
///
/// Internally, this macro expands:
///
/// ```ignore
/// #[inline_attr(attr_name(params))]
/// struct Item;
/// ```
/// to:
/// ```ignore
/// attr_name!((params) struct Item);
/// ```
#[proc_macro_attribute]
pub fn inline_attr(params: TokenStream1, item: TokenStream1) -> TokenStream1 {
let item: TokenStream = item.into();
let AttrParams { attr_path, tokens } = syn::parse_macro_input!(params);
quote!(#attr_path!((#tokens) #item);).into()
}
struct AttrParams {
attr_path: Path,
tokens: TokenStream,
}
impl Parse for AttrParams {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
attr_path: input.parse()?,
tokens: input
.parse::<Option<Group>>()?
.map_or_else(TokenStream::new, |group| group.stream()),
})
}
}
/// Use an inline procedural derive macro.
///
/// Simply replace where you would usually write `#[derive(MyDerive)]` with
/// `#[derive(InlineDerive)] #[inline_derive(MyDerive)]`.
///
/// Since inline procedural derive macros can't define their own helper attributes, this macro
/// reserves the `#[helper]` helper attribute for you to use.
///
/// Internally, this macro expands:
/// ```ignore
/// #[derive(InlineDerive)]
/// #[inline_derive(DeriveName1, DeriveName2)]
/// struct Item;
/// ```
/// to:
/// ```ignore
/// DeriveName1!(struct Item;);
/// DeriveName2!(struct Item;);
/// ```
#[proc_macro_error]
#[proc_macro_derive(InlineDerive, attributes(inline_derive, helper))]
pub fn inline_derive(item: TokenStream1) -> TokenStream1 {
let mut item: Item = syn::parse_macro_input!(item);
let attrs = match &mut item {
Item::Struct(item) => &mut item.attrs,
Item::Enum(item) => &mut item.attrs,
Item::Union(item) => &mut item.attrs,
_ => abort!(item, "Expected struct, enum or union"),
};
let attr = match attrs
.iter()
.position(|attr| attr.path.is_ident("inline_derive"))
{
Some(i) => attrs.remove(i),
None => abort!(item, "`inline_derive` attribute not present"),
};
let derives: Punctuated<Path, Token![,]> =
match attr.parse_args_with(Punctuated::parse_terminated) {
Ok(derives) => derives,
Err(e) => return e.to_compile_error().into(),
};
derives
.iter()
.map(|derive_path| quote!(#derive_path!(#item);))
.collect::<TokenStream>()
.into()
}