]> git.proxmox.com Git - ovs.git/blob - utilities/checkpatch.py
python: Add double newline after fuction or class (E305).
[ovs.git] / utilities / checkpatch.py
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.
15 from __future__ import print_function
16
17 import email
18 import getopt
19 import re
20 import sys
21
22 __errors = 0
23 __warnings = 0
24 print_file_name = None
25 checking_file = False
26
27
28 def 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
33
34
35 def print_error(message, lineno=None):
36 global __errors
37 print_file()
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
46 def print_warning(message, lineno=None):
47 global __warnings
48 print_file()
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]*')
58 __regex_subtracted_line = re.compile(r'^\-{1,2}[^\-][\w\W]*')
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]+$')
62 __regex_single_line_feed = re.compile(r'^\f$')
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]+\)')
67 __regex_is_for_if_single_line_bracket = \
68 re.compile(r'^ +(if|for|while) \(.*\)')
69 __regex_ends_with_bracket = \
70 re.compile(r'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$')
71
72 skip_leading_whitespace_check = False
73 skip_trailing_whitespace_check = False
74 skip_block_whitespace_check = False
75 skip_signoff_check = False
76
77 # Don't enforce character limit on files that include these characters in their
78 # name, as they may have legitimate reasons to have longer lines.
79 #
80 # Python isn't checked as flake8 performs these checks during build.
81 line_length_blacklist = ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
82 '.py']
83
84
85 def is_subtracted_line(line):
86 """Returns TRUE if the line in question has been removed."""
87 return __regex_subtracted_line.search(line) is not None
88
89
90 def is_added_line(line):
91 """Returns TRUE if the line in question is an added line.
92 """
93 global checking_file
94 return __regex_added_line.search(line) is not None or checking_file
95
96
97 def added_line(line):
98 """Returns the line formatted properly by removing diff syntax"""
99 global checking_file
100 if not checking_file:
101 return line[1:]
102 return line
103
104
105 def leading_whitespace_is_spaces(line):
106 """Returns TRUE if the leading whitespace in added lines is spaces
107 """
108 if skip_leading_whitespace_check:
109 return True
110 if (__regex_leading_with_whitespace_at_all.search(line) is not None and
111 __regex_single_line_feed.search(line) is None):
112 return __regex_leading_with_spaces.search(line) is not None
113
114 return True
115
116
117 def trailing_whitespace_or_crlf(line):
118 """Returns TRUE if the trailing characters is whitespace
119 """
120 if skip_trailing_whitespace_check:
121 return False
122 return (__regex_trailing_whitespace.search(line) is not None and
123 __regex_single_line_feed.search(line) is None)
124
125
126 def if_and_for_whitespace_checks(line):
127 """Return TRUE if there is appropriate whitespace after if, for, while
128 """
129 if skip_block_whitespace_check:
130 return True
131 if (__regex_for_if_missing_whitespace.search(line) is not None or
132 __regex_for_if_too_much_whitespace.search(line) is not None or
133 __regex_for_if_parens_whitespace.search(line)):
134 return False
135 return True
136
137
138 def if_and_for_end_with_bracket_check(line):
139 """Return TRUE if there is not a bracket at the end of an if, for, while
140 block which fits on a single line ie: 'if (foo)'"""
141
142 def balanced_parens(line):
143 """This is a rather naive counter - it won't deal with quotes"""
144 balance = 0
145 for letter in line:
146 if letter is '(':
147 balance += 1
148 elif letter is ')':
149 balance -= 1
150 return balance is 0
151
152 if __regex_is_for_if_single_line_bracket.search(line) is not None:
153 if not balanced_parens(line):
154 return True
155 if __regex_ends_with_bracket.search(line) is None:
156 return False
157 return True
158
159
160 def ovs_checkpatch_parse(text):
161 global print_file_name
162 lineno = 0
163 signatures = []
164 co_authors = []
165 parse = 0
166 current_file = ''
167 previous_file = ''
168 scissors = re.compile(r'^[\w]*---[\w]*')
169 hunks = re.compile('^(---|\+\+\+) (\S+)')
170 hunk_differences = re.compile(
171 r'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
172 is_signature = re.compile(r'((\s*Signed-off-by: )(.*))$',
173 re.I | re.M | re.S)
174 is_co_author = re.compile(r'(\s*(Co-authored-by: )(.*))$',
175 re.I | re.M | re.S)
176 skip_line_length_check = False
177
178 for line in text.split('\n'):
179 if current_file != previous_file:
180 previous_file = current_file
181 if any([fmt in current_file for fmt in line_length_blacklist]):
182 skip_line_length_check = True
183 else:
184 skip_line_length_check = False
185
186 lineno = lineno + 1
187 if len(line) <= 0:
188 continue
189
190 if checking_file:
191 parse = 2
192
193 if parse == 1:
194 match = hunks.match(line)
195 if match:
196 parse = parse + 1
197 current_file = match.group(2)
198 print_file_name = current_file
199 continue
200 elif parse == 0:
201 if scissors.match(line):
202 parse = parse + 1
203 if not skip_signoff_check:
204 if len(signatures) == 0:
205 print_error("No signatures found.")
206 elif len(signatures) != 1 + len(co_authors):
207 print_error("Too many signoffs; "
208 "are you missing Co-authored-by lines?")
209 if not set(co_authors) <= set(signatures):
210 print_error("Co-authored-by/Signed-off-by corruption")
211 elif is_signature.match(line):
212 m = is_signature.match(line)
213 signatures.append(m.group(3))
214 elif is_co_author.match(line):
215 m = is_co_author.match(line)
216 co_authors.append(m.group(3))
217 elif parse == 2:
218 print_line = False
219 newfile = hunks.match(line)
220 if newfile:
221 current_file = newfile.group(2)
222 print_file_name = current_file
223 continue
224 reset_line_number = hunk_differences.match(line)
225 if reset_line_number:
226 lineno = int(reset_line_number.group(3))
227 if lineno < 0:
228 lineno = -1 * lineno
229 lineno -= 1
230 if is_subtracted_line(line):
231 lineno -= 1
232 if not is_added_line(line):
233 continue
234
235 cmp_line = added_line(line)
236
237 # Skip files which have /datapath in them, since they are
238 # linux or windows coding standards
239 if '/datapath' in current_file:
240 continue
241 if (not current_file.endswith('.mk') and
242 not leading_whitespace_is_spaces(cmp_line)):
243 print_line = True
244 print_warning("Line has non-spaces leading whitespace",
245 lineno)
246 if trailing_whitespace_or_crlf(cmp_line):
247 print_line = True
248 print_warning("Line has trailing whitespace", lineno)
249 if len(cmp_line) > 79 and not skip_line_length_check:
250 print_line = True
251 print_warning("Line is greater than 79-characters long",
252 lineno)
253 if not if_and_for_whitespace_checks(cmp_line):
254 print_line = True
255 print_error("Improper whitespace around control block",
256 lineno)
257 if not if_and_for_end_with_bracket_check(cmp_line):
258 print_line = True
259 print_error("Inappropriate bracing around statement", lineno)
260 if print_line:
261 print("\n%s\n" % line)
262 if __errors or __warnings:
263 return -1
264 return 0
265
266
267 def usage():
268 print("Open vSwitch checkpatch.py")
269 print("Checks a patch for trivial mistakes.")
270 print("usage:")
271 print("%s [options] [patch file]" % sys.argv[0])
272 print("options:")
273 print("-h|--help\t\t\t\tThis help message")
274 print("-b|--skip-block-whitespace\t"
275 "Skips the if/while/for whitespace tests")
276 print("-f|--check-file\t\t\tCheck a file instead of a patchfile.")
277 print("-l|--skip-leading-whitespace\t"
278 "Skips the leading whitespace test")
279 print("-s|--skip-signoff-lines\t"
280 "Do not emit an error if no Signed-off-by line is present")
281 print("-t|--skip-trailing-whitespace\t"
282 "Skips the trailing whitespace test")
283
284
285 def ovs_checkpatch_file(filename):
286 global __warnings, __errors, checking_file
287 try:
288 mail = email.message_from_file(open(filename, 'r'))
289 except:
290 print_error("Unable to parse file '%s'. Is it a patch?" % filename)
291 return -1
292
293 for part in mail.walk():
294 if part.get_content_maintype() == 'multipart':
295 continue
296 result = ovs_checkpatch_parse(part.get_payload(decode=True))
297 if result < 0:
298 print("Warnings: %d, Errors: %d" % (__warnings, __errors))
299 return result
300
301
302 if __name__ == '__main__':
303 try:
304 optlist, args = getopt.getopt(sys.argv[1:], 'bhlstf',
305 ["check-file",
306 "help",
307 "skip-block-whitespace",
308 "skip-leading-whitespace",
309 "skip-signoff-lines",
310 "skip-trailing-whitespace"])
311 except:
312 print("Unknown option encountered. Please rerun with -h for help.")
313 sys.exit(-1)
314
315 for o, a in optlist:
316 if o in ("-h", "--help"):
317 usage()
318 sys.exit(0)
319 elif o in ("-b", "--skip-block-whitespace"):
320 skip_block_whitespace_check = True
321 elif o in ("-l", "--skip-leading-whitespace"):
322 skip_leading_whitespace_check = True
323 elif o in ("-s", "--skip-signoff-lines"):
324 skip_signoff_check = True
325 elif o in ("-t", "--skip-trailing-whitespace"):
326 skip_trailing_whitespace_check = True
327 elif o in ("-f", "--check-file"):
328 checking_file = True
329 else:
330 print("Unknown option '%s'" % o)
331 sys.exit(-1)
332 try:
333 filename = args[0]
334 except:
335 if sys.stdin.isatty():
336 usage()
337 sys.exit(-1)
338 sys.exit(ovs_checkpatch_parse(sys.stdin.read()))
339 sys.exit(ovs_checkpatch_file(filename))