2
2
//! output.
3
3
4
4
use std:: {
5
+ fs,
5
6
process:: Command ,
6
7
sync:: { Arc , Mutex , MutexGuard } ,
7
8
} ;
8
9
9
10
use anyhow:: { Context , Result } ;
10
11
use log:: Level ;
11
- // non-std crates
12
12
use serde:: Deserialize ;
13
- use serde_xml_rs:: de:: Deserializer ;
14
13
15
14
// project-specific crates/modules
16
15
use super :: MakeSuggestions ;
@@ -19,12 +18,10 @@ use crate::{
19
18
common_fs:: { get_line_cols_from_offset, FileObj } ,
20
19
} ;
21
20
22
- /// A Structure used to deserialize clang-format's XML output.
23
- #[ derive( Debug , Deserialize , PartialEq , Clone ) ]
24
- #[ serde( rename = "replacements" ) ]
21
+ #[ derive( Debug , Clone , Deserialize ) ]
25
22
pub struct FormatAdvice {
26
23
/// A list of [`Replacement`]s that clang-tidy wants to make.
27
- #[ serde( rename = "$value" ) ]
24
+ #[ serde( rename( deserialize = "replacement" ) ) ]
28
25
pub replacements : Vec < Replacement > ,
29
26
30
27
pub patched : Option < Vec < u8 > > ,
@@ -41,18 +38,12 @@ impl MakeSuggestions for FormatAdvice {
41
38
}
42
39
43
40
/// A single replacement that clang-format wants to make.
44
- #[ derive( Debug , Deserialize , PartialEq ) ]
41
+ #[ derive( Debug , PartialEq , Default , Clone , Copy , Deserialize ) ]
45
42
pub struct Replacement {
46
43
/// The byte offset where the replacement will start.
44
+ #[ serde( rename = "@offset" ) ]
47
45
pub offset : usize ,
48
46
49
- /// The amount of bytes that will be removed.
50
- pub length : usize ,
51
-
52
- /// The bytes (UTF-8 encoded) that will be added at the [`Replacement::offset`] position.
53
- #[ serde( rename = "$value" ) ]
54
- pub value : Option < String > ,
55
-
56
47
/// The line number described by the [`Replacement::offset`].
57
48
///
58
49
/// This value is not provided by the XML output, but we calculate it after
@@ -68,18 +59,6 @@ pub struct Replacement {
68
59
pub cols : usize ,
69
60
}
70
61
71
- impl Clone for Replacement {
72
- fn clone ( & self ) -> Self {
73
- Replacement {
74
- offset : self . offset ,
75
- length : self . length ,
76
- value : self . value . clone ( ) ,
77
- line : self . line ,
78
- cols : self . cols ,
79
- }
80
- }
81
- }
82
-
83
62
/// Get a string that summarizes the given `--style`
84
63
pub fn summarize_style ( style : & str ) -> String {
85
64
if [ "google" , "chromium" , "microsoft" , "mozilla" , "webkit" ] . contains ( & style) {
@@ -173,38 +152,37 @@ pub fn run_clang_format(
173
152
if output. stdout . is_empty ( ) {
174
153
return Ok ( logs) ;
175
154
}
176
- let xml = String :: from_utf8 ( output. stdout )
177
- . with_context ( || format ! ( "stdout from clang-format was not UTF-8 encoded: {file_name}" ) ) ?
178
- . lines ( )
179
- . collect :: < Vec < & str > > ( )
180
- . join ( "" ) ;
181
- let config = serde_xml_rs:: ParserConfig :: new ( )
182
- . trim_whitespace ( false )
183
- . whitespace_to_characters ( true )
184
- . ignore_root_level_whitespace ( true ) ;
185
- let event_reader = serde_xml_rs:: EventReader :: new_with_config ( xml. as_bytes ( ) , config) ;
186
- let mut format_advice = FormatAdvice :: deserialize ( & mut Deserializer :: new ( event_reader) )
187
- . unwrap_or ( FormatAdvice {
188
- replacements : vec ! [ ] ,
189
- patched : None ,
190
- } ) ;
155
+ let xml = String :: from_utf8 ( output. stdout ) . with_context ( || {
156
+ format ! ( "XML output from clang-format was not UTF-8 encoded: {file_name}" )
157
+ } ) ?;
158
+ let mut format_advice = quick_xml:: de:: from_str :: < FormatAdvice > ( & xml) . unwrap_or ( FormatAdvice {
159
+ replacements : vec ! [ ] ,
160
+ patched : None ,
161
+ } ) ;
191
162
format_advice. patched = patched;
192
163
if !format_advice. replacements . is_empty ( ) {
164
+ let original_contents = fs:: read ( & file. name ) . with_context ( || {
165
+ format ! (
166
+ "Failed to cache file's original content before applying clang-tidy changes: {}" ,
167
+ file_name. clone( )
168
+ )
169
+ } ) ?;
193
170
// get line and column numbers from format_advice.offset
194
171
let mut filtered_replacements = Vec :: new ( ) ;
195
172
for replacement in & mut format_advice. replacements {
196
- let ( line_number, columns) = get_line_cols_from_offset ( & file. name , replacement. offset ) ;
173
+ let ( line_number, columns) =
174
+ get_line_cols_from_offset ( & original_contents, replacement. offset ) ;
197
175
replacement. line = line_number;
198
176
replacement. cols = columns;
199
177
for range in & ranges {
200
178
if range. contains ( & line_number. try_into ( ) . unwrap_or ( 0 ) ) {
201
- filtered_replacements. push ( replacement. clone ( ) ) ;
179
+ filtered_replacements. push ( * replacement) ;
202
180
break ;
203
181
}
204
182
}
205
183
if ranges. is_empty ( ) {
206
184
// lines_changed_only is disabled
207
- filtered_replacements. push ( replacement. clone ( ) ) ;
185
+ filtered_replacements. push ( * replacement) ;
208
186
}
209
187
}
210
188
format_advice. replacements = filtered_replacements;
@@ -216,7 +194,6 @@ pub fn run_clang_format(
216
194
#[ cfg( test) ]
217
195
mod tests {
218
196
use super :: { summarize_style, FormatAdvice , Replacement } ;
219
- use serde:: Deserialize ;
220
197
221
198
#[ test]
222
199
fn parse_xml ( ) {
@@ -226,52 +203,42 @@ mod tests {
226
203
<replacement offset='147' length='0'> </replacement>
227
204
<replacement offset='161' length='0'></replacement>
228
205
<replacement offset='165' length='19'> </replacement>
229
- </replacements>"# ;
230
- //since whitespace is part of the elements' body, we need to remove the LFs first
231
- let xml = xml_raw . lines ( ) . collect :: < Vec < & str > > ( ) . join ( "" ) ;
206
+ </replacements>"#
207
+ . as_bytes ( )
208
+ . to_vec ( ) ;
232
209
233
210
let expected = FormatAdvice {
234
211
replacements : vec ! [
235
212
Replacement {
236
213
offset: 113 ,
237
- length: 5 ,
238
- value: Some ( String :: from( "\n " ) ) ,
239
- line: 0 ,
240
- cols: 0 ,
214
+ ..Default :: default ( )
241
215
} ,
242
216
Replacement {
243
217
offset: 147 ,
244
- length: 0 ,
245
- value: Some ( String :: from( " " ) ) ,
246
- line: 0 ,
247
- cols: 0 ,
218
+ ..Default :: default ( )
248
219
} ,
249
220
Replacement {
250
221
offset: 161 ,
251
- length: 0 ,
252
- value: None ,
253
- line: 0 ,
254
- cols: 0 ,
222
+ ..Default :: default ( )
255
223
} ,
256
224
Replacement {
257
225
offset: 165 ,
258
- length: 19 ,
259
- value: Some ( String :: from( "\n \n " ) ) ,
260
- line: 0 ,
261
- cols: 0 ,
226
+ ..Default :: default ( )
262
227
} ,
263
228
] ,
264
229
patched : None ,
265
230
} ;
266
- let config = serde_xml_rs:: ParserConfig :: new ( )
267
- . trim_whitespace ( false )
268
- . whitespace_to_characters ( true )
269
- . ignore_root_level_whitespace ( true ) ;
270
- let event_reader = serde_xml_rs:: EventReader :: new_with_config ( xml. as_bytes ( ) , config) ;
271
- let document =
272
- FormatAdvice :: deserialize ( & mut serde_xml_rs:: de:: Deserializer :: new ( event_reader) )
273
- . unwrap ( ) ;
274
- assert_eq ! ( expected, document) ;
231
+
232
+ let xml = String :: from_utf8 ( xml_raw) . unwrap ( ) ;
233
+
234
+ let document = quick_xml:: de:: from_str :: < FormatAdvice > ( & xml) . unwrap ( ) ;
235
+ assert_eq ! ( expected. replacements. len( ) , document. replacements. len( ) ) ;
236
+ for i in 0 ..expected. replacements . len ( ) {
237
+ assert_eq ! (
238
+ expected. replacements[ i] . offset,
239
+ document. replacements[ i] . offset
240
+ ) ;
241
+ }
275
242
}
276
243
277
244
fn formalize_style ( style : & str , expected : & str ) {
0 commit comments