style.py (10692:ab81a0feab55) | style.py (11319:7ca84595249c) |
---|---|
1#! /usr/bin/env python 2# Copyright (c) 2014 ARM Limited 3# All rights reserved 4# 5# The license below extends only to copyright in the software and shall 6# not be construed as granting a license to any other intellectual 7# property including but not limited to intellectual property relating 8# to a hardware implementation of the functionality of the software 9# licensed hereunder. You may use the software subject to the license 10# terms below provided that you ensure that this notice is replicated 11# unmodified and in its entirety in all distributions of the software, 12# modified or unmodified, in source code or in binary form. 13# 14# Copyright (c) 2006 The Regents of The University of Michigan 15# Copyright (c) 2007,2011 The Hewlett-Packard Development Company | 1#! /usr/bin/env python 2# Copyright (c) 2014 ARM Limited 3# All rights reserved 4# 5# The license below extends only to copyright in the software and shall 6# not be construed as granting a license to any other intellectual 7# property including but not limited to intellectual property relating 8# to a hardware implementation of the functionality of the software 9# licensed hereunder. You may use the software subject to the license 10# terms below provided that you ensure that this notice is replicated 11# unmodified and in its entirety in all distributions of the software, 12# modified or unmodified, in source code or in binary form. 13# 14# Copyright (c) 2006 The Regents of The University of Michigan 15# Copyright (c) 2007,2011 The Hewlett-Packard Development Company |
16# Copyright (c) 2016 Advanced Micro Devices, Inc. |
|
16# All rights reserved. 17# 18# Redistribution and use in source and binary forms, with or without 19# modification, are permitted provided that the following conditions are 20# met: redistributions of source code must retain the above copyright 21# notice, this list of conditions and the following disclaimer; 22# redistributions in binary form must reproduce the above copyright 23# notice, this list of conditions and the following disclaimer in the --- 10 unchanged lines hidden (view full) --- 34# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40# 41# Authors: Nathan Binkert | 17# All rights reserved. 18# 19# Redistribution and use in source and binary forms, with or without 20# modification, are permitted provided that the following conditions are 21# met: redistributions of source code must retain the above copyright 22# notice, this list of conditions and the following disclaimer; 23# redistributions in binary form must reproduce the above copyright 24# notice, this list of conditions and the following disclaimer in the --- 10 unchanged lines hidden (view full) --- 35# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 36# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 40# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41# 42# Authors: Nathan Binkert |
43# Steve Reinhardt |
|
42 43import heapq 44import os 45import re 46import sys 47 48from os.path import dirname, join as joinpath 49from itertools import count --- 7 unchanged lines hidden (view full) --- 57import sort_includes 58from file_types import lang_type 59 60all_regions = Regions(Region(neg_inf, pos_inf)) 61 62tabsize = 8 63lead = re.compile(r'^([ \t]+)') 64trail = re.compile(r'([ \t]+)$') | 44 45import heapq 46import os 47import re 48import sys 49 50from os.path import dirname, join as joinpath 51from itertools import count --- 7 unchanged lines hidden (view full) --- 59import sort_includes 60from file_types import lang_type 61 62all_regions = Regions(Region(neg_inf, pos_inf)) 63 64tabsize = 8 65lead = re.compile(r'^([ \t]+)') 66trail = re.compile(r'([ \t]+)$') |
65any_control = re.compile(r'\b(if|while|for)[ \t]*[(]') 66good_control = re.compile(r'\b(if|while|for) [(]') | 67any_control = re.compile(r'\b(if|while|for)([ \t]*)\(') |
67 68format_types = set(('C', 'C++')) 69 70 71def re_ignore(expr): 72 """Helper function to create regular expression ignore file 73 matcher functions""" 74 --- 73 unchanged lines hidden (view full) --- 148 149class StdioUI(UserInterface): 150 def do_prompt(self, prompt, results, default): 151 return raw_input(prompt) or default 152 153 def write(self, string): 154 sys.stdout.write(string) 155 | 68 69format_types = set(('C', 'C++')) 70 71 72def re_ignore(expr): 73 """Helper function to create regular expression ignore file 74 matcher functions""" 75 --- 73 unchanged lines hidden (view full) --- 149 150class StdioUI(UserInterface): 151 def do_prompt(self, prompt, results, default): 152 return raw_input(prompt) or default 153 154 def write(self, string): 155 sys.stdout.write(string) 156 |
157 |
|
156class Verifier(object): | 158class Verifier(object): |
157 def __init__(self, ui, repo): | 159 """Base class for style verifier objects 160 161 Subclasses must define these class attributes: 162 languages = set of strings identifying applicable languages 163 test_name = long descriptive name of test, will be used in 164 messages such as "error in <foo>" or "invalid <foo>" 165 opt_name = short name used to generate command-line options to 166 control the test (--fix-<foo>, --ignore-<foo>, etc.) 167 """ 168 169 def __init__(self, ui, repo, opts): |
158 self.ui = ui 159 self.repo = repo | 170 self.ui = ui 171 self.repo = repo |
172 # opt_name must be defined as a class attribute of derived classes. 173 # Check test-specific opts first as these have precedence. 174 self.opt_fix = opts.get('fix_' + self.opt_name, False) 175 self.opt_ignore = opts.get('ignore_' + self.opt_name, False) 176 self.opt_skip = opts.get('skip_' + self.opt_name, False) 177 # If no test-specific opts were set, then set based on "-all" opts. 178 if not (self.opt_fix or self.opt_ignore or self.opt_skip): 179 self.opt_fix = opts.get('fix_all', False) 180 self.opt_ignore = opts.get('ignore_all', False) 181 self.opt_skip = opts.get('skip_all', False) |
|
160 161 def __getattr__(self, attr): 162 if attr in ('prompt', 'write'): 163 return getattr(self.ui, attr) 164 165 if attr == 'wctx': 166 try: 167 wctx = repo.workingctx() --- 24 unchanged lines hidden (view full) --- 192 # the gem5 directory, it will be checked as a file, so symlink can be 193 # skipped. If the location is a file outside gem5, we don't want to 194 # check it anyway. 195 if os.path.islink(filename): 196 return True 197 return lang_type(filename) not in self.languages 198 199 def check(self, filename, regions=all_regions): | 182 183 def __getattr__(self, attr): 184 if attr in ('prompt', 'write'): 185 return getattr(self.ui, attr) 186 187 if attr == 'wctx': 188 try: 189 wctx = repo.workingctx() --- 24 unchanged lines hidden (view full) --- 214 # the gem5 directory, it will be checked as a file, so symlink can be 215 # skipped. If the location is a file outside gem5, we don't want to 216 # check it anyway. 217 if os.path.islink(filename): 218 return True 219 return lang_type(filename) not in self.languages 220 221 def check(self, filename, regions=all_regions): |
222 """Check specified regions of file 'filename'. 223 224 Line-by-line checks can simply provide a check_line() method 225 that returns True if the line is OK and False if it has an 226 error. Verifiers that need a multi-line view (like 227 SortedIncludes) must override this entire function. 228 229 Returns a count of errors (0 if none), though actual non-zero 230 count value is not currently used anywhere. 231 """ 232 |
|
200 f = self.open(filename, 'r') 201 202 errors = 0 203 for num,line in enumerate(f): 204 if num not in regions: 205 continue 206 if not self.check_line(line): 207 self.write("invalid %s in %s:%d\n" % \ | 233 f = self.open(filename, 'r') 234 235 errors = 0 236 for num,line in enumerate(f): 237 if num not in regions: 238 continue 239 if not self.check_line(line): 240 self.write("invalid %s in %s:%d\n" % \ |
208 (self.test_name, filename, num + 1)) | 241 (self.test_name, filename, num + 1)) |
209 if self.ui.verbose: | 242 if self.ui.verbose: |
210 self.write(">>%s<<\n" % line[-1]) | 243 self.write(">>%s<<\n" % line[:-1]) |
211 errors += 1 212 return errors 213 214 def fix(self, filename, regions=all_regions): | 244 errors += 1 245 return errors 246 247 def fix(self, filename, regions=all_regions): |
248 """Fix specified regions of file 'filename'. 249 250 Line-by-line fixes can simply provide a fix_line() method that 251 returns the fixed line. Verifiers that need a multi-line view 252 (like SortedIncludes) must override this entire function. 253 """ 254 |
|
215 f = self.open(filename, 'r+') 216 217 lines = list(f) 218 219 f.seek(0) 220 f.truncate() 221 222 for i,line in enumerate(lines): 223 if i in regions: 224 line = self.fix_line(line) 225 226 f.write(line) 227 f.close() 228 | 255 f = self.open(filename, 'r+') 256 257 lines = list(f) 258 259 f.seek(0) 260 f.truncate() 261 262 for i,line in enumerate(lines): 263 if i in regions: 264 line = self.fix_line(line) 265 266 f.write(line) 267 f.close() 268 |
229 def apply(self, filename, prompt, regions=all_regions): 230 if not self.skip(filename): | 269 270 def apply(self, filename, regions=all_regions): 271 """Possibly apply to specified regions of file 'filename'. 272 273 Verifier is skipped if --skip-<test> option was provided or if 274 file is not of an applicable type. Otherwise file is checked 275 and error messages printed. Errors are fixed or ignored if 276 the corresponding --fix-<test> or --ignore-<test> options were 277 provided. If neither, the user is prompted for an action. 278 279 Returns True to abort, False otherwise. 280 """ 281 if not (self.opt_skip or self.skip(filename)): |
231 errors = self.check(filename, regions) | 282 errors = self.check(filename, regions) |
232 if errors: 233 if prompt(filename, self.fix, regions): 234 return True | 283 if errors and not self.opt_ignore: 284 if self.opt_fix: 285 self.fix(filename, regions) 286 else: 287 result = self.ui.prompt("(a)bort, (i)gnore, or (f)ix?", 288 'aif', 'a') 289 if result == 'f': 290 self.fix(filename, regions) 291 elif result == 'a': 292 return True # abort 293 |
235 return False 236 237 238class Whitespace(Verifier): | 294 return False 295 296 297class Whitespace(Verifier): |
298 """Check whitespace. 299 300 Specifically: 301 - No tabs used for indent 302 - No trailing whitespace 303 """ 304 |
|
239 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons')) 240 test_name = 'whitespace' | 305 languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons')) 306 test_name = 'whitespace' |
307 opt_name = 'white' 308 |
|
241 def check_line(self, line): 242 match = lead.search(line) 243 if match and match.group(1).find('\t') != -1: 244 return False 245 246 match = trail.search(line) 247 if match: 248 return False --- 11 unchanged lines hidden (view full) --- 260 else: 261 newline += line[i:] 262 break 263 264 line = newline 265 266 return line.rstrip() + '\n' 267 | 309 def check_line(self, line): 310 match = lead.search(line) 311 if match and match.group(1).find('\t') != -1: 312 return False 313 314 match = trail.search(line) 315 if match: 316 return False --- 11 unchanged lines hidden (view full) --- 328 else: 329 newline += line[i:] 330 break 331 332 line = newline 333 334 return line.rstrip() + '\n' 335 |
336 337class ControlSpace(Verifier): 338 """Check for exactly one space after if/while/for""" 339 340 languages = set(('C', 'C++')) 341 test_name = 'spacing after if/while/for' 342 opt_name = 'control' 343 344 def check_line(self, line): 345 match = any_control.search(line) 346 return not (match and match.group(2) != " ") 347 348 def fix_line(self, line): 349 new_line = any_control.sub(r'\1 (', line) 350 return new_line 351 352 |
|
268class SortedIncludes(Verifier): | 353class SortedIncludes(Verifier): |
354 """Check for proper sorting of include statements""" 355 |
|
269 languages = sort_includes.default_languages | 356 languages = sort_includes.default_languages |
357 test_name = 'include file order' 358 opt_name = 'include' 359 |
|
270 def __init__(self, *args, **kwargs): 271 super(SortedIncludes, self).__init__(*args, **kwargs) 272 self.sort_includes = sort_includes.SortIncludes() 273 274 def check(self, filename, regions=all_regions): 275 f = self.open(filename, 'r') 276 277 lines = [ l.rstrip('\n') for l in f.xreadlines() ] --- 31 unchanged lines hidden (view full) --- 309 f.seek(0) 310 f.truncate() 311 312 for i,line in enumerate(sort_lines): 313 f.write(line) 314 f.write('\n') 315 f.close() 316 | 360 def __init__(self, *args, **kwargs): 361 super(SortedIncludes, self).__init__(*args, **kwargs) 362 self.sort_includes = sort_includes.SortIncludes() 363 364 def check(self, filename, regions=all_regions): 365 f = self.open(filename, 'r') 366 367 lines = [ l.rstrip('\n') for l in f.xreadlines() ] --- 31 unchanged lines hidden (view full) --- 399 f.seek(0) 400 f.truncate() 401 402 for i,line in enumerate(sort_lines): 403 f.write(line) 404 f.write('\n') 405 f.close() 406 |
407# list of all verifier classes 408all_verifiers = [ 409 Whitespace, 410 ControlSpace, 411 SortedIncludes 412] 413 |
|
317def linelen(line): 318 tabs = line.count('\t') 319 if not tabs: 320 return len(line) 321 322 count = 0 323 for c in line: 324 if c == '\t': --- 81 unchanged lines hidden (view full) --- 406 stats.trailwhite +=1 407 if verbose > 1: 408 msg(i, line, 'trailing whitespace') 409 bad() 410 411 # for c++, exactly one space betwen if/while/for and ( 412 if lang == 'C++': 413 match = any_control.search(line) | 414def linelen(line): 415 tabs = line.count('\t') 416 if not tabs: 417 return len(line) 418 419 count = 0 420 for c in line: 421 if c == '\t': --- 81 unchanged lines hidden (view full) --- 503 stats.trailwhite +=1 504 if verbose > 1: 505 msg(i, line, 'trailing whitespace') 506 bad() 507 508 # for c++, exactly one space betwen if/while/for and ( 509 if lang == 'C++': 510 match = any_control.search(line) |
414 if match and not good_control.search(line): | 511 if match and match.group(2) != " ": |
415 stats.badcontrol += 1 416 if verbose > 1: 417 msg(i, line, 'improper spacing after %s' % match.group(1)) 418 bad() 419 420 421def _modified_regions(repo, patterns, **kwargs): 422 opt_all = kwargs.get('all', False) --- 36 unchanged lines hidden (view full) --- 459 coding style violations. A list of files can be specified to limit 460 the checker to a subset of the repository. The style rules are 461 normally applied on a diff of the repository state (i.e., added 462 files are checked in their entirety while only modifications of 463 modified files are checked). 464 465 The --all option can be specified to include clean files and check 466 modified files in their entirety. | 512 stats.badcontrol += 1 513 if verbose > 1: 514 msg(i, line, 'improper spacing after %s' % match.group(1)) 515 bad() 516 517 518def _modified_regions(repo, patterns, **kwargs): 519 opt_all = kwargs.get('all', False) --- 36 unchanged lines hidden (view full) --- 556 coding style violations. A list of files can be specified to limit 557 the checker to a subset of the repository. The style rules are 558 normally applied on a diff of the repository state (i.e., added 559 files are checked in their entirety while only modifications of 560 modified files are checked). 561 562 The --all option can be specified to include clean files and check 563 modified files in their entirety. |
467 """ 468 opt_fix_all = opts.get('fix_all', False) 469 if not opt_fix_all: 470 opt_fix_white = opts.get('fix_white', False) 471 opt_fix_include = opts.get('fix_include', False) 472 else: 473 opt_fix_white = True 474 opt_fix_include = True | |
475 | 564 |
476 ui = MercurialUI(hgui, verbose=hgui.verbose) | 565 The --fix-<check>, --ignore-<check>, and --skip-<check> options 566 can be used to control individual style checks: |
477 | 567 |
478 def prompt(name, func, regions=all_regions): 479 result = ui.prompt("(a)bort, (i)gnore, or (f)ix?", 'aif', 'a') 480 if result == 'a': 481 return True 482 elif result == 'f': 483 func(name, regions) | 568 --fix-<check> will perform the check and automatically attempt to 569 fix sny style error (printing a warning if unsuccessful) |
484 | 570 |
485 return False | 571 --ignore-<check> will perform the check but ignore any errors 572 found (other than printing a message for each) |
486 | 573 |
487 def no_prompt(name, func, regions=all_regions): 488 func(name, regions) 489 return False | 574 --skip-<check> will skip performing the check entirely |
490 | 575 |
491 prompt_white = prompt if not opt_fix_white else no_prompt 492 prompt_include = prompt if not opt_fix_include else no_prompt | 576 If none of these options are given, all checks will be performed 577 and the user will be prompted on how to handle each error. |
493 | 578 |
494 whitespace = Whitespace(ui, repo) 495 sorted_includes = SortedIncludes(ui, repo) 496 for fname, mod_regions in _modified_regions(repo, pats, **opts): 497 if whitespace.apply(fname, prompt_white, mod_regions): 498 return True | 579 --fix-all, --ignore-all, and --skip-all are equivalent to specifying 580 --fix-<check>, --ignore-<check>, or --skip-<check> for all checks, 581 respectively. However, option settings for specific checks take 582 precedence. Thus --skip-all --fix-white can be used to skip every 583 check other than whitespace errors, which will be checked and 584 automatically fixed. |
499 | 585 |
500 if sorted_includes.apply(fname, prompt_include, mod_regions): 501 return True | 586 The -v/--verbose flag will display the offending line(s) as well 587 as their location. 588 """ |
502 | 589 |
590 ui = MercurialUI(hgui, verbose=hgui.verbose) 591 592 # instantiate varifier objects 593 verifiers = [v(ui, repo, opts) for v in all_verifiers] 594 595 for fname, mod_regions in _modified_regions(repo, pats, **opts): 596 for verifier in verifiers: 597 if verifier.apply(fname, mod_regions): 598 return True 599 |
|
503 return False 504 505def do_check_format(hgui, repo, *pats, **opts): 506 """check files for gem5 code formatting violations 507 508 Without an argument, checks all modified and added files for gem5 509 code formatting violations. A list of files can be specified to 510 limit the checker to a subset of the repository. The style rules --- 20 unchanged lines hidden (view full) --- 531 532 return False 533 534def check_hook(hooktype): 535 if hooktype not in ('pretxncommit', 'pre-qrefresh'): 536 raise AttributeError, \ 537 "This hook is not meant for %s" % hooktype 538 | 600 return False 601 602def do_check_format(hgui, repo, *pats, **opts): 603 """check files for gem5 code formatting violations 604 605 Without an argument, checks all modified and added files for gem5 606 code formatting violations. A list of files can be specified to 607 limit the checker to a subset of the repository. The style rules --- 20 unchanged lines hidden (view full) --- 628 629 return False 630 631def check_hook(hooktype): 632 if hooktype not in ('pretxncommit', 'pre-qrefresh'): 633 raise AttributeError, \ 634 "This hook is not meant for %s" % hooktype 635 |
636# This function provides a hook that is called before transaction 637# commit and on qrefresh |
|
539def check_style(ui, repo, hooktype, **kwargs): 540 check_hook(hooktype) 541 args = {} 542 543 try: 544 return do_check_style(ui, repo, **args) 545 except Exception, e: 546 import traceback --- 18 unchanged lines hidden (view full) --- 565 return arg 566 567_common_region_options = [ 568 ('a', 'all', False, 569 _("include clean files and unmodified parts of modified files")), 570 ('', 'no-ignore', False, _("ignore the style ignore list")), 571 ] 572 | 638def check_style(ui, repo, hooktype, **kwargs): 639 check_hook(hooktype) 640 args = {} 641 642 try: 643 return do_check_style(ui, repo, **args) 644 except Exception, e: 645 import traceback --- 18 unchanged lines hidden (view full) --- 664 return arg 665 666_common_region_options = [ 667 ('a', 'all', False, 668 _("include clean files and unmodified parts of modified files")), 669 ('', 'no-ignore', False, _("ignore the style ignore list")), 670 ] 671 |
672 673fix_opts = [('f', 'fix-all', False, _("fix all style errors"))] + \ 674 [('', 'fix-' + v.opt_name, False, 675 _('fix errors in ' + v.test_name)) for v in all_verifiers] 676ignore_opts = [('', 'ignore-all', False, _("ignore all style errors"))] + \ 677 [('', 'ignore-' + v.opt_name, False, 678 _('ignore errors in ' + v.test_name)) for v in all_verifiers] 679skip_opts = [('', 'skip-all', False, _("skip all style error checks"))] + \ 680 [('', 'skip-' + v.opt_name, False, 681 _('skip checking for ' + v.test_name)) for v in all_verifiers] 682all_opts = fix_opts + ignore_opts + skip_opts 683 684 |
|
573cmdtable = { 574 '^m5style' : ( | 685cmdtable = { 686 '^m5style' : ( |
575 do_check_style, [ 576 ('f', 'fix-all', False, _("automatically fix style issues")), 577 ('', 'fix-white', False, _("automatically fix white space issues")), 578 ('', 'fix-include', False, _("automatically fix include ordering")), 579 ] + _common_region_options + commands.walkopts, | 687 do_check_style, all_opts + _common_region_options + commands.walkopts, |
580 _('hg m5style [-a] [FILE]...')), 581 '^m5format' : 582 ( do_check_format, [ 583 ] + _common_region_options + commands.walkopts, 584 _('hg m5format [FILE]...')), 585} 586 587if __name__ == '__main__': --- 53 unchanged lines hidden --- | 688 _('hg m5style [-a] [FILE]...')), 689 '^m5format' : 690 ( do_check_format, [ 691 ] + _common_region_options + commands.walkopts, 692 _('hg m5format [FILE]...')), 693} 694 695if __name__ == '__main__': --- 53 unchanged lines hidden --- |