1#![feature(rustc_private)]
2#![deny(rust_2018_idioms)]
3#![warn(unreachable_pub)]
4#![recursion_limit = "256"]
5#![allow(clippy::match_like_matches_macro)]
6#![allow(unreachable_pub)]
7
8extern crate rustc_ast;
10extern crate rustc_ast_pretty;
11extern crate rustc_data_structures;
12extern crate rustc_errors;
13extern crate rustc_expand;
14extern crate rustc_parse;
15extern crate rustc_session;
16extern crate rustc_span;
17extern crate thin_vec;
18
19#[allow(unused_extern_crates)]
22extern crate rustc_driver;
23
24use std::cell::RefCell;
25use std::cmp::min;
26use std::collections::HashMap;
27use std::fmt;
28use std::io::{self, Write};
29use std::mem;
30use std::panic;
31use std::path::PathBuf;
32use std::rc::Rc;
33
34use rustc_ast::ast;
35use rustc_span::symbol;
36use thiserror::Error;
37
38use crate::comment::LineClasses;
39use crate::emitter::Emitter;
40use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile};
41use crate::modules::ModuleResolutionError;
42use crate::parse::parser::DirectoryOwnership;
43use crate::shape::Indent;
44use crate::utils::indent_next_line;
45
46pub use crate::config::{
47 CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range,
48 StyleEdition, Verbosity, Version, load_config,
49};
50
51pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder};
52
53pub use crate::rustfmt_diff::{ModifiedChunk, ModifiedLines};
54
55#[macro_use]
56mod utils;
57
58macro_rules! static_regex {
59 ($re:literal) => {{
60 static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
61 RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
62 }};
63}
64
65mod attr;
66mod chains;
67mod closures;
68mod comment;
69pub(crate) mod config;
70mod coverage;
71mod emitter;
72mod expr;
73mod format_report_formatter;
74pub(crate) mod formatting;
75pub(crate) mod header;
76mod ignore_path;
77mod imports;
78mod items;
79mod lists;
80mod macros;
81mod matches;
82mod missed_spans;
83pub(crate) mod modules;
84mod overflow;
85mod pairs;
86mod parse;
87mod patterns;
88mod range;
89mod release_channel;
90mod reorder;
91mod rewrite;
92pub(crate) mod rustfmt_diff;
93mod shape;
94mod skip;
95mod sort;
96pub(crate) mod source_file;
97pub(crate) mod source_map;
98mod spanned;
99mod stmt;
100mod string;
101#[cfg(test)]
102mod test;
103mod types;
104mod vertical;
105pub(crate) mod visitor;
106
107#[derive(Error, Debug)]
110pub enum ErrorKind {
111 #[error(
113 "line formatted, but exceeded maximum width \
114 (maximum: {1} (see `max_width` option), found: {0})"
115 )]
116 LineOverflow(usize, usize),
117 #[error("left behind trailing whitespace")]
119 TrailingWhitespace,
120 #[error("`rustfmt_skip` is deprecated; use `rustfmt::skip`")]
122 DeprecatedAttr,
123 #[error("invalid attribute")]
125 BadAttr,
126 #[error("io error: {0}")]
128 IoError(io::Error),
129 #[error("{0}")]
131 ModuleResolutionError(#[from] ModuleResolutionError),
132 #[error("parse error")]
134 ParseError,
135 #[error("version mismatch")]
138 VersionMismatch,
139 #[error("not formatted because a comment would be lost")]
141 LostComment,
142 #[error("Invalid glob pattern found in ignore list: {0}")]
144 InvalidGlobPattern(ignore::Error),
145}
146
147impl ErrorKind {
148 fn is_comment(&self) -> bool {
149 matches!(self, ErrorKind::LostComment)
150 }
151}
152
153impl From<io::Error> for ErrorKind {
154 fn from(e: io::Error) -> ErrorKind {
155 ErrorKind::IoError(e)
156 }
157}
158
159#[derive(Debug)]
162struct FormattedSnippet {
163 snippet: String,
164 non_formatted_ranges: Vec<(usize, usize)>,
165}
166
167impl FormattedSnippet {
168 fn unwrap_code_block(&mut self) {
171 self.non_formatted_ranges
172 .iter_mut()
173 .for_each(|(low, high)| {
174 *low -= 1;
175 *high -= 1;
176 });
177 }
178
179 fn is_line_non_formatted(&self, n: usize) -> bool {
181 self.non_formatted_ranges
182 .iter()
183 .any(|(low, high)| *low <= n && n <= *high)
184 }
185}
186
187#[derive(Clone)]
191pub struct FormatReport {
192 internal: Rc<RefCell<(FormatErrorMap, ReportedErrors)>>,
194 non_formatted_ranges: Vec<(usize, usize)>,
195}
196
197impl FormatReport {
198 fn new() -> FormatReport {
199 FormatReport {
200 internal: Rc::new(RefCell::new((HashMap::new(), ReportedErrors::default()))),
201 non_formatted_ranges: Vec::new(),
202 }
203 }
204
205 fn add_non_formatted_ranges(&mut self, mut ranges: Vec<(usize, usize)>) {
206 self.non_formatted_ranges.append(&mut ranges);
207 }
208
209 fn append(&self, f: FileName, mut v: Vec<FormattingError>) {
210 self.track_errors(&v);
211 self.internal
212 .borrow_mut()
213 .0
214 .entry(f)
215 .and_modify(|fe| fe.append(&mut v))
216 .or_insert(v);
217 }
218
219 fn track_errors(&self, new_errors: &[FormattingError]) {
220 let errs = &mut self.internal.borrow_mut().1;
221 if !new_errors.is_empty() {
222 errs.has_formatting_errors = true;
223 }
224 if errs.has_operational_errors && errs.has_check_errors && errs.has_unformatted_code_errors
225 {
226 return;
227 }
228 for err in new_errors {
229 match err.kind {
230 ErrorKind::LineOverflow(..) => {
231 errs.has_operational_errors = true;
232 }
233 ErrorKind::TrailingWhitespace => {
234 errs.has_operational_errors = true;
235 errs.has_unformatted_code_errors = true;
236 }
237 ErrorKind::LostComment => {
238 errs.has_unformatted_code_errors = true;
239 }
240 ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => {
241 errs.has_check_errors = true;
242 }
243 _ => {}
244 }
245 }
246 }
247
248 fn add_diff(&mut self) {
249 self.internal.borrow_mut().1.has_diff = true;
250 }
251
252 fn add_macro_format_failure(&mut self) {
253 self.internal.borrow_mut().1.has_macro_format_failure = true;
254 }
255
256 fn add_parsing_error(&mut self) {
257 self.internal.borrow_mut().1.has_parsing_errors = true;
258 }
259
260 fn warning_count(&self) -> usize {
261 self.internal
262 .borrow()
263 .0
264 .values()
265 .map(|errors| errors.len())
266 .sum()
267 }
268
269 pub fn has_warnings(&self) -> bool {
271 self.internal.borrow().1.has_formatting_errors
272 }
273
274 #[deprecated(note = "Use FormatReportFormatter with colors enabled instead")]
277 pub fn fancy_print(
278 &self,
279 mut t: Box<dyn term::Terminal<Output = io::Stderr>>,
280 ) -> Result<(), term::Error> {
281 writeln!(
282 t,
283 "{}",
284 FormatReportFormatterBuilder::new(self)
285 .enable_colors(true)
286 .build()
287 )?;
288 Ok(())
289 }
290}
291
292impl fmt::Display for FormatReport {
296 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
298 write!(fmt, "{}", FormatReportFormatterBuilder::new(self).build())?;
299 Ok(())
300 }
301}
302
303fn format_snippet(snippet: &str, config: &Config, is_macro_def: bool) -> Option<FormattedSnippet> {
306 let mut config = config.clone();
307 panic::catch_unwind(|| {
308 let mut out: Vec<u8> = Vec::with_capacity(snippet.len() * 2);
309 config.set().emit_mode(config::EmitMode::Stdout);
310 config.set().verbose(Verbosity::Quiet);
311 config.set().show_parse_errors(false);
312 if is_macro_def {
313 config.set().error_on_unformatted(true);
314 }
315
316 let (formatting_error, result) = {
317 let input = Input::Text(snippet.into());
318 let mut session = Session::new(config, Some(&mut out));
319 let result = session.format_input_inner(input, is_macro_def);
320 (
321 session.errors.has_macro_format_failure
322 || session.out.as_ref().unwrap().is_empty() && !snippet.is_empty()
323 || result.is_err()
324 || (is_macro_def && session.has_unformatted_code_errors()),
325 result,
326 )
327 };
328 if formatting_error {
329 None
330 } else {
331 String::from_utf8(out).ok().map(|snippet| FormattedSnippet {
332 snippet,
333 non_formatted_ranges: result.unwrap().non_formatted_ranges,
334 })
335 }
336 })
337 .ok()?
340}
341
342fn format_code_block(
347 code_snippet: &str,
348 config: &Config,
349 is_macro_def: bool,
350) -> Option<FormattedSnippet> {
351 const FN_MAIN_PREFIX: &str = "fn main() {\n";
352
353 fn enclose_in_main_block(s: &str, config: &Config) -> String {
354 let indent = Indent::from_width(config, config.tab_spaces());
355 let mut result = String::with_capacity(s.len() * 2);
356 result.push_str(FN_MAIN_PREFIX);
357 let mut need_indent = true;
358 for (kind, line) in LineClasses::new(s) {
359 if need_indent {
360 result.push_str(&indent.to_string(config));
361 }
362 result.push_str(&line);
363 result.push('\n');
364 need_indent = indent_next_line(kind, &line, config);
365 }
366 result.push('}');
367 result
368 }
369
370 let snippet = enclose_in_main_block(code_snippet, config);
372 let mut result = String::with_capacity(snippet.len());
373 let mut is_first = true;
374
375 let mut config_with_unix_newline = config.clone();
380 config_with_unix_newline
381 .set()
382 .newline_style(NewlineStyle::Unix);
383 let mut formatted = format_snippet(&snippet, &config_with_unix_newline, is_macro_def)?;
384 formatted.unwrap_code_block();
386
387 let block_len = formatted
390 .snippet
391 .rfind('}')
392 .unwrap_or_else(|| formatted.snippet.len());
393
394 let block_start = min(FN_MAIN_PREFIX.len(), block_len);
398
399 let mut is_indented = true;
400 let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config);
401 for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) {
402 if !is_first {
403 result.push('\n');
404 } else {
405 is_first = false;
406 }
407 let trimmed_line = if !is_indented {
408 line
409 } else if line.len() > config.max_width() {
410 return None;
415 } else if line.len() > indent_str.len() {
416 if line.starts_with(indent_str.as_ref()) {
418 let offset = if config.hard_tabs() {
419 1
420 } else {
421 config.tab_spaces()
422 };
423 &line[offset..]
424 } else {
425 line
426 }
427 } else {
428 line
429 };
430 result.push_str(trimmed_line);
431 is_indented = indent_next_line(kind, line, config);
432 }
433 Some(FormattedSnippet {
434 snippet: result,
435 non_formatted_ranges: formatted.non_formatted_ranges,
436 })
437}
438
439pub struct Session<'b, T: Write> {
441 pub config: Config,
442 pub out: Option<&'b mut T>,
443 pub(crate) errors: ReportedErrors,
444 source_file: SourceFile,
445 emitter: Box<dyn Emitter + 'b>,
446}
447
448impl<'b, T: Write + 'b> Session<'b, T> {
449 pub fn new(config: Config, mut out: Option<&'b mut T>) -> Session<'b, T> {
450 let emitter = create_emitter(&config);
451
452 if let Some(ref mut out) = out {
453 let _ = emitter.emit_header(out);
454 }
455
456 Session {
457 config,
458 out,
459 emitter,
460 errors: ReportedErrors::default(),
461 source_file: SourceFile::new(),
462 }
463 }
464
465 pub fn format(&mut self, input: Input) -> Result<FormatReport, ErrorKind> {
468 self.format_input_inner(input, false)
469 }
470
471 pub fn override_config<F, U>(&mut self, mut config: Config, f: F) -> U
472 where
473 F: FnOnce(&mut Session<'b, T>) -> U,
474 {
475 mem::swap(&mut config, &mut self.config);
476 let result = f(self);
477 mem::swap(&mut config, &mut self.config);
478 result
479 }
480
481 pub fn add_operational_error(&mut self) {
482 self.errors.has_operational_errors = true;
483 }
484
485 pub fn has_operational_errors(&self) -> bool {
486 self.errors.has_operational_errors
487 }
488
489 pub fn has_parsing_errors(&self) -> bool {
490 self.errors.has_parsing_errors
491 }
492
493 pub fn has_formatting_errors(&self) -> bool {
494 self.errors.has_formatting_errors
495 }
496
497 pub fn has_check_errors(&self) -> bool {
498 self.errors.has_check_errors
499 }
500
501 pub fn has_diff(&self) -> bool {
502 self.errors.has_diff
503 }
504
505 pub fn has_unformatted_code_errors(&self) -> bool {
506 self.errors.has_unformatted_code_errors
507 }
508
509 pub fn has_no_errors(&self) -> bool {
510 !(self.has_operational_errors()
511 || self.has_parsing_errors()
512 || self.has_formatting_errors()
513 || self.has_check_errors()
514 || self.has_diff()
515 || self.has_unformatted_code_errors()
516 || self.errors.has_macro_format_failure)
517 }
518}
519
520pub(crate) fn create_emitter<'a>(config: &Config) -> Box<dyn Emitter + 'a> {
521 match config.emit_mode() {
522 EmitMode::Files if config.make_backup() => {
523 Box::new(emitter::FilesWithBackupEmitter::default())
524 }
525 EmitMode::Files => Box::new(emitter::FilesEmitter::new(
526 config.print_misformatted_file_names(),
527 )),
528 EmitMode::Stdout | EmitMode::Coverage => {
529 Box::new(emitter::StdoutEmitter::new(config.verbose()))
530 }
531 EmitMode::Json => Box::new(emitter::JsonEmitter::default()),
532 EmitMode::ModifiedLines => Box::new(emitter::ModifiedLinesEmitter::default()),
533 EmitMode::Checkstyle => Box::new(emitter::CheckstyleEmitter::default()),
534 EmitMode::Diff => Box::new(emitter::DiffEmitter::new(config.clone())),
535 }
536}
537
538impl<'b, T: Write + 'b> Drop for Session<'b, T> {
539 fn drop(&mut self) {
540 if let Some(ref mut out) = self.out {
541 let _ = self.emitter.emit_footer(out);
542 }
543 }
544}
545
546#[derive(Debug)]
547pub enum Input {
548 File(PathBuf),
549 Text(String),
550}
551
552impl Input {
553 fn file_name(&self) -> FileName {
554 match *self {
555 Input::File(ref file) => FileName::Real(file.clone()),
556 Input::Text(..) => FileName::Stdin,
557 }
558 }
559
560 fn to_directory_ownership(&self) -> Option<DirectoryOwnership> {
561 match self {
562 Input::File(ref file) => {
563 let file_stem = file.file_stem()?;
566 if file.parent()?.to_path_buf().join(file_stem).is_dir() {
567 Some(DirectoryOwnership::Owned {
568 relative: file_stem.to_str().map(symbol::Ident::from_str),
569 })
570 } else {
571 None
572 }
573 }
574 _ => None,
575 }
576 }
577}
578
579#[cfg(test)]
580mod unit_tests {
581 use super::*;
582
583 #[test]
584 fn test_no_panic_on_format_snippet_and_format_code_block() {
585 let snippet = "let";
588 assert!(format_snippet(snippet, &Config::default(), false).is_none());
589 assert!(format_code_block(snippet, &Config::default(), false).is_none());
590 }
591
592 fn test_format_inner<F>(formatter: F, input: &str, expected: &str) -> bool
593 where
594 F: Fn(&str, &Config, bool) -> Option<FormattedSnippet>,
595 {
596 let output = formatter(input, &Config::default(), false);
597 output.is_some() && output.unwrap().snippet == expected
598 }
599
600 #[test]
601 fn test_format_snippet() {
602 let snippet = "fn main() { println!(\"hello, world\"); }";
603 #[cfg(not(windows))]
604 let expected = "fn main() {\n \
605 println!(\"hello, world\");\n\
606 }\n";
607 #[cfg(windows)]
608 let expected = "fn main() {\r\n \
609 println!(\"hello, world\");\r\n\
610 }\r\n";
611 assert!(test_format_inner(format_snippet, snippet, expected));
612 }
613
614 #[test]
615 fn test_format_code_block_fail() {
616 #[rustfmt::skip]
617 let code_block = "this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(x, y, z);";
618 assert!(format_code_block(code_block, &Config::default(), false).is_none());
619 }
620
621 #[test]
622 fn test_format_code_block() {
623 let code_block = "let x=3;";
625 let expected = "let x = 3;";
626 assert!(test_format_inner(format_code_block, code_block, expected));
627
628 let code_block =
630"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
631(
632chain_indent(context, shape.add_offset(parent_rewrite.len())),
633context.config.indent_style() == IndentStyle::Visual || is_small_parent,
634)
635} else if is_block_expr(context, &parent, &parent_rewrite) {
636match context.config.indent_style() {
637// Try to put the first child on the same line with parent's last line
638IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
639// The parent is a block, so align the rest of the chain with the closing
640// brace.
641IndentStyle::Visual => (parent_shape, false),
642}
643} else {
644(
645chain_indent(context, shape.add_offset(parent_rewrite.len())),
646false,
647)
648};
649";
650 let expected =
651"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
652 (
653 chain_indent(context, shape.add_offset(parent_rewrite.len())),
654 context.config.indent_style() == IndentStyle::Visual || is_small_parent,
655 )
656} else if is_block_expr(context, &parent, &parent_rewrite) {
657 match context.config.indent_style() {
658 // Try to put the first child on the same line with parent's last line
659 IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
660 // The parent is a block, so align the rest of the chain with the closing
661 // brace.
662 IndentStyle::Visual => (parent_shape, false),
663 }
664} else {
665 (
666 chain_indent(context, shape.add_offset(parent_rewrite.len())),
667 false,
668 )
669};";
670 assert!(test_format_inner(format_code_block, code_block, expected));
671 }
672}