Merge "Build and package Layer Management service binaries." into tizen
[profile/ivi/layer-management.git] / scripts / check_consistent_code_style.py
1 #!/usr/bin/python
2
3 ###########################################################################
4 #
5 # Copyright 2013 BMW Car IT GmbH
6 #
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
10 #
11 #        http://www.apache.org/licenses/LICENSE-2.0
12 #
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.
18 #
19 ###########################################################################
20
21 import sys, re, string
22 from common_modules.common import *
23
24
25
26 def check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
27     """
28     Check if spacing around binary opeartors is consistent in file
29
30     """
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) + ')'
37
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')
43
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))
51
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))
54
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)
58
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
63
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))
65
66     for i in range(len(file_lines)):
67         line = clean_file_lines[i]
68
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(" "))
71
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(" "))
74
75 def check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
76     """
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
79
80     """
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";((?! ).)");
84
85     for i in range(len(file_lines)):
86         line = clean_file_lines[i]
87
88         if re.search(space_before_re, line) != None:
89             log_warning(filename, i + 1, "unneeded space(s) before semicolon", file_lines[i].strip(" "))
90
91         if re.search(nospace_after_re, line) != None:
92             log_warning(filename, i + 1, "missing space after semicolon", file_lines[i].strip(" "))
93
94 def check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
95     """
96     Check that there are no unnecessary spaces around parentheses
97
98     """
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\)")
102
103     for i in range(len(file_lines)):
104         line = clean_file_lines[i]
105
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(" "))
108
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(" "))
111
112 def check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
113     """
114     Check that there is no space before commas, and there is exactly one space after coma
115
116     """
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+),")
120
121     for i in range(len(file_lines)):
122         line = clean_file_lines[i]
123
124         if re.search(nospace_after_re, line) != None:
125             log_warning(filename, i + 1, "missing space after comma", file_lines[i].strip(" "))
126
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(" "))
129
130         if re.search(space_before_re, line) != None:
131             log_warning(filename, i + 1, "unneeded space(s) before comma", file_lines[i].strip(" "))
132
133 def check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
134     """
135     Check that there is no space between function call/definition and left parenthese
136
137     """
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) + ")"
142
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)
148
149     #function pointer
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)
152
153     func_ptr_re = re.compile(func_ptr_re_text)
154
155     macro_re = re.compile(r"(\s*)#define")
156
157     for i in range(len(file_lines)):
158         line = clean_file_lines[i]
159
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(" "))
165
166 def check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
167     """
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
170
171     """
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) + ')'
176
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))
180
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
187
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))
189
190     for i in range(len(file_lines)):
191         line = clean_file_lines[i]
192
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(" "))
195
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)
203
204
205 if __name__ == "__main__":
206     targets = sys.argv[1:]
207     targets = get_all_files(targets)
208
209     if len(targets) == 0:
210         print """
211 \t**** No input provided ****
212 \tTakes a list of files/directories as input and performs specific style checking on all files/directories
213
214 \tGives warnings if there is inconsistency in spacing of commas, semicolons, operands, braces, function calls or definitions.
215 """
216         exit(0)
217
218     for t in targets:
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)