Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Side by Side Diff: Tools/Scripts/webkitpy/style/checkers/cpp.py

Issue 15747011: Enforced new rules for braces in conditional and loop bodies in style checker. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebased. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 # 2 #
3 # Copyright (C) 2009, 2010, 2012 Google Inc. All rights reserved. 3 # Copyright (C) 2009, 2010, 2012 Google Inc. All rights reserved.
4 # Copyright (C) 2009 Torch Mobile Inc. 4 # Copyright (C) 2009 Torch Mobile Inc.
5 # Copyright (C) 2009 Apple Inc. All rights reserved. 5 # Copyright (C) 2009 Apple Inc. All rights reserved.
6 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) 6 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
7 # 7 #
8 # Redistribution and use in source and binary forms, with or without 8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are 9 # modification, are permitted provided that the following conditions are
10 # met: 10 # met:
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 while True: 186 while True:
187 matched = search(pattern, s) 187 matched = search(pattern, s)
188 if not matched: 188 if not matched:
189 return s 189 return s
190 start_match_index = matched.start(0) 190 start_match_index = matched.start(0)
191 end_match_index = matched.end(0) 191 end_match_index = matched.end(0)
192 match_length = end_match_index - start_match_index 192 match_length = end_match_index - start_match_index
193 s = s[:start_match_index] + char_replacement * match_length + s[end_matc h_index:] 193 s = s[:start_match_index] + char_replacement * match_length + s[end_matc h_index:]
194 194
195 195
196 def _find_in_lines(regex, lines, start_position, not_found_position):
197 """Does a find starting at start position and going forward until
198 a match is found.
199
200 Returns the position where the regex started.
201 """
202 current_row = start_position.row
203
204 # Start with the given row and trim off everything before what should be mat ched.
205 current_line = lines[start_position.row][start_position.column:]
206 starting_offset = start_position.column
207 while True:
208 found_match = search(regex, current_line)
209 if found_match:
210 return Position(current_row, starting_offset + found_match.start())
211
212 # A match was not found so continue forward.
213 current_row += 1
214 starting_offset = 0
215 if current_row >= len(lines):
216 return not_found_position
217 current_line = lines[current_row]
218
196 def _rfind_in_lines(regex, lines, start_position, not_found_position): 219 def _rfind_in_lines(regex, lines, start_position, not_found_position):
197 """Does a reverse find starting at start position and going backwards until 220 """Does a reverse find starting at start position and going backwards until
198 a match is found. 221 a match is found.
199 222
200 Returns the position where the regex ended. 223 Returns the position where the regex ended.
201 """ 224 """
202 # Put the regex in a group and proceed it with a greedy expression that 225 # Put the regex in a group and proceed it with a greedy expression that
203 # matches anything to ensure that we get the last possible match in a line. 226 # matches anything to ensure that we get the last possible match in a line.
204 last_in_line_regex = r'.*(' + regex + ')' 227 last_in_line_regex = r'.*(' + regex + ')'
205 current_row = start_position.row 228 current_row = start_position.row
(...skipping 2148 matching lines...) Expand 10 before | Expand all | Expand 10 after
2354 and previous_line.find('#') < 0): 2377 and previous_line.find('#') < 0):
2355 error(line_number, 'whitespace/braces', 4, 2378 error(line_number, 'whitespace/braces', 4,
2356 'This { should be at the end of the previous line') 2379 'This { should be at the end of the previous line')
2357 elif (search(r'\)\s*(((const|OVERRIDE)\s*)*\s*)?{\s*$', line) 2380 elif (search(r'\)\s*(((const|OVERRIDE)\s*)*\s*)?{\s*$', line)
2358 and line.count('(') == line.count(')') 2381 and line.count('(') == line.count(')')
2359 and not search(r'\b(if|for|foreach|while|switch)\b', line) 2382 and not search(r'\b(if|for|foreach|while|switch)\b', line)
2360 and not match(r'\s+[A-Z_][A-Z_0-9]+\b', line)): 2383 and not match(r'\s+[A-Z_][A-Z_0-9]+\b', line)):
2361 error(line_number, 'whitespace/braces', 4, 2384 error(line_number, 'whitespace/braces', 4,
2362 'Place brace on its own line for function definitions.') 2385 'Place brace on its own line for function definitions.')
2363 2386
2364 if (match(r'\s*}\s*(else\s*({\s*)?)?$', line) and line_number > 1):
2365 # We check if a closed brace has started a line to see if a
2366 # one line control statement was previous.
2367 previous_line = clean_lines.elided[line_number - 2]
2368 last_open_brace = previous_line.rfind('{')
2369 if (last_open_brace != -1 and previous_line.find('}', last_open_brace) = = -1
2370 and search(r'\b(if|for|foreach|while|else)\b', previous_line)):
2371 error(line_number, 'whitespace/braces', 4,
2372 'One line control clauses should not use braces.')
2373
2374 # An else clause should be on the same line as the preceding closing brace. 2387 # An else clause should be on the same line as the preceding closing brace.
2375 if match(r'\s*else\s*', line): 2388 if match(r'\s*else\s*', line):
2376 previous_line = get_previous_non_blank_line(clean_lines, line_number)[0] 2389 previous_line = get_previous_non_blank_line(clean_lines, line_number)[0]
2377 if match(r'\s*}\s*$', previous_line): 2390 if match(r'\s*}\s*$', previous_line):
2378 error(line_number, 'whitespace/newline', 4, 2391 error(line_number, 'whitespace/newline', 4,
2379 'An else should appear on the same line as the preceding }') 2392 'An else should appear on the same line as the preceding }')
2380 2393
2381 # Likewise, an else should never have the else clause on the same line 2394 # Likewise, an else should never have the else clause on the same line
2382 if search(r'\belse [^\s{]', line) and not search(r'\belse if\b', line): 2395 if search(r'\belse [^\s{]', line) and not search(r'\belse if\b', line):
2383 error(line_number, 'whitespace/newline', 4, 2396 error(line_number, 'whitespace/newline', 4,
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
2624 width = 0 2637 width = 0
2625 for c in unicodedata.normalize('NFC', line): 2638 for c in unicodedata.normalize('NFC', line):
2626 if unicodedata.east_asian_width(c) in ('W', 'F'): 2639 if unicodedata.east_asian_width(c) in ('W', 'F'):
2627 width += 2 2640 width += 2
2628 elif not unicodedata.combining(c): 2641 elif not unicodedata.combining(c):
2629 width += 1 2642 width += 1
2630 return width 2643 return width
2631 return len(line) 2644 return len(line)
2632 2645
2633 2646
2647 def check_conditional_and_loop_bodies_for_brace_violations(clean_lines, line_num ber, error):
2648 """Scans the bodies of conditionals and loops, and in particular
2649 all the arms of conditionals, for violations in the use of braces.
2650
2651 Specifically:
2652
2653 (1) If an arm omits braces, then the following statement must be on one
2654 physical line.
2655 (2) If any arm uses braces, all arms must use them.
2656
2657 These checks are only done here if we find the start of an
2658 'if/for/foreach/while' statement, because this function fails fast
2659 if it encounters constructs it doesn't understand. Checks
2660 elsewhere validate other constraints, such as requiring '}' and
2661 'else' to be on the same line.
2662
2663 Args:
2664 clean_lines: A CleansedLines instance containing the file.
2665 line_number: The number of the line to check.
2666 error: The function to call with any errors found.
2667 """
2668
2669 # We work with the elided lines. Comments have been removed, but line
2670 # numbers are preserved, so we can still find situations where
2671 # single-expression control clauses span multiple lines, or when a
2672 # comment preceded the expression.
2673 lines = clean_lines.elided
2674 line = lines[line_number]
2675
2676 # Match control structures.
2677 control_match = match(r'\s*(if|foreach|for|while)\s*\(', line)
2678 if not control_match:
2679 return
2680
2681 # Found the start of a conditional or loop.
2682
2683 # The following loop handles all potential arms of the control clause.
2684 # The initial conditions are the following:
2685 # - We start on the opening paren '(' of the condition, *unless* we are
2686 # handling an 'else' block, in which case there is no condition.
2687 # - In the latter case, we start at the position just beyond the 'else'
2688 # token.
2689 expect_conditional_expression = True
2690 know_whether_using_braces = False
2691 using_braces = False
2692 search_for_else_clause = control_match.group(1) == "if"
2693 current_pos = Position(line_number, control_match.end() - 1)
2694
2695 while True:
2696 if expect_conditional_expression:
2697 # Try to find the end of the conditional expression,
2698 # potentially spanning multiple lines.
2699 open_paren_pos = current_pos
2700 close_paren_pos = close_expression(lines, open_paren_pos)
2701 if close_paren_pos.column < 0:
2702 return
2703 current_pos = close_paren_pos
2704
2705 end_line_of_conditional = current_pos.row
2706
2707 # Find the start of the body.
2708 current_pos = _find_in_lines(r'\S', lines, current_pos, None)
2709 if not current_pos:
2710 return
2711
2712 current_arm_uses_brace = False
2713 if lines[current_pos.row][current_pos.column] == '{':
2714 current_arm_uses_brace = True
2715 if know_whether_using_braces:
2716 if using_braces != current_arm_uses_brace:
2717 error(current_pos.row, 'whitespace/braces', 4,
2718 'If one part of an if-else statement uses curly braces, th e other part must too.')
2719 return
2720 know_whether_using_braces = True
2721 using_braces = current_arm_uses_brace
2722
2723 if using_braces:
2724 # Skip over the entire arm.
2725 current_pos = close_expression(lines, current_pos)
2726 if current_pos.column < 0:
2727 return
2728 else:
2729 # Skip over the current expression.
2730 current_line_number = current_pos.row
2731 current_pos = _find_in_lines(r';', lines, current_pos, None)
2732 if not current_pos:
2733 return
2734 # If the end of the expression is beyond the line just after
2735 # the close parenthesis or control clause, we've found a
2736 # single-expression arm that spans multiple lines. (We don't
2737 # fire this error for expressions ending on the same line; that
2738 # is a different error, handled elsewhere.)
2739 if current_pos.row > 1 + end_line_of_conditional:
2740 error(current_pos.row, 'whitespace/braces', 4,
2741 'A conditional or loop body must use braces if the stateme nt is more than one line long.')
2742 return
2743 current_pos = Position(current_pos.row, 1 + current_pos.column)
2744
2745 # At this point current_pos points just past the end of the last
2746 # arm. If we just handled the last control clause, we're done.
2747 if not search_for_else_clause:
2748 return
2749
2750 # Scan forward for the next non-whitespace character, and see
2751 # whether we are continuing a conditional (with an 'else' or
2752 # 'else if'), or are done.
2753 current_pos = _find_in_lines(r'\S', lines, current_pos, None)
2754 if not current_pos:
2755 return
2756 next_nonspace_string = lines[current_pos.row][current_pos.column:]
2757 next_conditional = match(r'(else\s*if|else)', next_nonspace_string)
2758 if not next_conditional:
2759 # Done processing this 'if' and all arms.
2760 return
2761 if next_conditional.group(1) == "else if":
2762 current_pos = _find_in_lines(r'\(', lines, current_pos, None)
2763 else:
2764 current_pos.column += 4 # skip 'else'
2765 expect_conditional_expression = False
2766 search_for_else_clause = False
2767 # End while loop
2768
2634 def check_style(clean_lines, line_number, file_extension, class_state, file_stat e, enum_state, error): 2769 def check_style(clean_lines, line_number, file_extension, class_state, file_stat e, enum_state, error):
2635 """Checks rules from the 'C++ style rules' section of cppguide.html. 2770 """Checks rules from the 'C++ style rules' section of cppguide.html.
2636 2771
2637 Most of these rules are hard to test (naming, comment style), but we 2772 Most of these rules are hard to test (naming, comment style), but we
2638 do what we can. In particular we check for 4-space indents, line lengths, 2773 do what we can. In particular we check for 4-space indents, line lengths,
2639 tab usage, spaces inside code, etc. 2774 tab usage, spaces inside code, etc.
2640 2775
2641 Args: 2776 Args:
2642 clean_lines: A CleansedLines instance containing the file. 2777 clean_lines: A CleansedLines instance containing the file.
2643 line_number: The number of the line to check. 2778 line_number: The number of the line to check.
(...skipping 963 matching lines...) Expand 10 before | Expand all | Expand 10 after
3607 check_function_definition(filename, file_extension, clean_lines, line, funct ion_state, error) 3742 check_function_definition(filename, file_extension, clean_lines, line, funct ion_state, error)
3608 check_pass_ptr_usage(clean_lines, line, function_state, error) 3743 check_pass_ptr_usage(clean_lines, line, function_state, error)
3609 check_for_leaky_patterns(clean_lines, line, function_state, error) 3744 check_for_leaky_patterns(clean_lines, line, function_state, error)
3610 check_for_multiline_comments_and_strings(clean_lines, line, error) 3745 check_for_multiline_comments_and_strings(clean_lines, line, error)
3611 check_style(clean_lines, line, file_extension, class_state, file_state, enum _state, error) 3746 check_style(clean_lines, line, file_extension, class_state, file_state, enum _state, error)
3612 check_language(filename, clean_lines, line, file_extension, include_state, 3747 check_language(filename, clean_lines, line, file_extension, include_state,
3613 file_state, error) 3748 file_state, error)
3614 check_for_non_standard_constructs(clean_lines, line, class_state, error) 3749 check_for_non_standard_constructs(clean_lines, line, class_state, error)
3615 check_posix_threading(clean_lines, line, error) 3750 check_posix_threading(clean_lines, line, error)
3616 check_invalid_increment(clean_lines, line, error) 3751 check_invalid_increment(clean_lines, line, error)
3617 3752 check_conditional_and_loop_bodies_for_brace_violations(clean_lines, line, er ror)
3618 3753
3619 def _process_lines(filename, file_extension, lines, error, min_confidence): 3754 def _process_lines(filename, file_extension, lines, error, min_confidence):
3620 """Performs lint checks and reports any errors to the given error function. 3755 """Performs lint checks and reports any errors to the given error function.
3621 3756
3622 Args: 3757 Args:
3623 filename: Filename of the file that is being processed. 3758 filename: Filename of the file that is being processed.
3624 file_extension: The extension (dot not included) of the file. 3759 file_extension: The extension (dot not included) of the file.
3625 lines: An array of strings, each representing a line of the file, with the 3760 lines: An array of strings, each representing a line of the file, with the
3626 last element being empty if the file is termined with a newline. 3761 last element being empty if the file is termined with a newline.
3627 error: A callable to which errors are reported, which takes 4 arguments: 3762 error: A callable to which errors are reported, which takes 4 arguments:
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
3776 self.handle_style_error, self.min_confidence) 3911 self.handle_style_error, self.min_confidence)
3777 3912
3778 3913
3779 # FIXME: Remove this function (requires refactoring unit tests). 3914 # FIXME: Remove this function (requires refactoring unit tests).
3780 def process_file_data(filename, file_extension, lines, error, min_confidence, un it_test_config): 3915 def process_file_data(filename, file_extension, lines, error, min_confidence, un it_test_config):
3781 global _unit_test_config 3916 global _unit_test_config
3782 _unit_test_config = unit_test_config 3917 _unit_test_config = unit_test_config
3783 checker = CppChecker(filename, file_extension, error, min_confidence) 3918 checker = CppChecker(filename, file_extension, error, min_confidence)
3784 checker.check(lines) 3919 checker.check(lines)
3785 _unit_test_config = {} 3920 _unit_test_config = {}
OLDNEW
« no previous file with comments | « no previous file | Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698