]> git.lizzy.rs Git - rust.git/blobdiff - src/formatting.rs
Rollup merge of #86274 - alexander-melentyev:spaces, r=bjorn3
[rust.git] / src / formatting.rs
index ad80e7e1aef00ae47db95a25f263a115147efe68..b69ecdc5cb8ae2985065bf54c5872d0528f1ac69 100644 (file)
@@ -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<FileRecord>;
 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<FormatReport, ErrorKind> {
+    pub(crate) fn format_input_inner(
+        &mut self,
+        input: Input,
+        is_macro_def: bool,
+    ) -> Result<FormatReport, ErrorKind> {
         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<FormatReport
             }
 
             let config = &self.config.clone();
-            let format_result = format_project(input, config, self);
+            let format_result = format_project(input, config, self, is_macro_def);
 
             format_result.map(|report| {
                 self.errors.add(&report.internal.borrow().1);
@@ -60,51 +61,54 @@ fn format_project<T: FormatHandler>(
     input: Input,
     config: &Config,
     handler: &mut T,
+    is_macro_def: bool,
 ) -> Result<FormatReport, ErrorKind> {
     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<FormattingError>,
     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<parse::DirectoryOwnership>,
-) -> 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 {
-                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::<Vec<_>>()
-        }),
-    };
-
-    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<SilentEmitter> {
-    Box::new(SilentEmitter {})
-}
-
-fn make_parse_sess(source_map: Rc<SourceMap>, 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<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();
     }
 }