3 # Copyright (c) 2014, 2016 ARM Limited
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.
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.
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.
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.
43 # Authors: Nathan Binkert
47 from abc
import ABCMeta
, abstractmethod
48 from difflib
import SequenceMatcher
57 from file_types
import lang_type
59 def _modified_regions(old
, new
):
60 m
= SequenceMatcher(a
=old
, b
=new
, autojunk
=False)
63 for tag
, i1
, i2
, j1
, j2
in m
.get_opcodes():
65 regions
.extend(Region(i1
, i2
))
69 class Verifier(object):
70 """Base class for style verifiers
72 Verifiers check for style violations and optionally fix such
73 violations. Implementations should either inherit from this class
74 (Verifier) if they need to work on entire files or LineVerifier if
75 they operate on a line-by-line basis.
77 Subclasses must define these class attributes:
78 languages = set of strings identifying applicable languages
79 test_name = long descriptive name of test, will be used in
80 messages such as "error in <foo>" or "invalid <foo>"
81 opt_name = short name used to generate command-line options to
82 control the test (--fix-<foo>, --ignore-<foo>, etc.)
86 __metaclass__
= ABCMeta
88 def __init__(self
, ui
, opts
, base
=None):
92 # opt_name must be defined as a class attribute of derived classes.
93 # Check test-specific opts first as these have precedence.
94 self
.opt_fix
= opts
.get('fix_' + self
.opt_name
, False)
95 self
.opt_ignore
= opts
.get('ignore_' + self
.opt_name
, False)
96 self
.opt_skip
= opts
.get('skip_' + self
.opt_name
, False)
97 # If no test-specific opts were set, then set based on "-all" opts.
98 if not (self
.opt_fix
or self
.opt_ignore
or self
.opt_skip
):
99 self
.opt_fix
= opts
.get('fix_all', False)
100 self
.opt_ignore
= opts
.get('ignore_all', False)
101 self
.opt_skip
= opts
.get('skip_all', False)
103 def normalize_filename(self
, name
):
104 abs_name
= os
.path
.abspath(name
)
105 if self
.base
is None:
108 abs_base
= os
.path
.abspath(self
.base
)
109 return os
.path
.relpath(abs_name
, start
=abs_base
)
111 def open(self
, filename
, mode
):
113 f
= file(filename
, mode
)
115 print 'could not open file %s: %s' % (filename
, msg
)
120 def skip(self
, filename
):
121 # We never want to handle symlinks, so always skip them: If the location
122 # pointed to is a directory, skip it. If the location is a file inside
123 # the gem5 directory, it will be checked as a file, so symlink can be
124 # skipped. If the location is a file outside gem5, we don't want to
126 if os
.path
.islink(filename
):
128 return lang_type(filename
) not in self
.languages
130 def apply(self
, filename
, regions
=all_regions
):
131 """Possibly apply to specified regions of file 'filename'.
133 Verifier is skipped if --skip-<test> option was provided or if
134 file is not of an applicable type. Otherwise file is checked
135 and error messages printed. Errors are fixed or ignored if
136 the corresponding --fix-<test> or --ignore-<test> options were
137 provided. If neither, the user is prompted for an action.
139 Returns True to abort, False otherwise.
141 if not (self
.opt_skip
or self
.skip(filename
)):
142 errors
= self
.check(filename
, regions
)
143 if errors
and not self
.opt_ignore
:
145 self
.fix(filename
, regions
)
147 result
= self
.ui
.prompt("(a)bort, (i)gnore, or (f)ix?",
150 self
.fix(filename
, regions
)
157 def check(self
, filename
, regions
=all_regions
):
158 """Check specified regions of file 'filename'.
160 Line-by-line checks can simply provide a check_line() method
161 that returns True if the line is OK and False if it has an
162 error. Verifiers that need a multi-line view (like
163 SortedIncludes) must override this entire function.
165 Returns a count of errors (0 if none), though actual non-zero
166 count value is not currently used anywhere.
171 def fix(self
, filename
, regions
=all_regions
):
172 """Fix specified regions of file 'filename'.
174 Line-by-line fixes can simply provide a fix_line() method that
175 returns the fixed line. Verifiers that need a multi-line view
176 (like SortedIncludes) must override this entire function.
180 class LineVerifier(Verifier
):
181 def check(self
, filename
, regions
=all_regions
):
182 f
= self
.open(filename
, 'r')
185 for num
,line
in enumerate(f
):
186 if num
not in regions
:
188 line
= line
.rstrip('\n')
189 if not self
.check_line(line
):
190 self
.ui
.write("invalid %s in %s:%d\n" % \
191 (self
.test_name
, filename
, num
+ 1))
193 self
.ui
.write(">>%s<<\n" % line
[:-1])
197 def fix(self
, filename
, regions
=all_regions
):
198 f
= self
.open(filename
, 'r+')
205 for i
,line
in enumerate(lines
):
206 line
= line
.rstrip('\n')
208 line
= self
.fix_line(line
)
216 def check_line(self
, line
):
220 def fix_line(self
, line
):
223 class Whitespace(LineVerifier
):
227 - No tabs used for indent
228 - No trailing whitespace
231 languages
= set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
232 test_name
= 'whitespace'
235 _lead
= re
.compile(r
'^([ \t]+)')
236 _trail
= re
.compile(r
'([ \t]+)$')
238 def check_line(self
, line
):
239 match
= Whitespace
._lead
.search(line
)
240 if match
and match
.group(1).find('\t') != -1:
243 match
= Whitespace
._trail
.search(line
)
249 def fix_line(self
, line
):
250 if Whitespace
._lead
.search(line
):
252 for i
,c
in enumerate(line
):
256 newline
+= ' ' * (tabsize
- len(newline
) % tabsize
)
263 return line
.rstrip() + '\n'
266 class SortedIncludes(Verifier
):
267 """Check for proper sorting of include statements"""
269 languages
= sort_includes
.default_languages
270 test_name
= 'include file order'
273 def __init__(self
, *args
, **kwargs
):
274 super(SortedIncludes
, self
).__init
__(*args
, **kwargs
)
275 self
.sort_includes
= sort_includes
.SortIncludes()
277 def check(self
, filename
, regions
=all_regions
):
278 f
= self
.open(filename
, 'r')
279 norm_fname
= self
.normalize_filename(filename
)
281 old
= [ l
.rstrip('\n') for l
in f
.xreadlines() ]
287 language
= lang_type(filename
, old
[0])
288 new
= list(self
.sort_includes(old
, norm_fname
, language
))
290 modified
= _modified_regions(old
, new
) & regions
293 self
.ui
.write("invalid sorting of includes in %s\n" % (filename
))
295 for start
, end
in modified
.regions
:
296 self
.ui
.write("bad region [%d, %d)\n" % (start
, end
))
301 def fix(self
, filename
, regions
=all_regions
):
302 f
= self
.open(filename
, 'r+')
305 lines
= [ l
.rstrip('\n') for l
in old
]
306 language
= lang_type(filename
, lines
[0])
307 sort_lines
= list(self
.sort_includes(lines
, filename
, language
))
308 new
= ''.join(line
+ '\n' for line
in sort_lines
)
313 for i
,line
in enumerate(sort_lines
):
319 class ControlSpace(LineVerifier
):
320 """Check for exactly one space after if/while/for"""
322 languages
= set(('C', 'C++'))
323 test_name
= 'spacing after if/while/for'
326 _any_control
= re
.compile(r
'\b(if|while|for)([ \t]*)\(')
328 def check_line(self
, line
):
329 match
= ControlSpace
._any
_control
.search(line
)
330 return not (match
and match
.group(2) != " ")
332 def fix_line(self
, line
):
333 new_line
= _any_control
.sub(r
'\1 (', line
)
337 class LineLength(LineVerifier
):
338 languages
= set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
339 test_name
= 'line length'
342 def check_line(self
, line
):
343 return style
.normalized_len(line
) <= 78
345 def fix(self
, filename
, regions
=all_regions
):
346 self
.ui
.write("Warning: cannot automatically fix overly long lines.\n")
348 def fix_line(self
, line
):
351 class BoolCompare(LineVerifier
):
352 languages
= set(('C', 'C++', 'python'))
353 test_name
= 'boolean comparison'
354 opt_name
= 'boolcomp'
356 regex
= re
.compile(r
'\s*==\s*([Tt]rue|[Ff]alse)\b')
358 def check_line(self
, line
):
359 return self
.regex
.search(line
) == None
361 def fix_line(self
, line
):
362 match
= self
.regex
.search(line
)
364 if match
.group(1) in ('true', 'True'):
365 line
= self
.regex
.sub('', line
)
367 self
.ui
.write("Warning: cannot automatically fix "
368 "comparisons with false/False.\n")
371 def is_verifier(cls
):
372 """Determine if a class is a Verifier that can be instantiated"""
374 return inspect
.isclass(cls
) and issubclass(cls
, Verifier
) and \
375 not inspect
.isabstract(cls
)
377 # list of all verifier classes
378 all_verifiers
= [ v
for n
, v
in \
379 inspect
.getmembers(sys
.modules
[__name__
], is_verifier
) ]