| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 = {} |
| OLD | NEW |