inkwell_internals/
lib.rs

1//! These macros are only intended to be used by inkwell internally
2//! and should not be expected to have public support nor stability.
3//! Here be dragons 🐉
4
5use proc_macro::TokenStream;
6use syn::parse_macro_input;
7
8mod cfg;
9mod r#enum;
10
11/// This macro can be used to specify version constraints for an enum/struct/union or
12/// other item which can be decorated with an attribute.
13///
14/// It takes one argument which is any range of major or `major.minor` LLVM versions.
15///
16/// To use with enum variants or struct fields, you need to decorate the parent item with
17/// the `#[llvm_versioned_item]` attribute, as this is the hook we need to modify the AST
18/// of those items.
19///
20/// # Examples
21///
22/// ```ignore
23/// // Inclusive range from 15 up to and including 18.
24/// #[llvm_versions(15..=18)]
25///
26/// // Exclusive range from 15 up to but not including 18.
27/// #[llvm_versions(15..18)]
28///
29/// // Inclusive range from 15.1 up to and including the latest release.
30/// #[llvm_versions(15.1..)]
31/// ```
32#[proc_macro_attribute]
33pub fn llvm_versions(args: TokenStream, input: TokenStream) -> TokenStream {
34    let args = parse_macro_input!(args as cfg::VersionRange);
35    cfg::expand(Some(args), input)
36}
37
38/// This attribute is used to decorate enums, structs, or unions which may contain
39/// variants/fields which make use of `#[llvm_versions(..)]`
40///
41/// # Examples
42///
43/// ```ignore
44/// #[llvm_versioned_item]
45/// enum InstructionOpcode {
46///     Call,
47///     #[llvm_versions(3.8..=latest)]
48///     CatchPad,
49///     ...
50/// }
51/// ```
52#[proc_macro_attribute]
53pub fn llvm_versioned_item(args: TokenStream, input: TokenStream) -> TokenStream {
54    parse_macro_input!(args as syn::parse::Nothing);
55    cfg::expand(None, input)
56}
57
58/// This attribute macro allows you to decorate an enum declaration which represents
59/// an LLVM enum with versioning constraints and/or custom variant names. There are
60/// a few expectations around the LLVM and Rust enums:
61///
62/// - Both enums have the same number of variants
63/// - The name of the LLVM variant can be derived by appending 'LLVM' to the Rust variant
64///
65/// The latter can be worked around manually with `#[llvm_variant]` if desired.
66///
67/// # Examples
68///
69/// ```ignore
70/// #[llvm_enum(LLVMOpcode)]
71/// enum InstructionOpcode {
72///     Call,
73///     #[llvm_versions(3.8..)]
74///     CatchPad,
75///     ...,
76///     #[llvm_variant(LLVMRet)]
77///     Return,
78///     ...
79/// }
80/// ```
81///
82/// The use of `#[llvm_variant(NAME)]` allows you to override the default
83/// naming scheme by providing the variant name which the source enum maps
84/// to. In the above example, `Ret` was deemed unnecessarily concise, so the
85/// source variant is named `Return` and mapped manually to `LLVMRet`.
86#[proc_macro_attribute]
87pub fn llvm_enum(args: TokenStream, input: TokenStream) -> TokenStream {
88    // Expect something like #[llvm_enum(LLVMOpcode)]
89    let llvm_ty = parse_macro_input!(args as syn::Path);
90    let llvm_enum_type = parse_macro_input!(input as r#enum::LLVMEnumType);
91    r#enum::llvm_enum(llvm_ty, llvm_enum_type).into()
92}