/// The type for a constant variable. #[derive(Debug)] pub(crate) enum VarType { /// A boolean.
Bool(bool), /// An integer.
Int(i64), /// A floating point number.
Float(f64), /// A character.
Char(u8), /// A string, not necessarily well-formed utf-8.
String(Vec<u8>),
}
/// A `Var` is our intermediate representation of a variable. #[derive(Debug)] pub(crate) struct Var { /// The name of the variable.
name: String, /// The mangled name of the variable.
mangled_name: Option<String>, /// The link name of the variable.
link_name: Option<String>, /// The type of the variable.
ty: TypeId, /// The value of the variable, that needs to be suitable for `ty`.
val: Option<VarType>, /// Whether this variable is const.
is_const: bool,
}
impl Var { /// Construct a new `Var`. pub(crate) fn new(
name: String,
mangled_name: Option<String>,
link_name: Option<String>,
ty: TypeId,
val: Option<VarType>,
is_const: bool,
) -> Var {
assert!(!name.is_empty());
Var {
name,
mangled_name,
link_name,
ty,
val,
is_const,
}
}
/// Is this variable `const` qualified? pub(crate) fn is_const(&self) -> bool { self.is_const
}
/// The value of this constant variable, if any. pub(crate) fn val(&self) -> Option<&VarType> { self.val.as_ref()
}
/// Get this variable's type. pub(crate) fn ty(&self) -> TypeId { self.ty
}
/// Get this variable's name. pub(crate) fn name(&self) -> &str {
&self.name
}
/// Get this variable's mangled name. pub(crate) fn mangled_name(&self) -> Option<&str> { self.mangled_name.as_deref()
}
/// Get this variable's link name. pubfn link_name(&self) -> Option<&str> { self.link_name.as_deref()
}
}
impl DotAttributes for Var { fn dot_attributes<W>(
&self,
_ctx: &BindgenContext,
out: &mut W,
) -> io::Result<()> where
W: io::Write,
{ ifself.is_const {
writeln!(out, "<tr><td>const</td><td>true</td></tr>")?;
}
fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind { if value < 0 ||
ctx.options().default_macro_constant_type ==
MacroTypeVariation::Signed
{ if value < i32::min_value() as i64 || value > i32::max_value() as i64 {
IntKind::I64
} elseif !ctx.options().fit_macro_constants ||
value < i16::min_value() as i64 ||
value > i16::max_value() as i64
{
IntKind::I32
} elseif value < i8::min_value() as i64 ||
value > i8::max_value() as i64
{
IntKind::I16
} else {
IntKind::I8
}
} elseif value > u32::max_value() as i64 {
IntKind::U64
} elseif !ctx.options().fit_macro_constants ||
value > u16::max_value() as i64
{
IntKind::U32
} elseif value > u8::max_value() as i64 {
IntKind::U16
} else {
IntKind::U8
}
}
/// Parses tokens from a CXCursor_MacroDefinition pointing into a function-like /// macro, and calls the func_macro callback. fn handle_function_macro(
cursor: &clang::Cursor,
callbacks: &dyncrate::callbacks::ParseCallbacks,
) { let is_closing_paren = |t: &ClangToken| { // Test cheap token kind before comparing exact spellings.
t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")"
}; let tokens: Vec<_> = cursor.tokens().iter().collect(); iflet Some(boundary) = tokens.iter().position(is_closing_paren) { letmut spelled = tokens.iter().map(ClangToken::spelling); // Add 1, to convert index to length. let left = spelled.by_ref().take(boundary + 1); let left = left.collect::<Vec<_>>().concat(); iflet Ok(left) = String::from_utf8(left) { let right: Vec<_> = spelled.collect();
callbacks.func_macro(&left, &right);
}
}
}
impl ClangSubItemParser for Var { fn parse(
cursor: clang::Cursor,
ctx: &mut BindgenContext,
) -> Result<ParseResult<Self>, ParseError> { use cexpr::expr::EvalResult; use cexpr::literal::CChar; use clang_sys::*; match cursor.kind() {
CXCursor_MacroDefinition => { for callbacks in &ctx.options().parse_callbacks { match callbacks.will_parse_macro(&cursor.spelling()) {
MacroParsingBehavior::Ignore => { return Err(ParseError::Continue);
}
MacroParsingBehavior::Default => {}
}
if cursor.is_macro_function_like() {
handle_function_macro(&cursor, callbacks.as_ref()); // We handled the macro, skip macro processing below. return Err(ParseError::Continue);
}
}
let value = parse_macro(ctx, &cursor);
let (id, value) = match value {
Some(v) => v,
None => return Err(ParseError::Continue),
};
assert!(!id.is_empty(), "Empty macro name?");
let previously_defined = ctx.parsed_macro(&id);
// NB: It's important to "note" the macro even if the result is // not an integer, otherwise we might loose other kind of // derived macros.
ctx.note_parsed_macro(id.clone(), value.clone());
if previously_defined { let name = String::from_utf8(id).unwrap();
duplicated_macro_diagnostic(&name, cursor.location(), ctx); return Err(ParseError::Continue);
}
// NOTE: Unwrapping, here and above, is safe, because the // identifier of a token comes straight from clang, and we // enforce utf8 there, so we should have already panicked at // this point. let name = String::from_utf8(id).unwrap(); let (type_kind, val) = match value {
EvalResult::Invalid => return Err(ParseError::Continue),
EvalResult::Float(f) => {
(TypeKind::Float(FloatKind::Double), VarType::Float(f))
}
EvalResult::Char(c) => { let c = match c {
CChar::Char(c) => {
assert_eq!(c.len_utf8(), 1);
c as u8
}
CChar::Raw(c) => {
assert!(c <= ::std::u8::MAX as u64);
c as u8
}
};
// TODO(emilio): do we have to special-case constant arrays in // some other places? let is_const = ty.is_const() ||
([CXType_ConstantArray, CXType_IncompleteArray]
.contains(&ty.kind()) &&
ty.elem_type()
.map_or(false, |element| element.is_const()));
let ty = match Item::from_ty(&ty, cursor, None, ctx) {
Ok(ty) => ty,
Err(e) => {
assert!(
matches!(ty.kind(), CXType_Auto | CXType_Unexposed), "Couldn't resolve constant type, and it \
wasn't an nondeductible auto type or unexposed \ type!"
); return Err(e);
}
};
// Note: Ty might not be totally resolved yet, see // tests/headers/inner_const.hpp // // That's fine because in that case we know it's not a literal. let canonical_ty = ctx
.safe_resolve_type(ty)
.and_then(|t| t.safe_canonical_type(ctx));
let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); let is_float = canonical_ty.map_or(false, |t| t.is_float());
// TODO: We could handle `char` more gracefully. // TODO: Strings, though the lookup is a bit more hard (we need // to look at the canonical type of the pointee too, and check // is char, u8, or i8 I guess). let value = if is_integer { let kind = match *canonical_ty.unwrap().kind() {
TypeKind::Int(kind) => kind,
_ => unreachable!(),
};
letmut val = cursor.evaluate().and_then(|v| v.as_int()); if val.is_none() || !kind.signedness_matches(val.unwrap()) {
val = get_integer_literal_from_cursor(&cursor);
}
/// Try and parse a macro using all the macros parsed until now. fn parse_macro(
ctx: &BindgenContext,
cursor: &clang::Cursor,
) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> { use cexpr::expr;
let cexpr_tokens = cursor.cexpr_tokens();
let parser = expr::IdentifierParser::new(ctx.parsed_macros());
fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> { use cexpr::expr; use cexpr::expr::EvalResult;
let cexpr_tokens = cursor.cexpr_tokens();
// TODO(emilio): We can try to parse other kinds of literals. match expr::expr(&cexpr_tokens) {
Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val),
_ => None,
}
}
fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> { use clang_sys::*; letmut value = None;
cursor.visit(|c| { match c.kind() {
CXCursor_IntegerLiteral | CXCursor_UnaryOperator => {
value = parse_int_literal_tokens(&c);
}
CXCursor_UnexposedExpr => {
value = get_integer_literal_from_cursor(&c);
}
_ => (),
} if value.is_some() {
CXChildVisit_Break
} else {
CXChildVisit_Continue
}
});
value
}
#[cfg(feature = "experimental")] // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually // useful. We have to change the logic where this function is called to be able to emit this // message only when the duplication is an actuall issue. // // If I understood correctly, `bindgen` ignores all `#undef` directives. Meaning that this: // ```c // #define FOO 1 // #undef FOO // #define FOO 2 // ``` // // Will trigger this message even though there's nothing wrong with it. #[allow(clippy::overly_complex_bool_expr)] iffalse && _ctx.options().emit_diagnostics { usecrate::diagnostics::{get_line, Diagnostic, Level, Slice}; use std::borrow::Cow;