Parse discriminant values from clang AST

This commit is contained in:
David Tolnay 2021-04-22 14:44:42 -07:00
parent ad7a3bdc48
commit e2250bca87
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
3 changed files with 90 additions and 4 deletions

View File

@ -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 @@ enum Clang {
NamespaceDecl(NamespaceDecl),
EnumDecl(EnumDecl),
EnumConstantDecl(EnumConstantDecl),
ImplicitCastExpr,
ConstantExpr(ConstantExpr),
Unknown,
}
@ -36,6 +39,11 @@ struct EnumConstantDecl {
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 @@ fn traverse<'a>(
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 @@ fn traverse<'a>(
cxx: cxx_name,
rust: rust_name,
},
discriminant: unimplemented!(),
discriminant,
expr: None,
});
}
@ -173,3 +207,33 @@ fn traverse<'a>(
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,
}
}
}

View File

@ -150,7 +150,7 @@ fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discr
}
impl Discriminant {
const fn zero() -> Self {
pub const fn zero() -> Self {
Discriminant {
sign: Sign::Positive,
magnitude: 0,
@ -179,6 +179,28 @@ impl Discriminant {
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 {

View File

@ -30,7 +30,6 @@ pub mod types;
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 @@ use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
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;