Add an automated tool that does basic checking of includes sort order.

This commit is contained in:
Pierre Bourdon 2014-02-19 01:45:41 +01:00
parent edb43cfe61
commit 939df46674
1 changed files with 89 additions and 0 deletions

89
Tools/check-includes.py Executable file
View File

@ -0,0 +1,89 @@
#! /usr/bin/env python
"""
check-includes.py <file...>
Checks if the includes are sorted properly and following the "system headers
before local headers" rule.
Ignores what is in #if blocks to avoid false negatives.
"""
import re
import sys
def exclude_if_blocks(lines):
'''Removes lines from #if ... #endif blocks.'''
level = 0
for l in lines:
if l.startswith('#if'):
level += 1
elif l.startswith('#endif'):
level -= 1
elif level == 0:
yield l
def filter_includes(lines):
'''Removes lines that are not #include and keeps only the file part.'''
for l in lines:
if l.startswith('#include'):
if 'NOLINT' not in l:
yield l.split(' ')[1]
class IncludeFileSorter(object):
def __init__(self, path):
self.path = path
def __lt__(self, other):
'''Sorting function for include files.
* System headers go before local headers (check the first character -
if it's different, then the one starting with " is the 'larger').
* Then, iterate on all the path components:
* If they are equal, try to continue to the next path component.
* If not, return whether the path component are smaller/larger.
* Paths with less components should go first, so after iterating, check
whether one path still has some / in it.
'''
a, b = self.path, other.path
if a[0] != b[0]:
return False if a[0] == '"' else True
a, b = a[1:-1].lower(), b[1:-1].lower()
while '/' in a and '/' in b:
ca, a = a.split('/', 1)
cb, b = b.split('/', 1)
if ca != cb:
return ca < cb
if '/' in a:
return False
elif '/' in b:
return True
else:
return a < b
def __eq__(self, other):
return self.path.lower() == other.path.lower()
def sort_includes(includes):
return sorted(includes, key=IncludeFileSorter)
def show_differences(bad, good):
bad = [' Current'] + bad
good = [' Should be'] + good
longest = max(len(i) for i in bad)
padded = [i + ' ' * (longest + 4 - len(i)) for i in bad]
return '\n'.join('%s%s' % t for t in zip(padded, good))
def check_file(path):
print('Checking %s' % path)
lines = (l.strip() for l in open(path).read().split('\n'))
lines = exclude_if_blocks(lines)
includes = list(filter_includes(lines))
sorted_includes = sort_includes(includes)
if includes != sorted_includes:
sys.stderr.write('%s: includes are incorrect\n' % path)
sys.stderr.write(show_differences(includes, sorted_includes) + '\n')
if __name__ == '__main__':
for path in sys.argv[1:]:
check_file(path)