contrib/check-code.py
author Idan Kamara <idankk86@gmail.com>
Tue, 21 Jun 2011 15:38:10 +0300
branchstable
changeset 14707 964a72038bb0
parent 14549 48ec0763afbb
child 14709 6c7283faa967
permissions -rwxr-xr-x
cmdserver, runcommand: properly handle the client sending no arguments No real reason for a client to do this, but still possible. Previously if the client sent no arguments, a list with an empty string [''] would be used as the arguments to dispatch, which would cause hg to complain about an ambiguous command. Instead, we simply check for no arguments and use an empty list instead (which is equivalent to invoking hg with no args on the command line).
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     1
#!/usr/bin/env python
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     2
#
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     3
# check-code - a style and portability checker for Mercurial
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     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
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     6
#
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     7
# This software may be used and distributed according to the terms of the
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     8
# GNU General Public License version 2 or any later version.
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     9
11816
e1359ad582f6 check-code: add exit status
Alecs King <alecsk@gmail.com>
parents: 11764
diff changeset
    10
import re, glob, os, sys
13074
637627f31c74 check-code: check for gratuitous whitespace after Python keywords
Thomas Arendsen Hein <thomas@jtah.de>
parents: 13031
diff changeset
    11
import keyword
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
    12
import optparse
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    13
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    14
def repquote(m):
10722
c4fb2103e734 check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10720
diff changeset
    15
    t = re.sub(r"\w", "x", m.group('text'))
10451
63a9bfad50ff check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents: 10412
diff changeset
    16
    t = re.sub(r"[^\sx]", "o", t)
10722
c4fb2103e734 check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10720
diff changeset
    17
    return m.group('quote') + t + m.group('quote')
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    18
10727
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
    19
def reppython(m):
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
    20
    comment = m.group('comment')
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
    21
    if comment:
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
    22
        return "#" * len(comment)
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
    23
    return repquote(m)
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    24
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    25
def repcomment(m):
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    26
    return m.group(1) + "#" * len(m.group(2))
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    27
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    28
def repccomment(m):
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    29
    t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    30
    return m.group(1) + t + "*/"
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    31
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    32
def repcallspaces(m):
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    33
    t = re.sub(r"\n\s+", "\n", m.group(2))
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    34
    return m.group(1) + t
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    35
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    36
def repinclude(m):
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    37
    return m.group(1) + "<foo>"
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    38
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    39
def rephere(m):
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    40
    t = re.sub(r"\S", "x", m.group(2))
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    41
    return m.group(1) + t
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    42
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    43
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    44
testpats = [
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    45
  [
10374
3aa35db5e38c check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents: 10373
diff changeset
    46
    (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
    47
    (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    48
    (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
    49
    (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
    50
    (r'echo.*\\n', "don't use 'echo \\n', use printf"),
11884
932448701e7d check-code: catch "echo -n" in tests
Martin Geisler <mg@lazybytes.net>
parents: 11599
diff changeset
    51
    (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
    52
    (r'^diff.*-\w*N', "don't use 'diff -N'"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    53
    (r'(^| )wc[^|]*$', "filter wc output"),
10374
3aa35db5e38c check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents: 10373
diff changeset
    54
    (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
    55
    (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
    56
    (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
    57
    (r'printf.*\\x', "don't use printf \\x, use Python"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    58
    (r'\$\(.*\)', "don't use $(expr), use `expr`"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    59
    (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    60
    (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    61
     "use egrep for extended grep syntax"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    62
    (r'/bin/', "don't use explicit paths for tools"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    63
    (r'\$PWD', "don't use $PWD, use `pwd`"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    64
    (r'[^\n]\Z', "no trailing newline"),
10658
95c7c4b7e67a test-merge-default and check-code.py: No "export x=x" in sh
Mads Kiilerich <mads@kiilerich.com>
parents: 10451
diff changeset
    65
    (r'export.*=', "don't export and assign at once"),
10802
6e4cf8319f54 check-code.py: Check for bare ^
Mads Kiilerich <mads@kiilerich.com>
parents: 10658
diff changeset
    66
    ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
11210
0c0088881562 check-code: add check for 'source'
Yuya Nishihara <yuya@tcha.org>
parents: 10905
diff changeset
    67
    (r'^source\b', "don't use 'source', use '.'"),
12367
3acd5f7ab9d0 tests: compatibility fix.
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 12366
diff changeset
    68
    (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
13301
4b07578967e6 check-code: do not complain about 'ls x | foo -v'
Martin Geisler <mg@aragost.com>
parents: 13161
diff changeset
    69
    (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
13524
121c89dd7983 check-code: catch "echo > $HGRCPATH" too
Martin Geisler <mg@aragost.com>
parents: 13522
diff changeset
    70
    (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    71
  ],
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    72
  # warnings
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    73
  []
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    74
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    75
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    76
testfilters = [
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    77
    (r"( *)(#([^\n]*\S)?)", repcomment),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    78
    (r"<<(\S+)((.|\n)*?\n\1)", rephere),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    79
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    80
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    81
uprefix = r"^  \$ "
12743
4c4aeaab2339 check-code: add 'no tab indent' check for unified tests
Adrian Buehlmann <adrian@cadifra.com>
parents: 12367
diff changeset
    82
uprefixc = r"^  > "
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    83
utestpats = [
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    84
  [
12785
c7d23b4ca4ba check-code: warning and fixes for whitespace in unified tests
Matt Mackall <mpm@selenic.com>
parents: 12770
diff changeset
    85
    (r'^(\S|  $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
12366
c01dc9087d9a tests: drop a bunch of sed calls from unified tests
Matt Mackall <mpm@selenic.com>
parents: 12364
diff changeset
    86
    (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    87
    (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    88
    (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    89
    (uprefix + r'.*\|\| echo.*(fail|error)',
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    90
     "explicit exit code checks unnecessary"),
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    91
    (uprefix + r'set -e', "don't use set -e"),
12743
4c4aeaab2339 check-code: add 'no tab indent' check for unified tests
Adrian Buehlmann <adrian@cadifra.com>
parents: 12367
diff changeset
    92
    (uprefixc + r'( *)\t', "don't use tabs to indent"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    93
  ],
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    94
  # warnings
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
    95
  []
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    96
]
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
    97
14203
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
    98
for i in [0, 1]:
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
    99
    for p, m in testpats[i]:
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
   100
        if p.startswith('^'):
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
   101
            p = uprefix + p[1:]
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
   102
        else:
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
   103
            p = uprefix + p
b230922eb0c3 check-code: fix checking for sh style in .t tests
Mads Kiilerich <mads@kiilerich.com>
parents: 14169
diff changeset
   104
        utestpats[i].append((p, m))
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   105
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   106
utestfilters = [
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   107
    (r"( *)(#([^\n]*\S)?)", repcomment),
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   108
]
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   109
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   110
pypats = [
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   111
  [
11568
d5d4e6a30613 check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents: 11522
diff changeset
   112
    (r'^\s*def\s*\w+\s*\(.*,\s*\(',
d5d4e6a30613 check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents: 11522
diff changeset
   113
     "tuple parameter unpacking not available in Python 3+"),
d5d4e6a30613 check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents: 11522
diff changeset
   114
    (r'lambda\s*\(.*,.*\)',
d5d4e6a30613 check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents: 11522
diff changeset
   115
     "tuple parameter unpacking not available in Python 3+"),
11764
16723af520b0 check-code: added a check for calls to the builtin cmp function
Renato Cunha <renatoc@gmail.com>
parents: 11672
diff changeset
   116
    (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
11569
f8576644a222 check-code: added check for reduce usage
Renato Cunha <renatoc@gmail.com>
parents: 11568
diff changeset
   117
    (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
11602
ba2520dd1e29 check-code: catch dict.has_key
Martin Geisler <mg@lazybytes.net>
parents: 11601
diff changeset
   118
    (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   119
    (r'^\s*\t', "don't use tabs"),
10412
5326800d6937 check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents: 10374
diff changeset
   120
    (r'\S;\s*\n', "semicolon"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   121
    (r'\w,\w', "missing whitespace after ,"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   122
    (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   123
    (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   124
    (r'.{85}', "line too long"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   125
    (r'[^\n]\Z', "no trailing newline"),
12770
614f0d8724ab check-code: find trailing whitespace
Martin Geisler <mg@lazybytes.net>
parents: 12743
diff changeset
   126
    (r'(\S\s+|^\s+)\n', "trailing whitespace"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   127
#    (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   128
#    (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
10286
cc0340ef47f7 check-code: check thyself
Matt Mackall <mpm@selenic.com>
parents: 10281
diff changeset
   129
    (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
cc0340ef47f7 check-code: check thyself
Matt Mackall <mpm@selenic.com>
parents: 10281
diff changeset
   130
     "linebreak after :"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   131
    (r'class\s[^(]:', "old-style class, use class foo(object)"),
13076
a861c7155f09 check-code: single check for Python keywords used as a function
Thomas Arendsen Hein <thomas@jtah.de>
parents: 13074
diff changeset
   132
    (r'\b(%s)\(' % '|'.join(keyword.kwlist),
a861c7155f09 check-code: single check for Python keywords used as a function
Thomas Arendsen Hein <thomas@jtah.de>
parents: 13074
diff changeset
   133
     "Python keyword is not a function"),
10412
5326800d6937 check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents: 10374
diff changeset
   134
    (r',]', "unneeded trailing ',' in list"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   135
#    (r'class\s[A-Z][^\(]*\((?!Exception)',
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   136
#     "don't capitalize non-exception classes"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   137
#    (r'in range\(', "use xrange"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   138
#    (r'^\s*print\s+', "avoid using print in core and extensions"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   139
    (r'[\x80-\xff]', "non-ASCII character literal"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   140
    (r'("\')\.format\(', "str.format() not available in Python 2.4"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   141
    (r'^\s*with\s+', "with not available in Python 2.4"),
14267
6332c02b3d68 check-code: complain about set.isdisjoint
Matt Mackall <mpm@selenic.com>
parents: 14203
diff changeset
   142
    (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
13160
07d08c130892 check-code: catch "except as"
Matt Mackall <mpm@selenic.com>
parents: 13076
diff changeset
   143
    (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
13161
11eb53464e68 check-code: catch os.path.relpath
Matt Mackall <mpm@selenic.com>
parents: 13160
diff changeset
   144
    (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
11345
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   145
    (r'(?<!def)\s+(any|all|format)\(',
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   146
     "any/all/format not available in Python 2.4"),
11522
eaa7666ad53f check-code: add test for callable
Martin Geisler <mg@aragost.com>
parents: 11345
diff changeset
   147
    (r'(?<!def)\s+(callable)\(',
eaa7666ad53f check-code: add test for callable
Martin Geisler <mg@aragost.com>
parents: 11345
diff changeset
   148
     "callable not available in Python 3, use hasattr(f, '__call__')"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   149
    (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
13074
637627f31c74 check-code: check for gratuitous whitespace after Python keywords
Thomas Arendsen Hein <thomas@jtah.de>
parents: 13031
diff changeset
   150
    (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
637627f31c74 check-code: check for gratuitous whitespace after Python keywords
Thomas Arendsen Hein <thomas@jtah.de>
parents: 13031
diff changeset
   151
     "gratuitous whitespace after Python keyword"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   152
    (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   153
#    (r'\s\s=', "gratuitous whitespace before ="),
11345
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   154
    (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   155
     "missing whitespace around operator"),
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   156
    (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   157
     "missing whitespace around operator"),
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   158
    (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   159
     "missing whitespace around operator"),
14303
e2be0bba0d83 check-code: add /= to operator list
Sune Foldager <cryo@cyanite.org>
parents: 14267
diff changeset
   160
    (r'[^+=*/!<>&| -](\s=|=\s)[^= ]',
11345
4b81f82b03e3 check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents: 11343
diff changeset
   161
     "wrong whitespace around ="),
10451
63a9bfad50ff check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents: 10412
diff changeset
   162
    (r'raise Exception', "don't raise generic exceptions"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   163
    (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   164
    (r' [=!]=\s+(True|False|None)',
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   165
     "comparison with singleton, use 'is' or 'is not' instead"),
14494
1ffeeb91c55d check-code: flag 0/1 used as constant Boolean expression
Martin Geisler <mg@lazybytes.net>
parents: 14303
diff changeset
   166
    (r'^\s*(while|if) [01]:',
1ffeeb91c55d check-code: flag 0/1 used as constant Boolean expression
Martin Geisler <mg@lazybytes.net>
parents: 14303
diff changeset
   167
     "use True/False for constant Boolean expression"),
14169
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   168
    (r'opener\([^)]*\).read\(',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   169
     "use opener.read() instead"),
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   170
    (r'opener\([^)]*\).write\(',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   171
     "use opener.write() instead"),
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   172
    (r'[\s\(](open|file)\([^)]*\)\.read\(',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   173
     "use util.readfile() instead"),
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   174
    (r'[\s\(](open|file)\([^)]*\)\.write\(',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   175
     "use util.readfile() instead"),
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   176
    (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   177
     "always assign an opened file to a variable, and close it afterwards"),
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   178
    (r'[\s\(](open|file)\([^)]*\)\.',
1b4b82063ce2 check-code: disallow calling opener(...).read() and opener(..).write()
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 14137
diff changeset
   179
     "always assign an opened file to a variable, and close it afterwards"),
14549
48ec0763afbb check-code: catch misspellings of descendant
Matt Mackall <mpm@selenic.com>
parents: 14494
diff changeset
   180
    (r'(?i)descendent', "the proper spelling is descendAnt"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   181
  ],
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   182
  # warnings
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   183
  [
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   184
    (r'.{81}', "warning: line over 80 characters"),
14005
bb391e0515ba check-code: warn about naked except clauses
Idan Kamara <idankk86@gmail.com>
parents: 13748
diff changeset
   185
    (r'^\s*except:$', "warning: naked except clause"),
11599
6fcc066c0c2c check-code: warn about untranslated ui.warn calls
Martin Geisler <mg@lazybytes.net>
parents: 11522
diff changeset
   186
    (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   187
     "warning: unwrapped ui message"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   188
  ]
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   189
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   190
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   191
pyfilters = [
10727
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
   192
    (r"""(?msx)(?P<comment>\#.*?$)|
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
   193
         ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
   194
          (?P<text>(([^\\]|\\.)*?))
62b8f15683f2 check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10723
diff changeset
   195
          (?P=quote))""", reppython),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   196
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   197
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   198
cpats = [
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   199
  [
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   200
    (r'//', "don't use //-style comments"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   201
    (r'^  ', "don't use spaces to indent"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   202
    (r'\S\t', "don't use tabs except for indent"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   203
    (r'(\S\s+|^\s+)\n', "trailing whitespace"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   204
    (r'.{85}', "line too long"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   205
    (r'(while|if|do|for)\(', "use space after while/if/do/for"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   206
    (r'return\(', "return is not a function"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   207
    (r' ;', "no space before ;"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   208
    (r'\w+\* \w+', "use int *foo, not int* foo"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   209
    (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   210
    (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   211
    (r'\w,\w', "missing whitespace after ,"),
13736
f3c4421e121c osutil: fix up check-code issues
Matt Mackall <mpm@selenic.com>
parents: 13524
diff changeset
   212
    (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   213
    (r'^#\s+\w', "use #foo, not # foo"),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   214
    (r'[^\n]\Z', "no trailing newline"),
13748
26f8844d1757 osutil: replace #import with #include, and add a check for it
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 13736
diff changeset
   215
    (r'^\s*#import\b', "use only #include in standard C code"),
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   216
  ],
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   217
  # warnings
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   218
  []
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   219
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   220
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   221
cfilters = [
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   222
    (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
10722
c4fb2103e734 check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 10720
diff changeset
   223
    (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   224
    (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   225
    (r'(\()([^)]+\))', repcallspaces),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   226
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   227
14137
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   228
inutilpats = [
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   229
  [
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   230
    (r'\bui\.', "don't use ui in util"),
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   231
  ],
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   232
  # warnings
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   233
  []
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   234
]
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   235
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   236
inrevlogpats = [
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   237
  [
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   238
    (r'\brepo\.', "don't use repo in revlog"),
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   239
  ],
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   240
  # warnings
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   241
  []
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   242
]
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   243
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   244
checks = [
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   245
    ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   246
    ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   247
    ('c', r'.*\.c$', cfilters, cpats),
12364
e128fa4615f2 check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents: 11886
diff changeset
   248
    ('unified test', r'.*\.t$', utestfilters, utestpats),
14137
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   249
    ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   250
     inrevlogpats),
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   251
    ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
83a94c2fe6f4 check-code: check for repo in revlog and ui in util
timeless <timeless@mozdev.org>
parents: 14136
diff changeset
   252
     inutilpats),
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   253
]
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   254
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   255
class norepeatlogger(object):
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   256
    def __init__(self):
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   257
        self._lastseen = None
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   258
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   259
    def log(self, fname, lineno, line, msg, blame):
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   260
        """print error related a to given line of a given file.
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   261
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   262
        The faulty line will also be printed but only once in the case
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   263
        of multiple errors.
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   264
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   265
        :fname: filename
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   266
        :lineno: line number
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   267
        :line: actual content of the line
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   268
        :msg: error message
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   269
        """
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   270
        msgid = fname, lineno, line
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   271
        if msgid != self._lastseen:
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   272
            if blame:
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   273
                print "%s:%d (%s):" % (fname, lineno, blame)
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   274
            else:
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   275
                print "%s:%d:" % (fname, lineno)
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   276
            print " > %s" % line
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   277
            self._lastseen = msgid
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   278
        print " " + msg
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   279
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   280
_defaultlogger = norepeatlogger()
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   281
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   282
def getblame(f):
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   283
    lines = []
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   284
    for l in os.popen('hg annotate -un %s' % f):
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   285
        start, line = l.split(':', 1)
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   286
        user, rev = start.split()
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   287
        lines.append((line[1:-1], user, rev))
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   288
    return lines
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   289
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   290
def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   291
              blame=False, debug=False):
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   292
    """checks style and portability of a given file
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   293
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   294
    :f: filepath
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   295
    :logfunc: function used to report error
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   296
              logfunc(filename, linenumber, linecontent, errormessage)
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   297
    :maxerr: number of error to display before arborting.
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   298
             Set to None (default) to report all errors
10720
fbcccf9ec58f check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10719
diff changeset
   299
fbcccf9ec58f check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10719
diff changeset
   300
    return True if no error is found, False otherwise.
10719
3be9ae49b628 code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10718
diff changeset
   301
    """
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   302
    blamecache = None
10720
fbcccf9ec58f check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10719
diff changeset
   303
    result = True
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   304
    for name, match, filters, pats in checks:
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   305
        if debug:
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   306
            print name, f
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   307
        fc = 0
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   308
        if not re.match(match, f):
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   309
            if debug:
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   310
                print "Skipping %s for %s it doesn't match %s" % (
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   311
                       name, match, f)
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   312
            continue
13400
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 13301
diff changeset
   313
        fp = open(f)
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 13301
diff changeset
   314
        pre = post = fp.read()
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 13301
diff changeset
   315
        fp.close()
10287
5da892be3497 check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents: 10286
diff changeset
   316
        if "no-" + "check-code" in pre:
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   317
            if debug:
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   318
                print "Skipping %s for %s it has no- and check-code" % (
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   319
                       name, f)
10287
5da892be3497 check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents: 10286
diff changeset
   320
            break
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   321
        for p, r in filters:
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   322
            post = re.sub(p, r, post)
14009
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   323
        if warnings:
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   324
            pats = pats[0] + pats[1]
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   325
        else:
64de9ca66511 check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara <idankk86@gmail.com>
parents: 14005
diff changeset
   326
            pats = pats[0]
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   327
        # print post # uncomment to show filtered version
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   328
        z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   329
        if debug:
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   330
            print "Checking %s for %s" % (name, f)
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   331
        for n, l in z:
10287
5da892be3497 check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents: 10286
diff changeset
   332
            if "check-code" + "-ignore" in l[0]:
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   333
                if debug:
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   334
                    print "Skipping %s for %s:%s (check-code -ignore)" % (
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   335
                           name, f, n)
10287
5da892be3497 check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents: 10286
diff changeset
   336
                continue
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   337
            for p, msg in pats:
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   338
                if re.search(p, l[1]):
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   339
                    bd = ""
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   340
                    if blame:
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   341
                        bd = 'working directory'
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   342
                        if not blamecache:
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   343
                            blamecache = getblame(f)
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   344
                        if n < len(blamecache):
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   345
                            bl, bu, br = blamecache[n]
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   346
                            if bl == l[0]:
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   347
                                bd = '%s@%s' % (bu, br)
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   348
                    logfunc(f, n + 1, l[0], msg, bd)
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   349
                    fc += 1
10720
fbcccf9ec58f check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10719
diff changeset
   350
                    result = False
10718
f18c37fd624f check-code: Add a ``maxerr`` argument to the ``checkfile`` function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10717
diff changeset
   351
            if maxerr is not None and fc >= maxerr:
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   352
                print " (too many errors, giving up)"
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   353
                break
10720
fbcccf9ec58f check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10719
diff changeset
   354
    return result
10717
b1f4fcef99b3 check-code: Add a ``checkfile`` function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10716
diff changeset
   355
10716
5f92bde72eef check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10707
diff changeset
   356
if __name__ == "__main__":
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   357
    parser = optparse.OptionParser("%prog [options] [files]")
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   358
    parser.add_option("-w", "--warnings", action="store_true",
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   359
                      help="include warning-level checks")
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   360
    parser.add_option("-p", "--per-file", type="int",
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   361
                      help="max warnings per file")
11604
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   362
    parser.add_option("-b", "--blame", action="store_true",
c5d40818b270 check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents: 11602
diff changeset
   363
                      help="use annotate to generate blame info")
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   364
    parser.add_option("", "--debug", action="store_true",
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   365
                      help="show debug information")
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   366
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   367
    parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False)
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   368
    (options, args) = parser.parse_args()
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   369
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   370
    if len(args) == 0:
10716
5f92bde72eef check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10707
diff changeset
   371
        check = glob.glob("*")
5f92bde72eef check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10707
diff changeset
   372
    else:
10895
217557b26bc7 check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents: 10814
diff changeset
   373
        check = args
10281
e7d3b509af8b Introduce check-code.py
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   374
10716
5f92bde72eef check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents: 10707
diff changeset
   375
    for f in check:
11816
e1359ad582f6 check-code: add exit status
Alecs King <alecsk@gmail.com>
parents: 11764
diff changeset
   376
        ret = 0
e1359ad582f6 check-code: add exit status
Alecs King <alecsk@gmail.com>
parents: 11764
diff changeset
   377
        if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
14135
673abd432104 check-code: adding debug flag
timeless <timeless@mozdev.org>
parents: 14009
diff changeset
   378
                         blame=options.blame, debug=options.debug):
11816
e1359ad582f6 check-code: add exit status
Alecs King <alecsk@gmail.com>
parents: 11764
diff changeset
   379
            ret = 1
e1359ad582f6 check-code: add exit status
Alecs King <alecsk@gmail.com>
parents: 11764
diff changeset
   380
    sys.exit(ret)