Parse discriminant values from clang AST
diff --git a/macro/src/clang.rs b/macro/src/clang.rs
index 953d0a5..a2e3425 100644
--- a/macro/src/clang.rs
+++ b/macro/src/clang.rs
@@ -1,13 +1,14 @@
use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
-use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
+use crate::syntax::{Api, Discriminant, Doc, Enum, ForeignName, Pair, Variant};
use proc_macro2::Ident;
use quote::format_ident;
use serde::Deserialize;
use std::env;
use std::fs;
use std::path::PathBuf;
+use std::str::FromStr;
const CXX_CLANG_AST: &str = "CXX_CLANG_AST";
@@ -18,6 +19,8 @@
NamespaceDecl(NamespaceDecl),
EnumDecl(EnumDecl),
EnumConstantDecl(EnumConstantDecl),
+ ImplicitCastExpr,
+ ConstantExpr(ConstantExpr),
Unknown,
}
@@ -36,6 +39,11 @@
name: String,
}
+#[derive(Deserialize)]
+struct ConstantExpr {
+ value: String,
+}
+
pub fn load(cx: &mut Errors, apis: &mut [Api]) {
let ref mut variants_from_header = Vec::new();
for api in apis {
@@ -151,6 +159,32 @@
Ok(ident) => ident,
Err(_) => format_ident!("__Variant{}", enm.variants.len()),
};
+ let discriminant = match discriminant_value(&node.inner) {
+ ParsedDiscriminant::Constant(discriminant) => discriminant,
+ ParsedDiscriminant::Successor => match enm.variants.last() {
+ None => Discriminant::zero(),
+ Some(last) => match last.discriminant.checked_succ() {
+ Some(discriminant) => discriminant,
+ None => {
+ let span = &enm.variants_from_header_attr;
+ let msg = format!(
+ "overflow processing discriminant value for variant: {}",
+ decl.name,
+ );
+ return cx.error(span, msg);
+ }
+ },
+ },
+ ParsedDiscriminant::Fail => {
+ let span = &enm.variants_from_header_attr;
+ let msg = format!(
+ "failed to obtain discriminant value for variant: {}",
+ decl.name,
+ );
+ cx.error(span, msg);
+ Discriminant::zero()
+ }
+ };
enm.variants.push(Variant {
doc: Doc::new(),
attrs: OtherAttrs::none(),
@@ -159,7 +193,7 @@
cxx: cxx_name,
rust: rust_name,
},
- discriminant: unimplemented!(),
+ discriminant,
expr: None,
});
}
@@ -173,3 +207,33 @@
let _ = namespace.pop().unwrap();
}
}
+
+enum ParsedDiscriminant {
+ Constant(Discriminant),
+ Successor,
+ Fail,
+}
+
+fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
+ if clang.is_empty() {
+ // No discriminant expression provided; use successor of previous
+ // descriminant.
+ return ParsedDiscriminant::Successor;
+ }
+
+ loop {
+ if clang.len() != 1 {
+ return ParsedDiscriminant::Fail;
+ }
+
+ let node = &clang[0];
+ match &node.kind {
+ Clang::ImplicitCastExpr => clang = &node.inner,
+ Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
+ Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
+ Err(_) => return ParsedDiscriminant::Fail,
+ },
+ _ => return ParsedDiscriminant::Fail,
+ }
+ }
+}
diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs
index 80f7c0b..01d0f1a 100644
--- a/syntax/discriminant.rs
+++ b/syntax/discriminant.rs
@@ -150,7 +150,7 @@
}
impl Discriminant {
- const fn zero() -> Self {
+ pub const fn zero() -> Self {
Discriminant {
sign: Sign::Positive,
magnitude: 0,
@@ -179,6 +179,28 @@
magnitude: i.wrapping_abs() as u64,
}
}
+
+ pub const fn checked_succ(self) -> Option<Self> {
+ match self.sign {
+ Sign::Negative => {
+ if self.magnitude == 1 {
+ Some(Discriminant::zero())
+ } else {
+ Some(Discriminant {
+ sign: Sign::Negative,
+ magnitude: self.magnitude - 1,
+ })
+ }
+ }
+ Sign::Positive => match self.magnitude.checked_add(1) {
+ Some(magnitude) => Some(Discriminant {
+ sign: Sign::Positive,
+ magnitude,
+ }),
+ None => None,
+ },
+ }
+ }
}
impl Display for Discriminant {
diff --git a/syntax/mod.rs b/syntax/mod.rs
index dd5de52..b22587f 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -30,7 +30,6 @@
mod visit;
use self::attrs::OtherAttrs;
-use self::discriminant::Discriminant;
use self::namespace::Namespace;
use self::parse::kw;
use self::symbol::Symbol;
@@ -41,6 +40,7 @@
pub use self::atom::Atom;
pub use self::derive::{Derive, Trait};
+pub use self::discriminant::Discriminant;
pub use self::doc::Doc;
pub use self::names::ForeignName;
pub use self::parse::parse_items;