llvm_plugin_inkwell_internals/
lib.rs1use proc_macro::TokenStream;
6use proc_macro2::Span;
7use quote::quote;
8use syn::fold::Fold;
9use syn::parse::{Error, Parse, ParseStream, Result};
10use syn::spanned::Spanned;
11use syn::{parse_macro_input, parse_quote};
12use syn::{Attribute, Field, Ident, Item, LitFloat, Token, Variant};
13
14const FEATURE_VERSIONS: [&str; 13] = [
16 "llvm4-0", "llvm5-0", "llvm6-0", "llvm7-0", "llvm8-0", "llvm9-0", "llvm10-0", "llvm11-0", "llvm12-0", "llvm13-0",
17 "llvm14-0", "llvm15-0", "llvm16-0",
18];
19
20fn get_latest_feature_index(features: &[&str]) -> usize {
22 features.len() - 1
23}
24
25fn get_feature_index(features: &[&str], feature: String, span: Span) -> Result<usize> {
27 let feat = feature.as_str();
28 match features.iter().position(|&s| s == feat) {
29 None => Err(Error::new(
30 span,
31 format!("Invalid feature version: {}, not defined", feature),
32 )),
33 Some(index) => Ok(index),
34 }
35}
36
37fn get_features(vt: VersionType) -> Result<Vec<&'static str>> {
39 let features = FEATURE_VERSIONS;
40 let latest = get_latest_feature_index(&features);
41 match vt {
42 VersionType::Specific(version, span) => {
43 let feature = f64_to_feature_string(version);
44 let index = get_feature_index(&features, feature, span)?;
45 Ok(features[index..=index].to_vec())
46 },
47 VersionType::InclusiveRangeToLatest(version, span) => {
48 let feature = f64_to_feature_string(version);
49 let index = get_feature_index(&features, feature, span)?;
50 Ok(features[index..=latest].to_vec())
51 },
52 VersionType::InclusiveRange((start, start_span), (end, end_span)) => {
53 let start_feature = f64_to_feature_string(start);
54 let end_feature = f64_to_feature_string(end);
55 let start_index = get_feature_index(&features, start_feature, start_span)?;
56 let end_index = get_feature_index(&features, end_feature, end_span)?;
57 if end_index < start_index {
58 let message = format!(
59 "Invalid version range: {} must be greater than or equal to {}",
60 start, end
61 );
62 Err(Error::new(end_span, message))
63 } else {
64 Ok(features[start_index..=end_index].to_vec())
65 }
66 },
67 VersionType::ExclusiveRangeToLatest(version, span) => {
68 let feature = f64_to_feature_string(version);
69 let index = get_feature_index(&features, feature, span)?;
70 if latest == index {
71 let message = format!(
72 "Invalid version range: {}..latest produces an empty feature set",
73 version
74 );
75 Err(Error::new(span, message))
76 } else {
77 Ok(features[index..latest].to_vec())
78 }
79 },
80 VersionType::ExclusiveRange((start, start_span), (end, end_span)) => {
81 let start_feature = f64_to_feature_string(start);
82 let end_feature = f64_to_feature_string(end);
83 let start_index = get_feature_index(&features, start_feature, start_span)?;
84 let end_index = get_feature_index(&features, end_feature, end_span)?;
85
86 match end_index.cmp(&start_index) {
87 std::cmp::Ordering::Equal => {
88 let message = format!(
89 "Invalid version range: {}..{} produces an empty feature set",
90 start, end
91 );
92 Err(Error::new(start_span, message))
93 },
94 std::cmp::Ordering::Less => {
95 let message = format!("Invalid version range: {} must be greater than {}", start, end);
96 Err(Error::new(end_span, message))
97 },
98
99 std::cmp::Ordering::Greater => Ok(features[start_index..end_index].to_vec()),
100 }
101 },
102 }
103}
104
105fn f64_to_feature_string(float: f64) -> String {
108 let int = float as u64;
109
110 format!("llvm{}-{}", int, (float * 10.) % 10.)
111}
112
113#[derive(Debug)]
115enum VersionType {
116 Specific(f64, Span),
117 InclusiveRange((f64, Span), (f64, Span)),
118 InclusiveRangeToLatest(f64, Span),
119 ExclusiveRange((f64, Span), (f64, Span)),
120 ExclusiveRangeToLatest(f64, Span),
121}
122impl Parse for VersionType {
123 fn parse(input: ParseStream) -> Result<Self> {
124 let lookahead = input.lookahead1();
126 if lookahead.peek(LitFloat) {
128 let from = input.parse::<LitFloat>().unwrap();
129 let from_val = from.base10_parse().unwrap();
130 if input.is_empty() {
132 return Ok(VersionType::Specific(from_val, from.span()));
133 }
134 let lookahead = input.lookahead1();
137 if lookahead.peek(Token![..=]) {
138 let _: Token![..=] = input.parse().unwrap();
139 let lookahead = input.lookahead1();
140 if lookahead.peek(Ident) {
141 let to = input.parse::<Ident>().unwrap();
142 if to == "latest" {
143 Ok(VersionType::InclusiveRangeToLatest(from_val, from.span()))
144 } else {
145 Err(Error::new(to.span(), "expected `latest` or `X.Y`"))
146 }
147 } else if lookahead.peek(LitFloat) {
148 let to = input.parse::<LitFloat>().unwrap();
149 let to_val = to.base10_parse().unwrap();
150 Ok(VersionType::InclusiveRange(
151 (from_val, from.span()),
152 (to_val, to.span()),
153 ))
154 } else {
155 Err(lookahead.error())
156 }
157 } else if lookahead.peek(Token![..]) {
158 let _: Token![..] = input.parse().unwrap();
159 let lookahead = input.lookahead1();
160 if lookahead.peek(Ident) {
161 let to = input.parse::<Ident>().unwrap();
162 if to == "latest" {
163 Ok(VersionType::ExclusiveRangeToLatest(from_val, from.span()))
164 } else {
165 Err(Error::new(to.span(), "expected `latest` or `X.Y`"))
166 }
167 } else if lookahead.peek(LitFloat) {
168 let to = input.parse::<LitFloat>().unwrap();
169 let to_val = to.base10_parse().unwrap();
170 Ok(VersionType::ExclusiveRange(
171 (from_val, from.span()),
172 (to_val, to.span()),
173 ))
174 } else {
175 Err(lookahead.error())
176 }
177 } else {
178 Err(lookahead.error())
179 }
180 } else {
181 Err(lookahead.error())
182 }
183 }
184}
185
186#[derive(Debug)]
188struct ParenthesizedFeatureSet(FeatureSet);
189impl Parse for ParenthesizedFeatureSet {
190 fn parse(input: ParseStream) -> Result<Self> {
191 input.parse::<FeatureSet>().map(Self)
192 }
193}
194
195#[derive(Clone, Debug)]
197struct FeatureSet(std::vec::IntoIter<&'static str>, Option<Error>);
198impl Default for FeatureSet {
199 fn default() -> Self {
200 #[allow(clippy::unnecessary_to_owned)] Self(FEATURE_VERSIONS.to_vec().into_iter(), None)
203 }
204}
205impl Parse for FeatureSet {
206 fn parse(input: ParseStream) -> Result<Self> {
207 let version_type = input.parse::<VersionType>()?;
208 let features = get_features(version_type)?;
209 Ok(Self(features.into_iter(), None))
210 }
211}
212impl Iterator for FeatureSet {
213 type Item = &'static str;
214
215 fn next(&mut self) -> Option<Self::Item> {
216 self.0.next()
217 }
218}
219impl FeatureSet {
220 #[inline]
221 fn has_error(&self) -> bool {
222 self.1.is_some()
223 }
224
225 #[inline]
226 fn set_error(&mut self, err: Error) {
227 self.1 = Some(err);
228 }
229
230 fn into_error(self) -> Error {
231 self.1.unwrap()
232 }
233
234 fn into_compile_error(self) -> TokenStream {
235 TokenStream::from(self.1.unwrap().to_compile_error())
236 }
237
238 fn expand_llvm_versions_attr(&mut self, attr: &Attribute) -> Attribute {
239 if self.has_error() {
241 return attr.clone();
242 }
243
244 if !attr.path().is_ident("llvm_versions") {
246 return attr.clone();
247 }
248
249 match attr.parse_args() {
251 Ok(ParenthesizedFeatureSet(features)) => {
252 parse_quote! {
253 #[cfg(any(#(feature = #features),*))]
254 }
255 },
256 Err(err) => {
257 self.set_error(err);
262 attr.clone()
263 },
264 }
265 }
266}
267impl Fold for FeatureSet {
268 fn fold_variant(&mut self, mut variant: Variant) -> Variant {
269 if self.has_error() {
270 return variant;
271 }
272
273 let attrs = variant
274 .attrs
275 .iter()
276 .map(|attr| self.expand_llvm_versions_attr(attr))
277 .collect::<Vec<_>>();
278 variant.attrs = attrs;
279 variant
280 }
281
282 fn fold_field(&mut self, mut field: Field) -> Field {
283 if self.has_error() {
284 return field;
285 }
286
287 let attrs = field
288 .attrs
289 .iter()
290 .map(|attr| self.expand_llvm_versions_attr(attr))
291 .collect::<Vec<_>>();
292 field.attrs = attrs;
293 field
294 }
295}
296
297#[proc_macro_attribute]
317pub fn llvm_versions(attribute_args: TokenStream, attributee: TokenStream) -> TokenStream {
318 let mut features = parse_macro_input!(attribute_args as FeatureSet);
319
320 let attributee = parse_macro_input!(attributee as Item);
321 let folded = features.fold_item(attributee);
322
323 if features.has_error() {
324 return features.into_compile_error();
325 }
326
327 let doc = if cfg!(feature = "nightly") {
330 let features2 = features.clone();
331 quote! {
332 #[doc(cfg(any(#(feature = #features2),*)))]
333 }
334 } else {
335 quote! {}
336 };
337
338 let q = quote! {
339 #[cfg(any(#(feature = #features),*))]
340 #doc
341 #folded
342 };
343
344 q.into()
345}
346
347#[proc_macro_attribute]
362pub fn llvm_versioned_item(_attribute_args: TokenStream, attributee: TokenStream) -> TokenStream {
363 let attributee = parse_macro_input!(attributee as Item);
364
365 let mut features = FeatureSet::default();
366 let folded = features.fold_item(attributee);
367
368 if features.has_error() {
369 return features.into_compile_error();
370 }
371
372 quote!(#folded).into()
373}
374
375struct EnumVariant {
378 llvm_variant: Ident,
379 rust_variant: Ident,
380 attrs: Vec<Attribute>,
381}
382impl EnumVariant {
383 fn new(variant: &Variant) -> Self {
384 let rust_variant = variant.ident.clone();
385 let llvm_variant = Ident::new(&format!("LLVM{}", rust_variant), variant.span());
386 let mut attrs = variant.attrs.clone();
387 attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
388 Self {
389 llvm_variant,
390 rust_variant,
391 attrs,
392 }
393 }
394
395 fn with_name(variant: &Variant, mut llvm_variant: Ident) -> Self {
396 let rust_variant = variant.ident.clone();
397 llvm_variant.set_span(rust_variant.span());
398 let mut attrs = variant.attrs.clone();
399 attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
400 Self {
401 llvm_variant,
402 rust_variant,
403 attrs,
404 }
405 }
406}
407
408#[derive(Default)]
410struct EnumVariants {
411 variants: Vec<EnumVariant>,
412 error: Option<Error>,
413}
414impl EnumVariants {
415 #[inline]
416 fn len(&self) -> usize {
417 self.variants.len()
418 }
419
420 #[inline]
421 fn iter(&self) -> core::slice::Iter<'_, EnumVariant> {
422 self.variants.iter()
423 }
424
425 #[inline]
426 fn has_error(&self) -> bool {
427 self.error.is_some()
428 }
429
430 #[inline]
431 fn set_error(&mut self, err: &str, span: Span) {
432 self.error = Some(Error::new(span, err));
433 }
434
435 fn into_error(self) -> Error {
436 self.error.unwrap()
437 }
438}
439impl Fold for EnumVariants {
440 fn fold_variant(&mut self, mut variant: Variant) -> Variant {
441 use syn::Meta;
442
443 if self.has_error() {
444 return variant;
445 }
446
447 if let Some(attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("llvm_variant")) {
449 if let Meta::List(meta) = &attr.meta {
451 if let Ok(Meta::Path(name)) = meta.parse_args() {
454 self.variants
455 .push(EnumVariant::with_name(&variant, name.get_ident().unwrap().clone()));
456 variant.attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
458 return variant;
459 }
460 }
461
462 self.set_error("expected #[llvm_variant(VARIANT_NAME)]", attr.span());
464 return variant;
465 }
466
467 self.variants.push(EnumVariant::new(&variant));
468 variant
469 }
470}
471
472struct LLVMEnumType {
474 name: Ident,
475 decl: syn::ItemEnum,
476 variants: EnumVariants,
477}
478impl Parse for LLVMEnumType {
479 fn parse(input: ParseStream) -> Result<Self> {
480 let decl = input.parse::<syn::ItemEnum>()?;
482 let name = decl.ident.clone();
483 let mut features = FeatureSet::default();
485 let decl = features.fold_item_enum(decl);
486 if features.has_error() {
487 return Err(features.into_error());
488 }
489
490 let mut variants = EnumVariants::default();
491 let decl = variants.fold_item_enum(decl);
492 if variants.has_error() {
493 return Err(variants.into_error());
494 }
495
496 Ok(Self { name, decl, variants })
497 }
498}
499
500#[proc_macro_attribute]
529pub fn llvm_enum(attribute_args: TokenStream, attributee: TokenStream) -> TokenStream {
530 use syn::{Arm, PatPath, Path};
531
532 let llvm_ty = parse_macro_input!(attribute_args as Path);
534 let llvm_enum_type = parse_macro_input!(attributee as LLVMEnumType);
535
536 let mut from_arms = Vec::with_capacity(llvm_enum_type.variants.len());
538 for variant in llvm_enum_type.variants.iter() {
539 let src_variant = variant.llvm_variant.clone();
540 let src_attrs: Vec<_> = variant
542 .attrs
543 .iter()
544 .filter(|&attr| !attr.meta.path().is_ident("doc"))
545 .collect();
546 let src_ty = llvm_ty.clone();
547 let dst_variant = variant.rust_variant.clone();
548 let dst_ty = llvm_enum_type.name.clone();
549
550 let pat = PatPath {
551 attrs: Vec::new(),
552 qself: None,
553 path: parse_quote!(#src_ty::#src_variant),
554 };
555
556 let arm: Arm = parse_quote! {
557 #(#src_attrs)*
558 #pat => { #dst_ty::#dst_variant }
559 };
560 from_arms.push(arm);
561 }
562
563 let mut to_arms = Vec::with_capacity(llvm_enum_type.variants.len());
565 for variant in llvm_enum_type.variants.iter() {
566 let src_variant = variant.rust_variant.clone();
567 let src_attrs: Vec<_> = variant
569 .attrs
570 .iter()
571 .filter(|&attr| !attr.meta.path().is_ident("doc"))
572 .collect();
573 let src_ty = llvm_enum_type.name.clone();
574 let dst_variant = variant.llvm_variant.clone();
575 let dst_ty = llvm_ty.clone();
576
577 let pat = PatPath {
578 attrs: Vec::new(),
579 qself: None,
580 path: parse_quote!(#src_ty::#src_variant),
581 };
582
583 let arm: Arm = parse_quote! {
584 #(#src_attrs)*
585 #pat => { #dst_ty::#dst_variant }
586 };
587 to_arms.push(arm);
588 }
589
590 let enum_ty = llvm_enum_type.name.clone();
591 let enum_decl = llvm_enum_type.decl;
592
593 let q = quote! {
594 #enum_decl
595
596 impl #enum_ty {
597 fn new(src: #llvm_ty) -> Self {
598 match src {
599 #(#from_arms)*
600 }
601 }
602 }
603 impl From<#llvm_ty> for #enum_ty {
604 fn from(src: #llvm_ty) -> Self {
605 Self::new(src)
606 }
607 }
608 impl Into<#llvm_ty> for #enum_ty {
609 fn into(self) -> #llvm_ty {
610 match self {
611 #(#to_arms),*
612 }
613 }
614 }
615 };
616 q.into()
617}