###########################################################################
"""
- Runs all enabled style checking
+Runs all enabled style checking
+
"""
import sys, re, string
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
\tTakes a list of files and directories as input and runs all enabled style-checking on all files.
\tTo enable/disable style-checking you can toggle the "ON"/"OFF" value of the configuration variables inside the file.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2:] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
-
+
if CHECK_LICENSE == "ON":
check_license_in_file(t, file_contents)
if CHECK_HEADER_GUARDS == "ON" and t[-2:] == ".h":
def check_class_name(filename, clean_file_contents):
- #a class is followed by classname, and possible an inherticance
+ """
+ Check that the file contains (maximum) one class that has a name identical to file name
+
+ """
+ #a class keyword is followed by classname, (and possibly an inherticance) and a left curly brace
class_def_re = re.compile(r'(?<!\w)class(\s+)(\w+)(\s*)((:( |\w|,)*)?)(\s*)\{')
+ #this is used to catch only the class name, because the (?>= ) is not included in the match
class_name_re = re.compile(r'(?<=((?<!\w)class))(\s+\w+)')
#filename without path and extension
#remove .h extension
good_name = good_name[:good_name.find(".h")]
+ #initially: no class found yet
class_found = False
for class_match in re.finditer(class_def_re, clean_file_contents):
line_number = clean_file_contents[:class_match.end()].count("\n")
+ #if a class was found before: give warning
if class_found:
log_warning(filename, line_number, "several class difinitions in file")
else:
+ #if no class was found before: set flag to True
class_found = True
+ #get class name
class_name_match = class_name_re.search(clean_file_contents, class_match.start())
class_name = clean_file_contents[class_name_match.start(): class_name_match.end()] if class_name_match != None else ""
class_name = class_name.strip(" \n\r\f\t")
+ #if class name does not match file name: give warning
if class_name != good_name:
log_warning(filename, line_number, "class name has to be identical to filename")
-
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if a file contains more than one class definition or if class name is not identical to file name
def check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Check if spacing around binary opeartors is consistent in file
+
+ """
#normal binary operators must all have 0 or 1 space between operator and operands
- binary_ops = ['=', r'\+', '%', r'\^', '==', '!=', '>=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=', '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?']
+ binary_ops = ['=', r'\+', '%', r'\^', '==', '!=', '>=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=', '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?', '/']
#operators that are not handled
- #r'\*', '&', '>', '<', '/', '-'
+ #r'\*', '&', '>', '<', '-'
#access binary operators must have no space between operator and operands
binary_ops_re_text = '(' + "|".join(binary_ops) + ')'
-
+
+ #initially: clean file contents from confusing content:
+ #remove all preprocessor directives
+ preprocessor_re = re.compile(r'#\s*\w+(\s+)(((?!\\).)+)((\\\n(((?!\\).)+))*)')
+ clean_file_contents = clean_string_from_regex(clean_file_contents, preprocessor_re, '')
+ clean_file_lines = clean_file_contents.split('\n')
+
#re for NO space between operator and operands
nospace_re = re.compile(r"\w{0}\w".format(binary_ops_re_text))
#re for ONE space between operator and operands
#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))
#this works because (?!...) does not consume strings
unbalanced_spaces_re = re.compile(r"(?!(\w{0}\w))(?!(\w\s{0}\s\w))(\w\s*{0}\s*\w)".format(binary_ops_re_text))
-
- #access_ops = ['.', '->', '::']
+
access_ops_re_text = r'(\.|->|::)'
bad_access_ops_re = re.compile(r"(\w\s+{0}\s*\w)|(\w\s*{0}\s+\w)".format(access_ops_re_text))
-
+
#check if there is general inconsistance in the file
nospace_match = re.search(nospace_re, clean_file_contents)
space_match = re.search(space_re, clean_file_contents)
-
+
+ #a single consistency in the file is enough to issue a warning
if nospace_match != None and space_match != None:
line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
line2 = clean_file_contents[:space_match.start()].count("\n") + 1
-
+
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))
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
if re.search(unbalanced_spaces_re, line) != None:
log_warning(filename, i + 1, "incorrect or unbalanced spaces between operands and binary operator", file_lines[i].strip(" "))
-
+
if re.search(bad_access_ops_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) around access/resolution operator", file_lines[i].strip(" "))
def check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Checks that there are no spaces before semicolons, and that there is a space after it in case there is other content
+ on the same line after it
+
+ """
#check if there is extra space before semicolon, or missing space after semicolon (if there is content after it like in for loop)
space_before_re = re.compile(r"\s;");
nospace_after_re = re.compile(r";((?! ).)");
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
if re.search(space_before_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) before semicolon", file_lines[i].strip(" "))
-
+
if re.search(nospace_after_re, line) != None:
log_warning(filename, i + 1, "missing space after semicolon", file_lines[i].strip(" "))
def check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Check that there are no unnecessary spaces around parentheses
+
+ """
#check if there are spaces between between right/left brace and contents
bad_left_brace_re = re.compile(r"\(\s")
bad_right_brace_re = re.compile(r"\s\)")
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
if re.search(bad_left_brace_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) after left brace", file_lines[i].strip(" "))
-
+
if re.search(bad_right_brace_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) before right brace", file_lines[i].strip(" "))
def check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Check that there is no space before commas, and there is exactly one space after coma
+
+ """
nospace_after_re = re.compile(r",((?!\s).)")
too_many_spaces_after_re = re.compile(r",\s(\s+)")
space_before_re = re.compile(r"\S(\s+),")
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
if re.search(nospace_after_re, line) != None:
log_warning(filename, i + 1, "missing space after comma", file_lines[i].strip(" "))
-
+
if re.search(too_many_spaces_after_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) after comma", file_lines[i].strip(" "))
log_warning(filename, i + 1, "unneeded space(s) before comma", file_lines[i].strip(" "))
def check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Check that there is no space between function call/definition and left parenthese
+
+ """
#in bad function definition or call there exists a space (or more) between function name and left brace (the last \s+ in the expression)
#the function name must not be a if, for, switch or while
reserved_words = ['if', 'for', 'while', 'switch', 'return', 'delete']
#the \s+ is the fauled pattern that should be caught, because it means a space exists between function name and left parenthese
bad_func_re_text = r"(\w+){0}(\s+)(\()".format(reserved_words_re_text)
bad_func_re = re.compile(bad_func_re_text)
-
+
#function pointer
return_type_re_text = r'\w+\s*(&|\*)*\s*'
func_ptr_re_text = r'\s*{0}\((\w|\*|:|\s)+\)(\s*)\('.format(return_type_re_text)
func_ptr_re = re.compile(func_ptr_re_text)
macro_re = re.compile(r"(\s*)#define")
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
+ #check all occurances in a line (a line can contain several function calls)
for bad_func_match in bad_func_re.finditer(line):
#print line[bad_func_match.start():], re.match(func_ptr_re, line[bad_func_match.start():]) != None
if re.search(func_ptr_re, line) == None and re.match(macro_re, line) == None:
log_warning(filename, i + 1, "unneeded space(s) between function name and left brace", file_lines[i].strip(" "))
-def check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+def check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+ """
+ Check that all occurances of (if, for,...etc) in file have a space between keyword and left brace or
+ that all occurances do not have a space between keyword and left brace
+
+ """
+ #the keywords to be checked
reserved_words = ['if', 'for', 'while', 'switch', 'return']
+ #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
reserved_words_re_text = r'\W(' + "|".join(reserved_words) + ')'
-
+
nospace_re = re.compile(r"{0}\(".format(reserved_words_re_text))
space_re = re.compile(r"{0}\s\(".format(reserved_words_re_text))
too_many_space_re = re.compile(r"{0}\s(\s+)\(".format(reserved_words_re_text))
-
+
+ #check if there is a general inconsistency (a signle occurance is in the file is enough to give a warning)
nospace_match = re.search(nospace_re, clean_file_contents)
space_match = re.search(space_re, clean_file_contents)
if nospace_match != None and space_match != None:
line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
line2 = clean_file_contents[:space_match.start()].count("\n") + 1
-
+
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))
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
if re.search(too_many_space_re, line) != None:
log_warning(filename, i + 1, "unneeded space(s) between keyword and left brace", file_lines[i].strip(" "))
def check_consistent_code_style(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
- check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+ check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories
\tGives warnings if there is inconsistency in spacing of commas, semicolons, operands, braces, function calls or definitions.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
from common_modules.common import *
def remove_tollerable_braces(clean_file_contents):
- #remove array assignments
- assignment_re = re.compile(r'=(\s*)(\{)((?!;).)*(\})(\s*);', re.DOTALL)
-
- no_assig_contents = clean_file_contents
- found_match = re.search(assignment_re, no_assig_contents)
-
- while found_match != None:
- matched_string = no_assig_contents[found_match.start(): found_match.end()]
- no_assig_contents = no_assig_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_assig_contents[found_match.end():]
+ """
+ Cleans file contents from code blocks that should not be checked:
+ 1- Array initialization blocks
+ 2- Empty functions
- found_match = re.search(assignment_re, no_assig_contents)
+ """
+ #array assignment begins with a left curly brace ({) followed by any number of characters that is not a semicolon
+ #it ends by a right curly brace (}) and a semicolon (;)
+ assignment_re = re.compile(r'=(\s*)(\{)((?!;).)*(\})(\s*);', re.DOTALL)
+ #remove array assignments from file contents
+ no_assig_contents = clean_string_from_regex(clean_file_contents, assignment_re, '')
#remove braces that are used for implementing empty functions
empty_func_re = re.compile(r'\{\s*\}')
-
- no_empty_func_contents = no_assig_contents
- found_match = re.search(empty_func_re, no_empty_func_contents)
-
- while found_match != None:
- matched_string = no_empty_func_contents[found_match.start(): found_match.end()]
- no_empty_func_contents = no_empty_func_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_empty_func_contents[found_match.end():]
-
- found_match = re.search(empty_func_re, no_empty_func_contents)
+ no_empty_func_contents = clean_string_from_regex(no_assig_contents, empty_func_re, '')
return no_empty_func_contents
def check_curly_braces_alone_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+ """
+ Checks that there is no unnecessary code on same line with curly braces.
+
+ """
clean_file_contents = remove_tollerable_braces(clean_file_contents)
clean_file_lines = clean_file_contents.split('\n')
-
+
+ #keep a stack booleans to indicate where type declarations are made
typedecl_stack = []
- #a type declaration contains the words struct, class , union or enum
+
+ #a type declaration contains the words struct, class, union or enum
+ #it can also have the typdef keyword in the beginning
typedecl_re = re.compile(r"(?!<\w)(typedef)?(\s*)(struct|class|enum|union)(?!\w)")
+
#some statements are allowed to have curly brace on same
+ #(do..while) blocks, namespaces and extern blocks
permissible_re = re.compile(r'''(\s*)
(
- do| # do keyword
- (namespace((?!\{).)*)| # namespace keyword
- (extern((?!\{).)*) # extern keyword
+ do| # do keyword
+ (namespace((?!\{).)*)| # namespace keyword
+ (extern((?!\{).)*) # extern keyword
)?
- (\s*)\{(\s*)
- (\\?)(\s*)
+ (\s*)\{(\s*) # open curly brace
+ (\\?)(\s*) # backslash indicates a multiline macro or multiline string
$''', re.VERBOSE)
-
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
def get_former_line():
+ """
+ Gets the last non-empty line
+
+ """
if i > 0:
for j in range(i - 1, 0 , -1):
if len(clean_file_lines[j].strip(" \t\n\f\r")):
return clean_file_lines[j]
- return None
-
+ return ""
+
former_line = get_former_line()
-
+
if line.count("{"):
+ #check if the line contains a type declaration
typedecl = re.search(typedecl_re, former_line)
typedecl_stack.append(typedecl)
-
+
+ #make a warning if line does not follow the permissible set of expressions
if re.match(permissible_re, line) == None :
log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
-
+
if line.count("}"):
typedecl = typedecl_stack.pop()
+
if typedecl:
+ #if the right curly brace marks the end of type declaration:
+ #it is possible to have code AFTER the curly brace in case of type declarations
+ #like instantiating a structure at end of structure definition
if re.match(r"(\s*)\}", line) == None:
log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
else:
+ #if the right curly brace does not mark the end of type declaration:
+ #the line must begin with the right curly brace, can possibly have a while expression and ends by semicolon
+ #this has to be the end of line ($) to avoid having a second statement on the same line
if re.match(r"(\s*)\}(\s*)((while((?!;).)*)?)(;?)(\s*)(\\?)(\s*)$", line) == None:
log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories
\tGives warnings if unnecessary code exists on the same line with curly braces.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
import sys, re, string
from common_modules.common import *
-from common_modules.config import G_MAX_INDENTATION_THRESHOLD
def check_empty_lines(filename, file_contents, file_lines):
+ """
+ Check that there are no non-necessary empty lines
+
+ """
#keep track of the current indentation
#need only to know if level of indent is zero or not
line_indent_zero = True
next_line = file_lines[i + 1] if i < len(file_lines) - 1 else ""
def is_next_indent_zero():
+ """
+ Checks the indentation of the next non-empty line
+ return True if it is at zero-level indentation
+
+ """
for j in range(i + 1, len(file_lines)):
if not is_line_empty(file_lines[j]):
return re.match(r'(\s+)(\S+)', file_lines[j]) == None
#if the line after it is a block end
elif re.match(r'(\s*)\}', next_line):
log_warning(filename, i + 1, "unneeded empty line (block ends with empty line)")
- #if non-empty line
else:
+ #if non-empty line: check if line is at zero indentation
#if the line contains one or more white-space characters followed by
#one or more non white-space characters
line_indent_zero = re.match(r'(\s+)(\S+)', line) == None
+ #if file does not end with an empty line: give warning
if re.search(r'\n$', file_contents) == None:
log_warning(filename, len(file_lines), "file does not end with new line")
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories
\tGives warnings if there are unneeded empty lines
from common_modules.common import *
def check_header_guards(filename, file_contents):
+ """
+ Check if a header files contains valid header guards:
+ 1- #ifndef GUARDNAME directly after license that has a guard name identical to file name in uppercase letters, underscores at beginnings and end are tollerable
+ 2- directly followed by #define that matches the guard name of #ifndef exactly
+ 3- the file has to end with an #endif
+
+ """
#remove strings
string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
file_contents = clean_string_from_regex(file_contents, string_re, 'x')
#remove multi-line comments
ml_comment_re = re.compile(r'(\s*)/\*((?!\*/).)*\*/', re.DOTALL)
file_contents = clean_string_from_regex(file_contents, ml_comment_re, '')
-
+
#remove single line comments
sl_comment_re = re.compile("//.*$", re.MULTILINE)
file_contents = re.sub(sl_comment_re, '', file_contents)
-
+
+ #remove any padding white space characters
file_contents = file_contents.strip(" \t\n\f\r")
-
def check_good_name(name):
#good name
good_name = filename
-
+
#remove path from filename (if needed)
if good_name.count("/") > 0:
good_name = good_name[good_name.rfind("/") + 1:]
-
+
#remove .h extension
good_name = good_name[:good_name.find(".h")]
-
+
#good name is all uppercase
good_name = good_name.upper()
-
+
#a good name can start with underscores, and can end with underscores, can optionally have a capital H character
good_name_re = re.compile("(_*){0}(_+H?_*)?".format(good_name))
-
+
return re.match(good_name_re, name) != None
-
+
#find the ifndef guard
ifndef_re = re.compile(r"(\s*)#ifndef .*", re.MULTILINE)
ifndef_match = re.match(ifndef_re, file_contents)
if ifndef_match == None:
log_warning(filename, 1, "header file does not contain a valid #ifndef guard at the beginning")
return None
-
+
+ #get guard name
ifndef_match_text = file_contents[:ifndef_match.end()]
ifndef_match_text = ifndef_match_text.strip(" \n\r\f\t")
splitted_match_text = ifndef_match_text.split(" ")
ifndef_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else ""
-
+
if not check_good_name(ifndef_guard_name):
log_warning(filename, 1, "#ifndef {0} guard name does not follow convention, name has to contain file name in uppercase".format(ifndef_guard_name))
return None
-
+
file_contents = file_contents[ifndef_match.end():]
-
+
#find the define guard
define_re = re.compile(r"(\s*)#define .*")
define_match = re.match(define_re, file_contents)
if define_match == None:
log_warning(filename, 1, "header file does not contain a #define guard at the beginning that matches #ifndef {0}".format(ifndef_guard_name))
return None
-
+
+ #get guard name
define_match_text = file_contents[:define_match.end()]
define_match_text = define_match_text.strip(" \n\r\f\t")
splitted_match_text = define_match_text.split(" ")
define_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else ""
-
+
+ #check if #ifndef and #define have identical guard names
if define_guard_name != ifndef_guard_name:
log_warning(filename, 1, "header guard names do not match :(#ifndef {0}) and (#define {1})".format(ifndef_guard_name, define_guard_name))
-
+
#find the endif guard
endif_re = re.compile(r"#endif.*(\s*)$")
endif_match = re.search(endif_re, file_contents)
log_warning(filename, 1, "header file does not end with #endif at last line")
return None
-
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories
\tGives warnings if header guards does not exist in header files. Header files must have a #ifndef and #define guards directly after the license.
\tThe names of the header guards must match together and match with the name of the file in uppercase letters.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h":
file_contents, _, _, _ = read_file(t)
import sys, re, string
from common_modules.common import *
from common_modules.config import G_MAX_INDENTATION_THRESHOLD
+from common_modules.config import G_TAB_SEQUENCE
-def check_indentation(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+def get_correct_indentation_list(clean_file_lines):
#It is important to keep track of the changes made to the indent level because
#curly braces of namespaces do not affect indentation, while curly braces of
#other statements like if, for and while increase/decease indentation
indent_levels_stack = [0]
+
+ #current indent level
indent_level = 0
- parentheses_count = 0
-
- def is_incomplete_statement(line, former_line):
- incomplete_statement = False
+
+ #indent level for every line
+ indentation_levels = [0] * len(clean_file_lines)
+
+ #get correct indentation for every line
+ for i in range(len(clean_file_lines)):
+ line = clean_file_lines[i]
+
+ def get_former_line():
+ """
+ Gets the last previous non-empty line
+
+ """
+ if i > 0:
+ for j in range(i - 1, 0 , -1):
+ if len(clean_file_lines[j].strip(" \t\n\f\r")):
+ return clean_file_lines[j]
+ return ""
+
+ #previous non-empty line
+ former_line = get_former_line()
+
+ # the next part of the code decreases the indent level
+ #
+ # the level of indent decrease when right curly braces are found
+ # in the line.
+ #
+ # if the line begins with a right curly brace then the line ITSELF
+ # gets a decreased indent level
+ #
+ # if the line has some content on the same line before the right
+ # curly brace (which is a bad practice, but nevertheless) the line
+ # ITSELF does not get a decreased level of indentation, but the
+ # following lines get a decreased level of indentation
+
+ #check if the line begins with a right curly brace
+ line_begins_with_right_brace = re.match(r"\s*\}", line) != None
+
+ #if the line DOES NOT begin with right curly brace then it has the same level
+ #of indentation as the previous line(s)
+ if not line_begins_with_right_brace:
+ indentation_levels[i] = indent_level
+
+ #this is to decrease the level of indentation in case there is a right curly brace "}"
+ #the level of indentation is decreased by popping elements from the indentation level stack
+ #a loop is to account for cases where there are several braces on same line
+ for _ in range(0, line.count("}") - line.count("{")):
+ indent_level = indent_levels_stack.pop()
+
+ #if the line begins with right curly brace then it gets a decreased
+ #level of indentation
+ if line_begins_with_right_brace:
+ indentation_levels[i] = indent_level
+
+ # the next part of the code increases the indent level
+ #
+ # indent level increases when a left curly brace is found
+ #
+ # an exception to this if the curly brace is the beginning
+ # of a namespace or an extern block, in this case the indent
+ # level does not increase
+
+ # A flag is used in the very specific case of having several left
+ # curly braces after a namespace/extern block (which is a bad practice)
+ #
+ # Initially the flag is False,soo that the first curly brace would not
+ # increase indentation, after that the flag is raised (set to True)
+ # so that every new left curly brace would incraese indentation
+ flag = False
+
+ #increase the level of indentation of there are open left curly braces "{"
+ #a loop is to account for cases where there are several braces on same line
+ for _ in range(0, line.count("{") - line.count("}")):
+ #push the current level of indentation to the stack
+ indent_levels_stack.append(indent_level)
+
+ #if there is no namesapce or extern declaration before curly brace: increase indentation
+ #that is because namespaces do not increase the level of indentation
+ namespace_or_extern_re_text = r'\s*(namespace|(extern((\s*\S*\s*)?)(\s*)($|\{)))'
+ namespace_or_extern_re = re.compile(namespace_or_extern_re_text)
+
+ #if the current line contains a namespace/extern declation
+ if re.match(namespace_or_extern_re, line) != None:
+ #increase the level of indentation only if the flag is raised
+ #(if it is NOT the first curly brace on line)
+ if flag:
+ indent_level += 1
+ else:
+ #raise the flag (so that next curly braces would increase indentation)
+ flag = True
+
+ #otherwise: if the previous line contains namesapce/extern declaration
+ elif re.match(namespace_or_extern_re, former_line) != None:
+ #the indent level does not increase only if the previous line does not contain
+ #a left curly brace, and if this is the first curly brace on the current line (flag is still False)
+ #if either of the conditions is false it is safe to increase the indent level
+ if re.search(r'(namespace|extern)((?!\{).)*\{', former_line) == None and not flag:
+ flag = True
+ else:
+ indent_level += 1
+
+ #otherwise: just increase indent level
+ else:
+ indent_level += 1
+
+ return indentation_levels
+
+def check_indentation(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+ """
+ Checks if all lines in a file (except multi-line comments, strings and macros) have correct indentation
+
+ """
+ def is_statement_supplement(line, former_line, parentheses_count):
+ """
+ Checks if the statement is the completion of a previous statement, like arethmatic expressions broken on several lines, or for loop
+ parts on separate lines. It also includes checks that the previous statement is well-closed (ends by semicolon or curly brace)
+
+ IMPORTANT: the first line of a for loop (or an arethmatic expression...etc) broken on several lines would still not be considered
+ a statement supplement
+
+ """
+ statement_supplement = False
#include statements not preceded by a semicolon in previous line
- incomplete_statement |= former_line != None and re.search(re.compile(r";(\s*)$"), former_line) == None
+ statement_supplement |= former_line != None and re.search(re.compile(r";(\s*)$"), former_line) == None
#exclude lines containing block beginnings and ends
- incomplete_statement &= re.search(re.compile(r"(\{|\})(\s*)$"), line) == None
+ statement_supplement &= re.search(re.compile(r"(\{|\})(\s*)$"), line) == None
#exclude statements preceded by block beginnings and ends
- incomplete_statement &= former_line != None and re.search(re.compile(r"(\{|\})(\s*)$"), former_line) == None
+ statement_supplement &= former_line != None and re.search(re.compile(r"(\{|\})(\s*)$"), former_line) == None
#exclude preprocessor directives (they dunt have to end with a ; for example)
- incomplete_statement &= re.match(re.compile(r"(\s*)#"), line) == None
+ statement_supplement &= not is_preprocessor_directive(line)
#exclude statements preceded by preprocessor directives that do not end with a slash \
- incomplete_statement &= former_line != None and re.match(re.compile(r"(\s*)#((?!\/).)*\/$"), former_line) == None
-
+ statement_supplement &= former_line != None and re.match(re.compile(r"(\s*)#((?!\/).)*\/$"), former_line) == None
#filter out the case where a line is preceeded by a label statement
- fl_label_statement = False
- if former_line != None:
- #case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator)
- fl_label_statement = re.match(r'(\s*)case(\s*)((?!:).)*(\s*):(?!:)(\s*)$', former_line) != None
- #label statements -> label : expr that does not begin with a : (to avoid matching with :: operator)
- fl_label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)(\s*)$', former_line) != None
-
- incomplete_statement &= not fl_label_statement
+ fl_label_statement = is_label_statement(former_line)
+ statement_supplement &= not fl_label_statement
#include statements that are contained inside parentheses
- incomplete_statement |= parentheses_count > 0
-
- return incomplete_statement
-
+ statement_supplement |= parentheses_count > 0
+
+ return statement_supplement
+
+ def is_macro_body(former_line):
+ """
+ Checks if the line belongs to a macro body by checking that the line before it ends with a slash
+
+ """
+ return former_line != None and re.search(r"\\(\s*)$", former_line) != None
+
def is_label_statement(line):
+ """
+ Checks if line starts with a label/case definition
+
+ """
label_statement = False
if line != None:
#case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator)
#label statements -> label : expr that does not begin with a : (to avoid matching with :: operator)
label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)', line) != None
return label_statement
-
+
def is_preprocessor_directive(line):
+ """
+ Checks if a line is a preprocessor directive
+
+ """
+ #preproccessor directives start with a #
return re.match(r"(\s*)#", line) != None
- def is_macro_body(former_line):
- return former_line != None and re.search(r"\\$", former_line) != None
-
+ #keeps count of open (non-closed) left parentheses
+ parentheses_count = 0
+
+ #configured tab character
+ tab_character = G_TAB_SEQUENCE[0]
+ tab_character_count = G_TAB_SEQUENCE[1]
+
+ #get list of correct levels of indentation for every line
+ correct_indent_levels = get_correct_indentation_list(clean_file_lines)
+
+ #indent level of current line and previous line
+ correct_il = 0
+ previous_il = 0
+
+ #check every line for correct indentation
for i in range(len(clean_file_lines)):
line = clean_file_lines[i]
- line_indent = 0
-
- #a loop is to account for cases where there are several braces on same line
- for _ in range(0, line.count("}") - line.count("{")):
- indent_level = indent_levels_stack.pop()
-
- #check correct line indentation
- found_match = re.search("(?! )", line)
- if found_match:
- line_indent = found_match.start() / 4.0
-
+ #correct indent level of current line
+ correct_il = correct_indent_levels[i]
+
+ #actual indent level of the line
+ actual_il = 0
+
+ #get the actual line indentation
+ #this regex searches for the first character in line that is not a space
+ #if no match found this means a zero level of indentation
+ indent_match = re.search("(?!{0})".format(tab_character), line)
+ if indent_match:
+ actual_il = 1.0 * indent_match.start() / tab_character_count
+
def get_former_line():
+ """
+ Gets the last previous non-empty line
+
+ """
if i > 0:
for j in range(i - 1, 0 , -1):
if len(clean_file_lines[j].strip(" \t\n\f\r")):
return clean_file_lines[j]
return ""
-
+
+ #previous non-empty line
former_line = get_former_line()
-
- incomplete_statement = is_incomplete_statement(line, former_line)
+
+ #make several checks about the line
+ statement_supplement = is_statement_supplement(line, former_line, parentheses_count)
label_statement = is_label_statement(line)
fl_label_statement = is_label_statement(former_line)
preprocessor_directive = is_preprocessor_directive(line)
macro_body = is_macro_body(former_line)
-
+
+ #if at non-empty line: check if indentation is correct
if len(line.strip(" \t\n\r")) > 0:
if macro_body:
#do not check anything, just ignore line
None
elif preprocessor_directive:
- #warning if not at 0 level indentation
- if line_indent != 0:
+ #preproccessor directives must have 0 level indentation
+ if actual_il != 0:
log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
elif label_statement:
- #warning if label statement has one level less indentation than the normal indentation level
- if line_indent != indent_level - 1:
+ #label statements must have one level less indentation than the normal indentation level
+ if actual_il != correct_il - 1:
log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
- elif (not incomplete_statement) and line_indent != indent_level:
- #warning if a complete statement (NON incomplete statement) is not at the normal level of indentation
+ elif (not statement_supplement) and actual_il != correct_il:
+ #a complete statement (NON supplement statement) must be at the normal level of indentation
+ log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+ elif statement_supplement and actual_il < correct_il:
+ #statement supplements must have a level of indentation at least equal to the normal level of indentation
log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
- elif incomplete_statement:
- if fl_label_statement and line_indent != indent_level:
- #warning if preceeded by a label statement but not exactly at normal indentation
- log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
- elif (not fl_label_statement ) and line_indent < indent_level:
- #warning if incomplete statement and indentation is less than normal indentation
- log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
-
- #a loop is to account for cases where there are several braces on same line
- for _ in range(0, line.count("{") - line.count("}")):
- indent_levels_stack.append(indent_level)
- #if there is no namesapce or extern declaration before curly brace: increase indentation
- #if re.match(r"(\s*)((namespace(\s+\w+)?(\s*)\{)|(extern(((?!\{).)*)\{))", line) == None and re.match(r"(\s*)(namespace|extern)", former_line) == None:
- #if re.match(r"(\s*)(namespace)", line) == None and re.match(r"(\s*)namespace", former_line) == None:
- namespace_or_extern_re_text = r'\s*(namespace|(extern((\s*\S*\s*)?)(\s*)($|\{)))'
- namespace_or_extern_re = re.compile(namespace_or_extern_re_text)
- if re.match(namespace_or_extern_re, line) == None and re.match(namespace_or_extern_re, former_line) == None:
- indent_level += 1
-
- #warning if indentation level is too much
- if indent_level == G_MAX_INDENTATION_THRESHOLD + 1:
- log_warning(filename, i + 1, "code block has too much indentation [maximum level of indentation is {0}]".format(G_MAX_INDENTATION_THRESHOLD),)
-
+ #warning if indentation level is too much
+ if correct_il == G_MAX_INDENTATION_THRESHOLD + 1 and previous_il < correct_il:
+ log_warning(filename, i + 1, "code block has too much indentation [maximum level of indentation is {0}]".format(G_MAX_INDENTATION_THRESHOLD),)
+
+ #update open parentheses count
parentheses_count += line.count("(") - line.count(")")
+ #update previous indent level
+ previous_il = correct_il
+
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories
\tGives warnings if lines (except multi-line comments and macros) do not have correct indentation.
\tIt also checks if code blocks have too much indentation (too deep). Code should have a maximum of {0} indentation levels.
""".format(G_MAX_INDENTATION_THRESHOLD)
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
import sys, re, string
from common_modules.common import *
-
-BMW_LICENSE_TEMPLATE = '''/***************************************************************************
-*
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-****************************************************************************/'''
+from common_modules.config import G_LICENSE_TEMPLATES
def clean_comment_chars(s):
"""
- Removes comment characters from a string
+ Removes comment characters from a string
+
"""
s = string.replace(s, "/", "")
s = string.replace(s, "*", "")
def make_license_re(license_text):
"""
- Makes a regular expression for every line in the license, this would match the license
- text tolerating extra spaces
+ Makes a regular expression for every line in the license, this would match the license
+ text tolerating extra spaces
+
"""
license_lines = license_text.split("\n")
license_lines_re = {}
for i in range(len(license_lines)):
license_line = license_lines[i]
re_text = clean_comment_chars(license_line)
+ #remove white space paddings
re_text = re_text.strip(" \n\t\r\f")
+ #replace special characters
re_text = string.replace(re_text, "(", "\(")
re_text = string.replace(re_text, ")", "\)")
re_text = string.replace(re_text, " ", "(\s+)")
re_text = string.replace(re_text, "\n", "(\s*)")
re_text = string.replace(re_text, "\t", "(\s+)")
re_text = string.replace(re_text, ".", "\.")
-
+
+ #this replaces the text [YYYY] with a regex that mathces years in one of the following forms:
+ #2013 or 2000-2013 or 2000 or 2000, 2001, 2002, 2013
re_text = string.replace(re_text, "[YYYY]", r"(\d{4})(((-(\d{4}))|(((\s*),(\s*)\d{4})+))?)")
-
+
if len(re_text) > 0:
re_text = "(\s*)" + re_text + "(\s*)"
current_text = ""
+ #remove unneeded space matches
while current_text != re_text:
current_text = re_text
re_text = string.replace(re_text, "(\s*)(\s*)", "(\s*)")
re_text = string.replace(re_text, "(\s+)(\s+)", "(\s+)")
re_text = string.replace(re_text, "(\s*)(\s+)", "(\s+)")
-
license_lines_re[i] = re_text
return license_lines_re
+def check_specific_license_in_file(filename, clean_file_contents, license_text):
+ """
+ Checks if the file contains a valid license according to the license template provided
-def check_license_in_file(filename, file_contents):
- clean_file_contents = clean_comment_chars(file_contents)
- license_text = BMW_LICENSE_TEMPLATE
-
+ """
license_lines = license_text.split("\n")
-
+
license_re = make_license_re(license_text)
-
+
#search for the first line of the license in the target file
line_re = re.compile(license_re.values()[0])
found_match = line_re.search(clean_file_contents)
-
+
if found_match:
clean_file_contents = clean_file_contents[found_match.start():]
-
- #check license as it is
+
+ #check that license exists without any added or removed words
for (line_num, line_re_text) in license_re.items():
line_re = re.compile(line_re_text)
found_match = line_re.match(clean_file_contents)
-
+
if found_match:
clean_file_contents = clean_file_contents[found_match.end():]
else:
- log_warning(filename, 1, "license does not match at", license_lines[line_num])
+ #log_warning(filename, 1, "license does not match at", license_lines[line_num])
+ return (line_num, license_lines[line_num])
+
+ return None # success
+
+def check_license_in_file(filename, file_contents):
+ """
+ Checks if the file contains a valid license.
+ It tries to find a match inside the file with any of the licenses configured
+
+ """
+ clean_file_contents = clean_comment_chars(file_contents)
+
+ #license that had the best match with the file
+ best_match = (-1, None)
+
+ #try to match with every license
+ for license in G_LICENSE_TEMPLATES:
+ call_result = check_specific_license_in_file(filename, clean_file_contents, license)
+
+ #if match is found just return
+ if call_result == None:
return None
+ #if no match found check if this license was a good candidate for the match
+ else:
+ best_match = call_result if call_result[0] > best_match[0] else best_match
+
+ #(this else clause is executed if the for loop exists naturally)
+ #if loop ended without return, this means no license matched
+ else:
+ #if no license matched at all
+ if best_match[1] == None:
+ log_warning(filename, 1, "no license found")
+
+ #get the license with the best match
+ else:
+ log_warning(filename, 1, "license does not match at", best_match[1])
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if the file does not contain a valid license text. It does not check if Copyright statements are included.
"""
exit(0)
-
+
for t in targets:
file_contents, _, _, _ = read_file(t)
check_license_in_file(t, file_contents)
def check_long_lines(filename, file_lines):
+ """
+ Checks if a line is too long by comparing it to a (configurable) threshold. If a line contains
+ string literals it is not checked
+
+ """
+ #regex for finding strings
string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
-
+
for i in range(len(file_lines)):
line = file_lines[i]
-
+
#if line is very long and DOES NOT contain strings
if len(line) > G_MAX_LINE_LENGTH_THRESHOLD and re.search(string_re, line) == None:
log_warning(filename, i + 1, "line too long, {0} characters [maximum is {1}]".format(len(line), G_MAX_LINE_LENGTH_THRESHOLD))
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if there are too long lines for readability. A line can have a maximum of {0} characters.
""".format(G_MAX_LINE_LENGTH_THRESHOLD)
exit(0)
-
+
for t in targets:
_, _, file_lines, _ = read_file(t)
check_long_lines(t, file_lines)
from common_modules.common import *
def check_single_definition_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+ """
+ Checks if a line contains several variable definitions
+
+ """
+ #remove arrays
+ #an array regex starts with "{", ends with "}" and contains anything in between
+ #an array of arrays is removed recursively inside out
+ array_re = re.compile(r'\{[^ {};]*\}')
+ clean_file_contents = clean_string_from_regex(clean_file_contents, array_re, '')
+
+ #remove brackets
+ array_brackets_re = re.compile(r'\[((?!\[|\]|;).)*\]')
+ clean_file_contents = clean_string_from_regex(clean_file_contents, array_brackets_re, '')
+
+ #remove angle brackets
+ template_re = re.compile(r'<((?![<>;]).)*>')
+ clean_file_contents = clean_string_from_regex(clean_file_contents, template_re, '')
+
#variable name can be preceeded by * for pointers or & for references, can be followed by an assignment
- var_decl_re_text = r"(((\**)|(&?))(\s*)(\w+)((=((?![,{]).)*)?))"
+ var_decl_re_text = r"""(
+ ((&?)(\*)*) # can start by & (for reference defs) and/or any number of * (for pointer defs)
+ (\s*)(\w+)(\s*) # followed by identifier
+ ((=((?![,;]).)*)?) # can be followed by an assignment
+ )"""
+
#types can be in namespaces or nested classes
- no_template_type_re_text = r"\w+(::\w+)*"
- simple_several_defs_re = re.compile(r"^(\s*){0}(\s+){1}((\s*),(\s*){1})+;".format(no_template_type_re_text, var_decl_re_text), re.MULTILINE)
-
- for match in re.finditer(simple_several_defs_re, clean_file_contents):
+ type_re_text = r"\w+(::\w+)*"
+
+ #it is enough to have
+ several_defs_re_text = r"""
+ ^(\s*){0}
+ (\s+){1}
+ ((\s*),(\s*){1})+;
+ """.format(type_re_text, var_decl_re_text)
+
+ several_defs_re = re.compile(several_defs_re_text, re.MULTILINE | re.VERBOSE)
+
+ for match in re.finditer(several_defs_re, clean_file_contents):
line_number = clean_file_contents.count("\n", 0, match.end())
log_warning(filename, line_number + 1, "several definitions on same line", file_lines[line_number].strip(" \t\r\n"))
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if a line contains several variable definitions (This does not include lines that contain template or array declarations).
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
from common_modules.common import *
def check_single_statement_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+ """
+ Checks if there are several statements on the same line
+
+ """
open_parantheses_count = 0
-
+
for i in range(len(file_lines)):
line = clean_file_lines[i]
-
+
open_parantheses_count += line.count("(") - line.count(")")
-
+
#check if there are several semicolons on the line and that is not in the middle of a for statement
if line.count(";") > 1 and open_parantheses_count == 0:
#check that there is NO right parentheses after semicolon
if __name__ == "__main__":
targets = sys.argv[1:]
targets = get_all_files(targets)
-
+
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if a line contains several statements.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
from common_modules.common import *
def check_tabs_no_spaces(filename, file_lines):
+ """
+ Checks if any tab character "\t" exists in the file
+
+ """
for i in range(len(file_lines)):
if file_lines[i].count("\t") > 0:
log_warning(filename, i + 1, "tab character (\\t) found, use 4 spaces instead", file_lines[i].strip(" "))
def check_correct_space_count(filename, file_lines):
+ """
+ Checks that the number of spaces at the beginning of every line is divislbe by four
+
+ """
in_multiline_comment = False
for i in range(len(file_lines)):
if in_multiline_comment:
+ #if end of muli-line comment
if file_lines[i].count("*/") > 0:
+ #just make sure there is no other new multi-line comment starting on the same line (after the current mult-line comment is closed)
in_multiline_comment = file_lines[i].count("/*") > 0 and file_lines[i].index("/*") > file_lines[i].index("*/")
else:
in_multiline_comment = file_lines[i].count("/*") > 0
-
+
+ #regex searches for the first character that is not a space character
found_match = re.search("(?! )", file_lines[i])
if found_match:
space_count = found_match.start()
def check_no_spacing_line_end(filename, file_lines):
+ """
+ Checks that lines do not end with unnecessary white space characters
+
+ """
for i in range(len(file_lines)):
if re.search(" $", file_lines[i]):
log_warning(filename, i + 1, "unneeded space(s) at end of line")
def check_tabbing_and_spacing(filename, file_lines):
+ """
+ Calls other functions that check general issues about tabbing and spacing
+
+ """
check_tabs_no_spaces(filename, file_lines)
check_correct_space_count(filename, file_lines)
check_no_spacing_line_end(filename, file_lines)
if len(targets) == 0:
print """
-\t**** No input provided ***
+\t**** No input provided ****
\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
\tGives warnings if a line contains unneeded spaces at end of line, contains tab characters (\\t)
\tor if spaces at line beginning are not divisible by 4.
"""
exit(0)
-
+
for t in targets:
if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
_, _, file_lines, _ = read_file(t)
###########################################################################
"""
- Contains utility functions used by other modules
+Contains utility functions used by other modules
"""
import sys, re, string, os
def clean_string_from_regex(s, regex, marker):
"""
- Removes all occurances of the passed regex from a string s, and puts instances of marker at the beginnings and ends
- of remved substring
- """
+ Removes all occurances of the passed regex from a string s, and puts instances of marker at the beginnings and ends
+ of remved substring
+ """
result = s
found_match = re.search(regex, result)
def get_clean_file_contents(file_contents):
"""
- Removes strings, C style and C++ comments style comments and macros from file_contents
+ Removes strings, C style and C++ comments style comments and macros from file_contents
+
"""
-
+
clean_file_contents = file_contents
-
+
#remove all strings
#a string is something that starts with a (") and ends with a ("), can have any character inside it except a (") that is not preceded by a slash (\) that is not preceded by another slash
#this means a string can contains a \\ escape sequence
#a string can contain a \" escape sequence
#a string can end with \\", but it does not end with \"
string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
-
+
#'x' is passed as a marker, to mark the beginning and of strings
#to preserve spacing around string beginning and end
clean_file_contents = clean_string_from_regex(clean_file_contents, string_re, 'x')
char_re = re.compile(r'\'((?!((?<!((?<!\\)\\))\')).)*\'')
clean_file_contents = clean_string_from_regex(clean_file_contents, char_re, 'x')
-
+
#remove multi-line comments
ml_comment_re = re.compile(r'(\s*)/\*((?!\*/).)*\*/', re.DOTALL)
clean_file_contents = clean_string_from_regex(clean_file_contents, ml_comment_re, '')
-
+
#remove single line comments
sl_comment_re = re.compile(r'[ \t\r\f]*//.*$', re.MULTILINE)
clean_file_contents = re.sub(sl_comment_re, '', clean_file_contents)
#a macro is #define statement followed by a few statements that have a slash in the line before it
macro_re = re.compile(r'#define(\s+)(((?!\\).)+)((\\\n(((?!\\).)+))*)')
clean_file_contents = clean_string_from_regex(clean_file_contents, macro_re, '')
-
+
return clean_file_contents
def read_file(filename):
"""
- Reads contents of the file, returns a 4-tuple containing:
+ Reads contents of the file, returns a 4-tuple containing:
1- raw file contents
2- file contents without comments, strings and macros
3- raw file contents as lines
4- file contents without comments, strings and macros as lines
+
"""
file_contents = open(filename).read()
clean_file_contents = get_clean_file_contents(file_contents)
-
+
file_lines = file_contents.split("\n")
clean_file_lines = clean_file_contents.split("\n")
-
+
return (file_contents, clean_file_contents, file_lines, clean_file_lines)
def get_all_files(targets):
"""
- Iterates over targets and gets names of all files
+ Iterates over targets and gets names of all files
+
"""
filenames = []
for t in targets:
for (path, _, files) in os.walk(t, followlinks=True):
for f in files:
filenames.append(os.path.abspath(os.path.join(path, f)))
-
+
#if file: process directly
else:
filenames.append(os.path.abspath(t))
-
+
return filenames
def log_warning(filename, line_number, description, line_content = None):
"""
- Prints a warning to the standard output
+ Prints a warning to the standard output
+
"""
if line_content == None:
print "{0}:{1}: warning: {2}".format(filename, line_number, description)
else:
print "{0}:{1}: warning: {2}: {3}".format(filename, line_number, description, line_content)
-
+
config.G_WARNING_COUNT += 1
def get_warning_count():
"""
- Returns the number of reported warnings
- """
- return config.G_WARNING_COUNT
-
-
+ Returns the number of reported warnings
+ """
+ return config.G_WARNING_COUNT
\ No newline at end of file
###########################################################################
"""
- Contains configurations constants
+Contains configurations constants
+
"""
G_MAX_INDENTATION_THRESHOLD = 6
G_MAX_LINE_LENGTH_THRESHOLD = 140
G_WARNING_COUNT = 0
+G_TAB_SEQUENCE = (' ', 4)
+
+
+G_LICENSE_TEMPLATES = ["""
+/***************************************************************************
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+****************************************************************************/
+""", """
+// Copyright [YYYY], Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+]