Mercurial > hg
annotate contrib/check-code.py @ 10545:b9e4a67329cd stable
Updated contrib/vim/patchreview.* to version 0.2.1
1) adds a :DiffReview command to review code changes
in the current workspace.
2) removes the need to have patchutils (specifically filterdiff)
installed on the system by implementing patch extraction in
pure vim script.
author | Manpreet Singh <junkblocker@yahoo.com> |
---|---|
date | Wed, 24 Feb 2010 13:12:17 -0800 |
parents | 63a9bfad50ff |
children | 95c7c4b7e67a |
rev | line source |
---|---|
10281 | 1 #!/usr/bin/env python |
2 # | |
3 # check-code - a style and portability checker for Mercurial | |
4 # | |
10290
7cc60de189d7
check-code: fix copyright date
Matt Mackall <mpm@selenic.com>
parents:
10287
diff
changeset
|
5 # Copyright 2010 Matt Mackall <mpm@selenic.com> |
10281 | 6 # |
7 # This software may be used and distributed according to the terms of the | |
8 # GNU General Public License version 2 or any later version. | |
9 | |
10 import sys, re, glob | |
11 | |
12 def repquote(m): | |
10451
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
13 t = re.sub(r"\w", "x", m.group(2)) |
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
14 t = re.sub(r"[^\sx]", "o", t) |
10281 | 15 return m.group(1) + t + m.group(1) |
16 | |
17 def repcomment(m): | |
18 return m.group(1) + "#" * len(m.group(2)) | |
19 | |
20 def repccomment(m): | |
21 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2)) | |
22 return m.group(1) + t + "*/" | |
23 | |
24 def repcallspaces(m): | |
25 t = re.sub(r"\n\s+", "\n", m.group(2)) | |
26 return m.group(1) + t | |
27 | |
28 def repinclude(m): | |
29 return m.group(1) + "<foo>" | |
30 | |
31 def rephere(m): | |
32 t = re.sub(r"\S", "x", m.group(2)) | |
33 return m.group(1) + t | |
34 | |
35 | |
36 testpats = [ | |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
37 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
38 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"), |
10281 | 39 (r'^function', "don't use 'function', use old style"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
40 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), |
10373
e4c7972002e4
check-code.py: escape backslash
Mads Kiilerich <mads@kiilerich.com>
parents:
10291
diff
changeset
|
41 (r'echo.*\\n', "don't use 'echo \\n', use printf"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
42 (r'^diff.*-\w*N', "don't use 'diff -N'"), |
10281 | 43 (r'(^| )wc[^|]*$', "filter wc output"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
44 (r'head -c', "don't use 'head -c', use 'dd'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
45 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
46 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
47 (r'printf.*\\x', "don't use printf \\x, use Python"), |
10281 | 48 (r'\$\(.*\)', "don't use $(expr), use `expr`"), |
49 (r'rm -rf \*', "don't use naked rm -rf, target a directory"), | |
50 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w', | |
51 "use egrep for extended grep syntax"), | |
52 (r'/bin/', "don't use explicit paths for tools"), | |
53 (r'\$PWD', "don't use $PWD, use `pwd`"), | |
54 (r'[^\n]\Z', "no trailing newline"), | |
55 ] | |
56 | |
57 testfilters = [ | |
58 (r"( *)(#([^\n]*\S)?)", repcomment), | |
59 (r"<<(\S+)((.|\n)*?\n\1)", rephere), | |
60 ] | |
61 | |
62 pypats = [ | |
63 (r'^\s*\t', "don't use tabs"), | |
10412
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
64 (r'\S;\s*\n', "semicolon"), |
10281 | 65 (r'\w,\w', "missing whitespace after ,"), |
66 (r'\w[+/*\-<>]\w', "missing whitespace in expression"), | |
67 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"), | |
68 (r'.{85}', "line too long"), | |
69 (r'[^\n]\Z', "no trailing newline"), | |
70 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"), | |
71 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"), | |
10286 | 72 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+', |
73 "linebreak after :"), | |
10281 | 74 (r'class\s[^(]:', "old-style class, use class foo(object)"), |
10291
61c93743fae0
check-code: del isn't a function
Matt Mackall <mpm@selenic.com>
parents:
10290
diff
changeset
|
75 (r'^\s+del\(', "del isn't a function"), |
10281 | 76 (r'^\s+except\(', "except isn't a function"), |
10412
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
77 (r',]', "unneeded trailing ',' in list"), |
10281 | 78 # (r'class\s[A-Z][^\(]*\((?!Exception)', |
79 # "don't capitalize non-exception classes"), | |
80 # (r'in range\(', "use xrange"), | |
81 # (r'^\s*print\s+', "avoid using print in core and extensions"), | |
82 (r'[\x80-\xff]', "non-ASCII character literal"), | |
83 (r'("\')\.format\(', "str.format() not available in Python 2.4"), | |
84 (r'^\s*with\s+', "with not available in Python 2.4"), | |
85 (r'if\s.*\selse', "if ... else form not available in Python 2.4"), | |
86 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"), | |
87 # (r'\s\s=', "gratuitous whitespace before ="), | |
10412
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
88 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', "missing whitespace around operator"), |
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
89 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s', "missing whitespace around operator"), |
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
90 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', "missing whitespace around operator"), |
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
91 (r'[^+=*!<>&| -](\s=|=\s)[^= ]', "wrong whitespace around ="), |
10451
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
92 (r'raise Exception', "don't raise generic exceptions"), |
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
93 (r'ui\.(status|progress|write|note)\([\'\"]x', "unwrapped ui message"), |
10281 | 94 ] |
95 | |
96 pyfilters = [ | |
97 (r"""(''')(([^']|\\'|'{1,2}(?!'))*)'''""", repquote), | |
98 (r'''(""")(([^"]|\\"|"{1,2}(?!"))*)"""''', repquote), | |
99 (r'''(?<!")(")(([^"\n]|\\")+)"(?!")''', repquote), | |
100 (r"""(?<!')(')(([^'\n]|\\')+)'(?!')""", repquote), | |
101 (r"( *)(#([^\n]*\S)?)", repcomment), | |
102 ] | |
103 | |
104 cpats = [ | |
105 (r'//', "don't use //-style comments"), | |
106 (r'^ ', "don't use spaces to indent"), | |
107 (r'\S\t', "don't use tabs except for indent"), | |
108 (r'(\S\s+|^\s+)\n', "trailing whitespace"), | |
109 (r'.{85}', "line too long"), | |
110 (r'(while|if|do|for)\(', "use space after while/if/do/for"), | |
111 (r'return\(', "return is not a function"), | |
112 (r' ;', "no space before ;"), | |
113 (r'\w+\* \w+', "use int *foo, not int* foo"), | |
114 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"), | |
115 (r'\S+ (\+\+|--)', "use foo++, not foo ++"), | |
116 (r'\w,\w', "missing whitespace after ,"), | |
117 (r'\w[+/*]\w', "missing whitespace in expression"), | |
118 (r'^#\s+\w', "use #foo, not # foo"), | |
119 (r'[^\n]\Z', "no trailing newline"), | |
120 ] | |
121 | |
122 cfilters = [ | |
123 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment), | |
124 (r'''(?<!")(")(([^"]|\\")+"(?!"))''', repquote), | |
125 (r'''(#\s*include\s+<)([^>]+)>''', repinclude), | |
126 (r'(\()([^)]+\))', repcallspaces), | |
127 ] | |
128 | |
129 checks = [ | |
130 ('python', r'.*\.(py|cgi)$', pyfilters, pypats), | |
131 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats), | |
132 ('c', r'.*\.c$', cfilters, cpats), | |
133 ] | |
134 | |
135 if len(sys.argv) == 1: | |
136 check = glob.glob("*") | |
137 else: | |
138 check = sys.argv[1:] | |
139 | |
140 for f in check: | |
141 for name, match, filters, pats in checks: | |
142 fc = 0 | |
143 if not re.match(match, f): | |
144 continue | |
145 pre = post = open(f).read() | |
10287
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
146 if "no-" + "check-code" in pre: |
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
147 break |
10281 | 148 for p, r in filters: |
149 post = re.sub(p, r, post) | |
150 # print post # uncomment to show filtered version | |
151 z = enumerate(zip(pre.splitlines(), post.splitlines(True))) | |
152 for n, l in z: | |
10287
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
153 if "check-code" + "-ignore" in l[0]: |
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
154 continue |
10281 | 155 lc = 0 |
156 for p, msg in pats: | |
157 if re.search(p, l[1]): | |
158 if not lc: | |
159 print "%s:%d:" % (f, n + 1) | |
160 print " > %s" % l[0] | |
161 print " %s" % msg | |
162 lc += 1 | |
163 fc += 1 | |
164 if fc == 15: | |
165 print " (too many errors, giving up)" | |
166 break | |
167 break |