summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/style
diff options
context:
space:
mode:
authorRussell Brenner <russellbrenner@google.com>2010-11-18 17:33:13 -0800
committerRussell Brenner <russellbrenner@google.com>2010-12-02 13:47:21 -0800
commit6b70adc33054f8aee8c54d0f460458a9df11b8a5 (patch)
tree103a13998c33944d6ab3b8318c509a037e639460 /WebKitTools/Scripts/webkitpy/style
parentbdf4ebc8e70b2d221b6ee7a65660918ecb1d33aa (diff)
downloadexternal_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.py16
-rwxr-xr-xWebKitTools/Scripts/webkitpy/style/checker_unittest.py27
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp.py205
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py98
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