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,ignore
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...], ignore_base=token)`:
135/// Returns a hash of location information for all tokens within the tree of
136/// `token`. If `alphabet` is specified, the hash is converted to a string
137/// using the characters in the `alphabet`. No zero padding is applied in this
138/// case.
139///
140/// `ignore_base` hashes tokens from that crate by content instead of location,
141/// so macro-synthesized def-site spans don't shift the hash when that crate
142/// is edited.
143/// ```rust,ignore
144/// # use linktime_proc_macro::combine;
145/// let location_hash = combine!(output=string input=("location_hash:" __LOCATIONHASH__(of=(a bunch of tokens) alphabet=[a-z])));
146/// assert_eq!(location_hash, "location_hash:dwsrxapjetrwwu");
147/// ```
148///
149/// - `__SOURCE__(of=token)`: The source text content of the `token`.
150///
151/// ```rust
152/// # use linktime_proc_macro::combine;
153/// macro_rules! source_of {
154/// ($token:item) => {
155/// combine!(output=string input=("source" "(@" __LINE__(of=$token) "):" __SOURCE__(of=$token)))
156/// };
157/// }
158/// // Use a macro to get the line and source text of a token
159/// assert_eq!(source_of!(fn foo() {}), "source(@12):fn foo () {}");
160/// assert_eq!(source_of!(static X: u32 = 1;), "source(@13):static X : u32 = 1 ;");
161///
162/// let source = combine!(output=string input=("source:" __SOURCE__(of=(my token))));
163/// assert_eq!(source, "source:my token");
164/// ```
165///
166/// ## String functions
167///
168/// - `__HASH__(string=(tokens...) [alphabet=[chars...]])`: The hash of the
169/// `tokens` when converted to a string, by default as a zero-padding
170/// lowercase hexadecimal string. `tokens` may contain nested function calls.
171/// If `alphabet` is specified, the hash is converted to a string using the
172/// characters in the `alphabet`. No zero padding is applied in this case.
173///
174/// ```rust
175/// # use linktime_proc_macro::combine;
176/// let hash = combine!(output=string input=("hash:" __HASH__(string=(__LENGTH__(string="a")))));
177/// assert_eq!(hash, "hash:65cd25028f98f158");
178/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1))));
179/// assert_eq!(hash, "hash:65cd25028f98f158");
180/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[0-9a-f])));
181/// assert_eq!(hash, "hash:65cd25028f98f158");
182/// let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[a-z])));
183/// assert_eq!(hash, "hash:cywprjlaqhbdie");
184/// ```
185///
186/// - `__LENGTH__(string=(tokens...))`: The length of the `tokens` when
187/// converted to a string.
188///
189/// ```rust
190/// # use linktime_proc_macro::combine;
191/// let length = combine!(output=string input=("length:" __LENGTH__(string="a")));
192/// assert_eq!(length, "length:1");
193/// ```
194///
195/// - `__SUBSTRING__(input=(input) start=start end=end length=length)`: The
196/// substring of the `input` from the `start` to the `end` index (exclusive)
197/// or `length` characters from the `start` index. If `end` or `length` would
198/// exceed the length of the input, the substring is truncated to the end of
199/// the input. If `end` or `length` are missing, the substring is the entire
200/// input from `start` to the end of the input.
201///
202/// ```rust
203/// # use linktime_proc_macro::combine;
204/// let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 end=2)));
205/// assert_eq!(substring, "substring:b");
206/// let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 length=2)));
207/// assert_eq!(substring, "substring:bc");
208/// ```
209///
210/// - `__PAD__(input=(input) length=length left=(padding...)
211/// right=(padding...))`: Pad the `input` to the `length` with the
212/// `padding...` (converted to a string).
213///
214/// ```rust
215/// # use linktime_proc_macro::combine;
216/// let pad = combine!(output=string input=("pad:" __PAD__(input=123 length=5 left=0)));
217/// assert_eq!(pad, "pad:00123");
218/// ```
219///
220/// ## Pattern functions
221///
222/// - `__REPLACE__(input=(input) pattern=(pattern...)|[chars...]
223/// replacement=(replacement))`: Replace all occurrences of the `pattern...`
224/// (converted to a string) in the `input` with the `replacement`. If
225/// `replacement` is missing or empty, the pattern is removed.
226///
227/// Note: `pattern` may be specified as a regex-like character group. Use square
228/// brackets to specify a character group.
229///
230/// ```rust
231/// # use linktime_proc_macro::combine;
232/// let replace = combine!(output=string input=("replace:" __REPLACE__(input=(a b c) pattern=(b) replacement=(x))));
233/// assert_eq!(replace, "replace:axc");
234///
235/// // Remove non-alnum characters
236/// let translate = combine!(output=string input=("replace:" __REPLACE__(input="⚠️ thx 1138" pattern=[^a-zA-Z0-9] replacement="")));
237/// assert_eq!(translate, "replace:thx1138");
238/// ```
239///
240/// - `__TRANSLATE__(input=(input) pattern=(pattern...)|[chars...]
241/// replacement=(replacement)|[chars...])`: Replace all occurrences of the
242/// `pattern...` (converted to a string) characters in the `input` with the
243/// `replacement` characters. If replacement is missing or empty, the
244/// character is removed. Otherwise, the replacement character is selected
245/// from the replacement string in order (repeating the last character if
246/// necessary).
247///
248/// Note: `pattern` and `replacement` may be specified as regex-like character
249/// groups. Use square brackets to specify a character group.
250///
251/// ```rust
252/// # use linktime_proc_macro::combine;
253/// // Deletes all digits
254/// let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=(0 1 2 3 4 5 6 7 8 9))));
255/// assert_eq!(translate, "translate:thx");
256/// // Uppercase all ASCII letters
257/// let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=[a-z] replacement=[A-Z])));
258/// assert_eq!(translate, "translate:THX1138");
259/// ```
260///
261/// - `__TRIM__(input=(input) left=(padding...)|[chars...]
262/// right=(padding...)|[chars...])`: Trim the
263/// `input` of the `left` and `right` patterns.
264///
265/// ```rust
266/// # use linktime_proc_macro::combine;
267/// // Note: because we are using escaped characters, we need to put them in a string
268/// let trim = combine!(output=string input=("trim:" __TRIM__(input=" thx 1138 " left=[" \n\t"] right=[" \n\t"])));
269/// assert_eq!(trim, "trim:thx 1138");
270/// let trim = combine!(output=string input=("trim:" __TRIM__(input=" thx 1138 " left=[' '] right=[' '])));
271/// assert_eq!(trim, "trim:thx 1138");
272/// ```
273///
274/// - `__CONTAINS__(input=(input) pattern=(pattern...)|[chars...])`: Check if
275/// the `input` contains the `pattern...`. If so, returns `1`, otherwise `0`.
276///
277/// ```rust
278/// # use linktime_proc_macro::combine;
279/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[0-9])));
280/// assert_eq!(contains, 1);
281/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[aeiou])));
282/// assert_eq!(contains, 0);
283/// let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern="1138")));
284/// assert_eq!(contains, 1);
285/// ```
286///
287/// - `__STREQ__(a=(tokens...) b=(tokens...))`: Compare `a` and `b` as strings.
288/// Returns `1` if equal, `0` otherwise.
289///
290/// ```rust
291/// # use linktime_proc_macro::combine;
292/// let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1138")));
293/// assert_eq!(eq, 1);
294/// let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1139")));
295/// assert_eq!(eq, 0);
296/// ```
297///
298/// ## Comparison functions
299///
300/// - `__IF__(test=(tokens...) then=(tokens...) else=(tokens...))`: If test is
301/// non-zero, emit the `then` tokens, otherwise emit the `else` tokens.
302///
303/// ```rust
304/// # use linktime_proc_macro::combine;
305/// // Outputs true or false idents
306/// assert!(combine!(output=ident input=(__IF__(test=1 then="true" else="false"))));
307/// assert!(combine!(output=ident input=(__IF__(test=(__CONTAINS__(input="thx 1138" pattern="1138")) then="true" else="false"))));
308/// ```
309///
310/// - `__LT__(a=(tokens...) b=(tokens...))`: Compare `a` < `b` as numbers.
311/// - `__GT__(a=(tokens...) b=(tokens...))`: Compare `a` > `b` as numbers.
312/// - `__LE__(a=(tokens...) b=(tokens...))`: Compare `a` <= `b` as numbers.
313/// - `__GE__(a=(tokens...) b=(tokens...))`: Compare `a` >= `b` as numbers.
314/// - `__EQ__(a=(tokens...) b=(tokens...))`: Compare `a` == `b` as numbers.
315/// - `__NE__(a=(tokens...) b=(tokens...))`: Compare `a` != `b` as numbers.
316///
317/// ```rust
318/// # use linktime_proc_macro::combine;
319/// let lt = combine!(output=isize input=(__LT__(a=1 b=2)));
320/// assert_eq!(lt, 1);
321/// let gt = combine!(output=isize input=(__GT__(a=1 b=2)));
322/// assert_eq!(gt, 0);
323/// ```
324///
325/// ## Math functions
326///
327/// - `__ADD__(a=(tokens...) b=(tokens...))`: Add the `a` and `b` tokens.
328/// - `__SUB__(a=(tokens...) b=(tokens...))`: Subtract the `b` from `a`.
329/// - `__MUL__(a=(tokens...) b=(tokens...))`: Multiply the `a` and `b` tokens.
330/// - `__DIV__(a=(tokens...) b=(tokens...))`: Divide the `a` by `b`.
331/// - `__AND__(a=(tokens...) b=(tokens...))`: Bitwise AND the `a` and `b`
332/// tokens.
333/// - `__OR__(a=(tokens...) b=(tokens...))`: Bitwise OR the `a` and `b` tokens.
334///
335/// ```rust
336/// # use linktime_proc_macro::combine;
337/// assert_eq!(combine!(output=isize input=("add:" __ADD__(a=1 b=2))), 3);
338/// assert_eq!(combine!(output=isize input=("sub:" __SUB__(a=1 b=2))), -1);
339/// assert_eq!(combine!(output=isize input=("mul:" __MUL__(a=1 b=2))), 2);
340/// assert_eq!(combine!(output=isize input=("div:" __DIV__(a=1 b=2))), 0);
341/// assert_eq!(combine!(output=isize input=("and:" __AND__(a=1 b=2))), 0);
342/// assert_eq!(combine!(output=isize input=("or:" __OR__(a=1 b=2))), 3);
343/// ```
344///
345/// ## Conversion functions
346///
347/// - `__TOSTRING__(input=(tokens...))`: Convert the `tokens` to a string
348/// literal.
349/// - `__RAW__(input=(tokens...))`: Convert the `tokens` to a string, ignoring
350/// nested function calls (recommended for user input).
351/// - `__TOIDENT__(input=(tokens...))`: Convert the `tokens` to an ident,
352/// stripping invalid characters.
353/// - `__TONUMBER__(input=(tokens...))`: Convert the `tokens` to a numeric
354/// literal.
355///
356/// ```rust
357/// # use linktime_proc_macro::combine;
358/// let string = combine!(output=string input=("string:" __TOSTRING__(input=(a b c))));
359/// assert_eq!(string, "string:abc");
360///
361/// let number = combine!(output=isize input=("number:" __TONUMBER__(input=(1 2 3))));
362/// assert_eq!(number, 123);
363/// let number = combine!(output=isize input=("number:" __TONUMBER__(input=("0x" 123 _ 456))));
364/// assert_eq!(number, 0x123456);
365///
366/// let ident = combine!(output=string input=("ident:" __TOIDENT__(input=(a $ b _ c))));
367/// assert_eq!(ident, "ident:ab_c");
368///
369/// let raw = combine!(output=string input=("raw:" __RAW__(input=(__TOSTRING__(input=(a b c))))));
370/// assert_eq!(raw, "raw:__TOSTRING__input=abc");
371/// ```
372#[proc_macro]
373pub fn combine(item: TokenStream) -> TokenStream {
374 combine::combine(item)
375}