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}