]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/emitter/checkstyle.rs
Auto merge of #82624 - ojeda:rwlock-example-deadlock, r=JohnTitor
[rust.git] / src / tools / rustfmt / src / emitter / checkstyle.rs
1 use self::xml::XmlEscaped;
2 use super::*;
3 use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
4 use std::io::{self, Write};
5 use std::path::Path;
6
7 mod xml;
8
9 #[derive(Debug, Default)]
10 pub(crate) struct CheckstyleEmitter;
11
12 impl Emitter for CheckstyleEmitter {
13     fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
14         writeln!(output, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
15         write!(output, r#"<checkstyle version="4.3">"#)?;
16         Ok(())
17     }
18
19     fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
20         writeln!(output, "</checkstyle>")
21     }
22
23     fn emit_formatted_file(
24         &mut self,
25         output: &mut dyn Write,
26         FormattedFile {
27             filename,
28             original_text,
29             formatted_text,
30         }: FormattedFile<'_>,
31     ) -> Result<EmitterResult, io::Error> {
32         const CONTEXT_SIZE: usize = 0;
33         let filename = ensure_real_path(filename);
34         let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
35         output_checkstyle_file(output, filename, diff)?;
36         Ok(EmitterResult::default())
37     }
38 }
39
40 pub(crate) fn output_checkstyle_file<T>(
41     mut writer: T,
42     filename: &Path,
43     diff: Vec<Mismatch>,
44 ) -> Result<(), io::Error>
45 where
46     T: Write,
47 {
48     write!(writer, r#"<file name="{}">"#, filename.display())?;
49     for mismatch in diff {
50         let begin_line = mismatch.line_number;
51         let mut current_line;
52         let mut line_counter = 0;
53         for line in mismatch.lines {
54             // Do nothing with `DiffLine::Context` and `DiffLine::Resulting`.
55             if let DiffLine::Expected(message) = line {
56                 current_line = begin_line + line_counter;
57                 line_counter += 1;
58                 write!(
59                     writer,
60                     r#"<error line="{}" severity="warning" message="Should be `{}`" />"#,
61                     current_line,
62                     XmlEscaped(&message)
63                 )?;
64             }
65         }
66     }
67     write!(writer, "</file>")?;
68     Ok(())
69 }
70
71 #[cfg(test)]
72 mod tests {
73     use super::*;
74     use std::path::PathBuf;
75
76     #[test]
77     fn emits_empty_record_on_file_with_no_mismatches() {
78         let file_name = "src/well_formatted.rs";
79         let mut writer = Vec::new();
80         let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]);
81         assert_eq!(
82             &writer[..],
83             format!(r#"<file name="{}"></file>"#, file_name).as_bytes()
84         );
85     }
86
87     // https://github.com/rust-lang/rustfmt/issues/1636
88     #[test]
89     fn emits_single_xml_tree_containing_all_files() {
90         let bin_file = "src/bin.rs";
91         let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"];
92         let bin_formatted = vec!["fn main() {", "    println!(\"Hello, world!\");", "}"];
93         let lib_file = "src/lib.rs";
94         let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"];
95         let lib_formatted = vec!["fn greet() {", "    println!(\"Greetings!\");", "}"];
96         let mut writer = Vec::new();
97         let mut emitter = CheckstyleEmitter::default();
98         let _ = emitter.emit_header(&mut writer);
99         let _ = emitter
100             .emit_formatted_file(
101                 &mut writer,
102                 FormattedFile {
103                     filename: &FileName::Real(PathBuf::from(bin_file)),
104                     original_text: &bin_original.join("\n"),
105                     formatted_text: &bin_formatted.join("\n"),
106                 },
107             )
108             .unwrap();
109         let _ = emitter
110             .emit_formatted_file(
111                 &mut writer,
112                 FormattedFile {
113                     filename: &FileName::Real(PathBuf::from(lib_file)),
114                     original_text: &lib_original.join("\n"),
115                     formatted_text: &lib_formatted.join("\n"),
116                 },
117             )
118             .unwrap();
119         let _ = emitter.emit_footer(&mut writer);
120         let exp_bin_xml = vec![
121             format!(r#"<file name="{}">"#, bin_file),
122             format!(
123                 r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
124                 XmlEscaped(&r#"    println!("Hello, world!");"#),
125             ),
126             String::from("</file>"),
127         ];
128         let exp_lib_xml = vec![
129             format!(r#"<file name="{}">"#, lib_file),
130             format!(
131                 r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
132                 XmlEscaped(&r#"    println!("Greetings!");"#),
133             ),
134             String::from("</file>"),
135         ];
136         assert_eq!(
137             String::from_utf8(writer).unwrap(),
138             vec![
139                 r#"<?xml version="1.0" encoding="utf-8"?>"#,
140                 "\n",
141                 r#"<checkstyle version="4.3">"#,
142                 &format!("{}{}", exp_bin_xml.join(""), exp_lib_xml.join("")),
143                 "</checkstyle>\n",
144             ]
145             .join(""),
146         );
147     }
148 }