diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index da8d7e7bf03..60c1312b6c6 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -108,7 +108,7 @@ fn determine_params(args: I) -> Option<(PathBuf, WriteMode)> opts.optopt("", "write-mode", "mode to write in", - "[replace|overwrite|display|diff]"); + "[replace|overwrite|display|diff|coverage]"); let matches = match opts.parse(args) { Ok(m) => m, Err(e) => { diff --git a/src/expr.rs b/src/expr.rs index 00d8ea0e0e2..20a637bffca 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -422,7 +422,7 @@ impl Rewrite for ast::Block { return Some(user_str); } - let mut visitor = FmtVisitor::from_codemap(context.codemap, context.config); + let mut visitor = FmtVisitor::from_codemap(context.codemap, context.config, None); visitor.block_indent = context.block_indent; let prefix = match self.rules { @@ -833,7 +833,7 @@ impl Rewrite for ast::Arm { let attr_str = if !attrs.is_empty() { // We only use this visitor for the attributes, should we use it for // more? - let mut attr_visitor = FmtVisitor::from_codemap(context.codemap, context.config); + let mut attr_visitor = FmtVisitor::from_codemap(context.codemap, context.config, None); attr_visitor.block_indent = context.block_indent; attr_visitor.last_pos = attrs[0].span.lo; if attr_visitor.visit_attrs(attrs) { diff --git a/src/filemap.rs b/src/filemap.rs index d94c1501734..72174679386 100644 --- a/src/filemap.rs +++ b/src/filemap.rs @@ -100,7 +100,7 @@ fn write_file(text: &StringBuffer, let file = try!(File::create(&filename)); try!(write_system_newlines(file, text, config)); } - WriteMode::Display => { + WriteMode::Display | WriteMode::Coverage => { println!("{}:\n", filename); let stdout = stdout(); let stdout_lock = stdout.lock(); diff --git a/src/lib.rs b/src/lib.rs index 51497e3b1c3..33753524348 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,8 @@ pub enum WriteMode { Diff, // Return the result as a mapping from filenames to Strings. Return, + // Display how much of the input file was processed + Coverage, } impl FromStr for WriteMode { @@ -193,6 +195,7 @@ impl FromStr for WriteMode { "display" => Ok(WriteMode::Display), "overwrite" => Ok(WriteMode::Overwrite), "diff" => Ok(WriteMode::Diff), + "coverage" => Ok(WriteMode::Coverage), _ => Err(()), } } @@ -277,11 +280,11 @@ impl fmt::Display for FormatReport { } // Formatting which depends on the AST. -fn fmt_ast(krate: &ast::Crate, codemap: &CodeMap, config: &Config) -> FileMap { +fn fmt_ast(krate: &ast::Crate, codemap: &CodeMap, config: &Config, mode: WriteMode) -> FileMap { let mut file_map = FileMap::new(); for (path, module) in modules::list_files(krate, codemap) { let path = path.to_str().unwrap(); - let mut visitor = FmtVisitor::from_codemap(codemap, config); + let mut visitor = FmtVisitor::from_codemap(codemap, config, Some(mode)); visitor.format_separate_mod(module, path); file_map.insert(path.to_owned(), visitor.buffer); } @@ -370,10 +373,10 @@ pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport { report } -pub fn format(file: &Path, config: &Config) -> FileMap { +pub fn format(file: &Path, config: &Config, mode: WriteMode) -> 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); + let mut file_map = fmt_ast(&krate, parse_session.codemap(), config, mode); // For some reason, the codemap does not include terminating // newlines so we must add one on for each file. This is sad. @@ -387,7 +390,7 @@ pub fn format(file: &Path, config: &Config) -> FileMap { // write_mode determines what happens to the result of running rustfmt, see // WriteMode. pub fn run(file: &Path, write_mode: WriteMode, config: &Config) { - let mut result = format(file, config); + let mut result = format(file, config, write_mode); println!("{}", fmt_lines(&mut result, config)); diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 04d3ac3366a..b83f07e05d7 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use WriteMode; use visitor::FmtVisitor; - use syntax::codemap::{self, BytePos, Span, Pos}; use comment::{CodeCharKind, CommentCodeSlices, rewrite_comment}; @@ -80,7 +80,7 @@ impl<'a> FmtVisitor<'a> { fn write_snippet_inner(&mut self, big_snippet: &str, big_diff: usize, - snippet: &str, + old_snippet: &str, process_last_snippet: F) where F: Fn(&mut FmtVisitor, &str, &str) { @@ -91,6 +91,26 @@ impl<'a> FmtVisitor<'a> { let mut last_wspace = None; let mut rewrite_next_comment = true; + fn replace_chars(string: &str) -> String { + string.chars() + .map(|ch| { + match ch.is_whitespace() { + true => ch, + false => 'X', + } + }) + .collect() + } + + let replaced = match self.write_mode { + Some(mode) => match mode { + WriteMode::Coverage => replace_chars(old_snippet), + _ => old_snippet.to_owned(), + }, + None => old_snippet.to_owned(), + }; + let snippet = &*replaced; + for (kind, offset, subslice) in CommentCodeSlices::new(snippet) { if let CodeCharKind::Comment = kind { let last_char = big_snippet[..(offset + big_diff)] diff --git a/src/visitor.rs b/src/visitor.rs index c58d16809aa..9a33fdae024 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -14,7 +14,7 @@ use syntax::visit; use strings::string_buffer::StringBuffer; -use Indent; +use {Indent, WriteMode}; use utils; use config::Config; use rewrite::{Rewrite, RewriteContext}; @@ -29,6 +29,7 @@ pub struct FmtVisitor<'a> { // TODO: RAII util for indenting pub block_indent: Indent, pub config: &'a Config, + pub write_mode: Option, } impl<'a> FmtVisitor<'a> { @@ -356,7 +357,10 @@ impl<'a> FmtVisitor<'a> { } } - pub fn from_codemap(codemap: &'a CodeMap, config: &'a Config) -> FmtVisitor<'a> { + pub fn from_codemap(codemap: &'a CodeMap, + config: &'a Config, + mode: Option) + -> FmtVisitor<'a> { FmtVisitor { codemap: codemap, buffer: StringBuffer::new(), @@ -366,6 +370,7 @@ impl<'a> FmtVisitor<'a> { alignment: 0, }, config: config, + write_mode: mode, } } diff --git a/tests/coverage-source/comments.rs b/tests/coverage-source/comments.rs new file mode 100644 index 00000000000..379e8e5820e --- /dev/null +++ b/tests/coverage-source/comments.rs @@ -0,0 +1,6 @@ +/// Here's a doc comment! +fn main() { + // foo is bar + let foo = "bar"; + // loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment!!!!! +} diff --git a/tests/coverage-target/comments.rs b/tests/coverage-target/comments.rs new file mode 100644 index 00000000000..74d17bffd15 --- /dev/null +++ b/tests/coverage-target/comments.rs @@ -0,0 +1,6 @@ +/// Here's a doc comment! +fn main() { + XX XXX XX XXX + let foo = "bar"; + XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXX +} diff --git a/tests/system.rs b/tests/system.rs index 649ccdf5647..4a924c57833 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -43,13 +43,25 @@ fn system_tests() { // Turn a DirEntry into a String that represents the relative path to the // file. let files = files.map(get_path_string); - let (_reports, count, fails) = check_files(files); + let (_reports, count, fails) = check_files(files, WriteMode::Return); // Display results. println!("Ran {} system tests.", count); assert!(fails == 0, "{} system tests failed", fails); } +// Do the same for tests/coverage-source directory +// the only difference is the coverage mode +#[test] +fn coverage_tests() { + let files = fs::read_dir("tests/coverage-source").ok().expect("Couldn't read source dir."); + let files = files.map(get_path_string); + let (_reports, count, fails) = check_files(files, WriteMode::Coverage); + + println!("Ran {} tests in coverage mode.", count); + assert!(fails == 0, "{} tests failed", fails); +} + // Idempotence tests. Files in tests/target are checked to be unaltered by // rustfmt. #[test] @@ -59,7 +71,7 @@ fn idempotence_tests() { .ok() .expect("Couldn't read target dir.") .map(get_path_string); - let (_reports, count, fails) = check_files(files); + let (_reports, count, fails) = check_files(files, WriteMode::Return); // Display results. println!("Ran {} idempotent tests.", count); @@ -78,7 +90,7 @@ fn self_tests() { // Hack because there's no `IntoIterator` impl for `[T; N]`. let files = files.chain(Some("src/lib.rs".to_owned()).into_iter()); - let (reports, count, fails) = check_files(files); + let (reports, count, fails) = check_files(files, WriteMode::Return); let mut warnings = 0; // Display results. @@ -97,7 +109,7 @@ fn self_tests() { // For each file, run rustfmt and collect the output. // Returns the number of files checked and the number of failures. -fn check_files(files: I) -> (Vec, u32, u32) +fn check_files(files: I, write_mode: WriteMode) -> (Vec, u32, u32) where I: Iterator { let mut count = 0; @@ -107,7 +119,7 @@ fn check_files(files: I) -> (Vec, u32, u32) for file_name in files.filter(|f| f.ends_with(".rs")) { println!("Testing '{}'...", file_name); - match idempotent_check(file_name) { + match idempotent_check(file_name, write_mode) { Ok(report) => reports.push(report), Err(msg) => { print_mismatches(msg); @@ -132,7 +144,9 @@ fn print_mismatches(result: HashMap>) { assert!(t.reset().unwrap()); } -pub fn idempotent_check(filename: String) -> Result>> { +pub fn idempotent_check(filename: String, + write_mode: WriteMode) + -> Result>> { let sig_comments = read_significant_comments(&filename); let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..])); @@ -145,14 +159,14 @@ pub fn idempotent_check(filename: String) -> Result HashMap { // Compare output to input. // TODO: needs a better name, more explanation. fn handle_result(result: HashMap, - target: Option<&str>) + target: Option<&str>, + write_mode: WriteMode) -> Result<(), HashMap>> { let mut failures = HashMap::new(); for (file_name, fmt_text) in result { // If file is in tests/source, compare to file with same name in tests/target. - let target = get_target(&file_name, target); + let target = get_target(&file_name, target, write_mode); let mut f = fs::File::open(&target).ok().expect("Couldn't open target."); let mut text = String::new(); @@ -231,9 +246,14 @@ fn handle_result(result: HashMap, } // Map source file paths to their target paths. -fn get_target(file_name: &str, target: Option<&str>) -> String { +fn get_target(file_name: &str, target: Option<&str>, write_mode: WriteMode) -> String { let file_path = Path::new(file_name); - let source_path_prefix = Path::new("tests/source/"); + let (source_path_prefix, target_path_prefix) = match write_mode { + WriteMode::Coverage => (Path::new("tests/coverage-source/"), + "tests/coverage-target/"), + _ => (Path::new("tests/source/"), "tests/target/"), + }; + if file_path.starts_with(source_path_prefix) { let mut components = file_path.components(); // Can't skip(2) as the resulting iterator can't as_path() @@ -246,7 +266,7 @@ fn get_target(file_name: &str, target: Option<&str>) -> String { }; let base = target.unwrap_or(new_target); - format!("tests/target/{}", base) + format!("{}{}", target_path_prefix, base) } else { file_name.to_owned() }