00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 """Does google-lint on c++ files.
00036
00037 The goal of this script is to identify places in the code that *may*
00038 be in non-compliance with google style. It does not attempt to fix
00039 up these problems -- the point is to educate. It does also not
00040 attempt to find all problems, or to ensure that everything it does
00041 find is legitimately a problem.
00042
00043 In particular, we can get very confused by /* and // inside strings!
00044 We do a small hack, which is to ignore //'s with "'s after them on the
00045 same line, but it is far from perfect (in either direction).
00046 """
00047
00048 import codecs
00049 import copy
00050 import getopt
00051 import math
00052 import os
00053 import re
00054 import sre_compile
00055 import string
00056 import sys
00057 import unicodedata
00058
00059
00060 _USAGE = """
00061 Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
00062 [--counting=total|toplevel|detailed] [--root=subdir]
00063 [--linelength=digits]
00064 <file> [file] ...
00065
00066 The style guidelines this tries to follow are those in
00067 http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
00068
00069 Every problem is given a confidence score from 1-5, with 5 meaning we are
00070 certain of the problem, and 1 meaning it could be a legitimate construct.
00071 This will miss some errors, and is not a substitute for a code review.
00072
00073 To suppress false-positive errors of a certain category, add a
00074 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
00075 suppresses errors of all categories on that line.
00076
00077 The files passed in will be linted; at least one file must be provided.
00078 Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
00079 extensions with the --extensions flag.
00080
00081 Flags:
00082
00083 output=vs7
00084 By default, the output is formatted to ease emacs parsing. Visual Studio
00085 compatible output (vs7) may also be used. Other formats are unsupported.
00086
00087 verbose=#
00088 Specify a number 0-5 to restrict errors to certain verbosity levels.
00089
00090 filter=-x,+y,...
00091 Specify a comma-separated list of category-filters to apply: only
00092 error messages whose category names pass the filters will be printed.
00093 (Category names are printed with the message and look like
00094 "[whitespace/indent]".) Filters are evaluated left to right.
00095 "-FOO" and "FOO" means "do not print categories that start with FOO".
00096 "+FOO" means "do print categories that start with FOO".
00097
00098 Examples: --filter=-whitespace,+whitespace/braces
00099 --filter=whitespace,runtime/printf,+runtime/printf_format
00100 --filter=-,+build/include_what_you_use
00101
00102 To see a list of all the categories used in cpplint, pass no arg:
00103 --filter=
00104
00105 counting=total|toplevel|detailed
00106 The total number of errors found is always printed. If
00107 'toplevel' is provided, then the count of errors in each of
00108 the top-level categories like 'build' and 'whitespace' will
00109 also be printed. If 'detailed' is provided, then a count
00110 is provided for each category like 'build/class'.
00111
00112 root=subdir
00113 The root directory used for deriving header guard CPP variable.
00114 By default, the header guard CPP variable is calculated as the relative
00115 path to the directory that contains .git, .hg, or .svn. When this flag
00116 is specified, the relative path is calculated from the specified
00117 directory. If the specified directory does not exist, this flag is
00118 ignored.
00119
00120 Examples:
00121 Assuing that src/.git exists, the header guard CPP variables for
00122 src/chrome/browser/ui/browser.h are:
00123
00124 No flag => CHROME_BROWSER_UI_BROWSER_H_
00125 --root=chrome => BROWSER_UI_BROWSER_H_
00126 --root=chrome/browser => UI_BROWSER_H_
00127
00128 linelength=digits
00129 This is the allowed line length for the project. The default value is
00130 80 characters.
00131
00132 Examples:
00133 --linelength=120
00134
00135 extensions=extension,extension,...
00136 The allowed file extensions that cpplint will check
00137
00138 Examples:
00139 --extensions=hpp,cpp
00140 """
00141
00142
00143
00144
00145
00146 _ERROR_CATEGORIES = [
00147 'build/class',
00148 'build/deprecated',
00149 'build/endif_comment',
00150 'build/explicit_make_pair',
00151 'build/forward_decl',
00152 'build/header_guard',
00153 'build/include',
00154 'build/include_alpha',
00155 'build/include_order',
00156 'build/include_what_you_use',
00157 'build/namespaces',
00158 'build/printf_format',
00159 'build/storage_class',
00160 'legal/copyright',
00161 'readability/alt_tokens',
00162 'readability/braces',
00163 'readability/casting',
00164 'readability/check',
00165 'readability/constructors',
00166 'readability/fn_size',
00167 'readability/function',
00168 'readability/multiline_comment',
00169 'readability/multiline_string',
00170 'readability/namespace',
00171 'readability/nolint',
00172 'readability/nul',
00173 'readability/streams',
00174 'readability/todo',
00175 'readability/utf8',
00176 'runtime/arrays',
00177 'runtime/casting',
00178 'runtime/explicit',
00179 'runtime/int',
00180 'runtime/init',
00181 'runtime/invalid_increment',
00182 'runtime/member_string_references',
00183 'runtime/memset',
00184 'runtime/operator',
00185 'runtime/printf',
00186 'runtime/printf_format',
00187 'runtime/references',
00188 'runtime/string',
00189 'runtime/threadsafe_fn',
00190 'runtime/vlog',
00191 'whitespace/blank_line',
00192 'whitespace/braces',
00193 'whitespace/comma',
00194 'whitespace/comments',
00195 'whitespace/empty_conditional_body',
00196 'whitespace/empty_loop_body',
00197 'whitespace/end_of_line',
00198 'whitespace/ending_newline',
00199 'whitespace/forcolon',
00200 'whitespace/indent',
00201 'whitespace/line_length',
00202 'whitespace/newline',
00203 'whitespace/operators',
00204 'whitespace/parens',
00205 'whitespace/semicolon',
00206 'whitespace/tab',
00207 'whitespace/todo'
00208 ]
00209
00210
00211
00212
00213
00214 _DEFAULT_FILTERS = ['-build/include_alpha']
00215
00216
00217
00218
00219
00220
00221
00222 _CPP_HEADERS = frozenset([
00223
00224 'algobase.h',
00225 'algo.h',
00226 'alloc.h',
00227 'builtinbuf.h',
00228 'bvector.h',
00229 'complex.h',
00230 'defalloc.h',
00231 'deque.h',
00232 'editbuf.h',
00233 'fstream.h',
00234 'function.h',
00235 'hash_map',
00236 'hash_map.h',
00237 'hash_set',
00238 'hash_set.h',
00239 'hashtable.h',
00240 'heap.h',
00241 'indstream.h',
00242 'iomanip.h',
00243 'iostream.h',
00244 'istream.h',
00245 'iterator.h',
00246 'list.h',
00247 'map.h',
00248 'multimap.h',
00249 'multiset.h',
00250 'ostream.h',
00251 'pair.h',
00252 'parsestream.h',
00253 'pfstream.h',
00254 'procbuf.h',
00255 'pthread_alloc',
00256 'pthread_alloc.h',
00257 'rope',
00258 'rope.h',
00259 'ropeimpl.h',
00260 'set.h',
00261 'slist',
00262 'slist.h',
00263 'stack.h',
00264 'stdiostream.h',
00265 'stl_alloc.h',
00266 'stl_relops.h',
00267 'streambuf.h',
00268 'stream.h',
00269 'strfile.h',
00270 'strstream.h',
00271 'tempbuf.h',
00272 'tree.h',
00273 'type_traits.h',
00274 'vector.h',
00275
00276 'algorithm',
00277 'array',
00278 'atomic',
00279 'bitset',
00280 'chrono',
00281 'codecvt',
00282 'complex',
00283 'condition_variable',
00284 'deque',
00285 'exception',
00286 'forward_list',
00287 'fstream',
00288 'functional',
00289 'future',
00290 'initializer_list',
00291 'iomanip',
00292 'ios',
00293 'iosfwd',
00294 'iostream',
00295 'istream',
00296 'iterator',
00297 'limits',
00298 'list',
00299 'locale',
00300 'map',
00301 'memory',
00302 'mutex',
00303 'new',
00304 'numeric',
00305 'ostream',
00306 'queue',
00307 'random',
00308 'ratio',
00309 'regex',
00310 'set',
00311 'sstream',
00312 'stack',
00313 'stdexcept',
00314 'streambuf',
00315 'string',
00316 'strstream',
00317 'system_error',
00318 'thread',
00319 'tuple',
00320 'typeindex',
00321 'typeinfo',
00322 'type_traits',
00323 'unordered_map',
00324 'unordered_set',
00325 'utility',
00326 'valarray',
00327 'vector',
00328
00329 'cassert',
00330 'ccomplex',
00331 'cctype',
00332 'cerrno',
00333 'cfenv',
00334 'cfloat',
00335 'cinttypes',
00336 'ciso646',
00337 'climits',
00338 'clocale',
00339 'cmath',
00340 'csetjmp',
00341 'csignal',
00342 'cstdalign',
00343 'cstdarg',
00344 'cstdbool',
00345 'cstddef',
00346 'cstdint',
00347 'cstdio',
00348 'cstdlib',
00349 'cstring',
00350 'ctgmath',
00351 'ctime',
00352 'cuchar',
00353 'cwchar',
00354 'cwctype',
00355 ])
00356
00357
00358
00359
00360 _CHECK_MACROS = [
00361 'DCHECK', 'CHECK',
00362 'EXPECT_TRUE_M', 'EXPECT_TRUE',
00363 'ASSERT_TRUE_M', 'ASSERT_TRUE',
00364 'EXPECT_FALSE_M', 'EXPECT_FALSE',
00365 'ASSERT_FALSE_M', 'ASSERT_FALSE',
00366 ]
00367
00368
00369 _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
00370
00371 for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
00372 ('>=', 'GE'), ('>', 'GT'),
00373 ('<=', 'LE'), ('<', 'LT')]:
00374 _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
00375 _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
00376 _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
00377 _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
00378 _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement
00379 _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement
00380
00381 for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
00382 ('>=', 'LT'), ('>', 'LE'),
00383 ('<=', 'GT'), ('<', 'GE')]:
00384 _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
00385 _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
00386 _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement
00387 _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement
00388
00389
00390
00391
00392
00393
00394 _ALT_TOKEN_REPLACEMENT = {
00395 'and': '&&',
00396 'bitor': '|',
00397 'or': '||',
00398 'xor': '^',
00399 'compl': '~',
00400 'bitand': '&',
00401 'and_eq': '&=',
00402 'or_eq': '|=',
00403 'xor_eq': '^=',
00404 'not': '!',
00405 'not_eq': '!='
00406 }
00407
00408
00409
00410
00411
00412
00413 _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
00414 r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
00415
00416
00417
00418
00419 _C_SYS_HEADER = 1
00420 _CPP_SYS_HEADER = 2
00421 _LIKELY_MY_HEADER = 3
00422 _POSSIBLE_MY_HEADER = 4
00423 _OTHER_HEADER = 5
00424
00425
00426 _NO_ASM = 0
00427 _INSIDE_ASM = 1
00428 _END_ASM = 2
00429 _BLOCK_ASM = 3
00430
00431
00432 _MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
00433 r'(?:\s+(volatile|__volatile__))?'
00434 r'\s*[{(]')
00435
00436
00437 _regexp_compile_cache = {}
00438
00439
00440 _RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?')
00441
00442
00443
00444 _error_suppressions = {}
00445
00446
00447
00448 _root = None
00449
00450
00451
00452 _line_length = 80
00453
00454
00455
00456 _valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
00457
00458 def ParseNolintSuppressions(filename, raw_line, linenum, error):
00459 """Updates the global list of error-suppressions.
00460
00461 Parses any NOLINT comments on the current line, updating the global
00462 error_suppressions store. Reports an error if the NOLINT comment
00463 was malformed.
00464
00465 Args:
00466 filename: str, the name of the input file.
00467 raw_line: str, the line of input text, with comments.
00468 linenum: int, the number of the current line.
00469 error: function, an error handler.
00470 """
00471
00472 matched = _RE_SUPPRESSION.search(raw_line)
00473 if matched:
00474 category = matched.group(1)
00475 if category in (None, '(*)'):
00476 _error_suppressions.setdefault(None, set()).add(linenum)
00477 else:
00478 if category.startswith('(') and category.endswith(')'):
00479 category = category[1:-1]
00480 if category in _ERROR_CATEGORIES:
00481 _error_suppressions.setdefault(category, set()).add(linenum)
00482 else:
00483 error(filename, linenum, 'readability/nolint', 5,
00484 'Unknown NOLINT error category: %s' % category)
00485
00486
00487 def ResetNolintSuppressions():
00488 "Resets the set of NOLINT suppressions to empty."
00489 _error_suppressions.clear()
00490
00491
00492 def IsErrorSuppressedByNolint(category, linenum):
00493 """Returns true if the specified error category is suppressed on this line.
00494
00495 Consults the global error_suppressions map populated by
00496 ParseNolintSuppressions/ResetNolintSuppressions.
00497
00498 Args:
00499 category: str, the category of the error.
00500 linenum: int, the current line number.
00501 Returns:
00502 bool, True iff the error should be suppressed due to a NOLINT comment.
00503 """
00504 return (linenum in _error_suppressions.get(category, set()) or
00505 linenum in _error_suppressions.get(None, set()))
00506
00507 def Match(pattern, s):
00508 """Matches the string with the pattern, caching the compiled regexp."""
00509
00510
00511
00512 if pattern not in _regexp_compile_cache:
00513 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
00514 return _regexp_compile_cache[pattern].match(s)
00515
00516
00517 def ReplaceAll(pattern, rep, s):
00518 """Replaces instances of pattern in a string with a replacement.
00519
00520 The compiled regex is kept in a cache shared by Match and Search.
00521
00522 Args:
00523 pattern: regex pattern
00524 rep: replacement text
00525 s: search string
00526
00527 Returns:
00528 string with replacements made (or original string if no replacements)
00529 """
00530 if pattern not in _regexp_compile_cache:
00531 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
00532 return _regexp_compile_cache[pattern].sub(rep, s)
00533
00534
00535 def Search(pattern, s):
00536 """Searches the string for the pattern, caching the compiled regexp."""
00537 if pattern not in _regexp_compile_cache:
00538 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
00539 return _regexp_compile_cache[pattern].search(s)
00540
00541
00542 class _IncludeState(dict):
00543 """Tracks line numbers for includes, and the order in which includes appear.
00544
00545 As a dict, an _IncludeState object serves as a mapping between include
00546 filename and line number on which that file was included.
00547
00548 Call CheckNextIncludeOrder() once for each header in the file, passing
00549 in the type constants defined above. Calls in an illegal order will
00550 raise an _IncludeError with an appropriate error message.
00551
00552 """
00553
00554
00555 _INITIAL_SECTION = 0
00556 _MY_H_SECTION = 1
00557 _C_SECTION = 2
00558 _CPP_SECTION = 3
00559 _OTHER_H_SECTION = 4
00560
00561 _TYPE_NAMES = {
00562 _C_SYS_HEADER: 'C system header',
00563 _CPP_SYS_HEADER: 'C++ system header',
00564 _LIKELY_MY_HEADER: 'header this file implements',
00565 _POSSIBLE_MY_HEADER: 'header this file may implement',
00566 _OTHER_HEADER: 'other header',
00567 }
00568 _SECTION_NAMES = {
00569 _INITIAL_SECTION: "... nothing. (This can't be an error.)",
00570 _MY_H_SECTION: 'a header this file implements',
00571 _C_SECTION: 'C system header',
00572 _CPP_SECTION: 'C++ system header',
00573 _OTHER_H_SECTION: 'other header',
00574 }
00575
00576 def __init__(self):
00577 dict.__init__(self)
00578 self.ResetSection()
00579
00580 def ResetSection(self):
00581
00582 self._section = self._INITIAL_SECTION
00583
00584 self._last_header = ''
00585
00586 def SetLastHeader(self, header_path):
00587 self._last_header = header_path
00588
00589 def CanonicalizeAlphabeticalOrder(self, header_path):
00590 """Returns a path canonicalized for alphabetical comparison.
00591
00592 - replaces "-" with "_" so they both cmp the same.
00593 - removes '-inl' since we don't require them to be after the main header.
00594 - lowercase everything, just in case.
00595
00596 Args:
00597 header_path: Path to be canonicalized.
00598
00599 Returns:
00600 Canonicalized path.
00601 """
00602 return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
00603
00604 def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
00605 """Check if a header is in alphabetical order with the previous header.
00606
00607 Args:
00608 clean_lines: A CleansedLines instance containing the file.
00609 linenum: The number of the line to check.
00610 header_path: Canonicalized header to be checked.
00611
00612 Returns:
00613 Returns true if the header is in alphabetical order.
00614 """
00615
00616
00617
00618
00619
00620 if (self._last_header > header_path and
00621 not Match(r'^\s*$', clean_lines.elided[linenum - 1])):
00622 return False
00623 return True
00624
00625 def CheckNextIncludeOrder(self, header_type):
00626 """Returns a non-empty error message if the next header is out of order.
00627
00628 This function also updates the internal state to be ready to check
00629 the next include.
00630
00631 Args:
00632 header_type: One of the _XXX_HEADER constants defined above.
00633
00634 Returns:
00635 The empty string if the header is in the right order, or an
00636 error message describing what's wrong.
00637
00638 """
00639 error_message = ('Found %s after %s' %
00640 (self._TYPE_NAMES[header_type],
00641 self._SECTION_NAMES[self._section]))
00642
00643 last_section = self._section
00644
00645 if header_type == _C_SYS_HEADER:
00646 if self._section <= self._C_SECTION:
00647 self._section = self._C_SECTION
00648 else:
00649 self._last_header = ''
00650 return error_message
00651 elif header_type == _CPP_SYS_HEADER:
00652 if self._section <= self._CPP_SECTION:
00653 self._section = self._CPP_SECTION
00654 else:
00655 self._last_header = ''
00656 return error_message
00657 elif header_type == _LIKELY_MY_HEADER:
00658 if self._section <= self._MY_H_SECTION:
00659 self._section = self._MY_H_SECTION
00660 else:
00661 self._section = self._OTHER_H_SECTION
00662 elif header_type == _POSSIBLE_MY_HEADER:
00663 if self._section <= self._MY_H_SECTION:
00664 self._section = self._MY_H_SECTION
00665 else:
00666
00667
00668 self._section = self._OTHER_H_SECTION
00669 else:
00670 assert header_type == _OTHER_HEADER
00671 self._section = self._OTHER_H_SECTION
00672
00673 if last_section != self._section:
00674 self._last_header = ''
00675
00676 return ''
00677
00678
00679 class _CppLintState(object):
00680 """Maintains module-wide state.."""
00681
00682 def __init__(self):
00683 self.verbose_level = 1
00684 self.error_count = 0
00685
00686 self.filters = _DEFAULT_FILTERS[:]
00687 self.counting = 'total'
00688 self.errors_by_category = {}
00689
00690
00691
00692
00693 self.output_format = 'emacs'
00694
00695 def SetOutputFormat(self, output_format):
00696 """Sets the output format for errors."""
00697 self.output_format = output_format
00698
00699 def SetVerboseLevel(self, level):
00700 """Sets the module's verbosity, and returns the previous setting."""
00701 last_verbose_level = self.verbose_level
00702 self.verbose_level = level
00703 return last_verbose_level
00704
00705 def SetCountingStyle(self, counting_style):
00706 """Sets the module's counting options."""
00707 self.counting = counting_style
00708
00709 def SetFilters(self, filters):
00710 """Sets the error-message filters.
00711
00712 These filters are applied when deciding whether to emit a given
00713 error message.
00714
00715 Args:
00716 filters: A string of comma-separated filters (eg "+whitespace/indent").
00717 Each filter should start with + or -; else we die.
00718
00719 Raises:
00720 ValueError: The comma-separated filters did not all start with '+' or '-'.
00721 E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
00722 """
00723
00724 self.filters = _DEFAULT_FILTERS[:]
00725 for filt in filters.split(','):
00726 clean_filt = filt.strip()
00727 if clean_filt:
00728 self.filters.append(clean_filt)
00729 for filt in self.filters:
00730 if not (filt.startswith('+') or filt.startswith('-')):
00731 raise ValueError('Every filter in --filters must start with + or -'
00732 ' (%s does not)' % filt)
00733
00734 def ResetErrorCounts(self):
00735 """Sets the module's error statistic back to zero."""
00736 self.error_count = 0
00737 self.errors_by_category = {}
00738
00739 def IncrementErrorCount(self, category):
00740 """Bumps the module's error statistic."""
00741 self.error_count += 1
00742 if self.counting in ('toplevel', 'detailed'):
00743 if self.counting != 'detailed':
00744 category = category.split('/')[0]
00745 if category not in self.errors_by_category:
00746 self.errors_by_category[category] = 0
00747 self.errors_by_category[category] += 1
00748
00749 def PrintErrorCounts(self):
00750 """Print a summary of errors by category, and the total."""
00751 for category, count in self.errors_by_category.iteritems():
00752 sys.stderr.write('Category \'%s\' errors found: %d\n' %
00753 (category, count))
00754 sys.stderr.write('Total errors found: %d\n' % self.error_count)
00755
00756 _cpplint_state = _CppLintState()
00757
00758
00759 def _OutputFormat():
00760 """Gets the module's output format."""
00761 return _cpplint_state.output_format
00762
00763
00764 def _SetOutputFormat(output_format):
00765 """Sets the module's output format."""
00766 _cpplint_state.SetOutputFormat(output_format)
00767
00768
00769 def _VerboseLevel():
00770 """Returns the module's verbosity setting."""
00771 return _cpplint_state.verbose_level
00772
00773
00774 def _SetVerboseLevel(level):
00775 """Sets the module's verbosity, and returns the previous setting."""
00776 return _cpplint_state.SetVerboseLevel(level)
00777
00778
00779 def _SetCountingStyle(level):
00780 """Sets the module's counting options."""
00781 _cpplint_state.SetCountingStyle(level)
00782
00783
00784 def _Filters():
00785 """Returns the module's list of output filters, as a list."""
00786 return _cpplint_state.filters
00787
00788
00789 def _SetFilters(filters):
00790 """Sets the module's error-message filters.
00791
00792 These filters are applied when deciding whether to emit a given
00793 error message.
00794
00795 Args:
00796 filters: A string of comma-separated filters (eg "whitespace/indent").
00797 Each filter should start with + or -; else we die.
00798 """
00799 _cpplint_state.SetFilters(filters)
00800
00801
00802 class _FunctionState(object):
00803 """Tracks current function name and the number of lines in its body."""
00804
00805 _NORMAL_TRIGGER = 250
00806 _TEST_TRIGGER = 400
00807
00808 def __init__(self):
00809 self.in_a_function = False
00810 self.lines_in_function = 0
00811 self.current_function = ''
00812
00813 def Begin(self, function_name):
00814 """Start analyzing function body.
00815
00816 Args:
00817 function_name: The name of the function being tracked.
00818 """
00819 self.in_a_function = True
00820 self.lines_in_function = 0
00821 self.current_function = function_name
00822
00823 def Count(self):
00824 """Count line in current function body."""
00825 if self.in_a_function:
00826 self.lines_in_function += 1
00827
00828 def Check(self, error, filename, linenum):
00829 """Report if too many lines in function body.
00830
00831 Args:
00832 error: The function to call with any errors found.
00833 filename: The name of the current file.
00834 linenum: The number of the line to check.
00835 """
00836 if Match(r'T(EST|est)', self.current_function):
00837 base_trigger = self._TEST_TRIGGER
00838 else:
00839 base_trigger = self._NORMAL_TRIGGER
00840 trigger = base_trigger * 2**_VerboseLevel()
00841
00842 if self.lines_in_function > trigger:
00843 error_level = int(math.log(self.lines_in_function / base_trigger, 2))
00844
00845 if error_level > 5:
00846 error_level = 5
00847 error(filename, linenum, 'readability/fn_size', error_level,
00848 'Small and focused functions are preferred:'
00849 ' %s has %d non-comment lines'
00850 ' (error triggered by exceeding %d lines).' % (
00851 self.current_function, self.lines_in_function, trigger))
00852
00853 def End(self):
00854 """Stop analyzing function body."""
00855 self.in_a_function = False
00856
00857
00858 class _IncludeError(Exception):
00859 """Indicates a problem with the include order in a file."""
00860 pass
00861
00862
00863 class FileInfo:
00864 """Provides utility functions for filenames.
00865
00866 FileInfo provides easy access to the components of a file's path
00867 relative to the project root.
00868 """
00869
00870 def __init__(self, filename):
00871 self._filename = filename
00872
00873 def FullName(self):
00874 """Make Windows paths like Unix."""
00875 return os.path.abspath(self._filename).replace('\\', '/')
00876
00877 def RepositoryName(self):
00878 """FullName after removing the local path to the repository.
00879
00880 If we have a real absolute path name here we can try to do something smart:
00881 detecting the root of the checkout and truncating /path/to/checkout from
00882 the name so that we get header guards that don't include things like
00883 "C:\Documents and Settings\..." or "/home/username/..." in them and thus
00884 people on different computers who have checked the source out to different
00885 locations won't see bogus errors.
00886 """
00887 fullname = self.FullName()
00888
00889 if os.path.exists(fullname):
00890 project_dir = os.path.dirname(fullname)
00891
00892 if os.path.exists(os.path.join(project_dir, ".svn")):
00893
00894
00895 root_dir = project_dir
00896 one_up_dir = os.path.dirname(root_dir)
00897 while os.path.exists(os.path.join(one_up_dir, ".svn")):
00898 root_dir = os.path.dirname(root_dir)
00899 one_up_dir = os.path.dirname(one_up_dir)
00900
00901 prefix = os.path.commonprefix([root_dir, project_dir])
00902 return fullname[len(prefix) + 1:]
00903
00904
00905
00906 root_dir = os.path.dirname(fullname)
00907 while (root_dir != os.path.dirname(root_dir) and
00908 not os.path.exists(os.path.join(root_dir, ".git")) and
00909 not os.path.exists(os.path.join(root_dir, ".hg")) and
00910 not os.path.exists(os.path.join(root_dir, ".svn"))):
00911 root_dir = os.path.dirname(root_dir)
00912
00913 if (os.path.exists(os.path.join(root_dir, ".git")) or
00914 os.path.exists(os.path.join(root_dir, ".hg")) or
00915 os.path.exists(os.path.join(root_dir, ".svn"))):
00916 prefix = os.path.commonprefix([root_dir, project_dir])
00917 return fullname[len(prefix) + 1:]
00918
00919
00920 return fullname
00921
00922 def Split(self):
00923 """Splits the file into the directory, basename, and extension.
00924
00925 For 'chrome/browser/browser.cc', Split() would
00926 return ('chrome/browser', 'browser', '.cc')
00927
00928 Returns:
00929 A tuple of (directory, basename, extension).
00930 """
00931
00932 googlename = self.RepositoryName()
00933 project, rest = os.path.split(googlename)
00934 return (project,) + os.path.splitext(rest)
00935
00936 def BaseName(self):
00937 """File base name - text after the final slash, before the final period."""
00938 return self.Split()[1]
00939
00940 def Extension(self):
00941 """File extension - text following the final period."""
00942 return self.Split()[2]
00943
00944 def NoExtension(self):
00945 """File has no source file extension."""
00946 return '/'.join(self.Split()[0:2])
00947
00948 def IsSource(self):
00949 """File has a source file extension."""
00950 return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
00951
00952
00953 def _ShouldPrintError(category, confidence, linenum):
00954 """If confidence >= verbose, category passes filter and is not suppressed."""
00955
00956
00957
00958
00959 if IsErrorSuppressedByNolint(category, linenum):
00960 return False
00961 if confidence < _cpplint_state.verbose_level:
00962 return False
00963
00964 is_filtered = False
00965 for one_filter in _Filters():
00966 if one_filter.startswith('-'):
00967 if category.startswith(one_filter[1:]):
00968 is_filtered = True
00969 elif one_filter.startswith('+'):
00970 if category.startswith(one_filter[1:]):
00971 is_filtered = False
00972 else:
00973 assert False
00974 if is_filtered:
00975 return False
00976
00977 return True
00978
00979
00980 def Error(filename, linenum, category, confidence, message):
00981 """Logs the fact we've found a lint error.
00982
00983 We log where the error was found, and also our confidence in the error,
00984 that is, how certain we are this is a legitimate style regression, and
00985 not a misidentification or a use that's sometimes justified.
00986
00987 False positives can be suppressed by the use of
00988 "cpplint(category)" comments on the offending line. These are
00989 parsed into _error_suppressions.
00990
00991 Args:
00992 filename: The name of the file containing the error.
00993 linenum: The number of the line containing the error.
00994 category: A string used to describe the "category" this bug
00995 falls under: "whitespace", say, or "runtime". Categories
00996 may have a hierarchy separated by slashes: "whitespace/indent".
00997 confidence: A number from 1-5 representing a confidence score for
00998 the error, with 5 meaning that we are certain of the problem,
00999 and 1 meaning that it could be a legitimate construct.
01000 message: The error message.
01001 """
01002 if _ShouldPrintError(category, confidence, linenum):
01003 _cpplint_state.IncrementErrorCount(category)
01004 if _cpplint_state.output_format == 'vs7':
01005 sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
01006 filename, linenum, message, category, confidence))
01007 elif _cpplint_state.output_format == 'eclipse':
01008 sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
01009 filename, linenum, message, category, confidence))
01010 else:
01011 sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
01012 filename, linenum, message, category, confidence))
01013
01014
01015
01016 _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
01017 r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
01018
01019 _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"')
01020
01021 _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'")
01022
01023
01024
01025
01026
01027
01028
01029
01030 _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
01031 r"""(\s*/\*.*\*/\s*$|
01032 /\*.*\*/\s+|
01033 \s+/\*.*\*/(?=\W)|
01034 /\*.*\*/)""", re.VERBOSE)
01035
01036
01037 def IsCppString(line):
01038 """Does line terminate so, that the next symbol is in string constant.
01039
01040 This function does not consider single-line nor multi-line comments.
01041
01042 Args:
01043 line: is a partial line of code starting from the 0..n.
01044
01045 Returns:
01046 True, if next character appended to 'line' is inside a
01047 string constant.
01048 """
01049
01050 line = line.replace(r'\\', 'XX')
01051 return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
01052
01053
01054 def CleanseRawStrings(raw_lines):
01055 """Removes C++11 raw strings from lines.
01056
01057 Before:
01058 static const char kData[] = R"(
01059 multi-line string
01060 )";
01061
01062 After:
01063 static const char kData[] = ""
01064 (replaced by blank line)
01065 "";
01066
01067 Args:
01068 raw_lines: list of raw lines.
01069
01070 Returns:
01071 list of lines with C++11 raw strings replaced by empty strings.
01072 """
01073
01074 delimiter = None
01075 lines_without_raw_strings = []
01076 for line in raw_lines:
01077 if delimiter:
01078
01079 end = line.find(delimiter)
01080 if end >= 0:
01081
01082
01083
01084 leading_space = Match(r'^(\s*)\S', line)
01085 line = leading_space.group(1) + '""' + line[end + len(delimiter):]
01086 delimiter = None
01087 else:
01088
01089 line = ''
01090
01091 else:
01092
01093
01094 matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
01095 if matched:
01096 delimiter = ')' + matched.group(2) + '"'
01097
01098 end = matched.group(3).find(delimiter)
01099 if end >= 0:
01100
01101 line = (matched.group(1) + '""' +
01102 matched.group(3)[end + len(delimiter):])
01103 delimiter = None
01104 else:
01105
01106 line = matched.group(1) + '""'
01107
01108 lines_without_raw_strings.append(line)
01109
01110
01111
01112 return lines_without_raw_strings
01113
01114
01115 def FindNextMultiLineCommentStart(lines, lineix):
01116 """Find the beginning marker for a multiline comment."""
01117 while lineix < len(lines):
01118 if lines[lineix].strip().startswith('/*'):
01119
01120 if lines[lineix].strip().find('*/', 2) < 0:
01121 return lineix
01122 lineix += 1
01123 return len(lines)
01124
01125
01126 def FindNextMultiLineCommentEnd(lines, lineix):
01127 """We are inside a comment, find the end marker."""
01128 while lineix < len(lines):
01129 if lines[lineix].strip().endswith('*/'):
01130 return lineix
01131 lineix += 1
01132 return len(lines)
01133
01134
01135 def RemoveMultiLineCommentsFromRange(lines, begin, end):
01136 """Clears a range of lines for multi-line comments."""
01137
01138
01139 for i in range(begin, end):
01140 lines[i] = '// dummy'
01141
01142
01143 def RemoveMultiLineComments(filename, lines, error):
01144 """Removes multiline (c-style) comments from lines."""
01145 lineix = 0
01146 while lineix < len(lines):
01147 lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
01148 if lineix_begin >= len(lines):
01149 return
01150 lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
01151 if lineix_end >= len(lines):
01152 error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
01153 'Could not find end of multi-line comment')
01154 return
01155 RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
01156 lineix = lineix_end + 1
01157
01158
01159 def CleanseComments(line):
01160 """Removes //-comments and single-line C-style /* */ comments.
01161
01162 Args:
01163 line: A line of C++ source.
01164
01165 Returns:
01166 The line with single-line comments removed.
01167 """
01168 commentpos = line.find('//')
01169 if commentpos != -1 and not IsCppString(line[:commentpos]):
01170 line = line[:commentpos].rstrip()
01171
01172 return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
01173
01174
01175 class CleansedLines(object):
01176 """Holds 3 copies of all lines with different preprocessing applied to them.
01177
01178 1) elided member contains lines without strings and comments,
01179 2) lines member contains lines without comments, and
01180 3) raw_lines member contains all the lines without processing.
01181 All these three members are of <type 'list'>, and of the same length.
01182 """
01183
01184 def __init__(self, lines):
01185 self.elided = []
01186 self.lines = []
01187 self.raw_lines = lines
01188 self.num_lines = len(lines)
01189 self.lines_without_raw_strings = CleanseRawStrings(lines)
01190 for linenum in range(len(self.lines_without_raw_strings)):
01191 self.lines.append(CleanseComments(
01192 self.lines_without_raw_strings[linenum]))
01193 elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
01194 self.elided.append(CleanseComments(elided))
01195
01196 def NumLines(self):
01197 """Returns the number of lines represented."""
01198 return self.num_lines
01199
01200 @staticmethod
01201 def _CollapseStrings(elided):
01202 """Collapses strings and chars on a line to simple "" or '' blocks.
01203
01204 We nix strings first so we're not fooled by text like '"http://"'
01205
01206 Args:
01207 elided: The line being processed.
01208
01209 Returns:
01210 The line with collapsed strings.
01211 """
01212 if not _RE_PATTERN_INCLUDE.match(elided):
01213
01214
01215
01216 elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
01217 elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided)
01218 elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided)
01219 return elided
01220
01221
01222 def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
01223 """Find the position just after the matching endchar.
01224
01225 Args:
01226 line: a CleansedLines line.
01227 startpos: start searching at this position.
01228 depth: nesting level at startpos.
01229 startchar: expression opening character.
01230 endchar: expression closing character.
01231
01232 Returns:
01233 On finding matching endchar: (index just after matching endchar, 0)
01234 Otherwise: (-1, new depth at end of this line)
01235 """
01236 for i in xrange(startpos, len(line)):
01237 if line[i] == startchar:
01238 depth += 1
01239 elif line[i] == endchar:
01240 depth -= 1
01241 if depth == 0:
01242 return (i + 1, 0)
01243 return (-1, depth)
01244
01245
01246 def CloseExpression(clean_lines, linenum, pos):
01247 """If input points to ( or { or [ or <, finds the position that closes it.
01248
01249 If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
01250 linenum/pos that correspond to the closing of the expression.
01251
01252 Args:
01253 clean_lines: A CleansedLines instance containing the file.
01254 linenum: The number of the line to check.
01255 pos: A position on the line.
01256
01257 Returns:
01258 A tuple (line, linenum, pos) pointer *past* the closing brace, or
01259 (line, len(lines), -1) if we never find a close. Note we ignore
01260 strings and comments when matching; and the line we return is the
01261 'cleansed' line at linenum.
01262 """
01263
01264 line = clean_lines.elided[linenum]
01265 startchar = line[pos]
01266 if startchar not in '({[<':
01267 return (line, clean_lines.NumLines(), -1)
01268 if startchar == '(': endchar = ')'
01269 if startchar == '[': endchar = ']'
01270 if startchar == '{': endchar = '}'
01271 if startchar == '<': endchar = '>'
01272
01273
01274 (end_pos, num_open) = FindEndOfExpressionInLine(
01275 line, pos, 0, startchar, endchar)
01276 if end_pos > -1:
01277 return (line, linenum, end_pos)
01278
01279
01280 while linenum < clean_lines.NumLines() - 1:
01281 linenum += 1
01282 line = clean_lines.elided[linenum]
01283 (end_pos, num_open) = FindEndOfExpressionInLine(
01284 line, 0, num_open, startchar, endchar)
01285 if end_pos > -1:
01286 return (line, linenum, end_pos)
01287
01288
01289 return (line, clean_lines.NumLines(), -1)
01290
01291
01292 def FindStartOfExpressionInLine(line, endpos, depth, startchar, endchar):
01293 """Find position at the matching startchar.
01294
01295 This is almost the reverse of FindEndOfExpressionInLine, but note
01296 that the input position and returned position differs by 1.
01297
01298 Args:
01299 line: a CleansedLines line.
01300 endpos: start searching at this position.
01301 depth: nesting level at endpos.
01302 startchar: expression opening character.
01303 endchar: expression closing character.
01304
01305 Returns:
01306 On finding matching startchar: (index at matching startchar, 0)
01307 Otherwise: (-1, new depth at beginning of this line)
01308 """
01309 for i in xrange(endpos, -1, -1):
01310 if line[i] == endchar:
01311 depth += 1
01312 elif line[i] == startchar:
01313 depth -= 1
01314 if depth == 0:
01315 return (i, 0)
01316 return (-1, depth)
01317
01318
01319 def ReverseCloseExpression(clean_lines, linenum, pos):
01320 """If input points to ) or } or ] or >, finds the position that opens it.
01321
01322 If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
01323 linenum/pos that correspond to the opening of the expression.
01324
01325 Args:
01326 clean_lines: A CleansedLines instance containing the file.
01327 linenum: The number of the line to check.
01328 pos: A position on the line.
01329
01330 Returns:
01331 A tuple (line, linenum, pos) pointer *at* the opening brace, or
01332 (line, 0, -1) if we never find the matching opening brace. Note
01333 we ignore strings and comments when matching; and the line we
01334 return is the 'cleansed' line at linenum.
01335 """
01336 line = clean_lines.elided[linenum]
01337 endchar = line[pos]
01338 if endchar not in ')}]>':
01339 return (line, 0, -1)
01340 if endchar == ')': startchar = '('
01341 if endchar == ']': startchar = '['
01342 if endchar == '}': startchar = '{'
01343 if endchar == '>': startchar = '<'
01344
01345
01346 (start_pos, num_open) = FindStartOfExpressionInLine(
01347 line, pos, 0, startchar, endchar)
01348 if start_pos > -1:
01349 return (line, linenum, start_pos)
01350
01351
01352 while linenum > 0:
01353 linenum -= 1
01354 line = clean_lines.elided[linenum]
01355 (start_pos, num_open) = FindStartOfExpressionInLine(
01356 line, len(line) - 1, num_open, startchar, endchar)
01357 if start_pos > -1:
01358 return (line, linenum, start_pos)
01359
01360
01361 return (line, 0, -1)
01362
01363
01364 def CheckForCopyright(filename, lines, error):
01365 """Logs an error if no Copyright message appears at the top of the file."""
01366
01367
01368
01369 for line in xrange(1, min(len(lines), 11)):
01370 if re.search(r'Copyright', lines[line], re.I): break
01371 else:
01372 error(filename, 0, 'legal/copyright', 5,
01373 'No copyright message found. '
01374 'You should have a line: "Copyright [year] <Copyright Owner>"')
01375
01376
01377 def GetHeaderGuardCPPVariable(filename):
01378 """Returns the CPP variable that should be used as a header guard.
01379
01380 Args:
01381 filename: The name of a C++ header file.
01382
01383 Returns:
01384 The CPP variable that should be used as a header guard in the
01385 named file.
01386
01387 """
01388
01389
01390
01391 filename = re.sub(r'_flymake\.h$', '.h', filename)
01392 filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
01393
01394 fileinfo = FileInfo(filename)
01395 file_path_from_root = fileinfo.RepositoryName()
01396 if _root:
01397 file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root)
01398 return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_'
01399
01400
01401 def CheckForHeaderGuard(filename, lines, error):
01402 """Checks that the file contains a header guard.
01403
01404 Logs an error if no #ifndef header guard is present. For other
01405 headers, checks that the full pathname is used.
01406
01407 Args:
01408 filename: The name of the C++ header file.
01409 lines: An array of strings, each representing a line of the file.
01410 error: The function to call with any errors found.
01411 """
01412
01413 cppvar = GetHeaderGuardCPPVariable(filename)
01414
01415 ifndef = None
01416 ifndef_linenum = 0
01417 define = None
01418 endif = None
01419 endif_linenum = 0
01420 for linenum, line in enumerate(lines):
01421 linesplit = line.split()
01422 if len(linesplit) >= 2:
01423
01424 if not ifndef and linesplit[0] == '#ifndef':
01425
01426 ifndef = linesplit[1]
01427 ifndef_linenum = linenum
01428 if not define and linesplit[0] == '#define':
01429 define = linesplit[1]
01430
01431 if line.startswith('#endif'):
01432 endif = line
01433 endif_linenum = linenum
01434
01435 if not ifndef:
01436 error(filename, 0, 'build/header_guard', 5,
01437 'No #ifndef header guard found, suggested CPP variable is: %s' %
01438 cppvar)
01439 return
01440
01441 if not define:
01442 error(filename, 0, 'build/header_guard', 5,
01443 'No #define header guard found, suggested CPP variable is: %s' %
01444 cppvar)
01445 return
01446
01447
01448
01449 if ifndef != cppvar:
01450 error_level = 0
01451 if ifndef != cppvar + '_':
01452 error_level = 5
01453
01454 ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
01455 error)
01456 error(filename, ifndef_linenum, 'build/header_guard', error_level,
01457 '#ifndef header guard has wrong style, please use: %s' % cppvar)
01458
01459 if define != ifndef:
01460 error(filename, 0, 'build/header_guard', 5,
01461 '#ifndef and #define don\'t match, suggested CPP variable is: %s' %
01462 cppvar)
01463 return
01464
01465 if endif != ('#endif // %s' % cppvar):
01466 error_level = 0
01467 if endif != ('#endif // %s' % (cppvar + '_')):
01468 error_level = 5
01469
01470 ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
01471 error)
01472 error(filename, endif_linenum, 'build/header_guard', error_level,
01473 '#endif line should be "#endif // %s"' % cppvar)
01474
01475
01476 def CheckForBadCharacters(filename, lines, error):
01477 """Logs an error for each line containing bad characters.
01478
01479 Two kinds of bad characters:
01480
01481 1. Unicode replacement characters: These indicate that either the file
01482 contained invalid UTF-8 (likely) or Unicode replacement characters (which
01483 it shouldn't). Note that it's possible for this to throw off line
01484 numbering if the invalid UTF-8 occurred adjacent to a newline.
01485
01486 2. NUL bytes. These are problematic for some tools.
01487
01488 Args:
01489 filename: The name of the current file.
01490 lines: An array of strings, each representing a line of the file.
01491 error: The function to call with any errors found.
01492 """
01493 for linenum, line in enumerate(lines):
01494 if u'\ufffd' in line:
01495 error(filename, linenum, 'readability/utf8', 5,
01496 'Line contains invalid UTF-8 (or Unicode replacement character).')
01497 if '\0' in line:
01498 error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
01499
01500
01501 def CheckForNewlineAtEOF(filename, lines, error):
01502 """Logs an error if there is no newline char at the end of the file.
01503
01504 Args:
01505 filename: The name of the current file.
01506 lines: An array of strings, each representing a line of the file.
01507 error: The function to call with any errors found.
01508 """
01509
01510
01511
01512
01513
01514 if len(lines) < 3 or lines[-2]:
01515 error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
01516 'Could not find a newline character at the end of the file.')
01517
01518
01519 def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
01520 """Logs an error if we see /* ... */ or "..." that extend past one line.
01521
01522 /* ... */ comments are legit inside macros, for one line.
01523 Otherwise, we prefer // comments, so it's ok to warn about the
01524 other. Likewise, it's ok for strings to extend across multiple
01525 lines, as long as a line continuation character (backslash)
01526 terminates each line. Although not currently prohibited by the C++
01527 style guide, it's ugly and unnecessary. We don't do well with either
01528 in this lint program, so we warn about both.
01529
01530 Args:
01531 filename: The name of the current file.
01532 clean_lines: A CleansedLines instance containing the file.
01533 linenum: The number of the line to check.
01534 error: The function to call with any errors found.
01535 """
01536 line = clean_lines.elided[linenum]
01537
01538
01539
01540 line = line.replace('\\\\', '')
01541
01542 if line.count('/*') > line.count('*/'):
01543 error(filename, linenum, 'readability/multiline_comment', 5,
01544 'Complex multi-line /*...*/-style comment found. '
01545 'Lint may give bogus warnings. '
01546 'Consider replacing these with //-style comments, '
01547 'with #if 0...#endif, '
01548 'or with more clearly structured multi-line comments.')
01549
01550 if (line.count('"') - line.count('\\"')) % 2:
01551 error(filename, linenum, 'readability/multiline_string', 5,
01552 'Multi-line string ("...") found. This lint script doesn\'t '
01553 'do well with such strings, and may give bogus warnings. '
01554 'Use C++11 raw strings or concatenation instead.')
01555
01556
01557 threading_list = (
01558 ('asctime(', 'asctime_r('),
01559 ('ctime(', 'ctime_r('),
01560 ('getgrgid(', 'getgrgid_r('),
01561 ('getgrnam(', 'getgrnam_r('),
01562 ('getlogin(', 'getlogin_r('),
01563 ('getpwnam(', 'getpwnam_r('),
01564 ('getpwuid(', 'getpwuid_r('),
01565 ('gmtime(', 'gmtime_r('),
01566 ('localtime(', 'localtime_r('),
01567 ('rand(', 'rand_r('),
01568 ('strtok(', 'strtok_r('),
01569 ('ttyname(', 'ttyname_r('),
01570 )
01571
01572
01573 def CheckPosixThreading(filename, clean_lines, linenum, error):
01574 """Checks for calls to thread-unsafe functions.
01575
01576 Much code has been originally written without consideration of
01577 multi-threading. Also, engineers are relying on their old experience;
01578 they have learned posix before threading extensions were added. These
01579 tests guide the engineers to use thread-safe functions (when using
01580 posix directly).
01581
01582 Args:
01583 filename: The name of the current file.
01584 clean_lines: A CleansedLines instance containing the file.
01585 linenum: The number of the line to check.
01586 error: The function to call with any errors found.
01587 """
01588 line = clean_lines.elided[linenum]
01589 for single_thread_function, multithread_safe_function in threading_list:
01590 ix = line.find(single_thread_function)
01591
01592 if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and
01593 line[ix - 1] not in ('_', '.', '>'))):
01594 error(filename, linenum, 'runtime/threadsafe_fn', 2,
01595 'Consider using ' + multithread_safe_function +
01596 '...) instead of ' + single_thread_function +
01597 '...) for improved thread safety.')
01598
01599
01600 def CheckVlogArguments(filename, clean_lines, linenum, error):
01601 """Checks that VLOG() is only used for defining a logging level.
01602
01603 For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
01604 VLOG(FATAL) are not.
01605
01606 Args:
01607 filename: The name of the current file.
01608 clean_lines: A CleansedLines instance containing the file.
01609 linenum: The number of the line to check.
01610 error: The function to call with any errors found.
01611 """
01612 line = clean_lines.elided[linenum]
01613 if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
01614 error(filename, linenum, 'runtime/vlog', 5,
01615 'VLOG() should be used with numeric verbosity level. '
01616 'Use LOG() if you want symbolic severity levels.')
01617
01618
01619
01620
01621 _RE_PATTERN_INVALID_INCREMENT = re.compile(
01622 r'^\s*\*\w+(\+\+|--);')
01623
01624
01625 def CheckInvalidIncrement(filename, clean_lines, linenum, error):
01626 """Checks for invalid increment *count++.
01627
01628 For example following function:
01629 void increment_counter(int* count) {
01630 *count++;
01631 }
01632 is invalid, because it effectively does count++, moving pointer, and should
01633 be replaced with ++*count, (*count)++ or *count += 1.
01634
01635 Args:
01636 filename: The name of the current file.
01637 clean_lines: A CleansedLines instance containing the file.
01638 linenum: The number of the line to check.
01639 error: The function to call with any errors found.
01640 """
01641 line = clean_lines.elided[linenum]
01642 if _RE_PATTERN_INVALID_INCREMENT.match(line):
01643 error(filename, linenum, 'runtime/invalid_increment', 5,
01644 'Changing pointer instead of value (or unused value of operator*).')
01645
01646
01647 class _BlockInfo(object):
01648 """Stores information about a generic block of code."""
01649
01650 def __init__(self, seen_open_brace):
01651 self.seen_open_brace = seen_open_brace
01652 self.open_parentheses = 0
01653 self.inline_asm = _NO_ASM
01654
01655 def CheckBegin(self, filename, clean_lines, linenum, error):
01656 """Run checks that applies to text up to the opening brace.
01657
01658 This is mostly for checking the text after the class identifier
01659 and the "{", usually where the base class is specified. For other
01660 blocks, there isn't much to check, so we always pass.
01661
01662 Args:
01663 filename: The name of the current file.
01664 clean_lines: A CleansedLines instance containing the file.
01665 linenum: The number of the line to check.
01666 error: The function to call with any errors found.
01667 """
01668 pass
01669
01670 def CheckEnd(self, filename, clean_lines, linenum, error):
01671 """Run checks that applies to text after the closing brace.
01672
01673 This is mostly used for checking end of namespace comments.
01674
01675 Args:
01676 filename: The name of the current file.
01677 clean_lines: A CleansedLines instance containing the file.
01678 linenum: The number of the line to check.
01679 error: The function to call with any errors found.
01680 """
01681 pass
01682
01683
01684 class _ClassInfo(_BlockInfo):
01685 """Stores information about a class."""
01686
01687 def __init__(self, name, class_or_struct, clean_lines, linenum):
01688 _BlockInfo.__init__(self, False)
01689 self.name = name
01690 self.starting_linenum = linenum
01691 self.is_derived = False
01692 if class_or_struct == 'struct':
01693 self.access = 'public'
01694 self.is_struct = True
01695 else:
01696 self.access = 'private'
01697 self.is_struct = False
01698
01699
01700
01701 initial_indent = Match(r'^( *)\S', clean_lines.raw_lines[linenum])
01702 if initial_indent:
01703 self.class_indent = len(initial_indent.group(1))
01704 else:
01705 self.class_indent = 0
01706
01707
01708
01709
01710
01711
01712 self.last_line = 0
01713 depth = 0
01714 for i in range(linenum, clean_lines.NumLines()):
01715 line = clean_lines.elided[i]
01716 depth += line.count('{') - line.count('}')
01717 if not depth:
01718 self.last_line = i
01719 break
01720
01721 def CheckBegin(self, filename, clean_lines, linenum, error):
01722
01723 if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
01724 self.is_derived = True
01725
01726 def CheckEnd(self, filename, clean_lines, linenum, error):
01727
01728
01729
01730 indent = Match(r'^( *)\}', clean_lines.elided[linenum])
01731 if indent and len(indent.group(1)) != self.class_indent:
01732 if self.is_struct:
01733 parent = 'struct ' + self.name
01734 else:
01735 parent = 'class ' + self.name
01736 error(filename, linenum, 'whitespace/indent', 3,
01737 'Closing brace should be aligned with beginning of %s' % parent)
01738
01739
01740 class _NamespaceInfo(_BlockInfo):
01741 """Stores information about a namespace."""
01742
01743 def __init__(self, name, linenum):
01744 _BlockInfo.__init__(self, False)
01745 self.name = name or ''
01746 self.starting_linenum = linenum
01747
01748 def CheckEnd(self, filename, clean_lines, linenum, error):
01749 """Check end of namespace comments."""
01750 line = clean_lines.raw_lines[linenum]
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763 if (linenum - self.starting_linenum < 10
01764 and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)):
01765 return
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779 if self.name:
01780
01781 if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) +
01782 r'[\*/\.\\\s]*$'),
01783 line):
01784 error(filename, linenum, 'readability/namespace', 5,
01785 'Namespace should be terminated with "// namespace %s"' %
01786 self.name)
01787 else:
01788
01789 if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):
01790 error(filename, linenum, 'readability/namespace', 5,
01791 'Namespace should be terminated with "// namespace"')
01792
01793
01794 class _PreprocessorInfo(object):
01795 """Stores checkpoints of nesting stacks when #if/#else is seen."""
01796
01797 def __init__(self, stack_before_if):
01798
01799 self.stack_before_if = stack_before_if
01800
01801
01802 self.stack_before_else = []
01803
01804
01805 self.seen_else = False
01806
01807
01808 class _NestingState(object):
01809 """Holds states related to parsing braces."""
01810
01811 def __init__(self):
01812
01813
01814
01815
01816
01817
01818 self.stack = []
01819
01820
01821 self.pp_stack = []
01822
01823 def SeenOpenBrace(self):
01824 """Check if we have seen the opening brace for the innermost block.
01825
01826 Returns:
01827 True if we have seen the opening brace, False if the innermost
01828 block is still expecting an opening brace.
01829 """
01830 return (not self.stack) or self.stack[-1].seen_open_brace
01831
01832 def InNamespaceBody(self):
01833 """Check if we are currently one level inside a namespace body.
01834
01835 Returns:
01836 True if top of the stack is a namespace block, False otherwise.
01837 """
01838 return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
01839
01840 def UpdatePreprocessor(self, line):
01841 """Update preprocessor stack.
01842
01843 We need to handle preprocessors due to classes like this:
01844 #ifdef SWIG
01845 struct ResultDetailsPageElementExtensionPoint {
01846 #else
01847 struct ResultDetailsPageElementExtensionPoint : public Extension {
01848 #endif
01849
01850 We make the following assumptions (good enough for most files):
01851 - Preprocessor condition evaluates to true from #if up to first
01852 #else/#elif/#endif.
01853
01854 - Preprocessor condition evaluates to false from #else/#elif up
01855 to #endif. We still perform lint checks on these lines, but
01856 these do not affect nesting stack.
01857
01858 Args:
01859 line: current line to check.
01860 """
01861 if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
01862
01863
01864 self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
01865 elif Match(r'^\s*#\s*(else|elif)\b', line):
01866
01867 if self.pp_stack:
01868 if not self.pp_stack[-1].seen_else:
01869
01870
01871
01872 self.pp_stack[-1].seen_else = True
01873 self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
01874
01875
01876 self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
01877 else:
01878
01879 pass
01880 elif Match(r'^\s*#\s*endif\b', line):
01881
01882 if self.pp_stack:
01883
01884
01885
01886 if self.pp_stack[-1].seen_else:
01887
01888
01889 self.stack = self.pp_stack[-1].stack_before_else
01890
01891 self.pp_stack.pop()
01892 else:
01893
01894 pass
01895
01896 def Update(self, filename, clean_lines, linenum, error):
01897 """Update nesting state with current line.
01898
01899 Args:
01900 filename: The name of the current file.
01901 clean_lines: A CleansedLines instance containing the file.
01902 linenum: The number of the line to check.
01903 error: The function to call with any errors found.
01904 """
01905 line = clean_lines.elided[linenum]
01906
01907
01908 self.UpdatePreprocessor(line)
01909
01910
01911
01912 if self.stack:
01913 inner_block = self.stack[-1]
01914 depth_change = line.count('(') - line.count(')')
01915 inner_block.open_parentheses += depth_change
01916
01917
01918 if inner_block.inline_asm in (_NO_ASM, _END_ASM):
01919 if (depth_change != 0 and
01920 inner_block.open_parentheses == 1 and
01921 _MATCH_ASM.match(line)):
01922
01923 inner_block.inline_asm = _INSIDE_ASM
01924 else:
01925
01926
01927 inner_block.inline_asm = _NO_ASM
01928 elif (inner_block.inline_asm == _INSIDE_ASM and
01929 inner_block.open_parentheses == 0):
01930
01931 inner_block.inline_asm = _END_ASM
01932
01933
01934
01935
01936 while True:
01937
01938
01939
01940
01941 namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)
01942 if not namespace_decl_match:
01943 break
01944
01945 new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)
01946 self.stack.append(new_namespace)
01947
01948 line = namespace_decl_match.group(2)
01949 if line.find('{') != -1:
01950 new_namespace.seen_open_brace = True
01951 line = line[line.find('{') + 1:]
01952
01953
01954
01955
01956
01957
01958
01959
01960
01961
01962
01963
01964
01965
01966
01967
01968
01969
01970
01971
01972
01973
01974 class_decl_match = Match(
01975 r'\s*(template\s*<[\w\s<>,:]*>\s*)?'
01976 r'(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)'
01977 r'(([^=>]|<[^<>]*>|<[^<>]*<[^<>]*>\s*>)*)$', line)
01978 if (class_decl_match and
01979 (not self.stack or self.stack[-1].open_parentheses == 0)):
01980 self.stack.append(_ClassInfo(
01981 class_decl_match.group(4), class_decl_match.group(2),
01982 clean_lines, linenum))
01983 line = class_decl_match.group(5)
01984
01985
01986
01987 if not self.SeenOpenBrace():
01988 self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
01989
01990
01991 if self.stack and isinstance(self.stack[-1], _ClassInfo):
01992 classinfo = self.stack[-1]
01993 access_match = Match(
01994 r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
01995 r':(?:[^:]|$)',
01996 line)
01997 if access_match:
01998 classinfo.access = access_match.group(2)
01999
02000
02001
02002 indent = access_match.group(1)
02003 if (len(indent) != classinfo.class_indent + 1 and
02004 Match(r'^\s*$', indent)):
02005 if classinfo.is_struct:
02006 parent = 'struct ' + classinfo.name
02007 else:
02008 parent = 'class ' + classinfo.name
02009 slots = ''
02010 if access_match.group(3):
02011 slots = access_match.group(3)
02012 error(filename, linenum, 'whitespace/indent', 3,
02013 '%s%s: should be indented +1 space inside %s' % (
02014 access_match.group(2), slots, parent))
02015
02016
02017 while True:
02018
02019 matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)
02020 if not matched:
02021 break
02022
02023 token = matched.group(1)
02024 if token == '{':
02025
02026
02027
02028 if not self.SeenOpenBrace():
02029 self.stack[-1].seen_open_brace = True
02030 else:
02031 self.stack.append(_BlockInfo(True))
02032 if _MATCH_ASM.match(line):
02033 self.stack[-1].inline_asm = _BLOCK_ASM
02034 elif token == ';' or token == ')':
02035
02036
02037
02038
02039
02040
02041
02042
02043 if not self.SeenOpenBrace():
02044 self.stack.pop()
02045 else:
02046
02047 if self.stack:
02048 self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
02049 self.stack.pop()
02050 line = matched.group(2)
02051
02052 def InnermostClass(self):
02053 """Get class info on the top of the stack.
02054
02055 Returns:
02056 A _ClassInfo object if we are inside a class, or None otherwise.
02057 """
02058 for i in range(len(self.stack), 0, -1):
02059 classinfo = self.stack[i - 1]
02060 if isinstance(classinfo, _ClassInfo):
02061 return classinfo
02062 return None
02063
02064 def CheckCompletedBlocks(self, filename, error):
02065 """Checks that all classes and namespaces have been completely parsed.
02066
02067 Call this when all lines in a file have been processed.
02068 Args:
02069 filename: The name of the current file.
02070 error: The function to call with any errors found.
02071 """
02072
02073
02074
02075 for obj in self.stack:
02076 if isinstance(obj, _ClassInfo):
02077 error(filename, obj.starting_linenum, 'build/class', 5,
02078 'Failed to find complete declaration of class %s' %
02079 obj.name)
02080 elif isinstance(obj, _NamespaceInfo):
02081 error(filename, obj.starting_linenum, 'build/namespaces', 5,
02082 'Failed to find complete declaration of namespace %s' %
02083 obj.name)
02084
02085
02086 def CheckForNonStandardConstructs(filename, clean_lines, linenum,
02087 nesting_state, error):
02088 r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
02089
02090 Complain about several constructs which gcc-2 accepts, but which are
02091 not standard C++. Warning about these in lint is one way to ease the
02092 transition to new compilers.
02093 - put storage class first (e.g. "static const" instead of "const static").
02094 - "%lld" instead of %qd" in printf-type functions.
02095 - "%1$d" is non-standard in printf-type functions.
02096 - "\%" is an undefined character escape sequence.
02097 - text after #endif is not allowed.
02098 - invalid inner-style forward declaration.
02099 - >? and <? operators, and their >?= and <?= cousins.
02100
02101 Additionally, check for constructor/destructor style violations and reference
02102 members, as it is very convenient to do so while checking for
02103 gcc-2 compliance.
02104
02105 Args:
02106 filename: The name of the current file.
02107 clean_lines: A CleansedLines instance containing the file.
02108 linenum: The number of the line to check.
02109 nesting_state: A _NestingState instance which maintains information about
02110 the current stack of nested blocks being parsed.
02111 error: A callable to which errors are reported, which takes 4 arguments:
02112 filename, line number, error level, and message
02113 """
02114
02115
02116 line = clean_lines.lines[linenum]
02117
02118 if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
02119 error(filename, linenum, 'runtime/printf_format', 3,
02120 '%q in format strings is deprecated. Use %ll instead.')
02121
02122 if Search(r'printf\s*\(.*".*%\d+\$', line):
02123 error(filename, linenum, 'runtime/printf_format', 2,
02124 '%N$ formats are unconventional. Try rewriting to avoid them.')
02125
02126
02127 line = line.replace('\\\\', '')
02128
02129 if Search(r'("|\').*\\(%|\[|\(|{)', line):
02130 error(filename, linenum, 'build/printf_format', 3,
02131 '%, [, (, and { are undefined character escapes. Unescape them.')
02132
02133
02134 line = clean_lines.elided[linenum]
02135
02136 if Search(r'\b(const|volatile|void|char|short|int|long'
02137 r'|float|double|signed|unsigned'
02138 r'|schar|u?int8|u?int16|u?int32|u?int64)'
02139 r'\s+(register|static|extern|typedef)\b',
02140 line):
02141 error(filename, linenum, 'build/storage_class', 5,
02142 'Storage class (static, extern, typedef, etc) should be first.')
02143
02144 if Match(r'\s*#\s*endif\s*[^/\s]+', line):
02145 error(filename, linenum, 'build/endif_comment', 5,
02146 'Uncommented text after #endif is non-standard. Use a comment.')
02147
02148 if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
02149 error(filename, linenum, 'build/forward_decl', 5,
02150 'Inner-style forward declarations are invalid. Remove this line.')
02151
02152 if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
02153 line):
02154 error(filename, linenum, 'build/deprecated', 3,
02155 '>? and <? (max and min) operators are non-standard and deprecated.')
02156
02157 if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
02158
02159
02160
02161
02162
02163
02164
02165 error(filename, linenum, 'runtime/member_string_references', 2,
02166 'const string& members are dangerous. It is much better to use '
02167 'alternatives, such as pointers or simple constants.')
02168
02169
02170
02171
02172 classinfo = nesting_state.InnermostClass()
02173 if not classinfo or not classinfo.seen_open_brace:
02174 return
02175
02176
02177
02178 base_classname = classinfo.name.split('::')[-1]
02179
02180
02181
02182 args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)'
02183 % re.escape(base_classname),
02184 line)
02185 if (args and
02186 args.group(1) != 'void' and
02187 not Match(r'(const\s+)?%s(\s+const)?\s*(?:<\w+>\s*)?&'
02188 % re.escape(base_classname), args.group(1).strip())):
02189 error(filename, linenum, 'runtime/explicit', 5,
02190 'Single-argument constructors should be marked explicit.')
02191
02192
02193 def CheckSpacingForFunctionCall(filename, line, linenum, error):
02194 """Checks for the correctness of various spacing around function calls.
02195
02196 Args:
02197 filename: The name of the current file.
02198 line: The text of the line to check.
02199 linenum: The number of the line to check.
02200 error: The function to call with any errors found.
02201 """
02202
02203
02204
02205
02206
02207 fncall = line
02208 for pattern in (r'\bif\s*\((.*)\)\s*{',
02209 r'\bfor\s*\((.*)\)\s*{',
02210 r'\bwhile\s*\((.*)\)\s*[{;]',
02211 r'\bswitch\s*\((.*)\)\s*{'):
02212 match = Search(pattern, line)
02213 if match:
02214 fncall = match.group(1)
02215 break
02216
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230 if (
02231 not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
02232 fncall) and
02233
02234 not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
02235
02236 not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
02237 if Search(r'\w\s*\(\s(?!\s*\\$)', fncall):
02238 error(filename, linenum, 'whitespace/parens', 4,
02239 'Extra space after ( in function call')
02240 elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):
02241 error(filename, linenum, 'whitespace/parens', 2,
02242 'Extra space after (')
02243 if (Search(r'\w\s+\(', fncall) and
02244 not Search(r'#\s*define|typedef', fncall) and
02245 not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)):
02246 error(filename, linenum, 'whitespace/parens', 4,
02247 'Extra space before ( in function call')
02248
02249
02250 if Search(r'[^)]\s+\)\s*[^{\s]', fncall):
02251
02252
02253 if Search(r'^\s+\)', fncall):
02254 error(filename, linenum, 'whitespace/parens', 2,
02255 'Closing ) should be moved to the previous line')
02256 else:
02257 error(filename, linenum, 'whitespace/parens', 2,
02258 'Extra space before )')
02259
02260
02261 def IsBlankLine(line):
02262 """Returns true if the given line is blank.
02263
02264 We consider a line to be blank if the line is empty or consists of
02265 only white spaces.
02266
02267 Args:
02268 line: A line of a string.
02269
02270 Returns:
02271 True, if the given line is blank.
02272 """
02273 return not line or line.isspace()
02274
02275
02276 def CheckForFunctionLengths(filename, clean_lines, linenum,
02277 function_state, error):
02278 """Reports for long function bodies.
02279
02280 For an overview why this is done, see:
02281 http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
02282
02283 Uses a simplistic algorithm assuming other style guidelines
02284 (especially spacing) are followed.
02285 Only checks unindented functions, so class members are unchecked.
02286 Trivial bodies are unchecked, so constructors with huge initializer lists
02287 may be missed.
02288 Blank/comment lines are not counted so as to avoid encouraging the removal
02289 of vertical space and comments just to get through a lint check.
02290 NOLINT *on the last line of a function* disables this check.
02291
02292 Args:
02293 filename: The name of the current file.
02294 clean_lines: A CleansedLines instance containing the file.
02295 linenum: The number of the line to check.
02296 function_state: Current function name and lines in body so far.
02297 error: The function to call with any errors found.
02298 """
02299 lines = clean_lines.lines
02300 line = lines[linenum]
02301 raw = clean_lines.raw_lines
02302 raw_line = raw[linenum]
02303 joined_line = ''
02304
02305 starting_func = False
02306 regexp = r'(\w(\w|::|\*|\&|\s)*)\('
02307 match_result = Match(regexp, line)
02308 if match_result:
02309
02310
02311 function_name = match_result.group(1).split()[-1]
02312 if function_name == 'TEST' or function_name == 'TEST_F' or (
02313 not Match(r'[A-Z_]+$', function_name)):
02314 starting_func = True
02315
02316 if starting_func:
02317 body_found = False
02318 for start_linenum in xrange(linenum, clean_lines.NumLines()):
02319 start_line = lines[start_linenum]
02320 joined_line += ' ' + start_line.lstrip()
02321 if Search(r'(;|})', start_line):
02322 body_found = True
02323 break
02324 elif Search(r'{', start_line):
02325 body_found = True
02326 function = Search(r'((\w|:)*)\(', line).group(1)
02327 if Match(r'TEST', function):
02328 parameter_regexp = Search(r'(\(.*\))', joined_line)
02329 if parameter_regexp:
02330 function += parameter_regexp.group(1)
02331 else:
02332 function += '()'
02333 function_state.Begin(function)
02334 break
02335 if not body_found:
02336
02337 error(filename, linenum, 'readability/fn_size', 5,
02338 'Lint failed to find start of function body.')
02339 elif Match(r'^\}\s*$', line):
02340 function_state.Check(error, filename, linenum)
02341 function_state.End()
02342 elif not Match(r'^\s*$', line):
02343 function_state.Count()
02344
02345
02346 _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')
02347
02348
02349 def CheckComment(comment, filename, linenum, error):
02350 """Checks for common mistakes in TODO comments.
02351
02352 Args:
02353 comment: The text of the comment from the line in question.
02354 filename: The name of the current file.
02355 linenum: The number of the line to check.
02356 error: The function to call with any errors found.
02357 """
02358 match = _RE_PATTERN_TODO.match(comment)
02359 if match:
02360
02361 leading_whitespace = match.group(1)
02362 if len(leading_whitespace) > 1:
02363 error(filename, linenum, 'whitespace/todo', 2,
02364 'Too many spaces before TODO')
02365
02366 username = match.group(2)
02367 if not username:
02368 error(filename, linenum, 'readability/todo', 2,
02369 'Missing username in TODO; it should look like '
02370 '"// TODO(my_username): Stuff."')
02371
02372 middle_whitespace = match.group(3)
02373
02374 if middle_whitespace != ' ' and middle_whitespace != '':
02375 error(filename, linenum, 'whitespace/todo', 2,
02376 'TODO(my_username) should be followed by a space')
02377
02378 def CheckAccess(filename, clean_lines, linenum, nesting_state, error):
02379 """Checks for improper use of DISALLOW* macros.
02380
02381 Args:
02382 filename: The name of the current file.
02383 clean_lines: A CleansedLines instance containing the file.
02384 linenum: The number of the line to check.
02385 nesting_state: A _NestingState instance which maintains information about
02386 the current stack of nested blocks being parsed.
02387 error: The function to call with any errors found.
02388 """
02389 line = clean_lines.elided[linenum]
02390
02391 matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|'
02392 r'DISALLOW_EVIL_CONSTRUCTORS|'
02393 r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line)
02394 if not matched:
02395 return
02396 if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo):
02397 if nesting_state.stack[-1].access != 'private':
02398 error(filename, linenum, 'readability/constructors', 3,
02399 '%s must be in the private: section' % matched.group(1))
02400
02401 else:
02402
02403
02404
02405
02406 pass
02407
02408
02409 def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix):
02410 """Find the corresponding > to close a template.
02411
02412 Args:
02413 clean_lines: A CleansedLines instance containing the file.
02414 linenum: Current line number.
02415 init_suffix: Remainder of the current line after the initial <.
02416
02417 Returns:
02418 True if a matching bracket exists.
02419 """
02420 line = init_suffix
02421 nesting_stack = ['<']
02422 while True:
02423
02424
02425
02426
02427
02428
02429
02430
02431 match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line)
02432 if match:
02433
02434 operator = match.group(1)
02435 line = match.group(2)
02436
02437 if nesting_stack[-1] == '<':
02438
02439 if operator in ('<', '(', '['):
02440 nesting_stack.append(operator)
02441 elif operator == '>':
02442 nesting_stack.pop()
02443 if not nesting_stack:
02444
02445 return True
02446 elif operator == ',':
02447
02448
02449
02450
02451 return True
02452 else:
02453
02454 return False
02455
02456 else:
02457
02458 if operator in ('<', '(', '['):
02459 nesting_stack.append(operator)
02460 elif operator in (')', ']'):
02461
02462
02463 nesting_stack.pop()
02464
02465 else:
02466
02467 linenum += 1
02468 if linenum >= len(clean_lines.elided):
02469 break
02470 line = clean_lines.elided[linenum]
02471
02472
02473
02474
02475 return True
02476
02477
02478 def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix):
02479 """Find the corresponding < that started a template.
02480
02481 Args:
02482 clean_lines: A CleansedLines instance containing the file.
02483 linenum: Current line number.
02484 init_prefix: Part of the current line before the initial >.
02485
02486 Returns:
02487 True if a matching bracket exists.
02488 """
02489 line = init_prefix
02490 nesting_stack = ['>']
02491 while True:
02492
02493 match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line)
02494 if match:
02495
02496 operator = match.group(2)
02497 line = match.group(1)
02498
02499 if nesting_stack[-1] == '>':
02500
02501 if operator in ('>', ')', ']'):
02502 nesting_stack.append(operator)
02503 elif operator == '<':
02504 nesting_stack.pop()
02505 if not nesting_stack:
02506
02507 return True
02508 elif operator == ',':
02509
02510
02511
02512 return True
02513 else:
02514
02515 return False
02516
02517 else:
02518
02519 if operator in ('>', ')', ']'):
02520 nesting_stack.append(operator)
02521 elif operator in ('(', '['):
02522 nesting_stack.pop()
02523
02524 else:
02525
02526 linenum -= 1
02527 if linenum < 0:
02528 break
02529 line = clean_lines.elided[linenum]
02530
02531
02532 return False
02533
02534
02535 def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
02536 """Checks for the correctness of various spacing issues in the code.
02537
02538 Things we check for: spaces around operators, spaces after
02539 if/for/while/switch, no spaces around parens in function calls, two
02540 spaces between code and comment, don't start a block with a blank
02541 line, don't end a function with a blank line, don't add a blank line
02542 after public/protected/private, don't have too many blank lines in a row.
02543
02544 Args:
02545 filename: The name of the current file.
02546 clean_lines: A CleansedLines instance containing the file.
02547 linenum: The number of the line to check.
02548 nesting_state: A _NestingState instance which maintains information about
02549 the current stack of nested blocks being parsed.
02550 error: The function to call with any errors found.
02551 """
02552
02553
02554
02555
02556 raw = clean_lines.lines_without_raw_strings
02557 line = raw[linenum]
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568
02569
02570
02571 if IsBlankLine(line) and not nesting_state.InNamespaceBody():
02572 elided = clean_lines.elided
02573 prev_line = elided[linenum - 1]
02574 prevbrace = prev_line.rfind('{')
02575
02576
02577
02578
02579 if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
02580
02581
02582
02583
02584
02585
02586
02587 exception = False
02588 if Match(r' {6}\w', prev_line):
02589
02590
02591 search_position = linenum-2
02592 while (search_position >= 0
02593 and Match(r' {6}\w', elided[search_position])):
02594 search_position -= 1
02595 exception = (search_position >= 0
02596 and elided[search_position][:5] == ' :')
02597 else:
02598
02599
02600
02601
02602
02603
02604 exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
02605 prev_line)
02606 or Match(r' {4}:', prev_line))
02607
02608 if not exception:
02609 error(filename, linenum, 'whitespace/blank_line', 2,
02610 'Redundant blank line at the start of a code block '
02611 'should be deleted.')
02612
02613
02614
02615
02616
02617
02618
02619
02620 if linenum + 1 < clean_lines.NumLines():
02621 next_line = raw[linenum + 1]
02622 if (next_line
02623 and Match(r'\s*}', next_line)
02624 and next_line.find('} else ') == -1):
02625 error(filename, linenum, 'whitespace/blank_line', 3,
02626 'Redundant blank line at the end of a code block '
02627 'should be deleted.')
02628
02629 matched = Match(r'\s*(public|protected|private):', prev_line)
02630 if matched:
02631 error(filename, linenum, 'whitespace/blank_line', 3,
02632 'Do not leave a blank line after "%s:"' % matched.group(1))
02633
02634
02635 commentpos = line.find('//')
02636 if commentpos != -1:
02637
02638
02639 if (line.count('"', 0, commentpos) -
02640 line.count('\\"', 0, commentpos)) % 2 == 0:
02641
02642 if (not Match(r'^\s*{ //', line) and
02643 ((commentpos >= 1 and
02644 line[commentpos-1] not in string.whitespace) or
02645 (commentpos >= 2 and
02646 line[commentpos-2] not in string.whitespace))):
02647 error(filename, linenum, 'whitespace/comments', 2,
02648 'At least two spaces is best between code and comments')
02649
02650 commentend = commentpos + 2
02651 if commentend < len(line) and not line[commentend] == ' ':
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661
02662 match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or
02663 Search(r'^/$', line[commentend:]) or
02664 Search(r'^!< ', line[commentend:]) or
02665 Search(r'^/< ', line[commentend:]) or
02666 Search(r'^/+ ', line[commentend:]))
02667 if not match:
02668 error(filename, linenum, 'whitespace/comments', 4,
02669 'Should have a space between // and comment')
02670 CheckComment(line[commentpos:], filename, linenum, error)
02671
02672 line = clean_lines.elided[linenum]
02673
02674
02675 line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line)
02676
02677
02678
02679
02680
02681 if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line):
02682 error(filename, linenum, 'whitespace/operators', 4,
02683 'Missing spaces around =')
02684
02685
02686
02687
02688
02689
02690
02691
02692
02693 match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line)
02694 if match:
02695 error(filename, linenum, 'whitespace/operators', 3,
02696 'Missing spaces around %s' % match.group(1))
02697
02698
02699
02700 match = Search(r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line)
02701 if (match and
02702 not (match.group(1).isdigit() and match.group(2).isdigit()) and
02703 not (match.group(1) == 'operator' and match.group(2) == ';')):
02704 error(filename, linenum, 'whitespace/operators', 3,
02705 'Missing spaces around <<')
02706 elif not Match(r'#.*include', line):
02707
02708 reduced_line = line.replace('->', '')
02709
02710
02711
02712
02713
02714 match = Search(r'[^\s<]<([^\s=<].*)', reduced_line)
02715 if (match and
02716 not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))):
02717 error(filename, linenum, 'whitespace/operators', 3,
02718 'Missing spaces around <')
02719
02720
02721
02722
02723 match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line)
02724 if (match and
02725 not FindPreviousMatchingAngleBracket(clean_lines, linenum,
02726 match.group(1))):
02727 error(filename, linenum, 'whitespace/operators', 3,
02728 'Missing spaces around >')
02729
02730
02731
02732
02733
02734
02735
02736
02737
02738
02739
02740
02741
02742 match = Search(r'>>[a-zA-Z_]', line)
02743 if match:
02744 error(filename, linenum, 'whitespace/operators', 3,
02745 'Missing spaces around >>')
02746
02747
02748 match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
02749 if match:
02750 error(filename, linenum, 'whitespace/operators', 4,
02751 'Extra space for operator %s' % match.group(1))
02752
02753
02754 match = Search(r' (if\(|for\(|while\(|switch\()', line)
02755 if match:
02756 error(filename, linenum, 'whitespace/parens', 5,
02757 'Missing space before ( in %s' % match.group(1))
02758
02759
02760
02761
02762
02763
02764 match = Search(r'\b(if|for|while|switch)\s*'
02765 r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
02766 line)
02767 if match:
02768 if len(match.group(2)) != len(match.group(4)):
02769 if not (match.group(3) == ';' and
02770 len(match.group(2)) == 1 + len(match.group(4)) or
02771 not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
02772 error(filename, linenum, 'whitespace/parens', 5,
02773 'Mismatching spaces inside () in %s' % match.group(1))
02774 if len(match.group(2)) not in [0, 1]:
02775 error(filename, linenum, 'whitespace/parens', 5,
02776 'Should have zero or one spaces inside ( and ) in %s' %
02777 match.group(1))
02778
02779
02780
02781
02782
02783
02784
02785
02786
02787
02788
02789 if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
02790 error(filename, linenum, 'whitespace/comma', 3,
02791 'Missing space after ,')
02792
02793
02794
02795
02796
02797 if Search(r';[^\s};\\)/]', line):
02798 error(filename, linenum, 'whitespace/semicolon', 3,
02799 'Missing space after ;')
02800
02801
02802 CheckSpacingForFunctionCall(filename, line, linenum, error)
02803
02804
02805
02806
02807
02808 match = Match(r'^(.*[^ ({]){', line)
02809 if match:
02810
02811
02812
02813
02814
02815
02816
02817
02818
02819
02820
02821
02822
02823
02824
02825
02826
02827
02828
02829
02830
02831
02832
02833
02834
02835
02836 (endline, endlinenum, endpos) = CloseExpression(
02837 clean_lines, linenum, len(match.group(1)))
02838 trailing_text = ''
02839 if endpos > -1:
02840 trailing_text = endline[endpos:]
02841 for offset in xrange(endlinenum + 1,
02842 min(endlinenum + 3, clean_lines.NumLines() - 1)):
02843 trailing_text += clean_lines.elided[offset]
02844 if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text):
02845 error(filename, linenum, 'whitespace/braces', 5,
02846 'Missing space before {')
02847
02848
02849 if Search(r'}else', line):
02850 error(filename, linenum, 'whitespace/braces', 5,
02851 'Missing space before else')
02852
02853
02854
02855 if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line):
02856 error(filename, linenum, 'whitespace/braces', 5,
02857 'Extra space before [')
02858
02859
02860
02861
02862 if Search(r':\s*;\s*$', line):
02863 error(filename, linenum, 'whitespace/semicolon', 5,
02864 'Semicolon defining empty statement. Use {} instead.')
02865 elif Search(r'^\s*;\s*$', line):
02866 error(filename, linenum, 'whitespace/semicolon', 5,
02867 'Line contains only semicolon. If this should be an empty statement, '
02868 'use {} instead.')
02869 elif (Search(r'\s+;\s*$', line) and
02870 not Search(r'\bfor\b', line)):
02871 error(filename, linenum, 'whitespace/semicolon', 5,
02872 'Extra space before last semicolon. If this should be an empty '
02873 'statement, use {} instead.')
02874
02875
02876
02877 if (Search('for *\(.*[^:]:[^: ]', line) or
02878 Search('for *\(.*[^: ]:[^:]', line)):
02879 error(filename, linenum, 'whitespace/forcolon', 2,
02880 'Missing space around colon in range-based for loop')
02881
02882
02883 def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
02884 """Checks for additional blank line issues related to sections.
02885
02886 Currently the only thing checked here is blank line before protected/private.
02887
02888 Args:
02889 filename: The name of the current file.
02890 clean_lines: A CleansedLines instance containing the file.
02891 class_info: A _ClassInfo objects.
02892 linenum: The number of the line to check.
02893 error: The function to call with any errors found.
02894 """
02895
02896
02897
02898
02899
02900
02901
02902
02903
02904
02905
02906 if (class_info.last_line - class_info.starting_linenum <= 24 or
02907 linenum <= class_info.starting_linenum):
02908 return
02909
02910 matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])
02911 if matched:
02912
02913
02914
02915
02916
02917
02918
02919
02920 prev_line = clean_lines.lines[linenum - 1]
02921 if (not IsBlankLine(prev_line) and
02922 not Search(r'\b(class|struct)\b', prev_line) and
02923 not Search(r'\\$', prev_line)):
02924
02925
02926
02927
02928 end_class_head = class_info.starting_linenum
02929 for i in range(class_info.starting_linenum, linenum):
02930 if Search(r'\{\s*$', clean_lines.lines[i]):
02931 end_class_head = i
02932 break
02933 if end_class_head < linenum - 1:
02934 error(filename, linenum, 'whitespace/blank_line', 3,
02935 '"%s:" should be preceded by a blank line' % matched.group(1))
02936
02937
02938 def GetPreviousNonBlankLine(clean_lines, linenum):
02939 """Return the most recent non-blank line and its line number.
02940
02941 Args:
02942 clean_lines: A CleansedLines instance containing the file contents.
02943 linenum: The number of the line to check.
02944
02945 Returns:
02946 A tuple with two elements. The first element is the contents of the last
02947 non-blank line before the current line, or the empty string if this is the
02948 first non-blank line. The second is the line number of that line, or -1
02949 if this is the first non-blank line.
02950 """
02951
02952 prevlinenum = linenum - 1
02953 while prevlinenum >= 0:
02954 prevline = clean_lines.elided[prevlinenum]
02955 if not IsBlankLine(prevline):
02956 return (prevline, prevlinenum)
02957 prevlinenum -= 1
02958 return ('', -1)
02959
02960
02961 def CheckBraces(filename, clean_lines, linenum, error):
02962 """Looks for misplaced braces (e.g. at the end of line).
02963
02964 Args:
02965 filename: The name of the current file.
02966 clean_lines: A CleansedLines instance containing the file.
02967 linenum: The number of the line to check.
02968 error: The function to call with any errors found.
02969 """
02970
02971 line = clean_lines.elided[linenum]
02972
02973 if Match(r'\s*{\s*$', line):
02974
02975
02976
02977
02978
02979
02980
02981 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
02982 if (not Search(r'[,;:}{(]\s*$', prevline) and
02983 not Match(r'\s*#', prevline)):
02984 error(filename, linenum, 'whitespace/braces', 4,
02985 '{ should almost always be at the end of the previous line')
02986
02987
02988 if Match(r'\s*else\s*', line):
02989 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
02990 if Match(r'\s*}\s*$', prevline):
02991 error(filename, linenum, 'whitespace/newline', 4,
02992 'An else should appear on the same line as the preceding }')
02993
02994
02995
02996 if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
02997 if Search(r'}\s*else if([^{]*)$', line):
02998
02999 pos = line.find('else if')
03000 pos = line.find('(', pos)
03001 if pos > 0:
03002 (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
03003 if endline[endpos:].find('{') == -1:
03004 error(filename, linenum, 'readability/braces', 5,
03005 'If an else has a brace on one side, it should have it on both')
03006 else:
03007 error(filename, linenum, 'readability/braces', 5,
03008 'If an else has a brace on one side, it should have it on both')
03009
03010
03011 if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
03012 error(filename, linenum, 'whitespace/newline', 4,
03013 'Else clause should never be on same line as else (use 2 lines)')
03014
03015
03016 if Match(r'\s*do [^\s{]', line):
03017 error(filename, linenum, 'whitespace/newline', 4,
03018 'do/while clauses should not be on a single line')
03019
03020
03021
03022
03023
03024
03025
03026
03027
03028
03029
03030
03031
03032
03033
03034
03035
03036
03037
03038
03039
03040
03041
03042
03043
03044
03045
03046
03047
03048
03049
03050
03051
03052
03053
03054
03055
03056
03057
03058
03059
03060
03061
03062
03063
03064 match = Match(r'^(.*\)\s*)\{', line)
03065 if match:
03066
03067
03068
03069
03070
03071
03072
03073
03074
03075
03076
03077
03078
03079
03080
03081
03082
03083
03084
03085
03086
03087
03088
03089 closing_brace_pos = match.group(1).rfind(')')
03090 opening_parenthesis = ReverseCloseExpression(
03091 clean_lines, linenum, closing_brace_pos)
03092 if opening_parenthesis[2] > -1:
03093 line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
03094 macro = Search(r'\b([A-Z_]+)\s*$', line_prefix)
03095 if ((macro and
03096 macro.group(1) not in (
03097 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
03098 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
03099 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
03100 Search(r'\s+=\s*$', line_prefix)):
03101 match = None
03102
03103 else:
03104
03105 match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
03106 if not match:
03107
03108
03109
03110
03111
03112
03113
03114
03115 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
03116 if prevline and Search(r'[;{}]\s*$', prevline):
03117 match = Match(r'^(\s*)\{', line)
03118
03119
03120 if match:
03121 (endline, endlinenum, endpos) = CloseExpression(
03122 clean_lines, linenum, len(match.group(1)))
03123 if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
03124
03125
03126
03127
03128
03129
03130
03131 error(filename, endlinenum, 'readability/braces', 4,
03132 "You don't need a ; after a }")
03133
03134
03135 def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
03136 """Look for empty loop/conditional body with only a single semicolon.
03137
03138 Args:
03139 filename: The name of the current file.
03140 clean_lines: A CleansedLines instance containing the file.
03141 linenum: The number of the line to check.
03142 error: The function to call with any errors found.
03143 """
03144
03145
03146
03147
03148
03149
03150
03151 line = clean_lines.elided[linenum]
03152 matched = Match(r'\s*(for|while|if)\s*\(', line)
03153 if matched:
03154
03155 (end_line, end_linenum, end_pos) = CloseExpression(
03156 clean_lines, linenum, line.find('('))
03157
03158
03159
03160
03161 if end_pos >= 0 and Match(r';', end_line[end_pos:]):
03162 if matched.group(1) == 'if':
03163 error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
03164 'Empty conditional bodies should use {}')
03165 else:
03166 error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
03167 'Empty loop bodies should use {} or continue')
03168
03169
03170 def CheckCheck(filename, clean_lines, linenum, error):
03171 """Checks the use of CHECK and EXPECT macros.
03172
03173 Args:
03174 filename: The name of the current file.
03175 clean_lines: A CleansedLines instance containing the file.
03176 linenum: The number of the line to check.
03177 error: The function to call with any errors found.
03178 """
03179
03180
03181 lines = clean_lines.elided
03182 check_macro = None
03183 start_pos = -1
03184 for macro in _CHECK_MACROS:
03185 i = lines[linenum].find(macro)
03186 if i >= 0:
03187 check_macro = macro
03188
03189
03190
03191
03192
03193 matched = Match(r'^(.*\b' + check_macro + r'\s*)\(', lines[linenum])
03194 if not matched:
03195 continue
03196 start_pos = len(matched.group(1))
03197 break
03198 if not check_macro or start_pos < 0:
03199
03200 return
03201
03202
03203 (last_line, end_line, end_pos) = CloseExpression(
03204 clean_lines, linenum, start_pos)
03205 if end_pos < 0:
03206 return
03207 if linenum == end_line:
03208 expression = lines[linenum][start_pos + 1:end_pos - 1]
03209 else:
03210 expression = lines[linenum][start_pos + 1:]
03211 for i in xrange(linenum + 1, end_line):
03212 expression += lines[i]
03213 expression += last_line[0:end_pos - 1]
03214
03215
03216
03217
03218 lhs = ''
03219 rhs = ''
03220 operator = None
03221 while expression:
03222 matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'
03223 r'==|!=|>=|>|<=|<|\()(.*)$', expression)
03224 if matched:
03225 token = matched.group(1)
03226 if token == '(':
03227
03228 expression = matched.group(2)
03229 (end, _) = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
03230 if end < 0:
03231 return
03232 lhs += '(' + expression[0:end]
03233 expression = expression[end:]
03234 elif token in ('&&', '||'):
03235
03236
03237
03238
03239
03240 return
03241 elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):
03242
03243 lhs += token
03244 expression = matched.group(2)
03245 else:
03246
03247 operator = token
03248 rhs = matched.group(2)
03249 break
03250 else:
03251
03252
03253
03254
03255
03256 matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)
03257 if not matched:
03258 matched = Match(r'^(\s*\S)(.*)$', expression)
03259 if not matched:
03260 break
03261 lhs += matched.group(1)
03262 expression = matched.group(2)
03263
03264
03265 if not (lhs and operator and rhs):
03266 return
03267
03268
03269
03270 if rhs.find('&&') > -1 or rhs.find('||') > -1:
03271 return
03272
03273
03274
03275
03276
03277
03278
03279 lhs = lhs.strip()
03280 rhs = rhs.strip()
03281 match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'
03282 if Match(match_constant, lhs) or Match(match_constant, rhs):
03283
03284
03285
03286
03287
03288
03289
03290
03291 error(filename, linenum, 'readability/check', 2,
03292 'Consider using %s instead of %s(a %s b)' % (
03293 _CHECK_REPLACEMENT[check_macro][operator],
03294 check_macro, operator))
03295
03296
03297 def CheckAltTokens(filename, clean_lines, linenum, error):
03298 """Check alternative keywords being used in boolean expressions.
03299
03300 Args:
03301 filename: The name of the current file.
03302 clean_lines: A CleansedLines instance containing the file.
03303 linenum: The number of the line to check.
03304 error: The function to call with any errors found.
03305 """
03306 line = clean_lines.elided[linenum]
03307
03308
03309 if Match(r'^\s*#', line):
03310 return
03311
03312
03313
03314
03315
03316
03317
03318
03319
03320 if line.find('/*') >= 0 or line.find('*/') >= 0:
03321 return
03322
03323 for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
03324 error(filename, linenum, 'readability/alt_tokens', 2,
03325 'Use operator %s instead of %s' % (
03326 _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))
03327
03328
03329 def GetLineWidth(line):
03330 """Determines the width of the line in column positions.
03331
03332 Args:
03333 line: A string, which may be a Unicode string.
03334
03335 Returns:
03336 The width of the line in column positions, accounting for Unicode
03337 combining characters and wide characters.
03338 """
03339 if isinstance(line, unicode):
03340 width = 0
03341 for uc in unicodedata.normalize('NFC', line):
03342 if unicodedata.east_asian_width(uc) in ('W', 'F'):
03343 width += 2
03344 elif not unicodedata.combining(uc):
03345 width += 1
03346 return width
03347 else:
03348 return len(line)
03349
03350
03351 def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
03352 error):
03353 """Checks rules from the 'C++ style rules' section of cppguide.html.
03354
03355 Most of these rules are hard to test (naming, comment style), but we
03356 do what we can. In particular we check for 2-space indents, line lengths,
03357 tab usage, spaces inside code, etc.
03358
03359 Args:
03360 filename: The name of the current file.
03361 clean_lines: A CleansedLines instance containing the file.
03362 linenum: The number of the line to check.
03363 file_extension: The extension (without the dot) of the filename.
03364 nesting_state: A _NestingState instance which maintains information about
03365 the current stack of nested blocks being parsed.
03366 error: The function to call with any errors found.
03367 """
03368
03369
03370
03371
03372 raw_lines = clean_lines.lines_without_raw_strings
03373 line = raw_lines[linenum]
03374
03375 if line.find('\t') != -1:
03376 error(filename, linenum, 'whitespace/tab', 1,
03377 'Tab found; better to use spaces')
03378
03379
03380
03381
03382
03383
03384
03385
03386
03387
03388
03389
03390
03391 initial_spaces = 0
03392 cleansed_line = clean_lines.elided[linenum]
03393 while initial_spaces < len(line) and line[initial_spaces] == ' ':
03394 initial_spaces += 1
03395 if line and line[-1].isspace():
03396 error(filename, linenum, 'whitespace/end_of_line', 4,
03397 'Line ends in whitespace. Consider deleting these extra spaces.')
03398
03399 elif ((initial_spaces == 1 or initial_spaces == 3) and
03400 not Match(r'\s*\w+\s*:\s*$', cleansed_line)):
03401 error(filename, linenum, 'whitespace/indent', 3,
03402 'Weird number of spaces at line-start. '
03403 'Are you using a 2-space indent?')
03404
03405
03406 is_header_guard = False
03407 if file_extension == 'h':
03408 cppvar = GetHeaderGuardCPPVariable(filename)
03409 if (line.startswith('#ifndef %s' % cppvar) or
03410 line.startswith('#define %s' % cppvar) or
03411 line.startswith('#endif // %s' % cppvar)):
03412 is_header_guard = True
03413
03414
03415
03416
03417
03418
03419
03420
03421 if (not line.startswith('#include') and not is_header_guard and
03422 not Match(r'^\s*//.*http(s?)://\S*$', line) and
03423 not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
03424 line_width = GetLineWidth(line)
03425 extended_length = int((_line_length * 1.25))
03426 if line_width > extended_length:
03427 error(filename, linenum, 'whitespace/line_length', 4,
03428 'Lines should very rarely be longer than %i characters' %
03429 extended_length)
03430 elif line_width > _line_length:
03431 error(filename, linenum, 'whitespace/line_length', 2,
03432 'Lines should be <= %i characters long' % _line_length)
03433
03434 if (cleansed_line.count(';') > 1 and
03435
03436 cleansed_line.find('for') == -1 and
03437 (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
03438 GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
03439
03440 not ((cleansed_line.find('case ') != -1 or
03441 cleansed_line.find('default:') != -1) and
03442 cleansed_line.find('break;') != -1)):
03443 error(filename, linenum, 'whitespace/newline', 0,
03444 'More than one command on the same line')
03445
03446
03447 CheckBraces(filename, clean_lines, linenum, error)
03448 CheckEmptyBlockBody(filename, clean_lines, linenum, error)
03449 CheckAccess(filename, clean_lines, linenum, nesting_state, error)
03450 CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
03451 CheckCheck(filename, clean_lines, linenum, error)
03452 CheckAltTokens(filename, clean_lines, linenum, error)
03453 classinfo = nesting_state.InnermostClass()
03454 if classinfo:
03455 CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)
03456
03457
03458 _RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"')
03459 _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
03460
03461
03462
03463
03464
03465 _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
03466
03467
03468 def _DropCommonSuffixes(filename):
03469 """Drops common suffixes like _test.cc or -inl.h from filename.
03470
03471 For example:
03472 >>> _DropCommonSuffixes('foo/foo-inl.h')
03473 'foo/foo'
03474 >>> _DropCommonSuffixes('foo/bar/foo.cc')
03475 'foo/bar/foo'
03476 >>> _DropCommonSuffixes('foo/foo_internal.h')
03477 'foo/foo'
03478 >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')
03479 'foo/foo_unusualinternal'
03480
03481 Args:
03482 filename: The input filename.
03483
03484 Returns:
03485 The filename with the common suffix removed.
03486 """
03487 for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
03488 'inl.h', 'impl.h', 'internal.h'):
03489 if (filename.endswith(suffix) and len(filename) > len(suffix) and
03490 filename[-len(suffix) - 1] in ('-', '_')):
03491 return filename[:-len(suffix) - 1]
03492 return os.path.splitext(filename)[0]
03493
03494
03495 def _IsTestFilename(filename):
03496 """Determines if the given filename has a suffix that identifies it as a test.
03497
03498 Args:
03499 filename: The input filename.
03500
03501 Returns:
03502 True if 'filename' looks like a test, False otherwise.
03503 """
03504 if (filename.endswith('_test.cc') or
03505 filename.endswith('_unittest.cc') or
03506 filename.endswith('_regtest.cc')):
03507 return True
03508 else:
03509 return False
03510
03511
03512 def _ClassifyInclude(fileinfo, include, is_system):
03513 """Figures out what kind of header 'include' is.
03514
03515 Args:
03516 fileinfo: The current file cpplint is running over. A FileInfo instance.
03517 include: The path to a #included file.
03518 is_system: True if the #include used <> rather than "".
03519
03520 Returns:
03521 One of the _XXX_HEADER constants.
03522
03523 For example:
03524 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)
03525 _C_SYS_HEADER
03526 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
03527 _CPP_SYS_HEADER
03528 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
03529 _LIKELY_MY_HEADER
03530 >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
03531 ... 'bar/foo_other_ext.h', False)
03532 _POSSIBLE_MY_HEADER
03533 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)
03534 _OTHER_HEADER
03535 """
03536
03537
03538 is_cpp_h = include in _CPP_HEADERS
03539
03540 if is_system:
03541 if is_cpp_h:
03542 return _CPP_SYS_HEADER
03543 else:
03544 return _C_SYS_HEADER
03545
03546
03547
03548
03549 target_dir, target_base = (
03550 os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))
03551 include_dir, include_base = os.path.split(_DropCommonSuffixes(include))
03552 if target_base == include_base and (
03553 include_dir == target_dir or
03554 include_dir == os.path.normpath(target_dir + '/../public')):
03555 return _LIKELY_MY_HEADER
03556
03557
03558
03559
03560
03561 target_first_component = _RE_FIRST_COMPONENT.match(target_base)
03562 include_first_component = _RE_FIRST_COMPONENT.match(include_base)
03563 if (target_first_component and include_first_component and
03564 target_first_component.group(0) ==
03565 include_first_component.group(0)):
03566 return _POSSIBLE_MY_HEADER
03567
03568 return _OTHER_HEADER
03569
03570
03571
03572 def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
03573 """Check rules that are applicable to #include lines.
03574
03575 Strings on #include lines are NOT removed from elided line, to make
03576 certain tasks easier. However, to prevent false positives, checks
03577 applicable to #include lines in CheckLanguage must be put here.
03578
03579 Args:
03580 filename: The name of the current file.
03581 clean_lines: A CleansedLines instance containing the file.
03582 linenum: The number of the line to check.
03583 include_state: An _IncludeState instance in which the headers are inserted.
03584 error: The function to call with any errors found.
03585 """
03586 fileinfo = FileInfo(filename)
03587
03588 line = clean_lines.lines[linenum]
03589
03590
03591 if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line):
03592 error(filename, linenum, 'build/include', 4,
03593 'Include the directory when naming .h files')
03594
03595
03596
03597
03598 match = _RE_PATTERN_INCLUDE.search(line)
03599 if match:
03600 include = match.group(2)
03601 is_system = (match.group(1) == '<')
03602 if include in include_state:
03603 error(filename, linenum, 'build/include', 4,
03604 '"%s" already included at %s:%s' %
03605 (include, filename, include_state[include]))
03606 else:
03607 include_state[include] = linenum
03608
03609
03610
03611
03612
03613
03614
03615
03616
03617
03618
03619
03620 error_message = include_state.CheckNextIncludeOrder(
03621 _ClassifyInclude(fileinfo, include, is_system))
03622 if error_message:
03623 error(filename, linenum, 'build/include_order', 4,
03624 '%s. Should be: %s.h, c system, c++ system, other.' %
03625 (error_message, fileinfo.BaseName()))
03626 canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
03627 if not include_state.IsInAlphabeticalOrder(
03628 clean_lines, linenum, canonical_include):
03629 error(filename, linenum, 'build/include_alpha', 4,
03630 'Include "%s" not in alphabetical order' % include)
03631 include_state.SetLastHeader(canonical_include)
03632
03633
03634 match = _RE_PATTERN_INCLUDE.match(line)
03635 if match:
03636 include = match.group(2)
03637 if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include):
03638
03639 if not _IsTestFilename(filename):
03640 error(filename, linenum, 'readability/streams', 3,
03641 'Streams are highly discouraged.')
03642
03643
03644 def _GetTextInside(text, start_pattern):
03645 r"""Retrieves all the text between matching open and close parentheses.
03646
03647 Given a string of lines and a regular expression string, retrieve all the text
03648 following the expression and between opening punctuation symbols like
03649 (, [, or {, and the matching close-punctuation symbol. This properly nested
03650 occurrences of the punctuations, so for the text like
03651 printf(a(), b(c()));
03652 a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
03653 start_pattern must match string having an open punctuation symbol at the end.
03654
03655 Args:
03656 text: The lines to extract text. Its comments and strings must be elided.
03657 It can be single line and can span multiple lines.
03658 start_pattern: The regexp string indicating where to start extracting
03659 the text.
03660 Returns:
03661 The extracted text.
03662 None if either the opening string or ending punctuation could not be found.
03663 """
03664
03665
03666
03667
03668 matching_punctuation = {'(': ')', '{': '}', '[': ']'}
03669 closing_punctuation = set(matching_punctuation.itervalues())
03670
03671
03672 match = re.search(start_pattern, text, re.M)
03673 if not match:
03674 return None
03675 start_position = match.end(0)
03676
03677 assert start_position > 0, (
03678 'start_pattern must ends with an opening punctuation.')
03679 assert text[start_position - 1] in matching_punctuation, (
03680 'start_pattern must ends with an opening punctuation.')
03681
03682 punctuation_stack = [matching_punctuation[text[start_position - 1]]]
03683 position = start_position
03684 while punctuation_stack and position < len(text):
03685 if text[position] == punctuation_stack[-1]:
03686 punctuation_stack.pop()
03687 elif text[position] in closing_punctuation:
03688
03689 return None
03690 elif text[position] in matching_punctuation:
03691 punctuation_stack.append(matching_punctuation[text[position]])
03692 position += 1
03693 if punctuation_stack:
03694
03695 return None
03696
03697 return text[start_position:position - 1]
03698
03699
03700
03701
03702
03703
03704
03705
03706
03707
03708
03709 _RE_PATTERN_IDENT = r'[_a-zA-Z]\w*'
03710 _RE_PATTERN_TYPE = (
03711 r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
03712 r'(?:\w|'
03713 r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
03714 r'::)+')
03715
03716 _RE_PATTERN_REF_PARAM = re.compile(
03717 r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
03718 r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
03719
03720
03721 _RE_PATTERN_CONST_REF_PARAM = (
03722 r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
03723 r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
03724
03725
03726 def CheckLanguage(filename, clean_lines, linenum, file_extension,
03727 include_state, nesting_state, error):
03728 """Checks rules from the 'C++ language rules' section of cppguide.html.
03729
03730 Some of these rules are hard to test (function overloading, using
03731 uint32 inappropriately), but we do the best we can.
03732
03733 Args:
03734 filename: The name of the current file.
03735 clean_lines: A CleansedLines instance containing the file.
03736 linenum: The number of the line to check.
03737 file_extension: The extension (without the dot) of the filename.
03738 include_state: An _IncludeState instance in which the headers are inserted.
03739 nesting_state: A _NestingState instance which maintains information about
03740 the current stack of nested blocks being parsed.
03741 error: The function to call with any errors found.
03742 """
03743
03744
03745 line = clean_lines.elided[linenum]
03746 if not line:
03747 return
03748
03749 match = _RE_PATTERN_INCLUDE.search(line)
03750 if match:
03751 CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
03752 return
03753
03754
03755
03756 if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line):
03757 include_state.ResetSection()
03758
03759
03760 fullname = os.path.abspath(filename).replace('\\', '/')
03761
03762
03763
03764
03765
03766
03767
03768 match = Search(
03769 r'(\bnew\s+)?\b'
03770 r'(int|float|double|bool|char|int32|uint32|int64|uint64)'
03771 r'(\([^)].*)', line)
03772 if match:
03773 matched_new = match.group(1)
03774 matched_type = match.group(2)
03775 matched_funcptr = match.group(3)
03776
03777
03778
03779
03780
03781
03782
03783
03784
03785
03786
03787 if (matched_new is None and
03788 not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
03789 Search(r'\bMockCallback<.*>', line) or
03790 Search(r'\bstd::function<.*>', line)) and
03791 not (matched_funcptr and
03792 Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',
03793 matched_funcptr))):
03794
03795
03796
03797
03798
03799
03800
03801
03802
03803 if (linenum < 2 or
03804 not (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',
03805 clean_lines.elided[linenum - 1]) or
03806 Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',
03807 clean_lines.elided[linenum - 2]))):
03808 error(filename, linenum, 'readability/casting', 4,
03809 'Using deprecated casting style. '
03810 'Use static_cast<%s>(...) instead' %
03811 matched_type)
03812
03813 CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
03814 'static_cast',
03815 r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
03816
03817
03818
03819
03820
03821 if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
03822 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error):
03823 pass
03824 else:
03825
03826 CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
03827 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error)
03828
03829
03830
03831
03832 match = Search(
03833 r'(?:&\(([^)]+)\)[\w(])|'
03834 r'(?:&(static|dynamic|down|reinterpret)_cast\b)', line)
03835 if match and match.group(1) != '*':
03836 error(filename, linenum, 'runtime/casting', 4,
03837 ('Are you taking an address of a cast? '
03838 'This is dangerous: could be a temp var. '
03839 'Take the address before doing the cast, rather than after'))
03840
03841
03842
03843
03844 if linenum + 1 < clean_lines.NumLines():
03845 extended_line = line + clean_lines.elided[linenum + 1]
03846 else:
03847 extended_line = line
03848
03849
03850
03851
03852 match = Match(
03853 r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)',
03854 line)
03855
03856
03857
03858
03859
03860
03861
03862 if (match and
03863 not Search(r'\boperator\W', line) and
03864 not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3))):
03865 error(filename, linenum, 'runtime/string', 4,
03866 'For a static/global string constant, use a C style string instead: '
03867 '"%schar %s[]".' %
03868 (match.group(1), match.group(2)))
03869
03870 if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line):
03871 error(filename, linenum, 'runtime/init', 4,
03872 'You seem to be initializing a member variable with itself.')
03873
03874 if file_extension == 'h':
03875
03876
03877
03878
03879
03880 pass
03881
03882
03883
03884 if Search(r'\bshort port\b', line):
03885 if not Search(r'\bunsigned short port\b', line):
03886 error(filename, linenum, 'runtime/int', 4,
03887 'Use "unsigned short" for ports, not "short"')
03888 else:
03889 match = Search(r'\b(short|long(?! +double)|long long)\b', line)
03890 if match:
03891 error(filename, linenum, 'runtime/int', 4,
03892 'Use int16/int64/etc, rather than the C type %s' % match.group(1))
03893
03894
03895 match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
03896 if match and match.group(2) != '0':
03897
03898 error(filename, linenum, 'runtime/printf', 3,
03899 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
03900 'to snprintf.' % (match.group(1), match.group(2)))
03901
03902
03903 if Search(r'\bsprintf\b', line):
03904 error(filename, linenum, 'runtime/printf', 5,
03905 'Never use sprintf. Use snprintf instead.')
03906 match = Search(r'\b(strcpy|strcat)\b', line)
03907 if match:
03908 error(filename, linenum, 'runtime/printf', 4,
03909 'Almost always, snprintf is better than %s' % match.group(1))
03910
03911
03912
03913
03914
03915
03916
03917 if Search(r'\boperator\s*&\s*\(\s*\)', line):
03918 error(filename, linenum, 'runtime/operator', 4,
03919 'Unary operator& is dangerous. Do not use it.')
03920
03921
03922
03923 if Search(r'\}\s*if\s*\(', line):
03924 error(filename, linenum, 'readability/braces', 4,
03925 'Did you mean "else if"? If not, start a new line for "if".')
03926
03927
03928
03929
03930
03931
03932
03933
03934 printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')
03935 if printf_args:
03936 match = Match(r'([\w.\->()]+)$', printf_args)
03937 if match and match.group(1) != '__VA_ARGS__':
03938 function_name = re.search(r'\b((?:string)?printf)\s*\(',
03939 line, re.I).group(1)
03940 error(filename, linenum, 'runtime/printf', 4,
03941 'Potential format string bug. Do %s("%%s", %s) instead.'
03942 % (function_name, match.group(1)))
03943
03944
03945 match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
03946 if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):
03947 error(filename, linenum, 'runtime/memset', 4,
03948 'Did you mean "memset(%s, 0, %s)"?'
03949 % (match.group(1), match.group(2)))
03950
03951 if Search(r'\busing namespace\b', line):
03952 error(filename, linenum, 'build/namespaces', 5,
03953 'Do not use namespace using-directives. '
03954 'Use using-declarations instead.')
03955
03956
03957 match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
03958 if (match and match.group(2) != 'return' and match.group(2) != 'delete' and
03959 match.group(3).find(']') == -1):
03960
03961
03962
03963 tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))
03964 is_const = True
03965 skip_next = False
03966 for tok in tokens:
03967 if skip_next:
03968 skip_next = False
03969 continue
03970
03971 if Search(r'sizeof\(.+\)', tok): continue
03972 if Search(r'arraysize\(\w+\)', tok): continue
03973
03974 tok = tok.lstrip('(')
03975 tok = tok.rstrip(')')
03976 if not tok: continue
03977 if Match(r'\d+', tok): continue
03978 if Match(r'0[xX][0-9a-fA-F]+', tok): continue
03979 if Match(r'k[A-Z0-9]\w*', tok): continue
03980 if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
03981 if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
03982
03983
03984
03985 if tok.startswith('sizeof'):
03986 skip_next = True
03987 continue
03988 is_const = False
03989 break
03990 if not is_const:
03991 error(filename, linenum, 'runtime/arrays', 1,
03992 'Do not use variable-length arrays. Use an appropriately named '
03993 "('k' followed by CamelCase) compile-time constant for the size.")
03994
03995
03996
03997
03998 match = Match(
03999 (r'\s*'
04000 r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))'
04001 r'\(.*\);$'),
04002 line)
04003 if match and linenum + 1 < clean_lines.NumLines():
04004 next_line = clean_lines.elided[linenum + 1]
04005
04006
04007
04008
04009
04010
04011 if not Search(r'^\s*}[\w\*,\s]*;', next_line):
04012 error(filename, linenum, 'readability/constructors', 3,
04013 match.group(1) + ' should be the last thing in the class')
04014
04015
04016
04017
04018 if (file_extension == 'h'
04019 and Search(r'\bnamespace\s*{', line)
04020 and line[-1] != '\\'):
04021 error(filename, linenum, 'build/namespaces', 4,
04022 'Do not use unnamed namespaces in header files. See '
04023 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
04024 ' for more information.')
04025
04026 def CheckForNonConstReference(filename, clean_lines, linenum,
04027 nesting_state, error):
04028 """Check for non-const references.
04029
04030 Separate from CheckLanguage since it scans backwards from current
04031 line, instead of scanning forward.
04032
04033 Args:
04034 filename: The name of the current file.
04035 clean_lines: A CleansedLines instance containing the file.
04036 linenum: The number of the line to check.
04037 nesting_state: A _NestingState instance which maintains information about
04038 the current stack of nested blocks being parsed.
04039 error: The function to call with any errors found.
04040 """
04041
04042 line = clean_lines.elided[linenum]
04043 if '&' not in line:
04044 return
04045
04046
04047
04048
04049
04050
04051
04052
04053
04054
04055
04056
04057
04058
04059
04060
04061
04062 if linenum > 1:
04063 previous = None
04064 if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
04065
04066 previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
04067 clean_lines.elided[linenum - 1])
04068 elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
04069
04070 previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
04071 clean_lines.elided[linenum - 1])
04072 if previous:
04073 line = previous.group(1) + line.lstrip()
04074 else:
04075
04076 endpos = line.rfind('>')
04077 if endpos > -1:
04078 (_, startline, startpos) = ReverseCloseExpression(
04079 clean_lines, linenum, endpos)
04080 if startpos > -1 and startline < linenum:
04081
04082
04083 line = ''
04084 for i in xrange(startline, linenum + 1):
04085 line += clean_lines.elided[i].strip()
04086
04087
04088
04089
04090
04091
04092
04093
04094
04095
04096 check_params = False
04097 if not nesting_state.stack:
04098 check_params = True
04099 elif (isinstance(nesting_state.stack[-1], _ClassInfo) or
04100 isinstance(nesting_state.stack[-1], _NamespaceInfo)):
04101 check_params = True
04102 elif Match(r'.*{\s*$', line):
04103 if (len(nesting_state.stack) == 1 or
04104 isinstance(nesting_state.stack[-2], _ClassInfo) or
04105 isinstance(nesting_state.stack[-2], _NamespaceInfo)):
04106 check_params = True
04107
04108
04109
04110
04111
04112
04113 whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
04114 r'operator\s*[<>][<>]|'
04115 r'static_assert|COMPILE_ASSERT'
04116 r')\s*\(')
04117 if Search(whitelisted_functions, line):
04118 check_params = False
04119 elif not Search(r'\S+\([^)]*$', line):
04120
04121
04122
04123 for i in xrange(2):
04124 if (linenum > i and
04125 Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
04126 check_params = False
04127 break
04128
04129 if check_params:
04130 decls = ReplaceAll(r'{[^}]*}', ' ', line)
04131 for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
04132 if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
04133 error(filename, linenum, 'runtime/references', 2,
04134 'Is this a non-const reference? '
04135 'If so, make const or use a pointer: ' +
04136 ReplaceAll(' *<', '<', parameter))
04137
04138
04139 def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
04140 error):
04141 """Checks for a C-style cast by looking for the pattern.
04142
04143 Args:
04144 filename: The name of the current file.
04145 linenum: The number of the line to check.
04146 line: The line of code to check.
04147 raw_line: The raw line of code to check, with comments.
04148 cast_type: The string for the C++ cast to recommend. This is either
04149 reinterpret_cast, static_cast, or const_cast, depending.
04150 pattern: The regular expression used to find C-style casts.
04151 error: The function to call with any errors found.
04152
04153 Returns:
04154 True if an error was emitted.
04155 False otherwise.
04156 """
04157 match = Search(pattern, line)
04158 if not match:
04159 return False
04160
04161
04162 sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1])
04163 if sizeof_match:
04164 return False
04165
04166
04167 if (line[0:match.start(1) - 1].endswith(' operator++') or
04168 line[0:match.start(1) - 1].endswith(' operator--')):
04169 return False
04170
04171
04172
04173
04174
04175
04176
04177
04178
04179
04180
04181
04182
04183
04184
04185
04186
04187
04188
04189
04190
04191
04192
04193
04194 remainder = line[match.end(0):]
04195 if Match(r'^\s*(?:;|const\b|throw\b|=|>|\{|\))', remainder):
04196
04197
04198
04199 if Match(r'^\s*>', remainder):
04200 return False
04201
04202
04203
04204
04205
04206 matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder)
04207 if matched_zero and matched_zero.group(1) != '0':
04208 return False
04209
04210
04211
04212 if Match(r'.*\)\s*$', line[0:match.start(0)]):
04213 return False
04214
04215
04216
04217 if '/*' in raw_line:
04218 return False
04219
04220
04221 error(filename, linenum, 'readability/function', 3,
04222 'All parameters should be named in a function')
04223 return True
04224
04225
04226 error(filename, linenum, 'readability/casting', 4,
04227 'Using C-style cast. Use %s<%s>(...) instead' %
04228 (cast_type, match.group(1)))
04229
04230 return True
04231
04232
04233 _HEADERS_CONTAINING_TEMPLATES = (
04234 ('<deque>', ('deque',)),
04235 ('<functional>', ('unary_function', 'binary_function',
04236 'plus', 'minus', 'multiplies', 'divides', 'modulus',
04237 'negate',
04238 'equal_to', 'not_equal_to', 'greater', 'less',
04239 'greater_equal', 'less_equal',
04240 'logical_and', 'logical_or', 'logical_not',
04241 'unary_negate', 'not1', 'binary_negate', 'not2',
04242 'bind1st', 'bind2nd',
04243 'pointer_to_unary_function',
04244 'pointer_to_binary_function',
04245 'ptr_fun',
04246 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
04247 'mem_fun_ref_t',
04248 'const_mem_fun_t', 'const_mem_fun1_t',
04249 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
04250 'mem_fun_ref',
04251 )),
04252 ('<limits>', ('numeric_limits',)),
04253 ('<list>', ('list',)),
04254 ('<map>', ('map', 'multimap',)),
04255 ('<memory>', ('allocator',)),
04256 ('<queue>', ('queue', 'priority_queue',)),
04257 ('<set>', ('set', 'multiset',)),
04258 ('<stack>', ('stack',)),
04259 ('<string>', ('char_traits', 'basic_string',)),
04260 ('<utility>', ('pair',)),
04261 ('<vector>', ('vector',)),
04262
04263
04264
04265 ('<hash_map>', ('hash_map', 'hash_multimap',)),
04266 ('<hash_set>', ('hash_set', 'hash_multiset',)),
04267 ('<slist>', ('slist',)),
04268 )
04269
04270 _RE_PATTERN_STRING = re.compile(r'\bstring\b')
04271
04272 _re_pattern_algorithm_header = []
04273 for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap',
04274 'transform'):
04275
04276
04277 _re_pattern_algorithm_header.append(
04278 (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
04279 _template,
04280 '<algorithm>'))
04281
04282 _re_pattern_templates = []
04283 for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
04284 for _template in _templates:
04285 _re_pattern_templates.append(
04286 (re.compile(r'(<|\b)' + _template + r'\s*<'),
04287 _template + '<>',
04288 _header))
04289
04290
04291 def FilesBelongToSameModule(filename_cc, filename_h):
04292 """Check if these two filenames belong to the same module.
04293
04294 The concept of a 'module' here is a as follows:
04295 foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
04296 same 'module' if they are in the same directory.
04297 some/path/public/xyzzy and some/path/internal/xyzzy are also considered
04298 to belong to the same module here.
04299
04300 If the filename_cc contains a longer path than the filename_h, for example,
04301 '/absolute/path/to/base/sysinfo.cc', and this file would include
04302 'base/sysinfo.h', this function also produces the prefix needed to open the
04303 header. This is used by the caller of this function to more robustly open the
04304 header file. We don't have access to the real include paths in this context,
04305 so we need this guesswork here.
04306
04307 Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
04308 according to this implementation. Because of this, this function gives
04309 some false positives. This should be sufficiently rare in practice.
04310
04311 Args:
04312 filename_cc: is the path for the .cc file
04313 filename_h: is the path for the header path
04314
04315 Returns:
04316 Tuple with a bool and a string:
04317 bool: True if filename_cc and filename_h belong to the same module.
04318 string: the additional prefix needed to open the header file.
04319 """
04320
04321 if not filename_cc.endswith('.cc'):
04322 return (False, '')
04323 filename_cc = filename_cc[:-len('.cc')]
04324 if filename_cc.endswith('_unittest'):
04325 filename_cc = filename_cc[:-len('_unittest')]
04326 elif filename_cc.endswith('_test'):
04327 filename_cc = filename_cc[:-len('_test')]
04328 filename_cc = filename_cc.replace('/public/', '/')
04329 filename_cc = filename_cc.replace('/internal/', '/')
04330
04331 if not filename_h.endswith('.h'):
04332 return (False, '')
04333 filename_h = filename_h[:-len('.h')]
04334 if filename_h.endswith('-inl'):
04335 filename_h = filename_h[:-len('-inl')]
04336 filename_h = filename_h.replace('/public/', '/')
04337 filename_h = filename_h.replace('/internal/', '/')
04338
04339 files_belong_to_same_module = filename_cc.endswith(filename_h)
04340 common_path = ''
04341 if files_belong_to_same_module:
04342 common_path = filename_cc[:-len(filename_h)]
04343 return files_belong_to_same_module, common_path
04344
04345
04346 def UpdateIncludeState(filename, include_state, io=codecs):
04347 """Fill up the include_state with new includes found from the file.
04348
04349 Args:
04350 filename: the name of the header to read.
04351 include_state: an _IncludeState instance in which the headers are inserted.
04352 io: The io factory to use to read the file. Provided for testability.
04353
04354 Returns:
04355 True if a header was succesfully added. False otherwise.
04356 """
04357 headerfile = None
04358 try:
04359 headerfile = io.open(filename, 'r', 'utf8', 'replace')
04360 except IOError:
04361 return False
04362 linenum = 0
04363 for line in headerfile:
04364 linenum += 1
04365 clean_line = CleanseComments(line)
04366 match = _RE_PATTERN_INCLUDE.search(clean_line)
04367 if match:
04368 include = match.group(2)
04369
04370
04371 include_state.setdefault(include, '%s:%d' % (filename, linenum))
04372 return True
04373
04374
04375 def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
04376 io=codecs):
04377 """Reports for missing stl includes.
04378
04379 This function will output warnings to make sure you are including the headers
04380 necessary for the stl containers and functions that you use. We only give one
04381 reason to include a header. For example, if you use both equal_to<> and
04382 less<> in a .h file, only one (the latter in the file) of these will be
04383 reported as a reason to include the <functional>.
04384
04385 Args:
04386 filename: The name of the current file.
04387 clean_lines: A CleansedLines instance containing the file.
04388 include_state: An _IncludeState instance.
04389 error: The function to call with any errors found.
04390 io: The IO factory to use to read the header file. Provided for unittest
04391 injection.
04392 """
04393 required = {}
04394
04395
04396 for linenum in xrange(clean_lines.NumLines()):
04397 line = clean_lines.elided[linenum]
04398 if not line or line[0] == '#':
04399 continue
04400
04401
04402 matched = _RE_PATTERN_STRING.search(line)
04403 if matched:
04404
04405
04406 prefix = line[:matched.start()]
04407 if prefix.endswith('std::') or not prefix.endswith('::'):
04408 required['<string>'] = (linenum, 'string')
04409
04410 for pattern, template, header in _re_pattern_algorithm_header:
04411 if pattern.search(line):
04412 required[header] = (linenum, template)
04413
04414
04415 if not '<' in line:
04416 continue
04417
04418 for pattern, template, header in _re_pattern_templates:
04419 if pattern.search(line):
04420 required[header] = (linenum, template)
04421
04422
04423
04424
04425 include_state = include_state.copy()
04426
04427
04428 header_found = False
04429
04430
04431 abs_filename = FileInfo(filename).FullName()
04432
04433
04434
04435
04436
04437
04438
04439
04440 abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
04441
04442
04443
04444 header_keys = include_state.keys()
04445 for header in header_keys:
04446 (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
04447 fullpath = common_path + header
04448 if same_module and UpdateIncludeState(fullpath, include_state, io):
04449 header_found = True
04450
04451
04452
04453
04454
04455
04456 if filename.endswith('.cc') and not header_found:
04457 return
04458
04459
04460 for required_header_unstripped in required:
04461 template = required[required_header_unstripped][1]
04462 if required_header_unstripped.strip('<>"') not in include_state:
04463 error(filename, required[required_header_unstripped][0],
04464 'build/include_what_you_use', 4,
04465 'Add #include ' + required_header_unstripped + ' for ' + template)
04466
04467
04468 _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')
04469
04470
04471 def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
04472 """Check that make_pair's template arguments are deduced.
04473
04474 G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are
04475 specified explicitly, and such use isn't intended in any case.
04476
04477 Args:
04478 filename: The name of the current file.
04479 clean_lines: A CleansedLines instance containing the file.
04480 linenum: The number of the line to check.
04481 error: The function to call with any errors found.
04482 """
04483 line = clean_lines.elided[linenum]
04484 match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
04485 if match:
04486 error(filename, linenum, 'build/explicit_make_pair',
04487 4,
04488 'For C++11-compatibility, omit template arguments from make_pair'
04489 ' OR use pair directly OR if appropriate, construct a pair directly')
04490
04491
04492 def ProcessLine(filename, file_extension, clean_lines, line,
04493 include_state, function_state, nesting_state, error,
04494 extra_check_functions=[]):
04495 """Processes a single line in the file.
04496
04497 Args:
04498 filename: Filename of the file that is being processed.
04499 file_extension: The extension (dot not included) of the file.
04500 clean_lines: An array of strings, each representing a line of the file,
04501 with comments stripped.
04502 line: Number of line being processed.
04503 include_state: An _IncludeState instance in which the headers are inserted.
04504 function_state: A _FunctionState instance which counts function lines, etc.
04505 nesting_state: A _NestingState instance which maintains information about
04506 the current stack of nested blocks being parsed.
04507 error: A callable to which errors are reported, which takes 4 arguments:
04508 filename, line number, error level, and message
04509 extra_check_functions: An array of additional check functions that will be
04510 run on each source line. Each function takes 4
04511 arguments: filename, clean_lines, line, error
04512 """
04513 raw_lines = clean_lines.raw_lines
04514 ParseNolintSuppressions(filename, raw_lines[line], line, error)
04515 nesting_state.Update(filename, clean_lines, line, error)
04516 if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM:
04517 return
04518 CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
04519 CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
04520 CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
04521 CheckLanguage(filename, clean_lines, line, file_extension, include_state,
04522 nesting_state, error)
04523 CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
04524 CheckForNonStandardConstructs(filename, clean_lines, line,
04525 nesting_state, error)
04526 CheckVlogArguments(filename, clean_lines, line, error)
04527 CheckPosixThreading(filename, clean_lines, line, error)
04528 CheckInvalidIncrement(filename, clean_lines, line, error)
04529 CheckMakePairUsesDeduction(filename, clean_lines, line, error)
04530 for check_fn in extra_check_functions:
04531 check_fn(filename, clean_lines, line, error)
04532
04533 def ProcessFileData(filename, file_extension, lines, error,
04534 extra_check_functions=[]):
04535 """Performs lint checks and reports any errors to the given error function.
04536
04537 Args:
04538 filename: Filename of the file that is being processed.
04539 file_extension: The extension (dot not included) of the file.
04540 lines: An array of strings, each representing a line of the file, with the
04541 last element being empty if the file is terminated with a newline.
04542 error: A callable to which errors are reported, which takes 4 arguments:
04543 filename, line number, error level, and message
04544 extra_check_functions: An array of additional check functions that will be
04545 run on each source line. Each function takes 4
04546 arguments: filename, clean_lines, line, error
04547 """
04548 lines = (['// marker so line numbers and indices both start at 1'] + lines +
04549 ['// marker so line numbers end in a known way'])
04550
04551 include_state = _IncludeState()
04552 function_state = _FunctionState()
04553 nesting_state = _NestingState()
04554
04555 ResetNolintSuppressions()
04556
04557 CheckForCopyright(filename, lines, error)
04558
04559 if file_extension == 'h':
04560 CheckForHeaderGuard(filename, lines, error)
04561
04562 RemoveMultiLineComments(filename, lines, error)
04563 clean_lines = CleansedLines(lines)
04564 for line in xrange(clean_lines.NumLines()):
04565 ProcessLine(filename, file_extension, clean_lines, line,
04566 include_state, function_state, nesting_state, error,
04567 extra_check_functions)
04568 nesting_state.CheckCompletedBlocks(filename, error)
04569
04570 CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
04571
04572
04573
04574 CheckForBadCharacters(filename, lines, error)
04575
04576 CheckForNewlineAtEOF(filename, lines, error)
04577
04578 def ProcessFile(filename, vlevel, extra_check_functions=[]):
04579 """Does google-lint on a single file.
04580
04581 Args:
04582 filename: The name of the file to parse.
04583
04584 vlevel: The level of errors to report. Every error of confidence
04585 >= verbose_level will be reported. 0 is a good default.
04586
04587 extra_check_functions: An array of additional check functions that will be
04588 run on each source line. Each function takes 4
04589 arguments: filename, clean_lines, line, error
04590 """
04591
04592 _SetVerboseLevel(vlevel)
04593
04594 try:
04595
04596
04597
04598
04599
04600
04601
04602
04603
04604
04605 if filename == '-':
04606 lines = codecs.StreamReaderWriter(sys.stdin,
04607 codecs.getreader('utf8'),
04608 codecs.getwriter('utf8'),
04609 'replace').read().split('\n')
04610 else:
04611 lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
04612
04613 carriage_return_found = False
04614
04615 for linenum in range(len(lines)):
04616 if lines[linenum].endswith('\r'):
04617 lines[linenum] = lines[linenum].rstrip('\r')
04618 carriage_return_found = True
04619
04620 except IOError:
04621 sys.stderr.write(
04622 "Skipping input '%s': Can't open for reading\n" % filename)
04623 return
04624
04625
04626 file_extension = filename[filename.rfind('.') + 1:]
04627
04628
04629
04630 if filename != '-' and file_extension not in _valid_extensions:
04631 sys.stderr.write('Ignoring %s; not a valid file name '
04632 '(%s)\n' % (filename, ', '.join(_valid_extensions)))
04633 else:
04634 ProcessFileData(filename, file_extension, lines, Error,
04635 extra_check_functions)
04636 if carriage_return_found and os.linesep != '\r\n':
04637
04638
04639 Error(filename, 0, 'whitespace/newline', 1,
04640 'One or more unexpected \\r (^M) found;'
04641 'better to use only a \\n')
04642
04643
04644
04645
04646
04647 def PrintUsage(message):
04648 """Prints a brief usage string and exits, optionally with an error message.
04649
04650 Args:
04651 message: The optional error message.
04652 """
04653 sys.stderr.write(_USAGE)
04654 if message:
04655 sys.exit('\nFATAL ERROR: ' + message)
04656 else:
04657 sys.exit(1)
04658
04659
04660 def PrintCategories():
04661 """Prints a list of all the error-categories used by error messages.
04662
04663 These are the categories used to filter messages via --filter.
04664 """
04665 sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
04666 sys.exit(0)
04667
04668
04669 def ParseArguments(args):
04670 """Parses the command line arguments.
04671
04672 This may set the output format and verbosity level as side-effects.
04673
04674 Args:
04675 args: The command line arguments:
04676
04677 Returns:
04678 The list of filenames to lint.
04679 """
04680 try:
04681 (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
04682 'counting=',
04683 'filter=',
04684 'root=',
04685 'linelength=',
04686 'extensions='])
04687 except getopt.GetoptError:
04688 PrintUsage('Invalid arguments.')
04689
04690 verbosity = _VerboseLevel()
04691 output_format = _OutputFormat()
04692 filters = ''
04693 counting_style = ''
04694
04695 for (opt, val) in opts:
04696 if opt == '--help':
04697 PrintUsage(None)
04698 elif opt == '--output':
04699 if val not in ('emacs', 'vs7', 'eclipse'):
04700 PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
04701 output_format = val
04702 elif opt == '--verbose':
04703 verbosity = int(val)
04704 elif opt == '--filter':
04705 filters = val
04706 if not filters:
04707 PrintCategories()
04708 elif opt == '--counting':
04709 if val not in ('total', 'toplevel', 'detailed'):
04710 PrintUsage('Valid counting options are total, toplevel, and detailed')
04711 counting_style = val
04712 elif opt == '--root':
04713 global _root
04714 _root = val
04715 elif opt == '--linelength':
04716 global _line_length
04717 try:
04718 _line_length = int(val)
04719 except ValueError:
04720 PrintUsage('Line length must be digits.')
04721 elif opt == '--extensions':
04722 global _valid_extensions
04723 try:
04724 _valid_extensions = set(val.split(','))
04725 except ValueError:
04726 PrintUsage('Extensions must be comma seperated list.')
04727
04728 if not filenames:
04729 PrintUsage('No files were specified.')
04730
04731 _SetOutputFormat(output_format)
04732 _SetVerboseLevel(verbosity)
04733 _SetFilters(filters)
04734 _SetCountingStyle(counting_style)
04735
04736 return filenames
04737
04738
04739 def main():
04740 filenames = ParseArguments(sys.argv[1:])
04741
04742
04743
04744 sys.stderr = codecs.StreamReaderWriter(sys.stderr,
04745 codecs.getreader('utf8'),
04746 codecs.getwriter('utf8'),
04747 'replace')
04748
04749 _cpplint_state.ResetErrorCounts()
04750 for filename in filenames:
04751 ProcessFile(filename, _cpplint_state.verbose_level)
04752 _cpplint_state.PrintErrorCounts()
04753
04754 sys.exit(_cpplint_state.error_count > 0)
04755
04756
04757 if __name__ == '__main__':
04758 main()