diff options
author | Russell Brenner <russellbrenner@google.com> | 2010-11-18 17:33:13 -0800 |
---|---|---|
committer | Russell Brenner <russellbrenner@google.com> | 2010-12-02 13:47:21 -0800 |
commit | 6b70adc33054f8aee8c54d0f460458a9df11b8a5 (patch) | |
tree | 103a13998c33944d6ab3b8318c509a037e639460 /WebKitTools/Scripts/webkitpy/style | |
parent | bdf4ebc8e70b2d221b6ee7a65660918ecb1d33aa (diff) | |
download | external_webkit-6b70adc33054f8aee8c54d0f460458a9df11b8a5.zip external_webkit-6b70adc33054f8aee8c54d0f460458a9df11b8a5.tar.gz external_webkit-6b70adc33054f8aee8c54d0f460458a9df11b8a5.tar.bz2 |
Merge WebKit at r72274: Initial merge by git.
Change-Id: Ie51f0b4a16da82942bd516dce59cfb79ebbe25fb
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/style')
-rw-r--r-- | WebKitTools/Scripts/webkitpy/style/checker.py | 16 | ||||
-rwxr-xr-x | WebKitTools/Scripts/webkitpy/style/checker_unittest.py | 27 | ||||
-rw-r--r-- | WebKitTools/Scripts/webkitpy/style/checkers/cpp.py | 205 | ||||
-rw-r--r-- | WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py | 98 |
4 files changed, 275 insertions, 71 deletions
diff --git a/WebKitTools/Scripts/webkitpy/style/checker.py b/WebKitTools/Scripts/webkitpy/style/checker.py index fb93eb9..e10eec5 100644 --- a/WebKitTools/Scripts/webkitpy/style/checker.py +++ b/WebKitTools/Scripts/webkitpy/style/checker.py @@ -131,11 +131,13 @@ _PATH_RULES_SPECIFIER = [ "WebKit/efl/ewk/", # There is no clean way to avoid "yy_*" names used by flex. "WebCore/css/CSSParser.cpp", - # There is no clean way to avoid "xxx_data" methods inside - # Qt's autotests since they are called automatically by the - # QtTest module. + # Qt code uses '_' in some places (such as private slots + # and on test xxx_data methos on tests) + "JavaScriptCore/qt/api/", + "WebKit/qt/Api/", "WebKit/qt/tests/", - "JavaScriptCore/qt/tests"], + "WebKit/qt/declarative/", + "WebKit/qt/examples/"], ["-readability/naming"]), ([# The GTK+ APIs use GTK+ naming style, which includes # lower-cased, underscore-separated values. @@ -232,15 +234,9 @@ _TEXT_FILE_EXTENSIONS = [ # WebKit maintains some files in Mozilla style on purpose to ease # future merges. _SKIPPED_FILES_WITH_WARNING = [ - # The Qt API and tests do not follow WebKit style. - # They follow Qt style. :) "gtk2drawing.c", # WebCore/platform/gtk/gtk2drawing.c "gtkdrawing.h", # WebCore/platform/gtk/gtkdrawing.h - "JavaScriptCore/qt/api/", "WebKit/gtk/tests/", - "WebKit/qt/Api/", - "WebKit/qt/tests/", - "WebKit/qt/examples/", # Soup API that is still being cooked, will be removed from WebKit # in a few months when it is merged into soup proper. The style # follows the libsoup style completely. diff --git a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py index 43d24fe..94d2c29 100755 --- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py +++ b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py @@ -223,11 +223,29 @@ class GlobalVariablesTest(unittest.TestCase): "readability/naming") assertNoCheck("WebCore/css/CSSParser.cpp", "readability/naming") + + # Test if Qt exceptions are indeed working + assertCheck("JavaScriptCore/qt/api/qscriptengine.cpp", + "readability/braces") + assertCheck("WebKit/qt/Api/qwebpage.cpp", + "readability/braces") + assertCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp", + "readability/braces") + assertCheck("WebKit/qt/declarative/platformplugin/WebPlugin.cpp", + "readability/braces") + assertCheck("WebKit/qt/examples/platformplugin/WebPlugin.cpp", + "readability/braces") + assertNoCheck("JavaScriptCore/qt/api/qscriptengine.cpp", + "readability/naming") + assertNoCheck("WebKit/qt/Api/qwebpage.cpp", + "readability/naming") assertNoCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp", "readability/naming") - assertNoCheck( - "JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp", - "readability/naming") + assertNoCheck("WebKit/qt/declarative/platformplugin/WebPlugin.cpp", + "readability/naming") + assertNoCheck("WebKit/qt/examples/platformplugin/WebPlugin.cpp", + "readability/naming") + assertNoCheck("WebCore/ForwardingHeaders/debugger/Debugger.h", "build/header_guard") @@ -277,12 +295,9 @@ class CheckerDispatcherSkipTest(unittest.TestCase): paths_to_skip = [ "gtk2drawing.c", "gtkdrawing.h", - "JavaScriptCore/qt/api/qscriptengine_p.h", "WebCore/platform/gtk/gtk2drawing.c", "WebCore/platform/gtk/gtkdrawing.h", "WebKit/gtk/tests/testatk.c", - "WebKit/qt/Api/qwebpage.h", - "WebKit/qt/tests/qwebsecurityorigin/tst_qwebsecurityorigin.cpp", ] for path in paths_to_skip: diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py index cd9e6ae..590bba9 100644 --- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py +++ b/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py @@ -152,6 +152,38 @@ def subn(pattern, replacement, s): return _regexp_compile_cache[pattern].subn(replacement, s) +def iteratively_replace_matches_with_char(pattern, char_replacement, s): + """Returns the string with replacement done. + + Every character in the match is replaced with char. + Due to the iterative nature, pattern should not match char or + there will be an infinite loop. + + Example: + pattern = r'<[^>]>' # template parameters + char_replacement = '_' + s = 'A<B<C, D>>' + Returns 'A_________' + + Args: + pattern: The regex to match. + char_replacement: The character to put in place of every + character of the match. + s: The string on which to do the replacements. + + Returns: + True, if the given line is blank. + """ + while True: + matched = search(pattern, s) + if not matched: + return s + start_match_index = matched.start(0) + end_match_index = matched.end(0) + match_length = end_match_index - start_match_index + s = s[:start_match_index] + char_replacement * match_length + s[end_match_index:] + + def up_to_unmatched_closing_paren(s): """Splits a string into two parts up to first unmatched ')'. @@ -284,20 +316,27 @@ class _FunctionState(object): self.current_function = '' self.in_a_function = False self.lines_in_function = 0 + # Make sure these will not be mistaken for real lines (even when a + # small amount is added to them). + self.body_start_line_number = -1000 + self.ending_line_number = -1000 - def begin(self, function_name): + def begin(self, function_name, body_start_line_number, ending_line_number): """Start analyzing function body. Args: function_name: The name of the function being tracked. + ending_line_number: The line number where the function ends. """ self.in_a_function = True self.lines_in_function = 0 self.current_function = function_name + self.body_start_line_number = body_start_line_number + self.ending_line_number = ending_line_number - def count(self): + def count(self, line_number): """Count line in current function body.""" - if self.in_a_function: + if self.in_a_function and line_number >= self.body_start_line_number: self.lines_in_function += 1 def check(self, error, line_number): @@ -325,7 +364,7 @@ class _FunctionState(object): self.current_function, self.lines_in_function, trigger)) def end(self): - """Stop analizing function body.""" + """Stop analyzing function body.""" self.in_a_function = False @@ -577,8 +616,8 @@ class CleansedLines(object): def close_expression(clean_lines, line_number, pos): """If input points to ( or { or [, finds the position that closes it. - If lines[line_number][pos] points to a '(' or '{' or '[', finds the the - line_number/pos that correspond to the closing of the expression. + If clean_lines.elided[line_number][pos] points to a '(' or '{' or '[', finds + the line_number/pos that correspond to the closing of the expression. Args: clean_lines: A CleansedLines instance containing the file. @@ -587,8 +626,8 @@ def close_expression(clean_lines, line_number, pos): Returns: A tuple (line, line_number, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the + ('', len(clean_lines.elided), -1) if we never find a close. Note we + ignore strings and comments when matching; and the line we return is the 'cleansed' line at line_number. """ @@ -604,8 +643,10 @@ def close_expression(clean_lines, line_number, pos): end_character = '}' num_open = line.count(start_character) - line.count(end_character) - while line_number < clean_lines.num_lines() and num_open > 0: + while num_open > 0: line_number += 1 + if line_number >= clean_lines.num_lines(): + return ('', len(clean_lines.elided), -1) line = clean_lines.elided[line_number] num_open += line.count(start_character) - line.count(end_character) # OK, now find the end_character that actually got us back to even @@ -1109,17 +1150,85 @@ def is_blank_line(line): return not line or line.isspace() +def detect_functions(clean_lines, line_number, function_state, error): + """Finds where functions start and end. + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + + Args: + clean_lines: A CleansedLines instance containing the file. + line_number: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + # Are we now past the end of a function? + if function_state.ending_line_number + 1 == line_number: + function_state.end() + + # If we're in a function, don't try to detect a new one. + if function_state.in_a_function: + return + + lines = clean_lines.lines + line = lines[line_number] + raw = clean_lines.raw_lines + raw_line = raw[line_number] + + regexp = r'\s*(\w(\w|::|\*|\&|\s|<|>|,|~)*)\(' # decls * & space::name( ... + match_result = match(regexp, line) + if not match_result: + return + + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name != 'TEST' and function_name != 'TEST_F' and match(r'[A-Z_]+$', function_name): + return + + joined_line = '' + for start_line_number in xrange(line_number, clean_lines.num_lines()): + start_line = lines[start_line_number] + joined_line += ' ' + start_line.lstrip() + if search(r'(;|})', start_line): # Declarations and trivial functions + return # ... ignore + + if search(r'{', start_line): + # Replace template constructs with _ so that no spaces remain in the function name, + # while keeping the column numbers of other characters the same as "line". + line_with_no_templates = iteratively_replace_matches_with_char(r'<[^<>]*>', '_', line) + match_function = search(r'((\w|:|<|>|,|~)*)\(', line_with_no_templates) + if not match_function: + return # The '(' must have been inside of a template. + + # Use the column numbers from the modified line to find the + # function name in the original line. + function = line[match_function.start(1):match_function.end(1)] + + if match(r'TEST', function): # Handle TEST... macros + parameter_regexp = search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + open_brace_index = start_line.find('{') + ending_line_number = close_expression(clean_lines, start_line_number, open_brace_index)[1] + function_state.begin(function, start_line_number + 1, ending_line_number) + return + + # No body for the function (or evidence of a non-function) was found. + error(line_number, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + + def check_for_function_lengths(clean_lines, line_number, function_state, error): """Reports for long function bodies. For an overview why this is done, see: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal of vertical space and commments just to get through a lint check. NOLINT *on the last line of a function* disables this check. @@ -1134,47 +1243,38 @@ def check_for_function_lengths(clean_lines, line_number, function_state, error): line = lines[line_number] raw = clean_lines.raw_lines raw_line = raw[line_number] - joined_line = '' - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or (not match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_line_number in xrange(line_number, clean_lines.num_lines()): - start_line = lines[start_line_number] - joined_line += ' ' + start_line.lstrip() - if search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - if search(r'{', start_line): - body_found = True - function = search(r'((\w|:)*)\(', line).group(1) - if match(r'TEST', function): # Handle TEST... macros - parameter_regexp = search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(line_number, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif match(r'^\}\s*$', line): # function end + if function_state.ending_line_number == line_number: # last line if not search(r'\bNOLINT\b', raw_line): function_state.check(error, line_number) - function_state.end() elif not match(r'^\s*$', line): - function_state.count() # Count non-blank/non-comment lines. + function_state.count(line_number) # Count non-blank/non-comment lines. + + +def check_pass_ptr_usage(clean_lines, line_number, function_state, error): + """Check for proper usage of Pass*Ptr. + + Currently this is limited to detecting declarations of Pass*Ptr + variables inside of functions. + + Args: + clean_lines: A CleansedLines instance containing the file. + line_number: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + if not function_state.in_a_function: + return + + lines = clean_lines.lines + line = lines[line_number] + if line_number >= function_state.body_start_line_number: + matched_pass_ptr = match(r'^\s*Pass([A-Z][A-Za-z]*)Ptr<', line) + if matched_pass_ptr: + type_name = 'Pass%sPtr' % matched_pass_ptr.group(1) + error(line_number, 'readability/pass_ptr', 5, + 'Local variables should never be %s (see ' + 'http://webkit.org/coding/RefPtr.html).' % type_name) def check_spacing(file_extension, clean_lines, line_number, error): @@ -2855,9 +2955,11 @@ def process_line(filename, file_extension, """ raw_lines = clean_lines.raw_lines + detect_functions(clean_lines, line, function_state, error) check_for_function_lengths(clean_lines, line, function_state, error) if search(r'\bNOLINT\b', raw_lines[line]): # ignore nolint lines return + check_pass_ptr_usage(clean_lines, line, function_state, error) check_for_multiline_comments_and_strings(clean_lines, line, error) check_style(clean_lines, line, file_extension, class_state, file_state, error) check_language(filename, clean_lines, line, file_extension, include_state, @@ -2942,6 +3044,7 @@ class CppChecker(object): 'readability/multiline_string', 'readability/naming', 'readability/null', + 'readability/pass_ptr', 'readability/streams', 'readability/todo', 'readability/utf8', diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py index 6d5c24b..13b053c 100644 --- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py +++ b/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py @@ -205,10 +205,27 @@ class CppStyleTestBase(unittest.TestCase): cpp_style.remove_multi_line_comments(lines, error_collector) lines = cpp_style.CleansedLines(lines) for i in xrange(lines.num_lines()): + cpp_style.detect_functions(lines, i, + function_state, error_collector) cpp_style.check_for_function_lengths(lines, i, function_state, error_collector) return error_collector.results() + # Similar to perform_function_lengths_check, but calls check_pass_ptr_usage + # instead of check_for_function_lengths. + def perform_pass_ptr_check(self, code): + error_collector = ErrorCollector(self.assert_) + function_state = cpp_style._FunctionState(self.min_confidence) + lines = code.split('\n') + cpp_style.remove_multi_line_comments(lines, error_collector) + lines = cpp_style.CleansedLines(lines) + for i in xrange(lines.num_lines()): + cpp_style.detect_functions(lines, i, + function_state, error_collector) + cpp_style.check_pass_ptr_usage(lines, i, + function_state, error_collector) + return error_collector.results() + def perform_include_what_you_use(self, code, filename='foo.h', io=codecs): # First, build up the include state. error_collector = ErrorCollector(self.assert_) @@ -2432,6 +2449,20 @@ class CheckForFunctionLengthsTest(CppStyleTestBase): def test_function_length_check_definition_above_severity1(self): self.assert_function_length_check_above_error_level(1) + def test_function_length_check_definition_severity1_plus_indented(self): + error_level = 1 + error_lines = self.trigger_lines(error_level) + 1 + trigger_level = self.trigger_lines(self.min_confidence) + indent_spaces = ' ' + self.assert_function_lengths_check( + re.sub(r'(?m)^(.)', indent_spaces + r'\1', + 'void test_indent(int x)\n' + self.function_body(error_lines)), + ('Small and focused functions are preferred: ' + 'test_indent() has %d non-comment lines ' + '(error triggered by exceeding %d lines).' + ' [readability/fn_size] [%d]') + % (error_lines, trigger_level, error_level)) + def test_function_length_check_definition_severity1_plus_blanks(self): error_level = 1 error_lines = self.trigger_lines(error_level) + 1 @@ -2449,11 +2480,11 @@ class CheckForFunctionLengthsTest(CppStyleTestBase): error_lines = self.trigger_lines(error_level) + 1 trigger_level = self.trigger_lines(self.min_confidence) self.assert_function_lengths_check( - ('my_namespace::my_other_namespace::MyVeryLongTypeName*\n' - 'my_namespace::my_other_namespace::MyFunction(int arg1, char* arg2)' + ('my_namespace::my_other_namespace::MyVeryLongTypeName<Type1, bool func(const Element*)>*\n' + 'my_namespace::my_other_namespace<Type3, Type4>::~MyFunction<Type5<Type6, Type7> >(int arg1, char* arg2)' + self.function_body(error_lines)), ('Small and focused functions are preferred: ' - 'my_namespace::my_other_namespace::MyFunction()' + 'my_namespace::my_other_namespace<Type3, Type4>::~MyFunction<Type5<Type6, Type7> >()' ' has %d non-comment lines ' '(error triggered by exceeding %d lines).' ' [readability/fn_size] [%d]') @@ -2484,7 +2515,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase): 'FixGoogleUpdate_AllValues_MachineApp) has %d non-comment lines ' '(error triggered by exceeding %d lines).' ' [readability/fn_size] [%d]') - % (error_lines+1, trigger_level, error_level)) + % (error_lines, trigger_level, error_level)) def test_function_length_check_definition_severity1_for_bad_test_doesnt_break(self): error_level = 1 @@ -2714,6 +2745,65 @@ class NoNonVirtualDestructorsTest(CppStyleTestBase): 'virtual method(s), one declared at line 2. [runtime/virtual] [4]']) +class PassPtrTest(CppStyleTestBase): + # For http://webkit.org/coding/RefPtr.html + + def assert_pass_ptr_check(self, code, expected_message): + """Check warnings for Pass*Ptr are as expected. + + Args: + code: C++ source code expected to generate a warning message. + expected_message: Message expected to be generated by the C++ code. + """ + self.assertEquals(expected_message, + self.perform_pass_ptr_check(code)) + + def test_pass_ref_ptr_in_function(self): + # Local variables should never be PassRefPtr. + self.assert_pass_ptr_check( + 'int myFunction()\n' + '{\n' + ' PassRefPtr<Type1> variable = variable2;\n' + '}', + 'Local variables should never be PassRefPtr (see ' + 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]') + + def test_pass_own_ptr_in_function(self): + # Local variables should never be PassRefPtr. + self.assert_pass_ptr_check( + 'int myFunction()\n' + '{\n' + ' PassOwnPtr<Type1> variable = variable2;\n' + '}', + 'Local variables should never be PassOwnPtr (see ' + 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]') + + def test_pass_other_type_ptr_in_function(self): + # Local variables should never be PassRefPtr. + self.assert_pass_ptr_check( + 'int myFunction()\n' + '{\n' + ' PassOtherTypePtr<Type1> variable;\n' + '}', + 'Local variables should never be PassOtherTypePtr (see ' + 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]') + + def test_pass_ref_ptr_return_value(self): + self.assert_pass_ptr_check( + 'PassRefPtr<Type1>\n' + 'myFunction(int)\n' + '{\n' + '}', + '') + + def test_pass_ref_ptr_parameter_value(self): + self.assert_pass_ptr_check( + 'int myFunction(PassRefPtr<Type1>)\n' + '{\n' + '}', + '') + + class WebKitStyleTest(CppStyleTestBase): # for http://webkit.org/coding/coding-style.html |