contrib/check-code.py
changeset 14009 64de9ca66511
parent 14005 bb391e0515ba
child 14135 673abd432104
equal deleted inserted replaced
14008:da65edcac72a 14009:64de9ca66511
    40     t = re.sub(r"\S", "x", m.group(2))
    40     t = re.sub(r"\S", "x", m.group(2))
    41     return m.group(1) + t
    41     return m.group(1) + t
    42 
    42 
    43 
    43 
    44 testpats = [
    44 testpats = [
       
    45   [
    45     (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
    46     (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
    46     (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
    47     (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
    47     (r'^function', "don't use 'function', use old style"),
    48     (r'^function', "don't use 'function', use old style"),
    48     (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"),
    49     (r'echo.*\\n', "don't use 'echo \\n', use printf"),
    50     (r'echo.*\\n', "don't use 'echo \\n', use printf"),
    65     ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
    66     ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
    66     (r'^source\b', "don't use 'source', use '.'"),
    67     (r'^source\b', "don't use 'source', use '.'"),
    67     (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"),
    68     (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
    69     (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
    69     (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
    70     (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
       
    71   ],
       
    72   # warnings
       
    73   []
    70 ]
    74 ]
    71 
    75 
    72 testfilters = [
    76 testfilters = [
    73     (r"( *)(#([^\n]*\S)?)", repcomment),
    77     (r"( *)(#([^\n]*\S)?)", repcomment),
    74     (r"<<(\S+)((.|\n)*?\n\1)", rephere),
    78     (r"<<(\S+)((.|\n)*?\n\1)", rephere),
    75 ]
    79 ]
    76 
    80 
    77 uprefix = r"^  \$ "
    81 uprefix = r"^  \$ "
    78 uprefixc = r"^  > "
    82 uprefixc = r"^  > "
    79 utestpats = [
    83 utestpats = [
       
    84   [
    80     (r'^(\S|  $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
    85     (r'^(\S|  $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
    81     (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
    86     (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
    82     (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
    87     (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
    83     (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
    88     (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
    84     (uprefix + r'.*\|\| echo.*(fail|error)',
    89     (uprefix + r'.*\|\| echo.*(fail|error)',
    85      "explicit exit code checks unnecessary"),
    90      "explicit exit code checks unnecessary"),
    86     (uprefix + r'set -e', "don't use set -e"),
    91     (uprefix + r'set -e', "don't use set -e"),
    87     (uprefixc + r'( *)\t', "don't use tabs to indent"),
    92     (uprefixc + r'( *)\t', "don't use tabs to indent"),
    88 ]
    93   ],
    89 
    94   # warnings
    90 for p, m in testpats:
    95   []
       
    96 ]
       
    97 
       
    98 for p, m in testpats[0] + testpats[1]:
    91     if p.startswith('^'):
    99     if p.startswith('^'):
    92         p = uprefix + p[1:]
   100         p = uprefix + p[1:]
    93     else:
   101     else:
    94         p = uprefix + p
   102         p = uprefix + p
    95     utestpats.append((p, m))
   103     utestpats.append((p, m))
    97 utestfilters = [
   105 utestfilters = [
    98     (r"( *)(#([^\n]*\S)?)", repcomment),
   106     (r"( *)(#([^\n]*\S)?)", repcomment),
    99 ]
   107 ]
   100 
   108 
   101 pypats = [
   109 pypats = [
       
   110   [
   102     (r'^\s*def\s*\w+\s*\(.*,\s*\(',
   111     (r'^\s*def\s*\w+\s*\(.*,\s*\(',
   103      "tuple parameter unpacking not available in Python 3+"),
   112      "tuple parameter unpacking not available in Python 3+"),
   104     (r'lambda\s*\(.*,.*\)',
   113     (r'lambda\s*\(.*,.*\)',
   105      "tuple parameter unpacking not available in Python 3+"),
   114      "tuple parameter unpacking not available in Python 3+"),
   106     (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
   115     (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
   110     (r'\S;\s*\n', "semicolon"),
   119     (r'\S;\s*\n', "semicolon"),
   111     (r'\w,\w', "missing whitespace after ,"),
   120     (r'\w,\w', "missing whitespace after ,"),
   112     (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
   121     (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
   113     (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
   122     (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
   114     (r'.{85}', "line too long"),
   123     (r'.{85}', "line too long"),
   115     (r'.{81}', "warning: line over 80 characters"),
       
   116     (r'[^\n]\Z', "no trailing newline"),
   124     (r'[^\n]\Z', "no trailing newline"),
   117     (r'(\S\s+|^\s+)\n', "trailing whitespace"),
   125     (r'(\S\s+|^\s+)\n', "trailing whitespace"),
   118 #    (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
   126 #    (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
   119 #    (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
   127 #    (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
   120     (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
   128     (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
   148     (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
   156     (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
   149      "missing whitespace around operator"),
   157      "missing whitespace around operator"),
   150     (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
   158     (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
   151      "wrong whitespace around ="),
   159      "wrong whitespace around ="),
   152     (r'raise Exception', "don't raise generic exceptions"),
   160     (r'raise Exception', "don't raise generic exceptions"),
       
   161     (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
       
   162     (r' [=!]=\s+(True|False|None)',
       
   163      "comparison with singleton, use 'is' or 'is not' instead"),
       
   164   ],
       
   165   # warnings
       
   166   [
       
   167     (r'.{81}', "warning: line over 80 characters"),
   153     (r'^\s*except:$', "warning: naked except clause"),
   168     (r'^\s*except:$', "warning: naked except clause"),
   154     (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
   169     (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
   155      "warning: unwrapped ui message"),
   170      "warning: unwrapped ui message"),
   156     (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
   171   ]
   157     (r' [=!]=\s+(True|False|None)',
       
   158      "comparison with singleton, use 'is' or 'is not' instead"),
       
   159 ]
   172 ]
   160 
   173 
   161 pyfilters = [
   174 pyfilters = [
   162     (r"""(?msx)(?P<comment>\#.*?$)|
   175     (r"""(?msx)(?P<comment>\#.*?$)|
   163          ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
   176          ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
   164           (?P<text>(([^\\]|\\.)*?))
   177           (?P<text>(([^\\]|\\.)*?))
   165           (?P=quote))""", reppython),
   178           (?P=quote))""", reppython),
   166 ]
   179 ]
   167 
   180 
   168 cpats = [
   181 cpats = [
       
   182   [
   169     (r'//', "don't use //-style comments"),
   183     (r'//', "don't use //-style comments"),
   170     (r'^  ', "don't use spaces to indent"),
   184     (r'^  ', "don't use spaces to indent"),
   171     (r'\S\t', "don't use tabs except for indent"),
   185     (r'\S\t', "don't use tabs except for indent"),
   172     (r'(\S\s+|^\s+)\n', "trailing whitespace"),
   186     (r'(\S\s+|^\s+)\n', "trailing whitespace"),
   173     (r'.{85}', "line too long"),
   187     (r'.{85}', "line too long"),
   180     (r'\w,\w', "missing whitespace after ,"),
   194     (r'\w,\w', "missing whitespace after ,"),
   181     (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
   195     (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
   182     (r'^#\s+\w', "use #foo, not # foo"),
   196     (r'^#\s+\w', "use #foo, not # foo"),
   183     (r'[^\n]\Z', "no trailing newline"),
   197     (r'[^\n]\Z', "no trailing newline"),
   184     (r'^\s*#import\b', "use only #include in standard C code"),
   198     (r'^\s*#import\b', "use only #include in standard C code"),
       
   199   ],
       
   200   # warnings
       
   201   []
   185 ]
   202 ]
   186 
   203 
   187 cfilters = [
   204 cfilters = [
   188     (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
   205     (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
   189     (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
   206     (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
   256         fp.close()
   273         fp.close()
   257         if "no-" + "check-code" in pre:
   274         if "no-" + "check-code" in pre:
   258             break
   275             break
   259         for p, r in filters:
   276         for p, r in filters:
   260             post = re.sub(p, r, post)
   277             post = re.sub(p, r, post)
       
   278         if warnings:
       
   279             pats = pats[0] + pats[1]
       
   280         else:
       
   281             pats = pats[0]
   261         # print post # uncomment to show filtered version
   282         # print post # uncomment to show filtered version
   262         z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
   283         z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
   263         for n, l in z:
   284         for n, l in z:
   264             if "check-code" + "-ignore" in l[0]:
   285             if "check-code" + "-ignore" in l[0]:
   265                 continue
   286                 continue
   266             for p, msg in pats:
   287             for p, msg in pats:
   267                 if not warnings and msg.startswith("warning"):
       
   268                     continue
       
   269                 if re.search(p, l[1]):
   288                 if re.search(p, l[1]):
   270                     bd = ""
   289                     bd = ""
   271                     if blame:
   290                     if blame:
   272                         bd = 'working directory'
   291                         bd = 'working directory'
   273                         if not blamecache:
   292                         if not blamecache: