Mercurial > public > mercurial-scm > hg
comparison contrib/check-code.py @ 15372:695ac6aca77f stable
check-code: fix issues with finding patterns in unified tests, fix tests
- old-style patterns without ^ were getting improperly anchored
- finditer was matching against beginning of line poorly
- \s was matching newlines
- [^x] was matching newlines
so we:
- remove earlier hacks for multiline matching
- fix unified test anchoring by adding .*
- replace \s with [ \t]
- replace [^x] with [^\nx]
- force all matches into multiline mode so ^ anchors work
This uncovers a number of test issues that are then repaired.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 27 Oct 2011 17:22:04 -0500 |
parents | 572c22c88be6 |
children | 3bece03bf3c6 |
comparison
equal
deleted
inserted
replaced
15371:f26ed4ea46d8 | 15372:695ac6aca77f |
---|---|
43 | 43 |
44 testpats = [ | 44 testpats = [ |
45 [ | 45 [ |
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"), | 46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"), |
47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"), | 47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"), |
48 (r'(^|\n)function', "don't use 'function', use old style"), | 48 (r'^function', "don't use 'function', use old style"), |
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), | 49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), |
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"), | 50 (r'echo.*\\n', "don't use 'echo \\n', use printf"), |
51 (r'echo -n', "don't use 'echo -n', use printf"), | 51 (r'echo -n', "don't use 'echo -n', use printf"), |
52 (r'(^|\n)diff.*-\w*N', "don't use 'diff -N'"), | 52 (r'^diff.*-\w*N', "don't use 'diff -N'"), |
53 (r'(^| )wc[^|\n]*$', "filter wc output"), | 53 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"), |
54 (r'head -c', "don't use 'head -c', use 'dd'"), | 54 (r'head -c', "don't use 'head -c', use 'dd'"), |
55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), | 55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), |
56 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"), | 56 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"), |
57 (r'printf.*\\x', "don't use printf \\x, use Python"), | 57 (r'printf.*\\x', "don't use printf \\x, use Python"), |
58 (r'\$\(.*\)', "don't use $(expr), use `expr`"), | 58 (r'\$\(.*\)', "don't use $(expr), use `expr`"), |
59 (r'rm -rf \*', "don't use naked rm -rf, target a directory"), | 59 (r'rm -rf \*', "don't use naked rm -rf, target a directory"), |
60 (r'(^|\|\s*)grep (-\w\s+)*[^|\n]*[(|]\w', | 60 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w', |
61 "use egrep for extended grep syntax"), | 61 "use egrep for extended grep syntax"), |
62 (r'/bin/', "don't use explicit paths for tools"), | 62 (r'/bin/', "don't use explicit paths for tools"), |
63 (r'\$PWD', "don't use $PWD, use `pwd`"), | 63 (r'\$PWD', "don't use $PWD, use `pwd`"), |
64 (r'[^\n]\Z', "no trailing newline"), | 64 (r'[^\n]\Z', "no trailing newline"), |
65 (r'export.*=', "don't export and assign at once"), | 65 (r'export.*=', "don't export and assign at once"), |
66 ('(^|\n)([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"), | 66 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"), |
67 (r'(^|\n)source\b', "don't use 'source', use '.'"), | 67 (r'^source\b', "don't use 'source', use '.'"), |
68 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), | 68 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), |
69 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), | 69 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), |
70 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"), | 70 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"), |
71 (r'stop\(\)', "don't use 'stop' as a shell function name"), | 71 (r'^stop\(\)', "don't use 'stop' as a shell function name"), |
72 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"), | 72 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"), |
73 ], | 73 ], |
74 # warnings | 74 # warnings |
75 [] | 75 [] |
76 ] | 76 ] |
78 testfilters = [ | 78 testfilters = [ |
79 (r"( *)(#([^\n]*\S)?)", repcomment), | 79 (r"( *)(#([^\n]*\S)?)", repcomment), |
80 (r"<<(\S+)((.|\n)*?\n\1)", rephere), | 80 (r"<<(\S+)((.|\n)*?\n\1)", rephere), |
81 ] | 81 ] |
82 | 82 |
83 uprefix = r"(^|\n) \$\s*" | 83 uprefix = r"^ \$ " |
84 uprefixc = r"(^|\n) > " | 84 uprefixc = r"^ > " |
85 utestpats = [ | 85 utestpats = [ |
86 [ | 86 [ |
87 (r'(^|\n)(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"), | 87 (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"), |
88 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"), | 88 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"), |
89 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"), | 89 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"), |
90 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"), | 90 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"), |
91 (uprefix + r'.*\|\| echo.*(fail|error)', | 91 (uprefix + r'.*\|\| echo.*(fail|error)', |
92 "explicit exit code checks unnecessary"), | 92 "explicit exit code checks unnecessary"), |
97 [] | 97 [] |
98 ] | 98 ] |
99 | 99 |
100 for i in [0, 1]: | 100 for i in [0, 1]: |
101 for p, m in testpats[i]: | 101 for p, m in testpats[i]: |
102 if p.startswith(r'(^|\n)'): | 102 if p.startswith(r'^'): |
103 p = uprefix + p[6:] | 103 p = uprefix + p[1:] |
104 else: | 104 else: |
105 p = uprefix + p | 105 p = uprefix + ".*" + p |
106 utestpats[i].append((p, m)) | 106 utestpats[i].append((p, m)) |
107 | 107 |
108 utestfilters = [ | 108 utestfilters = [ |
109 (r"( *)(#([^\n]*\S)?)", repcomment), | 109 (r"( *)(#([^\n]*\S)?)", repcomment), |
110 ] | 110 ] |
121 (r'^\s*\t', "don't use tabs"), | 121 (r'^\s*\t', "don't use tabs"), |
122 (r'\S;\s*\n', "semicolon"), | 122 (r'\S;\s*\n', "semicolon"), |
123 (r'\w,\w', "missing whitespace after ,"), | 123 (r'\w,\w', "missing whitespace after ,"), |
124 (r'\w[+/*\-<>]\w', "missing whitespace in expression"), | 124 (r'\w[+/*\-<>]\w', "missing whitespace in expression"), |
125 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"), | 125 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"), |
126 (r'(?m)(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n' | 126 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n' |
127 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'), | 127 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'), |
128 (r'.{85}', "line too long"), | 128 (r'.{85}', "line too long"), |
129 (r'(?m) x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'), | 129 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'), |
130 (r'[^\n]\Z', "no trailing newline"), | 130 (r'[^\n]\Z', "no trailing newline"), |
131 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), | 131 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), |
132 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"), | 132 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"), |
133 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"), | 133 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"), |
134 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+', | 134 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+', |
342 print "Checking %s for %s" % (name, f) | 342 print "Checking %s for %s" % (name, f) |
343 | 343 |
344 prelines = None | 344 prelines = None |
345 errors = [] | 345 errors = [] |
346 for p, msg in pats: | 346 for p, msg in pats: |
347 # fix-up regexes for multiline searches | |
348 po = p | |
349 # \s doesn't match \n | |
350 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p) | |
351 # [^...] doesn't match newline | |
352 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p) | |
353 | |
354 #print po, '=>', p | |
355 | |
347 pos = 0 | 356 pos = 0 |
348 n = 0 | 357 n = 0 |
349 for m in re.finditer(p, post): | 358 for m in re.finditer(p, post, re.MULTILINE): |
350 if prelines is None: | 359 if prelines is None: |
351 prelines = pre.splitlines() | 360 prelines = pre.splitlines() |
352 postlines = post.splitlines(True) | 361 postlines = post.splitlines(True) |
353 | 362 |
354 start = m.start() | 363 start = m.start() |