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