]> spindle.queued.net Git - midori/commitdiff
Provide script 'check-style' for validating patches
authorChristian Dywan <christian@twotoasts.de>
Tue, 27 Apr 2010 21:56:27 +0000 (23:56 +0200)
committerChristian Dywan <christian@twotoasts.de>
Tue, 27 Apr 2010 21:56:27 +0000 (23:56 +0200)
Run the script with a patch file as the first argument in order
to validate the patch in terms of coding style, like so:

./tools/check-style my-patch.diff

As a shortcut, you can also check current changes:

./tools/check-style .

tools/check-style [new file with mode: 0755]

diff --git a/tools/check-style b/tools/check-style
new file mode 100755 (executable)
index 0000000..fc752fc
--- /dev/null
@@ -0,0 +1,161 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2010 Christian Dywan <christian@twotoasts.de>
+# Copyright (C) 2010 Arno Renevier <arno@renevier.net>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# See the file COPYING for the full license text.
+#
+# check-style: Verify C source code according to coding style.
+
+import glob, re, string, subprocess, sys, os
+
+if len (sys.argv) < 2:
+    name = os.path.basename (sys.argv[0])
+    print ('Usage:\n  ' + name + ' FILENAMES\n'
+           '  Pass "-" to read stdin, eg. "cat my-feature.diff | ' + name + ' -"\n'
+           '  Pass "." to mean "git diff ^HEAD | ' + name + ' -"')
+    sys.exit (1)
+
+# Coding style violations
+violations = [
+    ['.{101}', 'Line longer than 100 columns'],
+    ['^[ ]{1,3}[^ ]+', 'Indentation is less than 4 spaces'],
+    # FIXME: Don't match empty strings
+    # FIXME: Don't match indented function arguments
+    # ['^(?!(([ ]{4})*[^ ]+))', 'Indentation is not 4 spaces'],
+    ['.*[ ]+$', 'Trailing whitespace'],
+    [r"\t+", 'Tabs instead of spaces'],
+    ["[^0-9],[^ ][^0-9]", 'No space after comma'],
+    # ['(([A-Z][a-z]+)+)\*?[ ][a-z]{2,}', 'Good variable name'],
+    # ['(g?char|g(boolean|pointer))\*?[ ][a-z]{2,}', 'Good variable name'],
+    # ['(g?int|guint)[ ][a-z]+', 'Good iterator name'],
+    # ['(struct)[ ]+[_]?([A-Z][a-z]+)+', 'Good type name'],
+    ['^\s*\w+(?<!\\breturn)\s+\*\s*\w*\s*[;,]', 'Space between type and asterisk'],
+    ["(\w[+|-|*|/|<|>|=]{1,2}\w)", 'No space around operators'],
+    ["\/\*[^ *\n]", 'No space after open comment'],
+    ['[^ *]\*\/', 'No space before close comment'],
+    ['\)\{', 'No space between ) and {'],
+    [';[^ \s]', 'No space or newline after semicolon'],
+    # ['(if)( \([^ ].*[^ ]\))$', 'Good if style'],
+    ['^#\s+(if(n?def)?|define|else|elif)[ ].*$', 'Space between # and cpp'],
+    [r'^\s*\*\w+(\+\+|--);', 'Invalid increment, use (*i)++ or *i += 1'],
+    ['asctime|ctime|getgrgid|getprgnam|getlogin \
+     |getpwnam|getpwuid|gmtime|localtime \
+     |rand|readdir|strtok|ttyname', 'Not thread-safe posix, use _r variant'],
+]
+# No validation for strings, comments, includes
+omissions = [
+    [r'["]{1}.*["]', 'STRING'],
+    ["'\\\?.'", 'CHAR'],
+    ["^\s*\/\*.*\*\/\s*$", 'COMMENT'],
+    ['#include <.*>', 'INCLUDE'],
+]
+
+# Output format
+fmt = '%s - %d: %s'
+
+# Pre-compile expressions
+for violation in violations:
+    violation[0] = re.compile (violation[0])
+for omission in omissions:
+    omission[0] = re.compile (omission[0])
+
+for filename_or_glob in sys.argv[1:]:
+    if filename_or_glob == '-':
+        handles = [sys.stdin]
+    else:
+        handles = []
+        for filename in glob.glob (filename_or_glob):
+            if os.path.isdir (filename):
+                gitdiff = subprocess.Popen (['git', 'diff', '^HEAD',
+                    '--relative', filename], stdout=subprocess.PIPE)
+                handles.append (gitdiff.stdout)
+            else:
+                handles.append (open (filename))
+    if not handles:
+        print (filename_or_glob + ' not found')
+        sys.exit (1)
+
+    for handle in handles:
+        previous = ''
+        i = 0
+        mode = ''
+        filename = handle.name
+        comment = False
+        curly = []
+
+        for line in handle:
+            line = line[:-1]
+            i += 1
+
+            # Parse diff, only validate modified lines
+            if i == 1 and 'diff' in line:
+                mode = 'diff'
+            if mode == 'diff':
+                if line[:3] == '+++':
+                    filename = line[6:]
+                    comment = False
+                    curly = []
+                    continue
+                if line[:2] == '@@':
+                    i = int (line.split (' ')[2].split (',')[0][1:]) - 1
+                    curly = []
+                if line[0] == '-':
+                    i = i -1
+                if line[0] != '+':
+                    continue
+                line = line[1:]
+
+            # Spurious blank lines
+            if previous == line == '':
+                print (fmt % (filename, i, 'Spurious blank line'))
+                previous = line
+                continue
+            previous = line
+
+            # Skip multi-line comment blocks
+            if '/*' in line and not '*/' in line:
+                comment = True
+            if comment:
+                if '*/' in line and not '/*' in line:
+                    comment = False
+                continue
+
+            cleaned = line
+            for omission in omissions:
+                cleaned = omission[0].sub (omission[1], cleaned)
+
+            # Validate curly bracket indentation
+            if '{' in cleaned and not '}' in cleaned:
+                curly.append ((cleaned.index ('{'), cleaned))
+            if '}' in cleaned and not '{' in cleaned and not '},' in cleaned:
+                if len (curly) == 0 or curly[-1][0] != cleaned.index ('}'):
+                    print (fmt % (filename, i, 'Misindented curly bracket'))
+                    print (curly[-1][1])
+                    print (line)
+                    curly.pop()
+                    continue
+                curly.pop()
+
+            # Validate preprocessor indentation
+            # FIXME: Don't warn if the *following* line is a curly
+            cpp = cleaned.find ('#if')
+            if cpp != -1:
+                if len (curly) != 0 and cpp != curly[-1][0] + 4:
+                    print (fmt % (filename, i, 'Misindented preprocessor #if'))
+                    print (curly[-1][1])
+                    print (line)
+
+            violated = False
+            for violation in violations:
+                if violation[0].search (cleaned):
+                    violated = True
+                    print (fmt % (filename, i, violation[1]))
+            if violated:
+                print (line)
+