@@ 2514-2885 (lines=372) @@ | ||
2511 | self.seen_else = False |
|
2512 | ||
2513 | ||
2514 | class NestingState(object): |
|
2515 | """Holds states related to parsing braces.""" |
|
2516 | ||
2517 | def __init__(self): |
|
2518 | # Stack for tracking all braces. An object is pushed whenever we |
|
2519 | # see a "{", and popped when we see a "}". Only 3 types of |
|
2520 | # objects are possible: |
|
2521 | # - _ClassInfo: a class or struct. |
|
2522 | # - _NamespaceInfo: a namespace. |
|
2523 | # - _BlockInfo: some other type of block. |
|
2524 | self.stack = [] |
|
2525 | ||
2526 | # Top of the previous stack before each Update(). |
|
2527 | # |
|
2528 | # Because the nesting_stack is updated at the end of each line, we |
|
2529 | # had to do some convoluted checks to find out what is the current |
|
2530 | # scope at the beginning of the line. This check is simplified by |
|
2531 | # saving the previous top of nesting stack. |
|
2532 | # |
|
2533 | # We could save the full stack, but we only need the top. Copying |
|
2534 | # the full nesting stack would slow down cpplint by ~10%. |
|
2535 | self.previous_stack_top = [] |
|
2536 | ||
2537 | # Stack of _PreprocessorInfo objects. |
|
2538 | self.pp_stack = [] |
|
2539 | ||
2540 | def SeenOpenBrace(self): |
|
2541 | """Check if we have seen the opening brace for the innermost block. |
|
2542 | ||
2543 | Returns: |
|
2544 | True if we have seen the opening brace, False if the innermost |
|
2545 | block is still expecting an opening brace. |
|
2546 | """ |
|
2547 | return (not self.stack) or self.stack[-1].seen_open_brace |
|
2548 | ||
2549 | def InNamespaceBody(self): |
|
2550 | """Check if we are currently one level inside a namespace body. |
|
2551 | ||
2552 | Returns: |
|
2553 | True if top of the stack is a namespace block, False otherwise. |
|
2554 | """ |
|
2555 | return self.stack and isinstance(self.stack[-1], _NamespaceInfo) |
|
2556 | ||
2557 | def InExternC(self): |
|
2558 | """Check if we are currently one level inside an 'extern "C"' block. |
|
2559 | ||
2560 | Returns: |
|
2561 | True if top of the stack is an extern block, False otherwise. |
|
2562 | """ |
|
2563 | return self.stack and isinstance(self.stack[-1], _ExternCInfo) |
|
2564 | ||
2565 | def InClassDeclaration(self): |
|
2566 | """Check if we are currently one level inside a class or struct declaration. |
|
2567 | ||
2568 | Returns: |
|
2569 | True if top of the stack is a class/struct, False otherwise. |
|
2570 | """ |
|
2571 | return self.stack and isinstance(self.stack[-1], _ClassInfo) |
|
2572 | ||
2573 | def InAsmBlock(self): |
|
2574 | """Check if we are currently one level inside an inline ASM block. |
|
2575 | ||
2576 | Returns: |
|
2577 | True if the top of the stack is a block containing inline ASM. |
|
2578 | """ |
|
2579 | return self.stack and self.stack[-1].inline_asm != _NO_ASM |
|
2580 | ||
2581 | def InTemplateArgumentList(self, clean_lines, linenum, pos): |
|
2582 | """Check if current position is inside template argument list. |
|
2583 | ||
2584 | Args: |
|
2585 | clean_lines: A CleansedLines instance containing the file. |
|
2586 | linenum: The number of the line to check. |
|
2587 | pos: position just after the suspected template argument. |
|
2588 | Returns: |
|
2589 | True if (linenum, pos) is inside template arguments. |
|
2590 | """ |
|
2591 | while linenum < clean_lines.NumLines(): |
|
2592 | # Find the earliest character that might indicate a template argument |
|
2593 | line = clean_lines.elided[linenum] |
|
2594 | match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) |
|
2595 | if not match: |
|
2596 | linenum += 1 |
|
2597 | pos = 0 |
|
2598 | continue |
|
2599 | token = match.group(1) |
|
2600 | pos += len(match.group(0)) |
|
2601 | ||
2602 | # These things do not look like template argument list: |
|
2603 | # class Suspect { |
|
2604 | # class Suspect x; } |
|
2605 | if token in ('{', '}', ';'): return False |
|
2606 | ||
2607 | # These things look like template argument list: |
|
2608 | # template <class Suspect> |
|
2609 | # template <class Suspect = default_value> |
|
2610 | # template <class Suspect[]> |
|
2611 | # template <class Suspect...> |
|
2612 | if token in ('>', '=', '[', ']', '.'): return True |
|
2613 | ||
2614 | # Check if token is an unmatched '<'. |
|
2615 | # If not, move on to the next character. |
|
2616 | if token != '<': |
|
2617 | pos += 1 |
|
2618 | if pos >= len(line): |
|
2619 | linenum += 1 |
|
2620 | pos = 0 |
|
2621 | continue |
|
2622 | ||
2623 | # We can't be sure if we just find a single '<', and need to |
|
2624 | # find the matching '>'. |
|
2625 | (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) |
|
2626 | if end_pos < 0: |
|
2627 | # Not sure if template argument list or syntax error in file |
|
2628 | return False |
|
2629 | linenum = end_line |
|
2630 | pos = end_pos |
|
2631 | return False |
|
2632 | ||
2633 | def UpdatePreprocessor(self, line): |
|
2634 | """Update preprocessor stack. |
|
2635 | ||
2636 | We need to handle preprocessors due to classes like this: |
|
2637 | #ifdef SWIG |
|
2638 | struct ResultDetailsPageElementExtensionPoint { |
|
2639 | #else |
|
2640 | struct ResultDetailsPageElementExtensionPoint : public Extension { |
|
2641 | #endif |
|
2642 | ||
2643 | We make the following assumptions (good enough for most files): |
|
2644 | - Preprocessor condition evaluates to true from #if up to first |
|
2645 | #else/#elif/#endif. |
|
2646 | ||
2647 | - Preprocessor condition evaluates to false from #else/#elif up |
|
2648 | to #endif. We still perform lint checks on these lines, but |
|
2649 | these do not affect nesting stack. |
|
2650 | ||
2651 | Args: |
|
2652 | line: current line to check. |
|
2653 | """ |
|
2654 | if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): |
|
2655 | # Beginning of #if block, save the nesting stack here. The saved |
|
2656 | # stack will allow us to restore the parsing state in the #else case. |
|
2657 | self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) |
|
2658 | elif Match(r'^\s*#\s*(else|elif)\b', line): |
|
2659 | # Beginning of #else block |
|
2660 | if self.pp_stack: |
|
2661 | if not self.pp_stack[-1].seen_else: |
|
2662 | # This is the first #else or #elif block. Remember the |
|
2663 | # whole nesting stack up to this point. This is what we |
|
2664 | # keep after the #endif. |
|
2665 | self.pp_stack[-1].seen_else = True |
|
2666 | self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) |
|
2667 | ||
2668 | # Restore the stack to how it was before the #if |
|
2669 | self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) |
|
2670 | else: |
|
2671 | # TODO(unknown): unexpected #else, issue warning? |
|
2672 | pass |
|
2673 | elif Match(r'^\s*#\s*endif\b', line): |
|
2674 | # End of #if or #else blocks. |
|
2675 | if self.pp_stack: |
|
2676 | # If we saw an #else, we will need to restore the nesting |
|
2677 | # stack to its former state before the #else, otherwise we |
|
2678 | # will just continue from where we left off. |
|
2679 | if self.pp_stack[-1].seen_else: |
|
2680 | # Here we can just use a shallow copy since we are the last |
|
2681 | # reference to it. |
|
2682 | self.stack = self.pp_stack[-1].stack_before_else |
|
2683 | # Drop the corresponding #if |
|
2684 | self.pp_stack.pop() |
|
2685 | else: |
|
2686 | # TODO(unknown): unexpected #endif, issue warning? |
|
2687 | pass |
|
2688 | ||
2689 | # TODO(unknown): Update() is too long, but we will refactor later. |
|
2690 | def Update(self, filename, clean_lines, linenum, error): |
|
2691 | """Update nesting state with current line. |
|
2692 | ||
2693 | Args: |
|
2694 | filename: The name of the current file. |
|
2695 | clean_lines: A CleansedLines instance containing the file. |
|
2696 | linenum: The number of the line to check. |
|
2697 | error: The function to call with any errors found. |
|
2698 | """ |
|
2699 | line = clean_lines.elided[linenum] |
|
2700 | ||
2701 | # Remember top of the previous nesting stack. |
|
2702 | # |
|
2703 | # The stack is always pushed/popped and not modified in place, so |
|
2704 | # we can just do a shallow copy instead of copy.deepcopy. Using |
|
2705 | # deepcopy would slow down cpplint by ~28%. |
|
2706 | if self.stack: |
|
2707 | self.previous_stack_top = self.stack[-1] |
|
2708 | else: |
|
2709 | self.previous_stack_top = None |
|
2710 | ||
2711 | # Update pp_stack |
|
2712 | self.UpdatePreprocessor(line) |
|
2713 | ||
2714 | # Count parentheses. This is to avoid adding struct arguments to |
|
2715 | # the nesting stack. |
|
2716 | if self.stack: |
|
2717 | inner_block = self.stack[-1] |
|
2718 | depth_change = line.count('(') - line.count(')') |
|
2719 | inner_block.open_parentheses += depth_change |
|
2720 | ||
2721 | # Also check if we are starting or ending an inline assembly block. |
|
2722 | if inner_block.inline_asm in (_NO_ASM, _END_ASM): |
|
2723 | if (depth_change != 0 and |
|
2724 | inner_block.open_parentheses == 1 and |
|
2725 | _MATCH_ASM.match(line)): |
|
2726 | # Enter assembly block |
|
2727 | inner_block.inline_asm = _INSIDE_ASM |
|
2728 | else: |
|
2729 | # Not entering assembly block. If previous line was _END_ASM, |
|
2730 | # we will now shift to _NO_ASM state. |
|
2731 | inner_block.inline_asm = _NO_ASM |
|
2732 | elif (inner_block.inline_asm == _INSIDE_ASM and |
|
2733 | inner_block.open_parentheses == 0): |
|
2734 | # Exit assembly block |
|
2735 | inner_block.inline_asm = _END_ASM |
|
2736 | ||
2737 | # Consume namespace declaration at the beginning of the line. Do |
|
2738 | # this in a loop so that we catch same line declarations like this: |
|
2739 | # namespace proto2 { namespace bridge { class MessageSet; } } |
|
2740 | while True: |
|
2741 | # Match start of namespace. The "\b\s*" below catches namespace |
|
2742 | # declarations even if it weren't followed by a whitespace, this |
|
2743 | # is so that we don't confuse our namespace checker. The |
|
2744 | # missing spaces will be flagged by CheckSpacing. |
|
2745 | namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) |
|
2746 | if not namespace_decl_match: |
|
2747 | break |
|
2748 | ||
2749 | new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) |
|
2750 | self.stack.append(new_namespace) |
|
2751 | ||
2752 | line = namespace_decl_match.group(2) |
|
2753 | if line.find('{') != -1: |
|
2754 | new_namespace.seen_open_brace = True |
|
2755 | line = line[line.find('{') + 1:] |
|
2756 | ||
2757 | # Look for a class declaration in whatever is left of the line |
|
2758 | # after parsing namespaces. The regexp accounts for decorated classes |
|
2759 | # such as in: |
|
2760 | # class LOCKABLE API Object { |
|
2761 | # }; |
|
2762 | class_decl_match = Match( |
|
2763 | r'^(\s*(?:template\s*<[\w\s<>,:=]*>\s*)?' |
|
2764 | r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' |
|
2765 | r'(.*)$', line) |
|
2766 | if (class_decl_match and |
|
2767 | (not self.stack or self.stack[-1].open_parentheses == 0)): |
|
2768 | # We do not want to accept classes that are actually template arguments: |
|
2769 | # template <class Ignore1, |
|
2770 | # class Ignore2 = Default<Args>, |
|
2771 | # template <Args> class Ignore3> |
|
2772 | # void Function() {}; |
|
2773 | # |
|
2774 | # To avoid template argument cases, we scan forward and look for |
|
2775 | # an unmatched '>'. If we see one, assume we are inside a |
|
2776 | # template argument list. |
|
2777 | end_declaration = len(class_decl_match.group(1)) |
|
2778 | if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): |
|
2779 | self.stack.append(_ClassInfo( |
|
2780 | class_decl_match.group(3), class_decl_match.group(2), |
|
2781 | clean_lines, linenum)) |
|
2782 | line = class_decl_match.group(4) |
|
2783 | ||
2784 | # If we have not yet seen the opening brace for the innermost block, |
|
2785 | # run checks here. |
|
2786 | if not self.SeenOpenBrace(): |
|
2787 | self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) |
|
2788 | ||
2789 | # Update access control if we are inside a class/struct |
|
2790 | if self.stack and isinstance(self.stack[-1], _ClassInfo): |
|
2791 | classinfo = self.stack[-1] |
|
2792 | access_match = Match( |
|
2793 | r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' |
|
2794 | r':(?:[^:]|$)', |
|
2795 | line) |
|
2796 | if access_match: |
|
2797 | classinfo.access = access_match.group(2) |
|
2798 | ||
2799 | # Check that access keywords are indented +1 space. Skip this |
|
2800 | # check if the keywords are not preceded by whitespaces. |
|
2801 | indent = access_match.group(1) |
|
2802 | if (len(indent) != classinfo.class_indent + 1 and |
|
2803 | Match(r'^\s*$', indent)): |
|
2804 | if classinfo.is_struct: |
|
2805 | parent = 'struct ' + classinfo.name |
|
2806 | else: |
|
2807 | parent = 'class ' + classinfo.name |
|
2808 | slots = '' |
|
2809 | if access_match.group(3): |
|
2810 | slots = access_match.group(3) |
|
2811 | error(filename, linenum, 'whitespace/indent', 3, |
|
2812 | '%s%s: should be indented +1 space inside %s' % ( |
|
2813 | access_match.group(2), slots, parent)) |
|
2814 | ||
2815 | # Consume braces or semicolons from what's left of the line |
|
2816 | while True: |
|
2817 | # Match first brace, semicolon, or closed parenthesis. |
|
2818 | matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) |
|
2819 | if not matched: |
|
2820 | break |
|
2821 | ||
2822 | token = matched.group(1) |
|
2823 | if token == '{': |
|
2824 | # If namespace or class hasn't seen a opening brace yet, mark |
|
2825 | # namespace/class head as complete. Push a new block onto the |
|
2826 | # stack otherwise. |
|
2827 | if not self.SeenOpenBrace(): |
|
2828 | self.stack[-1].seen_open_brace = True |
|
2829 | elif Match(r'^extern\s*"[^"]*"\s*\{', line): |
|
2830 | self.stack.append(_ExternCInfo(linenum)) |
|
2831 | else: |
|
2832 | self.stack.append(_BlockInfo(linenum, True)) |
|
2833 | if _MATCH_ASM.match(line): |
|
2834 | self.stack[-1].inline_asm = _BLOCK_ASM |
|
2835 | ||
2836 | elif token == ';' or token == ')': |
|
2837 | # If we haven't seen an opening brace yet, but we already saw |
|
2838 | # a semicolon, this is probably a forward declaration. Pop |
|
2839 | # the stack for these. |
|
2840 | # |
|
2841 | # Similarly, if we haven't seen an opening brace yet, but we |
|
2842 | # already saw a closing parenthesis, then these are probably |
|
2843 | # function arguments with extra "class" or "struct" keywords. |
|
2844 | # Also pop these stack for these. |
|
2845 | if not self.SeenOpenBrace(): |
|
2846 | self.stack.pop() |
|
2847 | else: # token == '}' |
|
2848 | # Perform end of block checks and pop the stack. |
|
2849 | if self.stack: |
|
2850 | self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) |
|
2851 | self.stack.pop() |
|
2852 | line = matched.group(2) |
|
2853 | ||
2854 | def InnermostClass(self): |
|
2855 | """Get class info on the top of the stack. |
|
2856 | ||
2857 | Returns: |
|
2858 | A _ClassInfo object if we are inside a class, or None otherwise. |
|
2859 | """ |
|
2860 | for i in range(len(self.stack), 0, -1): |
|
2861 | classinfo = self.stack[i - 1] |
|
2862 | if isinstance(classinfo, _ClassInfo): |
|
2863 | return classinfo |
|
2864 | return None |
|
2865 | ||
2866 | def CheckCompletedBlocks(self, filename, error): |
|
2867 | """Checks that all classes and namespaces have been completely parsed. |
|
2868 | ||
2869 | Call this when all lines in a file have been processed. |
|
2870 | Args: |
|
2871 | filename: The name of the current file. |
|
2872 | error: The function to call with any errors found. |
|
2873 | """ |
|
2874 | # Note: This test can result in false positives if #ifdef constructs |
|
2875 | # get in the way of brace matching. See the testBuildClass test in |
|
2876 | # cpplint_unittest.py for an example of this. |
|
2877 | for obj in self.stack: |
|
2878 | if isinstance(obj, _ClassInfo): |
|
2879 | error(filename, obj.starting_linenum, 'build/class', 5, |
|
2880 | 'Failed to find complete declaration of class %s' % |
|
2881 | obj.name) |
|
2882 | elif isinstance(obj, _NamespaceInfo): |
|
2883 | error(filename, obj.starting_linenum, 'build/namespaces', 5, |
|
2884 | 'Failed to find complete declaration of namespace %s' % |
|
2885 | obj.name) |
|
2886 | ||
2887 | ||
2888 | def CheckForNonStandardConstructs(filename, clean_lines, linenum, |
@@ 2514-2885 (lines=372) @@ | ||
2511 | self.seen_else = False |
|
2512 | ||
2513 | ||
2514 | class NestingState(object): |
|
2515 | """Holds states related to parsing braces.""" |
|
2516 | ||
2517 | def __init__(self): |
|
2518 | # Stack for tracking all braces. An object is pushed whenever we |
|
2519 | # see a "{", and popped when we see a "}". Only 3 types of |
|
2520 | # objects are possible: |
|
2521 | # - _ClassInfo: a class or struct. |
|
2522 | # - _NamespaceInfo: a namespace. |
|
2523 | # - _BlockInfo: some other type of block. |
|
2524 | self.stack = [] |
|
2525 | ||
2526 | # Top of the previous stack before each Update(). |
|
2527 | # |
|
2528 | # Because the nesting_stack is updated at the end of each line, we |
|
2529 | # had to do some convoluted checks to find out what is the current |
|
2530 | # scope at the beginning of the line. This check is simplified by |
|
2531 | # saving the previous top of nesting stack. |
|
2532 | # |
|
2533 | # We could save the full stack, but we only need the top. Copying |
|
2534 | # the full nesting stack would slow down cpplint by ~10%. |
|
2535 | self.previous_stack_top = [] |
|
2536 | ||
2537 | # Stack of _PreprocessorInfo objects. |
|
2538 | self.pp_stack = [] |
|
2539 | ||
2540 | def SeenOpenBrace(self): |
|
2541 | """Check if we have seen the opening brace for the innermost block. |
|
2542 | ||
2543 | Returns: |
|
2544 | True if we have seen the opening brace, False if the innermost |
|
2545 | block is still expecting an opening brace. |
|
2546 | """ |
|
2547 | return (not self.stack) or self.stack[-1].seen_open_brace |
|
2548 | ||
2549 | def InNamespaceBody(self): |
|
2550 | """Check if we are currently one level inside a namespace body. |
|
2551 | ||
2552 | Returns: |
|
2553 | True if top of the stack is a namespace block, False otherwise. |
|
2554 | """ |
|
2555 | return self.stack and isinstance(self.stack[-1], _NamespaceInfo) |
|
2556 | ||
2557 | def InExternC(self): |
|
2558 | """Check if we are currently one level inside an 'extern "C"' block. |
|
2559 | ||
2560 | Returns: |
|
2561 | True if top of the stack is an extern block, False otherwise. |
|
2562 | """ |
|
2563 | return self.stack and isinstance(self.stack[-1], _ExternCInfo) |
|
2564 | ||
2565 | def InClassDeclaration(self): |
|
2566 | """Check if we are currently one level inside a class or struct declaration. |
|
2567 | ||
2568 | Returns: |
|
2569 | True if top of the stack is a class/struct, False otherwise. |
|
2570 | """ |
|
2571 | return self.stack and isinstance(self.stack[-1], _ClassInfo) |
|
2572 | ||
2573 | def InAsmBlock(self): |
|
2574 | """Check if we are currently one level inside an inline ASM block. |
|
2575 | ||
2576 | Returns: |
|
2577 | True if the top of the stack is a block containing inline ASM. |
|
2578 | """ |
|
2579 | return self.stack and self.stack[-1].inline_asm != _NO_ASM |
|
2580 | ||
2581 | def InTemplateArgumentList(self, clean_lines, linenum, pos): |
|
2582 | """Check if current position is inside template argument list. |
|
2583 | ||
2584 | Args: |
|
2585 | clean_lines: A CleansedLines instance containing the file. |
|
2586 | linenum: The number of the line to check. |
|
2587 | pos: position just after the suspected template argument. |
|
2588 | Returns: |
|
2589 | True if (linenum, pos) is inside template arguments. |
|
2590 | """ |
|
2591 | while linenum < clean_lines.NumLines(): |
|
2592 | # Find the earliest character that might indicate a template argument |
|
2593 | line = clean_lines.elided[linenum] |
|
2594 | match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) |
|
2595 | if not match: |
|
2596 | linenum += 1 |
|
2597 | pos = 0 |
|
2598 | continue |
|
2599 | token = match.group(1) |
|
2600 | pos += len(match.group(0)) |
|
2601 | ||
2602 | # These things do not look like template argument list: |
|
2603 | # class Suspect { |
|
2604 | # class Suspect x; } |
|
2605 | if token in ('{', '}', ';'): return False |
|
2606 | ||
2607 | # These things look like template argument list: |
|
2608 | # template <class Suspect> |
|
2609 | # template <class Suspect = default_value> |
|
2610 | # template <class Suspect[]> |
|
2611 | # template <class Suspect...> |
|
2612 | if token in ('>', '=', '[', ']', '.'): return True |
|
2613 | ||
2614 | # Check if token is an unmatched '<'. |
|
2615 | # If not, move on to the next character. |
|
2616 | if token != '<': |
|
2617 | pos += 1 |
|
2618 | if pos >= len(line): |
|
2619 | linenum += 1 |
|
2620 | pos = 0 |
|
2621 | continue |
|
2622 | ||
2623 | # We can't be sure if we just find a single '<', and need to |
|
2624 | # find the matching '>'. |
|
2625 | (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) |
|
2626 | if end_pos < 0: |
|
2627 | # Not sure if template argument list or syntax error in file |
|
2628 | return False |
|
2629 | linenum = end_line |
|
2630 | pos = end_pos |
|
2631 | return False |
|
2632 | ||
2633 | def UpdatePreprocessor(self, line): |
|
2634 | """Update preprocessor stack. |
|
2635 | ||
2636 | We need to handle preprocessors due to classes like this: |
|
2637 | #ifdef SWIG |
|
2638 | struct ResultDetailsPageElementExtensionPoint { |
|
2639 | #else |
|
2640 | struct ResultDetailsPageElementExtensionPoint : public Extension { |
|
2641 | #endif |
|
2642 | ||
2643 | We make the following assumptions (good enough for most files): |
|
2644 | - Preprocessor condition evaluates to true from #if up to first |
|
2645 | #else/#elif/#endif. |
|
2646 | ||
2647 | - Preprocessor condition evaluates to false from #else/#elif up |
|
2648 | to #endif. We still perform lint checks on these lines, but |
|
2649 | these do not affect nesting stack. |
|
2650 | ||
2651 | Args: |
|
2652 | line: current line to check. |
|
2653 | """ |
|
2654 | if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): |
|
2655 | # Beginning of #if block, save the nesting stack here. The saved |
|
2656 | # stack will allow us to restore the parsing state in the #else case. |
|
2657 | self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) |
|
2658 | elif Match(r'^\s*#\s*(else|elif)\b', line): |
|
2659 | # Beginning of #else block |
|
2660 | if self.pp_stack: |
|
2661 | if not self.pp_stack[-1].seen_else: |
|
2662 | # This is the first #else or #elif block. Remember the |
|
2663 | # whole nesting stack up to this point. This is what we |
|
2664 | # keep after the #endif. |
|
2665 | self.pp_stack[-1].seen_else = True |
|
2666 | self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) |
|
2667 | ||
2668 | # Restore the stack to how it was before the #if |
|
2669 | self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) |
|
2670 | else: |
|
2671 | # TODO(unknown): unexpected #else, issue warning? |
|
2672 | pass |
|
2673 | elif Match(r'^\s*#\s*endif\b', line): |
|
2674 | # End of #if or #else blocks. |
|
2675 | if self.pp_stack: |
|
2676 | # If we saw an #else, we will need to restore the nesting |
|
2677 | # stack to its former state before the #else, otherwise we |
|
2678 | # will just continue from where we left off. |
|
2679 | if self.pp_stack[-1].seen_else: |
|
2680 | # Here we can just use a shallow copy since we are the last |
|
2681 | # reference to it. |
|
2682 | self.stack = self.pp_stack[-1].stack_before_else |
|
2683 | # Drop the corresponding #if |
|
2684 | self.pp_stack.pop() |
|
2685 | else: |
|
2686 | # TODO(unknown): unexpected #endif, issue warning? |
|
2687 | pass |
|
2688 | ||
2689 | # TODO(unknown): Update() is too long, but we will refactor later. |
|
2690 | def Update(self, filename, clean_lines, linenum, error): |
|
2691 | """Update nesting state with current line. |
|
2692 | ||
2693 | Args: |
|
2694 | filename: The name of the current file. |
|
2695 | clean_lines: A CleansedLines instance containing the file. |
|
2696 | linenum: The number of the line to check. |
|
2697 | error: The function to call with any errors found. |
|
2698 | """ |
|
2699 | line = clean_lines.elided[linenum] |
|
2700 | ||
2701 | # Remember top of the previous nesting stack. |
|
2702 | # |
|
2703 | # The stack is always pushed/popped and not modified in place, so |
|
2704 | # we can just do a shallow copy instead of copy.deepcopy. Using |
|
2705 | # deepcopy would slow down cpplint by ~28%. |
|
2706 | if self.stack: |
|
2707 | self.previous_stack_top = self.stack[-1] |
|
2708 | else: |
|
2709 | self.previous_stack_top = None |
|
2710 | ||
2711 | # Update pp_stack |
|
2712 | self.UpdatePreprocessor(line) |
|
2713 | ||
2714 | # Count parentheses. This is to avoid adding struct arguments to |
|
2715 | # the nesting stack. |
|
2716 | if self.stack: |
|
2717 | inner_block = self.stack[-1] |
|
2718 | depth_change = line.count('(') - line.count(')') |
|
2719 | inner_block.open_parentheses += depth_change |
|
2720 | ||
2721 | # Also check if we are starting or ending an inline assembly block. |
|
2722 | if inner_block.inline_asm in (_NO_ASM, _END_ASM): |
|
2723 | if (depth_change != 0 and |
|
2724 | inner_block.open_parentheses == 1 and |
|
2725 | _MATCH_ASM.match(line)): |
|
2726 | # Enter assembly block |
|
2727 | inner_block.inline_asm = _INSIDE_ASM |
|
2728 | else: |
|
2729 | # Not entering assembly block. If previous line was _END_ASM, |
|
2730 | # we will now shift to _NO_ASM state. |
|
2731 | inner_block.inline_asm = _NO_ASM |
|
2732 | elif (inner_block.inline_asm == _INSIDE_ASM and |
|
2733 | inner_block.open_parentheses == 0): |
|
2734 | # Exit assembly block |
|
2735 | inner_block.inline_asm = _END_ASM |
|
2736 | ||
2737 | # Consume namespace declaration at the beginning of the line. Do |
|
2738 | # this in a loop so that we catch same line declarations like this: |
|
2739 | # namespace proto2 { namespace bridge { class MessageSet; } } |
|
2740 | while True: |
|
2741 | # Match start of namespace. The "\b\s*" below catches namespace |
|
2742 | # declarations even if it weren't followed by a whitespace, this |
|
2743 | # is so that we don't confuse our namespace checker. The |
|
2744 | # missing spaces will be flagged by CheckSpacing. |
|
2745 | namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) |
|
2746 | if not namespace_decl_match: |
|
2747 | break |
|
2748 | ||
2749 | new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) |
|
2750 | self.stack.append(new_namespace) |
|
2751 | ||
2752 | line = namespace_decl_match.group(2) |
|
2753 | if line.find('{') != -1: |
|
2754 | new_namespace.seen_open_brace = True |
|
2755 | line = line[line.find('{') + 1:] |
|
2756 | ||
2757 | # Look for a class declaration in whatever is left of the line |
|
2758 | # after parsing namespaces. The regexp accounts for decorated classes |
|
2759 | # such as in: |
|
2760 | # class LOCKABLE API Object { |
|
2761 | # }; |
|
2762 | class_decl_match = Match( |
|
2763 | r'^(\s*(?:template\s*<[\w\s<>,:=]*>\s*)?' |
|
2764 | r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' |
|
2765 | r'(.*)$', line) |
|
2766 | if (class_decl_match and |
|
2767 | (not self.stack or self.stack[-1].open_parentheses == 0)): |
|
2768 | # We do not want to accept classes that are actually template arguments: |
|
2769 | # template <class Ignore1, |
|
2770 | # class Ignore2 = Default<Args>, |
|
2771 | # template <Args> class Ignore3> |
|
2772 | # void Function() {}; |
|
2773 | # |
|
2774 | # To avoid template argument cases, we scan forward and look for |
|
2775 | # an unmatched '>'. If we see one, assume we are inside a |
|
2776 | # template argument list. |
|
2777 | end_declaration = len(class_decl_match.group(1)) |
|
2778 | if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): |
|
2779 | self.stack.append(_ClassInfo( |
|
2780 | class_decl_match.group(3), class_decl_match.group(2), |
|
2781 | clean_lines, linenum)) |
|
2782 | line = class_decl_match.group(4) |
|
2783 | ||
2784 | # If we have not yet seen the opening brace for the innermost block, |
|
2785 | # run checks here. |
|
2786 | if not self.SeenOpenBrace(): |
|
2787 | self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) |
|
2788 | ||
2789 | # Update access control if we are inside a class/struct |
|
2790 | if self.stack and isinstance(self.stack[-1], _ClassInfo): |
|
2791 | classinfo = self.stack[-1] |
|
2792 | access_match = Match( |
|
2793 | r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' |
|
2794 | r':(?:[^:]|$)', |
|
2795 | line) |
|
2796 | if access_match: |
|
2797 | classinfo.access = access_match.group(2) |
|
2798 | ||
2799 | # Check that access keywords are indented +1 space. Skip this |
|
2800 | # check if the keywords are not preceded by whitespaces. |
|
2801 | indent = access_match.group(1) |
|
2802 | if (len(indent) != classinfo.class_indent + 1 and |
|
2803 | Match(r'^\s*$', indent)): |
|
2804 | if classinfo.is_struct: |
|
2805 | parent = 'struct ' + classinfo.name |
|
2806 | else: |
|
2807 | parent = 'class ' + classinfo.name |
|
2808 | slots = '' |
|
2809 | if access_match.group(3): |
|
2810 | slots = access_match.group(3) |
|
2811 | error(filename, linenum, 'whitespace/indent', 3, |
|
2812 | '%s%s: should be indented +1 space inside %s' % ( |
|
2813 | access_match.group(2), slots, parent)) |
|
2814 | ||
2815 | # Consume braces or semicolons from what's left of the line |
|
2816 | while True: |
|
2817 | # Match first brace, semicolon, or closed parenthesis. |
|
2818 | matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) |
|
2819 | if not matched: |
|
2820 | break |
|
2821 | ||
2822 | token = matched.group(1) |
|
2823 | if token == '{': |
|
2824 | # If namespace or class hasn't seen a opening brace yet, mark |
|
2825 | # namespace/class head as complete. Push a new block onto the |
|
2826 | # stack otherwise. |
|
2827 | if not self.SeenOpenBrace(): |
|
2828 | self.stack[-1].seen_open_brace = True |
|
2829 | elif Match(r'^extern\s*"[^"]*"\s*\{', line): |
|
2830 | self.stack.append(_ExternCInfo(linenum)) |
|
2831 | else: |
|
2832 | self.stack.append(_BlockInfo(linenum, True)) |
|
2833 | if _MATCH_ASM.match(line): |
|
2834 | self.stack[-1].inline_asm = _BLOCK_ASM |
|
2835 | ||
2836 | elif token == ';' or token == ')': |
|
2837 | # If we haven't seen an opening brace yet, but we already saw |
|
2838 | # a semicolon, this is probably a forward declaration. Pop |
|
2839 | # the stack for these. |
|
2840 | # |
|
2841 | # Similarly, if we haven't seen an opening brace yet, but we |
|
2842 | # already saw a closing parenthesis, then these are probably |
|
2843 | # function arguments with extra "class" or "struct" keywords. |
|
2844 | # Also pop these stack for these. |
|
2845 | if not self.SeenOpenBrace(): |
|
2846 | self.stack.pop() |
|
2847 | else: # token == '}' |
|
2848 | # Perform end of block checks and pop the stack. |
|
2849 | if self.stack: |
|
2850 | self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) |
|
2851 | self.stack.pop() |
|
2852 | line = matched.group(2) |
|
2853 | ||
2854 | def InnermostClass(self): |
|
2855 | """Get class info on the top of the stack. |
|
2856 | ||
2857 | Returns: |
|
2858 | A _ClassInfo object if we are inside a class, or None otherwise. |
|
2859 | """ |
|
2860 | for i in range(len(self.stack), 0, -1): |
|
2861 | classinfo = self.stack[i - 1] |
|
2862 | if isinstance(classinfo, _ClassInfo): |
|
2863 | return classinfo |
|
2864 | return None |
|
2865 | ||
2866 | def CheckCompletedBlocks(self, filename, error): |
|
2867 | """Checks that all classes and namespaces have been completely parsed. |
|
2868 | ||
2869 | Call this when all lines in a file have been processed. |
|
2870 | Args: |
|
2871 | filename: The name of the current file. |
|
2872 | error: The function to call with any errors found. |
|
2873 | """ |
|
2874 | # Note: This test can result in false positives if #ifdef constructs |
|
2875 | # get in the way of brace matching. See the testBuildClass test in |
|
2876 | # cpplint_unittest.py for an example of this. |
|
2877 | for obj in self.stack: |
|
2878 | if isinstance(obj, _ClassInfo): |
|
2879 | error(filename, obj.starting_linenum, 'build/class', 5, |
|
2880 | 'Failed to find complete declaration of class %s' % |
|
2881 | obj.name) |
|
2882 | elif isinstance(obj, _NamespaceInfo): |
|
2883 | error(filename, obj.starting_linenum, 'build/namespaces', 5, |
|
2884 | 'Failed to find complete declaration of namespace %s' % |
|
2885 | obj.name) |
|
2886 | ||
2887 | ||
2888 | def CheckForNonStandardConstructs(filename, clean_lines, linenum, |