]> git.lizzy.rs Git - rust.git/blob - src/mod.rs
Extract out more files
[rust.git] / src / mod.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![feature(box_syntax)]
12 #![feature(box_patterns)]
13 #![feature(rustc_private)]
14 #![feature(collections)]
15 #![feature(exit_status)]
16 #![feature(str_char)]
17
18 // TODO we're going to allocate a whole bunch of temp Strings, is it worth
19 // keeping some scratch mem for this and running our own StrPool?
20 // TODO for lint violations of names, emit a refactor script
21
22 // TODO priorities
23 // Fix fns and methods properly
24 //   dead spans
25 //
26 // Smoke testing till we can use it
27 //   no newline at the end of doc.rs
28
29 #[macro_use]
30 extern crate log;
31
32 extern crate getopts;
33 extern crate rustc;
34 extern crate rustc_driver;
35 extern crate syntax;
36
37 extern crate strings;
38
39 use rustc::session::Session;
40 use rustc::session::config::{self, Input};
41 use rustc_driver::{driver, CompilerCalls, Compilation};
42
43 use syntax::ast;
44 use syntax::codemap::CodeMap;
45 use syntax::diagnostics;
46 use syntax::visit;
47
48 use std::path::PathBuf;
49
50 use changes::ChangeSet;
51 use visitor::FmtVisitor;
52
53 mod changes;
54 mod visitor;
55 mod functions;
56 mod missed_spans;
57 mod lists;
58 mod utils;
59 mod types;
60 mod expr;
61 mod imports;
62
63 const IDEAL_WIDTH: usize = 80;
64 const LEEWAY: usize = 5;
65 const MAX_WIDTH: usize = 100;
66 const MIN_STRING: usize = 10;
67 const TAB_SPACES: usize = 4;
68 const FN_BRACE_STYLE: BraceStyle = BraceStyle::SameLineWhere;
69 const FN_RETURN_INDENT: ReturnIndent = ReturnIndent::WithArgs;
70
71 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
72 pub enum WriteMode {
73     Overwrite,
74     // str is the extension of the new file
75     NewFile(&'static str),
76     // Write the output to stdout.
77     Display,
78 }
79
80 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
81 enum BraceStyle {
82     AlwaysNextLine,
83     PreferSameLine,
84     // Prefer same line except where there is a where clause, in which case force
85     // the brace to the next line.
86     SameLineWhere,
87 }
88
89 // How to indent a function's return type.
90 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
91 enum ReturnIndent {
92     // Aligned with the arguments
93     WithArgs,
94     // Aligned with the where clause
95     WithWhereClause,
96 }
97
98 // Formatting which depends on the AST.
99 fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap) -> ChangeSet<'a> {
100     let mut visitor = FmtVisitor::from_codemap(codemap);
101     visit::walk_crate(&mut visitor, krate);
102     let files = codemap.files.borrow();
103     if let Some(last) = files.last() {
104         visitor.format_missing(last.end_pos);
105     }
106
107     visitor.changes
108 }
109
110 // Formatting done on a char by char basis.
111 fn fmt_lines(changes: &mut ChangeSet) {
112     // Iterate over the chars in the change set.
113     for (f, text) in changes.text() {
114         let mut trims = vec![];
115         let mut last_wspace: Option<usize> = None;
116         let mut line_len = 0;
117         let mut cur_line = 1;
118         for (c, b) in text.chars() {
119             if c == '\n' { // TOOD test for \r too
120                 // Check for (and record) trailing whitespace.
121                 if let Some(lw) = last_wspace {
122                     trims.push((cur_line, lw, b));
123                     line_len -= b - lw;
124                 }
125                 // Check for any line width errors we couldn't correct.
126                 if line_len > MAX_WIDTH {
127                     // FIXME store the error rather than reporting immediately.
128                     println!("Rustfmt couldn't fix (sorry). {}:{}: line longer than {} characters",
129                              f, cur_line, MAX_WIDTH);
130                 }
131                 line_len = 0;
132                 cur_line += 1;
133                 last_wspace = None;
134             } else {
135                 line_len += 1;
136                 if c.is_whitespace() {
137                     if last_wspace.is_none() {
138                         last_wspace = Some(b);
139                     }
140                 } else {
141                     last_wspace = None;
142                 }
143             }
144         }
145
146         for &(l, _, _) in trims.iter() {
147             // FIXME store the error rather than reporting immediately.
148             println!("Rustfmt left trailing whitespace at {}:{} (sorry)", f, l);
149         }
150     }
151 }
152
153 struct RustFmtCalls {
154     input_path: Option<PathBuf>,
155 }
156
157 impl<'a> CompilerCalls<'a> for RustFmtCalls {
158     fn early_callback(&mut self,
159                       _: &getopts::Matches,
160                       _: &diagnostics::registry::Registry)
161                       -> Compilation {
162         Compilation::Continue
163     }
164
165     fn some_input(&mut self,
166                   input: Input,
167                   input_path: Option<PathBuf>)
168                   -> (Input, Option<PathBuf>) {
169         match input_path {
170             Some(ref ip) => self.input_path = Some(ip.clone()),
171             _ => {
172                 // FIXME should handle string input and write to stdout or something
173                 panic!("No input path");
174             }
175         }
176         (input, input_path)
177     }
178
179     fn no_input(&mut self,
180                 _: &getopts::Matches,
181                 _: &config::Options,
182                 _: &Option<PathBuf>,
183                 _: &Option<PathBuf>,
184                 _: &diagnostics::registry::Registry)
185                 -> Option<(Input, Option<PathBuf>)> {
186         panic!("No input supplied to RustFmt");
187     }
188
189     fn late_callback(&mut self,
190                      _: &getopts::Matches,
191                      _: &Session,
192                      _: &Input,
193                      _: &Option<PathBuf>,
194                      _: &Option<PathBuf>)
195                      -> Compilation {
196         Compilation::Continue
197     }
198
199     fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> {
200         let mut control = driver::CompileController::basic();
201         control.after_parse.stop = Compilation::Stop;
202         control.after_parse.callback = box |state| {
203             let krate = state.krate.unwrap();
204             let codemap = state.session.codemap();
205             let mut changes = fmt_ast(krate, codemap);
206             fmt_lines(&mut changes);
207
208             // FIXME(#5) Should be user specified whether to show or replace.
209             let result = changes.write_all_files(WriteMode::Display);
210
211             if let Err(msg) = result {
212                 println!("Error writing files: {}", msg);
213             }
214         };
215
216         control
217     }
218 }
219
220 fn main() {
221     let args: Vec<_> = std::env::args().collect();
222     let mut call_ctxt = RustFmtCalls { input_path: None };
223     rustc_driver::run_compiler(&args, &mut call_ctxt);
224     std::env::set_exit_status(0);
225
226     // TODO unit tests
227     // let fmt = ListFormatting {
228     //     tactic: ListTactic::Horizontal,
229     //     separator: ",",
230     //     trailing_separator: SeparatorTactic::Vertical,
231     //     indent: 2,
232     //     h_width: 80,
233     //     v_width: 100,
234     // };
235     // let inputs = vec![(format!("foo"), String::new()),
236     //                   (format!("foo"), String::new()),
237     //                   (format!("foo"), String::new()),
238     //                   (format!("foo"), String::new()),
239     //                   (format!("foo"), String::new()),
240     //                   (format!("foo"), String::new()),
241     //                   (format!("foo"), String::new()),
242     //                   (format!("foo"), String::new())];
243     // let s = write_list(&inputs, &fmt);
244     // println!("  {}", s);
245 }
246
247 // FIXME comments
248 // comments aren't in the AST, which makes processing them difficult, but then
249 // comments are complicated anyway. I think I am happy putting off tackling them
250 // for now. Long term the soluton is for comments to be in the AST, but that means
251 // only the libsyntax AST, not the rustc one, which means waiting for the ASTs
252 // to diverge one day....
253
254 // Once we do have comments, we just have to implement a simple word wrapping
255 // algorithm to keep the width under IDEAL_WIDTH. We should also convert multiline
256 // /* ... */ comments to // and check doc comments are in the right place and of
257 // the right kind.
258
259 // Should also make sure comments have the right indent