]> git.proxmox.com Git - rustc.git/blame - src/tools/rustfmt/src/format_report_formatter.rs
New upstream version 1.52.1+dfsg1
[rustc.git] / src / tools / rustfmt / src / format_report_formatter.rs
CommitLineData
f20569fa
XL
1use crate::config::FileName;
2use crate::formatting::FormattingError;
3use crate::{ErrorKind, FormatReport};
4use annotate_snippets::display_list::DisplayList;
5use annotate_snippets::formatter::DisplayListFormatter;
6use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
7use std::fmt::{self, Display};
8
9/// A builder for [`FormatReportFormatter`].
10pub struct FormatReportFormatterBuilder<'a> {
11 report: &'a FormatReport,
12 enable_colors: bool,
13}
14
15impl<'a> FormatReportFormatterBuilder<'a> {
16 /// Creates a new [`FormatReportFormatterBuilder`].
17 pub fn new(report: &'a FormatReport) -> Self {
18 Self {
19 report,
20 enable_colors: false,
21 }
22 }
23
24 /// Enables colors and formatting in the output.
25 pub fn enable_colors(self, enable_colors: bool) -> Self {
26 Self {
27 enable_colors,
28 ..self
29 }
30 }
31
32 /// Creates a new [`FormatReportFormatter`] from the settings in this builder.
33 pub fn build(self) -> FormatReportFormatter<'a> {
34 FormatReportFormatter {
35 report: self.report,
36 enable_colors: self.enable_colors,
37 }
38 }
39}
40
41/// Formats the warnings/errors in a [`FormatReport`].
42///
43/// Can be created using a [`FormatReportFormatterBuilder`].
44pub struct FormatReportFormatter<'a> {
45 report: &'a FormatReport,
46 enable_colors: bool,
47}
48
49impl<'a> Display for FormatReportFormatter<'a> {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let formatter = DisplayListFormatter::new(self.enable_colors, false);
52 let errors_by_file = &self.report.internal.borrow().0;
53
54 for (file, errors) in errors_by_file {
55 for error in errors {
56 let snippet = formatting_error_to_snippet(file, error);
57 writeln!(f, "{}\n", formatter.format(&DisplayList::from(snippet)))?;
58 }
59 }
60
61 if !errors_by_file.is_empty() {
62 let snippet = formatting_failure_snippet(self.report.warning_count());
63 writeln!(f, "{}", formatter.format(&DisplayList::from(snippet)))?;
64 }
65
66 Ok(())
67 }
68}
69
70fn formatting_failure_snippet(warning_count: usize) -> Snippet {
71 Snippet {
72 title: Some(Annotation {
73 id: None,
74 label: Some(format!(
75 "rustfmt has failed to format. See previous {} errors.",
76 warning_count
77 )),
78 annotation_type: AnnotationType::Warning,
79 }),
80 footer: Vec::new(),
81 slices: Vec::new(),
82 }
83}
84
85fn formatting_error_to_snippet(file: &FileName, error: &FormattingError) -> Snippet {
86 let slices = vec![snippet_code_slice(file, error)];
87 let title = Some(snippet_title(error));
88 let footer = snippet_footer(error).into_iter().collect();
89
90 Snippet {
91 title,
92 footer,
93 slices,
94 }
95}
96
97fn snippet_title(error: &FormattingError) -> Annotation {
98 let annotation_type = error_kind_to_snippet_annotation_type(&error.kind);
99
100 Annotation {
101 id: title_annotation_id(error),
102 label: Some(error.kind.to_string()),
103 annotation_type,
104 }
105}
106
107fn snippet_footer(error: &FormattingError) -> Option<Annotation> {
108 let message_suffix = error.msg_suffix();
109
110 if !message_suffix.is_empty() {
111 Some(Annotation {
112 id: None,
113 label: Some(message_suffix.to_string()),
114 annotation_type: AnnotationType::Note,
115 })
116 } else {
117 None
118 }
119}
120
121fn snippet_code_slice(file: &FileName, error: &FormattingError) -> Slice {
122 let annotations = slice_annotation(error).into_iter().collect();
123 let origin = Some(format!("{}:{}", file, error.line));
124 let source = error.line_buffer.clone();
125
126 Slice {
127 source,
128 line_start: error.line,
129 origin,
130 fold: false,
131 annotations,
132 }
133}
134
135fn slice_annotation(error: &FormattingError) -> Option<SourceAnnotation> {
136 let (range_start, range_length) = error.format_len();
137 let range_end = range_start + range_length;
138
139 if range_length > 0 {
140 Some(SourceAnnotation {
141 annotation_type: AnnotationType::Error,
142 range: (range_start, range_end),
143 label: String::new(),
144 })
145 } else {
146 None
147 }
148}
149
150fn title_annotation_id(error: &FormattingError) -> Option<String> {
151 const INTERNAL_ERROR_ID: &str = "internal";
152
153 if error.is_internal() {
154 Some(INTERNAL_ERROR_ID.to_string())
155 } else {
156 None
157 }
158}
159
160fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationType {
161 match error_kind {
162 ErrorKind::LineOverflow(..)
163 | ErrorKind::TrailingWhitespace
164 | ErrorKind::IoError(_)
165 | ErrorKind::ModuleResolutionError(_)
166 | ErrorKind::ParseError
167 | ErrorKind::LostComment
168 | ErrorKind::LicenseCheck
169 | ErrorKind::BadAttr
170 | ErrorKind::InvalidGlobPattern(_)
171 | ErrorKind::VersionMismatch => AnnotationType::Error,
172 ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning,
173 }
174}