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;
75mod ignore_path;
76mod imports;
77mod items;
78mod lists;
79mod macros;
80mod matches;
81mod missed_spans;
82pub(crate) mod modules;
83mod overflow;
84mod pairs;
85mod parse;
86mod patterns;
87mod release_channel;
88mod reorder;
89mod rewrite;
90pub(crate) mod rustfmt_diff;
91mod shape;
92mod skip;
93mod sort;
94pub(crate) mod source_file;
95pub(crate) mod source_map;
96mod spanned;
97mod stmt;
98mod string;
99#[cfg(test)]
100mod test;
101mod types;
102mod vertical;
103pub(crate) mod visitor;
104
105#[derive(Error, Debug)]
108pub enum ErrorKind {
109 #[error(
111 "line formatted, but exceeded maximum width \
112 (maximum: {1} (see `max_width` option), found: {0})"
113 )]
114 LineOverflow(usize, usize),
115 #[error("left behind trailing whitespace")]
117 TrailingWhitespace,
118 #[error("`rustfmt_skip` is deprecated; use `rustfmt::skip`")]
120 DeprecatedAttr,
121 #[error("invalid attribute")]
123 BadAttr,
124 #[error("io error: {0}")]
126 IoError(io::Error),
127 #[error("{0}")]
129 ModuleResolutionError(#[from] ModuleResolutionError),
130 #[error("parse error")]
132 ParseError,
133 #[error("version mismatch")]
136 VersionMismatch,
137 #[error("not formatted because a comment would be lost")]
139 LostComment,
140 #[error("Invalid glob pattern found in ignore list: {0}")]
142 InvalidGlobPattern(ignore::Error),
143}
144
145impl ErrorKind {
146 fn is_comment(&self) -> bool {
147 matches!(self, ErrorKind::LostComment)
148 }
149}
150
151impl From<io::Error> for ErrorKind {
152 fn from(e: io::Error) -> ErrorKind {
153 ErrorKind::IoError(e)
154 }
155}
156
157#[derive(Debug)]
160struct FormattedSnippet {
161 snippet: String,
162 non_formatted_ranges: Vec<(usize, usize)>,
163}
164
165impl FormattedSnippet {
166 fn unwrap_code_block(&mut self) {
169 self.non_formatted_ranges
170 .iter_mut()
171 .for_each(|(low, high)| {
172 *low -= 1;
173 *high -= 1;
174 });
175 }
176
177 fn is_line_non_formatted(&self, n: usize) -> bool {
179 self.non_formatted_ranges
180 .iter()
181 .any(|(low, high)| *low <= n && n <= *high)
182 }
183}
184
185#[derive(Clone)]
189pub struct FormatReport {
190 internal: Rc<RefCell<(FormatErrorMap, ReportedErrors)>>,
192 non_formatted_ranges: Vec<(usize, usize)>,
193}
194
195impl FormatReport {
196 fn new() -> FormatReport {
197 FormatReport {
198 internal: Rc::new(RefCell::new((HashMap::new(), ReportedErrors::default()))),
199 non_formatted_ranges: Vec::new(),
200 }
201 }
202
203 fn add_non_formatted_ranges(&mut self, mut ranges: Vec<(usize, usize)>) {
204 self.non_formatted_ranges.append(&mut ranges);
205 }
206
207 fn append(&self, f: FileName, mut v: Vec<FormattingError>) {
208 self.track_errors(&v);
209 self.internal
210 .borrow_mut()
211 .0
212 .entry(f)
213 .and_modify(|fe| fe.append(&mut v))
214 .or_insert(v);
215 }
216
217 fn track_errors(&self, new_errors: &[FormattingError]) {
218 let errs = &mut self.internal.borrow_mut().1;
219 if !new_errors.is_empty() {
220 errs.has_formatting_errors = true;
221 }
222 if errs.has_operational_errors && errs.has_check_errors && errs.has_unformatted_code_errors
223 {
224 return;
225 }
226 for err in new_errors {
227 match err.kind {
228 ErrorKind::LineOverflow(..) => {
229 errs.has_operational_errors = true;
230 }
231 ErrorKind::TrailingWhitespace => {
232 errs.has_operational_errors = true;
233 errs.has_unformatted_code_errors = true;
234 }
235 ErrorKind::LostComment => {
236 errs.has_unformatted_code_errors = true;
237 }
238 ErrorKind::DeprecatedAttr | ErrorKind::BadAttr | ErrorKind::VersionMismatch => {
239 errs.has_check_errors = true;
240 }
241 _ => {}
242 }
243 }
244 }
245
246 fn add_diff(&mut self) {
247 self.internal.borrow_mut().1.has_diff = true;
248 }
249
250 fn add_macro_format_failure(&mut self) {
251 self.internal.borrow_mut().1.has_macro_format_failure = true;
252 }
253
254 fn add_parsing_error(&mut self) {
255 self.internal.borrow_mut().1.has_parsing_errors = true;
256 }
257
258 fn warning_count(&self) -> usize {
259 self.internal
260 .borrow()
261 .0
262 .values()
263 .map(|errors| errors.len())
264 .sum()
265 }
266
267 pub fn has_warnings(&self) -> bool {
269 self.internal.borrow().1.has_formatting_errors
270 }
271
272 #[deprecated(note = "Use FormatReportFormatter with colors enabled instead")]
275 pub fn fancy_print(
276 &self,
277 mut t: Box<dyn term::Terminal<Output = io::Stderr>>,
278 ) -> Result<(), term::Error> {
279 writeln!(
280 t,
281 "{}",
282 FormatReportFormatterBuilder::new(self)
283 .enable_colors(true)
284 .build()
285 )?;
286 Ok(())
287 }
288}
289
290impl fmt::Display for FormatReport {
294 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
296 write!(fmt, "{}", FormatReportFormatterBuilder::new(self).build())?;
297 Ok(())
298 }
299}
300
301fn format_snippet(snippet: &str, config: &Config, is_macro_def: bool) -> Option<FormattedSnippet> {
304 let mut config = config.clone();
305 panic::catch_unwind(|| {
306 let mut out: Vec<u8> = Vec::with_capacity(snippet.len() * 2);
307 config.set().emit_mode(config::EmitMode::Stdout);
308 config.set().verbose(Verbosity::Quiet);
309 config.set().show_parse_errors(false);
310 if is_macro_def {
311 config.set().error_on_unformatted(true);
312 }
313
314 let (formatting_error, result) = {
315 let input = Input::Text(snippet.into());
316 let mut session = Session::new(config, Some(&mut out));
317 let result = session.format_input_inner(input, is_macro_def);
318 (
319 session.errors.has_macro_format_failure
320 || session.out.as_ref().unwrap().is_empty() && !snippet.is_empty()
321 || result.is_err()
322 || (is_macro_def && session.has_unformatted_code_errors()),
323 result,
324 )
325 };
326 if formatting_error {
327 None
328 } else {
329 String::from_utf8(out).ok().map(|snippet| FormattedSnippet {
330 snippet,
331 non_formatted_ranges: result.unwrap().non_formatted_ranges,
332 })
333 }
334 })
335 .ok()?
338}
339
340fn format_code_block(
345 code_snippet: &str,
346 config: &Config,
347 is_macro_def: bool,
348) -> Option<FormattedSnippet> {
349 const FN_MAIN_PREFIX: &str = "fn main() {\n";
350
351 fn enclose_in_main_block(s: &str, config: &Config) -> String {
352 let indent = Indent::from_width(config, config.tab_spaces());
353 let mut result = String::with_capacity(s.len() * 2);
354 result.push_str(FN_MAIN_PREFIX);
355 let mut need_indent = true;
356 for (kind, line) in LineClasses::new(s) {
357 if need_indent {
358 result.push_str(&indent.to_string(config));
359 }
360 result.push_str(&line);
361 result.push('\n');
362 need_indent = indent_next_line(kind, &line, config);
363 }
364 result.push('}');
365 result
366 }
367
368 let snippet = enclose_in_main_block(code_snippet, config);
370 let mut result = String::with_capacity(snippet.len());
371 let mut is_first = true;
372
373 let mut config_with_unix_newline = config.clone();
378 config_with_unix_newline
379 .set()
380 .newline_style(NewlineStyle::Unix);
381 let mut formatted = format_snippet(&snippet, &config_with_unix_newline, is_macro_def)?;
382 formatted.unwrap_code_block();
384
385 let block_len = formatted
388 .snippet
389 .rfind('}')
390 .unwrap_or_else(|| formatted.snippet.len());
391
392 let block_start = min(FN_MAIN_PREFIX.len(), block_len);
396
397 let mut is_indented = true;
398 let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config);
399 for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) {
400 if !is_first {
401 result.push('\n');
402 } else {
403 is_first = false;
404 }
405 let trimmed_line = if !is_indented {
406 line
407 } else if line.len() > config.max_width() {
408 return None;
413 } else if line.len() > indent_str.len() {
414 if line.starts_with(indent_str.as_ref()) {
416 let offset = if config.hard_tabs() {
417 1
418 } else {
419 config.tab_spaces()
420 };
421 &line[offset..]
422 } else {
423 line
424 }
425 } else {
426 line
427 };
428 result.push_str(trimmed_line);
429 is_indented = indent_next_line(kind, line, config);
430 }
431 Some(FormattedSnippet {
432 snippet: result,
433 non_formatted_ranges: formatted.non_formatted_ranges,
434 })
435}
436
437pub struct Session<'b, T: Write> {
439 pub config: Config,
440 pub out: Option<&'b mut T>,
441 pub(crate) errors: ReportedErrors,
442 source_file: SourceFile,
443 emitter: Box<dyn Emitter + 'b>,
444}
445
446impl<'b, T: Write + 'b> Session<'b, T> {
447 pub fn new(config: Config, mut out: Option<&'b mut T>) -> Session<'b, T> {
448 let emitter = create_emitter(&config);
449
450 if let Some(ref mut out) = out {
451 let _ = emitter.emit_header(out);
452 }
453
454 Session {
455 config,
456 out,
457 emitter,
458 errors: ReportedErrors::default(),
459 source_file: SourceFile::new(),
460 }
461 }
462
463 pub fn format(&mut self, input: Input) -> Result<FormatReport, ErrorKind> {
466 self.format_input_inner(input, false)
467 }
468
469 pub fn override_config<F, U>(&mut self, mut config: Config, f: F) -> U
470 where
471 F: FnOnce(&mut Session<'b, T>) -> U,
472 {
473 mem::swap(&mut config, &mut self.config);
474 let result = f(self);
475 mem::swap(&mut config, &mut self.config);
476 result
477 }
478
479 pub fn add_operational_error(&mut self) {
480 self.errors.has_operational_errors = true;
481 }
482
483 pub fn has_operational_errors(&self) -> bool {
484 self.errors.has_operational_errors
485 }
486
487 pub fn has_parsing_errors(&self) -> bool {
488 self.errors.has_parsing_errors
489 }
490
491 pub fn has_formatting_errors(&self) -> bool {
492 self.errors.has_formatting_errors
493 }
494
495 pub fn has_check_errors(&self) -> bool {
496 self.errors.has_check_errors
497 }
498
499 pub fn has_diff(&self) -> bool {
500 self.errors.has_diff
501 }
502
503 pub fn has_unformatted_code_errors(&self) -> bool {
504 self.errors.has_unformatted_code_errors
505 }
506
507 pub fn has_no_errors(&self) -> bool {
508 !(self.has_operational_errors()
509 || self.has_parsing_errors()
510 || self.has_formatting_errors()
511 || self.has_check_errors()
512 || self.has_diff()
513 || self.has_unformatted_code_errors()
514 || self.errors.has_macro_format_failure)
515 }
516}
517
518pub(crate) fn create_emitter<'a>(config: &Config) -> Box<dyn Emitter + 'a> {
519 match config.emit_mode() {
520 EmitMode::Files if config.make_backup() => {
521 Box::new(emitter::FilesWithBackupEmitter::default())
522 }
523 EmitMode::Files => Box::new(emitter::FilesEmitter::new(
524 config.print_misformatted_file_names(),
525 )),
526 EmitMode::Stdout | EmitMode::Coverage => {
527 Box::new(emitter::StdoutEmitter::new(config.verbose()))
528 }
529 EmitMode::Json => Box::new(emitter::JsonEmitter::default()),
530 EmitMode::ModifiedLines => Box::new(emitter::ModifiedLinesEmitter::default()),
531 EmitMode::Checkstyle => Box::new(emitter::CheckstyleEmitter::default()),
532 EmitMode::Diff => Box::new(emitter::DiffEmitter::new(config.clone())),
533 }
534}
535
536impl<'b, T: Write + 'b> Drop for Session<'b, T> {
537 fn drop(&mut self) {
538 if let Some(ref mut out) = self.out {
539 let _ = self.emitter.emit_footer(out);
540 }
541 }
542}
543
544#[derive(Debug)]
545pub enum Input {
546 File(PathBuf),
547 Text(String),
548}
549
550impl Input {
551 fn file_name(&self) -> FileName {
552 match *self {
553 Input::File(ref file) => FileName::Real(file.clone()),
554 Input::Text(..) => FileName::Stdin,
555 }
556 }
557
558 fn to_directory_ownership(&self) -> Option<DirectoryOwnership> {
559 match self {
560 Input::File(ref file) => {
561 let file_stem = file.file_stem()?;
564 if file.parent()?.to_path_buf().join(file_stem).is_dir() {
565 Some(DirectoryOwnership::Owned {
566 relative: file_stem.to_str().map(symbol::Ident::from_str),
567 })
568 } else {
569 None
570 }
571 }
572 _ => None,
573 }
574 }
575}
576
577#[cfg(test)]
578mod unit_tests {
579 use super::*;
580
581 #[test]
582 fn test_no_panic_on_format_snippet_and_format_code_block() {
583 let snippet = "let";
586 assert!(format_snippet(snippet, &Config::default(), false).is_none());
587 assert!(format_code_block(snippet, &Config::default(), false).is_none());
588 }
589
590 fn test_format_inner<F>(formatter: F, input: &str, expected: &str) -> bool
591 where
592 F: Fn(&str, &Config, bool) -> Option<FormattedSnippet>,
593 {
594 let output = formatter(input, &Config::default(), false);
595 output.is_some() && output.unwrap().snippet == expected
596 }
597
598 #[test]
599 fn test_format_snippet() {
600 let snippet = "fn main() { println!(\"hello, world\"); }";
601 #[cfg(not(windows))]
602 let expected = "fn main() {\n \
603 println!(\"hello, world\");\n\
604 }\n";
605 #[cfg(windows)]
606 let expected = "fn main() {\r\n \
607 println!(\"hello, world\");\r\n\
608 }\r\n";
609 assert!(test_format_inner(format_snippet, snippet, expected));
610 }
611
612 #[test]
613 fn test_format_code_block_fail() {
614 #[rustfmt::skip]
615 let code_block = "this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(x, y, z);";
616 assert!(format_code_block(code_block, &Config::default(), false).is_none());
617 }
618
619 #[test]
620 fn test_format_code_block() {
621 let code_block = "let x=3;";
623 let expected = "let x = 3;";
624 assert!(test_format_inner(format_code_block, code_block, expected));
625
626 let code_block =
628"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
629(
630chain_indent(context, shape.add_offset(parent_rewrite.len())),
631context.config.indent_style() == IndentStyle::Visual || is_small_parent,
632)
633} else if is_block_expr(context, &parent, &parent_rewrite) {
634match context.config.indent_style() {
635// Try to put the first child on the same line with parent's last line
636IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
637// The parent is a block, so align the rest of the chain with the closing
638// brace.
639IndentStyle::Visual => (parent_shape, false),
640}
641} else {
642(
643chain_indent(context, shape.add_offset(parent_rewrite.len())),
644false,
645)
646};
647";
648 let expected =
649"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
650 (
651 chain_indent(context, shape.add_offset(parent_rewrite.len())),
652 context.config.indent_style() == IndentStyle::Visual || is_small_parent,
653 )
654} else if is_block_expr(context, &parent, &parent_rewrite) {
655 match context.config.indent_style() {
656 // Try to put the first child on the same line with parent's last line
657 IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
658 // The parent is a block, so align the rest of the chain with the closing
659 // brace.
660 IndentStyle::Visual => (parent_shape, false),
661 }
662} else {
663 (
664 chain_indent(context, shape.add_offset(parent_rewrite.len())),
665 false,
666 )
667};";
668 assert!(test_format_inner(format_code_block, code_block, expected));
669 }
670}