third_party_rust_bindgen/bindgen/ir/ty.rs
David Tolnay 0a78cde484
Fix name collision between C enum and typedef (#2326)
Fixes #2008.

Example:

```c
enum Enum { Variant };
typedef int16_t Enum;
```

This is valid and idiomatic C (though not valid C++). `cbindgen` uses this idiom as the default C translation of Rust enums, the equivalent of what would be `enum Enum : int16_t { Variant };` in C++.

`bindgen header.h` before:

```rust
pub const Enum_Variant: Enum = 0;
pub type Enum = ::std::os::raw::c_uint;
pub type Enum = i16;
```

```console
error[E0428]: the name `Enum` is defined multiple times
 --> generated.rs:3:1
  |
2 | pub type Enum = ::std::os::raw::c_uint;
  | --------------------------------------- previous definition of the type `Enum` here
3 | pub type Enum = i16;
  | ^^^^^^^^^^^^^^^^^^^^ `Enum` redefined here
  |
  = note: `Enum` must be defined only once in the type namespace of this module
```

After:

```rust

pub const Enum_Variant: Enum = 0;
pub type Enum = i16;
```
2022-11-28 09:40:20 -05:00

1293 lines
47 KiB
Rust

//! Everything related to types in our intermediate representation.
use super::comp::CompInfo;
use super::context::{BindgenContext, ItemId, TypeId};
use super::dot::DotAttributes;
use super::enum_ty::Enum;
use super::function::FunctionSig;
use super::int::IntKind;
use super::item::{IsOpaque, Item};
use super::layout::{Layout, Opaque};
use super::objc::ObjCInterface;
use super::template::{
AsTemplateParam, TemplateInstantiation, TemplateParameters,
};
use super::traversal::{EdgeKind, Trace, Tracer};
use crate::clang::{self, Cursor};
use crate::parse::{ParseError, ParseResult};
use std::borrow::Cow;
use std::io;
/// The base representation of a type in bindgen.
///
/// A type has an optional name, which if present cannot be empty, a `layout`
/// (size, alignment and packedness) if known, a `Kind`, which determines which
/// kind of type it is, and whether the type is const.
#[derive(Debug)]
pub struct Type {
/// The name of the type, or None if it was an unnamed struct or union.
name: Option<String>,
/// The layout of the type, if known.
layout: Option<Layout>,
/// The inner kind of the type
kind: TypeKind,
/// Whether this type is const-qualified.
is_const: bool,
}
/// The maximum number of items in an array for which Rust implements common
/// traits, and so if we have a type containing an array with more than this
/// many items, we won't be able to derive common traits on that type.
///
pub const RUST_DERIVE_IN_ARRAY_LIMIT: usize = 32;
impl Type {
/// Get the underlying `CompInfo` for this type, or `None` if this is some
/// other kind of type.
pub fn as_comp(&self) -> Option<&CompInfo> {
match self.kind {
TypeKind::Comp(ref ci) => Some(ci),
_ => None,
}
}
/// Get the underlying `CompInfo` for this type as a mutable reference, or
/// `None` if this is some other kind of type.
pub fn as_comp_mut(&mut self) -> Option<&mut CompInfo> {
match self.kind {
TypeKind::Comp(ref mut ci) => Some(ci),
_ => None,
}
}
/// Construct a new `Type`.
pub fn new(
name: Option<String>,
layout: Option<Layout>,
kind: TypeKind,
is_const: bool,
) -> Self {
Type {
name,
layout,
kind,
is_const,
}
}
/// Which kind of type is this?
pub fn kind(&self) -> &TypeKind {
&self.kind
}
/// Get a mutable reference to this type's kind.
pub fn kind_mut(&mut self) -> &mut TypeKind {
&mut self.kind
}
/// Get this type's name.
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
/// Whether this is a block pointer type.
pub fn is_block_pointer(&self) -> bool {
matches!(self.kind, TypeKind::BlockPointer(..))
}
/// Is this an integer type, including `bool` or `char`?
pub fn is_int(&self) -> bool {
matches!(self.kind, TypeKind::Int(_))
}
/// Is this a compound type?
pub fn is_comp(&self) -> bool {
matches!(self.kind, TypeKind::Comp(..))
}
/// Is this a union?
pub fn is_union(&self) -> bool {
match self.kind {
TypeKind::Comp(ref comp) => comp.is_union(),
_ => false,
}
}
/// Is this type of kind `TypeKind::TypeParam`?
pub fn is_type_param(&self) -> bool {
matches!(self.kind, TypeKind::TypeParam)
}
/// Is this a template instantiation type?
pub fn is_template_instantiation(&self) -> bool {
matches!(self.kind, TypeKind::TemplateInstantiation(..))
}
/// Is this a template alias type?
pub fn is_template_alias(&self) -> bool {
matches!(self.kind, TypeKind::TemplateAlias(..))
}
/// Is this a function type?
pub fn is_function(&self) -> bool {
matches!(self.kind, TypeKind::Function(..))
}
/// Is this an enum type?
pub fn is_enum(&self) -> bool {
matches!(self.kind, TypeKind::Enum(..))
}
/// Is this either a builtin or named type?
pub fn is_builtin_or_type_param(&self) -> bool {
matches!(
self.kind,
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Function(..) |
TypeKind::Array(..) |
TypeKind::Reference(..) |
TypeKind::Pointer(..) |
TypeKind::Int(..) |
TypeKind::Float(..) |
TypeKind::TypeParam
)
}
/// Creates a new named type, with name `name`.
pub fn named(name: String) -> Self {
let name = if name.is_empty() { None } else { Some(name) };
Self::new(name, None, TypeKind::TypeParam, false)
}
/// Is this a floating point type?
pub fn is_float(&self) -> bool {
matches!(self.kind, TypeKind::Float(..))
}
/// Is this a boolean type?
pub fn is_bool(&self) -> bool {
matches!(self.kind, TypeKind::Int(IntKind::Bool))
}
/// Is this an integer type?
pub fn is_integer(&self) -> bool {
matches!(self.kind, TypeKind::Int(..))
}
/// Cast this type to an integer kind, or `None` if it is not an integer
/// type.
pub fn as_integer(&self) -> Option<IntKind> {
match self.kind {
TypeKind::Int(int_kind) => Some(int_kind),
_ => None,
}
}
/// Is this a `const` qualified type?
pub fn is_const(&self) -> bool {
self.is_const
}
/// Is this a reference to another type?
pub fn is_type_ref(&self) -> bool {
matches!(
self.kind,
TypeKind::ResolvedTypeRef(_) | TypeKind::UnresolvedTypeRef(_, _, _)
)
}
/// Is this an unresolved reference?
pub fn is_unresolved_ref(&self) -> bool {
matches!(self.kind, TypeKind::UnresolvedTypeRef(_, _, _))
}
/// Is this a incomplete array type?
pub fn is_incomplete_array(&self, ctx: &BindgenContext) -> Option<ItemId> {
match self.kind {
TypeKind::Array(item, len) => {
if len == 0 {
Some(item.into())
} else {
None
}
}
TypeKind::ResolvedTypeRef(inner) => {
ctx.resolve_type(inner).is_incomplete_array(ctx)
}
_ => None,
}
}
/// What is the layout of this type?
pub fn layout(&self, ctx: &BindgenContext) -> Option<Layout> {
self.layout.or_else(|| {
match self.kind {
TypeKind::Comp(ref ci) => ci.layout(ctx),
TypeKind::Array(inner, length) if length == 0 => Some(
Layout::new(0, ctx.resolve_type(inner).layout(ctx)?.align),
),
// FIXME(emilio): This is a hack for anonymous union templates.
// Use the actual pointer size!
TypeKind::Pointer(..) => Some(Layout::new(
ctx.target_pointer_size(),
ctx.target_pointer_size(),
)),
TypeKind::ResolvedTypeRef(inner) => {
ctx.resolve_type(inner).layout(ctx)
}
_ => None,
}
})
}
/// Whether this named type is an invalid C++ identifier. This is done to
/// avoid generating invalid code with some cases we can't handle, see:
///
/// tests/headers/381-decltype-alias.hpp
pub fn is_invalid_type_param(&self) -> bool {
match self.kind {
TypeKind::TypeParam => {
let name = self.name().expect("Unnamed named type?");
!clang::is_valid_identifier(name)
}
_ => false,
}
}
/// Takes `name`, and returns a suitable identifier representation for it.
fn sanitize_name(name: &str) -> Cow<str> {
if clang::is_valid_identifier(name) {
return Cow::Borrowed(name);
}
let name = name.replace(|c| c == ' ' || c == ':' || c == '.', "_");
Cow::Owned(name)
}
/// Get this type's santizied name.
pub fn sanitized_name<'a>(
&'a self,
ctx: &BindgenContext,
) -> Option<Cow<'a, str>> {
let name_info = match *self.kind() {
TypeKind::Pointer(inner) => Some((inner, Cow::Borrowed("ptr"))),
TypeKind::Reference(inner) => Some((inner, Cow::Borrowed("ref"))),
TypeKind::Array(inner, length) => {
Some((inner, format!("array{}", length).into()))
}
_ => None,
};
if let Some((inner, prefix)) = name_info {
ctx.resolve_item(inner)
.expect_type()
.sanitized_name(ctx)
.map(|name| format!("{}_{}", prefix, name).into())
} else {
self.name().map(Self::sanitize_name)
}
}
/// See safe_canonical_type.
pub fn canonical_type<'tr>(
&'tr self,
ctx: &'tr BindgenContext,
) -> &'tr Type {
self.safe_canonical_type(ctx)
.expect("Should have been resolved after parsing!")
}
/// Returns the canonical type of this type, that is, the "inner type".
///
/// For example, for a `typedef`, the canonical type would be the
/// `typedef`ed type, for a template instantiation, would be the template
/// its specializing, and so on. Return None if the type is unresolved.
pub fn safe_canonical_type<'tr>(
&'tr self,
ctx: &'tr BindgenContext,
) -> Option<&'tr Type> {
match self.kind {
TypeKind::TypeParam |
TypeKind::Array(..) |
TypeKind::Vector(..) |
TypeKind::Comp(..) |
TypeKind::Opaque |
TypeKind::Int(..) |
TypeKind::Float(..) |
TypeKind::Complex(..) |
TypeKind::Function(..) |
TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Pointer(..) |
TypeKind::BlockPointer(..) |
TypeKind::ObjCId |
TypeKind::ObjCSel |
TypeKind::ObjCInterface(..) => Some(self),
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(inner) |
TypeKind::TemplateAlias(inner, _) => {
ctx.resolve_type(inner).safe_canonical_type(ctx)
}
TypeKind::TemplateInstantiation(ref inst) => ctx
.resolve_type(inst.template_definition())
.safe_canonical_type(ctx),
TypeKind::UnresolvedTypeRef(..) => None,
}
}
/// There are some types we don't want to stop at when finding an opaque
/// item, so we can arrive to the proper item that needs to be generated.
pub fn should_be_traced_unconditionally(&self) -> bool {
matches!(
self.kind,
TypeKind::Comp(..) |
TypeKind::Function(..) |
TypeKind::Pointer(..) |
TypeKind::Array(..) |
TypeKind::Reference(..) |
TypeKind::TemplateInstantiation(..) |
TypeKind::ResolvedTypeRef(..)
)
}
}
impl IsOpaque for Type {
type Extra = Item;
fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool {
match self.kind {
TypeKind::Opaque => true,
TypeKind::TemplateInstantiation(ref inst) => {
inst.is_opaque(ctx, item)
}
TypeKind::Comp(ref comp) => comp.is_opaque(ctx, &self.layout),
TypeKind::ResolvedTypeRef(to) => to.is_opaque(ctx, &()),
_ => false,
}
}
}
impl AsTemplateParam for Type {
type Extra = Item;
fn as_template_param(
&self,
ctx: &BindgenContext,
item: &Item,
) -> Option<TypeId> {
self.kind.as_template_param(ctx, item)
}
}
impl AsTemplateParam for TypeKind {
type Extra = Item;
fn as_template_param(
&self,
ctx: &BindgenContext,
item: &Item,
) -> Option<TypeId> {
match *self {
TypeKind::TypeParam => Some(item.id().expect_type_id(ctx)),
TypeKind::ResolvedTypeRef(id) => id.as_template_param(ctx, &()),
_ => None,
}
}
}
impl DotAttributes for Type {
fn dot_attributes<W>(
&self,
ctx: &BindgenContext,
out: &mut W,
) -> io::Result<()>
where
W: io::Write,
{
if let Some(ref layout) = self.layout {
writeln!(
out,
"<tr><td>size</td><td>{}</td></tr>
<tr><td>align</td><td>{}</td></tr>",
layout.size, layout.align
)?;
if layout.packed {
writeln!(out, "<tr><td>packed</td><td>true</td></tr>")?;
}
}
if self.is_const {
writeln!(out, "<tr><td>const</td><td>true</td></tr>")?;
}
self.kind.dot_attributes(ctx, out)
}
}
impl DotAttributes for TypeKind {
fn dot_attributes<W>(
&self,
ctx: &BindgenContext,
out: &mut W,
) -> io::Result<()>
where
W: io::Write,
{
writeln!(
out,
"<tr><td>type kind</td><td>{}</td></tr>",
self.kind_name()
)?;
if let TypeKind::Comp(ref comp) = *self {
comp.dot_attributes(ctx, out)?;
}
Ok(())
}
}
impl TypeKind {
fn kind_name(&self) -> &'static str {
match *self {
TypeKind::Void => "Void",
TypeKind::NullPtr => "NullPtr",
TypeKind::Comp(..) => "Comp",
TypeKind::Opaque => "Opaque",
TypeKind::Int(..) => "Int",
TypeKind::Float(..) => "Float",
TypeKind::Complex(..) => "Complex",
TypeKind::Alias(..) => "Alias",
TypeKind::TemplateAlias(..) => "TemplateAlias",
TypeKind::Array(..) => "Array",
TypeKind::Vector(..) => "Vector",
TypeKind::Function(..) => "Function",
TypeKind::Enum(..) => "Enum",
TypeKind::Pointer(..) => "Pointer",
TypeKind::BlockPointer(..) => "BlockPointer",
TypeKind::Reference(..) => "Reference",
TypeKind::TemplateInstantiation(..) => "TemplateInstantiation",
TypeKind::UnresolvedTypeRef(..) => "UnresolvedTypeRef",
TypeKind::ResolvedTypeRef(..) => "ResolvedTypeRef",
TypeKind::TypeParam => "TypeParam",
TypeKind::ObjCInterface(..) => "ObjCInterface",
TypeKind::ObjCId => "ObjCId",
TypeKind::ObjCSel => "ObjCSel",
}
}
}
#[test]
fn is_invalid_type_param_valid() {
let ty = Type::new(Some("foo".into()), None, TypeKind::TypeParam, false);
assert!(!ty.is_invalid_type_param())
}
#[test]
fn is_invalid_type_param_valid_underscore_and_numbers() {
let ty = Type::new(
Some("_foo123456789_".into()),
None,
TypeKind::TypeParam,
false,
);
assert!(!ty.is_invalid_type_param())
}
#[test]
fn is_invalid_type_param_valid_unnamed_kind() {
let ty = Type::new(Some("foo".into()), None, TypeKind::Void, false);
assert!(!ty.is_invalid_type_param())
}
#[test]
fn is_invalid_type_param_invalid_start() {
let ty = Type::new(Some("1foo".into()), None, TypeKind::TypeParam, false);
assert!(ty.is_invalid_type_param())
}
#[test]
fn is_invalid_type_param_invalid_remaing() {
let ty = Type::new(Some("foo-".into()), None, TypeKind::TypeParam, false);
assert!(ty.is_invalid_type_param())
}
#[test]
#[should_panic]
fn is_invalid_type_param_unnamed() {
let ty = Type::new(None, None, TypeKind::TypeParam, false);
assert!(ty.is_invalid_type_param())
}
#[test]
fn is_invalid_type_param_empty_name() {
let ty = Type::new(Some("".into()), None, TypeKind::TypeParam, false);
assert!(ty.is_invalid_type_param())
}
impl TemplateParameters for Type {
fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> {
self.kind.self_template_params(ctx)
}
}
impl TemplateParameters for TypeKind {
fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> {
match *self {
TypeKind::ResolvedTypeRef(id) => {
ctx.resolve_type(id).self_template_params(ctx)
}
TypeKind::Comp(ref comp) => comp.self_template_params(ctx),
TypeKind::TemplateAlias(_, ref args) => args.clone(),
TypeKind::Opaque |
TypeKind::TemplateInstantiation(..) |
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Int(_) |
TypeKind::Float(_) |
TypeKind::Complex(_) |
TypeKind::Array(..) |
TypeKind::Vector(..) |
TypeKind::Function(_) |
TypeKind::Enum(_) |
TypeKind::Pointer(_) |
TypeKind::BlockPointer(_) |
TypeKind::Reference(_) |
TypeKind::UnresolvedTypeRef(..) |
TypeKind::TypeParam |
TypeKind::Alias(_) |
TypeKind::ObjCId |
TypeKind::ObjCSel |
TypeKind::ObjCInterface(_) => vec![],
}
}
}
/// The kind of float this type represents.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FloatKind {
/// A `float`.
Float,
/// A `double`.
Double,
/// A `long double`.
LongDouble,
/// A `__float128`.
Float128,
}
/// The different kinds of types that we can parse.
#[derive(Debug)]
pub enum TypeKind {
/// The void type.
Void,
/// The `nullptr_t` type.
NullPtr,
/// A compound type, that is, a class, struct, or union.
Comp(CompInfo),
/// An opaque type that we just don't understand. All usage of this shoulf
/// result in an opaque blob of bytes generated from the containing type's
/// layout.
Opaque,
/// An integer type, of a given kind. `bool` and `char` are also considered
/// integers.
Int(IntKind),
/// A floating point type.
Float(FloatKind),
/// A complex floating point type.
Complex(FloatKind),
/// A type alias, with a name, that points to another type.
Alias(TypeId),
/// A templated alias, pointing to an inner type, just as `Alias`, but with
/// template parameters.
TemplateAlias(TypeId, Vec<TypeId>),
/// A packed vector type: element type, number of elements
Vector(TypeId, usize),
/// An array of a type and a length.
Array(TypeId, usize),
/// A function type, with a given signature.
Function(FunctionSig),
/// An `enum` type.
Enum(Enum),
/// A pointer to a type. The bool field represents whether it's const or
/// not.
Pointer(TypeId),
/// A pointer to an Apple block.
BlockPointer(TypeId),
/// A reference to a type, as in: int& foo().
Reference(TypeId),
/// An instantiation of an abstract template definition with a set of
/// concrete template arguments.
TemplateInstantiation(TemplateInstantiation),
/// A reference to a yet-to-resolve type. This stores the clang cursor
/// itself, and postpones its resolution.
///
/// These are gone in a phase after parsing where these are mapped to
/// already known types, and are converted to ResolvedTypeRef.
///
/// see tests/headers/typeref.hpp to see somewhere where this is a problem.
UnresolvedTypeRef(
clang::Type,
clang::Cursor,
/* parent_id */
Option<ItemId>,
),
/// An indirection to another type.
///
/// These are generated after we resolve a forward declaration, or when we
/// replace one type with another.
ResolvedTypeRef(TypeId),
/// A named type, that is, a template parameter.
TypeParam,
/// Objective C interface. Always referenced through a pointer
ObjCInterface(ObjCInterface),
/// Objective C 'id' type, points to any object
ObjCId,
/// Objective C selector type
ObjCSel,
}
impl Type {
/// This is another of the nasty methods. This one is the one that takes
/// care of the core logic of converting a clang type to a `Type`.
///
/// It's sort of nasty and full of special-casing, but hopefully the
/// comments in every special case justify why they're there.
pub fn from_clang_ty(
potential_id: ItemId,
ty: &clang::Type,
location: Cursor,
parent_id: Option<ItemId>,
ctx: &mut BindgenContext,
) -> Result<ParseResult<Self>, ParseError> {
use clang_sys::*;
{
let already_resolved = ctx.builtin_or_resolved_ty(
potential_id,
parent_id,
ty,
Some(location),
);
if let Some(ty) = already_resolved {
debug!("{:?} already resolved: {:?}", ty, location);
return Ok(ParseResult::AlreadyResolved(ty.into()));
}
}
let layout = ty.fallible_layout(ctx).ok();
let cursor = ty.declaration();
let is_anonymous = cursor.is_anonymous();
let mut name = if is_anonymous {
None
} else {
Some(cursor.spelling()).filter(|n| !n.is_empty())
};
debug!(
"from_clang_ty: {:?}, ty: {:?}, loc: {:?}",
potential_id, ty, location
);
debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types());
let canonical_ty = ty.canonical_type();
// Parse objc protocols as if they were interfaces
let mut ty_kind = ty.kind();
match location.kind() {
CXCursor_ObjCProtocolDecl | CXCursor_ObjCCategoryDecl => {
ty_kind = CXType_ObjCInterface
}
_ => {}
}
// Objective C template type parameter
// FIXME: This is probably wrong, we are attempting to find the
// objc template params, which seem to manifest as a typedef.
// We are rewriting them as id to suppress multiple conflicting
// typedefs at root level
if ty_kind == CXType_Typedef {
let is_template_type_param =
ty.declaration().kind() == CXCursor_TemplateTypeParameter;
let is_canonical_objcpointer =
canonical_ty.kind() == CXType_ObjCObjectPointer;
// We have found a template type for objc interface
if is_canonical_objcpointer && is_template_type_param {
// Objective-C generics are just ids with fancy name.
// To keep it simple, just name them ids
name = Some("id".to_owned());
}
}
if location.kind() == CXCursor_ClassTemplatePartialSpecialization {
// Sorry! (Not sorry)
warn!(
"Found a partial template specialization; bindgen does not \
support partial template specialization! Constructing \
opaque type instead."
);
return Ok(ParseResult::New(
Opaque::from_clang_ty(&canonical_ty, ctx),
None,
));
}
let kind = if location.kind() == CXCursor_TemplateRef ||
(ty.template_args().is_some() && ty_kind != CXType_Typedef)
{
// This is a template instantiation.
match TemplateInstantiation::from_ty(ty, ctx) {
Some(inst) => TypeKind::TemplateInstantiation(inst),
None => TypeKind::Opaque,
}
} else {
match ty_kind {
CXType_Unexposed
if *ty != canonical_ty &&
canonical_ty.kind() != CXType_Invalid &&
ty.ret_type().is_none() &&
// Sometime clang desugars some types more than
// what we need, specially with function
// pointers.
//
// We should also try the solution of inverting
// those checks instead of doing this, that is,
// something like:
//
// CXType_Unexposed if ty.ret_type().is_some()
// => { ... }
//
// etc.
!canonical_ty.spelling().contains("type-parameter") =>
{
debug!("Looking for canonical type: {:?}", canonical_ty);
return Self::from_clang_ty(
potential_id,
&canonical_ty,
location,
parent_id,
ctx,
);
}
CXType_Unexposed | CXType_Invalid => {
// For some reason Clang doesn't give us any hint in some
// situations where we should generate a function pointer (see
// tests/headers/func_ptr_in_struct.h), so we do a guess here
// trying to see if it has a valid return type.
if ty.ret_type().is_some() {
let signature =
FunctionSig::from_ty(ty, &location, ctx)?;
TypeKind::Function(signature)
// Same here, with template specialisations we can safely
// assume this is a Comp(..)
} else if ty.is_fully_instantiated_template() {
debug!(
"Template specialization: {:?}, {:?} {:?}",
ty, location, canonical_ty
);
let complex = CompInfo::from_ty(
potential_id,
ty,
Some(location),
ctx,
)
.expect("C'mon");
TypeKind::Comp(complex)
} else {
match location.kind() {
CXCursor_CXXBaseSpecifier |
CXCursor_ClassTemplate => {
if location.kind() == CXCursor_CXXBaseSpecifier
{
// In the case we're parsing a base specifier
// inside an unexposed or invalid type, it means
// that we're parsing one of two things:
//
// * A template parameter.
// * A complex class that isn't exposed.
//
// This means, unfortunately, that there's no
// good way to differentiate between them.
//
// Probably we could try to look at the
// declaration and complicate more this logic,
// but we'll keep it simple... if it's a valid
// C++ identifier, we'll consider it as a
// template parameter.
//
// This is because:
//
// * We expect every other base that is a
// proper identifier (that is, a simple
// struct/union declaration), to be exposed,
// so this path can't be reached in that
// case.
//
// * Quite conveniently, complex base
// specifiers preserve their full names (that
// is: Foo<T> instead of Foo). We can take
// advantage of this.
//
// If we find some edge case where this doesn't
// work (which I guess is unlikely, see the
// different test cases[1][2][3][4]), we'd need
// to find more creative ways of differentiating
// these two cases.
//
// [1]: inherit_named.hpp
// [2]: forward-inherit-struct-with-fields.hpp
// [3]: forward-inherit-struct.hpp
// [4]: inherit-namespaced.hpp
if location.spelling().chars().all(|c| {
c.is_alphanumeric() || c == '_'
}) {
return Err(ParseError::Recurse);
}
} else {
name = Some(location.spelling());
}
let complex = CompInfo::from_ty(
potential_id,
ty,
Some(location),
ctx,
);
match complex {
Ok(complex) => TypeKind::Comp(complex),
Err(_) => {
warn!(
"Could not create complex type \
from class template or base \
specifier, using opaque blob"
);
let opaque =
Opaque::from_clang_ty(ty, ctx);
return Ok(ParseResult::New(
opaque, None,
));
}
}
}
CXCursor_TypeAliasTemplateDecl => {
debug!("TypeAliasTemplateDecl");
// We need to manually unwind this one.
let mut inner = Err(ParseError::Continue);
let mut args = vec![];
location.visit(|cur| {
match cur.kind() {
CXCursor_TypeAliasDecl => {
let current = cur.cur_type();
debug_assert_eq!(
current.kind(),
CXType_Typedef
);
name = Some(location.spelling());
let inner_ty = cur
.typedef_type()
.expect("Not valid Type?");
inner = Ok(Item::from_ty_or_ref(
inner_ty,
cur,
Some(potential_id),
ctx,
));
}
CXCursor_TemplateTypeParameter => {
let param = Item::type_param(
None, cur, ctx,
)
.expect(
"Item::type_param shouldn't \
ever fail if we are looking \
at a TemplateTypeParameter",
);
args.push(param);
}
_ => {}
}
CXChildVisit_Continue
});
let inner_type = match inner {
Ok(inner) => inner,
Err(..) => {
warn!(
"Failed to parse template alias \
{:?}",
location
);
return Err(ParseError::Continue);
}
};
TypeKind::TemplateAlias(inner_type, args)
}
CXCursor_TemplateRef => {
let referenced = location.referenced().unwrap();
let referenced_ty = referenced.cur_type();
debug!(
"TemplateRef: location = {:?}; referenced = \
{:?}; referenced_ty = {:?}",
location,
referenced,
referenced_ty
);
return Self::from_clang_ty(
potential_id,
&referenced_ty,
referenced,
parent_id,
ctx,
);
}
CXCursor_TypeRef => {
let referenced = location.referenced().unwrap();
let referenced_ty = referenced.cur_type();
let declaration = referenced_ty.declaration();
debug!(
"TypeRef: location = {:?}; referenced = \
{:?}; referenced_ty = {:?}",
location, referenced, referenced_ty
);
let id = Item::from_ty_or_ref_with_id(
potential_id,
referenced_ty,
declaration,
parent_id,
ctx,
);
return Ok(ParseResult::AlreadyResolved(
id.into(),
));
}
CXCursor_NamespaceRef => {
return Err(ParseError::Continue);
}
_ => {
if ty.kind() == CXType_Unexposed {
warn!(
"Unexposed type {:?}, recursing inside, \
loc: {:?}",
ty,
location
);
return Err(ParseError::Recurse);
}
warn!("invalid type {:?}", ty);
return Err(ParseError::Continue);
}
}
}
}
CXType_Auto => {
if canonical_ty == *ty {
debug!("Couldn't find deduced type: {:?}", ty);
return Err(ParseError::Continue);
}
return Self::from_clang_ty(
potential_id,
&canonical_ty,
location,
parent_id,
ctx,
);
}
// NOTE: We don't resolve pointers eagerly because the pointee type
// might not have been parsed, and if it contains templates or
// something else we might get confused, see the comment inside
// TypeRef.
//
// We might need to, though, if the context is already in the
// process of resolving them.
CXType_ObjCObjectPointer |
CXType_MemberPointer |
CXType_Pointer => {
let mut pointee = ty.pointee_type().unwrap();
if *ty != canonical_ty {
let canonical_pointee =
canonical_ty.pointee_type().unwrap();
// clang sometimes loses pointee constness here, see
// #2244.
if canonical_pointee.is_const() != pointee.is_const() {
pointee = canonical_pointee;
}
}
let inner =
Item::from_ty_or_ref(pointee, location, None, ctx);
TypeKind::Pointer(inner)
}
CXType_BlockPointer => {
let pointee = ty.pointee_type().expect("Not valid Type?");
let inner =
Item::from_ty_or_ref(pointee, location, None, ctx);
TypeKind::BlockPointer(inner)
}
// XXX: RValueReference is most likely wrong, but I don't think we
// can even add bindings for that, so huh.
CXType_RValueReference | CXType_LValueReference => {
let inner = Item::from_ty_or_ref(
ty.pointee_type().unwrap(),
location,
None,
ctx,
);
TypeKind::Reference(inner)
}
// XXX DependentSizedArray is wrong
CXType_VariableArray | CXType_DependentSizedArray => {
let inner = Item::from_ty(
ty.elem_type().as_ref().unwrap(),
location,
None,
ctx,
)
.expect("Not able to resolve array element?");
TypeKind::Pointer(inner)
}
CXType_IncompleteArray => {
let inner = Item::from_ty(
ty.elem_type().as_ref().unwrap(),
location,
None,
ctx,
)
.expect("Not able to resolve array element?");
TypeKind::Array(inner, 0)
}
CXType_FunctionNoProto | CXType_FunctionProto => {
let signature = FunctionSig::from_ty(ty, &location, ctx)?;
TypeKind::Function(signature)
}
CXType_Typedef => {
let inner = cursor.typedef_type().expect("Not valid Type?");
let inner_id =
Item::from_ty_or_ref(inner, location, None, ctx);
if inner_id == potential_id {
warn!(
"Generating oqaque type instead of self-referential \
typedef");
// This can happen if we bail out of recursive situations
// within the clang parsing.
TypeKind::Opaque
} else {
// Check if this type definition is an alias to a pointer of a `struct` /
// `union` / `enum` with the same name and add the `_ptr` suffix to it to
// avoid name collisions.
if let Some(ref mut name) = name {
if inner.kind() == CXType_Pointer &&
!ctx.options().c_naming
{
let pointee = inner.pointee_type().unwrap();
if pointee.kind() == CXType_Elaborated &&
pointee.declaration().spelling() == *name
{
*name += "_ptr";
}
}
}
TypeKind::Alias(inner_id)
}
}
CXType_Enum => {
let enum_ = Enum::from_ty(ty, ctx).expect("Not an enum?");
if !is_anonymous {
let pretty_name = ty.spelling();
if clang::is_valid_identifier(&pretty_name) {
name = Some(pretty_name);
}
}
TypeKind::Enum(enum_)
}
CXType_Record => {
let complex = CompInfo::from_ty(
potential_id,
ty,
Some(location),
ctx,
)
.expect("Not a complex type?");
if !is_anonymous {
// The pretty-printed name may contain typedefed name,
// but may also be "struct (anonymous at .h:1)"
let pretty_name = ty.spelling();
if clang::is_valid_identifier(&pretty_name) {
name = Some(pretty_name);
}
}
TypeKind::Comp(complex)
}
CXType_Vector => {
let inner = Item::from_ty(
ty.elem_type().as_ref().unwrap(),
location,
None,
ctx,
)?;
TypeKind::Vector(inner, ty.num_elements().unwrap())
}
CXType_ConstantArray => {
let inner = Item::from_ty(
ty.elem_type().as_ref().unwrap(),
location,
None,
ctx,
)
.expect("Not able to resolve array element?");
TypeKind::Array(inner, ty.num_elements().unwrap())
}
CXType_Elaborated => {
return Self::from_clang_ty(
potential_id,
&ty.named(),
location,
parent_id,
ctx,
);
}
CXType_ObjCId => TypeKind::ObjCId,
CXType_ObjCSel => TypeKind::ObjCSel,
CXType_ObjCClass | CXType_ObjCInterface => {
let interface = ObjCInterface::from_ty(&location, ctx)
.expect("Not a valid objc interface?");
if !is_anonymous {
name = Some(interface.rust_name());
}
TypeKind::ObjCInterface(interface)
}
CXType_Dependent => {
return Err(ParseError::Continue);
}
_ => {
warn!(
"unsupported type: kind = {:?}; ty = {:?}; at {:?}",
ty.kind(),
ty,
location
);
return Err(ParseError::Continue);
}
}
};
name = name.filter(|n| !n.is_empty());
let is_const = ty.is_const() ||
(ty.kind() == CXType_ConstantArray &&
ty.elem_type()
.map_or(false, |element| element.is_const()));
let ty = Type::new(name, layout, kind, is_const);
// TODO: maybe declaration.canonical()?
Ok(ParseResult::New(ty, Some(cursor.canonical())))
}
}
impl Trace for Type {
type Extra = Item;
fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, item: &Item)
where
T: Tracer,
{
if self
.name()
.map_or(false, |name| context.is_stdint_type(name))
{
// These types are special-cased in codegen and don't need to be traversed.
return;
}
match *self.kind() {
TypeKind::Pointer(inner) |
TypeKind::Reference(inner) |
TypeKind::Array(inner, _) |
TypeKind::Vector(inner, _) |
TypeKind::BlockPointer(inner) |
TypeKind::Alias(inner) |
TypeKind::ResolvedTypeRef(inner) => {
tracer.visit_kind(inner.into(), EdgeKind::TypeReference);
}
TypeKind::TemplateAlias(inner, ref template_params) => {
tracer.visit_kind(inner.into(), EdgeKind::TypeReference);
for param in template_params {
tracer.visit_kind(
param.into(),
EdgeKind::TemplateParameterDefinition,
);
}
}
TypeKind::TemplateInstantiation(ref inst) => {
inst.trace(context, tracer, &());
}
TypeKind::Comp(ref ci) => ci.trace(context, tracer, item),
TypeKind::Function(ref sig) => sig.trace(context, tracer, &()),
TypeKind::Enum(ref en) => {
if let Some(repr) = en.repr() {
tracer.visit(repr.into());
}
}
TypeKind::UnresolvedTypeRef(_, _, Some(id)) => {
tracer.visit(id);
}
TypeKind::ObjCInterface(ref interface) => {
interface.trace(context, tracer, &());
}
// None of these variants have edges to other items and types.
TypeKind::Opaque |
TypeKind::UnresolvedTypeRef(_, _, None) |
TypeKind::TypeParam |
TypeKind::Void |
TypeKind::NullPtr |
TypeKind::Int(_) |
TypeKind::Float(_) |
TypeKind::Complex(_) |
TypeKind::ObjCId |
TypeKind::ObjCSel => {}
}
}
}