3 ###########################################################################
5 # Copyright 2013 BMW Car IT GmbH
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 ###########################################################################
21 import sys, re, string
22 from common_modules.common import *
26 def check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
28 Check if spacing around binary opeartors is consistent in file
31 #normal binary operators must all have 0 or 1 space between operator and operands
32 binary_ops = ['=', r'\+', '%', r'\^', '==', '!=', '>=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=', '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?', '/']
33 #operators that are not handled
34 #r'\*', '&', '>', '<', '-'
35 #access binary operators must have no space between operator and operands
36 binary_ops_re_text = '(' + "|".join(binary_ops) + ')'
38 #initially: clean file contents from confusing content:
39 #remove all preprocessor directives
40 preprocessor_re = re.compile(r'#\s*\w+(\s+)(((?!\\).)+)((\\\n(((?!\\).)+))*)')
41 clean_file_contents = clean_string_from_regex(clean_file_contents, preprocessor_re, '')
42 clean_file_lines = clean_file_contents.split('\n')
44 #re for NO space between operator and operands
45 nospace_re = re.compile(r"\w{0}\w".format(binary_ops_re_text))
46 #re for ONE space between operator and operands
47 space_re = re.compile(r"\w\s{0}\s\w".format(binary_ops_re_text))
48 #unbalanced is a match for binary op (\w\s*{0}\s*\w) that is neither no space (?!(\w{0}\w)) nor single space (?!(\w\s{0}\s\w))
49 #this works because (?!...) does not consume strings
50 unbalanced_spaces_re = re.compile(r"(?!(\w{0}\w))(?!(\w\s{0}\s\w))(\w\s*{0}\s*\w)".format(binary_ops_re_text))
52 access_ops_re_text = r'(\.|->|::)'
53 bad_access_ops_re = re.compile(r"(\w\s+{0}\s*\w)|(\w\s*{0}\s+\w)".format(access_ops_re_text))
55 #check if there is general inconsistance in the file
56 nospace_match = re.search(nospace_re, clean_file_contents)
57 space_match = re.search(space_re, clean_file_contents)
59 #a single consistency in the file is enough to issue a warning
60 if nospace_match != None and space_match != None:
61 line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
62 line2 = clean_file_contents[:space_match.start()].count("\n") + 1
64 log_warning(filename, line1, "inconsistent spacing between operands and binary operator. All operands in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2))
66 for i in range(len(file_lines)):
67 line = clean_file_lines[i]
69 if re.search(unbalanced_spaces_re, line) != None:
70 log_warning(filename, i + 1, "incorrect or unbalanced spaces between operands and binary operator", file_lines[i].strip(" "))
72 if re.search(bad_access_ops_re, line) != None:
73 log_warning(filename, i + 1, "unneeded space(s) around access/resolution operator", file_lines[i].strip(" "))
75 def check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
77 Checks that there are no spaces before semicolons, and that there is a space after it in case there is other content
78 on the same line after it
81 #check if there is extra space before semicolon, or missing space after semicolon (if there is content after it like in for loop)
82 space_before_re = re.compile(r"\s;");
83 nospace_after_re = re.compile(r";((?! ).)");
85 for i in range(len(file_lines)):
86 line = clean_file_lines[i]
88 if re.search(space_before_re, line) != None:
89 log_warning(filename, i + 1, "unneeded space(s) before semicolon", file_lines[i].strip(" "))
91 if re.search(nospace_after_re, line) != None:
92 log_warning(filename, i + 1, "missing space after semicolon", file_lines[i].strip(" "))
94 def check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
96 Check that there are no unnecessary spaces around parentheses
99 #check if there are spaces between between right/left brace and contents
100 bad_left_brace_re = re.compile(r"\(\s")
101 bad_right_brace_re = re.compile(r"\s\)")
103 for i in range(len(file_lines)):
104 line = clean_file_lines[i]
106 if re.search(bad_left_brace_re, line) != None:
107 log_warning(filename, i + 1, "unneeded space(s) after left brace", file_lines[i].strip(" "))
109 if re.search(bad_right_brace_re, line) != None:
110 log_warning(filename, i + 1, "unneeded space(s) before right brace", file_lines[i].strip(" "))
112 def check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
114 Check that there is no space before commas, and there is exactly one space after coma
117 nospace_after_re = re.compile(r",((?!\s).)")
118 too_many_spaces_after_re = re.compile(r",\s(\s+)")
119 space_before_re = re.compile(r"\S(\s+),")
121 for i in range(len(file_lines)):
122 line = clean_file_lines[i]
124 if re.search(nospace_after_re, line) != None:
125 log_warning(filename, i + 1, "missing space after comma", file_lines[i].strip(" "))
127 if re.search(too_many_spaces_after_re, line) != None:
128 log_warning(filename, i + 1, "unneeded space(s) after comma", file_lines[i].strip(" "))
130 if re.search(space_before_re, line) != None:
131 log_warning(filename, i + 1, "unneeded space(s) before comma", file_lines[i].strip(" "))
133 def check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
135 Check that there is no space between function call/definition and left parenthese
138 #in bad function definition or call there exists a space (or more) between function name and left brace (the last \s+ in the expression)
139 #the function name must not be a if, for, switch or while
140 reserved_words = ['if', 'for', 'while', 'switch', 'return', 'delete']
141 reserved_words_re_text = "(?<!\W" + ")(?<!\W".join(reserved_words) + ")"
143 #the bad function is anything except the reserved words, and the closing brace is not directly followed by another open brace
144 #(to differentiate function calls/defs from function pointer defs)
145 #the \s+ is the fauled pattern that should be caught, because it means a space exists between function name and left parenthese
146 bad_func_re_text = r"(\w+){0}(\s+)(\()".format(reserved_words_re_text)
147 bad_func_re = re.compile(bad_func_re_text)
150 return_type_re_text = r'\w+\s*(&|\*)*\s*'
151 func_ptr_re_text = r'\s*{0}\((\w|\*|:|\s)+\)(\s*)\('.format(return_type_re_text)
153 func_ptr_re = re.compile(func_ptr_re_text)
155 macro_re = re.compile(r"(\s*)#define")
157 for i in range(len(file_lines)):
158 line = clean_file_lines[i]
160 #check all occurances in a line (a line can contain several function calls)
161 for bad_func_match in bad_func_re.finditer(line):
162 #print line[bad_func_match.start():], re.match(func_ptr_re, line[bad_func_match.start():]) != None
163 if re.search(func_ptr_re, line) == None and re.match(macro_re, line) == None:
164 log_warning(filename, i + 1, "unneeded space(s) between function name and left brace", file_lines[i].strip(" "))
166 def check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
168 Check that all occurances of (if, for,...etc) in file have a space between keyword and left brace or
169 that all occurances do not have a space between keyword and left brace
172 #the keywords to be checked
173 reserved_words = ['if', 'for', 'while', 'switch', 'return']
174 #the \W makes sure the keyword is actually a keyword and not a part of a function name, e.g, if() would match but foo_if() would be filtered out
175 reserved_words_re_text = r'\W(' + "|".join(reserved_words) + ')'
177 nospace_re = re.compile(r"{0}\(".format(reserved_words_re_text))
178 space_re = re.compile(r"{0}\s\(".format(reserved_words_re_text))
179 too_many_space_re = re.compile(r"{0}\s(\s+)\(".format(reserved_words_re_text))
181 #check if there is a general inconsistency (a signle occurance is in the file is enough to give a warning)
182 nospace_match = re.search(nospace_re, clean_file_contents)
183 space_match = re.search(space_re, clean_file_contents)
184 if nospace_match != None and space_match != None:
185 line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
186 line2 = clean_file_contents[:space_match.start()].count("\n") + 1
188 log_warning(filename, line1, "inconsistent spacing between keyword and left brace. All braces in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2))
190 for i in range(len(file_lines)):
191 line = clean_file_lines[i]
193 if re.search(too_many_space_re, line) != None:
194 log_warning(filename, i + 1, "unneeded space(s) between keyword and left brace", file_lines[i].strip(" "))
196 def check_consistent_code_style(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
197 check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
198 check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
199 check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
200 check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
201 check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
202 check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
205 if __name__ == "__main__":
206 targets = sys.argv[1:]
207 targets = get_all_files(targets)
209 if len(targets) == 0:
211 \t**** No input provided ****
212 \tTakes a list of files/directories as input and performs specific style checking on all files/directories
214 \tGives warnings if there is inconsistency in spacing of commas, semicolons, operands, braces, function calls or definitions.
219 if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
220 file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
221 check_consistent_code_style(t, file_contents, clean_file_contents, file_lines, clean_file_lines)