Skip to content

Commit

Permalink
Merge pull request #293 from emilio/bitflags
Browse files Browse the repository at this point in the history
Improve support for bitflags. r=eqrion
  • Loading branch information
emilio authored Feb 26, 2019
2 parents e712cc4 + c05ce30 commit 8b4b9a3
Show file tree
Hide file tree
Showing 64 changed files with 833 additions and 289 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-4.9
- g++-4.9
env:
- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9"
- gcc-7
- g++-7
script:
- export CC=gcc-7
- export CXX=g++-7
- cargo fmt --all -- --check
- cargo build --verbose
- cargo test --verbose
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ serde_json = "1.0"
serde_derive = "1.0"
tempfile = "3.0"
toml = "0.4"
proc-macro2 = "0.4"
quote = "0.6"

[dependencies.syn]
version = "0.15.0"
default-features = false
features = ["clone-impls", "derive", "extra-traits", "full", "parsing", "printing"]
features = ["clone-impls", "extra-traits", "full", "parsing", "printing"]

[[bin]]
name = "cbindgen"
Expand Down
50 changes: 32 additions & 18 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ use std::io::{Read, Write};
use std::path;

use bindgen::config::{Config, Language};
use bindgen::ir::{Constant, Function, ItemContainer, Static};
use bindgen::ir::{
Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct,
};
use bindgen::writer::{Source, SourceWriter};

/// A bindings header that can be written.
pub struct Bindings {
config: Config,
pub config: Config,
/// The map from path to struct, used to lookup whether a given type is a
/// transparent struct. This is needed to generate code for constants.
struct_map: ItemMap<Struct>,
globals: Vec<Static>,
constants: Vec<Constant>,
items: Vec<ItemContainer>,
Expand All @@ -23,20 +28,29 @@ pub struct Bindings {
impl Bindings {
pub(crate) fn new(
config: Config,
struct_map: ItemMap<Struct>,
constants: Vec<Constant>,
globals: Vec<Static>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
) -> Bindings {
Bindings {
config: config,
globals: globals,
constants: constants,
items: items,
functions: functions,
config,
struct_map,
globals,
constants,
items,
functions,
}
}

// FIXME(emilio): What to do when the configuration doesn't match?
pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.struct_map.for_items(path, |s| any |= s.is_transparent);
any
}

pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
// Don't compare files if we've never written this file before
if !path.as_ref().is_file() {
Expand Down Expand Up @@ -126,7 +140,7 @@ impl Bindings {
}

pub fn write<F: Write>(&self, file: F) {
let mut out = SourceWriter::new(file, &self.config);
let mut out = SourceWriter::new(file, self);

if !self.config.no_includes
|| !self.config.includes.is_empty()
Expand All @@ -142,7 +156,7 @@ impl Bindings {
for constant in &self.constants {
if constant.ty.is_primitive_or_ptr_primitive() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out);
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
Expand All @@ -158,22 +172,22 @@ impl Bindings {
}

out.new_line_if_not_start();
match item {
&ItemContainer::Constant(..) => unreachable!(),
&ItemContainer::Static(..) => unreachable!(),
&ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
&ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
match *item {
ItemContainer::Constant(..) => unreachable!(),
ItemContainer::Static(..) => unreachable!(),
ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
}
out.new_line();
}

for constant in &self.constants {
if !constant.ty.is_primitive_or_ptr_primitive() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out);
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
Expand Down
138 changes: 138 additions & 0 deletions src/bindgen/bitflags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use proc_macro2::TokenStream;
use syn;
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};

// $(#[$outer:meta])*
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
// $(
// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
// )+
// }
#[derive(Debug)]
pub struct Bitflags {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
struct_token: Token![struct],
name: syn::Ident,
colon_token: Token![:],
repr: syn::Type,
flags: Flags,
}

impl Bitflags {
pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) {
let Bitflags {
ref attrs,
ref vis,
ref name,
ref repr,
ref flags,
..
} = *self;

let struct_ = parse_quote! {
#(#attrs)*
#vis struct #name {
bits: #repr,
}
};

let consts = flags.expand(name);
let impl_ = parse_quote! {
impl #name {
#consts
}
};

(struct_, impl_)
}
}

impl Parse for Bitflags {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
vis: input.parse()?,
struct_token: input.parse()?,
name: input.parse()?,
colon_token: input.parse()?,
repr: input.parse()?,
flags: input.parse()?,
})
}
}

// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
#[derive(Debug)]
struct Flag {
attrs: Vec<syn::Attribute>,
const_token: Token![const],
name: syn::Ident,
equals_token: Token![=],
value: syn::Expr,
semicolon_token: Token![;],
}

impl Flag {
fn expand(&self, struct_name: &syn::Ident) -> TokenStream {
let Flag {
ref attrs,
ref name,
ref value,
..
} = *self;
quote! {
#(#attrs)*
const #name : #struct_name = #struct_name { bits: #value };
}
}
}

impl Parse for Flag {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
const_token: input.parse()?,
name: input.parse()?,
equals_token: input.parse()?,
value: input.parse()?,
semicolon_token: input.parse()?,
})
}
}

#[derive(Debug)]
struct Flags(Vec<Flag>);

impl Parse for Flags {
fn parse(input: ParseStream) -> ParseResult<Self> {
let content;
let _ = braced!(content in input);
let mut flags = vec![];
while !content.is_empty() {
flags.push(content.parse()?);
}
Ok(Flags(flags))
}
}

impl Flags {
fn expand(&self, struct_name: &syn::Ident) -> TokenStream {
let mut ts = quote! {};
for flag in &self.0 {
ts.extend(flag.expand(struct_name));
}
ts
}
}

pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
let parser = Bitflags::parse;
parser.parse2(tokens)
}
4 changes: 4 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ pub struct StructConfig {
pub derive_gt: bool,
/// Whether to generate a greater than or equal to operator on structs with one field
pub derive_gte: bool,
/// Whether associated constants should be in the body. Only applicable to
/// non-transparent structs, and in C++-only.
pub associated_constants_in_body: bool,
}

impl Default for StructConfig {
Expand All @@ -297,6 +300,7 @@ impl Default for StructConfig {
derive_lte: false,
derive_gt: false,
derive_gte: false,
associated_constants_in_body: false,
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions src/bindgen/ir/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,12 @@ impl Cfg {
}
}

pub fn append(parent: &Option<Cfg>, child: Option<Cfg>) -> Option<Cfg> {
pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
match (parent, child) {
(&None, None) => None,
(&None, Some(child)) => Some(child),
(&Some(ref parent), None) => Some(parent.clone()),
(&Some(ref parent), Some(ref child)) => {
Some(Cfg::All(vec![parent.clone(), child.clone()]))
}
(None, None) => None,
(None, Some(child)) => Some(child),
(Some(parent), None) => Some(parent.clone()),
(Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
}
}

Expand Down
Loading

0 comments on commit 8b4b9a3

Please sign in to comment.