X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fformatting.rs;h=b69ecdc5cb8ae2985065bf54c5872d0528f1ac69;hb=d13020cd3168483449d22eb1f1246e2bb8ae197d;hp=ad80e7e1aef00ae47db95a25f263a115147efe68;hpb=4352681d624bcbfb9d5aa9d2f5057091622402a6;p=rust.git diff --git a/src/formatting.rs b/src/formatting.rs index ad80e7e1aef..b69ecdc5cb8 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -2,38 +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, Emitter}; -use syntax::errors::{DiagnosticBuilder, Handler}; -use syntax::parse::{self, ParseSess}; -use syntax::source_map::{FilePathMapping, SourceMap, Span, DUMMY_SP}; +use rustc_ast::ast; +use rustc_span::Span; +use self::newline_style::apply_newline_style; use crate::comment::{CharClasses, FullCodeCharKind}; use crate::config::{Config, FileName, Verbosity}; use crate::issues::BadIssueSeeker; -use crate::utils::{count_newlines, get_skip_macro_names}; -use crate::visitor::{FmtVisitor, SnippetProvider}; +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 { @@ -45,7 +46,7 @@ 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 directory_ownership = input.to_directory_ownership(); - let krate = match parse_crate( - input, - &parse_session, - config, - &mut report, - 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(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); let mut context = FormatContext::new(&krate, report, parse_session, config, handler); - let files = modules::ModResolver::new( - context.parse_session.source_map(), - directory_ownership.unwrap_or(parse::DirectoryOwnership::UnownedViaMod(false)), - input_is_stdin, + &context.parse_session, + directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaBlock), + !input_is_stdin && !config.skip_children(), ) - .visit_crate(&krate) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - for (path, (module, _)) in files { - if (config.skip_children() && path != main_file) || config.ignore().skip_file(&path) { + .visit_crate(&krate)?; + + timer = timer.done_parsing(); + + // Suppress error output if we have to do any further parsing. + context.parse_session.set_silent_emitter(); + + for (path, module) in files { + 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(); @@ -130,46 +134,36 @@ 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, 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(), ); - visitor - .skip_macro_names - .borrow_mut() - .append(&mut get_skip_macro_names(&self.krate.attrs)); - - // 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); - }; - - debug_assert_eq!(visitor.line_number, count_newlines(&visitor.buffer)); + 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, + count_newlines(&visitor.buffer), + "failed in format_file visitor.buffer:\n {:?}", + &visitor.buffer + ); // For some reason, the source_map does not include terminating // newlines so we must add one on for each file. This is sad. @@ -178,22 +172,25 @@ 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.clone()); + .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, @@ -205,7 +202,7 @@ fn format_file( trait FormatHandler { fn handle_formatted_file( &mut self, - source_map: &SourceMap, + parse_session: &ParseSess, path: FileName, result: String, report: &mut FormatReport, @@ -216,14 +213,21 @@ 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), &path, &result, out, &self.config) { - Ok(has_diff) if has_diff => 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); @@ -249,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(std::borrow::Cow::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, } } @@ -298,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()) @@ -335,17 +331,21 @@ 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; } } @@ -444,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, @@ -469,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, } @@ -533,7 +532,7 @@ fn new_line(&mut self, kind: FullCodeCharKind) { && !self.is_skipped_line() && self.should_report_error(kind, &error_kind) { - let is_string = self.is_string; + let is_string = self.current_line_contains_string_literal; self.push_err(error_kind, kind.is_comment(), is_string); } } @@ -547,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) { @@ -560,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; } } @@ -575,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(..) => { @@ -599,108 +600,11 @@ fn is_skipped_line(&self) -> bool { } } -fn parse_crate( - input: Input, - parse_session: &ParseSess, - config: &Config, - report: &mut FormatReport, - directory_ownership: Option, -) -> Result { - 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 { - parse::new_sub_parser_from_file(parse_session, file, dir_own, None, DUMMY_SP) - } else { - parse::new_parser_from_file(parse_session, file) - }) - } - Input::Text(text) => parse::maybe_new_parser_from_source_str( - parse_session, - syntax::source_map::FileName::Custom("stdin".to_owned()), - text, - ) - .map(|mut parser| { - parser.recurse_into_file_modules = false; - parser - }) - .map_err(|diags| { - diags - .into_iter() - .map(|d| DiagnosticBuilder::new_diagnostic(&parse_session.span_diagnostic, d)) - .collect::>() - }), - }; - - 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(db) => Ok(Err(db)), - }; - - match result { - Ok(Ok(c)) => { - if !parse_session.span_diagnostic.has_errors() { - 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) -} - -/// Emitter which discards every error. -struct SilentEmitter; - -impl Emitter for SilentEmitter { - fn emit(&mut self, _db: &DiagnosticBuilder<'_>) {} -} - -fn silent_emitter() -> Box { - Box::new(SilentEmitter {}) -} - -fn make_parse_sess(source_map: Rc, config: &Config) -> ParseSess { - let tty_handler = if config.hide_parse_errors() { - let silent_emitter = silent_emitter(); - Handler::with_emitter(true, None, 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, None, 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(); } }