diff --git a/Cargo.lock b/Cargo.lock index 3f0f65df06..8639b56e04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,7 @@ dependencies = [ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -339,6 +340,14 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "which" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -393,5 +402,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d238435618c0f298d2d75596c2d4fa7d4ea469c0c1c3ff824737ed50ad5ab61c" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 30c319f7f5..69ed15f824 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ syntex_syntax = "0.58" regex = "0.2" # This kinda sucks: https://github.com/rust-lang/cargo/issues/1982 clap = "2" +which = "1.0.2" [dependencies.aster] features = ["with-syntex"] diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 886f386032..6113e5d5b5 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -175,7 +175,7 @@ impl<'a> CodegenResult<'a> { /// counter internally so the next time we ask for the overload for this /// name, we get the incremented value, and so on. fn overload_number(&mut self, name: &str) -> u32 { - let mut counter = + let counter = self.overload_counters.entry(name.into()).or_insert(0); let number = *counter; *counter += 1; @@ -2030,7 +2030,7 @@ impl MethodCodegen for Method { } let count = { - let mut count = method_names.entry(name.clone()).or_insert(0); + let count = method_names.entry(name.clone()).or_insert(0); *count += 1; *count - 1 }; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index ecdfb5debd..98002b68a7 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -460,7 +460,7 @@ fn raw_fields_to_fields_and_bitfield_units(ctx: &BindgenContext, /// (potentially multiple) bitfield units. fn bitfields_to_allocation_units(ctx: &BindgenContext, bitfield_unit_count: &mut usize, - mut fields: &mut E, + fields: &mut E, raw_bitfields: I) where E: Extend, I: IntoIterator @@ -478,7 +478,7 @@ fn bitfields_to_allocation_units(ctx: &BindgenContext, // TODO(emilio): Take into account C++'s wide bitfields, and // packing, sigh. - fn flush_allocation_unit(mut fields: &mut E, + fn flush_allocation_unit(fields: &mut E, bitfield_unit_count: &mut usize, unit_size_in_bits: usize, unit_align_in_bits: usize, diff --git a/src/ir/context.rs b/src/ir/context.rs index a0663d8cef..a2493aee3e 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -466,8 +466,8 @@ impl<'ctx> BindgenContext<'ctx> { assert!(item.id() != self.root_module); assert!(!self.items.contains_key(&item.id())); - if let Some(mut parent) = self.items.get_mut(&item.parent_id()) { - if let Some(mut module) = parent.as_module_mut() { + if let Some(parent) = self.items.get_mut(&item.parent_id()) { + if let Some(module) = parent.as_module_mut() { debug!("add_item_to_module: adding {:?} as child of parent module {:?}", item.id(), item.parent_id()); @@ -607,7 +607,7 @@ impl<'ctx> BindgenContext<'ctx> { to opaque blob"); Item::new_opaque_type(self.next_item_id(), &ty, self) }); - let mut item = self.items.get_mut(&id).unwrap(); + let item = self.items.get_mut(&id).unwrap(); *item.kind_mut().as_type_mut().unwrap().kind_mut() = TypeKind::ResolvedTypeRef(resolved); @@ -699,7 +699,7 @@ impl<'ctx> BindgenContext<'ctx> { debug!("Replacing {:?} with {:?}", id, replacement); let new_parent = { - let mut item = self.items.get_mut(&id).unwrap(); + let item = self.items.get_mut(&id).unwrap(); *item.kind_mut().as_type_mut().unwrap().kind_mut() = TypeKind::ResolvedTypeRef(replacement); item.parent_id() diff --git a/src/ir/template.rs b/src/ir/template.rs index 30189908ce..f18283278c 100644 --- a/src/ir/template.rs +++ b/src/ir/template.rs @@ -322,7 +322,7 @@ impl IsOpaque for TemplateInstantiation { arg_path[1..].join("::") }).collect(); { - let mut last = path.last_mut().unwrap(); + let last = path.last_mut().unwrap(); last.push('<'); last.push_str(&args.join(", ")); last.push('>'); diff --git a/src/lib.rs b/src/lib.rs index e72800c0fc..12ab8ac2c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ extern crate peeking_take_while; extern crate regex; #[macro_use] extern crate lazy_static; +extern crate which; #[cfg(feature = "logging")] #[macro_use] @@ -455,6 +456,19 @@ impl Builder { ); } + if !self.options.rustfmt_bindings { + output_vector.push("--rustfmt-bindings".into()); + } + + if let Some(path) = self.options + .rustfmt_configuration_file + .as_ref() + .and_then(|f| f.to_str()) + { + output_vector.push("--rustfmt-configuration-file".into()); + output_vector.push(path.into()); + } + output_vector } @@ -840,6 +854,20 @@ impl Builder { self } + /// Set whether rustfmt should format the generated bindings. + pub fn rustfmt_bindings(mut self, doit: bool) -> Self { + self.options.rustfmt_bindings = doit; + self + } + + /// Set the absolute path to the rustfmt configuration file, if None, the standard rustfmt + /// options are used. + pub fn rustfmt_configuration_file(mut self, path: Option) -> Self { + self = self.rustfmt_bindings(true); + self.options.rustfmt_configuration_file = path; + self + } + /// Generate the Rust bindings using the options built up thus far. pub fn generate<'ctx>(mut self) -> Result, ()> { self.options.input_header = self.input_headers.pop(); @@ -1099,6 +1127,13 @@ pub struct BindgenOptions { /// Features to enable, derived from `rust_target` rust_features: RustFeatures, + + /// Whether rustfmt should format the generated bindings. + pub rustfmt_bindings: bool, + + /// The absolute path to the rustfmt configuration file, if None, the standard rustfmt + /// options are used. + pub rustfmt_configuration_file: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1183,6 +1218,8 @@ impl Default for BindgenOptions { objc_extern_crate: false, enable_mangling: true, prepend_enum_name: true, + rustfmt_bindings: false, + rustfmt_configuration_file: None, } } } @@ -1334,14 +1371,18 @@ impl<'ctx> Bindings<'ctx> { /// Write these bindings as source text to a file. pub fn write_to_file>(&self, path: P) -> io::Result<()> { - let file = try!( - OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(path) - ); - self.write(Box::new(file)) + { + let file = try!( + OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path.as_ref()) + ); + self.write(Box::new(file))?; + } + + self.rustfmt_generated_file(path.as_ref()) } /// Write these bindings as source text to the given `Write`able. @@ -1364,6 +1405,63 @@ impl<'ctx> Bindings<'ctx> { try!(eof(&mut ps.s)); ps.s.out.flush() } + + /// Checks if rustfmt_bindings is set and runs rustfmt on the file + fn rustfmt_generated_file(&self, file: &Path) -> io::Result<()> { + if !self.context.options().rustfmt_bindings { + return Ok(()); + } + + let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") { + rustfmt + } else { + return Err(io::Error::new( + io::ErrorKind::Other, + "Rustfmt activated, but it could not be found in global path.", + )); + }; + + let mut cmd = Command::new(rustfmt); + + if let Some(path) = self.context + .options() + .rustfmt_configuration_file + .as_ref() + .and_then(|f| f.to_str()) + { + cmd.args(&["--config-path", path]); + } + + if let Ok(output) = cmd.arg(file).output() { + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + match output.status.code() { + Some(2) => Err(io::Error::new( + io::ErrorKind::Other, + format!("Rustfmt parsing errors:\n{}", stderr), + )), + Some(3) => { + warn!( + "Rustfmt could not format some lines:\n{}", + stderr + ); + Ok(()) + } + _ => Err(io::Error::new( + io::ErrorKind::Other, + format!("Internal rustfmt error:\n{}", stderr), + )), + } + } else { + Ok(()) + } + } else { + Err(io::Error::new( + io::ErrorKind::Other, + "Error executing rustfmt!", + )) + } + } } /// Determines whether the given cursor is in any of the files matched by the diff --git a/src/options.rs b/src/options.rs index 28faea993e..7b5169b7f9 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,6 +4,7 @@ use clap::{App, Arg}; use std::fs::File; use std::io::{self, Error, ErrorKind, Write, stderr}; use std::str::FromStr; +use std::path::PathBuf; /// Construct a new [`Builder`](./struct.Builder.html) from command line flags. pub fn builder_from_flags( @@ -231,7 +232,20 @@ where .help("Preprocess and dump the input header files to disk. \ Useful when debugging bindgen, using C-Reduce, or when \ filing issues. The resulting file will be named \ - something like `__bindgen.i` or `__bindgen.ii`.") + something like `__bindgen.i` or `__bindgen.ii`."), + Arg::with_name("rustfmt-bindings") + .long("rustfmt-bindings") + .help("Format the generated bindings with rustfmt. \ + Rustfmt needs to be in the global PATH."), + Arg::with_name("rustfmt-configuration-file") + .long("rustfmt-configuration-file") + .help("The absolute path to the rustfmt configuration file. \ + The configuration file will be used for formatting the bindings. \ + Setting this parameter, will automatically set --rustfmt-bindings.") + .value_name("path") + .takes_value(true) + .multiple(false) + .number_of_values(1), ]) // .args() .get_matches_from(args); @@ -458,6 +472,27 @@ where builder.dump_preprocessed_input()?; } + if matches.is_present("rustfmt-bindings") { + builder = builder.rustfmt_bindings(true); + } + + if let Some(path_str) = matches.value_of("rustfmt-configuration-file") { + let path = PathBuf::from(path_str); + + if !path.is_absolute() { + return Err(Error::new(ErrorKind::Other, + "--rustfmt-configuration--file needs to be an absolute path!")); + } + + if path.to_str().is_none() { + return Err( + Error::new(ErrorKind::Other, + "--rustfmt-configuration-file contains non-valid UTF8 characters.")); + } + + builder = builder.rustfmt_configuration_file(Some(path)); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose))