Code Duplication    Length = 372-372 lines in 2 locations

core/build-support/cpplint.py 1 location

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

sdk/build-support/cpplint.py 1 location

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