7 FIRST_INCLUDE
= 'config.h'
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'"],
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
):
49 print >> sys
.stderr
, "error: %s: %s" % (filename
, desc
)
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 *[^*]')
66 m
= stat_use_re
.search(line
[1])
68 msgfmt
= "%d: If you use struct stat, you must #include <sys/stat.h> first"
69 Problem(filename
, msgfmt
% line
[0])
72 m
= stat_hdr_re
.search(line
[1])
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
:
84 "%s should be included by most files" % FIRST_INCLUDE
)
86 def CheckLineSmells(filename
, lines
, fulltext
):
88 for smell
in COMPILED_LINE_SMELLS
:
89 match
= smell
[0].search(line
[1])
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
)
99 Problem(filename
, ' %s'
105 def SniffSourceFile(filename
, lines
, fulltext
):
106 for sniffer
in [CheckFirstInclude
, CheckStatHeader
,
107 CheckLineSmells
, CheckFileSmells
]:
108 sniffer(filename
, lines
, fulltext
)
113 for srcfile
in args
[1:]:
117 for line
in f
.readlines():
118 lines
.append( (line_number
, line
) )
120 fulltext
= '\n'.join([line
[1] for line
in lines
])
121 SniffSourceFile(srcfile
, lines
, fulltext
)
129 if __name__
== "__main__":
130 sys
.exit(main(sys
.argv
))