]> git.proxmox.com Git - mirror_ovs.git/blame - utilities/checkpatch.py
ovn-trace: Implement ct_next and ct_clear actions.
[mirror_ovs.git] / utilities / checkpatch.py
CommitLineData
c599d5cc
AC
1#!/usr/bin/env python
2# Copyright (c) 2016 Red Hat, Inc.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at:
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15from __future__ import print_function
16
17import email
18import getopt
19import re
20import sys
21
22__errors = 0
23__warnings = 0
3239c793 24print_file_name = None
fb9410d8 25checking_file = False
3239c793
AC
26
27
28def print_file():
29 global print_file_name
30 if print_file_name:
31 print("In file %s" % print_file_name)
32 print_file_name = None
c599d5cc
AC
33
34
35def print_error(message, lineno=None):
36 global __errors
3239c793 37 print_file()
c599d5cc
AC
38 if lineno is not None:
39 print("E(%d): %s" % (lineno, message))
40 else:
41 print("E: %s" % (message))
42
43 __errors = __errors + 1
44
45
46def print_warning(message, lineno=None):
47 global __warnings
3239c793 48 print_file()
c599d5cc
AC
49 if lineno:
50 print("W(%d): %s" % (lineno, message))
51 else:
52 print("W: %s" % (message))
53
54 __warnings = __warnings + 1
55
56
57__regex_added_line = re.compile(r'^\+{1,2}[^\+][\w\W]*')
4d7f5e51 58__regex_subtracted_line = re.compile(r'^\-{1,2}[^\-][\w\W]*')
c599d5cc
AC
59__regex_leading_with_whitespace_at_all = re.compile(r'^\s+')
60__regex_leading_with_spaces = re.compile(r'^ +[\S]+')
61__regex_trailing_whitespace = re.compile(r'[^\S]+$')
c61e93d6 62__regex_single_line_feed = re.compile(r'^\f$')
a1193b4d
AC
63__regex_for_if_missing_whitespace = re.compile(r' +(if|for|while)[\(]')
64__regex_for_if_too_much_whitespace = re.compile(r' +(if|for|while) +[\(]')
65__regex_for_if_parens_whitespace = \
66 re.compile(r' +(if|for|while) \( +[\s\S]+\)')
30c7ffd5
AC
67__regex_is_for_if_single_line_bracket = \
68 re.compile(r'^ +(if|for|while) \(.*\)')
a1193b4d
AC
69__regex_ends_with_bracket = \
70 re.compile(r'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$')
6fc2799e 71__regex_ptr_declaration_missing_whitespace = re.compile(r'[a-zA-Z0-9]\*')
c599d5cc
AC
72
73skip_leading_whitespace_check = False
74skip_trailing_whitespace_check = False
75skip_block_whitespace_check = False
76skip_signoff_check = False
77
6982ee96
JS
78# Don't enforce character limit on files that include these characters in their
79# name, as they may have legitimate reasons to have longer lines.
80#
81# Python isn't checked as flake8 performs these checks during build.
82line_length_blacklist = ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
83 '.py']
84
c599d5cc 85
4d7f5e51
AC
86def is_subtracted_line(line):
87 """Returns TRUE if the line in question has been removed."""
88 return __regex_subtracted_line.search(line) is not None
89
fb9410d8 90
c599d5cc
AC
91def is_added_line(line):
92 """Returns TRUE if the line in question is an added line.
93 """
fb9410d8
AC
94 global checking_file
95 return __regex_added_line.search(line) is not None or checking_file
96
97
98def added_line(line):
99 """Returns the line formatted properly by removing diff syntax"""
100 global checking_file
101 if not checking_file:
102 return line[1:]
103 return line
c599d5cc
AC
104
105
106def leading_whitespace_is_spaces(line):
107 """Returns TRUE if the leading whitespace in added lines is spaces
108 """
109 if skip_leading_whitespace_check:
110 return True
c61e93d6
DDP
111 if (__regex_leading_with_whitespace_at_all.search(line) is not None and
112 __regex_single_line_feed.search(line) is None):
c599d5cc 113 return __regex_leading_with_spaces.search(line) is not None
c61e93d6 114
c599d5cc
AC
115 return True
116
117
118def trailing_whitespace_or_crlf(line):
119 """Returns TRUE if the trailing characters is whitespace
120 """
121 if skip_trailing_whitespace_check:
122 return False
c61e93d6
DDP
123 return (__regex_trailing_whitespace.search(line) is not None and
124 __regex_single_line_feed.search(line) is None)
c599d5cc
AC
125
126
127def if_and_for_whitespace_checks(line):
128 """Return TRUE if there is appropriate whitespace after if, for, while
129 """
130 if skip_block_whitespace_check:
131 return True
132 if (__regex_for_if_missing_whitespace.search(line) is not None or
133 __regex_for_if_too_much_whitespace.search(line) is not None or
134 __regex_for_if_parens_whitespace.search(line)):
135 return False
136 return True
137
138
30c7ffd5
AC
139def if_and_for_end_with_bracket_check(line):
140 """Return TRUE if there is not a bracket at the end of an if, for, while
141 block which fits on a single line ie: 'if (foo)'"""
142
143 def balanced_parens(line):
144 """This is a rather naive counter - it won't deal with quotes"""
145 balance = 0
146 for letter in line:
973496b9 147 if letter == '(':
30c7ffd5 148 balance += 1
973496b9 149 elif letter == ')':
30c7ffd5 150 balance -= 1
973496b9 151 return balance == 0
30c7ffd5
AC
152
153 if __regex_is_for_if_single_line_bracket.search(line) is not None:
154 if not balanced_parens(line):
155 return True
156 if __regex_ends_with_bracket.search(line) is None:
157 return False
158 return True
159
160
6fc2799e
JS
161def pointer_whitespace_check(line):
162 """Return TRUE if there is no space between a pointer name and the
163 asterisk that denotes this is a apionter type, ie: 'struct foo*'"""
164 return __regex_ptr_declaration_missing_whitespace.search(line) is not None
165
166
c599d5cc 167def ovs_checkpatch_parse(text):
3239c793 168 global print_file_name
c599d5cc
AC
169 lineno = 0
170 signatures = []
171 co_authors = []
172 parse = 0
173 current_file = ''
6982ee96 174 previous_file = ''
c599d5cc
AC
175 scissors = re.compile(r'^[\w]*---[\w]*')
176 hunks = re.compile('^(---|\+\+\+) (\S+)')
4d7f5e51
AC
177 hunk_differences = re.compile(
178 r'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
c599d5cc
AC
179 is_signature = re.compile(r'((\s*Signed-off-by: )(.*))$',
180 re.I | re.M | re.S)
181 is_co_author = re.compile(r'(\s*(Co-authored-by: )(.*))$',
182 re.I | re.M | re.S)
6982ee96 183 skip_line_length_check = False
c599d5cc 184
bddbdd43 185 for line in text.decode().split('\n'):
6982ee96
JS
186 if current_file != previous_file:
187 previous_file = current_file
188 if any([fmt in current_file for fmt in line_length_blacklist]):
189 skip_line_length_check = True
190 else:
191 skip_line_length_check = False
192
c599d5cc
AC
193 lineno = lineno + 1
194 if len(line) <= 0:
195 continue
196
fb9410d8
AC
197 if checking_file:
198 parse = 2
199
c599d5cc
AC
200 if parse == 1:
201 match = hunks.match(line)
202 if match:
203 parse = parse + 1
204 current_file = match.group(2)
3239c793 205 print_file_name = current_file
c599d5cc
AC
206 continue
207 elif parse == 0:
208 if scissors.match(line):
209 parse = parse + 1
210 if not skip_signoff_check:
211 if len(signatures) == 0:
212 print_error("No signatures found.")
83f76d4e 213 elif len(signatures) != 1 + len(co_authors):
c599d5cc
AC
214 print_error("Too many signoffs; "
215 "are you missing Co-authored-by lines?")
216 if not set(co_authors) <= set(signatures):
217 print_error("Co-authored-by/Signed-off-by corruption")
218 elif is_signature.match(line):
219 m = is_signature.match(line)
220 signatures.append(m.group(3))
221 elif is_co_author.match(line):
222 m = is_co_author.match(line)
223 co_authors.append(m.group(3))
224 elif parse == 2:
225 print_line = False
226 newfile = hunks.match(line)
227 if newfile:
228 current_file = newfile.group(2)
3239c793 229 print_file_name = current_file
c599d5cc 230 continue
4d7f5e51
AC
231 reset_line_number = hunk_differences.match(line)
232 if reset_line_number:
233 lineno = int(reset_line_number.group(3))
234 if lineno < 0:
235 lineno = -1 * lineno
236 lineno -= 1
237 if is_subtracted_line(line):
238 lineno -= 1
c599d5cc
AC
239 if not is_added_line(line):
240 continue
fb9410d8
AC
241
242 cmp_line = added_line(line)
243
c599d5cc
AC
244 # Skip files which have /datapath in them, since they are
245 # linux or windows coding standards
246 if '/datapath' in current_file:
247 continue
248 if (not current_file.endswith('.mk') and
fb9410d8 249 not leading_whitespace_is_spaces(cmp_line)):
c599d5cc
AC
250 print_line = True
251 print_warning("Line has non-spaces leading whitespace",
252 lineno)
fb9410d8 253 if trailing_whitespace_or_crlf(cmp_line):
c599d5cc
AC
254 print_line = True
255 print_warning("Line has trailing whitespace", lineno)
fb9410d8 256 if len(cmp_line) > 79 and not skip_line_length_check:
c599d5cc
AC
257 print_line = True
258 print_warning("Line is greater than 79-characters long",
259 lineno)
fb9410d8 260 if not if_and_for_whitespace_checks(cmp_line):
c599d5cc 261 print_line = True
e3ecc9d3
AC
262 print_error("Improper whitespace around control block",
263 lineno)
fb9410d8 264 if not if_and_for_end_with_bracket_check(cmp_line):
30c7ffd5 265 print_line = True
e3ecc9d3 266 print_error("Inappropriate bracing around statement", lineno)
6fc2799e
JS
267 if pointer_whitespace_check(cmp_line):
268 print_line = True
269 print_error("Inappropriate spacing in pointer declaration",
270 lineno)
c599d5cc 271 if print_line:
e3ecc9d3 272 print("\n%s\n" % line)
c599d5cc
AC
273 if __errors or __warnings:
274 return -1
275 return 0
276
277
278def usage():
279 print("Open vSwitch checkpatch.py")
280 print("Checks a patch for trivial mistakes.")
281 print("usage:")
282 print("%s [options] [patch file]" % sys.argv[0])
283 print("options:")
284 print("-h|--help\t\t\t\tThis help message")
285 print("-b|--skip-block-whitespace\t"
286 "Skips the if/while/for whitespace tests")
fb9410d8 287 print("-f|--check-file\t\t\tCheck a file instead of a patchfile.")
c599d5cc
AC
288 print("-l|--skip-leading-whitespace\t"
289 "Skips the leading whitespace test")
290 print("-s|--skip-signoff-lines\t"
291 "Do not emit an error if no Signed-off-by line is present")
292 print("-t|--skip-trailing-whitespace\t"
293 "Skips the trailing whitespace test")
294
a5e9c53c 295
c599d5cc 296def ovs_checkpatch_file(filename):
fb9410d8 297 global __warnings, __errors, checking_file
c599d5cc
AC
298 try:
299 mail = email.message_from_file(open(filename, 'r'))
300 except:
301 print_error("Unable to parse file '%s'. Is it a patch?" % filename)
302 return -1
303
304 for part in mail.walk():
305 if part.get_content_maintype() == 'multipart':
306 continue
7d6b834f
AC
307 result = ovs_checkpatch_parse(part.get_payload(decode=True))
308 if result < 0:
309 print("Warnings: %d, Errors: %d" % (__warnings, __errors))
310 return result
c599d5cc 311
884e0dfe 312
c599d5cc
AC
313if __name__ == '__main__':
314 try:
fb9410d8
AC
315 optlist, args = getopt.getopt(sys.argv[1:], 'bhlstf',
316 ["check-file",
317 "help",
c599d5cc
AC
318 "skip-block-whitespace",
319 "skip-leading-whitespace",
320 "skip-signoff-lines",
321 "skip-trailing-whitespace"])
322 except:
323 print("Unknown option encountered. Please rerun with -h for help.")
324 sys.exit(-1)
325
326 for o, a in optlist:
327 if o in ("-h", "--help"):
328 usage()
329 sys.exit(0)
330 elif o in ("-b", "--skip-block-whitespace"):
331 skip_block_whitespace_check = True
332 elif o in ("-l", "--skip-leading-whitespace"):
333 skip_leading_whitespace_check = True
334 elif o in ("-s", "--skip-signoff-lines"):
335 skip_signoff_check = True
336 elif o in ("-t", "--skip-trailing-whitespace"):
337 skip_trailing_whitespace_check = True
fb9410d8
AC
338 elif o in ("-f", "--check-file"):
339 checking_file = True
c599d5cc
AC
340 else:
341 print("Unknown option '%s'" % o)
342 sys.exit(-1)
343 try:
344 filename = args[0]
345 except:
346 if sys.stdin.isatty():
347 usage()
348 sys.exit(-1)
349 sys.exit(ovs_checkpatch_parse(sys.stdin.read()))
350 sys.exit(ovs_checkpatch_file(filename))