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
//! These macros are only intended to be used by inkwell internally
//! and should not be expected to have public support nor stability.
//! Here be dragons 🐉

use proc_macro::TokenStream;
use syn::parse_macro_input;

mod cfg;
mod r#enum;

/// This macro can be used to specify version constraints for an enum/struct/union or
/// other item which can be decorated with an attribute.
///
/// It takes one argument which is any range of major or `major.minor` LLVM versions.
///
/// To use with enum variants or struct fields, you need to decorate the parent item with
/// the `#[llvm_versioned_item]` attribute, as this is the hook we need to modify the AST
/// of those items.
///
/// # Examples
///
/// ```ignore
/// // Inclusive range from 15 up to and including 18.
/// #[llvm_versions(15..=18)]
///
/// // Exclusive range from 15 up to but not including 18.
/// #[llvm_versions(15..18)]
///
/// // Inclusive range from 15.1 up to and including the latest release.
/// #[llvm_versions(15.1..)]
/// ```
#[proc_macro_attribute]
pub fn llvm_versions(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = parse_macro_input!(args as cfg::VersionRange);
    cfg::expand(Some(args), input)
}

/// This attribute is used to decorate enums, structs, or unions which may contain
/// variants/fields which make use of `#[llvm_versions(..)]`
///
/// # Examples
///
/// ```ignore
/// #[llvm_versioned_item]
/// enum InstructionOpcode {
///     Call,
///     #[llvm_versions(3.8..=latest)]
///     CatchPad,
///     ...
/// }
/// ```
#[proc_macro_attribute]
pub fn llvm_versioned_item(args: TokenStream, input: TokenStream) -> TokenStream {
    parse_macro_input!(args as syn::parse::Nothing);
    cfg::expand(None, input)
}

/// This attribute macro allows you to decorate an enum declaration which represents
/// an LLVM enum with versioning constraints and/or custom variant names. There are
/// a few expectations around the LLVM and Rust enums:
///
/// - Both enums have the same number of variants
/// - The name of the LLVM variant can be derived by appending 'LLVM' to the Rust variant
///
/// The latter can be worked around manually with `#[llvm_variant]` if desired.
///
/// # Examples
///
/// ```ignore
/// #[llvm_enum(LLVMOpcode)]
/// enum InstructionOpcode {
///     Call,
///     #[llvm_versions(3.8..)]
///     CatchPad,
///     ...,
///     #[llvm_variant(LLVMRet)]
///     Return,
///     ...
/// }
/// ```
///
/// The use of `#[llvm_variant(NAME)]` allows you to override the default
/// naming scheme by providing the variant name which the source enum maps
/// to. In the above example, `Ret` was deemed unnecessarily concise, so the
/// source variant is named `Return` and mapped manually to `LLVMRet`.
#[proc_macro_attribute]
pub fn llvm_enum(args: TokenStream, input: TokenStream) -> TokenStream {
    // Expect something like #[llvm_enum(LLVMOpcode)]
    let llvm_ty = parse_macro_input!(args as syn::Path);
    let llvm_enum_type = parse_macro_input!(input as r#enum::LLVMEnumType);
    r#enum::llvm_enum(llvm_ty, llvm_enum_type).into()
}