diff --git a/check_source/check_style_c.py b/check_source/check_style_c.py index e8507211d137d85d5ced0e4afc87c2b827ea8e0d..d3d40314f1fd1da92e16a6af86be43b87c23ecaf 100755 --- a/check_source/check_style_c.py +++ b/check_source/check_style_c.py @@ -48,6 +48,12 @@ def is_ignore(f): return False +def hash_of_file(fp): + import hashlib + with open(fp, 'rb') as fh: + return hashlib.sha512(fh.read()).digest() + + # TODO # # Add checks for: @@ -238,7 +244,7 @@ def extract_ws_indent(index): return text[:len(text) - len(text.lstrip("\t"))] -def extract_statement_if(index_kw): +def extract_statement_if(fn, index_kw): # assert(tokens[index_kw].text == "if") # ignore preprocessor @@ -257,7 +263,7 @@ def extract_statement_if(index_kw): # print(tokens[i_next]) if tokens[i_next].type != Token.Punctuation or tokens[i_next].text != "(": - warning("E105", "no '(' after '%s'" % tokens[index_kw].text, i_start, i_next) + warning(fn, "E105", "no '(' after '%s'" % tokens[index_kw].text, i_start, i_next) return None i_end = tk_match_backet(i_next) @@ -372,20 +378,20 @@ def tk_range_find_by_type(index_start, index_end, type_, filter_tk=None): return -1 -def warning(id_, message, index_kw_start, index_kw_end): +def warning(fn, id_, message, index_kw_start, index_kw_end): if PRINT_QTC_TASKFORMAT: - print("%s\t%d\t%s\t%s %s" % (filepath, tokens[index_kw_start].line, "comment", id_, message)) + fn("%s\t%d\t%s\t%s %s" % (filepath, tokens[index_kw_start].line, "comment", id_, message)) else: - print("%s:%d: %s: %s" % (filepath, tokens[index_kw_start].line, id_, message)) + fn("%s:%d: %s: %s" % (filepath, tokens[index_kw_start].line, id_, message)) if WARN_TEXT: - print(tk_range_to_str(index_kw_start, index_kw_end, expand_tabs=True)) + fn(tk_range_to_str(index_kw_start, index_kw_end, expand_tabs=True)) -def warning_lineonly(id_, message, line): +def warning_lineonly(fn, id_, message, line): if PRINT_QTC_TASKFORMAT: - print("%s\t%d\t%s\t%s %s" % (filepath, line, "comment", id_, message)) + fn("%s\t%d\t%s\t%s %s" % (filepath, line, "comment", id_, message)) else: - print("%s:%d: %s: %s" % (filepath, line, id_, message)) + fn("%s:%d: %s: %s" % (filepath, line, id_, message)) # print(tk_range_to_str(index_kw_start, index_kw_end)) @@ -393,17 +399,17 @@ def warning_lineonly(id_, message, line): # ------------------------------------------------------------------ # Own Blender rules here! -def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): +def blender_check_kw_if(fn, index_kw_start, index_kw, index_kw_end): # check if we have: 'if(' if not tk_item_is_ws(tokens[index_kw + 1]): - warning("E106", "no white space between '%s('" % tokens[index_kw].text, index_kw_start, index_kw_end) + warning(fn, "E106", "no white space between '%s('" % tokens[index_kw].text, index_kw_start, index_kw_end) # check for: ){ index_next = tk_advance_ws_newline(index_kw_end, 1) if tokens[index_next].type == Token.Punctuation and tokens[index_next].text == "{": if not tk_item_is_ws_newline(tokens[index_next - 1]): - warning("E107", "no white space between trailing bracket '%s (){'" % + warning(fn, "E107", "no white space between trailing bracket '%s (){'" % tokens[index_kw].text, index_kw_start, index_kw_end) # check for: if () @@ -420,7 +426,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): # allow this to go unnoticed pass else: - warning("E108", "if body brace on a new line '%s ()\\n{'" % + warning(fn, "E108", "if body brace on a new line '%s ()\\n{'" % tokens[index_kw].text, index_kw, index_kw_end) else: # no '{' on a multi-line if @@ -437,7 +443,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): # b); # if not (tokens[index_next].type == Token.Punctuation and tokens[index_next].text == ";"): - warning("E109", "multi-line if should use a brace '%s (\\n\\n) statement;'" % + warning(fn, "E109", "multi-line if should use a brace '%s (\\n\\n) statement;'" % tokens[index_kw].text, index_kw, index_kw_end) # check for correct single line use & indentation @@ -461,7 +467,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): has_sep = True break if not has_sep: - warning("E200", "bad single line indent '%s (...) {'" % + warning(fn, "E200", "bad single line indent '%s (...) {'" % tokens[index_kw].text, index_kw, index_next) del has_sep del ws_kw, ws_end @@ -479,7 +485,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): ws_kw = extract_ws_indent(index_kw) ws_end = extract_ws_indent(index_end) if len(ws_kw) + 1 != len(ws_end): - warning("E201", "bad single line indent '%s (...) {'" % + warning(fn, "E201", "bad single line indent '%s (...) {'" % tokens[index_kw].text, index_kw, index_end) del ws_kw, ws_end del index_end @@ -492,7 +498,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): # if tokens[index_kw_end].line == tokens[index_next].line: if not (tokens[index_next].type == Token.Punctuation and tokens[index_next].text == ";"): - warning("E103", "multi-line should use a on a new line '%s (\\n\\n) {'" % + warning(fn, "E103", "multi-line should use a on a new line '%s (\\n\\n) {'" % tokens[index_kw].text, index_kw, index_kw_end) # Note: this could be split into its own function @@ -538,7 +544,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): # needed for some comments pass else: - warning("E110", "if body brace mult-line indent mismatch", i, i) + warning(fn, "E110", "if body brace mult-line indent mismatch", i, i) del index_kw_bracket del ws_indent del l_last @@ -550,18 +556,18 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end): index_final = tk_match_backet(index_next) index_final_step = tk_advance_no_ws(index_final, 1) if tokens[index_final_step].text == ";": - warning("E111", "semi-colon after brace '%s () { ... };'" % + warning(fn, "E111", "semi-colon after brace '%s () { ... };'" % tokens[index_kw].text, index_final_step, index_final_step) -def blender_check_kw_else(index_kw): +def blender_check_kw_else(fn, index_kw): # for 'else if' use the if check. i_next = tk_advance_ws_newline(index_kw, 1) # check there is at least one space between: # else{ if index_kw + 1 == i_next: - warning("E112", "else has no space between following brace 'else{'", index_kw, i_next) + warning(fn, "E112", "else has no space between following brace 'else{'", index_kw, i_next) # check if there are more than 1 spaces after else, but nothing after the following brace # else { @@ -577,7 +583,7 @@ def blender_check_kw_else(index_kw): # check if the next data after { is on a newline i_next_next = tk_advance_ws_newline(i_next, 1) if tokens[i_next].line != tokens[i_next_next].line: - warning("E113", "unneeded whitespace before brace 'else ... {'", index_kw, i_next) + warning(fn, "E113", "unneeded whitespace before brace 'else ... {'", index_kw, i_next) # this check only tests for: # else @@ -596,7 +602,7 @@ def blender_check_kw_else(index_kw): if tokens[i_newline].text.startswith("#"): pass else: - warning("E114", "else body brace on a new line 'else\\n{'", index_kw, i_next) + warning(fn, "E114", "else body brace on a new line 'else\\n{'", index_kw, i_next) # this check only tests for: # else @@ -629,7 +635,7 @@ def blender_check_kw_else(index_kw): # allow this to go unnoticed pass else: - warning("E115", "else if is split by a new line 'else\\nif'", index_kw, i_next) + warning(fn, "E115", "else if is split by a new line 'else\\nif'", index_kw, i_next) # check # } else @@ -637,10 +643,10 @@ def blender_check_kw_else(index_kw): i_prev = tk_advance_no_ws(index_kw, -1) if tokens[i_prev].type == Token.Punctuation and tokens[i_prev].text == "}": if tokens[index_kw].line == tokens[i_prev].line: - warning("E116", "else has no newline before the brace '} else'", i_prev, index_kw) + warning(fn, "E116", "else has no newline before the brace '} else'", i_prev, index_kw) -def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end): +def blender_check_kw_switch(fn, index_kw_start, index_kw, index_kw_end): # In this function we check the body of the switch # switch (value) { @@ -680,7 +686,7 @@ def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end): if ws_other_indent.isspace(): ws_test_other = ws_test[tokens[i].text] if not ws_other_indent.startswith(ws_test_other): - warning("E117", "%s is not indented enough" % tokens[i].text, i, i) + warning(fn, "E117", "%s is not indented enough" % tokens[i].text, i, i) if tokens[i].text == "case": # while where here, check: @@ -691,27 +697,27 @@ def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end): # can be None when the identifier isn't an 'int' if i_case is not None: if tokens[i_case - 1].text.isspace(): - warning("E132", "%s space before colon" % tokens[i].text, i, i_case) + warning(fn, "E132", "%s space before colon" % tokens[i].text, i, i_case) del i_case else: - warning("E119", "switch isn't the first token in the line", index_kw_start, index_kw_end) + warning(fn, "E119", "switch isn't the first token in the line", index_kw_start, index_kw_end) else: - warning("E120", "switch brace missing", index_kw_start, index_kw_end) + warning(fn, "E120", "switch brace missing", index_kw_start, index_kw_end) def blender_check_kw_sizeof(index_kw): if tokens[index_kw + 1].text != "(": - warning("E121", "expected '%s('" % tokens[index_kw].text, index_kw, index_kw + 1) + warning(fn, "E121", "expected '%s('" % tokens[index_kw].text, index_kw, index_kw + 1) -def blender_check_cast(index_kw_start, index_kw_end): +def blender_check_cast(fn, index_kw_start, index_kw_end): # detect: '( float...' if tokens[index_kw_start + 1].text.isspace(): - warning("E122", "cast has space after first bracket '( type...'", index_kw_start, index_kw_end) + warning(fn, "E122", "cast has space after first bracket '( type...'", index_kw_start, index_kw_end) # detect: '...float )' if tokens[index_kw_end - 1].text.isspace(): - warning("E123", "cast has space before last bracket '... )'", index_kw_start, index_kw_end) + warning(fn, "E123", "cast has space before last bracket '... )'", index_kw_start, index_kw_end) # detect no space before operator: '(float*)' for i in range(index_kw_start + 1, index_kw_end): @@ -722,10 +728,10 @@ def blender_check_cast(index_kw_start, index_kw_end): elif tokens[i - 1].text.isspace(): pass else: - warning("E124", "cast has no preceding whitespace '(type*)'", index_kw_start, index_kw_end) + warning(fn, "E124", "cast has no preceding whitespace '(type*)'", index_kw_start, index_kw_end) -def blender_check_comma(index_kw): +def blender_check_comma(fn, index_kw): i_next = tk_advance_ws_newline(index_kw, 1) # check there is at least one space between: @@ -733,13 +739,13 @@ def blender_check_comma(index_kw): if index_kw + 1 == i_next: # allow: (struct FooBar){ .a, .b, .c,} if tokens[i_next].text != "}": - warning("E125", "comma has no space after it ',sometext'", index_kw, i_next) + warning(fn, "E125", "comma has no space after it ',sometext'", index_kw, i_next) if tokens[index_kw - 1].type == Token.Text and tokens[index_kw - 1].text.isspace(): - warning("E126", "comma space before it 'sometext ,", index_kw, i_next) + warning(fn, "E126", "comma space before it 'sometext ,", index_kw, i_next) -def blender_check_period(index_kw): +def blender_check_period(fn, index_kw): # check we're now apart of ... if (tokens[index_kw - 1].text == ".") or (tokens[index_kw + 1].text == "."): return @@ -751,9 +757,9 @@ def blender_check_period(index_kw): # ... check for this case by allowing comma or brace beforehand. i_prev = tk_advance_ws_newline(index_kw - 1, -1) if tokens[i_prev].text not in {",", "{"}: - warning("E127", "period space before it 'sometext .", index_kw, index_kw) + warning(fn, "E127", "period space before it 'sometext .", index_kw, index_kw) if tokens[index_kw + 1].type == Token.Text and tokens[index_kw + 1].text.isspace(): - warning("E128", "period space after it '. sometext", index_kw, index_kw) + warning(fn, "E128", "period space after it '. sometext", index_kw, index_kw) def _is_ws_pad(index_start, index_end): @@ -761,7 +767,7 @@ def _is_ws_pad(index_start, index_end): tokens[index_end + 1].text.isspace()) -def blender_check_operator(index_start, index_end, op_text, is_cpp): +def blender_check_operator(fn, index_start, index_end, op_text, is_cpp): if op_text == "->": # allow compiler to handle return @@ -783,17 +789,17 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): # Or: (struct FooBar)&var (tokens[index_prev].flag & IS_CAST) ): - warning("E130", "no space before operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space before operator '%s'" % op_text, index_start, index_end) if (not tokens[index_end + 1].text.isspace() and tokens[index_end + 1].text not in {"]", ")", "}"}): # TODO, needs work to be useful - # warning("E130", "no space after operator '%s'" % op_text, index_start, index_end) + # warning(fn, "E130", "no space after operator '%s'" % op_text, index_start, index_end) pass elif op_text in {"/", "%", "^", "|", "=", "<", ">", "?"}: if not _is_ws_pad(index_start, index_end): if not (is_cpp and ("<" in op_text or ">" in op_text)): - warning("E130", "no space around operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end) elif op_text == ":": # check we're not # case 1: @@ -804,7 +810,7 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): if not (tokens[index_start - 1].text.isspace() or tokens[index_start - 1].text == "default"): i_case = tk_advance_line_to_token(index_start, -1, "case", Token.Keyword) if i_case is None: - warning("E130", "no space around operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end) elif op_text == "&": pass # TODO, check if this is a pointer reference or not elif op_text == "*": @@ -826,9 +832,9 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): pass elif ((tokens[index_start - 1].type in Token.Number) or (tokens[index_start + 1].type in Token.Number)): - warning("E130", "no space around operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end) elif not (tokens[index_start - 1].text.isspace() or tokens[index_start - 1].text in {"(", "[", "{"}): - warning("E130", "no space before operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space before operator '%s'" % op_text, index_start, index_end) elif len(op_text) == 2: # todo, remove operator check from `if` if op_text in {"+=", "-=", "*=", "/=", "&=", "|=", "^=", @@ -841,20 +847,20 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): }: if not _is_ws_pad(index_start, index_end): if not (is_cpp and ("<" in op_text or ">" in op_text)): - warning("E130", "no space around operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end) elif op_text in {"++", "--"}: pass # TODO, figure out the side we are adding to! ''' if (tokens[index_start - 1].text.isspace() or tokens[index_end + 1].text.isspace()): - warning("E130", "spaces surrounding operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "spaces surrounding operator '%s'" % op_text, index_start, index_end) ''' elif op_text in {"!!", "!*"}: # operators we _dont_ want whitespace after (pointers mainly) # we can assume these are pointers if tokens[index_end + 1].text.isspace(): - warning("E130", "spaces after operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "spaces after operator '%s'" % op_text, index_start, index_end) elif op_text == "**": pass # handle below @@ -865,12 +871,12 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): elif op_text == "*>": pass # ignore for now, C++ <Class *> else: - warning("E000.0", "unhandled operator 2 '%s'" % op_text, index_start, index_end) + warning(fn, "E000.0", "unhandled operator 2 '%s'" % op_text, index_start, index_end) elif len(op_text) == 3: if op_text in {">>=", "<<="}: if not _is_ws_pad(index_start, index_end): if not (is_cpp and ("<" in op_text or ">" in op_text)): - warning("E130", "no space around operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end) elif op_text == "***": pass elif op_text in {"*--", "*++"}: @@ -882,22 +888,22 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): elif op_text == "::~": pass else: - warning("E000.1", "unhandled operator 3 '%s'" % op_text, index_start, index_end) + warning(fn, "E000.1", "unhandled operator 3 '%s'" % op_text, index_start, index_end) elif len(op_text) == 4: if op_text == "*>::": pass else: - warning("E000.2", "unhandled operator 4 '%s'" % op_text, index_start, index_end) + warning(fn, "E000.2", "unhandled operator 4 '%s'" % op_text, index_start, index_end) else: - warning("E000.3", "unhandled operator (len > 4) '%s'" % op_text, index_start, index_end) + warning(fn, "E000.3", "unhandled operator (len > 4) '%s'" % op_text, index_start, index_end) if len(op_text) > 1: if op_text[0] == "*" and op_text[-1] == "*": if ((not tokens[index_start - 1].text.isspace()) and (not tokens[index_start - 1].type == Token.Punctuation)): - warning("E130", "no space before pointer operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "no space before pointer operator '%s'" % op_text, index_start, index_end) if tokens[index_end + 1].text.isspace(): - warning("E130", "space before pointer operator '%s'" % op_text, index_start, index_end) + warning(fn, "E130", "space before pointer operator '%s'" % op_text, index_start, index_end) # check if we are first in the line if op_text[0] == "!": @@ -930,18 +936,18 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp): pass else: if tk_index_is_linestart(index_start): - warning("E143", "operator starts a new line '%s'" % op_text, index_start, index_end) + warning(fn, "E143", "operator starts a new line '%s'" % op_text, index_start, index_end) -def blender_check_linelength(index_start, index_end, length): +def blender_check_linelength(fn, index_start, index_end, length): if length > LIN_SIZE: text = tk_range_to_str(index_start, index_end, expand_tabs=True) for l in text.split("\n"): if len(l) > LIN_SIZE: - warning("E144", "line length %d > %d" % (len(l), LIN_SIZE), index_start, index_end) + warning(fn, "E144", "line length %d > %d" % (len(l), LIN_SIZE), index_start, index_end) -def blender_check_function_definition(i): +def blender_check_function_definition(fn, i): # Warning, this is a fairly slow check and guesses # based on some fuzzy rules @@ -1006,10 +1012,10 @@ def blender_check_function_definition(i): i_begin += 1 # now we are done skipping stuff - warning("E101", "function's '{' must be on a newline", i_begin, i) + warning(fn, "E101", "function's '{' must be on a newline", i_begin, i) -def blender_check_brace_indent(i): +def blender_check_brace_indent(fn, i): # assert(tokens[index].text == "{") i_match = tk_match_backet(i) @@ -1028,10 +1034,10 @@ def blender_check_brace_indent(i): ws_i = ws_i[:len(ws_i) - len(ws_i.lstrip())] ws_i_match = ws_i_match[:len(ws_i_match) - len(ws_i_match_lstrip)] if ws_i != ws_i_match: - warning("E104", "indentation '{' does not match brace", i, i_match) + warning(fn, "E104", "indentation '{' does not match brace", i, i_match) -def quick_check_includes(lines): +def quick_check_includes(fn, lines): # Find overly relative headers (could check other things here too...) # header dupes @@ -1051,19 +1057,19 @@ def quick_check_includes(lines): if os.path.exists(l_header_full): l_header_rel = os.path.relpath(l_header_full, base) if l_header.count("/") != l_header_rel.count("/"): - warning_lineonly("E170", "overly relative include %r" % l_header, i + 1) + warning_lineonly(fn, "E170", "overly relative include %r" % l_header, i + 1) # check if we're in mode than once len_inc = len(inc) inc.add(l_header) if len(inc) == len_inc: - warning_lineonly("E171", "duplicate includes %r" % l_header, i + 1) + warning_lineonly(fn, "E171", "duplicate includes %r" % l_header, i + 1) quick_check_includes.re_inc_match = re.compile(r"\s*#\s*include\s+\"([a-zA-Z0-9_\-\.\/]+)\"").match -def quick_check_indentation(lines): +def quick_check_indentation(fn, lines): """ Quick check for multiple tab indents. """ @@ -1091,8 +1097,9 @@ def quick_check_indentation(lines): # we have indent, check previous line if not ls_prev.rstrip().endswith("\\"): # report indented line - warning_lineonly("E145", "indentation found with preprocessor " - "(expected none or after '#')", i + 1) + warning_lineonly( + fn, "E145", "indentation found with preprocessor " + "(expected none or after '#')", i + 1) skip = True if ls[0:2] == "//": @@ -1126,8 +1133,9 @@ def quick_check_indentation(lines): tabs = l[:len(l) - len(ls)] t = len(tabs) if (t > t_prev + 1) and (t_prev != -1): - warning_lineonly("E146", "indentation mismatch (indent of %d) '%s'" % - (t - t_prev, tabs), i + 1) + warning_lineonly( + fn, "E146", "indentation mismatch (indent of %d) '%s'" % + (t - t_prev, tabs), i + 1) t_prev = t # check for same indentation with different space/tab mix @@ -1135,8 +1143,9 @@ def quick_check_indentation(lines): ws_expand = ws.expandtabs(4) if ws_expand == ws_prev_expand: if ws != ws_prev: - warning_lineonly("E152", "indentation tab/space mismatch", - i + 1) + warning_lineonly( + fn, "E152", "indentation tab/space mismatch", + i + 1) ws_prev = ws ws_prev_expand = ws_expand @@ -1146,7 +1155,7 @@ re_ifndef = re.compile(r"^\s*#\s*ifndef\s+([A-z0-9_]+).*$") re_define = re.compile(r"^\s*#\s*define\s+([A-z0-9_]+).*$") -def quick_check_include_guard(lines): +def quick_check_include_guard(fn, lines): # found = 0 def_value = "" ok = False @@ -1182,13 +1191,15 @@ def quick_check_include_guard(lines): # print("found:", def_value, "->", filepath) if def_value != guard: # print("%s: %s -> %s" % (filepath, def_value, guard)) - warning_lineonly("E147", "non-conforming include guard (found %r, expected %r)" % - (def_value, guard), i + 1) + warning_lineonly( + fn, "E147", "non-conforming include guard (found %r, expected %r)" % + (def_value, guard), i + 1) else: - warning_lineonly("E148", "missing include guard %r" % guard, 1) + warning_lineonly( + fn, "E148", "missing include guard %r" % guard, 1) -def quick_check_source(fp, code, args): +def quick_check_source(fp, code, args, fn): global filepath @@ -1199,14 +1210,14 @@ def quick_check_source(fp, code, args): lines = code.split("\n") if is_header: - quick_check_include_guard(lines) + quick_check_include_guard(fn, lines) - quick_check_includes(lines) + quick_check_includes(fn, lines) - quick_check_indentation(lines) + quick_check_indentation(fn, lines) -def scan_source(fp, code, args): +def scan_source(fp, code, args, fn): # print("scanning: %r" % fp) global filepath @@ -1238,20 +1249,20 @@ def scan_source(fp, code, args): # print(tok.type, tok.text) if tok.type == Token.Keyword: if tok.text in {"switch", "while", "if", "for"}: - item_range = extract_statement_if(i) + item_range = extract_statement_if(fn, i) if item_range is not None: - blender_check_kw_if(item_range[0], i, item_range[1]) + blender_check_kw_if(fn, item_range[0], i, item_range[1]) if tok.text == "switch": - blender_check_kw_switch(item_range[0], i, item_range[1]) + blender_check_kw_switch(fn, item_range[0], i, item_range[1]) elif tok.text == "else": - blender_check_kw_else(i) + blender_check_kw_else(fn, i) elif tok.text == "sizeof": blender_check_kw_sizeof(i) elif tok.type == Token.Punctuation: if tok.text == ",": - blender_check_comma(i) + blender_check_comma(fn, i) elif tok.text == ".": - blender_check_period(i) + blender_check_period(fn, i) elif tok.text == "[": # note, we're quite relaxed about this but # disallow 'foo [' @@ -1260,16 +1271,16 @@ def scan_source(fp, code, args): # c++ can do delete [] pass else: - warning("E149", "space before '['", i, i) + warning(fn, "E149", "space before '['", i, i) elif tok.text == "(": # check if this is a cast, eg: # (char), (char **), (float (*)[3]) item_range = extract_cast(i) if item_range is not None: - blender_check_cast(item_range[0], item_range[1]) + blender_check_cast(fn, item_range[0], item_range[1]) elif tok.text == "{": # check matching brace is indented correctly (slow!) - blender_check_brace_indent(i) + blender_check_brace_indent(fn, i) # check previous character is either a '{' or whitespace. if ((tokens[i - 1].line == tok.line) and @@ -1277,15 +1288,15 @@ def scan_source(fp, code, args): tokens[i - 1].text == "{" or tokens[i - 1].flag & IS_CAST) ): - warning("E150", "no space before '{'", i, i) + warning(fn, "E150", "no space before '{'", i, i) - blender_check_function_definition(i) + blender_check_function_definition(fn, i) elif tok.type == Token.Operator: # we check these in pairs, only want first if tokens[i - 1].type != Token.Operator: op, index_kw_end = extract_operator(i) - blender_check_operator(i, index_kw_end, op, is_cpp) + blender_check_operator(fn, i, index_kw_end, op, is_cpp) elif tok.type in Token.Comment: doxyfn = None if "\\file" in tok.text: @@ -1296,12 +1307,12 @@ def scan_source(fp, code, args): if doxyfn is not None: doxyfn_base = os.path.basename(doxyfn) if doxyfn_base != filepath_base: - warning("E151", "doxygen filename mismatch %s != %s" % (doxyfn_base, filepath_base), i, i) + warning(fn, "E151", "doxygen filename mismatch %s != %s" % (doxyfn_base, filepath_base), i, i) doxyfn_split = doxyfn.split("/") if len(doxyfn_split) > 1: fp_split = filepath_split[-len(doxyfn_split):] if doxyfn_split != fp_split: - warning("E151", "doxygen filepath mismatch %s != %s" % (doxyfn, "/".join(fp_split)), i, i) + warning(fn, "E151", "doxygen filepath mismatch %s != %s" % (doxyfn, "/".join(fp_split)), i, i) del fp_split del doxyfn_base, doxyfn_split del doxyfn @@ -1309,7 +1320,7 @@ def scan_source(fp, code, args): # ensure line length if (not args.no_length_check) and tok.type == Token.Text and tok.text == "\n": # check line len - blender_check_linelength(index_line_start, i - 1, col) + blender_check_linelength(fn, index_line_start, i - 1, col) col = 0 index_line_start = i + 1 @@ -1326,21 +1337,51 @@ def scan_source(fp, code, args): # #print(value, end="") -def scan_source_filepath(filepath, args): +def scan_source_filepath__cached(filepath, args, fn, cache): + cache_files_src, cache_files_dst = cache + h_code = hash_of_file(filepath) + info_src = cache_files_src.get(filepath) + if info_src is not None and info_src[0] == h_code: + for a in info_src[1]: + fn(*a) + cache_files_dst[filepath] = info_src + return None + else: + lines = [] + info_dst = (h_code, lines) + def fn_wrap(*a): + fn_wrap.lines.append(a) + fn_wrap.fn(*a) + fn_wrap.fn = fn + fn_wrap.lines = lines + cache_files_dst[filepath] = info_dst + return cache_files_src, cache_files_dst, fn_wrap + + +def scan_source_filepath(filepath, args, fn, cache=None): # for quick tests # ~ if not filepath.endswith("creator.c"): # ~ return code = open(filepath, 'r', encoding="utf-8").read() + if cache: + cache_result = scan_source_filepath__cached(filepath, args, fn, cache) + if cache_result is None: + # No need to ececute + return + else: + cache_files_src, cache_files_dst, fn = cache_result + del cache_result + # fast checks which don't require full parsing - quick_check_source(filepath, code, args) + quick_check_source(filepath, code, args, fn) # use lexer - scan_source(filepath, code, args) + scan_source(filepath, code, args, fn) -def scan_source_recursive(dirpath, args): +def scan_source_recursive(dirpath, args, fn, cache=None): import os from os.path import join, splitext @@ -1367,7 +1408,7 @@ def scan_source_recursive(dirpath, args): if is_ignore(filepath): continue - scan_source_filepath(filepath, args) + scan_source_filepath(filepath, args, fn, cache) def create_parser(): @@ -1394,6 +1435,9 @@ def main(argv=None): import sys import os + # For cache + import pickle + if argv is None: argv = sys.argv[1:] @@ -1403,6 +1447,7 @@ def main(argv=None): print("Scanning:", SOURCE_DIR) + if 0: # SOURCE_DIR = os.path.normpath( # os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", "..")))) @@ -1410,13 +1455,35 @@ def main(argv=None): scan_source_recursive(os.path.join(SOURCE_DIR, "source/blender/makesrna/intern"), args) sys.exit(0) + cache_filename = os.environ.get("CHECK_STYLE_C_CACHE", "") + if cache_filename: + if os.path.exists(cache_filename): + with open(cache_filename, 'rb') as fh: + (hash_of_script_src, cache_files_src) = pickle.load(fh) + else: + hash_of_script_src = b'' + + cache_files_dst = {} + hash_of_script_dst = hash_of_file(__file__) + # If we change the Python code, ignore old cache. + if hash_of_script_src != hash_of_script_dst: + cache_files_src = {} + cache = (cache_files_src, cache_files_dst) + else: + cache = None + for filepath in args.paths: if os.path.isdir(filepath): # recursive search - scan_source_recursive(filepath, args) + scan_source_recursive(filepath, args, print, cache) else: # single file - scan_source_filepath(filepath, args) + scan_source_filepath(filepath, args, print, cache) + + + if cache_filename: + with open(cache_filename, 'wb') as fh: + pickle.dump((hash_of_script_dst, cache_files_dst), fh) if __name__ == "__main__": diff --git a/tests/check_source/check_style_c_test.py b/tests/check_source/check_style_c_test.py index 631ad143f9847d825d444729a1f920f84c21d90f..a7573c6372bb59ebe37a83d9844bce5c02725db7 100755 --- a/tests/check_source/check_style_c_test.py +++ b/tests/check_source/check_style_c_test.py @@ -14,9 +14,7 @@ sys.path.append( import unittest -warnings = [] import check_style_c -check_style_c.print = warnings.append # ---- parser = check_style_c.create_parser() @@ -36,8 +34,8 @@ FUNC_END = """ def test_code(code): - warnings.clear() - check_style_c.scan_source("test.c", code, args) + warnings = [] + check_style_c.scan_source("test.c", code, args, warnings.append) err_found = [w.split(":", 3)[2].strip() for w in warnings] # print(warnings) return err_found