parameters: (filename, clean_lines, linenum, nesting_state, error)
docstring: Checks for the correctness of various spacing issues in the code.
  
    Things we check for: spaces around operators, spaces after
    if/for/while/switch, no spaces around parens in function calls, two
    spaces between code and comment, don't start a block with a blank
    line, don't end a function with a blank line, don't add a blank line
    after public/protected/private, don't have too many blank lines in a row.
  
    Args:
      filename: The name of the current file.
      clean_lines: A CleansedLines instance containing the file.
      linenum: The number of the line to check.
      nesting_state: A _NestingState instance which maintains information about
                     the current stack of nested blocks being parsed.
      error: The function to call with any errors found.
docstring_summary: Checks for the correctness of various spacing issues in the code.
docstring_tokens: Checks for the correctness of various spacing issues in the code .
function: def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
    """Checks for the correctness of various spacing issues in the code.
  
    Things we check for: spaces around operators, spaces after
    if/for/while/switch, no spaces around parens in function calls, two
    spaces between code and comment, don't start a block with a blank
    line, don't end a function with a blank line, don't add a blank line
    after public/protected/private, don't have too many blank lines in a row.
  
    Args:
      filename: The name of the current file.
      clean_lines: A CleansedLines instance containing the file.
      linenum: The number of the line to check.
      nesting_state: A _NestingState instance which maintains information about
                     the current stack of nested blocks being parsed.
      error: The function to call with any errors found.
    """
  
    # Don't use "elided" lines here, otherwise we can't check commented lines.
    # Don't want to use "raw" either, because we don't want to check inside C++11
    # raw strings,
    raw = clean_lines.lines_without_raw_strings
    line = raw[linenum]
  
    # Before nixing comments, check if the line is blank for no good
    # reason.  This includes the first line after a block is opened, and
    # blank lines at the end of a function (ie, right before a line like '}'
    #
    # Skip all the blank line checks if we are immediately inside a
    # namespace body.  In other words, don't issue blank line warnings
    # for this block:
    #   namespace {
    #
    #   }
    #
    # A warning about missing end of namespace comments will be issued instead.
    if IsBlankLine(line) and not nesting_state.InNamespaceBody():
      elided = clean_lines.elided
      prev_line = elided[linenum - 1]
      prevbrace = prev_line.rfind('{')
      # TODO(unknown): Don't complain if line before blank line, and line after,
      #                both start with alnums and are indented the same amount.
      #                This ignores whitespace at the start of a namespace block
      #                because those are not usually indented.
      if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
        # OK, we have a blank line at the start of a code block.  Before we
        # complain, we check if it is an exception to the rule: The previous
        # non-empty line has the parameters of a function header that are indented
        # 4 spaces (because they did not fit in a 80 column line when placed on
        # the same line as the function name).  We also check for the case where
        # the previous line is indented 6 spaces, which may happen when the
        # initializers of a constructor do not fit into a 80 column line.
        exception = False
        if Match(r' {6}\w', prev_line):  # Initializer list?
          # We are looking for the opening column of initializer list, which
          # should be indented 4 spaces to cause 6 space indentation afterwards.
          search_position = linenum-2
          while (search_position >= 0
                 and Match(r' {6}\w', elided[search_position])):
            search_position -= 1
          exception = (search_position >= 0
                       and elided[search_position][:5] == '    :')
        else:
          # Search for the function arguments or an initializer list.  We use a
          # simple heuristic here: If the line is indented 4 spaces; and we have a
          # closing paren, without the opening paren, followed by an opening brace
          # or colon (for initializer lists) we assume that it is the last line of
          # a function header.  If we have a colon indented 4 spaces, it is an
          # initializer list.
          exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
                             prev_line)
                       or Match(r' {4}:', prev_line))
  
        if not exception:
          error(filename, linenum, 'whitespace/blank_line', 2,
                'Redundant blank line at the start of a code block '
                'should be deleted.')
      # Ignore blank lines at the end of a block in a long if-else
      # chain, like this:
      #   if (condition1) {
      #     // Something followed by a blank line
      #
      #   } else if (condition2) {
      #     // Something else
      #   }
      if linenum + 1 < clean_lines.NumLines():
        next_line = raw[linenum + 1]
        if (next_line
            and Match(r'\s*}', next_line)
            and next_line.find('} else ') == -1):
          error(filename, linenum, 'whitespace/blank_line', 3,
                'Redundant blank line at the end of a code block '
                'should be deleted.')
  
      matched = Match(r'\s*(public|protected|private):', prev_line)
      if matched:
        error(filename, linenum, 'whitespace/blank_line', 3,
              'Do not leave a blank line after "%s:"' % matched.group(1))
  
    # Next, we complain if there's a comment too near the text
    commentpos = line.find('//')
    if commentpos != -1:
      # Check if the // may be in quotes.  If so, ignore it
      # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
      if (line.count('"', 0, commentpos) -
          line.count('\\"', 0, commentpos)) % 2 == 0:   # not in quotes
        # Allow one space for new scopes, two spaces otherwise:
        if (not Match(r'^\s*{ //', line) and
            ((commentpos >= 1 and
              line[commentpos-1] not in string.whitespace) or
             (commentpos >= 2 and
              line[commentpos-2] not in string.whitespace))):
          error(filename, linenum, 'whitespace/comments', 2,
                'At least two spaces is best between code and comments')
        # There should always be a space between the // and the comment
        commentend = commentpos + 2
        if commentend < len(line) and not line[commentend] == ' ':
          # but some lines are exceptions -- e.g. if they're big
          # comment delimiters like:
          # //----------------------------------------------------------
          # or are an empty C++ style Doxygen comment, like:
          # ///
          # or C++ style Doxygen comments placed after the variable:
          # ///<  Header comment
          # //!<  Header comment
          # or they begin with multiple slashes followed by a space:
          # //////// Header comment
          match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or
                   Search(r'^/$', line[commentend:]) or
                   Search(r'^!< ', line[commentend:]) or
                   Search(r'^/< ', line[commentend:]) or
                   Search(r'^/+ ', line[commentend:]))
          if not match:
            error(filename, linenum, 'whitespace/comments', 4,
                  'Should have a space between // and comment')
        CheckComment(line[commentpos:], filename, linenum, error)
  
    line = clean_lines.elided[linenum]  # get rid of comments and strings
  
    # Don't try to do spacing checks for operator methods
    line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line)
  
    # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".
    # Otherwise not.  Note we only check for non-spaces on *both* sides;
    # sometimes people put non-spaces on one side when aligning ='s among
    # many lines (not that this is behavior that I approve of...)
    if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line):
      error(filename, linenum, 'whitespace/operators', 4,
            'Missing spaces around =')
  
    # It's ok not to have spaces around binary operators like + - * /, but if
    # there's too little whitespace, we get concerned.  It's hard to tell,
    # though, so we punt on this one for now.  TODO.
  
    # You should always have whitespace around binary operators.
    #
    # Check <= and >= first to avoid false positives with < and >, then
    # check non-include lines for spacing around < and >.
    match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line)
    if match:
      error(filename, linenum, 'whitespace/operators', 3,
            'Missing spaces around %s' % match.group(1))
    # We allow no-spaces around << when used like this: 10<<20, but
    # not otherwise (particularly, not when used as streams)
    # Also ignore using ns::operator<<;
    match = Search(r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line)
    if (match and
        not (match.group(1).isdigit() and match.group(2).isdigit()) and
        not (match.group(1) == 'operator' and match.group(2) == ';')):
      error(filename, linenum, 'whitespace/operators', 3,
            'Missing spaces around <<')
    elif not Match(r'#.*include', line):
      # Avoid false positives on ->
      reduced_line = line.replace('->', '')
  
      # Look for < that is not surrounded by spaces.  This is only
      # triggered if both sides are missing spaces, even though
      # technically should should flag if at least one side is missing a
      # space.  This is done to avoid some false positives with shifts.
      match = Search(r'[^\s<]<([^\s=<].*)', reduced_line)
      if (match and
          not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))):
        error(filename, linenum, 'whitespace/operators', 3,
              'Missing spaces around <')
  
      # Look for > that is not surrounded by spaces.  Similar to the
      # above, we only trigger if both sides are missing spaces to avoid
      # false positives with shifts.
      match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line)
      if (match and
          not FindPreviousMatchingAngleBracket(clean_lines, linenum,
                                               match.group(1))):
        error(filename, linenum, 'whitespace/operators', 3,
              'Missing spaces around >')
  
    # We allow no-spaces around >> for almost anything.  This is because
    # C++11 allows ">>" to close nested templates, which accounts for
    # most cases when ">>" is not followed by a space.
    #
    # We still warn on ">>" followed by alpha character, because that is
    # likely due to ">>" being used for right shifts, e.g.:
    #   value >> alpha
    #
    # When ">>" is used to close templates, the alphanumeric letter that
    # follows would be part of an identifier, and there should still be
    # a space separating the template type and the identifier.
    #   type<type<type>> alpha
    match = Search(r'>>[a-zA-Z_]', line)
    if match:
      error(filename, linenum, 'whitespace/operators', 3,
            'Missing spaces around >>')
  
    # There shouldn't be space around unary operators
    match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
    if match:
      error(filename, linenum, 'whitespace/operators', 4,
            'Extra space for operator %s' % match.group(1))
  
    # A pet peeve of mine: no spaces after an if, while, switch, or for
    match = Search(r' (if\(|for\(|while\(|switch\()', line)
    if match:
      error(filename, linenum, 'whitespace/parens', 5,
            'Missing space before ( in %s' % match.group(1))
  
    # For if/for/while/switch, the left and right parens should be
    # consistent about how many spaces are inside the parens, and
    # there should either be zero or one spaces inside the parens.
    # We don't want: "if ( foo)" or "if ( foo   )".
    # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
    match = Search(r'\b(if|for|while|switch)\s*'
                   r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
                   line)
    if match:
      if len(match.group(2)) != len(match.group(4)):
        if not (match.group(3) == ';' and
                len(match.group(2)) == 1 + len(match.group(4)) or
                not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
          error(filename, linenum, 'whitespace/parens', 5,
                'Mismatching spaces inside () in %s' % match.group(1))
      if len(match.group(2)) not in [0, 1]:
        error(filename, linenum, 'whitespace/parens', 5,
              'Should have zero or one spaces inside ( and ) in %s' %
              match.group(1))
  
    # You should always have a space after a comma (either as fn arg or operator)
    #
    # This does not apply when the non-space character following the
    # comma is another comma, since the only time when that happens is
    # for empty macro arguments.
    #
    # We run this check in two passes: first pass on elided lines to
    # verify that lines contain missing whitespaces, second pass on raw
    # lines to confirm that those missing whitespaces are not due to
    # elided comments.
    if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
      error(filename, linenum, 'whitespace/comma', 3,
            'Missing space after ,')
  
    # You should always have a space after a semicolon
    # except for few corner cases
    # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
    # space after ;
    if Search(r';[^\s};\\)/]', line):
      error(filename, linenum, 'whitespace/semicolon', 3,
            'Missing space after ;')
  
    # Next we will look for issues with function calls.
    CheckSpacingForFunctionCall(filename, line, linenum, error)
  
    # Except after an opening paren, or after another opening brace (in case of
    # an initializer list, for instance), you should have spaces before your
    # braces. And since you should never have braces at the beginning of a line,
    # this is an easy test.
    match = Match(r'^(.*[^ ({]){', line)
    if match:
      # Try a bit harder to check for brace initialization.  This
      # happens in one of the following forms:
      #   Constructor() : initializer_list_{} { ... }
      #   Constructor{}.MemberFunction()
      #   Type variable{};
      #   FunctionCall(type{}, ...);
      #   LastArgument(..., type{});
      #   LOG(INFO) << type{} << " ...";
      #   map_of_type[{...}] = ...;
      #
      # We check for the character following the closing brace, and
      # silence the warning if it's one of those listed above, i.e.
      # "{.;,)<]".
      #
      # To account for nested initializer list, we allow any number of
      # closing braces up to "{;,)<".  We can't simply silence the
      # warning on first sight of closing brace, because that would
      # cause false negatives for things that are not initializer lists.
      #   Silence this:         But not this:
      #     Outer{                if (...) {
      #       Inner{...}            if (...){  // Missing space before {
      #     };                    }
      #
      # There is a false negative with this approach if people inserted
      # spurious semicolons, e.g. "if (cond){};", but we will catch the
      # spurious semicolon with a separate check.
      (endline, endlinenum, endpos) = CloseExpression(
          clean_lines, linenum, len(match.group(1)))
      trailing_text = ''
      if endpos > -1:
        trailing_text = endline[endpos:]
      for offset in xrange(endlinenum + 1,
                           min(endlinenum + 3, clean_lines.NumLines() - 1)):
        trailing_text += clean_lines.elided[offset]
      if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text):
        error(filename, linenum, 'whitespace/braces', 5,
              'Missing space before {')
  
    # Make sure '} else {' has spaces.
    if Search(r'}else', line):
      error(filename, linenum, 'whitespace/braces', 5,
            'Missing space before else')
  
    # You shouldn't have spaces before your brackets, except maybe after
    # 'delete []' or 'new char * []'.
    if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line):
      error(filename, linenum, 'whitespace/braces', 5,
            'Extra space before [')
  
    # You shouldn't have a space before a semicolon at the end of the line.
    # There's a special case for "for" since the style guide allows space before
    # the semicolon there.
    if Search(r':\s*;\s*$', line):
      error(filename, linenum, 'whitespace/semicolon', 5,
            'Semicolon defining empty statement. Use {} instead.')
    elif Search(r'^\s*;\s*$', line):
      error(filename, linenum, 'whitespace/semicolon', 5,
            'Line contains only semicolon. If this should be an empty statement, '
            'use {} instead.')
    elif (Search(r'\s+;\s*$', line) and
          not Search(r'\bfor\b', line)):
      error(filename, linenum, 'whitespace/semicolon', 5,
            'Extra space before last semicolon. If this should be an empty '
            'statement, use {} instead.')
  
    # In range-based for, we wanted spaces before and after the colon, but
    # not around "::" tokens that might appear.
    if (Search('for *\(.*[^:]:[^: ]', line) or
        Search('for *\(.*[^: ]:[^:]', line)):
      error(filename, linenum, 'whitespace/forcolon', 2,
            'Missing space around colon in range-based for loop')
function_tokens: def CheckSpacing ( filename , clean_lines , linenum , nesting_state , error ) : # Don't use "elided" lines here, otherwise we can't check commented lines. # Don't want to use "raw" either, because we don't want to check inside C++11 # raw strings, raw = clean_lines . lines_without_raw_strings line = raw [ linenum ] # Before nixing comments, check if the line is blank for no good # reason.  This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' # # Skip all the blank line checks if we are immediately inside a # namespace body.  In other words, don't issue blank line warnings # for this block: #   namespace { # #   } # # A warning about missing end of namespace comments will be issued instead. if IsBlankLine ( line ) and not nesting_state . InNamespaceBody ( ) : elided = clean_lines . elided prev_line = elided [ linenum - 1 ] prevbrace = prev_line . rfind ( '{' ) # TODO(unknown): Don't complain if line before blank line, and line after, #                both start with alnums and are indented the same amount. #                This ignores whitespace at the start of a namespace block #                because those are not usually indented. if prevbrace != - 1 and prev_line [ prevbrace : ] . find ( '}' ) == - 1 : # OK, we have a blank line at the start of a code block.  Before we # complain, we check if it is an exception to the rule: The previous # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name).  We also check for the case where # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False if Match ( r' {6}\w' , prev_line ) : # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum - 2 while ( search_position >= 0 and Match ( r' {6}\w' , elided [ search_position ] ) ) : search_position -= 1 exception = ( search_position >= 0 and elided [ search_position ] [ : 5 ] == '    :' ) else : # Search for the function arguments or an initializer list.  We use a # simple heuristic here: If the line is indented 4 spaces; and we have a # closing paren, without the opening paren, followed by an opening brace # or colon (for initializer lists) we assume that it is the last line of # a function header.  If we have a colon indented 4 spaces, it is an # initializer list. exception = ( Match ( r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)' , prev_line ) or Match ( r' {4}:' , prev_line ) ) if not exception : error ( filename , linenum , 'whitespace/blank_line' , 2 , 'Redundant blank line at the start of a code block ' 'should be deleted.' ) # Ignore blank lines at the end of a block in a long if-else # chain, like this: #   if (condition1) { #     // Something followed by a blank line # #   } else if (condition2) { #     // Something else #   } if linenum + 1 < clean_lines . NumLines ( ) : next_line = raw [ linenum + 1 ] if ( next_line and Match ( r'\s*}' , next_line ) and next_line . find ( '} else ' ) == - 1 ) : error ( filename , linenum , 'whitespace/blank_line' , 3 , 'Redundant blank line at the end of a code block ' 'should be deleted.' ) matched = Match ( r'\s*(public|protected|private):' , prev_line ) if matched : error ( filename , linenum , 'whitespace/blank_line' , 3 , 'Do not leave a blank line after "%s:"' % matched . group ( 1 ) ) # Next, we complain if there's a comment too near the text commentpos = line . find ( '//' ) if commentpos != - 1 : # Check if the // may be in quotes.  If so, ignore it # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison if ( line . count ( '"' , 0 , commentpos ) - line . count ( '\\"' , 0 , commentpos ) ) % 2 == 0 : # not in quotes # Allow one space for new scopes, two spaces otherwise: if ( not Match ( r'^\s*{ //' , line ) and ( ( commentpos >= 1 and line [ commentpos - 1 ] not in string . whitespace ) or ( commentpos >= 2 and line [ commentpos - 2 ] not in string . whitespace ) ) ) : error ( filename , linenum , 'whitespace/comments' , 2 , 'At least two spaces is best between code and comments' ) # There should always be a space between the // and the comment commentend = commentpos + 2 if commentend < len ( line ) and not line [ commentend ] == ' ' : # but some lines are exceptions -- e.g. if they're big # comment delimiters like: # //---------------------------------------------------------- # or are an empty C++ style Doxygen comment, like: # /// # or C++ style Doxygen comments placed after the variable: # ///<  Header comment # //!<  Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment match = ( Search ( r'[=/-]{4,}\s*$' , line [ commentend : ] ) or Search ( r'^/$' , line [ commentend : ] ) or Search ( r'^!< ' , line [ commentend : ] ) or Search ( r'^/< ' , line [ commentend : ] ) or Search ( r'^/+ ' , line [ commentend : ] ) ) if not match : error ( filename , linenum , 'whitespace/comments' , 4 , 'Should have a space between // and comment' ) CheckComment ( line [ commentpos : ] , filename , linenum , error ) line = clean_lines . elided [ linenum ] # get rid of comments and strings # Don't try to do spacing checks for operator methods line = re . sub ( r'operator(==|!=|<|<<|<=|>=|>>|>)\(' , 'operator\(' , line ) # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not.  Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if Search ( r'[\w.]=[\w.]' , line ) and not Search ( r'\b(if|while) ' , line ) : error ( filename , linenum , 'whitespace/operators' , 4 , 'Missing spaces around =' ) # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned.  It's hard to tell, # though, so we punt on this one for now.  TODO. # You should always have whitespace around binary operators. # # Check <= and >= first to avoid false positives with < and >, then # check non-include lines for spacing around < and >. match = Search ( r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]' , line ) if match : error ( filename , linenum , 'whitespace/operators' , 3 , 'Missing spaces around %s' % match . group ( 1 ) ) # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) # Also ignore using ns::operator<<; match = Search ( r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)' , line ) if ( match and not ( match . group ( 1 ) . isdigit ( ) and match . group ( 2 ) . isdigit ( ) ) and not ( match . group ( 1 ) == 'operator' and match . group ( 2 ) == ';' ) ) : error ( filename , linenum , 'whitespace/operators' , 3 , 'Missing spaces around <<' ) elif not Match ( r'#.*include' , line ) : # Avoid false positives on -> reduced_line = line . replace ( '->' , '' ) # Look for < that is not surrounded by spaces.  This is only # triggered if both sides are missing spaces, even though # technically should should flag if at least one side is missing a # space.  This is done to avoid some false positives with shifts. match = Search ( r'[^\s<]<([^\s=<].*)' , reduced_line ) if ( match and not FindNextMatchingAngleBracket ( clean_lines , linenum , match . group ( 1 ) ) ) : error ( filename , linenum , 'whitespace/operators' , 3 , 'Missing spaces around <' ) # Look for > that is not surrounded by spaces.  Similar to the # above, we only trigger if both sides are missing spaces to avoid # false positives with shifts. match = Search ( r'^(.*[^\s>])>[^\s=>]' , reduced_line ) if ( match and not FindPreviousMatchingAngleBracket ( clean_lines , linenum , match . group ( 1 ) ) ) : error ( filename , linenum , 'whitespace/operators' , 3 , 'Missing spaces around >' ) # We allow no-spaces around >> for almost anything.  This is because # C++11 allows ">>" to close nested templates, which accounts for # most cases when ">>" is not followed by a space. # # We still warn on ">>" followed by alpha character, because that is # likely due to ">>" being used for right shifts, e.g.: #   value >> alpha # # When ">>" is used to close templates, the alphanumeric letter that # follows would be part of an identifier, and there should still be # a space separating the template type and the identifier. #   type<type<type>> alpha match = Search ( r'>>[a-zA-Z_]' , line ) if match : error ( filename , linenum , 'whitespace/operators' , 3 , 'Missing spaces around >>' ) # There shouldn't be space around unary operators match = Search ( r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])' , line ) if match : error ( filename , linenum , 'whitespace/operators' , 4 , 'Extra space for operator %s' % match . group ( 1 ) ) # A pet peeve of mine: no spaces after an if, while, switch, or for match = Search ( r' (if\(|for\(|while\(|switch\()' , line ) if match : error ( filename , linenum , 'whitespace/parens' , 5 , 'Missing space before ( in %s' % match . group ( 1 ) ) # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo   )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. match = Search ( r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$' , line ) if match : if len ( match . group ( 2 ) ) != len ( match . group ( 4 ) ) : if not ( match . group ( 3 ) == ';' and len ( match . group ( 2 ) ) == 1 + len ( match . group ( 4 ) ) or not match . group ( 2 ) and Search ( r'\bfor\s*\(.*; \)' , line ) ) : error ( filename , linenum , 'whitespace/parens' , 5 , 'Mismatching spaces inside () in %s' % match . group ( 1 ) ) if len ( match . group ( 2 ) ) not in [ 0 , 1 ] : error ( filename , linenum , 'whitespace/parens' , 5 , 'Should have zero or one spaces inside ( and ) in %s' % match . group ( 1 ) ) # You should always have a space after a comma (either as fn arg or operator) # # This does not apply when the non-space character following the # comma is another comma, since the only time when that happens is # for empty macro arguments. # # We run this check in two passes: first pass on elided lines to # verify that lines contain missing whitespaces, second pass on raw # lines to confirm that those missing whitespaces are not due to # elided comments. if Search ( r',[^,\s]' , line ) and Search ( r',[^,\s]' , raw [ linenum ] ) : error ( filename , linenum , 'whitespace/comma' , 3 , 'Missing space after ,' ) # You should always have a space after a semicolon # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; if Search ( r';[^\s};\\)/]' , line ) : error ( filename , linenum , 'whitespace/semicolon' , 3 , 'Missing space after ;' ) # Next we will look for issues with function calls. CheckSpacingForFunctionCall ( filename , line , linenum , error ) # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. match = Match ( r'^(.*[^ ({]){' , line ) if match : # Try a bit harder to check for brace initialization.  This # happens in one of the following forms: #   Constructor() : initializer_list_{} { ... } #   Constructor{}.MemberFunction() #   Type variable{}; #   FunctionCall(type{}, ...); #   LastArgument(..., type{}); #   LOG(INFO) << type{} << " ..."; #   map_of_type[{...}] = ...; # # We check for the character following the closing brace, and # silence the warning if it's one of those listed above, i.e. # "{.;,)<]". # # To account for nested initializer list, we allow any number of # closing braces up to "{;,)<".  We can't simply silence the # warning on first sight of closing brace, because that would # cause false negatives for things that are not initializer lists. #   Silence this:         But not this: #     Outer{                if (...) { #       Inner{...}            if (...){  // Missing space before { #     };                    } # # There is a false negative with this approach if people inserted # spurious semicolons, e.g. "if (cond){};", but we will catch the # spurious semicolon with a separate check. ( endline , endlinenum , endpos ) = CloseExpression ( clean_lines , linenum , len ( match . group ( 1 ) ) ) trailing_text = '' if endpos > - 1 : trailing_text = endline [ endpos : ] for offset in xrange ( endlinenum + 1 , min ( endlinenum + 3 , clean_lines . NumLines ( ) - 1 ) ) : trailing_text += clean_lines . elided [ offset ] if not Match ( r'^[\s}]*[{.;,)<\]]' , trailing_text ) : error ( filename , linenum , 'whitespace/braces' , 5 , 'Missing space before {' ) # Make sure '} else {' has spaces. if Search ( r'}else' , line ) : error ( filename , linenum , 'whitespace/braces' , 5 , 'Missing space before else' ) # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'new char * []'. if Search ( r'\w\s+\[' , line ) and not Search ( r'delete\s+\[' , line ) : error ( filename , linenum , 'whitespace/braces' , 5 , 'Extra space before [' ) # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search ( r':\s*;\s*$' , line ) : error ( filename , linenum , 'whitespace/semicolon' , 5 , 'Semicolon defining empty statement. Use {} instead.' ) elif Search ( r'^\s*;\s*$' , line ) : error ( filename , linenum , 'whitespace/semicolon' , 5 , 'Line contains only semicolon. If this should be an empty statement, ' 'use {} instead.' ) elif ( Search ( r'\s+;\s*$' , line ) and not Search ( r'\bfor\b' , line ) ) : error ( filename , linenum , 'whitespace/semicolon' , 5 , 'Extra space before last semicolon. If this should be an empty ' 'statement, use {} instead.' ) # In range-based for, we wanted spaces before and after the colon, but # not around "::" tokens that might appear. if ( Search ( 'for *\(.*[^:]:[^: ]' , line ) or Search ( 'for *\(.*[^: ]:[^:]' , line ) ) : error ( filename , linenum , 'whitespace/forcolon' , 2 , 'Missing space around colon in range-based for loop' )