From e6e7ce178f39b837e1b2b8dff33d587780a2a37e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 1 May 2015 18:40:05 +0200 Subject: main.rs -> lib.rs --- src/lib.rs | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 345 ------------------------------------------------------------ 2 files changed, 345 insertions(+), 345 deletions(-) create mode 100644 src/lib.rs delete mode 100644 src/main.rs (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000000..e16537198f6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,345 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] +#![feature(box_patterns)] +#![feature(rustc_private)] +#![feature(collections)] +#![feature(str_char)] + +#![cfg_attr(not(test), feature(exit_status))] + +// 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 + + +#[macro_use] +extern crate log; + +extern crate getopts; +extern crate rustc; +extern crate rustc_driver; +extern crate syntax; + +extern crate strings; + +use rustc::session::Session; +use rustc::session::config::{self, Input}; +use rustc_driver::{driver, CompilerCalls, Compilation}; + +use syntax::ast; +use syntax::codemap::CodeMap; +use syntax::diagnostics; +use syntax::visit; + +use std::path::PathBuf; +use std::collections::HashMap; + +use changes::ChangeSet; +use visitor::FmtVisitor; + +mod changes; +mod visitor; +mod functions; +mod missed_spans; +mod lists; +mod utils; +mod types; +mod expr; +mod imports; + +const IDEAL_WIDTH: usize = 80; +const LEEWAY: usize = 5; +const MAX_WIDTH: usize = 100; +const MIN_STRING: usize = 10; +const TAB_SPACES: usize = 4; +const FN_BRACE_STYLE: BraceStyle = BraceStyle::SameLineWhere; +const FN_RETURN_INDENT: ReturnIndent = ReturnIndent::WithArgs; +// When we get scoped annotations, we should have rustfmt::skip. +const SKIP_ANNOTATION: &'static str = "rustfmt_skip"; + +#[derive(Copy, Clone)] +pub enum WriteMode { + Overwrite, + // str is the extension of the new file + NewFile(&'static str), + // Write the output to stdout. + Display, + // Return the result as a mapping from filenames to StringBuffers. + Return(&'static Fn(HashMap)), +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum BraceStyle { + AlwaysNextLine, + PreferSameLine, + // Prefer same line except where there is a where clause, in which case force + // the brace to the next line. + SameLineWhere, +} + +// How to indent a function's return type. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum ReturnIndent { + // Aligned with the arguments + WithArgs, + // Aligned with the where clause + WithWhereClause, +} + +// Formatting which depends on the AST. +fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap) -> ChangeSet<'a> { + let mut visitor = FmtVisitor::from_codemap(codemap); + visit::walk_crate(&mut visitor, krate); + let files = codemap.files.borrow(); + if let Some(last) = files.last() { + visitor.format_missing(last.end_pos); + } + + visitor.changes +} + +// Formatting done on a char by char or line by line basis. +// TODO warn on TODOs and FIXMEs without an issue number +// TODO warn on bad license +// TODO other stuff for parity with make tidy +fn fmt_lines(changes: &mut ChangeSet) { + let mut truncate_todo = Vec::new(); + + // Iterate over the chars in the change set. + for (f, text) in changes.text() { + let mut trims = vec![]; + let mut last_wspace: Option = None; + let mut line_len = 0; + let mut cur_line = 1; + let mut newline_count = 0; + for (c, b) in text.chars() { + if c == '\n' { // TOOD test for \r too + // Check for (and record) trailing whitespace. + if let Some(lw) = last_wspace { + trims.push((cur_line, lw, b)); + line_len -= b - lw; + } + // Check for any line width errors we couldn't correct. + if line_len > MAX_WIDTH { + // TODO store the error rather than reporting immediately. + println!("Rustfmt couldn't fix (sorry). {}:{}: line longer than {} characters", + f, cur_line, MAX_WIDTH); + } + line_len = 0; + cur_line += 1; + newline_count += 1; + last_wspace = None; + } else { + newline_count = 0; + line_len += 1; + if c.is_whitespace() { + if last_wspace.is_none() { + last_wspace = Some(b); + } + } else { + last_wspace = None; + } + } + } + + if newline_count > 1 { + debug!("track truncate: {} {} {}", f, text.len, newline_count); + truncate_todo.push((f.to_string(), text.len - newline_count + 1)) + } + + for &(l, _, _) in trims.iter() { + // TODO store the error rather than reporting immediately. + println!("Rustfmt left trailing whitespace at {}:{} (sorry)", f, l); + } + } + + for (f, l) in truncate_todo { + changes.get_mut(&f).truncate(l); + } +} + +struct RustFmtCalls { + input_path: Option, + write_mode: WriteMode, +} + +impl<'a> CompilerCalls<'a> for RustFmtCalls { + fn early_callback(&mut self, + _: &getopts::Matches, + _: &diagnostics::registry::Registry) + -> Compilation { + Compilation::Continue + } + + fn some_input(&mut self, + input: Input, + input_path: Option) + -> (Input, Option) { + match input_path { + Some(ref ip) => self.input_path = Some(ip.clone()), + _ => { + // FIXME should handle string input and write to stdout or something + panic!("No input path"); + } + } + (input, input_path) + } + + fn no_input(&mut self, + _: &getopts::Matches, + _: &config::Options, + _: &Option, + _: &Option, + _: &diagnostics::registry::Registry) + -> Option<(Input, Option)> { + panic!("No input supplied to RustFmt"); + } + + fn late_callback(&mut self, + _: &getopts::Matches, + _: &Session, + _: &Input, + _: &Option, + _: &Option) + -> Compilation { + Compilation::Continue + } + + fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { + let write_mode = self.write_mode; + let mut control = driver::CompileController::basic(); + control.after_parse.stop = Compilation::Stop; + control.after_parse.callback = box move |state| { + let krate = state.krate.unwrap(); + let codemap = state.session.codemap(); + let mut changes = fmt_ast(krate, codemap); + // For some reason, the codemap does not include terminating newlines + // so we must add one on for each file. This is sad. + changes.append_newlines(); + fmt_lines(&mut changes); + + // FIXME(#5) Should be user specified whether to show or replace. + let result = changes.write_all_files(write_mode); + + match result { + Err(msg) => println!("Error writing files: {}", msg), + Ok(result) => { + if let WriteMode::Return(callback) = write_mode { + callback(result); + } + } + } + }; + + control + } +} + +fn run(args: Vec, write_mode: WriteMode) { + let mut call_ctxt = RustFmtCalls { input_path: None, write_mode: write_mode }; + rustc_driver::run_compiler(&args, &mut call_ctxt); +} + +#[cfg(not(test))] +fn main() { + let args: Vec<_> = std::env::args().collect(); + //run(args, WriteMode::Display); + run(args, WriteMode::Overwrite); + std::env::set_exit_status(0); + + // TODO unit tests + // let fmt = ListFormatting { + // tactic: ListTactic::Horizontal, + // separator: ",", + // trailing_separator: SeparatorTactic::Vertical, + // indent: 2, + // h_width: 80, + // v_width: 100, + // }; + // let inputs = vec![(format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new()), + // (format!("foo"), String::new())]; + // let s = write_list(&inputs, &fmt); + // println!(" {}", s); +} + + +#[cfg(test)] +mod test { + use std::collections::HashMap; + use std::fs; + use std::io::Read; + use std::sync::atomic; + use super::*; + use super::run; + + // For now, the only supported regression tests are idempotent tests - the input and + // output must match exactly. + // FIXME(#28) would be good to check for error messages and fail on them, or at least report. + #[test] + fn idempotent_tests() { + println!("Idempotent tests:"); + FAILURES.store(0, atomic::Ordering::Relaxed); + + // Get all files in the tests/idem directory + let files = fs::read_dir("tests/idem").unwrap(); + // For each file, run rustfmt and collect the output + let mut count = 0; + for entry in files { + let path = entry.unwrap().path(); + let file_name = path.to_str().unwrap(); + println!("Testing '{}'...", file_name); + run(vec!["rustfmt".to_owned(), file_name.to_owned()], WriteMode::Return(HANDLE_RESULT)); + count += 1; + } + // And also dogfood ourselves! + println!("Testing 'src/main.rs'..."); + run(vec!["rustfmt".to_string(), "src/main.rs".to_string()], + WriteMode::Return(HANDLE_RESULT)); + count += 1; + + // Display results + let fails = FAILURES.load(atomic::Ordering::Relaxed); + println!("Ran {} idempotent tests; {} failures.", count, fails); + assert!(fails == 0, "{} idempotent tests failed", fails); + } + + // 'global' used by sys_tests and handle_result. + static FAILURES: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; + // Ick, just needed to get a &'static to handle_result. + static HANDLE_RESULT: &'static Fn(HashMap) = &handle_result; + + // Compare output to input. + fn handle_result(result: HashMap) { + let mut fails = 0; + + for file_name in result.keys() { + let mut f = fs::File::open(file_name).unwrap(); + let mut text = String::new(); + f.read_to_string(&mut text).unwrap(); + if result[file_name] != text { + fails += 1; + println!("Mismatch in {}.", file_name); + println!("{}", result[file_name]); + } + } + + if fails > 0 { + FAILURES.fetch_add(1, atomic::Ordering::Relaxed); + } + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e16537198f6..00000000000 --- a/src/main.rs +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(box_syntax)] -#![feature(box_patterns)] -#![feature(rustc_private)] -#![feature(collections)] -#![feature(str_char)] - -#![cfg_attr(not(test), feature(exit_status))] - -// 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 - - -#[macro_use] -extern crate log; - -extern crate getopts; -extern crate rustc; -extern crate rustc_driver; -extern crate syntax; - -extern crate strings; - -use rustc::session::Session; -use rustc::session::config::{self, Input}; -use rustc_driver::{driver, CompilerCalls, Compilation}; - -use syntax::ast; -use syntax::codemap::CodeMap; -use syntax::diagnostics; -use syntax::visit; - -use std::path::PathBuf; -use std::collections::HashMap; - -use changes::ChangeSet; -use visitor::FmtVisitor; - -mod changes; -mod visitor; -mod functions; -mod missed_spans; -mod lists; -mod utils; -mod types; -mod expr; -mod imports; - -const IDEAL_WIDTH: usize = 80; -const LEEWAY: usize = 5; -const MAX_WIDTH: usize = 100; -const MIN_STRING: usize = 10; -const TAB_SPACES: usize = 4; -const FN_BRACE_STYLE: BraceStyle = BraceStyle::SameLineWhere; -const FN_RETURN_INDENT: ReturnIndent = ReturnIndent::WithArgs; -// When we get scoped annotations, we should have rustfmt::skip. -const SKIP_ANNOTATION: &'static str = "rustfmt_skip"; - -#[derive(Copy, Clone)] -pub enum WriteMode { - Overwrite, - // str is the extension of the new file - NewFile(&'static str), - // Write the output to stdout. - Display, - // Return the result as a mapping from filenames to StringBuffers. - Return(&'static Fn(HashMap)), -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum BraceStyle { - AlwaysNextLine, - PreferSameLine, - // Prefer same line except where there is a where clause, in which case force - // the brace to the next line. - SameLineWhere, -} - -// How to indent a function's return type. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum ReturnIndent { - // Aligned with the arguments - WithArgs, - // Aligned with the where clause - WithWhereClause, -} - -// Formatting which depends on the AST. -fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap) -> ChangeSet<'a> { - let mut visitor = FmtVisitor::from_codemap(codemap); - visit::walk_crate(&mut visitor, krate); - let files = codemap.files.borrow(); - if let Some(last) = files.last() { - visitor.format_missing(last.end_pos); - } - - visitor.changes -} - -// Formatting done on a char by char or line by line basis. -// TODO warn on TODOs and FIXMEs without an issue number -// TODO warn on bad license -// TODO other stuff for parity with make tidy -fn fmt_lines(changes: &mut ChangeSet) { - let mut truncate_todo = Vec::new(); - - // Iterate over the chars in the change set. - for (f, text) in changes.text() { - let mut trims = vec![]; - let mut last_wspace: Option = None; - let mut line_len = 0; - let mut cur_line = 1; - let mut newline_count = 0; - for (c, b) in text.chars() { - if c == '\n' { // TOOD test for \r too - // Check for (and record) trailing whitespace. - if let Some(lw) = last_wspace { - trims.push((cur_line, lw, b)); - line_len -= b - lw; - } - // Check for any line width errors we couldn't correct. - if line_len > MAX_WIDTH { - // TODO store the error rather than reporting immediately. - println!("Rustfmt couldn't fix (sorry). {}:{}: line longer than {} characters", - f, cur_line, MAX_WIDTH); - } - line_len = 0; - cur_line += 1; - newline_count += 1; - last_wspace = None; - } else { - newline_count = 0; - line_len += 1; - if c.is_whitespace() { - if last_wspace.is_none() { - last_wspace = Some(b); - } - } else { - last_wspace = None; - } - } - } - - if newline_count > 1 { - debug!("track truncate: {} {} {}", f, text.len, newline_count); - truncate_todo.push((f.to_string(), text.len - newline_count + 1)) - } - - for &(l, _, _) in trims.iter() { - // TODO store the error rather than reporting immediately. - println!("Rustfmt left trailing whitespace at {}:{} (sorry)", f, l); - } - } - - for (f, l) in truncate_todo { - changes.get_mut(&f).truncate(l); - } -} - -struct RustFmtCalls { - input_path: Option, - write_mode: WriteMode, -} - -impl<'a> CompilerCalls<'a> for RustFmtCalls { - fn early_callback(&mut self, - _: &getopts::Matches, - _: &diagnostics::registry::Registry) - -> Compilation { - Compilation::Continue - } - - fn some_input(&mut self, - input: Input, - input_path: Option) - -> (Input, Option) { - match input_path { - Some(ref ip) => self.input_path = Some(ip.clone()), - _ => { - // FIXME should handle string input and write to stdout or something - panic!("No input path"); - } - } - (input, input_path) - } - - fn no_input(&mut self, - _: &getopts::Matches, - _: &config::Options, - _: &Option, - _: &Option, - _: &diagnostics::registry::Registry) - -> Option<(Input, Option)> { - panic!("No input supplied to RustFmt"); - } - - fn late_callback(&mut self, - _: &getopts::Matches, - _: &Session, - _: &Input, - _: &Option, - _: &Option) - -> Compilation { - Compilation::Continue - } - - fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { - let write_mode = self.write_mode; - let mut control = driver::CompileController::basic(); - control.after_parse.stop = Compilation::Stop; - control.after_parse.callback = box move |state| { - let krate = state.krate.unwrap(); - let codemap = state.session.codemap(); - let mut changes = fmt_ast(krate, codemap); - // For some reason, the codemap does not include terminating newlines - // so we must add one on for each file. This is sad. - changes.append_newlines(); - fmt_lines(&mut changes); - - // FIXME(#5) Should be user specified whether to show or replace. - let result = changes.write_all_files(write_mode); - - match result { - Err(msg) => println!("Error writing files: {}", msg), - Ok(result) => { - if let WriteMode::Return(callback) = write_mode { - callback(result); - } - } - } - }; - - control - } -} - -fn run(args: Vec, write_mode: WriteMode) { - let mut call_ctxt = RustFmtCalls { input_path: None, write_mode: write_mode }; - rustc_driver::run_compiler(&args, &mut call_ctxt); -} - -#[cfg(not(test))] -fn main() { - let args: Vec<_> = std::env::args().collect(); - //run(args, WriteMode::Display); - run(args, WriteMode::Overwrite); - std::env::set_exit_status(0); - - // TODO unit tests - // let fmt = ListFormatting { - // tactic: ListTactic::Horizontal, - // separator: ",", - // trailing_separator: SeparatorTactic::Vertical, - // indent: 2, - // h_width: 80, - // v_width: 100, - // }; - // let inputs = vec![(format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new()), - // (format!("foo"), String::new())]; - // let s = write_list(&inputs, &fmt); - // println!(" {}", s); -} - - -#[cfg(test)] -mod test { - use std::collections::HashMap; - use std::fs; - use std::io::Read; - use std::sync::atomic; - use super::*; - use super::run; - - // For now, the only supported regression tests are idempotent tests - the input and - // output must match exactly. - // FIXME(#28) would be good to check for error messages and fail on them, or at least report. - #[test] - fn idempotent_tests() { - println!("Idempotent tests:"); - FAILURES.store(0, atomic::Ordering::Relaxed); - - // Get all files in the tests/idem directory - let files = fs::read_dir("tests/idem").unwrap(); - // For each file, run rustfmt and collect the output - let mut count = 0; - for entry in files { - let path = entry.unwrap().path(); - let file_name = path.to_str().unwrap(); - println!("Testing '{}'...", file_name); - run(vec!["rustfmt".to_owned(), file_name.to_owned()], WriteMode::Return(HANDLE_RESULT)); - count += 1; - } - // And also dogfood ourselves! - println!("Testing 'src/main.rs'..."); - run(vec!["rustfmt".to_string(), "src/main.rs".to_string()], - WriteMode::Return(HANDLE_RESULT)); - count += 1; - - // Display results - let fails = FAILURES.load(atomic::Ordering::Relaxed); - println!("Ran {} idempotent tests; {} failures.", count, fails); - assert!(fails == 0, "{} idempotent tests failed", fails); - } - - // 'global' used by sys_tests and handle_result. - static FAILURES: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; - // Ick, just needed to get a &'static to handle_result. - static HANDLE_RESULT: &'static Fn(HashMap) = &handle_result; - - // Compare output to input. - fn handle_result(result: HashMap) { - let mut fails = 0; - - for file_name in result.keys() { - let mut f = fs::File::open(file_name).unwrap(); - let mut text = String::new(); - f.read_to_string(&mut text).unwrap(); - if result[file_name] != text { - fails += 1; - println!("Mismatch in {}.", file_name); - println!("{}", result[file_name]); - } - } - - if fails > 0 { - FAILURES.fetch_add(1, atomic::Ordering::Relaxed); - } - } -} -- cgit 1.4.1-3-g733a5