cvsimport
[findutils.git] / build-aux / src-sniff.py
blob85e6506b217932816e19b54e354b6f6c58da9ffe
1 #! /usr/bin/env python
4 import re
5 import sys
7 FIRST_INCLUDE = 'config.h'
8 problems = 0
10 LINE_SMELLS_SRC = [
11 [r'(?<!\w)free \(\(', "don't cast the argument to free()"],
12 [r'\*\) *x(m|c|re)alloc(?!\w)',"don't cast the result of x*alloc"],
13 [r'\*\) *alloca(?!\w)',"don't cast the result of alloca"],
14 [r'[ ] ',"found SPACE-TAB; remove the space"],
15 [r'(?<!\w)([fs]?scanf|ato([filq]|ll))(?!\w)',
16 'do not use *scan''f, ato''f, ato''i, ato''l, ato''ll, ato''q, or ss''canf'],
17 [r'error \(EXIT_SUCCESS',"passing EXIT_SUCCESS to error is confusing"],
18 [r'file[s]ystem', "prefer writing 'file system' to 'filesystem'"],
19 [r'HAVE''_CONFIG_H', "Avoid checking HAVE_CONFIG_H"],
20 # [r'HAVE_FCNTL_H', "Avoid checking HAVE_FCNTL_H"],
21 [r'O_NDELAY', "Avoid using O_NDELAY"],
22 [r'the *the', "'the the' is probably not deliberate"],
23 [r'(?<!\w)error \([^_"]*[^_]"[^"]*[a-z]{3}', "untranslated error message"],
24 [r'^# *if\s+defined *\(', "useless parentheses in '#if defined'"],
28 FILE_SMELLS_SRC = [
29 [r'# *include <assert.h>(?!.*assert \()',
30 "If you include <assert.h>, use assert()."],
31 [r'# *include "quotearg.h"(?!.*(?<!\w)quotearg(_[^ ]+)? \()',
32 "If you include \"quotearg.h\", use one of its functions."],
33 [r'# *include "quote.h"(?!.*(?<!\w)quote(_[^ ]+)? \()',
34 "If you include \"quote.h\", use one of its functions."],
35 [r'\s$', "trailing whitespace"],
38 # missing check: ChangeLog prefixes
39 # missing: sc_always_defined_macros from coreutils
40 # missing: sc_tight_scope
42 COMPILED_LINE_SMELLS = [(re.compile(pong[0]), pong[1])
43 for pong in LINE_SMELLS_SRC]
44 COMPILED_FILE_SMELLS = [(re.compile(pong[0], re.DOTALL), pong[1])
45 for pong in FILE_SMELLS_SRC]
47 def Problem(filename, desc):
48 global problems
49 print >> sys.stderr, "error: %s: %s" % (filename, desc)
50 problems += 1
52 def Warning(filename, desc):
53 print >> sys.stderr, "warning: %s: %s" % (filename, desc)
56 def BuildIncludeList(text):
57 include_re = re.compile(r'# *include +[<"](.*)[>"]')
58 return [m.group(1) for m in include_re.finditer(text)]
61 def CheckStatHeader(filename, lines, fulltext):
62 stat_hdr_re = re.compile(r'# *include .*<sys/stat.h>')
63 # It's OK to have a pointer though.
64 stat_use_re = re.compile(r'struct stat\W *[^*]')
65 for line in lines:
66 m = stat_use_re.search(line[1])
67 if m:
68 msgfmt = "%d: If you use struct stat, you must #include <sys/stat.h> first"
69 Problem(filename, msgfmt % line[0])
70 # Diagnose only once
71 break
72 m = stat_hdr_re.search(line[1])
73 if m:
74 break
76 def CheckFirstInclude(filename, lines, fulltext):
77 includes = BuildIncludeList(fulltext)
78 if includes and includes[0] != FIRST_INCLUDE:
79 if FIRST_INCLUDE in includes:
80 fmt = "%s is first include file, but %s should be included first"
81 Problem(filename, fmt % (includes[0], FIRST_INCLUDE))
82 if FIRST_INCLUDE not in includes:
83 Warning(filename,
84 "%s should be included by most files" % FIRST_INCLUDE)
86 def CheckLineSmells(filename, lines, fulltext):
87 for line in lines:
88 for smell in COMPILED_LINE_SMELLS:
89 match = smell[0].search(line[1])
90 if match:
91 Problem(filename, '%d: "%s": %s'
92 % (line[0], match.group(0), smell[1]))
95 def CheckFileSmells(filename, lines, fulltext):
96 for smell in COMPILED_FILE_SMELLS:
97 match = smell[0].search(fulltext)
98 if match:
99 Problem(filename, ' %s'
100 % (smell[1],))
105 def SniffSourceFile(filename, lines, fulltext):
106 for sniffer in [CheckFirstInclude, CheckStatHeader,
107 CheckLineSmells, CheckFileSmells]:
108 sniffer(filename, lines, fulltext)
111 def main(args):
112 "main program"
113 for srcfile in args[1:]:
114 f = open(srcfile)
115 line_number = 1
116 lines = []
117 for line in f.readlines():
118 lines.append( (line_number, line) )
119 line_number += 1
120 fulltext = '\n'.join([line[1] for line in lines])
121 SniffSourceFile(srcfile, lines, fulltext)
122 f.close()
123 if problems:
124 return 1
125 else:
126 return 0
129 if __name__ == "__main__":
130 sys.exit(main(sys.argv))