Skip to main content

linktime_proc_macro/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod combine;
4mod fallback;
5mod generate;
6mod hash;
7
8use proc_macro::TokenStream;
9
10/// Generates macros in the low-level crate and the linktime crate.
11macro_rules! generators {
12    ( $( ($crate_name:ident/$crate_name_str:literal: $( $macro_name:ident/$macro_name_linktime:ident ),*) )* ) => {
13        $($(
14            #[cfg(feature = $crate_name_str)]
15            #[allow(missing_docs)]
16            #[doc(hidden)]
17            #[proc_macro_attribute]
18            pub fn $macro_name(attribute: TokenStream, item: TokenStream) -> TokenStream {
19                crate::generate::generate(stringify!($crate_name), stringify!($macro_name), attribute, item)
20            }
21        )*)*
22        $($(
23            #[cfg(feature = $crate_name_str)]
24            #[allow(missing_docs)]
25            #[doc(hidden)]
26            #[proc_macro_attribute]
27            pub fn $macro_name_linktime(attribute: TokenStream, item: TokenStream) -> TokenStream {
28                crate::generate::generate("linktime", stringify!($macro_name), attribute, item)
29            }
30        )*)*
31    };
32}
33
34generators! {
35    (ctor/"ctor": ctor/ctor_linktime)
36    (dtor/"dtor": dtor/dtor_linktime)
37    (link_section/"link_section": in_section/in_section_linktime, section/section_linktime)
38    (scattered_collect/"scattered_collect": scatter/scatter_linktime, gather/gather_linktime)
39}
40
41/// Combines idents and strings into a single ident or string.
42///
43/// Both idents and strings are decoded as literal strings. Punctuation is
44/// ignored when building `ident` output.
45///
46/// Arguments are specified via named arguments:
47///
48/// - `output`: The type of the combined value. Must be either `ident`, `string`
49///   or `isize`.
50/// - `input`: The input tokens to combine.
51/// - `prefix`: The prefix tokens to emit before the combined value.
52/// - `suffix`: The suffix tokens to emit after the combined value.
53/// - `paren`: The group to emit around the combined value.
54/// - `paren_prefix`: The prefix tokens to emit before the value inside the
55///   group.
56/// - `paren_suffix`: The suffix tokens to emit after the value inside the
57///   group.
58/// - `span`: The span of the combined value. If not specified, the call-site's
59///   span is used.
60///
61/// ```rust
62/// # use linktime_proc_macro::combine;
63/// // Idents, strings and numeric literals are combined as-is.
64/// let str = combine!(output=string input=(prefix _ "NAME" _ suffix));
65/// assert_eq!(str, "prefix_NAME_suffix");
66/// let str = combine!(output=string input=(prefix _ NAME _ 1 _ suffix));
67/// assert_eq!(str, "prefix_NAME_1_suffix");
68///
69/// // Resolves to `let prefix_NAME_suffix = 1;`
70/// combine!(output=ident input=(prefix _ "NAME" _ suffix) prefix=(let ) suffix=(= 1;));
71/// assert_eq!(prefix_NAME_suffix, 1);
72///
73/// // Set the span of the combined value to one of a specific token (useful for macros)
74/// combine!(output=ident input=(prefix _ "NAME" _ suffix) span=token);
75/// ```
76///
77/// ## Token handling
78///
79/// The macro will ignore grouping tokens. For ident output, the macro will also
80/// ignore punctuation tokens and will strip any non-ident-compatible
81/// characters.
82///
83/// ## Functions
84///
85/// The macro also supports a number of special function tokens which are
86/// resolved recursively and can be nested arbitrarily deep.
87///
88/// ```rust
89/// # use linktime_proc_macro::combine;
90/// macro_rules! make_name_max_length {
91///     ($prefix:ident $name:ident $suffix:ident) => {
92///       // Ensure user calls are not expanded recursively
93///       /* $crate:: */ make_name_max_length!(@internal (__RAW__(input=($prefix))) (__RAW__(input=($name))) (__RAW__(input=($suffix))))
94///     };
95///     (@internal $prefix:tt $name:tt $suffix:tt) => {
96///         combine!(output=string input=(
97///             __IF__(
98///               test=(__GT__(a=(__LENGTH__(string=($prefix _ $name _ $suffix))) b=20))
99///               then=(
100///                 __SUBSTRING__(input=($prefix _ $name _ $suffix) start=0 end=10)
101///                 _ $
102///                 // uppercase hash
103///                 __TRANSLATE__(
104///                   input=(__SUBSTRING__(input=(__HASH__(string=($prefix _ $name _ $suffix))) length=10))
105///                   pattern=[a-f] replacement=[A-F]
106///                 )
107///               )
108///               else=(
109///                 $prefix _ $name _ $suffix _ __LENGTH__(string=($name))
110///               )
111///             )
112///         ))
113///     };
114/// }
115/// assert_eq!(make_name_max_length!(prefix NAME suffix), "prefix_NAME_suffix_4");
116/// assert_eq!(make_name_max_length!(prefix LONG_NAME suffix), "prefix_LON_$64470473B5");
117/// ```
118///
119/// ## Source span functions
120///
121///  - `__FILE__(of=token)`: The file name of the file containing the `token`.
122///    Supported on Rust 1.88+, returns "" otherwise.
123///  - `__LINE__(of=token)`: The line number of the `token`. Supported on Rust
124///    1.88+, returns 0 otherwise.
125///  - `__COLUMN__(of=token)`: The column number of the `token`. Supported on
126///    Rust 1.88+, returns 0 otherwise.
127///
128/// ```rust
129/// # use linktime_proc_macro::combine;
130/// let file = combine!(output=string input=("file:" __FILE__(of=token) ":" __LINE__(of=token) ":" __COLUMN__(of=token)));
131/// assert_eq!(file, "file:linktime-proc-macro/src/lib.rs:6:110");
132/// ```
133///
134///  - `__LOCATIONHASH__(of=token, alphabet=[chars...])`: Returns a hash of
135///    location information for all tokens within the tree of `token`. If
136///    `alphabet` is specified, the hash is converted to a string using the
137///    characters in the `alphabet`. No zero padding is applied in this case.
138///
139/// ```rust
140/// # use linktime_proc_macro::combine;
141/// let location_hash = combine!(output=string input=("location_hash:" __LOCATIONHASH__(of=(a bunch of tokens) alphabet=[a-z])));
142/// assert_eq!(location_hash, "location_hash:dwsrxapjetrwwu");
143/// ```
144///
145///  - `__SOURCE__(of=token)`: The source text content of the `token`.
146///
147/// ```rust
148/// # use linktime_proc_macro::combine;
149/// macro_rules! source_of {
150///     ($token:item) => {
151///         combine!(output=string input=("source" "(@" __LINE__(of=$token) "):" __SOURCE__(of=$token)))
152///     };
153/// }
154/// // Use a macro to get the line and source text of a token
155/// assert_eq!(source_of!(fn foo() {}), "source(@12):fn foo () {}");
156/// assert_eq!(source_of!(static X: u32 = 1;), "source(@13):static X : u32 = 1 ;");
157///
158/// let source = combine!(output=string input=("source:" __SOURCE__(of=(my token))));
159/// assert_eq!(source, "source:my token");
160/// ```
161///
162/// ## String functions
163///
164///  - `__HASH__(string=(tokens...) [alphabet=[chars...]])`: The hash of the
165///    `tokens` when converted to a string, by default as a zero-padding
166///    lowercase hexadecimal string. `tokens` may contain nested function calls.
167///    If `alphabet` is specified, the hash is converted to a string using the
168///    characters in the `alphabet`. No zero padding is applied in this case.
169///
170/// ```rust
171/// # use linktime_proc_macro::combine;
172/// let hash = combine!(output=string input=("hash:" __HASH__(string=(__LENGTH__(string="a")))));
173/// assert_eq!(hash, "hash:65cd25028f98f158");
174/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1))));
175/// assert_eq!(hash, "hash:65cd25028f98f158");
176/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[0-9a-f])));
177/// assert_eq!(hash, "hash:65cd25028f98f158");
178/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[a-z])));
179/// assert_eq!(hash, "hash:cywprjlaqhbdie");
180/// ```
181///
182///  - `__LENGTH__(string=(tokens...))`: The length of the `tokens` when
183///    converted to a string.
184///
185/// ```rust
186/// # use linktime_proc_macro::combine;
187/// let length = combine!(output=string input=("length:" __LENGTH__(string="a")));
188/// assert_eq!(length, "length:1");
189/// ```
190///
191///  - `__SUBSTRING__(input=(input) start=start end=end length=length)`: The
192///    substring of the `input` from the `start` to the `end` index (exclusive)
193///    or `length` characters from the `start` index. If `end` or `length` would
194///    exceed the length of the input, the substring is truncated to the end of
195///    the input. If `end` or `length` are missing, the substring is the entire
196///    input from `start` to the end of the input.
197///
198/// ```rust
199/// # use linktime_proc_macro::combine;
200/// let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 end=2)));
201/// assert_eq!(substring, "substring:b");
202/// let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 length=2)));
203/// assert_eq!(substring, "substring:bc");
204/// ```
205///
206///  - `__PAD__(input=(input) length=length left=(padding...)
207///    right=(padding...))`: Pad the `input` to the `length` with the
208///    `padding...` (converted to a string).
209///
210/// ```rust
211/// # use linktime_proc_macro::combine;
212/// let pad = combine!(output=string input=("pad:" __PAD__(input=123 length=5 left=0)));
213/// assert_eq!(pad, "pad:00123");
214/// ```
215///
216/// ## Pattern functions
217///
218///  - `__REPLACE__(input=(input) pattern=(pattern...)|[chars...]
219///    replacement=(replacement))`: Replace all occurrences of the `pattern...`
220///    (converted to a string) in the `input` with the `replacement`. If
221///    `replacement` is missing or empty, the pattern is removed.
222///
223/// Note: `pattern` may be specified as a regex-like character group. Use square
224/// brackets to specify a character group.
225///
226/// ```rust
227/// # use linktime_proc_macro::combine;
228/// let replace = combine!(output=string input=("replace:" __REPLACE__(input=(a b c) pattern=(b) replacement=(x))));
229/// assert_eq!(replace, "replace:axc");
230///
231/// // Remove non-alnum characters
232/// let translate = combine!(output=string input=("replace:" __REPLACE__(input="⚠️ thx 1138" pattern=[^a-zA-Z0-9] replacement="")));
233/// assert_eq!(translate, "replace:thx1138");
234/// ```
235///
236///  - `__TRANSLATE__(input=(input) pattern=(pattern...)|[chars...]
237///    replacement=(replacement)|[chars...])`: Replace all occurrences of the
238///    `pattern...` (converted to a string) characters in the `input` with the
239///    `replacement` characters. If replacement is missing or empty, the
240///    character is removed. Otherwise, the replacement character is selected
241///    from the replacement string in order (repeating the last character if
242///    necessary).
243///
244/// Note: `pattern` and `replacement` may be specified as regex-like character
245/// groups. Use square brackets to specify a character group.
246///
247/// ```rust
248/// # use linktime_proc_macro::combine;
249/// // Deletes all digits
250/// let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=(0 1 2 3 4 5 6 7 8 9))));
251/// assert_eq!(translate, "translate:thx");
252/// // Uppercase all ASCII letters
253/// let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=[a-z] replacement=[A-Z])));
254/// assert_eq!(translate, "translate:THX1138");
255/// ```
256///
257///  - `__TRIM__(input=(input) left=(padding...)|[chars...]
258///    right=(padding...)|[chars...])`: Trim the
259///    `input` of the `left` and `right` patterns.
260///
261/// ```rust
262/// # use linktime_proc_macro::combine;
263/// // Note: because we are using escaped characters, we need to put them in a string
264/// let trim = combine!(output=string input=("trim:" __TRIM__(input="  thx 1138  " left=[" \n\t"] right=[" \n\t"])));
265/// assert_eq!(trim, "trim:thx 1138");
266/// let trim = combine!(output=string input=("trim:" __TRIM__(input="  thx 1138  " left=[' '] right=[' '])));
267/// assert_eq!(trim, "trim:thx 1138");
268/// ```
269///
270///  - `__CONTAINS__(input=(input) pattern=(pattern...)|[chars...])`: Check if
271///    the `input` contains the `pattern...`. If so, returns `1`, otherwise `0`.
272///
273/// ```rust
274/// # use linktime_proc_macro::combine;
275/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[0-9])));
276/// assert_eq!(contains, 1);
277/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[aeiou])));
278/// assert_eq!(contains, 0);
279/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern="1138")));
280/// assert_eq!(contains, 1);
281/// ```
282///
283///  - `__STREQ__(a=(tokens...) b=(tokens...))`: Compare `a` and `b` as strings.
284///    Returns `1` if equal, `0` otherwise.
285///
286/// ```rust
287/// # use linktime_proc_macro::combine;
288/// let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1138")));
289/// assert_eq!(eq, 1);
290/// let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1139")));
291/// assert_eq!(eq, 0);
292/// ```
293///
294/// ## Comparison functions
295///
296///  - `__IF__(test=(tokens...) then=(tokens...) else=(tokens...))`: If test is
297///    non-zero, emit the `then` tokens, otherwise emit the `else` tokens.
298///
299/// ```rust
300/// # use linktime_proc_macro::combine;
301/// // Outputs true or false idents
302/// assert!(combine!(output=ident input=(__IF__(test=1 then="true" else="false"))));
303/// assert!(combine!(output=ident input=(__IF__(test=(__CONTAINS__(input="thx 1138" pattern="1138")) then="true" else="false"))));
304/// ```
305///
306/// - `__LT__(a=(tokens...) b=(tokens...))`: Compare `a` < `b` as numbers.
307/// - `__GT__(a=(tokens...) b=(tokens...))`: Compare `a` > `b` as numbers.
308/// - `__LE__(a=(tokens...) b=(tokens...))`: Compare `a` <= `b` as numbers.
309/// - `__GE__(a=(tokens...) b=(tokens...))`: Compare `a` >= `b` as numbers.
310/// - `__EQ__(a=(tokens...) b=(tokens...))`: Compare `a` == `b` as numbers.
311/// - `__NE__(a=(tokens...) b=(tokens...))`: Compare `a` != `b` as numbers.
312///
313/// ```rust
314/// # use linktime_proc_macro::combine;
315/// let lt = combine!(output=isize input=(__LT__(a=1 b=2)));
316/// assert_eq!(lt, 1);
317/// let gt = combine!(output=isize input=(__GT__(a=1 b=2)));
318/// assert_eq!(gt, 0);
319/// ```
320///
321/// ## Math functions
322///
323///  - `__ADD__(a=(tokens...) b=(tokens...))`: Add the `a` and `b` tokens.
324///  - `__SUB__(a=(tokens...) b=(tokens...))`: Subtract the `b` from `a`.
325///  - `__MUL__(a=(tokens...) b=(tokens...))`: Multiply the `a` and `b` tokens.
326///  - `__DIV__(a=(tokens...) b=(tokens...))`: Divide the `a` by `b`.
327///  - `__AND__(a=(tokens...) b=(tokens...))`: Bitwise AND the `a` and `b`
328///    tokens.
329///  - `__OR__(a=(tokens...) b=(tokens...))`: Bitwise OR the `a` and `b` tokens.
330///
331/// ```rust
332/// # use linktime_proc_macro::combine;
333/// assert_eq!(combine!(output=isize input=("add:" __ADD__(a=1 b=2))), 3);
334/// assert_eq!(combine!(output=isize input=("sub:" __SUB__(a=1 b=2))), -1);
335/// assert_eq!(combine!(output=isize input=("mul:" __MUL__(a=1 b=2))), 2);
336/// assert_eq!(combine!(output=isize input=("div:" __DIV__(a=1 b=2))), 0);
337/// assert_eq!(combine!(output=isize input=("and:" __AND__(a=1 b=2))), 0);
338/// assert_eq!(combine!(output=isize input=("or:" __OR__(a=1 b=2))), 3);
339/// ```
340///
341/// ## Conversion functions
342///
343///  - `__TOSTRING__(input=(tokens...))`: Convert the `tokens` to a string
344///    literal.
345///  - `__RAW__(input=(tokens...))`: Convert the `tokens` to a string, ignoring
346///    nested function calls (recommended for user input).
347///  - `__TOIDENT__(input=(tokens...))`: Convert the `tokens` to an ident,
348///    stripping invalid characters.
349///  - `__TONUMBER__(input=(tokens...))`: Convert the `tokens` to a numeric
350///    literal.
351///
352/// ```rust
353/// # use linktime_proc_macro::combine;
354/// let string = combine!(output=string input=("string:" __TOSTRING__(input=(a b c))));
355/// assert_eq!(string, "string:abc");
356///
357/// let number = combine!(output=isize input=("number:" __TONUMBER__(input=(1 2 3))));
358/// assert_eq!(number, 123);
359/// let number = combine!(output=isize input=("number:" __TONUMBER__(input=("0x" 123 _ 456))));
360/// assert_eq!(number, 0x123456);
361///
362/// let ident = combine!(output=string input=("ident:" __TOIDENT__(input=(a $ b _ c))));
363/// assert_eq!(ident, "ident:ab_c");
364///
365/// let raw = combine!(output=string input=("raw:" __RAW__(input=(__TOSTRING__(input=(a b c))))));
366/// assert_eq!(raw, "raw:__TOSTRING__input=abc");
367/// ```
368#[proc_macro]
369pub fn combine(item: TokenStream) -> TokenStream {
370    combine::combine(item)
371}