From cda463275ed9642e231c92614a986af4a815b351 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 14:34:05 -0700 Subject: [PATCH 1/8] Use the log/env_logger crates from crates.io --- Cargo.lock | 11 +++++++++++ Cargo.toml | 2 ++ src/bin/rustfmt.rs | 3 +++ 3 files changed, 16 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c00a673ebf6..61a586da4f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,8 @@ name = "rustfmt" version = "0.0.1" dependencies = [ "diff 0.1.7 (git+https://github.com/utkarshkukreti/diff.rs.git)", + "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "strings 0.0.1 (git+https://github.com/nrc/strings.rs.git)", @@ -24,6 +26,15 @@ name = "diff" version = "0.1.7" source = "git+https://github.com/utkarshkukreti/diff.rs.git#6edb9454bf4127087aced0fe07ab3ea6894083cb" +[[package]] +name = "env_logger" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index e8b81eb8482..fc8f0aa6026 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,7 @@ regex = "0.1.41" term = "0.2.11" strings = { version = "0.0.1", git = "https://github.com/nrc/strings.rs.git" } diff = { git = "https://github.com/utkarshkukreti/diff.rs.git" } +log = "0.3.2" +env_logger = "0.3.1" [dev-dependencies] diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 01c292948df..9d8596e5e13 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -15,6 +15,7 @@ extern crate log; extern crate rustfmt; extern crate toml; +extern crate env_logger; use rustfmt::{WriteMode, run}; use rustfmt::config::Config; @@ -71,6 +72,8 @@ fn execute() -> i32 { fn main() { use std::io::Write; + let _ = env_logger::init(); + let exit_code = execute(); // Make sure standard output is flushed before we exit std::io::stdout().flush().unwrap(); From 36abfe5dc25cd0c34579f2f20a2201fd87496e4d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 14:35:42 -0700 Subject: [PATCH 2/8] Remove usage of many unstable features This removes usage of: * PathExt * split_last * split_last_mut * catch_panic The catch_panic one was a little tricky as the ident interner needed to be cloned across threads (a little unsafely), but it should otherwise be good to go. --- src/bin/rustfmt.rs | 7 +++---- src/chains.rs | 12 ++++++------ src/comment.rs | 2 +- src/lib.rs | 7 ------- src/macros.rs | 30 +++++++++++++++++++++++++++--- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 9d8596e5e13..40f55d61154 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -7,8 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(path_ext)] -#![feature(rustc_private)] + #![cfg(not(test))] #[macro_use] @@ -21,7 +20,7 @@ use rustfmt::{WriteMode, run}; use rustfmt::config::Config; use std::env; -use std::fs::{File, PathExt}; +use std::fs::{self, File}; use std::io::{self, Read}; use std::path::PathBuf; use std::str::FromStr; @@ -31,7 +30,7 @@ fn lookup_project_file() -> io::Result { let mut current = try!(env::current_dir()); loop { let config_file = current.join("rustfmt.toml"); - if config_file.exists() { + if fs::metadata(&config_file).is_ok() { return Ok(config_file); } else { current = match current.parent() { diff --git a/src/chains.rs b/src/chains.rs index 4b6824a9073..4716f545b3f 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -72,11 +72,9 @@ pub fn rewrite_chain(mut expr: &ast::Expr, .collect::>>()); // Total of all items excluding the last. - let almost_total = rewrites.split_last() - .unwrap() - .1 - .iter() - .fold(0, |a, b| a + first_line_width(b)) + + let almost_total = rewrites[..rewrites.len() - 1] + .iter() + .fold(0, |a, b| a + first_line_width(b)) + parent_rewrite.len(); let total_width = almost_total + first_line_width(rewrites.last().unwrap()); let veto_single_line = if context.config.take_source_hints && subexpr_list.len() > 1 { @@ -95,7 +93,9 @@ pub fn rewrite_chain(mut expr: &ast::Expr, match subexpr_list[0].node { ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) if context.config.chains_overflow_last => { - let (last, init) = rewrites.split_last_mut().unwrap(); + let len = rewrites.len(); + let (init, last) = rewrites.split_at_mut(len - 1); + let last = &mut last[0]; if init.iter().all(|s| !s.contains('\n')) && total_width <= width { let last_rewrite = width.checked_sub(almost_total) diff --git a/src/comment.rs b/src/comment.rs index 045d1cb5047..091b4293ad3 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -437,7 +437,7 @@ mod test { } #[test] - #[rustfmt_skip] + #[cfg_attr(rustfmt, rustfmt_skip)] fn format_comments() { let config = Default::default(); assert_eq!("/* test */", rewrite_comment(" //test", true, 100, Indent::new(0, 100), diff --git a/src/lib.rs b/src/lib.rs index 03422a20c36..0e3dff21b69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,13 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_private)] -#![feature(custom_attribute)] -#![feature(slice_splits)] -#![feature(slice_patterns)] -#![feature(catch_panic)] -#![allow(unused_attributes)] - // TODO we're going to allocate a whole bunch of temp Strings, is it worth // keeping some scratch mem for this and running our own StrPool? // TODO for lint violations of names, emit a refactor script diff --git a/src/macros.rs b/src/macros.rs index 9035b0c7d8b..37ae0af3a0b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -20,11 +20,14 @@ // and those with brackets will be formatted as array literals. use std::thread; +use std::collections::hash_map::{HashMap, Entry}; use syntax::ast; use syntax::parse::token::{Eof, Comma, Token}; use syntax::parse::{ParseSess, tts_to_parser}; use syntax::codemap::{mk_sp, BytePos}; +use syntax::parse::token; +use syntax::util::interner::StrInterner; use Indent; use rewrite::RewriteContext; @@ -82,13 +85,16 @@ pub fn rewrite_macro(mac: &ast::Mac, } let wrapped_tt_vec = ForceSend(mac.node.tts.clone()); + let my_interner = ForceSend(clone_interner()); + // Wrap expression parsing logic in a thread since the libsyntax parser // panics on failure, which we do not want to propagate. // The expression vector is wrapped in an Option inside a Result. - let expr_vec_result = thread::catch_panic(move || { + let expr_vec_result = thread::spawn(move || { let parse_session = ParseSess::new(); let mut parser = tts_to_parser(&parse_session, wrapped_tt_vec.0, vec![]); let mut expr_vec = vec![]; + token::get_ident_interner().reset(my_interner.0); loop { expr_vec.push(parser.parse_expr()); @@ -106,9 +112,10 @@ pub fn rewrite_macro(mac: &ast::Mac, } } - Some(expr_vec) + Some(ForceSend((expr_vec, clone_interner()))) }); - let expr_vec = try_opt!(try_opt!(expr_vec_result.ok())); + let (expr_vec, interner) = try_opt!(try_opt!(expr_vec_result.join().ok())).0; + token::get_ident_interner().reset(interner); match style { MacroStyle::Parens => { @@ -139,6 +146,23 @@ pub fn rewrite_macro(mac: &ast::Mac, } } +fn clone_interner() -> StrInterner { + let old = token::get_ident_interner(); + let new = StrInterner::new(); + let mut map = HashMap::new(); + for name in (0..old.len()).map(|i| i as u32).map(ast::Name) { + match map.entry(old.get(name)) { + Entry::Occupied(e) => { + new.gensym_copy(*e.get()); + } + Entry::Vacant(e) => { + e.insert(new.intern(&old.get(name))); + } + } + } + return new +} + fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle { let snippet = context.snippet(mac.span); let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); From 579fb34417844cf6242c62ba0b6f2f9c72772ba0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 14:37:13 -0700 Subject: [PATCH 3/8] Remove dependence on rustc/rustc_driver, use syntex Instead just parse manually with the `syntex_syntax` crate which is a clone of libsyntax on crates.io which builds on stable Rust. --- Cargo.lock | 24 +++++++++++++++ Cargo.toml | 1 + src/lib.rs | 77 +++++++++---------------------------------------- src/macros.rs | 74 ++++++++++++----------------------------------- tests/system.rs | 3 +- 5 files changed, 57 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61a586da4f3..d3f20f8b759 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ dependencies = [ "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "strings 0.0.1 (git+https://github.com/nrc/strings.rs.git)", + "syntex_syntax 0.18.0 (git+https://github.com/serde-rs/syntex)", "term 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -21,6 +22,11 @@ dependencies = [ "memchr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bitflags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "diff" version = "0.1.7" @@ -93,6 +99,19 @@ dependencies = [ "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syntex_syntax" +version = "0.18.0" +source = "git+https://github.com/serde-rs/syntex#176ca5d8add606fac8d503b10c89ddb82f02d92b" +dependencies = [ + "bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "term" version = "0.2.12" @@ -115,6 +134,11 @@ name = "unicode-segmentation" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.4" diff --git a/Cargo.toml b/Cargo.toml index fc8f0aa6026..60df8ac6904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ regex = "0.1.41" term = "0.2.11" strings = { version = "0.0.1", git = "https://github.com/nrc/strings.rs.git" } diff = { git = "https://github.com/utkarshkukreti/diff.rs.git" } +syntex_syntax = { git = "https://github.com/serde-rs/syntex" } log = "0.3.2" env_logger = "0.3.1" diff --git a/src/lib.rs b/src/lib.rs index 0e3dff21b69..51497e3b1c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,10 +15,7 @@ #[macro_use] extern crate log; -extern crate getopts; -extern crate rustc; -extern crate rustc_driver; -extern crate syntax; +extern crate syntex_syntax as syntax; extern crate rustc_serialize; extern crate strings; @@ -28,22 +25,15 @@ extern crate regex; extern crate diff; extern crate term; -use rustc::session::Session; -use rustc::session::config as rustc_config; -use rustc::session::config::Input; -use rustc_driver::{driver, CompilerCalls, Compilation}; - use syntax::ast; use syntax::codemap::{CodeMap, Span}; -use syntax::diagnostics; +use syntax::parse::{self, ParseSess}; use std::ops::{Add, Sub}; -use std::path::PathBuf; +use std::path::Path; use std::collections::HashMap; use std::fmt; use std::str::FromStr; -use std::rc::Rc; -use std::cell::RefCell; use issues::{BadIssueSeeker, Issue}; use filemap::FileMap; @@ -380,65 +370,24 @@ pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport { report } -struct RustFmtCalls { - config: Rc, - result: Rc>>, -} +pub fn format(file: &Path, config: &Config) -> FileMap { + let parse_session = ParseSess::new(); + let krate = parse::parse_crate_from_file(file, Vec::new(), &parse_session); + let mut file_map = fmt_ast(&krate, parse_session.codemap(), config); -impl<'a> CompilerCalls<'a> for RustFmtCalls { - fn no_input(&mut self, - _: &getopts::Matches, - _: &rustc_config::Options, - _: &Option, - _: &Option, - _: &diagnostics::registry::Registry) - -> Option<(Input, Option)> { - panic!("No input supplied to RustFmt"); - } + // For some reason, the codemap does not include terminating + // newlines so we must add one on for each file. This is sad. + filemap::append_newlines(&mut file_map); - fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { - let result = self.result.clone(); - let config = self.config.clone(); - - let mut control = driver::CompileController::basic(); - control.after_parse.stop = Compilation::Stop; - control.after_parse.callback = Box::new(move |state| { - let krate = state.krate.unwrap(); - let codemap = state.session.codemap(); - let mut file_map = fmt_ast(krate, codemap, &*config); - // For some reason, the codemap does not include terminating - // newlines so we must add one on for each file. This is sad. - filemap::append_newlines(&mut file_map); - - *result.borrow_mut() = Some(file_map); - }); - - control - } -} - -pub fn format(args: Vec, config: &Config) -> FileMap { - let result = Rc::new(RefCell::new(None)); - - { - let config = Rc::new(config.clone()); - let mut call_ctxt = RustFmtCalls { - config: config, - result: result.clone(), - }; - rustc_driver::run_compiler(&args, &mut call_ctxt); - } - - // Peel the union. - Rc::try_unwrap(result).ok().unwrap().into_inner().unwrap() + return file_map; } // args are the arguments passed on the command line, generally passed through // to the compiler. // write_mode determines what happens to the result of running rustfmt, see // WriteMode. -pub fn run(args: Vec, write_mode: WriteMode, config: &Config) { - let mut result = format(args, config); +pub fn run(file: &Path, write_mode: WriteMode, config: &Config) { + let mut result = format(file, config); println!("{}", fmt_lines(&mut result, config)); diff --git a/src/macros.rs b/src/macros.rs index 37ae0af3a0b..bede00d6e9e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,15 +19,10 @@ // List-like invocations with parentheses will be formatted as function calls, // and those with brackets will be formatted as array literals. -use std::thread; -use std::collections::hash_map::{HashMap, Entry}; - use syntax::ast; use syntax::parse::token::{Eof, Comma, Token}; use syntax::parse::{ParseSess, tts_to_parser}; use syntax::codemap::{mk_sp, BytePos}; -use syntax::parse::token; -use syntax::util::interner::StrInterner; use Indent; use rewrite::RewriteContext; @@ -37,12 +32,6 @@ use utils::{wrap_str, span_after}; static FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"]; -// We need to pass `TokenTree`s to our expression parsing thread, but they are -// not `Send`. We wrap them in a `Send` container to force our will. -// FIXME: this is a pretty terrible hack. Any other solution would be preferred. -struct ForceSend(pub T); -unsafe impl Send for ForceSend {} - // FIXME: use the enum from libsyntax? #[derive(Clone, Copy)] enum MacroStyle { @@ -84,38 +73,28 @@ pub fn rewrite_macro(mac: &ast::Mac, }; } - let wrapped_tt_vec = ForceSend(mac.node.tts.clone()); - let my_interner = ForceSend(clone_interner()); + let parse_session = ParseSess::new(); + let mut parser = tts_to_parser(&parse_session, mac.node.tts.clone(), Vec::new()); + let mut expr_vec = Vec::new(); - // Wrap expression parsing logic in a thread since the libsyntax parser - // panics on failure, which we do not want to propagate. - // The expression vector is wrapped in an Option inside a Result. - let expr_vec_result = thread::spawn(move || { - let parse_session = ParseSess::new(); - let mut parser = tts_to_parser(&parse_session, wrapped_tt_vec.0, vec![]); - let mut expr_vec = vec![]; - token::get_ident_interner().reset(my_interner.0); + loop { + expr_vec.push(match parser.parse_expr_nopanic() { + Ok(expr) => expr, + Err(..) => return None, + }); - loop { - expr_vec.push(parser.parse_expr()); - - match parser.token { - Token::Eof => break, - Token::Comma => (), - _ => panic!("Macro not list-like, skiping..."), - } - - let _ = parser.bump(); - - if parser.token == Token::Eof { - return None; - } + match parser.token { + Token::Eof => break, + Token::Comma => (), + _ => return None, } - Some(ForceSend((expr_vec, clone_interner()))) - }); - let (expr_vec, interner) = try_opt!(try_opt!(expr_vec_result.join().ok())).0; - token::get_ident_interner().reset(interner); + let _ = parser.bump(); + + if parser.token == Token::Eof { + return None; + } + } match style { MacroStyle::Parens => { @@ -146,23 +125,6 @@ pub fn rewrite_macro(mac: &ast::Mac, } } -fn clone_interner() -> StrInterner { - let old = token::get_ident_interner(); - let new = StrInterner::new(); - let mut map = HashMap::new(); - for name in (0..old.len()).map(|i| i as u32).map(ast::Name) { - match map.entry(old.get(name)) { - Entry::Occupied(e) => { - new.gensym_copy(*e.get()); - } - Entry::Vacant(e) => { - e.insert(new.intern(&old.get(name))); - } - } - } - return new -} - fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle { let snippet = context.snippet(mac.span); let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); diff --git a/tests/system.rs b/tests/system.rs index b6dee149ed1..649ccdf5647 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -135,7 +135,6 @@ fn print_mismatches(result: HashMap>) { pub fn idempotent_check(filename: String) -> Result>> { let sig_comments = read_significant_comments(&filename); let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..])); - let args = vec!["rustfmt".to_owned(), filename]; for (key, val) in &sig_comments { if key != "target" && key != "config" { @@ -146,7 +145,7 @@ pub fn idempotent_check(filename: String) -> Result Date: Thu, 22 Oct 2015 14:38:16 -0700 Subject: [PATCH 4/8] Move option parsing to crates.io-based getopts crate Should help adding more options in the future as well! --- Cargo.lock | 6 ++++ Cargo.toml | 1 + src/bin/rustfmt.rs | 74 ++++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3f20f8b759..66061bd1e25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.0.1" dependencies = [ "diff 0.1.7 (git+https://github.com/utkarshkukreti/diff.rs.git)", "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -41,6 +42,11 @@ dependencies = [ "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "getopts" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kernel32-sys" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 60df8ac6904..ebd4e8e7f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,6 @@ diff = { git = "https://github.com/utkarshkukreti/diff.rs.git" } syntex_syntax = { git = "https://github.com/serde-rs/syntex" } log = "0.3.2" env_logger = "0.3.1" +getopts = "0.2" [dev-dependencies] diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 40f55d61154..6b3405aa936 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -15,6 +15,7 @@ extern crate log; extern crate rustfmt; extern crate toml; extern crate env_logger; +extern crate getopts; use rustfmt::{WriteMode, run}; use rustfmt::config::Config; @@ -23,7 +24,8 @@ use std::env; use std::fs::{self, File}; use std::io::{self, Read}; use std::path::PathBuf; -use std::str::FromStr; + +use getopts::Options; // Try to find a project file in the current directory and its parents. fn lookup_project_file() -> io::Result { @@ -52,7 +54,7 @@ fn lookup_and_read_project_file() -> io::Result<(PathBuf, String)> { } fn execute() -> i32 { - let (args, write_mode) = match determine_params(std::env::args()) { + let (file, write_mode) = match determine_params(std::env::args().skip(1)) { Some(params) => params, None => return 1, }; @@ -65,7 +67,7 @@ fn execute() -> i32 { Err(_) => Default::default(), }; - run(args, write_mode, &config); + run(&file, write_mode, &config); 0 } @@ -83,50 +85,52 @@ fn main() { std::process::exit(exit_code); } -fn print_usage>(reason: S) { - println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display|diff]] \ - ", - reason.into()); +fn print_usage(opts: &Options, reason: &str) { + let reason = format!("{}\nusage: {} [options] ", + reason, + env::current_exe().unwrap().display()); + println!("{}", opts.usage(&reason)); Config::print_docs(); } -fn determine_params(args: I) -> Option<(Vec, WriteMode)> +fn determine_params(args: I) -> Option<(PathBuf, WriteMode)> where I: Iterator { - let arg_prefix = "-"; - let write_mode_prefix = "--write-mode="; - let help_mode = "-h"; - let long_help_mode = "--help"; - let mut write_mode = WriteMode::Replace; - let mut rustc_args = Vec::new(); + let mut opts = Options::new(); + opts.optflag("h", "help", "show this message"); + opts.optopt("", + "write-mode", + "mode to write in", + "[replace|overwrite|display|diff]"); + let matches = match opts.parse(args) { + Ok(m) => m, + Err(e) => { + print_usage(&opts, &e.to_string()); + return None; + } + }; - // The NewFile option currently isn't supported because it requires another - // parameter, but it can be added later. - for arg in args { - if arg.starts_with(write_mode_prefix) { - match FromStr::from_str(&arg[write_mode_prefix.len()..]) { - Ok(mode) => write_mode = mode, - Err(_) => { - print_usage("Unrecognized write mode"); + if matches.opt_present("h") { + print_usage(&opts, ""); + } + + let write_mode = match matches.opt_str("write-mode") { + Some(mode) => { + match mode.parse() { + Ok(mode) => mode, + Err(..) => { + print_usage(&opts, "Unrecognized write mode"); return None; } } - } else if arg.starts_with(help_mode) || arg.starts_with(long_help_mode) { - print_usage(""); - return None; - } else if arg.starts_with(arg_prefix) { - print_usage("Invalid argument"); - return None; - } else { - // Pass everything else to rustc - rustc_args.push(arg); } - } + None => WriteMode::Replace, + }; - if rustc_args.len() < 2 { - print_usage("Please provide a file to be formatted"); + if matches.free.len() != 1 { + print_usage(&opts, "Please provide one file to format"); return None; } - Some((rustc_args, write_mode)) + Some((PathBuf::from(&matches.free[0]), write_mode)) } From e6b7ad3e39f36db3168fd750f7e7b50df40997a8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 15:02:53 -0700 Subject: [PATCH 5/8] Ignore #[cfg_attr(rustfmt, rustfmt_skip)] functions This adds to #[rustfmt_skip] but is usable on stable Rust! --- src/utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 0fd749a5471..fea385532d0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -75,6 +75,9 @@ pub fn last_line_width(s: &str) -> usize { fn is_skip(meta_item: &MetaItem) -> bool { match meta_item.node { MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION, + MetaItem_::MetaList(ref s, ref l) => { + *s == "cfg_attr" && l.len() == 2 && is_skip(&l[1]) + } _ => false, } } From 9006a8b87dc1df358043c224cf141403b3b8c4f1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 15:09:12 -0700 Subject: [PATCH 6/8] Test on stable/beta branches on Travis as well --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9683ca582eb..8025d275cc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: rust rust: + - stable + - beta - nightly sudo: false From 4121b503e95a33bc3c235a707c3575fd1574ca97 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Oct 2015 16:20:45 -0700 Subject: [PATCH 7/8] Add test for a macro not containing an exprssion --- tests/source/macro_not_expr.rs | 7 +++++++ tests/target/macro_not_expr.rs | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/source/macro_not_expr.rs create mode 100644 tests/target/macro_not_expr.rs diff --git a/tests/source/macro_not_expr.rs b/tests/source/macro_not_expr.rs new file mode 100644 index 00000000000..d8de4dce38f --- /dev/null +++ b/tests/source/macro_not_expr.rs @@ -0,0 +1,7 @@ +macro_rules! test { + ($($t:tt)*) => {} +} + +fn main() { + test!( a : B => c d ); +} diff --git a/tests/target/macro_not_expr.rs b/tests/target/macro_not_expr.rs new file mode 100644 index 00000000000..d8de4dce38f --- /dev/null +++ b/tests/target/macro_not_expr.rs @@ -0,0 +1,7 @@ +macro_rules! test { + ($($t:tt)*) => {} +} + +fn main() { + test!( a : B => c d ); +} From 7fdfff6e58cd30548b1d02946364b746243cdc02 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 23 Oct 2015 13:51:29 -0700 Subject: [PATCH 8/8] Update README for stable build --- README.md | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8c76e8ec51c..06e8dae54f3 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,34 @@ A tool for formatting Rust code according to style guidelines. ## Gotchas -* For things you do not want rustfmt to mangle, use -```rust - #[rustfmt_skip] - ``` -* When you run rustfmt use a file called rustfmt.toml to override the default settings of rustfmt. -* We create a functioning executable called rustfmt in the target directory + +* For things you do not want rustfmt to mangle, use one of + ```rust + #[rustfmt_skip] + #[cfg_attr(rustfmt, rustfmt_skip)] + ``` +* When you run rustfmt use a file called rustfmt.toml to override the default + settings of rustfmt. +* We create a functioning executable called `rustfmt` in the target directory + +## Installation + +> **Note:** this method currently requires you to be running a nightly install +> of Rust as `cargo install` has not yet made its way onto the stable channel. + +``` +cargo install --git https://github.com/nrc/rustfmt +``` + +or if you're using `multirust` + +``` +multirust run nightly cargo install --git https://github.com/nrc/rustfmt +``` ## How to build and test -You'll need a pretty up to date version of the **nightly** version of Rust. + +First make sure you've got Rust **1.3.0** or greater available, then: `cargo build` to build.