@@ -39,170 +39,169 @@ def write_file(file_name, text):
39
39
f .truncate ()
40
40
41
41
42
- def run_test_once (args , extra_args ):
43
- resource_dir = args .resource_dir
44
- assume_file_name = args .assume_filename
45
- input_file_name = args .input_file_name
46
- check_name = args .check_name
47
- temp_file_name = args .temp_file_name
48
- expect_clang_tidy_error = args .expect_clang_tidy_error
49
- std = args .std
50
-
51
- file_name_with_extension = assume_file_name or input_file_name
52
- _ , extension = os .path .splitext (file_name_with_extension )
53
- if extension not in ['.c' , '.hpp' , '.m' , '.mm' ]:
54
- extension = '.cpp'
55
- temp_file_name = temp_file_name + extension
56
-
57
- clang_tidy_extra_args = extra_args
58
- clang_extra_args = []
59
- if '--' in extra_args :
60
- i = clang_tidy_extra_args .index ('--' )
61
- clang_extra_args = clang_tidy_extra_args [i + 1 :]
62
- clang_tidy_extra_args = clang_tidy_extra_args [:i ]
63
-
64
- # If the test does not specify a config style, force an empty one; otherwise
65
- # autodetection logic can discover a ".clang-tidy" file that is not related to
66
- # the test.
67
- if not any (
68
- [arg .startswith ('-config=' ) for arg in clang_tidy_extra_args ]):
69
- clang_tidy_extra_args .append ('-config={}' )
70
-
71
- if extension in ['.m' , '.mm' ]:
72
- clang_extra_args = ['-fobjc-abi-version=2' , '-fobjc-arc' , '-fblocks' ] + \
73
- clang_extra_args
74
-
75
- if extension in ['.cpp' , '.hpp' , '.mm' ]:
76
- clang_extra_args .append ('-std=' + std )
77
-
78
- # Tests should not rely on STL being available, and instead provide mock
79
- # implementations of relevant APIs.
80
- clang_extra_args .append ('-nostdinc++' )
81
-
82
- if resource_dir is not None :
83
- clang_extra_args .append ('-resource-dir=%s' % resource_dir )
84
-
85
- with open (input_file_name , 'r' , encoding = 'utf-8' ) as input_file :
86
- input_text = input_file .read ()
87
-
88
- check_fixes_prefixes = []
89
- check_messages_prefixes = []
90
- check_notes_prefixes = []
91
-
92
- has_check_fixes = False
93
- has_check_messages = False
94
- has_check_notes = False
95
-
96
- for check in args .check_suffix :
97
- if check and not re .match ('^[A-Z0-9\-]+$' , check ):
98
- sys .exit ('Only A..Z, 0..9 and "-" are ' +
99
- 'allowed in check suffixes list, but "%s" was given' % (check ))
100
-
101
- file_check_suffix = ('-' + check ) if check else ''
102
- check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix
103
- check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix
104
- check_notes_prefix = 'CHECK-NOTES' + file_check_suffix
105
-
106
- has_check_fix = check_fixes_prefix in input_text
107
- has_check_message = check_messages_prefix in input_text
108
- has_check_note = check_notes_prefix in input_text
109
-
110
- if has_check_note and has_check_message :
111
- sys .exit ('Please use either %s or %s but not both' %
112
- (check_notes_prefix , check_messages_prefix ))
113
-
114
- if not has_check_fix and not has_check_message and not has_check_note :
115
- sys .exit ('%s, %s or %s not found in the input' %
116
- (check_fixes_prefix , check_messages_prefix , check_notes_prefix ))
117
-
118
- has_check_fixes = has_check_fixes or has_check_fix
119
- has_check_messages = has_check_messages or has_check_message
120
- has_check_notes = has_check_notes or has_check_note
121
-
122
- if has_check_fix :
123
- check_fixes_prefixes .append (check_fixes_prefix )
124
- if has_check_message :
125
- check_messages_prefixes .append (check_messages_prefix )
126
- if has_check_note :
127
- check_notes_prefixes .append (check_notes_prefix )
128
-
129
- assert has_check_fixes or has_check_messages or has_check_notes
130
- # Remove the contents of the CHECK lines to avoid CHECKs matching on
131
- # themselves. We need to keep the comments to preserve line numbers while
132
- # avoiding empty lines which could potentially trigger formatting-related
133
- # checks.
134
- cleaned_test = re .sub ('// *CHECK-[A-Z0-9\-]*:[^\r \n ]*' , '//' , input_text )
135
-
136
- write_file (temp_file_name , cleaned_test )
137
-
138
- original_file_name = temp_file_name + ".orig"
139
- write_file (original_file_name , cleaned_test )
140
-
141
- args = ['clang-tidy' , temp_file_name , '-fix' , '--checks=-*,' + check_name ] + \
142
- clang_tidy_extra_args + ['--' ] + clang_extra_args
143
- if expect_clang_tidy_error :
144
- args .insert (0 , 'not' )
145
- print ('Running ' + repr (args ) + '...' )
42
+ def try_run (args , raise_error = True ):
146
43
try :
147
- clang_tidy_output = \
148
- subprocess .check_output (args , stderr = subprocess .STDOUT ).decode ()
44
+ process_output = \
45
+ subprocess .check_output (args , stderr = subprocess .STDOUT ).decode (errors = 'ignore' )
149
46
except subprocess .CalledProcessError as e :
150
- print ('clang-tidy failed:\n ' + e .output .decode ())
151
- raise
152
-
153
- print ('------------------------ clang-tidy output -----------------------' )
154
- print (clang_tidy_output .encode ())
155
- print ('\n ------------------------------------------------------------------' )
156
-
157
- try :
158
- diff_output = subprocess .check_output (
159
- ['diff' , '-u' , original_file_name , temp_file_name ],
160
- stderr = subprocess .STDOUT )
161
- except subprocess .CalledProcessError as e :
162
- diff_output = e .output
163
-
164
- print ('------------------------------ Fixes -----------------------------\n ' +
165
- diff_output .decode (errors = 'ignore' ) +
166
- '\n ------------------------------------------------------------------' )
167
-
168
- if has_check_fixes :
169
- try :
170
- subprocess .check_output (
171
- ['FileCheck' , '-input-file=' + temp_file_name , input_file_name ,
172
- '-check-prefixes=' + ',' .join (check_fixes_prefixes ),
173
- '-strict-whitespace' ],
174
- stderr = subprocess .STDOUT )
175
- except subprocess .CalledProcessError as e :
176
- print ('FileCheck failed:\n ' + e .output .decode ())
177
- raise
178
-
179
- if has_check_messages :
180
- messages_file = temp_file_name + '.msg'
181
- write_file (messages_file , clang_tidy_output )
182
- try :
183
- subprocess .check_output (
184
- ['FileCheck' , '-input-file=' + messages_file , input_file_name ,
185
- '-check-prefixes=' + ',' .join (check_messages_prefixes ),
186
- '-implicit-check-not={{warning|error}}:' ],
187
- stderr = subprocess .STDOUT )
188
- except subprocess .CalledProcessError as e :
189
- print ('FileCheck failed:\n ' + e .output .decode ())
190
- raise
191
-
192
- if has_check_notes :
193
- notes_file = temp_file_name + '.notes'
194
- filtered_output = [line for line in clang_tidy_output .splitlines ()
195
- if not "note: FIX-IT applied" in line ]
196
- write_file (notes_file , '\n ' .join (filtered_output ))
197
- try :
198
- subprocess .check_output (
199
- ['FileCheck' , '-input-file=' + notes_file , input_file_name ,
200
- '-check-prefixes=' + ',' .join (check_notes_prefixes ),
201
- '-implicit-check-not={{note|warning|error}}:' ],
202
- stderr = subprocess .STDOUT )
203
- except subprocess .CalledProcessError as e :
204
- print ('FileCheck failed:\n ' + e .output .decode ())
47
+ process_output = e .output .decode (errors = 'ignore' )
48
+ print ('%s failed:\n %s' % (' ' .join (args ), process_output ))
49
+ if raise_error :
205
50
raise
51
+ return process_output
52
+
53
+
54
+ class CheckRunner :
55
+ def __init__ (self , args , extra_args ):
56
+ self .resource_dir = args .resource_dir
57
+ self .assume_file_name = args .assume_filename
58
+ self .input_file_name = args .input_file_name
59
+ self .check_name = args .check_name
60
+ self .temp_file_name = args .temp_file_name
61
+ self .original_file_name = self .temp_file_name + ".orig"
62
+ self .expect_clang_tidy_error = args .expect_clang_tidy_error
63
+ self .std = args .std
64
+ self .check_suffix = args .check_suffix
65
+ self .input_text = ''
66
+ self .check_fixes_prefixes = []
67
+ self .check_messages_prefixes = []
68
+ self .check_notes_prefixes = []
69
+ self .has_check_fixes = False
70
+ self .has_check_messages = False
71
+ self .has_check_notes = False
72
+
73
+ file_name_with_extension = self .assume_file_name or self .input_file_name
74
+ _ , extension = os .path .splitext (file_name_with_extension )
75
+ if extension not in ['.c' , '.hpp' , '.m' , '.mm' ]:
76
+ extension = '.cpp'
77
+ self .temp_file_name = self .temp_file_name + extension
78
+
79
+ self .clang_extra_args = []
80
+ self .clang_tidy_extra_args = extra_args
81
+ if '--' in extra_args :
82
+ i = self .clang_tidy_extra_args .index ('--' )
83
+ self .clang_extra_args = self .clang_tidy_extra_args [i + 1 :]
84
+ self .clang_tidy_extra_args = self .clang_tidy_extra_args [:i ]
85
+
86
+ # If the test does not specify a config style, force an empty one; otherwise
87
+ # auto-detection logic can discover a ".clang-tidy" file that is not related to
88
+ # the test.
89
+ if not any (
90
+ [arg .startswith ('-config=' ) for arg in self .clang_tidy_extra_args ]):
91
+ self .clang_tidy_extra_args .append ('-config={}' )
92
+
93
+ if extension in ['.m' , '.mm' ]:
94
+ self .clang_extra_args = ['-fobjc-abi-version=2' , '-fobjc-arc' , '-fblocks' ] + \
95
+ self .clang_extra_args
96
+
97
+ if extension in ['.cpp' , '.hpp' , '.mm' ]:
98
+ self .clang_extra_args .append ('-std=' + self .std )
99
+
100
+ # Tests should not rely on STL being available, and instead provide mock
101
+ # implementations of relevant APIs.
102
+ self .clang_extra_args .append ('-nostdinc++' )
103
+
104
+ if self .resource_dir is not None :
105
+ self .clang_extra_args .append ('-resource-dir=%s' % self .resource_dir )
106
+
107
+ def read_input (self ):
108
+ with open (self .input_file_name , 'r' , encoding = 'utf-8' ) as input_file :
109
+ self .input_text = input_file .read ()
110
+
111
+ def get_prefixes (self ):
112
+ for check in self .check_suffix :
113
+ if check and not re .match ('^[A-Z0-9\\ -]+$' , check ):
114
+ sys .exit ('Only A..Z, 0..9 and "-" are allowed in check suffixes list,'
115
+ + ' but "%s" was given' % check )
116
+
117
+ file_check_suffix = ('-' + check ) if check else ''
118
+ check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix
119
+ check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix
120
+ check_notes_prefix = 'CHECK-NOTES' + file_check_suffix
121
+
122
+ has_check_fix = check_fixes_prefix in self .input_text
123
+ has_check_message = check_messages_prefix in self .input_text
124
+ has_check_note = check_notes_prefix in self .input_text
125
+
126
+ if has_check_note and has_check_message :
127
+ sys .exit ('Please use either %s or %s but not both' %
128
+ (check_notes_prefix , check_messages_prefix ))
129
+
130
+ if not has_check_fix and not has_check_message and not has_check_note :
131
+ sys .exit ('%s, %s or %s not found in the input' %
132
+ (check_fixes_prefix , check_messages_prefix , check_notes_prefix ))
133
+
134
+ self .has_check_fixes = self .has_check_fixes or has_check_fix
135
+ self .has_check_messages = self .has_check_messages or has_check_message
136
+ self .has_check_notes = self .has_check_notes or has_check_note
137
+
138
+ if has_check_fix :
139
+ self .check_fixes_prefixes .append (check_fixes_prefix )
140
+ if has_check_message :
141
+ self .check_messages_prefixes .append (check_messages_prefix )
142
+ if has_check_note :
143
+ self .check_notes_prefixes .append (check_notes_prefix )
144
+
145
+ assert self .has_check_fixes or self .has_check_messages or self .has_check_notes
146
+
147
+ def prepare_test_inputs (self ):
148
+ # Remove the contents of the CHECK lines to avoid CHECKs matching on
149
+ # themselves. We need to keep the comments to preserve line numbers while
150
+ # avoiding empty lines which could potentially trigger formatting-related
151
+ # checks.
152
+ cleaned_test = re .sub ('// *CHECK-[A-Z0-9\\ -]*:[^\r \n ]*' , '//' , self .input_text )
153
+ write_file (self .temp_file_name , cleaned_test )
154
+ write_file (self .original_file_name , cleaned_test )
155
+
156
+ def run_clang_tidy (self ):
157
+ args = ['clang-tidy' , self .temp_file_name , '-fix' , '--checks=-*,' + self .check_name ] + \
158
+ self .clang_tidy_extra_args + ['--' ] + self .clang_extra_args
159
+ if self .expect_clang_tidy_error :
160
+ args .insert (0 , 'not' )
161
+ print ('Running ' + repr (args ) + '...' )
162
+ clang_tidy_output = try_run (args )
163
+ print ('------------------------ clang-tidy output -----------------------' )
164
+ print (clang_tidy_output .encode ())
165
+ print ('\n ------------------------------------------------------------------' )
166
+
167
+ diff_output = try_run (['diff' , '-u' , self .original_file_name , self .temp_file_name ], False )
168
+ print ('------------------------------ Fixes -----------------------------\n ' +
169
+ diff_output +
170
+ '\n ------------------------------------------------------------------' )
171
+ return clang_tidy_output
172
+
173
+ def check_fixes (self ):
174
+ if self .has_check_fixes :
175
+ try_run (['FileCheck' , '-input-file=' + self .temp_file_name , self .input_file_name ,
176
+ '-check-prefixes=' + ',' .join (self .check_fixes_prefixes ),
177
+ '-strict-whitespace' ])
178
+
179
+ def check_messages (self , clang_tidy_output ):
180
+ if self .has_check_messages :
181
+ messages_file = self .temp_file_name + '.msg'
182
+ write_file (messages_file , clang_tidy_output )
183
+ try_run (['FileCheck' , '-input-file=' + messages_file , self .input_file_name ,
184
+ '-check-prefixes=' + ',' .join (self .check_messages_prefixes ),
185
+ '-implicit-check-not={{warning|error}}:' ])
186
+
187
+ def check_notes (self , clang_tidy_output ):
188
+ if self .has_check_notes :
189
+ notes_file = self .temp_file_name + '.notes'
190
+ filtered_output = [line for line in clang_tidy_output .splitlines ()
191
+ if not ("note: FIX-IT applied" in line )]
192
+ write_file (notes_file , '\n ' .join (filtered_output ))
193
+ try_run (['FileCheck' , '-input-file=' + notes_file , self .input_file_name ,
194
+ '-check-prefixes=' + ',' .join (self .check_notes_prefixes ),
195
+ '-implicit-check-not={{note|warning|error}}:' ])
196
+
197
+ def run (self ):
198
+ self .read_input ()
199
+ self .get_prefixes ()
200
+ self .prepare_test_inputs ()
201
+ clang_tidy_output = self .run_clang_tidy ()
202
+ self .check_fixes ()
203
+ self .check_messages (clang_tidy_output )
204
+ self .check_notes (clang_tidy_output )
206
205
207
206
208
207
def expand_std (std ):
@@ -223,7 +222,7 @@ def csv(string):
223
222
return string .split (',' )
224
223
225
224
226
- def main ():
225
+ def parse_arguments ():
227
226
parser = argparse .ArgumentParser ()
228
227
parser .add_argument ('-expect-clang-tidy-error' , action = 'store_true' )
229
228
parser .add_argument ('-resource-dir' )
@@ -232,20 +231,23 @@ def main():
232
231
parser .add_argument ('check_name' )
233
232
parser .add_argument ('temp_file_name' )
234
233
parser .add_argument (
235
- '-check-suffix' ,
236
- '-check-suffixes' ,
237
- default = ['' ],
238
- type = csv ,
239
- help = 'comma-separated list of FileCheck suffixes' )
234
+ '-check-suffix' ,
235
+ '-check-suffixes' ,
236
+ default = ['' ],
237
+ type = csv ,
238
+ help = 'comma-separated list of FileCheck suffixes' )
240
239
parser .add_argument ('-std' , type = csv , default = ['c++11-or-later' ])
240
+ return parser .parse_known_args ()
241
241
242
- args , extra_args = parser .parse_known_args ()
242
+
243
+ def main ():
244
+ args , extra_args = parse_arguments ()
243
245
244
246
abbreviated_stds = args .std
245
247
for abbreviated_std in abbreviated_stds :
246
248
for std in expand_std (abbreviated_std ):
247
249
args .std = std
248
- run_test_once (args , extra_args )
250
+ CheckRunner (args , extra_args ). run ( )
249
251
250
252
251
253
if __name__ == '__main__' :
0 commit comments