verifiers.py (11592:92509f1b24f7) verifiers.py (11716:95a34c2188f2)
1#!/usr/bin/env python
2#
3# Copyright (c) 2014, 2016 ARM Limited
4# All rights reserved
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder. You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Copyright (c) 2006 The Regents of The University of Michigan
16# Copyright (c) 2007,2011 The Hewlett-Packard Development Company
17# Copyright (c) 2016 Advanced Micro Devices, Inc.
18# All rights reserved.
19#
20# Redistribution and use in source and binary forms, with or without
21# modification, are permitted provided that the following conditions are
22# met: redistributions of source code must retain the above copyright
23# notice, this list of conditions and the following disclaimer;
24# redistributions in binary form must reproduce the above copyright
25# notice, this list of conditions and the following disclaimer in the
26# documentation and/or other materials provided with the distribution;
27# neither the name of the copyright holders nor the names of its
28# contributors may be used to endorse or promote products derived from
29# this software without specific prior written permission.
30#
31# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42#
43# Authors: Nathan Binkert
44# Steve Reinhardt
45# Andreas Sandberg
46
47from abc import ABCMeta, abstractmethod
48from difflib import SequenceMatcher
49import inspect
50import os
51import re
52import sys
53
54import style
55import sort_includes
56from region import *
57from file_types import lang_type
58
59
60def safefix(fix_func):
61 """ Decorator for the fix functions of the Verifier class.
62 This function wraps the fix function and creates a backup file
63 just in case there is an error.
64 """
65 def safefix_wrapper(*args, **kwargs):
66 # Check to be sure that this is decorating a function we expect:
67 # a class method with filename as the first argument (after self)
68 assert(os.path.exists(args[1]))
69 self = args[0]
70 assert(is_verifier(self.__class__))
71 filename = args[1]
72
73 # Now, Let's make a backup file.
74 from shutil import copyfile
75 backup_name = filename+'.bak'
76 copyfile(filename, backup_name)
77
78 # Try to apply the fix. If it fails, then we revert the file
79 # Either way, we need to clean up our backup file
80 try:
81 fix_func(*args, **kwargs)
82 except Exception as e:
83 # Restore the original file to the backup file
84 self.ui.write("Error! Restoring the original file.\n")
85 copyfile(backup_name, filename)
86 raise
87 finally:
88 # Clean up the backup file
89 os.remove(backup_name)
90
91 return safefix_wrapper
92
93def _modified_regions(old, new):
94 try:
95 m = SequenceMatcher(a=old, b=new, autojunk=False)
96 except TypeError:
97 # autojunk was introduced in Python 2.7. We need a fallback
98 # mechanism to support old Python versions.
99 m = SequenceMatcher(a=old, b=new)
100 regions = Regions()
101 for tag, i1, i2, j1, j2 in m.get_opcodes():
102 if tag != "equal":
103 regions.extend(Region(i1, i2))
104 return regions
105
106
107class Verifier(object):
108 """Base class for style verifiers
109
110 Verifiers check for style violations and optionally fix such
111 violations. Implementations should either inherit from this class
112 (Verifier) if they need to work on entire files or LineVerifier if
113 they operate on a line-by-line basis.
114
115 Subclasses must define these class attributes:
116 languages = set of strings identifying applicable languages
117 test_name = long descriptive name of test, will be used in
118 messages such as "error in <foo>" or "invalid <foo>"
119 opt_name = short name used to generate command-line options to
120 control the test (--fix-<foo>, --ignore-<foo>, etc.)
121
122 """
123
124 __metaclass__ = ABCMeta
125
126 def __init__(self, ui, opts, base=None):
127 self.ui = ui
128 self.base = base
129
130 # opt_name must be defined as a class attribute of derived classes.
131 # Check test-specific opts first as these have precedence.
132 self.opt_fix = opts.get('fix_' + self.opt_name, False)
133 self.opt_ignore = opts.get('ignore_' + self.opt_name, False)
134 self.opt_skip = opts.get('skip_' + self.opt_name, False)
135 # If no test-specific opts were set, then set based on "-all" opts.
136 if not (self.opt_fix or self.opt_ignore or self.opt_skip):
137 self.opt_fix = opts.get('fix_all', False)
138 self.opt_ignore = opts.get('ignore_all', False)
139 self.opt_skip = opts.get('skip_all', False)
140
141 def normalize_filename(self, name):
142 abs_name = os.path.abspath(name)
143 if self.base is None:
144 return abs_name
145
146 abs_base = os.path.abspath(self.base)
147 return os.path.relpath(abs_name, start=abs_base)
148
149 def open(self, filename, mode):
150 try:
151 f = file(filename, mode)
152 except OSError, msg:
153 print 'could not open file %s: %s' % (filename, msg)
154 return None
155
156 return f
157
158 def skip(self, filename):
159 # We never want to handle symlinks, so always skip them: If the
160 # location pointed to is a directory, skip it. If the location is a
161 # file inside the gem5 directory, it will be checked as a file, so
162 # symlink can be skipped. If the location is a file outside gem5, we
163 # don't want to check it anyway.
164 if os.path.islink(filename):
165 return True
166 return lang_type(filename) not in self.languages
167
168 def apply(self, filename, regions=all_regions):
169 """Possibly apply to specified regions of file 'filename'.
170
171 Verifier is skipped if --skip-<test> option was provided or if
172 file is not of an applicable type. Otherwise file is checked
173 and error messages printed. Errors are fixed or ignored if
174 the corresponding --fix-<test> or --ignore-<test> options were
175 provided. If neither, the user is prompted for an action.
176
177 Returns True to abort, False otherwise.
178 """
179 if not (self.opt_skip or self.skip(filename)):
180 errors = self.check(filename, regions)
181 if errors and not self.opt_ignore:
182 if self.opt_fix:
183 self.fix(filename, regions)
184 else:
185 result = self.ui.prompt("(a)bort, (i)gnore, or (f)ix?",
186 'aif', 'a')
187 if result == 'f':
188 self.fix(filename, regions)
189 elif result == 'a':
190 return True # abort
191
192 return False
193
194 @abstractmethod
1#!/usr/bin/env python
2#
3# Copyright (c) 2014, 2016 ARM Limited
4# All rights reserved
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder. You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Copyright (c) 2006 The Regents of The University of Michigan
16# Copyright (c) 2007,2011 The Hewlett-Packard Development Company
17# Copyright (c) 2016 Advanced Micro Devices, Inc.
18# All rights reserved.
19#
20# Redistribution and use in source and binary forms, with or without
21# modification, are permitted provided that the following conditions are
22# met: redistributions of source code must retain the above copyright
23# notice, this list of conditions and the following disclaimer;
24# redistributions in binary form must reproduce the above copyright
25# notice, this list of conditions and the following disclaimer in the
26# documentation and/or other materials provided with the distribution;
27# neither the name of the copyright holders nor the names of its
28# contributors may be used to endorse or promote products derived from
29# this software without specific prior written permission.
30#
31# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42#
43# Authors: Nathan Binkert
44# Steve Reinhardt
45# Andreas Sandberg
46
47from abc import ABCMeta, abstractmethod
48from difflib import SequenceMatcher
49import inspect
50import os
51import re
52import sys
53
54import style
55import sort_includes
56from region import *
57from file_types import lang_type
58
59
60def safefix(fix_func):
61 """ Decorator for the fix functions of the Verifier class.
62 This function wraps the fix function and creates a backup file
63 just in case there is an error.
64 """
65 def safefix_wrapper(*args, **kwargs):
66 # Check to be sure that this is decorating a function we expect:
67 # a class method with filename as the first argument (after self)
68 assert(os.path.exists(args[1]))
69 self = args[0]
70 assert(is_verifier(self.__class__))
71 filename = args[1]
72
73 # Now, Let's make a backup file.
74 from shutil import copyfile
75 backup_name = filename+'.bak'
76 copyfile(filename, backup_name)
77
78 # Try to apply the fix. If it fails, then we revert the file
79 # Either way, we need to clean up our backup file
80 try:
81 fix_func(*args, **kwargs)
82 except Exception as e:
83 # Restore the original file to the backup file
84 self.ui.write("Error! Restoring the original file.\n")
85 copyfile(backup_name, filename)
86 raise
87 finally:
88 # Clean up the backup file
89 os.remove(backup_name)
90
91 return safefix_wrapper
92
93def _modified_regions(old, new):
94 try:
95 m = SequenceMatcher(a=old, b=new, autojunk=False)
96 except TypeError:
97 # autojunk was introduced in Python 2.7. We need a fallback
98 # mechanism to support old Python versions.
99 m = SequenceMatcher(a=old, b=new)
100 regions = Regions()
101 for tag, i1, i2, j1, j2 in m.get_opcodes():
102 if tag != "equal":
103 regions.extend(Region(i1, i2))
104 return regions
105
106
107class Verifier(object):
108 """Base class for style verifiers
109
110 Verifiers check for style violations and optionally fix such
111 violations. Implementations should either inherit from this class
112 (Verifier) if they need to work on entire files or LineVerifier if
113 they operate on a line-by-line basis.
114
115 Subclasses must define these class attributes:
116 languages = set of strings identifying applicable languages
117 test_name = long descriptive name of test, will be used in
118 messages such as "error in <foo>" or "invalid <foo>"
119 opt_name = short name used to generate command-line options to
120 control the test (--fix-<foo>, --ignore-<foo>, etc.)
121
122 """
123
124 __metaclass__ = ABCMeta
125
126 def __init__(self, ui, opts, base=None):
127 self.ui = ui
128 self.base = base
129
130 # opt_name must be defined as a class attribute of derived classes.
131 # Check test-specific opts first as these have precedence.
132 self.opt_fix = opts.get('fix_' + self.opt_name, False)
133 self.opt_ignore = opts.get('ignore_' + self.opt_name, False)
134 self.opt_skip = opts.get('skip_' + self.opt_name, False)
135 # If no test-specific opts were set, then set based on "-all" opts.
136 if not (self.opt_fix or self.opt_ignore or self.opt_skip):
137 self.opt_fix = opts.get('fix_all', False)
138 self.opt_ignore = opts.get('ignore_all', False)
139 self.opt_skip = opts.get('skip_all', False)
140
141 def normalize_filename(self, name):
142 abs_name = os.path.abspath(name)
143 if self.base is None:
144 return abs_name
145
146 abs_base = os.path.abspath(self.base)
147 return os.path.relpath(abs_name, start=abs_base)
148
149 def open(self, filename, mode):
150 try:
151 f = file(filename, mode)
152 except OSError, msg:
153 print 'could not open file %s: %s' % (filename, msg)
154 return None
155
156 return f
157
158 def skip(self, filename):
159 # We never want to handle symlinks, so always skip them: If the
160 # location pointed to is a directory, skip it. If the location is a
161 # file inside the gem5 directory, it will be checked as a file, so
162 # symlink can be skipped. If the location is a file outside gem5, we
163 # don't want to check it anyway.
164 if os.path.islink(filename):
165 return True
166 return lang_type(filename) not in self.languages
167
168 def apply(self, filename, regions=all_regions):
169 """Possibly apply to specified regions of file 'filename'.
170
171 Verifier is skipped if --skip-<test> option was provided or if
172 file is not of an applicable type. Otherwise file is checked
173 and error messages printed. Errors are fixed or ignored if
174 the corresponding --fix-<test> or --ignore-<test> options were
175 provided. If neither, the user is prompted for an action.
176
177 Returns True to abort, False otherwise.
178 """
179 if not (self.opt_skip or self.skip(filename)):
180 errors = self.check(filename, regions)
181 if errors and not self.opt_ignore:
182 if self.opt_fix:
183 self.fix(filename, regions)
184 else:
185 result = self.ui.prompt("(a)bort, (i)gnore, or (f)ix?",
186 'aif', 'a')
187 if result == 'f':
188 self.fix(filename, regions)
189 elif result == 'a':
190 return True # abort
191
192 return False
193
194 @abstractmethod
195 def check(self, filename, regions=all_regions):
195 def check(self, filename, regions=all_regions, fobj=None, silent=False):
196 """Check specified regions of file 'filename'.
197
196 """Check specified regions of file 'filename'.
197
198 Given that it is possible that the current contents of the file
199 differ from the file as 'staged to commit', for those cases, and
200 maybe others, the argument fobj should be a file object open and reset
201 with the contents matching what the file would look like after the
202 commit. This is needed keep the messages using 'filename' meaningful.
203
204 The argument silent is useful to prevent output when we run check in
205 the staged file vs the actual file to detect if the user forgot
206 staging fixes to the commit. This way, we prevent reporting errors
207 twice in stderr.
208
198 Line-by-line checks can simply provide a check_line() method
199 that returns True if the line is OK and False if it has an
200 error. Verifiers that need a multi-line view (like
201 SortedIncludes) must override this entire function.
202
203 Returns a count of errors (0 if none), though actual non-zero
204 count value is not currently used anywhere.
205 """
206 pass
207
208 @abstractmethod
209 def fix(self, filename, regions=all_regions):
210 """Fix specified regions of file 'filename'.
211
212 Line-by-line fixes can simply provide a fix_line() method that
213 returns the fixed line. Verifiers that need a multi-line view
214 (like SortedIncludes) must override this entire function.
215 """
216 pass
217
218class LineVerifier(Verifier):
209 Line-by-line checks can simply provide a check_line() method
210 that returns True if the line is OK and False if it has an
211 error. Verifiers that need a multi-line view (like
212 SortedIncludes) must override this entire function.
213
214 Returns a count of errors (0 if none), though actual non-zero
215 count value is not currently used anywhere.
216 """
217 pass
218
219 @abstractmethod
220 def fix(self, filename, regions=all_regions):
221 """Fix specified regions of file 'filename'.
222
223 Line-by-line fixes can simply provide a fix_line() method that
224 returns the fixed line. Verifiers that need a multi-line view
225 (like SortedIncludes) must override this entire function.
226 """
227 pass
228
229class LineVerifier(Verifier):
219 def check(self, filename, regions=all_regions):
220 f = self.open(filename, 'r')
230 def check(self, filename, regions=all_regions, fobj=None, silent=False):
231 close = False
232 if fobj is None:
233 fobj = self.open(filename, 'r')
234 close = True
221
222 lang = lang_type(filename)
223 assert lang in self.languages
224
225 errors = 0
235
236 lang = lang_type(filename)
237 assert lang in self.languages
238
239 errors = 0
226 for num,line in enumerate(f):
240 for num,line in enumerate(fobj):
227 if num not in regions:
228 continue
229 line = line.rstrip('\n')
230 if not self.check_line(line, language=lang):
241 if num not in regions:
242 continue
243 line = line.rstrip('\n')
244 if not self.check_line(line, language=lang):
231 self.ui.write("invalid %s in %s:%d\n" % \
232 (self.test_name, filename, num + 1))
233 if self.ui.verbose:
234 self.ui.write(">>%s<<\n" % line[:-1])
245 if not silent:
246 self.ui.write("invalid %s in %s:%d\n" % \
247 (self.test_name, filename, num + 1))
248 if self.ui.verbose:
249 self.ui.write(">>%s<<\n" % line[:-1])
235 errors += 1
250 errors += 1
236 f.close()
251 if close:
252 fobj.close()
237 return errors
238
239 @safefix
240 def fix(self, filename, regions=all_regions):
241 f = self.open(filename, 'r+')
242
243 lang = lang_type(filename)
244 assert lang in self.languages
245
246 lines = list(f)
247
248 f.seek(0)
249 f.truncate()
250
251 for i,line in enumerate(lines):
252 line = line.rstrip('\n')
253 if i in regions:
254 line = self.fix_line(line, language=lang)
255
256 f.write(line)
257 f.write("\n")
258 f.close()
259 self.current_language = None
260
261 @abstractmethod
262 def check_line(self, line, **kwargs):
263 pass
264
265 @abstractmethod
266 def fix_line(self, line, **kwargs):
267 pass
268
269class Whitespace(LineVerifier):
270 """Check whitespace.
271
272 Specifically:
273 - No tabs used for indent
274 - No trailing whitespace
275 """
276
277 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons',
278 'make', 'dts'))
279 trail_only = set(('make', 'dts'))
280
281 test_name = 'whitespace'
282 opt_name = 'white'
283
284 _lead = re.compile(r'^([ \t]+)')
285 _trail = re.compile(r'([ \t]+)$')
286
287
288 def skip_lead(self, language):
289 return language in Whitespace.trail_only
290
291 def check_line(self, line, language):
292 if not self.skip_lead(language):
293 match = Whitespace._lead.search(line)
294 if match and match.group(1).find('\t') != -1:
295 return False
296
297 match = Whitespace._trail.search(line)
298 if match:
299 return False
300
301 return True
302
303 def fix_line(self, line, language):
304 if not self.skip_lead(language) and Whitespace._lead.search(line):
305 newline = ''
306 for i,c in enumerate(line):
307 if c == ' ':
308 newline += ' '
309 elif c == '\t':
310 newline += ' ' * (style.tabsize - \
311 len(newline) % style.tabsize)
312 else:
313 newline += line[i:]
314 break
315
316 line = newline
317
318 return line.rstrip()
319
320
321class SortedIncludes(Verifier):
322 """Check for proper sorting of include statements"""
323
324 languages = sort_includes.default_languages
325 test_name = 'include file order'
326 opt_name = 'include'
327
328 def __init__(self, *args, **kwargs):
329 super(SortedIncludes, self).__init__(*args, **kwargs)
330 self.sort_includes = sort_includes.SortIncludes()
331
253 return errors
254
255 @safefix
256 def fix(self, filename, regions=all_regions):
257 f = self.open(filename, 'r+')
258
259 lang = lang_type(filename)
260 assert lang in self.languages
261
262 lines = list(f)
263
264 f.seek(0)
265 f.truncate()
266
267 for i,line in enumerate(lines):
268 line = line.rstrip('\n')
269 if i in regions:
270 line = self.fix_line(line, language=lang)
271
272 f.write(line)
273 f.write("\n")
274 f.close()
275 self.current_language = None
276
277 @abstractmethod
278 def check_line(self, line, **kwargs):
279 pass
280
281 @abstractmethod
282 def fix_line(self, line, **kwargs):
283 pass
284
285class Whitespace(LineVerifier):
286 """Check whitespace.
287
288 Specifically:
289 - No tabs used for indent
290 - No trailing whitespace
291 """
292
293 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons',
294 'make', 'dts'))
295 trail_only = set(('make', 'dts'))
296
297 test_name = 'whitespace'
298 opt_name = 'white'
299
300 _lead = re.compile(r'^([ \t]+)')
301 _trail = re.compile(r'([ \t]+)$')
302
303
304 def skip_lead(self, language):
305 return language in Whitespace.trail_only
306
307 def check_line(self, line, language):
308 if not self.skip_lead(language):
309 match = Whitespace._lead.search(line)
310 if match and match.group(1).find('\t') != -1:
311 return False
312
313 match = Whitespace._trail.search(line)
314 if match:
315 return False
316
317 return True
318
319 def fix_line(self, line, language):
320 if not self.skip_lead(language) and Whitespace._lead.search(line):
321 newline = ''
322 for i,c in enumerate(line):
323 if c == ' ':
324 newline += ' '
325 elif c == '\t':
326 newline += ' ' * (style.tabsize - \
327 len(newline) % style.tabsize)
328 else:
329 newline += line[i:]
330 break
331
332 line = newline
333
334 return line.rstrip()
335
336
337class SortedIncludes(Verifier):
338 """Check for proper sorting of include statements"""
339
340 languages = sort_includes.default_languages
341 test_name = 'include file order'
342 opt_name = 'include'
343
344 def __init__(self, *args, **kwargs):
345 super(SortedIncludes, self).__init__(*args, **kwargs)
346 self.sort_includes = sort_includes.SortIncludes()
347
332 def check(self, filename, regions=all_regions):
333 f = self.open(filename, 'r')
348 def check(self, filename, regions=all_regions, fobj=None, silent=False):
349 close = False
350 if fobj is None:
351 fobj = self.open(filename, 'r')
352 close = True
334 norm_fname = self.normalize_filename(filename)
335
353 norm_fname = self.normalize_filename(filename)
354
336 old = [ l.rstrip('\n') for l in f.xreadlines() ]
337 f.close()
355 old = [ l.rstrip('\n') for l in fobj.xreadlines() ]
356 if close:
357 fobj.close()
338
339 if len(old) == 0:
340 return 0
341
342 language = lang_type(filename, old[0])
343 new = list(self.sort_includes(old, norm_fname, language))
344
345 modified = _modified_regions(old, new) & regions
346
347 if modified:
358
359 if len(old) == 0:
360 return 0
361
362 language = lang_type(filename, old[0])
363 new = list(self.sort_includes(old, norm_fname, language))
364
365 modified = _modified_regions(old, new) & regions
366
367 if modified:
348 self.ui.write("invalid sorting of includes in %s\n" % (filename))
349 if self.ui.verbose:
350 for start, end in modified.regions:
351 self.ui.write("bad region [%d, %d)\n" % (start, end))
368 if not silent:
369 self.ui.write("invalid sorting of includes in %s\n"
370 % (filename))
371 if self.ui.verbose:
372 for start, end in modified.regions:
373 self.ui.write("bad region [%d, %d)\n" % (start, end))
352 return 1
353
354 return 0
355
356 @safefix
357 def fix(self, filename, regions=all_regions):
358 f = self.open(filename, 'r+')
359
360 old = f.readlines()
361 lines = [ l.rstrip('\n') for l in old ]
362 language = lang_type(filename, lines[0])
363 sort_lines = list(self.sort_includes(lines, filename, language))
364 new = ''.join(line + '\n' for line in sort_lines)
365
366 f.seek(0)
367 f.truncate()
368
369 for i,line in enumerate(sort_lines):
370 f.write(line)
371 f.write('\n')
372 f.close()
373
374
375class ControlSpace(LineVerifier):
376 """Check for exactly one space after if/while/for"""
377
378 languages = set(('C', 'C++'))
379 test_name = 'spacing after if/while/for'
380 opt_name = 'control'
381
382 _any_control = re.compile(r'\b(if|while|for)([ \t]*)\(')
383
384 def check_line(self, line, **kwargs):
385 match = ControlSpace._any_control.search(line)
386 return not (match and match.group(2) != " ")
387
388 def fix_line(self, line, **kwargs):
389 new_line = ControlSpace._any_control.sub(r'\1 (', line)
390 return new_line
391
392
393class LineLength(LineVerifier):
394 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
395 test_name = 'line length'
396 opt_name = 'length'
397
398 def check_line(self, line, **kwargs):
399 return style.normalized_len(line) <= 79
400
401 def fix(self, filename, regions=all_regions, **kwargs):
402 self.ui.write("Warning: cannot automatically fix overly long lines.\n")
403
404 def fix_line(self, line):
405 pass
406
407class ControlCharacters(LineVerifier):
408 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
409 test_name = 'control character'
410 opt_name = 'ascii'
411
412 valid = ('\n', '\t')
413 invalid = "".join([chr(i) for i in range(0, 0x20) if chr(i) not in valid])
414
415 def check_line(self, line, **kwargs):
416 return self.fix_line(line) == line
417
418 def fix_line(self, line, **kwargs):
419 return line.translate(None, ControlCharacters.invalid)
420
421class BoolCompare(LineVerifier):
422 languages = set(('C', 'C++', 'python'))
423 test_name = 'boolean comparison'
424 opt_name = 'boolcomp'
425
426 regex = re.compile(r'\s*==\s*([Tt]rue|[Ff]alse)\b')
427
428 def check_line(self, line, **kwargs):
429 return self.regex.search(line) == None
430
431 def fix_line(self, line, **kwargs):
432 match = self.regex.search(line)
433 if match:
434 if match.group(1) in ('true', 'True'):
435 line = self.regex.sub('', line)
436 else:
437 self.ui.write("Warning: cannot automatically fix "
438 "comparisons with false/False.\n")
439 return line
440
441def is_verifier(cls):
442 """Determine if a class is a Verifier that can be instantiated"""
443
444 return inspect.isclass(cls) and issubclass(cls, Verifier) and \
445 not inspect.isabstract(cls)
446
447# list of all verifier classes
448all_verifiers = [ v for n, v in \
449 inspect.getmembers(sys.modules[__name__], is_verifier) ]
374 return 1
375
376 return 0
377
378 @safefix
379 def fix(self, filename, regions=all_regions):
380 f = self.open(filename, 'r+')
381
382 old = f.readlines()
383 lines = [ l.rstrip('\n') for l in old ]
384 language = lang_type(filename, lines[0])
385 sort_lines = list(self.sort_includes(lines, filename, language))
386 new = ''.join(line + '\n' for line in sort_lines)
387
388 f.seek(0)
389 f.truncate()
390
391 for i,line in enumerate(sort_lines):
392 f.write(line)
393 f.write('\n')
394 f.close()
395
396
397class ControlSpace(LineVerifier):
398 """Check for exactly one space after if/while/for"""
399
400 languages = set(('C', 'C++'))
401 test_name = 'spacing after if/while/for'
402 opt_name = 'control'
403
404 _any_control = re.compile(r'\b(if|while|for)([ \t]*)\(')
405
406 def check_line(self, line, **kwargs):
407 match = ControlSpace._any_control.search(line)
408 return not (match and match.group(2) != " ")
409
410 def fix_line(self, line, **kwargs):
411 new_line = ControlSpace._any_control.sub(r'\1 (', line)
412 return new_line
413
414
415class LineLength(LineVerifier):
416 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
417 test_name = 'line length'
418 opt_name = 'length'
419
420 def check_line(self, line, **kwargs):
421 return style.normalized_len(line) <= 79
422
423 def fix(self, filename, regions=all_regions, **kwargs):
424 self.ui.write("Warning: cannot automatically fix overly long lines.\n")
425
426 def fix_line(self, line):
427 pass
428
429class ControlCharacters(LineVerifier):
430 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
431 test_name = 'control character'
432 opt_name = 'ascii'
433
434 valid = ('\n', '\t')
435 invalid = "".join([chr(i) for i in range(0, 0x20) if chr(i) not in valid])
436
437 def check_line(self, line, **kwargs):
438 return self.fix_line(line) == line
439
440 def fix_line(self, line, **kwargs):
441 return line.translate(None, ControlCharacters.invalid)
442
443class BoolCompare(LineVerifier):
444 languages = set(('C', 'C++', 'python'))
445 test_name = 'boolean comparison'
446 opt_name = 'boolcomp'
447
448 regex = re.compile(r'\s*==\s*([Tt]rue|[Ff]alse)\b')
449
450 def check_line(self, line, **kwargs):
451 return self.regex.search(line) == None
452
453 def fix_line(self, line, **kwargs):
454 match = self.regex.search(line)
455 if match:
456 if match.group(1) in ('true', 'True'):
457 line = self.regex.sub('', line)
458 else:
459 self.ui.write("Warning: cannot automatically fix "
460 "comparisons with false/False.\n")
461 return line
462
463def is_verifier(cls):
464 """Determine if a class is a Verifier that can be instantiated"""
465
466 return inspect.isclass(cls) and issubclass(cls, Verifier) and \
467 not inspect.isabstract(cls)
468
469# list of all verifier classes
470all_verifiers = [ v for n, v in \
471 inspect.getmembers(sys.modules[__name__], is_verifier) ]