]> git.proxmox.com Git - mirror_ovs.git/blame - utilities/checkpatch.py
ovs-actions: Remove unneeded unicode symbols.
[mirror_ovs.git] / utilities / checkpatch.py
CommitLineData
c599d5cc 1#!/usr/bin/env python
ebba2af6 2# Copyright (c) 2016, 2017 Red Hat, Inc.
4e99b70d 3# Copyright (c) 2018 Nicira, Inc.
c599d5cc
AC
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at:
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16from __future__ import print_function
17
18import email
19import getopt
a1fccabc 20import os
c599d5cc
AC
21import re
22import sys
23
a9e5ac0f
BS
24RETURN_CHECK_INITIAL_STATE = 0
25RETURN_CHECK_STATE_WITH_RETURN = 1
26RETURN_CHECK_AWAITING_BRACE = 2
c599d5cc
AC
27__errors = 0
28__warnings = 0
a9e5ac0f 29empty_return_check_state = 0
3239c793 30print_file_name = None
fb9410d8 31checking_file = False
ebba2af6
AC
32total_line = 0
33colors = False
999c7773 34spellcheck_comments = False
de8fa82a 35quiet = False
0fb02e8e
BP
36spell_check_dict = None
37
38
39def open_spell_check_dict():
40 import enchant
41
42 try:
43 extra_keywords = ['ovs', 'vswitch', 'vswitchd', 'ovs-vswitchd',
44 'netdev', 'selinux', 'ovs-ctl', 'dpctl', 'ofctl',
45 'openvswitch', 'dpdk', 'hugepage', 'hugepages',
46 'pmd', 'upcall', 'vhost', 'rx', 'tx', 'vhostuser',
47 'openflow', 'qsort', 'rxq', 'txq', 'perf', 'stats',
48 'struct', 'int', 'char', 'bool', 'upcalls', 'nicira',
49 'bitmask', 'ipv4', 'ipv6', 'tcp', 'tcp4', 'tcpv4',
50 'udp', 'udp4', 'udpv4', 'icmp', 'icmp4', 'icmpv6',
51 'vlan', 'vxlan', 'cksum', 'csum', 'checksum',
52 'ofproto', 'numa', 'mempool', 'mempools', 'mbuf',
53 'mbufs', 'hmap', 'cmap', 'smap', 'dhcpv4', 'dhcp',
54 'dhcpv6', 'opts', 'metadata', 'geneve', 'mutex',
55 'netdev', 'netdevs', 'subtable', 'virtio', 'qos',
56 'policer', 'datapath', 'tunctl', 'attr', 'ethernet',
57 'ether', 'defrag', 'defragment', 'loopback', 'sflow',
58 'acl', 'initializer', 'recirc', 'xlated', 'unclosed',
59 'netlink', 'msec', 'usec', 'nsec', 'ms', 'us', 'ns',
60 'kilobits', 'kbps', 'kilobytes', 'megabytes', 'mbps',
61 'gigabytes', 'gbps', 'megabits', 'gigabits', 'pkts',
62 'tuple', 'miniflow', 'megaflow', 'conntrack',
63 'vlans', 'vxlans', 'arg', 'tpid', 'xbundle',
64 'xbundles', 'mbundle', 'mbundles', 'netflow',
65 'localnet', 'odp', 'pre', 'dst', 'dest', 'src',
66 'ethertype', 'cvlan', 'ips', 'msg', 'msgs',
67 'liveness', 'userspace', 'eventmask', 'datapaths',
68 'slowpath', 'fastpath', 'multicast', 'unicast',
69 'revalidation', 'namespace', 'qdisc', 'uuid',
70 'ofport', 'subnet', 'revalidation', 'revalidator',
71 'revalidate', 'l2', 'l3', 'l4', 'openssl', 'mtu',
72 'ifindex', 'enum', 'enums', 'http', 'https', 'num',
73 'vconn', 'vconns', 'conn', 'nat', 'memset', 'memcmp',
74 'strcmp', 'strcasecmp', 'tc', 'ufid', 'api',
75 'ofpbuf', 'ofpbufs', 'hashmaps', 'hashmap', 'deref',
76 'dereference', 'hw', 'prio', 'sendmmsg', 'sendmsg',
77 'malloc', 'free', 'alloc', 'pid', 'ppid', 'pgid',
78 'uid', 'gid', 'sid', 'utime', 'stime', 'cutime',
79 'cstime', 'vsize', 'rss', 'rsslim', 'whcan', 'gtime',
80 'eip', 'rip', 'cgtime', 'dbg', 'gw', 'sbrec', 'bfd',
81 'sizeof', 'pmds', 'nic', 'nics', 'hwol', 'encap',
82 'decap', 'tlv', 'tlvs', 'decapsulation', 'fd',
83 'cacheline', 'xlate', 'skiplist', 'idl',
84 'comparator', 'natting', 'alg', 'pasv', 'epasv',
85 'wildcard', 'nated', 'amd64', 'x86_64',
86 'recirculation']
87
88 global spell_check_dict
89 spell_check_dict = enchant.Dict("en_US")
90 for kw in extra_keywords:
91 spell_check_dict.add(kw)
92
93 return True
94 except:
95 return False
3239c793
AC
96
97
ebba2af6
AC
98def get_color_end():
99 global colors
100 if colors:
101 return "\033[00m"
102 return ""
c599d5cc
AC
103
104
ebba2af6
AC
105def get_red_begin():
106 global colors
107 if colors:
108 return "\033[91m"
109 return ""
110
111
112def get_yellow_begin():
113 global colors
114 if colors:
115 return "\033[93m"
116 return ""
117
118
119def print_error(message):
c599d5cc 120 global __errors
ebba2af6 121 print("%sERROR%s: %s" % (get_red_begin(), get_color_end(), message))
c599d5cc
AC
122
123 __errors = __errors + 1
124
125
ebba2af6 126def print_warning(message):
c599d5cc 127 global __warnings
ebba2af6 128 print("%sWARNING%s: %s" % (get_yellow_begin(), get_color_end(), message))
c599d5cc
AC
129
130 __warnings = __warnings + 1
131
132
81c2316f 133def reset_counters():
f61d40de 134 global __errors, __warnings, total_line
81c2316f
IM
135
136 __errors = 0
137 __warnings = 0
f61d40de 138 total_line = 0
81c2316f
IM
139
140
b537de13
BP
141# These are keywords whose names are normally followed by a space and
142# something in parentheses (usually an expression) then a left curly brace.
143#
144# 'do' almost qualifies but it's also used as "do { ... } while (...);".
145__parenthesized_constructs = 'if|for|while|switch|[_A-Z]+FOR_EACH[_A-Z]*'
146
c599d5cc 147__regex_added_line = re.compile(r'^\+{1,2}[^\+][\w\W]*')
4d7f5e51 148__regex_subtracted_line = re.compile(r'^\-{1,2}[^\-][\w\W]*')
c599d5cc
AC
149__regex_leading_with_whitespace_at_all = re.compile(r'^\s+')
150__regex_leading_with_spaces = re.compile(r'^ +[\S]+')
151__regex_trailing_whitespace = re.compile(r'[^\S]+$')
c61e93d6 152__regex_single_line_feed = re.compile(r'^\f$')
b537de13
BP
153__regex_for_if_missing_whitespace = re.compile(r' +(%s)[\(]'
154 % __parenthesized_constructs)
155__regex_for_if_too_much_whitespace = re.compile(r' +(%s) +[\(]'
156 % __parenthesized_constructs)
a1193b4d 157__regex_for_if_parens_whitespace = \
b537de13 158 re.compile(r' +(%s) \( +[\s\S]+\)' % __parenthesized_constructs)
30c7ffd5 159__regex_is_for_if_single_line_bracket = \
b537de13 160 re.compile(r'^ +(%s) \(.*\)' % __parenthesized_constructs)
a1193b4d
AC
161__regex_ends_with_bracket = \
162 re.compile(r'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$')
d56ec3bc 163__regex_ptr_declaration_missing_whitespace = re.compile(r'[a-zA-Z0-9]\*[^*]')
d6ec6c10 164__regex_is_comment_line = re.compile(r'^\s*(/\*|\*\s)')
4e99b70d 165__regex_has_comment = re.compile(r'.*(/\*|\*\s)')
12f62e9d 166__regex_trailing_operator = re.compile(r'^[^ ]* [^ ]*[?:]$')
3f9e248f
JS
167__regex_conditional_else_bracing = re.compile(r'^\s*else\s*{?$')
168__regex_conditional_else_bracing2 = re.compile(r'^\s*}\selse\s*$')
4e99b70d 169__regex_has_xxx_mark = re.compile(r'.*xxx.*', re.IGNORECASE)
8503a516
FL
170__regex_added_doc_rst = re.compile(
171 r'\ndiff .*Documentation/.*rst\nnew file mode')
a9e5ac0f 172__regex_empty_return = re.compile(r'\s*return;')
16770c6d
BS
173__regex_if_macros = re.compile(r'^ +(%s) \([\S][\s\S]+[\S]\) { \\' %
174 __parenthesized_constructs)
c599d5cc
AC
175
176skip_leading_whitespace_check = False
177skip_trailing_whitespace_check = False
178skip_block_whitespace_check = False
179skip_signoff_check = False
180
6982ee96
JS
181# Don't enforce character limit on files that include these characters in their
182# name, as they may have legitimate reasons to have longer lines.
183#
184# Python isn't checked as flake8 performs these checks during build.
9aef43f0
BP
185line_length_blacklist = re.compile(
186 r'\.(am|at|etc|in|m4|mk|patch|py)$|debian/rules')
6982ee96 187
ae9c0985
BP
188# Don't enforce a requirement that leading whitespace be all spaces on
189# files that include these characters in their name, since these kinds
190# of files need lines with leading tabs.
9aef43f0 191leading_whitespace_blacklist = re.compile(r'\.(mk|am|at)$|debian/rules')
ae9c0985 192
c599d5cc 193
4d7f5e51
AC
194def is_subtracted_line(line):
195 """Returns TRUE if the line in question has been removed."""
196 return __regex_subtracted_line.search(line) is not None
197
fb9410d8 198
c599d5cc
AC
199def is_added_line(line):
200 """Returns TRUE if the line in question is an added line.
201 """
fb9410d8
AC
202 global checking_file
203 return __regex_added_line.search(line) is not None or checking_file
204
205
206def added_line(line):
207 """Returns the line formatted properly by removing diff syntax"""
208 global checking_file
209 if not checking_file:
210 return line[1:]
211 return line
c599d5cc
AC
212
213
214def leading_whitespace_is_spaces(line):
215 """Returns TRUE if the leading whitespace in added lines is spaces
216 """
217 if skip_leading_whitespace_check:
218 return True
c61e93d6
DDP
219 if (__regex_leading_with_whitespace_at_all.search(line) is not None and
220 __regex_single_line_feed.search(line) is None):
c599d5cc 221 return __regex_leading_with_spaces.search(line) is not None
c61e93d6 222
c599d5cc
AC
223 return True
224
225
226def trailing_whitespace_or_crlf(line):
227 """Returns TRUE if the trailing characters is whitespace
228 """
229 if skip_trailing_whitespace_check:
230 return False
c61e93d6
DDP
231 return (__regex_trailing_whitespace.search(line) is not None and
232 __regex_single_line_feed.search(line) is None)
c599d5cc
AC
233
234
235def if_and_for_whitespace_checks(line):
236 """Return TRUE if there is appropriate whitespace after if, for, while
237 """
238 if skip_block_whitespace_check:
239 return True
240 if (__regex_for_if_missing_whitespace.search(line) is not None or
241 __regex_for_if_too_much_whitespace.search(line) is not None or
242 __regex_for_if_parens_whitespace.search(line)):
243 return False
244 return True
245
246
30c7ffd5
AC
247def if_and_for_end_with_bracket_check(line):
248 """Return TRUE if there is not a bracket at the end of an if, for, while
249 block which fits on a single line ie: 'if (foo)'"""
250
251 def balanced_parens(line):
252 """This is a rather naive counter - it won't deal with quotes"""
253 balance = 0
254 for letter in line:
973496b9 255 if letter == '(':
30c7ffd5 256 balance += 1
973496b9 257 elif letter == ')':
30c7ffd5 258 balance -= 1
973496b9 259 return balance == 0
30c7ffd5
AC
260
261 if __regex_is_for_if_single_line_bracket.search(line) is not None:
262 if not balanced_parens(line):
263 return True
16770c6d
BS
264
265 if __regex_ends_with_bracket.search(line) is None and \
266 __regex_if_macros.match(line) is None:
30c7ffd5 267 return False
3f9e248f
JS
268 if __regex_conditional_else_bracing.match(line) is not None:
269 return False
270 if __regex_conditional_else_bracing2.match(line) is not None:
271 return False
30c7ffd5
AC
272 return True
273
274
6fc2799e
JS
275def pointer_whitespace_check(line):
276 """Return TRUE if there is no space between a pointer name and the
277 asterisk that denotes this is a apionter type, ie: 'struct foo*'"""
278 return __regex_ptr_declaration_missing_whitespace.search(line) is not None
279
280
517f04ad
AC
281def line_length_check(line):
282 """Return TRUE if the line length is too long"""
283 if len(line) > 79:
860ffe70
BP
284 print_warning("Line is %d characters long (recommended limit is 79)"
285 % len(line))
517f04ad
AC
286 return True
287 return False
288
289
d6ec6c10
BP
290def is_comment_line(line):
291 """Returns TRUE if the current line is part of a block comment."""
292 return __regex_is_comment_line.match(line) is not None
293
8df9a0c4 294
4e99b70d
JP
295def has_comment(line):
296 """Returns TRUE if the current line contains a comment or is part of
297 a block comment."""
298 return __regex_has_comment.match(line) is not None
d6ec6c10 299
8df9a0c4 300
12f62e9d
JS
301def trailing_operator(line):
302 """Returns TRUE if the current line ends with an operatorsuch as ? or :"""
303 return __regex_trailing_operator.match(line) is not None
304
8df9a0c4 305
4e99b70d
JP
306def has_xxx_mark(line):
307 """Returns TRUE if the current line contains 'xxx'."""
308 return __regex_has_xxx_mark.match(line) is not None
309
12f62e9d 310
999c7773 311def filter_comments(current_line, keep=False):
8361e647
AC
312 """remove all of the c-style comments in a line"""
313 STATE_NORMAL = 0
314 STATE_COMMENT_SLASH = 1
315 STATE_COMMENT_CONTENTS = 3
316 STATE_COMMENT_END_SLASH = 4
317
318 state = STATE_NORMAL
319 sanitized_line = ''
320 check_state = STATE_NORMAL
321 only_whitespace = True
322
999c7773
AC
323 if keep:
324 check_state = STATE_COMMENT_CONTENTS
325
8361e647
AC
326 for c in current_line:
327 if c == '/':
328 if state == STATE_NORMAL:
329 state = STATE_COMMENT_SLASH
330 elif state == STATE_COMMENT_SLASH:
331 # This is for c++ style comments. We will warn later
332 return sanitized_line[:1]
333 elif state == STATE_COMMENT_END_SLASH:
334 c = ''
335 state = STATE_NORMAL
336 elif c == '*':
337 if only_whitespace:
338 # just assume this is a continuation from the previous line
339 # as a comment
340 state = STATE_COMMENT_END_SLASH
341 elif state == STATE_COMMENT_SLASH:
342 state = STATE_COMMENT_CONTENTS
343 sanitized_line = sanitized_line[:-1]
344 elif state == STATE_COMMENT_CONTENTS:
345 state = STATE_COMMENT_END_SLASH
346 elif state == STATE_COMMENT_END_SLASH:
347 # Need to re-introduce the star from the previous state, since
348 # it may have been clipped by the state check below.
349 c = '*' + c
350 state = STATE_COMMENT_CONTENTS
351 elif state == STATE_COMMENT_SLASH:
352 # Need to re-introduce the slash from the previous state, since
353 # it may have been clipped by the state check below.
354 c = '/' + c
355 state = STATE_NORMAL
356
357 if state != check_state:
358 c = ''
359
360 if not c.isspace():
361 only_whitespace = False
362
363 sanitized_line += c
364
365 return sanitized_line
366
367
999c7773 368def check_comment_spelling(line):
0fb02e8e 369 if not spell_check_dict or not spellcheck_comments:
999c7773
AC
370 return False
371
372 comment_words = filter_comments(line, True).replace(':', ' ').split(' ')
373 for word in comment_words:
374 skip = False
375 strword = re.subn(r'\W+', '', word)[0].replace(',', '')
376 if len(strword) and not spell_check_dict.check(strword.lower()):
377 if any([check_char in word
378 for check_char in ['=', '(', '-', '_', '/', '\'']]):
379 skip = True
380
381 # special case the '.'
382 if '.' in word and not word.endswith('.'):
383 skip = True
384
385 # skip proper nouns and references to macros
386 if strword.isupper() or (strword[0].isupper() and
387 strword[1:].islower()):
388 skip = True
389
390 # skip words that start with numbers
391 if strword.startswith(tuple('0123456789')):
392 skip = True
393
394 if not skip:
860ffe70
BP
395 print_warning("Check for spelling mistakes (e.g. \"%s\")"
396 % strword)
999c7773
AC
397 return True
398
399 return False
400
401
8503a516
FL
402def __check_doc_is_listed(text, doctype, docdir, docfile):
403 if doctype == 'rst':
404 beginre = re.compile(r'\+\+\+.*{}/index.rst'.format(docdir))
405 docre = re.compile(r'\n\+.*{}'.format(docfile.replace('.rst', '')))
406 elif doctype == 'automake':
407 beginre = re.compile(r'\+\+\+.*Documentation/automake.mk')
408 docre = re.compile(r'\n\+\t{}/{}'.format(docdir, docfile))
409 else:
410 raise NotImplementedError("Invalid doctype: {}".format(doctype))
411
412 res = beginre.search(text)
413 if res is None:
414 return True
415
416 hunkstart = res.span()[1]
417 hunkre = re.compile(r'\n(---|\+\+\+) (\S+)')
418 res = hunkre.search(text[hunkstart:])
419 if res is None:
420 hunkend = len(text)
421 else:
422 hunkend = hunkstart + res.span()[0]
423
424 hunk = text[hunkstart:hunkend]
425 # find if the file is being added.
426 if docre.search(hunk) is not None:
427 return False
428
429 return True
430
431
432def __check_new_docs(text, doctype):
433 """Check if the documentation is listed properly. If doctype is 'rst' then
434 the index.rst is checked. If the doctype is 'automake' then automake.mk
435 is checked. Returns TRUE if the new file is not listed."""
436 failed = False
437 new_docs = __regex_added_doc_rst.findall(text)
438 for doc in new_docs:
439 docpathname = doc.split(' ')[2]
440 gitdocdir, docfile = os.path.split(docpathname.rstrip('\n'))
441 if docfile == "index.rst":
442 continue
443
444 if gitdocdir.startswith('a/'):
445 docdir = gitdocdir.replace('a/', '', 1)
446 else:
447 docdir = gitdocdir
448
449 if __check_doc_is_listed(text, doctype, docdir, docfile):
450 if doctype == 'rst':
451 print_warning("New doc {} not listed in {}/index.rst".format(
452 docfile, docdir))
453 elif doctype == 'automake':
454 print_warning("New doc {} not listed in "
455 "Documentation/automake.mk".format(docfile))
456 else:
457 raise NotImplementedError("Invalid doctype: {}".format(
458 doctype))
459
460 failed = True
461
462 return failed
463
464
465def check_doc_docs_automake(text):
466 return __check_new_docs(text, 'automake')
467
468
469def check_new_docs_index(text):
470 return __check_new_docs(text, 'rst')
471
472
a9e5ac0f
BS
473def empty_return_with_brace(line):
474 """Returns TRUE if a function contains a return; followed
475 by one or more line feeds and terminates with a '}'
476 at start of line"""
477
478 def empty_return(line):
479 """Returns TRUE if a function has a 'return;'"""
480 return __regex_empty_return.match(line) is not None
481
482 global empty_return_check_state
483 if empty_return_check_state == RETURN_CHECK_INITIAL_STATE \
484 and empty_return(line):
485 empty_return_check_state = RETURN_CHECK_STATE_WITH_RETURN
486 elif empty_return_check_state == RETURN_CHECK_STATE_WITH_RETURN \
487 and (re.match(r'^}$', line) or len(line) == 0):
488 if re.match('^}$', line):
489 empty_return_check_state = RETURN_CHECK_AWAITING_BRACE
490 else:
491 empty_return_check_state = RETURN_CHECK_INITIAL_STATE
492
493 if empty_return_check_state == RETURN_CHECK_AWAITING_BRACE:
494 empty_return_check_state = RETURN_CHECK_INITIAL_STATE
495 return True
496
497 return False
498
499
8503a516
FL
500file_checks = [
501 {'regex': __regex_added_doc_rst,
502 'check': check_new_docs_index},
503 {'regex': __regex_added_doc_rst,
504 'check': check_doc_docs_automake}
505]
506
517f04ad
AC
507checks = [
508 {'regex': None,
09b94206 509 'match_name': lambda x: not line_length_blacklist.search(x),
860ffe70 510 'check': lambda x: line_length_check(x)},
907848bd 511
ae9c0985 512 {'regex': None,
09b94206 513 'match_name': lambda x: not leading_whitespace_blacklist.search(x),
907848bd 514 'check': lambda x: not leading_whitespace_is_spaces(x),
ebba2af6 515 'print': lambda: print_warning("Line has non-spaces leading whitespace")},
907848bd
AC
516
517 {'regex': None, 'match_name': None,
518 'check': lambda x: trailing_whitespace_or_crlf(x),
ebba2af6 519 'print': lambda: print_warning("Line has trailing whitespace")},
907848bd 520
bcd93351 521 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
d6ec6c10 522 'prereq': lambda x: not is_comment_line(x),
907848bd 523 'check': lambda x: not if_and_for_whitespace_checks(x),
ebba2af6 524 'print': lambda: print_error("Improper whitespace around control block")},
907848bd 525
bcd93351 526 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
d6ec6c10 527 'prereq': lambda x: not is_comment_line(x),
907848bd 528 'check': lambda x: not if_and_for_end_with_bracket_check(x),
ebba2af6 529 'print': lambda: print_error("Inappropriate bracing around statement")},
907848bd 530
bcd93351 531 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
d6ec6c10 532 'prereq': lambda x: not is_comment_line(x),
907848bd
AC
533 'check': lambda x: pointer_whitespace_check(x),
534 'print':
12f62e9d
JS
535 lambda: print_error("Inappropriate spacing in pointer declaration")},
536
537 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
538 'prereq': lambda x: not is_comment_line(x),
539 'check': lambda x: trailing_operator(x),
540 'print':
541 lambda: print_error("Line has '?' or ':' operator at end of line")},
4e99b70d
JP
542
543 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
544 'prereq': lambda x: has_comment(x),
545 'check': lambda x: has_xxx_mark(x),
546 'print': lambda: print_warning("Comment with 'xxx' marker")},
999c7773
AC
547
548 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
549 'prereq': lambda x: has_comment(x),
860ffe70 550 'check': lambda x: check_comment_spelling(x)},
a9e5ac0f
BS
551
552 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
553 'check': lambda x: empty_return_with_brace(x),
554 'interim_line': True,
555 'print':
556 lambda: print_warning("Empty return followed by brace, consider omitting")
557 },
517f04ad
AC
558]
559
560
b95d82bf 561def regex_function_factory(func_name):
6ecf961e 562 regex = re.compile(r'\b%s\([^)]*\)' % func_name)
b95d82bf
JS
563 return lambda x: regex.search(x) is not None
564
565
566def regex_error_factory(description):
567 return lambda: print_error(description)
568
569
570std_functions = [
571 ('malloc', 'Use xmalloc() in place of malloc()'),
572 ('calloc', 'Use xcalloc() in place of calloc()'),
573 ('realloc', 'Use xrealloc() in place of realloc()'),
574 ('strdup', 'Use xstrdup() in place of strdup()'),
575 ('asprintf', 'Use xasprintf() in place of asprintf()'),
576 ('vasprintf', 'Use xvasprintf() in place of vasprintf()'),
577 ('strcpy', 'Use ovs_strlcpy() in place of strcpy()'),
578 ('strlcpy', 'Use ovs_strlcpy() in place of strlcpy()'),
579 ('strncpy', 'Use ovs_strzcpy() in place of strncpy()'),
580 ('strerror', 'Use ovs_strerror() in place of strerror()'),
581 ('sleep', 'Use xsleep() in place of sleep()'),
582 ('abort', 'Use ovs_abort() in place of abort()'),
d9d849fe 583 ('assert', 'Use ovs_assert() in place of assert()'),
b95d82bf
JS
584 ('error', 'Use ovs_error() in place of error()'),
585]
586checks += [
bcd93351 587 {'regex': '(\.c|\.h)(\.in)?$',
b95d82bf 588 'match_name': None,
d6ec6c10 589 'prereq': lambda x: not is_comment_line(x),
b95d82bf
JS
590 'check': regex_function_factory(function_name),
591 'print': regex_error_factory(description)}
592 for (function_name, description) in std_functions]
593
594
0d7b16da
JS
595def regex_operator_factory(operator):
596 regex = re.compile(r'^[^#][^"\']*[^ "]%s[^ "\'][^"]*' % operator)
8361e647 597 return lambda x: regex.search(filter_comments(x)) is not None
0d7b16da
JS
598
599
600infix_operators = \
bbb2cb20 601 [re.escape(op) for op in ['%', '<<', '>>', '<=', '>=', '==', '!=',
0d7b16da
JS
602 '^', '|', '&&', '||', '?:', '=', '+=', '-=', '*=', '/=', '%=',
603 '&=', '^=', '|=', '<<=', '>>=']] \
604 + ['[^<" ]<[^=" ]', '[^->" ]>[^=" ]', '[^ !()/"]\*[^/]', '[^ !&()"]&',
bbb2cb20 605 '[^" +(]\+[^"+;]', '[^" -(]-[^"->;]', '[^" <>=!^|+\-*/%&]=[^"=]',
0056086d 606 '[^* ]/[^* ]']
0d7b16da
JS
607checks += [
608 {'regex': '(\.c|\.h)(\.in)?$', 'match_name': None,
609 'prereq': lambda x: not is_comment_line(x),
610 'check': regex_operator_factory(operator),
611 'print': lambda: print_warning("Line lacks whitespace around operator")}
612 for operator in infix_operators]
613
614
517f04ad
AC
615def get_file_type_checks(filename):
616 """Returns the list of checks for a file based on matching the filename
617 against regex."""
618 global checks
619 checkList = []
620 for check in checks:
621 if check['regex'] is None and check['match_name'] is None:
622 checkList.append(check)
623 if check['regex'] is not None and \
624 re.compile(check['regex']).search(filename) is not None:
625 checkList.append(check)
626 elif check['match_name'] is not None and check['match_name'](filename):
627 checkList.append(check)
628 return checkList
629
630
631def run_checks(current_file, line, lineno):
632 """Runs the various checks for the particular line. This will take
633 filename into account."""
ebba2af6 634 global checking_file, total_line
a84a1edb 635 print_line = False
517f04ad 636 for check in get_file_type_checks(current_file):
d6ec6c10
BP
637 if 'prereq' in check and not check['prereq'](line):
638 continue
517f04ad 639 if check['check'](line):
860ffe70
BP
640 if 'print' in check:
641 check['print']()
a84a1edb
AC
642 print_line = True
643
644 if print_line:
ebba2af6
AC
645 if checking_file:
646 print("%s:%d:" % (current_file, lineno))
647 else:
648 print("#%d FILE: %s:%d:" % (total_line, current_file, lineno))
649 print("%s\n" % line)
517f04ad
AC
650
651
a9e5ac0f
BS
652def interim_line_check(current_file, line, lineno):
653 """Runs the various checks for the particular interim line. This will
654 take filename into account, and will check for the 'interim_line'
655 key before running the check."""
656 global checking_file, total_line
657 print_line = False
658 for check in get_file_type_checks(current_file):
659 if 'prereq' in check and not check['prereq'](line):
660 continue
661 if 'interim_line' in check and check['interim_line']:
662 if check['check'](line):
663 if 'print' in check:
664 check['print']()
665 print_line = True
666
667 if print_line:
668 if checking_file:
669 print("%s:%d:" % (current_file, lineno))
670 else:
671 print("#%d FILE: %s:%d:" % (total_line, current_file, lineno))
672 print("%s\n" % line)
673
674
8503a516
FL
675def run_file_checks(text):
676 """Runs the various checks for the text."""
677 for check in file_checks:
678 if check['regex'].search(text) is not None:
679 check['check'](text)
680
681
3267343a 682def ovs_checkpatch_parse(text, filename, author=None, committer=None):
a9e5ac0f
BS
683 global print_file_name, total_line, checking_file, \
684 empty_return_check_state
6ed80686
AC
685
686 PARSE_STATE_HEADING = 0
687 PARSE_STATE_DIFF_HEADER = 1
688 PARSE_STATE_CHANGE_BODY = 2
689
c599d5cc
AC
690 lineno = 0
691 signatures = []
692 co_authors = []
693 parse = 0
95bd35d3 694 current_file = filename if checking_file else ''
6982ee96 695 previous_file = ''
128b46a0 696 seppatch = re.compile(r'^---([\w]*| \S+)$')
c599d5cc 697 hunks = re.compile('^(---|\+\+\+) (\S+)')
4d7f5e51
AC
698 hunk_differences = re.compile(
699 r'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
3267343a
BP
700 is_author = re.compile(r'^(Author|From): (.*)$', re.I | re.M | re.S)
701 is_committer = re.compile(r'^(Commit: )(.*)$', re.I | re.M | re.S)
3ccb8899 702 is_signature = re.compile(r'^(Signed-off-by: )(.*)$',
c599d5cc 703 re.I | re.M | re.S)
3ccb8899 704 is_co_author = re.compile(r'^(Co-authored-by: )(.*)$',
c599d5cc 705 re.I | re.M | re.S)
bc03d850
IM
706 is_gerrit_change_id = re.compile(r'(\s*(change-id: )(.*))$',
707 re.I | re.M | re.S)
c599d5cc 708
81c2316f
IM
709 reset_counters()
710
822a3d88 711 for line in text.split('\n'):
6982ee96
JS
712 if current_file != previous_file:
713 previous_file = current_file
6982ee96 714
c599d5cc 715 lineno = lineno + 1
ebba2af6 716 total_line = total_line + 1
c599d5cc
AC
717 if len(line) <= 0:
718 continue
719
fb9410d8 720 if checking_file:
6ed80686 721 parse = PARSE_STATE_CHANGE_BODY
fb9410d8 722
6ed80686 723 if parse == PARSE_STATE_DIFF_HEADER:
c599d5cc
AC
724 match = hunks.match(line)
725 if match:
6ed80686 726 parse = PARSE_STATE_CHANGE_BODY
2797ff00 727 current_file = match.group(2)[2:]
3239c793 728 print_file_name = current_file
c599d5cc 729 continue
6ed80686 730 elif parse == PARSE_STATE_HEADING:
128b46a0 731 if seppatch.match(line):
6ed80686 732 parse = PARSE_STATE_DIFF_HEADER
c599d5cc 733 if not skip_signoff_check:
3267343a
BP
734
735 # Check that the patch has an author, that the
736 # author is not among the co-authors, and that the
737 # co-authors are unique.
738 if not author:
739 print_error("Patch lacks author.")
740 continue
3bd2e465
BP
741 if " via " in author or "@openvswitch.org" in author:
742 print_error("Author should not be mailing list.")
743 continue
3267343a
BP
744 if author in co_authors:
745 print_error("Author should not be also be co-author.")
746 continue
747 if len(set(co_authors)) != len(co_authors):
748 print_error("Duplicate co-author.")
749
750 # Check that the author, all co-authors, and the
751 # committer (if any) signed off.
752 if author not in signatures:
753 print_error("Author %s needs to sign off." % author)
754 for ca in co_authors:
755 if ca not in signatures:
756 print_error("Co-author %s needs to sign off." % ca)
757 break
758 if (committer
759 and author != committer
760 and committer not in signatures):
761 print_error("Committer %s needs to sign off."
762 % committer)
763
764 # Check for signatures that we do not expect.
765 # This is only a warning because there can be,
766 # rarely, a signature chain.
767 #
768 # If we don't have a known committer, and there is
769 # a single extra sign-off, then do not warn
770 # because that extra sign-off is probably the
771 # committer.
772 extra_sigs = [x for x in signatures
773 if x not in co_authors
774 and x != author
775 and x != committer]
776 if len(extra_sigs) > 1 or (committer and extra_sigs):
777 print_warning("Unexpected sign-offs from developers "
778 "who are not authors or co-authors or "
779 "committers: %s"
780 % ", ".join(extra_sigs))
781 elif is_committer.match(line):
782 committer = is_committer.match(line).group(2)
783 elif is_author.match(line):
784 author = is_author.match(line).group(2)
c599d5cc
AC
785 elif is_signature.match(line):
786 m = is_signature.match(line)
3ccb8899 787 signatures.append(m.group(2))
c599d5cc
AC
788 elif is_co_author.match(line):
789 m = is_co_author.match(line)
3ccb8899 790 co_authors.append(m.group(2))
bc03d850
IM
791 elif is_gerrit_change_id.match(line):
792 print_error(
793 "Remove Gerrit Change-Id's before submitting upstream.")
794 print("%d: %s\n" % (lineno, line))
6ed80686 795 elif parse == PARSE_STATE_CHANGE_BODY:
c599d5cc
AC
796 newfile = hunks.match(line)
797 if newfile:
bbbe2fa2 798 current_file = newfile.group(2)[2:]
3239c793 799 print_file_name = current_file
c599d5cc 800 continue
4d7f5e51
AC
801 reset_line_number = hunk_differences.match(line)
802 if reset_line_number:
a9e5ac0f 803 empty_return_check_state = RETURN_CHECK_INITIAL_STATE
4d7f5e51
AC
804 lineno = int(reset_line_number.group(3))
805 if lineno < 0:
806 lineno = -1 * lineno
807 lineno -= 1
a9e5ac0f 808
4d7f5e51
AC
809 if is_subtracted_line(line):
810 lineno -= 1
c599d5cc 811 continue
fb9410d8
AC
812
813 cmp_line = added_line(line)
814
a9e5ac0f
BS
815 if not is_added_line(line):
816 interim_line_check(current_file, cmp_line, lineno)
817 continue
818
c599d5cc
AC
819 # Skip files which have /datapath in them, since they are
820 # linux or windows coding standards
2797ff00 821 if current_file.startswith('datapath'):
c599d5cc 822 continue
4f74db48
JS
823 if current_file.startswith('include/linux'):
824 continue
517f04ad 825 run_checks(current_file, cmp_line, lineno)
8503a516
FL
826
827 run_file_checks(text)
c599d5cc
AC
828 if __errors or __warnings:
829 return -1
830 return 0
831
832
833def usage():
a1fccabc
BP
834 print("""\
835Open vSwitch checkpatch.py
836Checks a patch for trivial mistakes.
837usage:
b90cfa86 838%s [options] [PATCH1 [PATCH2 ...] | -f SOURCE1 [SOURCE2 ...] | -1 | -2 | ...]
a1fccabc
BP
839
840Input options:
841-f|--check-file Arguments are source files, not patches.
842-1, -2, ... Check recent commits in this repo.
843
844Check options:
845-h|--help This help message
846-b|--skip-block-whitespace Skips the if/while/for whitespace tests
847-l|--skip-leading-whitespace Skips the leading whitespace test
de8fa82a 848-q|--quiet Only print error and warning information
a1fccabc 849-s|--skip-signoff-lines Tolerate missing Signed-off-by line
999c7773 850-S|--spellcheck-comments Check C comments for possible spelling mistakes
a1fccabc
BP
851-t|--skip-trailing-whitespace Skips the trailing whitespace test"""
852 % sys.argv[0])
c599d5cc 853
a5e9c53c 854
81c2316f 855def ovs_checkpatch_print_result(result):
de8fa82a
AC
856 global quiet, __warnings, __errors, total_line
857
81c2316f
IM
858 if result < 0:
859 print("Lines checked: %d, Warnings: %d, Errors: %d\n" %
860 (total_line, __warnings, __errors))
de8fa82a 861 elif not quiet:
81c2316f
IM
862 print("Lines checked: %d, no obvious problems found\n" % (total_line))
863
864
c599d5cc
AC
865def ovs_checkpatch_file(filename):
866 try:
867 mail = email.message_from_file(open(filename, 'r'))
868 except:
869 print_error("Unable to parse file '%s'. Is it a patch?" % filename)
870 return -1
871
872 for part in mail.walk():
873 if part.get_content_maintype() == 'multipart':
874 continue
3267343a
BP
875 result = ovs_checkpatch_parse(part.get_payload(decode=False), filename,
876 mail.get('Author', mail['From']),
877 mail['Commit'])
81c2316f 878 ovs_checkpatch_print_result(result)
7d6b834f 879 return result
c599d5cc 880
884e0dfe 881
a1fccabc
BP
882def partition(pred, iterable):
883 """Returns [[trues], [falses]], where [trues] is the items in
884 'iterable' that satisfy 'pred' and [falses] is all the rest."""
885 trues = []
886 falses = []
887 for item in iterable:
888 if pred(item):
889 trues.append(item)
890 else:
891 falses.append(item)
892 return trues, falses
893
894
c599d5cc
AC
895if __name__ == '__main__':
896 try:
a1fccabc
BP
897 numeric_options, args = partition(lambda s: re.match('-[0-9]+$', s),
898 sys.argv[1:])
899 n_patches = int(numeric_options[-1][1:]) if numeric_options else 0
900
de8fa82a 901 optlist, args = getopt.getopt(args, 'bhlstfSq',
fb9410d8
AC
902 ["check-file",
903 "help",
c599d5cc
AC
904 "skip-block-whitespace",
905 "skip-leading-whitespace",
906 "skip-signoff-lines",
999c7773 907 "skip-trailing-whitespace",
de8fa82a
AC
908 "spellcheck-comments",
909 "quiet"])
c599d5cc
AC
910 except:
911 print("Unknown option encountered. Please rerun with -h for help.")
912 sys.exit(-1)
913
914 for o, a in optlist:
915 if o in ("-h", "--help"):
916 usage()
917 sys.exit(0)
918 elif o in ("-b", "--skip-block-whitespace"):
919 skip_block_whitespace_check = True
920 elif o in ("-l", "--skip-leading-whitespace"):
921 skip_leading_whitespace_check = True
922 elif o in ("-s", "--skip-signoff-lines"):
923 skip_signoff_check = True
924 elif o in ("-t", "--skip-trailing-whitespace"):
925 skip_trailing_whitespace_check = True
fb9410d8
AC
926 elif o in ("-f", "--check-file"):
927 checking_file = True
999c7773 928 elif o in ("-S", "--spellcheck-comments"):
0fb02e8e 929 if not open_spell_check_dict():
64b90b30 930 print("WARNING: The enchant library isn't available.")
999c7773
AC
931 print(" Please install python enchant.")
932 else:
933 spellcheck_comments = True
de8fa82a
AC
934 elif o in ("-q", "--quiet"):
935 quiet = True
c599d5cc
AC
936 else:
937 print("Unknown option '%s'" % o)
938 sys.exit(-1)
ebba2af6
AC
939
940 if sys.stdout.isatty():
941 colors = True
942
a1fccabc
BP
943 if n_patches:
944 status = 0
d962bad2
IM
945
946 git_log = 'git log --no-color --no-merges --pretty=format:"%H %s" '
947 with os.popen(git_log + '-%d' % n_patches, 'r') as f:
948 commits = f.read().split("\n")
949
057653ab 950 for i in reversed(range(0, n_patches)):
d962bad2 951 revision, name = commits[i].split(" ", 1)
3267343a
BP
952 f = os.popen('''git format-patch -1 --stdout --pretty=format:"\
953Author: %an <%ae>
954Commit: %cn <%ce>
955Subject: %s
956
957%b" ''' + revision, 'r')
a1fccabc
BP
958 patch = f.read()
959 f.close()
960
de8fa82a
AC
961 if not quiet:
962 print('== Checking %s ("%s") ==' % (revision[0:12], name))
81c2316f
IM
963 result = ovs_checkpatch_parse(patch, revision)
964 ovs_checkpatch_print_result(result)
965 if result:
a1fccabc
BP
966 status = -1
967 sys.exit(status)
968
b90cfa86 969 if not args:
c599d5cc
AC
970 if sys.stdin.isatty():
971 usage()
972 sys.exit(-1)
81c2316f
IM
973 result = ovs_checkpatch_parse(sys.stdin.read(), '-')
974 ovs_checkpatch_print_result(result)
975 sys.exit(result)
b90cfa86
IM
976
977 status = 0
978 for filename in args:
de8fa82a
AC
979 if not quiet:
980 print('== Checking "%s" ==' % filename)
b90cfa86
IM
981 result = ovs_checkpatch_file(filename)
982 if result:
983 status = -1
984 sys.exit(status)