diff options
Diffstat (limited to 'src/formatting.rs')
| -rw-r--r-- | src/formatting.rs | 458 |
1 files changed, 39 insertions, 419 deletions
diff --git a/src/formatting.rs b/src/formatting.rs index bed87748253..104e631f441 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -1,29 +1,20 @@ // High level formatting functions. -use std::cell::RefCell; use std::collections::HashMap; use std::io::{self, Write}; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::rc::Rc; use std::time::{Duration, Instant}; -use rustc_data_structures::sync::{Lrc, Send}; -use rustc_errors::emitter::{Emitter, EmitterWriter}; -use rustc_errors::{ColorConfig, Diagnostic, DiagnosticBuilder, Handler, Level as DiagnosticLevel}; -use rustc_session::parse::ParseSess; -use rustc_span::{ - source_map::{FilePathMapping, SourceMap}, - Span, DUMMY_SP, -}; +use rustc_span::Span; use syntax::ast; use self::newline_style::apply_newline_style; use crate::comment::{CharClasses, FullCodeCharKind}; use crate::config::{Config, FileName, Verbosity}; -use crate::ignore_path::IgnorePathSet; use crate::issues::BadIssueSeeker; +use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; +use crate::syntux::session::ParseSess; use crate::utils::count_newlines; -use crate::visitor::{FmtVisitor, SnippetProvider}; +use crate::visitor::FmtVisitor; use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; mod newline_style; @@ -71,54 +62,41 @@ fn format_project<T: FormatHandler>( let main_file = input.file_name(); let input_is_stdin = main_file == FileName::Stdin; - let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) { - Ok(set) => Rc::new(set), - Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)), - }; - if config.skip_children() && ignore_path_set.is_match(&main_file) { + let mut parse_session = ParseSess::new(config)?; + if config.skip_children() && parse_session.ignore_file(&main_file) { return Ok(FormatReport::new()); } // Parse the crate. - let can_reset_parser_errors = Rc::new(RefCell::new(false)); - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let mut parse_session = make_parse_sess( - source_map.clone(), - config, - Rc::clone(&ignore_path_set), - can_reset_parser_errors.clone(), - ); let mut report = FormatReport::new(); let directory_ownership = input.to_directory_ownership(); - let krate = match parse_crate( - input, - &parse_session, - config, - &mut report, - directory_ownership, - can_reset_parser_errors.clone(), - ) { + let krate = match Parser::parse_crate(config, input, directory_ownership, &parse_session) { Ok(krate) => krate, // Surface parse error via Session (errors are merged there from report) - Err(ErrorKind::ParseError) => return Ok(report), - Err(e) => return Err(e), + Err(e) => { + let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError; + should_emit_verbose(forbid_verbose, config, || { + eprintln!("The Rust parser panicked"); + }); + report.add_parsing_error(); + return Ok(report); + } }; timer = timer.done_parsing(); // Suppress error output if we have to do any further parsing. - let silent_emitter = silent_emitter(); - parse_session.span_diagnostic = Handler::with_emitter(true, None, silent_emitter); + parse_session.set_silent_emitter(); let mut context = FormatContext::new(&krate, report, parse_session, config, handler); let files = modules::ModResolver::new( &context.parse_session, - directory_ownership.unwrap_or(rustc_parse::DirectoryOwnership::UnownedViaMod), + directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaMod), !(input_is_stdin || config.skip_children()), ) .visit_crate(&krate) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; for (path, module) in files { - let should_ignore = !input_is_stdin && ignore_path_set.is_match(&path); + let should_ignore = !input_is_stdin && context.ignore_file(&path); if (config.skip_children() && path != main_file) || should_ignore { continue; } @@ -150,6 +128,10 @@ struct FormatContext<'a, T: FormatHandler> { } impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { + fn ignore_file(&self, path: &FileName) -> bool { + self.parse_session.ignore_file(path) + } + // Formats a single file/module. fn format_file( &mut self, @@ -157,15 +139,8 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { module: &ast::Mod, is_root: bool, ) -> Result<(), ErrorKind> { - let source_file = self - .parse_session - .source_map() - .lookup_char_pos(module.inner.lo()) - .file; - let big_snippet = source_file.src.as_ref().unwrap(); - let snippet_provider = - SnippetProvider::new(source_file.start_pos, source_file.end_pos, big_snippet); - let mut visitor = FmtVisitor::from_source_map( + let snippet_provider = self.parse_session.snippet_provider(module.inner); + let mut visitor = FmtVisitor::from_parse_sess( &self.parse_session, &self.config, &snippet_provider, @@ -175,16 +150,16 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { // Format inner attributes if available. if !self.krate.attrs.is_empty() && is_root { - visitor.skip_empty_lines(source_file.end_pos); + visitor.skip_empty_lines(snippet_provider.end_pos()); if visitor.visit_attrs(&self.krate.attrs, ast::AttrStyle::Inner) { visitor.push_rewrite(module.inner, None); } else { - visitor.format_separate_mod(module, &*source_file); + visitor.format_separate_mod(module, snippet_provider.end_pos()); } } else { - visitor.last_pos = source_file.start_pos; - visitor.skip_empty_lines(source_file.end_pos); - visitor.format_separate_mod(module, &*source_file); + visitor.last_pos = snippet_provider.start_pos(); + visitor.skip_empty_lines(snippet_provider.end_pos()); + visitor.format_separate_mod(module, snippet_provider.end_pos()); }; debug_assert_eq!( @@ -209,7 +184,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { apply_newline_style( self.config.newline_style(), &mut visitor.buffer, - &big_snippet, + snippet_provider.entire_snippet(), ); if visitor.macro_rewrite_failure { @@ -219,7 +194,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { .add_non_formatted_ranges(visitor.skipped_range.borrow().clone()); self.handler.handle_formatted_file( - self.parse_session.source_map(), + &self.parse_session, path, visitor.buffer.to_owned(), &mut self.report, @@ -231,7 +206,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { trait FormatHandler { fn handle_formatted_file( &mut self, - source_map: &SourceMap, + parse_session: &ParseSess, path: FileName, result: String, report: &mut FormatReport, @@ -242,14 +217,14 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { // Called for each formatted file. fn handle_formatted_file( &mut self, - source_map: &SourceMap, + parse_session: &ParseSess, path: FileName, result: String, report: &mut FormatReport, ) -> Result<(), ErrorKind> { if let Some(ref mut out) = self.out { match source_file::write_file( - Some(source_map), + Some(parse_session), &path, &result, out, @@ -282,23 +257,15 @@ pub(crate) struct FormattingError { impl FormattingError { pub(crate) fn from_span( span: Span, - source_map: &SourceMap, + parse_sess: &ParseSess, kind: ErrorKind, ) -> FormattingError { FormattingError { - line: source_map.lookup_char_pos(span.lo()).line, + line: parse_sess.line_of_byte_pos(span.lo()), is_comment: kind.is_comment(), kind, is_string: false, - line_buffer: source_map - .span_to_lines(span) - .ok() - .and_then(|fl| { - fl.file - .get_line(fl.lines[0].line_index) - .map(std::borrow::Cow::into_owned) - }) - .unwrap_or_else(String::new), + line_buffer: parse_sess.span_to_first_line_string(span), } } @@ -633,358 +600,11 @@ impl<'a> FormatLines<'a> { } } -fn parse_crate( - input: Input, - parse_session: &ParseSess, - config: &Config, - report: &mut FormatReport, - directory_ownership: Option<rustc_parse::DirectoryOwnership>, - can_reset_parser_errors: Rc<RefCell<bool>>, -) -> Result<ast::Crate, ErrorKind> { - let input_is_stdin = input.is_text(); - - let parser = match input { - Input::File(ref file) => { - // Use `new_sub_parser_from_file` when we the input is a submodule. - Ok(if let Some(dir_own) = directory_ownership { - rustc_parse::new_sub_parser_from_file(parse_session, file, dir_own, None, DUMMY_SP) - } else { - rustc_parse::new_parser_from_file(parse_session, file) - }) - } - Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( - parse_session, - rustc_span::FileName::Custom("stdin".to_owned()), - text, - ) - .map(|mut parser| { - parser.recurse_into_file_modules = false; - parser - }), - }; - - let result = match parser { - Ok(mut parser) => { - parser.cfg_mods = false; - if config.skip_children() { - parser.recurse_into_file_modules = false; - } - - let mut parser = AssertUnwindSafe(parser); - catch_unwind(move || parser.0.parse_crate_mod().map_err(|d| vec![d])) - } - Err(diagnostics) => { - for diagnostic in diagnostics { - parse_session.span_diagnostic.emit_diagnostic(&diagnostic); - } - report.add_parsing_error(); - return Err(ErrorKind::ParseError); - } - }; - - match result { - Ok(Ok(c)) => { - if !parse_session.span_diagnostic.has_errors() { - return Ok(c); - } - // This scenario occurs when the parser encountered errors - // but was still able to recover. If all of the parser errors - // occurred in files that are ignored, then reset - // the error count and continue. - // https://github.com/rust-lang/rustfmt/issues/3779 - if *can_reset_parser_errors.borrow() { - parse_session.span_diagnostic.reset_err_count(); - return Ok(c); - } - } - Ok(Err(mut diagnostics)) => diagnostics.iter_mut().for_each(DiagnosticBuilder::emit), - Err(_) => { - // Note that if you see this message and want more information, - // then run the `parse_crate_mod` function above without - // `catch_unwind` so rustfmt panics and you can get a backtrace. - should_emit_verbose(input_is_stdin, config, || { - println!("The Rust parser panicked") - }); - } - } - - report.add_parsing_error(); - Err(ErrorKind::ParseError) -} - -struct SilentOnIgnoredFilesEmitter { - ignore_path_set: Rc<IgnorePathSet>, - source_map: Rc<SourceMap>, - emitter: Box<dyn Emitter + Send>, - has_non_ignorable_parser_errors: bool, - can_reset: Rc<RefCell<bool>>, -} - -impl SilentOnIgnoredFilesEmitter { - fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) { - self.has_non_ignorable_parser_errors = true; - *self.can_reset.borrow_mut() = false; - self.emitter.emit_diagnostic(db); - } -} - -impl Emitter for SilentOnIgnoredFilesEmitter { - fn source_map(&self) -> Option<&Lrc<SourceMap>> { - None - } - - fn emit_diagnostic(&mut self, db: &Diagnostic) { - if db.level == DiagnosticLevel::Fatal { - return self.handle_non_ignoreable_error(db); - } - if let Some(primary_span) = &db.span.primary_span() { - let file_name = self.source_map.span_to_filename(*primary_span); - if let rustc_span::FileName::Real(ref path) = file_name { - if self - .ignore_path_set - .is_match(&FileName::Real(path.to_path_buf())) - { - if !self.has_non_ignorable_parser_errors { - *self.can_reset.borrow_mut() = true; - } - return; - } - }; - } - self.handle_non_ignoreable_error(db); - } -} - -/// Emitter which discards every error. -struct SilentEmitter; - -impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc<SourceMap>> { - None - } - fn emit_diagnostic(&mut self, _db: &Diagnostic) {} -} - -fn silent_emitter() -> Box<dyn Emitter + Send> { - Box::new(SilentEmitter {}) -} - -fn make_parse_sess( - source_map: Rc<SourceMap>, - config: &Config, - ignore_path_set: Rc<IgnorePathSet>, - can_reset: Rc<RefCell<bool>>, -) -> ParseSess { - let supports_color = term::stderr().map_or(false, |term| term.supports_color()); - let color_cfg = if supports_color { - ColorConfig::Auto - } else { - ColorConfig::Never - }; - - let emitter = if config.hide_parse_errors() { - silent_emitter() - } else { - Box::new(EmitterWriter::stderr( - color_cfg, - Some(source_map.clone()), - false, - false, - None, - false, - )) - }; - let handler = Handler::with_emitter( - true, - None, - Box::new(SilentOnIgnoredFilesEmitter { - has_non_ignorable_parser_errors: false, - source_map: source_map.clone(), - emitter, - ignore_path_set, - can_reset, - }), - ); - - ParseSess::with_span_handler(handler, source_map) -} - -fn should_emit_verbose<F>(is_stdin: bool, config: &Config, f: F) +fn should_emit_verbose<F>(forbid_verbose_output: bool, config: &Config, f: F) where F: Fn(), { - if config.verbose() == Verbosity::Verbose && !is_stdin { + if config.verbose() == Verbosity::Verbose && !forbid_verbose_output { f(); } } - -#[cfg(test)] -mod tests { - use super::*; - - mod emitter { - use super::*; - use crate::config::IgnoreList; - use crate::is_nightly_channel; - use crate::utils::mk_sp; - use rustc_span::{BytePos, FileName as SourceMapFileName, MultiSpan, DUMMY_SP}; - use std::path::{Path, PathBuf}; - - struct TestEmitter { - num_emitted_errors: Rc<RefCell<u32>>, - } - - impl Emitter for TestEmitter { - fn source_map(&self) -> Option<&Lrc<SourceMap>> { - None - } - fn emit_diagnostic(&mut self, _db: &Diagnostic) { - *self.num_emitted_errors.borrow_mut() += 1; - } - } - - fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic { - Diagnostic { - level, - code: None, - message: vec![], - children: vec![], - suggestions: vec![], - span: span.unwrap_or_else(MultiSpan::new), - sort_span: DUMMY_SP, - } - } - - fn build_emitter( - num_emitted_errors: Rc<RefCell<u32>>, - can_reset: Rc<RefCell<bool>>, - source_map: Option<Rc<SourceMap>>, - ignore_list: Option<IgnoreList>, - ) -> SilentOnIgnoredFilesEmitter { - let emitter_writer = TestEmitter { num_emitted_errors }; - let source_map = - source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty()))); - let ignore_path_set = - Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap()); - SilentOnIgnoredFilesEmitter { - has_non_ignorable_parser_errors: false, - source_map, - emitter: Box::new(emitter_writer), - ignore_path_set, - can_reset, - } - } - - fn get_ignore_list(config: &str) -> IgnoreList { - Config::from_toml(config, Path::new("")).unwrap().ignore() - } - - #[test] - fn handles_fatal_parse_error_in_ignored_file() { - let num_emitted_errors = Rc::new(RefCell::new(0)); - let can_reset_errors = Rc::new(RefCell::new(false)); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let source = - String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source); - let mut emitter = build_emitter( - Rc::clone(&num_emitted_errors), - Rc::clone(&can_reset_errors), - Some(Rc::clone(&source_map)), - Some(ignore_list), - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span)); - emitter.emit_diagnostic(&fatal_diagnostic); - assert_eq!(*num_emitted_errors.borrow(), 1); - assert_eq!(*can_reset_errors.borrow(), false); - } - - #[test] - fn handles_recoverable_parse_error_in_ignored_file() { - if !is_nightly_channel!() { - return; - } - let num_emitted_errors = Rc::new(RefCell::new(0)); - let can_reset_errors = Rc::new(RefCell::new(false)); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source); - let mut emitter = build_emitter( - Rc::clone(&num_emitted_errors), - Rc::clone(&can_reset_errors), - Some(Rc::clone(&source_map)), - Some(ignore_list), - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); - emitter.emit_diagnostic(&non_fatal_diagnostic); - assert_eq!(*num_emitted_errors.borrow(), 0); - assert_eq!(*can_reset_errors.borrow(), true); - } - - #[test] - fn handles_recoverable_parse_error_in_non_ignored_file() { - if !is_nightly_channel!() { - return; - } - let num_emitted_errors = Rc::new(RefCell::new(0)); - let can_reset_errors = Rc::new(RefCell::new(false)); - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source); - let mut emitter = build_emitter( - Rc::clone(&num_emitted_errors), - Rc::clone(&can_reset_errors), - Some(Rc::clone(&source_map)), - None, - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); - emitter.emit_diagnostic(&non_fatal_diagnostic); - assert_eq!(*num_emitted_errors.borrow(), 1); - assert_eq!(*can_reset_errors.borrow(), false); - } - - #[test] - fn handles_mix_of_recoverable_parse_error() { - if !is_nightly_channel!() { - return; - } - let num_emitted_errors = Rc::new(RefCell::new(0)); - let can_reset_errors = Rc::new(RefCell::new(false)); - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let bar_source = String::from(r#"pub fn bar() { 1x; }"#); - let foo_source = String::from(r#"pub fn foo() { 1x; }"#); - let fatal_source = - String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map - .new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source); - source_map - .new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source); - source_map.new_source_file( - SourceMapFileName::Real(PathBuf::from("fatal.rs")), - fatal_source, - ); - let mut emitter = build_emitter( - Rc::clone(&num_emitted_errors), - Rc::clone(&can_reset_errors), - Some(Rc::clone(&source_map)), - Some(ignore_list), - ); - let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); - let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span)); - let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span)); - let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); - emitter.emit_diagnostic(&bar_diagnostic); - emitter.emit_diagnostic(&foo_diagnostic); - emitter.emit_diagnostic(&fatal_diagnostic); - assert_eq!(*num_emitted_errors.borrow(), 2); - assert_eq!(*can_reset_errors.borrow(), false); - } - } -} |
