X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fformatting.rs;h=b69ecdc5cb8ae2985065bf54c5872d0528f1ac69;hb=d13020cd3168483449d22eb1f1246e2bb8ae197d;hp=d940c35f1c734532b416fef590c2fa5a208f32e9;hpb=e2b9c66cc9d3228339d07cb5410f6cabdac97de8;p=rust.git diff --git a/src/formatting.rs b/src/formatting.rs index d940c35f1c7..b69ecdc5cb8 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,37 +2,39 @@ 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 syntax::ast; -use syntax::errors::emitter::{ColorConfig, EmitterWriter}; -use syntax::errors::Handler; -use syntax::parse::{self, ParseSess}; -use syntax::source_map::{FilePathMapping, SourceMap, Span}; +use rustc_ast::ast; +use rustc_span::Span; -use comment::{CharClasses, FullCodeCharKind}; -use config::{Config, FileName, Verbosity}; -use issues::BadIssueSeeker; -use visitor::{FmtVisitor, SnippetProvider}; -use {modules, source_file, ErrorKind, FormatReport, Input, Session}; +use self::newline_style::apply_newline_style; +use crate::comment::{CharClasses, FullCodeCharKind}; +use crate::config::{Config, FileName, Verbosity}; +use crate::issues::BadIssueSeeker; +use crate::modules::Module; +use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; +use crate::syntux::session::ParseSess; +use crate::utils::count_newlines; +use crate::visitor::FmtVisitor; +use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; + +mod newline_style; // A map of the files of a crate, with their new content pub(crate) type SourceFile = Vec; pub(crate) type FileRecord = (FileName, String); impl<'b, T: Write + 'b> Session<'b, T> { - pub(crate) fn format_input_inner(&mut self, input: Input) -> Result { + pub(crate) fn format_input_inner( + &mut self, + input: Input, + is_macro_def: bool, + ) -> Result { if !self.config.version_meets_requirement() { return Err(ErrorKind::VersionMismatch); } - syntax::with_globals(|| { - syntax_pos::hygiene::set_default_edition( - self.config.edition().to_libsyntax_pos_edition(), - ); - + rustc_span::with_session_globals(self.config.edition().into(), || { if self.config.disable_all_formatting() { // When the input is from stdin, echo back the input. if let Input::Text(ref buf) = input { @@ -44,14 +46,10 @@ pub(crate) fn format_input_inner(&mut self, input: Input) -> Result( input: Input, config: &Config, handler: &mut T, + is_macro_def: bool, ) -> Result { let mut timer = Timer::start(); let main_file = input.file_name(); let input_is_stdin = main_file == FileName::Stdin; + let parse_session = ParseSess::new(config)?; + if config.skip_children() && parse_session.ignore_file(&main_file) { + return Ok(FormatReport::new()); + } + // Parse the crate. - let source_map = Rc::new(SourceMap::new(FilePathMapping::empty())); - let mut parse_session = make_parse_sess(source_map.clone(), config); let mut report = FormatReport::new(); - let krate = parse_crate(input, &parse_session, config, &mut report)?; + let directory_ownership = input.to_directory_ownership(); + let krate = match Parser::parse_crate(input, &parse_session) { + Ok(krate) => krate, + // Surface parse error via Session (errors are merged there from report) + 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); + } + }; + + let mut context = FormatContext::new(&krate, report, parse_session, config, handler); + let files = modules::ModResolver::new( + &context.parse_session, + directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaBlock), + !input_is_stdin && !config.skip_children(), + ) + .visit_crate(&krate)?; + timer = timer.done_parsing(); // Suppress error output if we have to do any further parsing. - let silent_emitter = silent_emitter(source_map); - parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter); - - let mut context = FormatContext::new(&krate, report, parse_session, config, handler); + context.parse_session.set_silent_emitter(); - let files = modules::list_files(&krate, context.parse_session.source_map())?; for (path, module) in files { - if (config.skip_children() && path != main_file) || config.ignore().skip_file(&path) { + let should_ignore = !input_is_stdin && context.ignore_file(&path); + if (config.skip_children() && path != main_file) || should_ignore { continue; } should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); - let is_root = path == main_file; - context.format_file(path, module, is_root)?; + context.format_file(path, &module, is_macro_def)?; } timer = timer.done_formatting(); @@ -106,7 +125,7 @@ fn format_project( // Used for formatting files. #[derive(new)] -struct FormatContext<'a, T: FormatHandler + 'a> { +struct FormatContext<'a, T: FormatHandler> { krate: &'a ast::Crate, report: FormatReport, parse_session: ParseSess, @@ -115,44 +134,35 @@ struct FormatContext<'a, T: FormatHandler + 'a> { } 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, path: FileName, - module: &ast::Mod, - is_root: bool, + module: &Module<'_>, + is_macro_def: 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, big_snippet); - let mut visitor = FmtVisitor::from_source_map( + let snippet_provider = self.parse_session.snippet_provider(module.span); + let mut visitor = FmtVisitor::from_parse_sess( &self.parse_session, &self.config, &snippet_provider, self.report.clone(), ); - - // Format inner attributes if available. - if !self.krate.attrs.is_empty() && is_root { - visitor.skip_empty_lines(source_file.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); - } - } else { - visitor.last_pos = source_file.start_pos; - visitor.skip_empty_lines(source_file.end_pos); - visitor.format_separate_mod(module, &*source_file); - }; + visitor.skip_context.update_with_attrs(&self.krate.attrs); + visitor.is_macro_def = is_macro_def; + 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!( visitor.line_number, - ::utils::count_newlines(&visitor.buffer) + count_newlines(&visitor.buffer), + "failed in format_file visitor.buffer:\n {:?}", + &visitor.buffer ); // For some reason, the source_map does not include terminating @@ -162,20 +172,29 @@ fn format_file( format_lines( &mut visitor.buffer, &path, - &visitor.skipped_range, + &visitor.skipped_range.borrow(), &self.config, &self.report, ); - self.config - .newline_style() - .apply(&mut visitor.buffer, &big_snippet); + + apply_newline_style( + self.config.newline_style(), + &mut visitor.buffer, + snippet_provider.entire_snippet(), + ); if visitor.macro_rewrite_failure { self.report.add_macro_format_failure(); } + self.report + .add_non_formatted_ranges(visitor.skipped_range.borrow().clone()); - self.handler - .handle_formatted_file(path, visitor.buffer.to_owned(), &mut self.report) + self.handler.handle_formatted_file( + &self.parse_session, + path, + visitor.buffer.to_owned(), + &mut self.report, + ) } } @@ -183,6 +202,7 @@ fn format_file( trait FormatHandler { fn handle_formatted_file( &mut self, + parse_session: &ParseSess, path: FileName, result: String, report: &mut FormatReport, @@ -193,13 +213,21 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { // Called for each formatted file. fn handle_formatted_file( &mut self, + 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(&result, &path, out, &self.config) { - Ok(b) if b => report.add_diff(), + match source_file::write_file( + Some(parse_session), + &path, + &result, + out, + &mut *self.emitter, + self.config.newline_style(), + ) { + Ok(ref result) if result.has_diff => report.add_diff(), Err(e) => { // Create a new error with path_str to help users see which files failed let err_msg = format!("{}: {}", path, e); @@ -225,35 +253,26 @@ 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(|l| l.into_owned()) - }) - .unwrap_or_else(String::new), + line_buffer: parse_sess.span_to_first_line_string(span), } } - pub(crate) fn msg_prefix(&self) -> &str { + pub(crate) fn is_internal(&self) -> bool { match self.kind { ErrorKind::LineOverflow(..) | ErrorKind::TrailingWhitespace | ErrorKind::IoError(_) | ErrorKind::ParseError - | ErrorKind::LostComment => "internal error:", - ErrorKind::LicenseCheck | ErrorKind::BadAttr | ErrorKind::VersionMismatch => "error:", - ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => "warning:", + | ErrorKind::LostComment => true, + _ => false, } } @@ -274,7 +293,8 @@ pub(crate) fn format_len(&self) -> (usize, usize) { | ErrorKind::DeprecatedAttr | ErrorKind::BadIssue(_) | ErrorKind::BadAttr - | ErrorKind::LostComment => { + | ErrorKind::LostComment + | ErrorKind::LicenseCheck => { let trailing_ws_start = self .line_buffer .rfind(|c: char| !c.is_whitespace()) @@ -292,9 +312,9 @@ pub(crate) fn format_len(&self) -> (usize, usize) { pub(crate) type FormatErrorMap = HashMap>; -#[derive(Default, Debug)] +#[derive(Default, Debug, PartialEq)] pub(crate) struct ReportedErrors { - // Encountered e.g. an IO error. + // Encountered e.g., an IO error. pub(crate) has_operational_errors: bool, // Failed to reformat code because of parsing errors. @@ -311,39 +331,24 @@ pub(crate) struct ReportedErrors { /// Formatted code differs from existing code (--check only). pub(crate) has_diff: bool, + + /// Formatted code missed something, like lost comments or extra trailing space + pub(crate) has_unformatted_code_errors: bool, } impl ReportedErrors { /// Combine two summaries together. - pub fn add(&mut self, other: &ReportedErrors) { + pub(crate) fn add(&mut self, other: &ReportedErrors) { self.has_operational_errors |= other.has_operational_errors; self.has_parsing_errors |= other.has_parsing_errors; self.has_formatting_errors |= other.has_formatting_errors; self.has_macro_format_failure |= other.has_macro_format_failure; self.has_check_errors |= other.has_check_errors; self.has_diff |= other.has_diff; + self.has_unformatted_code_errors |= other.has_unformatted_code_errors; } } -/// A single span of changed lines, with 0 or more removed lines -/// and a vector of 0 or more inserted lines. -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct ModifiedChunk { - /// The first to be removed from the original text - pub line_number_orig: u32, - /// The number of lines which have been replaced - pub lines_removed: u32, - /// The new lines - pub lines: Vec, -} - -/// Set of changed sections of a file. -#[derive(Debug, PartialEq, Eq)] -pub(crate) struct ModifiedLines { - /// The set of changed chunks. - pub chunks: Vec, -} - #[derive(Clone, Copy, Debug)] enum Timer { Disabled, @@ -408,7 +413,7 @@ fn duration_to_f32(d: Duration) -> f32 { } // Formatting done on a char by char or line by line basis. -// FIXME(#20) other stuff for parity with make tidy +// FIXME(#20): other stuff for parity with make tidy. fn format_lines( text: &mut String, name: &FileName, @@ -439,8 +444,7 @@ struct FormatLines<'a> { errors: Vec, issue_seeker: BadIssueSeeker, line_buffer: String, - // true if the current line contains a string literal. - is_string: bool, + current_line_contains_string_literal: bool, format_line: bool, allow_issue_seek: bool, config: &'a Config, @@ -464,7 +468,7 @@ fn new( allow_issue_seek: !issue_seeker.is_disabled(), issue_seeker, line_buffer: String::with_capacity(config.max_width() * 2), - is_string: false, + current_line_contains_string_literal: false, format_line: config.file_lines().contains_line(name, 1), config, } @@ -528,7 +532,8 @@ fn new_line(&mut self, kind: FullCodeCharKind) { && !self.is_skipped_line() && self.should_report_error(kind, &error_kind) { - self.push_err(error_kind, kind.is_comment(), self.is_string); + let is_string = self.current_line_contains_string_literal; + self.push_err(error_kind, kind.is_comment(), is_string); } } @@ -541,7 +546,7 @@ fn new_line(&mut self, kind: FullCodeCharKind) { self.newline_count += 1; self.last_was_space = false; self.line_buffer.clear(); - self.is_string = false; + self.current_line_contains_string_literal = false; } fn char(&mut self, c: char, kind: FullCodeCharKind) { @@ -554,7 +559,7 @@ fn char(&mut self, c: char, kind: FullCodeCharKind) { self.last_was_space = c.is_whitespace(); self.line_buffer.push(c); if kind.is_string() { - self.is_string = true; + self.current_line_contains_string_literal = true; } } @@ -569,12 +574,14 @@ fn push_err(&mut self, kind: ErrorKind, is_comment: bool, is_string: bool) { } fn should_report_error(&self, char_kind: FullCodeCharKind, error_kind: &ErrorKind) -> bool { - let allow_error_report = - if char_kind.is_comment() || self.is_string || error_kind.is_comment() { - self.config.error_on_unformatted() - } else { - true - }; + let allow_error_report = if char_kind.is_comment() + || self.current_line_contains_string_literal + || error_kind.is_comment() + { + self.config.error_on_unformatted() + } else { + true + }; match error_kind { ErrorKind::LineOverflow(..) => { @@ -585,7 +592,7 @@ fn should_report_error(&self, char_kind: FullCodeCharKind, error_kind: &ErrorKin } } - /// Returns true if the line with the given line number was skipped by `#[rustfmt::skip]`. + /// Returns `true` if the line with the given line number was skipped by `#[rustfmt::skip]`. fn is_skipped_line(&self) -> bool { self.skipped_range .iter() @@ -593,83 +600,11 @@ fn is_skipped_line(&self) -> bool { } } -fn parse_crate( - input: Input, - parse_session: &ParseSess, - config: &Config, - report: &mut FormatReport, -) -> Result { - let input_is_stdin = input.is_text(); - - let mut parser = match input { - Input::File(file) => parse::new_parser_from_file(parse_session, &file), - Input::Text(text) => parse::new_parser_from_source_str( - parse_session, - syntax::source_map::FileName::Custom("stdin".to_owned()), - text, - ), - }; - - parser.cfg_mods = false; - if config.skip_children() { - parser.recurse_into_file_modules = false; - } - - let mut parser = AssertUnwindSafe(parser); - let result = catch_unwind(move || parser.0.parse_crate_mod()); - - match result { - Ok(Ok(c)) => { - if !parse_session.span_diagnostic.has_errors() { - return Ok(c); - } - } - Ok(Err(mut e)) => e.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) -} - -fn silent_emitter(source_map: Rc) -> Box { - Box::new(EmitterWriter::new( - Box::new(Vec::new()), - Some(source_map), - false, - false, - )) -} - -fn make_parse_sess(source_map: Rc, config: &Config) -> ParseSess { - let tty_handler = if config.hide_parse_errors() { - let silent_emitter = silent_emitter(source_map.clone()); - Handler::with_emitter(true, false, silent_emitter) - } else { - let supports_color = term::stderr().map_or(false, |term| term.supports_color()); - let color_cfg = if supports_color { - ColorConfig::Auto - } else { - ColorConfig::Never - }; - Handler::with_tty_emitter(color_cfg, true, false, Some(source_map.clone())) - }; - - ParseSess::with_span_handler(tty_handler, source_map) -} - -fn should_emit_verbose(is_stdin: bool, config: &Config, f: F) +fn should_emit_verbose(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(); } }