| @@ 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, |
|