]> git.lizzy.rs Git - rust.git/blob - src/formatting.rs
eec33e6ba7945170f7b07054d2f8f2008acce788
[rust.git] / src / formatting.rs
1 // High level formatting functions.
2
3 use std::cell::RefCell;
4 use std::collections::HashMap;
5 use std::io::{self, Write};
6 use std::panic::{catch_unwind, AssertUnwindSafe};
7 use std::rc::Rc;
8 use std::time::{Duration, Instant};
9
10 use syntax::ast;
11 use syntax::errors::emitter::{ColorConfig, Emitter, EmitterWriter};
12 use syntax::errors::{Diagnostic, DiagnosticBuilder, Handler};
13 use syntax::parse::{self, ParseSess};
14 use syntax::source_map::{FilePathMapping, SourceMap, Span, DUMMY_SP};
15
16 use self::newline_style::apply_newline_style;
17 use crate::comment::{CharClasses, FullCodeCharKind};
18 use crate::config::{Config, FileName, Verbosity};
19 use crate::ignore_path::IgnorePathSet;
20 use crate::issues::BadIssueSeeker;
21 use crate::utils::count_newlines;
22 use crate::visitor::{FmtVisitor, SnippetProvider};
23 use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session};
24
25 mod newline_style;
26
27 // A map of the files of a crate, with their new content
28 pub(crate) type SourceFile = Vec<FileRecord>;
29 pub(crate) type FileRecord = (FileName, String);
30
31 impl<'b, T: Write + 'b> Session<'b, T> {
32     pub(crate) fn format_input_inner(&mut self, input: Input) -> Result<FormatReport, ErrorKind> {
33         if !self.config.version_meets_requirement() {
34             return Err(ErrorKind::VersionMismatch);
35         }
36
37         syntax::with_globals(self.config.edition().to_libsyntax_pos_edition(), || {
38             if self.config.disable_all_formatting() {
39                 // When the input is from stdin, echo back the input.
40                 if let Input::Text(ref buf) = input {
41                     if let Err(e) = io::stdout().write_all(buf.as_bytes()) {
42                         return Err(From::from(e));
43                     }
44                 }
45                 return Ok(FormatReport::new());
46             }
47
48             let config = &self.config.clone();
49             let format_result = format_project(input, config, self);
50
51             format_result.map(|report| {
52                 self.errors.add(&report.internal.borrow().1);
53                 report
54             })
55         })
56     }
57 }
58
59 // Format an entire crate (or subset of the module tree).
60 fn format_project<T: FormatHandler>(
61     input: Input,
62     config: &Config,
63     handler: &mut T,
64 ) -> Result<FormatReport, ErrorKind> {
65     let mut timer = Timer::start();
66
67     let main_file = input.file_name();
68     let input_is_stdin = main_file == FileName::Stdin;
69
70     let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
71         Ok(set) => Rc::new(set),
72         Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
73     };
74     if config.skip_children() && ignore_path_set.is_match(&main_file) {
75         return Ok(FormatReport::new());
76     }
77
78     // Parse the crate.
79     let can_reset_parser_errors = Rc::new(RefCell::new(false));
80     let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
81     let mut parse_session = make_parse_sess(
82         source_map.clone(),
83         config,
84         Rc::clone(&ignore_path_set),
85         can_reset_parser_errors.clone(),
86     );
87     let mut report = FormatReport::new();
88     let directory_ownership = input.to_directory_ownership();
89     let krate = match parse_crate(
90         input,
91         &parse_session,
92         config,
93         &mut report,
94         directory_ownership,
95         can_reset_parser_errors.clone(),
96     ) {
97         Ok(krate) => krate,
98         // Surface parse error via Session (errors are merged there from report)
99         Err(ErrorKind::ParseError) => return Ok(report),
100         Err(e) => return Err(e),
101     };
102     timer = timer.done_parsing();
103
104     // Suppress error output if we have to do any further parsing.
105     let silent_emitter = silent_emitter();
106     parse_session.span_diagnostic = Handler::with_emitter(true, None, silent_emitter);
107
108     let mut context = FormatContext::new(&krate, report, parse_session, config, handler);
109     let files = modules::ModResolver::new(
110         &context.parse_session,
111         directory_ownership.unwrap_or(parse::DirectoryOwnership::UnownedViaMod(true)),
112         !(input_is_stdin || config.skip_children()),
113     )
114     .visit_crate(&krate)
115     .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
116     for (path, module) in files {
117         let should_ignore = !input_is_stdin && ignore_path_set.is_match(&path);
118         if (config.skip_children() && path != main_file) || should_ignore {
119             continue;
120         }
121         should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path));
122         let is_root = path == main_file;
123         context.format_file(path, &module, is_root)?;
124     }
125     timer = timer.done_formatting();
126
127     should_emit_verbose(input_is_stdin, config, || {
128         println!(
129             "Spent {0:.3} secs in the parsing phase, and {1:.3} secs in the formatting phase",
130             timer.get_parse_time(),
131             timer.get_format_time(),
132         )
133     });
134
135     Ok(context.report)
136 }
137
138 // Used for formatting files.
139 #[derive(new)]
140 struct FormatContext<'a, T: FormatHandler> {
141     krate: &'a ast::Crate,
142     report: FormatReport,
143     parse_session: ParseSess,
144     config: &'a Config,
145     handler: &'a mut T,
146 }
147
148 impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
149     // Formats a single file/module.
150     fn format_file(
151         &mut self,
152         path: FileName,
153         module: &ast::Mod,
154         is_root: bool,
155     ) -> Result<(), ErrorKind> {
156         let source_file = self
157             .parse_session
158             .source_map()
159             .lookup_char_pos(module.inner.lo())
160             .file;
161         let big_snippet = source_file.src.as_ref().unwrap();
162         let snippet_provider = SnippetProvider::new(source_file.start_pos, big_snippet);
163         let mut visitor = FmtVisitor::from_source_map(
164             &self.parse_session,
165             &self.config,
166             &snippet_provider,
167             self.report.clone(),
168         );
169         visitor.skip_context.update_with_attrs(&self.krate.attrs);
170
171         // Format inner attributes if available.
172         if !self.krate.attrs.is_empty() && is_root {
173             visitor.skip_empty_lines(source_file.end_pos);
174             if visitor.visit_attrs(&self.krate.attrs, ast::AttrStyle::Inner) {
175                 visitor.push_rewrite(module.inner, None);
176             } else {
177                 visitor.format_separate_mod(module, &*source_file);
178             }
179         } else {
180             visitor.last_pos = source_file.start_pos;
181             visitor.skip_empty_lines(source_file.end_pos);
182             visitor.format_separate_mod(module, &*source_file);
183         };
184
185         debug_assert_eq!(
186             visitor.line_number,
187             count_newlines(&visitor.buffer),
188             "failed in format_file visitor.buffer:\n {:?}",
189             &visitor.buffer
190         );
191
192         // For some reason, the source_map does not include terminating
193         // newlines so we must add one on for each file. This is sad.
194         source_file::append_newline(&mut visitor.buffer);
195
196         format_lines(
197             &mut visitor.buffer,
198             &path,
199             &visitor.skipped_range.borrow(),
200             &self.config,
201             &self.report,
202         );
203
204         apply_newline_style(
205             self.config.newline_style(),
206             &mut visitor.buffer,
207             &big_snippet,
208         );
209
210         if visitor.macro_rewrite_failure {
211             self.report.add_macro_format_failure();
212         }
213         self.report
214             .add_non_formatted_ranges(visitor.skipped_range.borrow().clone());
215
216         self.handler.handle_formatted_file(
217             self.parse_session.source_map(),
218             path,
219             visitor.buffer.to_owned(),
220             &mut self.report,
221         )
222     }
223 }
224
225 // Handle the results of formatting.
226 trait FormatHandler {
227     fn handle_formatted_file(
228         &mut self,
229         source_map: &SourceMap,
230         path: FileName,
231         result: String,
232         report: &mut FormatReport,
233     ) -> Result<(), ErrorKind>;
234 }
235
236 impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
237     // Called for each formatted file.
238     fn handle_formatted_file(
239         &mut self,
240         source_map: &SourceMap,
241         path: FileName,
242         result: String,
243         report: &mut FormatReport,
244     ) -> Result<(), ErrorKind> {
245         if let Some(ref mut out) = self.out {
246             match source_file::write_file(Some(source_map), &path, &result, out, &mut *self.emitter)
247             {
248                 Ok(ref result) if result.has_diff => report.add_diff(),
249                 Err(e) => {
250                     // Create a new error with path_str to help users see which files failed
251                     let err_msg = format!("{}: {}", path, e);
252                     return Err(io::Error::new(e.kind(), err_msg).into());
253                 }
254                 _ => {}
255             }
256         }
257
258         self.source_file.push((path, result));
259         Ok(())
260     }
261 }
262
263 pub(crate) struct FormattingError {
264     pub(crate) line: usize,
265     pub(crate) kind: ErrorKind,
266     is_comment: bool,
267     is_string: bool,
268     pub(crate) line_buffer: String,
269 }
270
271 impl FormattingError {
272     pub(crate) fn from_span(
273         span: Span,
274         source_map: &SourceMap,
275         kind: ErrorKind,
276     ) -> FormattingError {
277         FormattingError {
278             line: source_map.lookup_char_pos(span.lo()).line,
279             is_comment: kind.is_comment(),
280             kind,
281             is_string: false,
282             line_buffer: source_map
283                 .span_to_lines(span)
284                 .ok()
285                 .and_then(|fl| {
286                     fl.file
287                         .get_line(fl.lines[0].line_index)
288                         .map(std::borrow::Cow::into_owned)
289                 })
290                 .unwrap_or_else(String::new),
291         }
292     }
293
294     pub(crate) fn is_internal(&self) -> bool {
295         match self.kind {
296             ErrorKind::LineOverflow(..)
297             | ErrorKind::TrailingWhitespace
298             | ErrorKind::IoError(_)
299             | ErrorKind::ParseError
300             | ErrorKind::LostComment => true,
301             _ => false,
302         }
303     }
304
305     pub(crate) fn msg_suffix(&self) -> &str {
306         if self.is_comment || self.is_string {
307             "set `error_on_unformatted = false` to suppress \
308              the warning against comments or string literals\n"
309         } else {
310             ""
311         }
312     }
313
314     // (space, target)
315     pub(crate) fn format_len(&self) -> (usize, usize) {
316         match self.kind {
317             ErrorKind::LineOverflow(found, max) => (max, found - max),
318             ErrorKind::TrailingWhitespace
319             | ErrorKind::DeprecatedAttr
320             | ErrorKind::BadIssue(_)
321             | ErrorKind::BadAttr
322             | ErrorKind::LostComment
323             | ErrorKind::LicenseCheck => {
324                 let trailing_ws_start = self
325                     .line_buffer
326                     .rfind(|c: char| !c.is_whitespace())
327                     .map(|pos| pos + 1)
328                     .unwrap_or(0);
329                 (
330                     trailing_ws_start,
331                     self.line_buffer.len() - trailing_ws_start,
332                 )
333             }
334             _ => unreachable!(),
335         }
336     }
337 }
338
339 pub(crate) type FormatErrorMap = HashMap<FileName, Vec<FormattingError>>;
340
341 #[derive(Default, Debug, PartialEq)]
342 pub(crate) struct ReportedErrors {
343     // Encountered e.g., an IO error.
344     pub(crate) has_operational_errors: bool,
345
346     // Failed to reformat code because of parsing errors.
347     pub(crate) has_parsing_errors: bool,
348
349     // Code is valid, but it is impossible to format it properly.
350     pub(crate) has_formatting_errors: bool,
351
352     // Code contains macro call that was unable to format.
353     pub(crate) has_macro_format_failure: bool,
354
355     // Failed a check, such as the license check or other opt-in checking.
356     pub(crate) has_check_errors: bool,
357
358     /// Formatted code differs from existing code (--check only).
359     pub(crate) has_diff: bool,
360 }
361
362 impl ReportedErrors {
363     /// Combine two summaries together.
364     pub(crate) fn add(&mut self, other: &ReportedErrors) {
365         self.has_operational_errors |= other.has_operational_errors;
366         self.has_parsing_errors |= other.has_parsing_errors;
367         self.has_formatting_errors |= other.has_formatting_errors;
368         self.has_macro_format_failure |= other.has_macro_format_failure;
369         self.has_check_errors |= other.has_check_errors;
370         self.has_diff |= other.has_diff;
371     }
372 }
373
374 #[derive(Clone, Copy, Debug)]
375 enum Timer {
376     Disabled,
377     Initialized(Instant),
378     DoneParsing(Instant, Instant),
379     DoneFormatting(Instant, Instant, Instant),
380 }
381
382 impl Timer {
383     fn start() -> Timer {
384         if cfg!(target_arch = "wasm32") {
385             Timer::Disabled
386         } else {
387             Timer::Initialized(Instant::now())
388         }
389     }
390     fn done_parsing(self) -> Self {
391         match self {
392             Timer::Disabled => Timer::Disabled,
393             Timer::Initialized(init_time) => Timer::DoneParsing(init_time, Instant::now()),
394             _ => panic!("Timer can only transition to DoneParsing from Initialized state"),
395         }
396     }
397
398     fn done_formatting(self) -> Self {
399         match self {
400             Timer::Disabled => Timer::Disabled,
401             Timer::DoneParsing(init_time, parse_time) => {
402                 Timer::DoneFormatting(init_time, parse_time, Instant::now())
403             }
404             _ => panic!("Timer can only transition to DoneFormatting from DoneParsing state"),
405         }
406     }
407
408     /// Returns the time it took to parse the source files in seconds.
409     fn get_parse_time(&self) -> f32 {
410         match *self {
411             Timer::Disabled => panic!("this platform cannot time execution"),
412             Timer::DoneParsing(init, parse_time) | Timer::DoneFormatting(init, parse_time, _) => {
413                 // This should never underflow since `Instant::now()` guarantees monotonicity.
414                 Self::duration_to_f32(parse_time.duration_since(init))
415             }
416             Timer::Initialized(..) => unreachable!(),
417         }
418     }
419
420     /// Returns the time it took to go from the parsed AST to the formatted output. Parsing time is
421     /// not included.
422     fn get_format_time(&self) -> f32 {
423         match *self {
424             Timer::Disabled => panic!("this platform cannot time execution"),
425             Timer::DoneFormatting(_init, parse_time, format_time) => {
426                 Self::duration_to_f32(format_time.duration_since(parse_time))
427             }
428             Timer::DoneParsing(..) | Timer::Initialized(..) => unreachable!(),
429         }
430     }
431
432     fn duration_to_f32(d: Duration) -> f32 {
433         d.as_secs() as f32 + d.subsec_nanos() as f32 / 1_000_000_000f32
434     }
435 }
436
437 // Formatting done on a char by char or line by line basis.
438 // FIXME(#20): other stuff for parity with make tidy.
439 fn format_lines(
440     text: &mut String,
441     name: &FileName,
442     skipped_range: &[(usize, usize)],
443     config: &Config,
444     report: &FormatReport,
445 ) {
446     let mut formatter = FormatLines::new(name, skipped_range, config);
447     formatter.check_license(text);
448     formatter.iterate(text);
449
450     if formatter.newline_count > 1 {
451         debug!("track truncate: {} {}", text.len(), formatter.newline_count);
452         let line = text.len() - formatter.newline_count + 1;
453         text.truncate(line);
454     }
455
456     report.append(name.clone(), formatter.errors);
457 }
458
459 struct FormatLines<'a> {
460     name: &'a FileName,
461     skipped_range: &'a [(usize, usize)],
462     last_was_space: bool,
463     line_len: usize,
464     cur_line: usize,
465     newline_count: usize,
466     errors: Vec<FormattingError>,
467     issue_seeker: BadIssueSeeker,
468     line_buffer: String,
469     current_line_contains_string_literal: bool,
470     format_line: bool,
471     allow_issue_seek: bool,
472     config: &'a Config,
473 }
474
475 impl<'a> FormatLines<'a> {
476     fn new(
477         name: &'a FileName,
478         skipped_range: &'a [(usize, usize)],
479         config: &'a Config,
480     ) -> FormatLines<'a> {
481         let issue_seeker = BadIssueSeeker::new(config.report_todo(), config.report_fixme());
482         FormatLines {
483             name,
484             skipped_range,
485             last_was_space: false,
486             line_len: 0,
487             cur_line: 1,
488             newline_count: 0,
489             errors: vec![],
490             allow_issue_seek: !issue_seeker.is_disabled(),
491             issue_seeker,
492             line_buffer: String::with_capacity(config.max_width() * 2),
493             current_line_contains_string_literal: false,
494             format_line: config.file_lines().contains_line(name, 1),
495             config,
496         }
497     }
498
499     fn check_license(&mut self, text: &mut String) {
500         if let Some(ref license_template) = self.config.license_template {
501             if !license_template.is_match(text) {
502                 self.errors.push(FormattingError {
503                     line: self.cur_line,
504                     kind: ErrorKind::LicenseCheck,
505                     is_comment: false,
506                     is_string: false,
507                     line_buffer: String::new(),
508                 });
509             }
510         }
511     }
512
513     // Iterate over the chars in the file map.
514     fn iterate(&mut self, text: &mut String) {
515         for (kind, c) in CharClasses::new(text.chars()) {
516             if c == '\r' {
517                 continue;
518             }
519
520             if self.allow_issue_seek && self.format_line {
521                 // Add warnings for bad todos/ fixmes
522                 if let Some(issue) = self.issue_seeker.inspect(c) {
523                     self.push_err(ErrorKind::BadIssue(issue), false, false);
524                 }
525             }
526
527             if c == '\n' {
528                 self.new_line(kind);
529             } else {
530                 self.char(c, kind);
531             }
532         }
533     }
534
535     fn new_line(&mut self, kind: FullCodeCharKind) {
536         if self.format_line {
537             // Check for (and record) trailing whitespace.
538             if self.last_was_space {
539                 if self.should_report_error(kind, &ErrorKind::TrailingWhitespace)
540                     && !self.is_skipped_line()
541                 {
542                     self.push_err(
543                         ErrorKind::TrailingWhitespace,
544                         kind.is_comment(),
545                         kind.is_string(),
546                     );
547                 }
548                 self.line_len -= 1;
549             }
550
551             // Check for any line width errors we couldn't correct.
552             let error_kind = ErrorKind::LineOverflow(self.line_len, self.config.max_width());
553             if self.line_len > self.config.max_width()
554                 && !self.is_skipped_line()
555                 && self.should_report_error(kind, &error_kind)
556             {
557                 let is_string = self.current_line_contains_string_literal;
558                 self.push_err(error_kind, kind.is_comment(), is_string);
559             }
560         }
561
562         self.line_len = 0;
563         self.cur_line += 1;
564         self.format_line = self
565             .config
566             .file_lines()
567             .contains_line(self.name, self.cur_line);
568         self.newline_count += 1;
569         self.last_was_space = false;
570         self.line_buffer.clear();
571         self.current_line_contains_string_literal = false;
572     }
573
574     fn char(&mut self, c: char, kind: FullCodeCharKind) {
575         self.newline_count = 0;
576         self.line_len += if c == '\t' {
577             self.config.tab_spaces()
578         } else {
579             1
580         };
581         self.last_was_space = c.is_whitespace();
582         self.line_buffer.push(c);
583         if kind.is_string() {
584             self.current_line_contains_string_literal = true;
585         }
586     }
587
588     fn push_err(&mut self, kind: ErrorKind, is_comment: bool, is_string: bool) {
589         self.errors.push(FormattingError {
590             line: self.cur_line,
591             kind,
592             is_comment,
593             is_string,
594             line_buffer: self.line_buffer.clone(),
595         });
596     }
597
598     fn should_report_error(&self, char_kind: FullCodeCharKind, error_kind: &ErrorKind) -> bool {
599         let allow_error_report = if char_kind.is_comment()
600             || self.current_line_contains_string_literal
601             || error_kind.is_comment()
602         {
603             self.config.error_on_unformatted()
604         } else {
605             true
606         };
607
608         match error_kind {
609             ErrorKind::LineOverflow(..) => {
610                 self.config.error_on_line_overflow() && allow_error_report
611             }
612             ErrorKind::TrailingWhitespace | ErrorKind::LostComment => allow_error_report,
613             _ => true,
614         }
615     }
616
617     /// Returns `true` if the line with the given line number was skipped by `#[rustfmt::skip]`.
618     fn is_skipped_line(&self) -> bool {
619         self.skipped_range
620             .iter()
621             .any(|&(lo, hi)| lo <= self.cur_line && self.cur_line <= hi)
622     }
623 }
624
625 fn parse_crate(
626     input: Input,
627     parse_session: &ParseSess,
628     config: &Config,
629     report: &mut FormatReport,
630     directory_ownership: Option<parse::DirectoryOwnership>,
631     can_reset_parser_errors: Rc<RefCell<bool>>,
632 ) -> Result<ast::Crate, ErrorKind> {
633     let input_is_stdin = input.is_text();
634
635     let parser = match input {
636         Input::File(ref file) => {
637             // Use `new_sub_parser_from_file` when we the input is a submodule.
638             Ok(if let Some(dir_own) = directory_ownership {
639                 parse::new_sub_parser_from_file(parse_session, file, dir_own, None, DUMMY_SP)
640             } else {
641                 parse::new_parser_from_file(parse_session, file)
642             })
643         }
644         Input::Text(text) => parse::maybe_new_parser_from_source_str(
645             parse_session,
646             syntax::source_map::FileName::Custom("stdin".to_owned()),
647             text,
648         )
649         .map(|mut parser| {
650             parser.recurse_into_file_modules = false;
651             parser
652         }),
653     };
654
655     let result = match parser {
656         Ok(mut parser) => {
657             parser.cfg_mods = false;
658             if config.skip_children() {
659                 parser.recurse_into_file_modules = false;
660             }
661
662             let mut parser = AssertUnwindSafe(parser);
663             catch_unwind(move || parser.0.parse_crate_mod().map_err(|d| vec![d]))
664         }
665         Err(diagnostics) => {
666             for diagnostic in diagnostics {
667                 parse_session.span_diagnostic.emit_diagnostic(&diagnostic);
668             }
669             report.add_parsing_error();
670             return Err(ErrorKind::ParseError);
671         }
672     };
673
674     match result {
675         Ok(Ok(c)) => {
676             if !parse_session.span_diagnostic.has_errors() {
677                 return Ok(c);
678             }
679             // This scenario occurs when the parser encountered errors
680             // but was still able to recover. If all of the parser errors
681             // occurred in files that are ignored, then reset
682             // the error count and continue.
683             // https://github.com/rust-lang/rustfmt/issues/3779
684             if *can_reset_parser_errors.borrow() {
685                 parse_session.span_diagnostic.reset_err_count();
686                 return Ok(c);
687             }
688         }
689         Ok(Err(mut diagnostics)) => diagnostics.iter_mut().for_each(DiagnosticBuilder::emit),
690         Err(_) => {
691             // Note that if you see this message and want more information,
692             // then run the `parse_crate_mod` function above without
693             // `catch_unwind` so rustfmt panics and you can get a backtrace.
694             should_emit_verbose(input_is_stdin, config, || {
695                 println!("The Rust parser panicked")
696             });
697         }
698     }
699
700     report.add_parsing_error();
701     Err(ErrorKind::ParseError)
702 }
703
704 struct SilentOnIgnoredFilesEmitter {
705     ignore_path_set: Rc<IgnorePathSet>,
706     source_map: Rc<SourceMap>,
707     emitter: EmitterWriter,
708     has_non_ignorable_parser_errors: bool,
709     can_reset: Rc<RefCell<bool>>,
710 }
711
712 impl Emitter for SilentOnIgnoredFilesEmitter {
713     fn emit_diagnostic(&mut self, db: &Diagnostic) {
714         if let Some(primary_span) = &db.span.primary_span() {
715             let file_name = self.source_map.span_to_filename(*primary_span);
716             match file_name {
717                 syntax_pos::FileName::Real(ref path) => {
718                     if self
719                         .ignore_path_set
720                         .is_match(&FileName::Real(path.to_path_buf()))
721                     {
722                         if !self.has_non_ignorable_parser_errors {
723                             *self.can_reset.borrow_mut() = true;
724                         }
725                         return;
726                     }
727                 }
728                 _ => (),
729             };
730         }
731
732         self.has_non_ignorable_parser_errors = true;
733         *self.can_reset.borrow_mut() = false;
734         self.emitter.emit_diagnostic(db);
735     }
736 }
737
738 /// Emitter which discards every error.
739 struct SilentEmitter;
740
741 impl Emitter for SilentEmitter {
742     fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
743 }
744
745 fn silent_emitter() -> Box<SilentEmitter> {
746     Box::new(SilentEmitter {})
747 }
748
749 fn make_parse_sess(
750     source_map: Rc<SourceMap>,
751     config: &Config,
752     ignore_path_set: Rc<IgnorePathSet>,
753     can_reset: Rc<RefCell<bool>>,
754 ) -> ParseSess {
755     let tty_handler = if config.hide_parse_errors() {
756         let silent_emitter = silent_emitter();
757         Handler::with_emitter(true, None, silent_emitter)
758     } else {
759         let supports_color = term::stderr().map_or(false, |term| term.supports_color());
760         let color_cfg = if supports_color {
761             ColorConfig::Auto
762         } else {
763             ColorConfig::Never
764         };
765
766         let emitter_writer = EmitterWriter::stderr(
767             color_cfg,
768             Some(source_map.clone()),
769             false,
770             false,
771             None,
772             false,
773         );
774         let emitter = Box::new(SilentOnIgnoredFilesEmitter {
775             has_non_ignorable_parser_errors: false,
776             ignore_path_set: ignore_path_set,
777             source_map: Rc::clone(&source_map),
778             emitter: emitter_writer,
779             can_reset,
780         });
781         Handler::with_emitter(true, None, emitter)
782     };
783
784     ParseSess::with_span_handler(tty_handler, source_map)
785 }
786
787 fn should_emit_verbose<F>(is_stdin: bool, config: &Config, f: F)
788 where
789     F: Fn(),
790 {
791     if config.verbose() == Verbosity::Verbose && !is_stdin {
792         f();
793     }
794 }