amiro-os / tools / cpplint / python / cpplint.py @ a7e67622
History | View | Annotate | Download (236.226 KB)
| 1 |
#!/usr/bin/env python
|
|---|---|
| 2 |
#
|
| 3 |
# Copyright (c) 2009 Google Inc. All rights reserved.
|
| 4 |
#
|
| 5 |
# Redistribution and use in source and binary forms, with or without
|
| 6 |
# modification, are permitted provided that the following conditions are
|
| 7 |
# met:
|
| 8 |
#
|
| 9 |
# * Redistributions of source code must retain the above copyright
|
| 10 |
# notice, this list of conditions and the following disclaimer.
|
| 11 |
# * Redistributions in binary form must reproduce the above
|
| 12 |
# copyright notice, this list of conditions and the following disclaimer
|
| 13 |
# in the documentation and/or other materials provided with the
|
| 14 |
# distribution.
|
| 15 |
# * Neither the name of Google Inc. nor the names of its
|
| 16 |
# contributors may be used to endorse or promote products derived from
|
| 17 |
# this software without specific prior written permission.
|
| 18 |
#
|
| 19 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 20 |
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 21 |
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 22 |
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 23 |
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 24 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 25 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 26 |
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 27 |
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 28 |
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 29 |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 30 |
|
| 31 |
"""Does google-lint on c++ files.
|
| 32 |
|
| 33 |
The goal of this script is to identify places in the code that *may*
|
| 34 |
be in non-compliance with google style. It does not attempt to fix
|
| 35 |
up these problems -- the point is to educate. It does also not
|
| 36 |
attempt to find all problems, or to ensure that everything it does
|
| 37 |
find is legitimately a problem.
|
| 38 |
|
| 39 |
In particular, we can get very confused by /* and // inside strings!
|
| 40 |
We do a small hack, which is to ignore //'s with "'s after them on the
|
| 41 |
same line, but it is far from perfect (in either direction).
|
| 42 |
"""
|
| 43 |
|
| 44 |
import codecs |
| 45 |
import copy |
| 46 |
import getopt |
| 47 |
import math # for log |
| 48 |
import os |
| 49 |
import re |
| 50 |
import sre_compile |
| 51 |
import string |
| 52 |
import sys |
| 53 |
import unicodedata |
| 54 |
|
| 55 |
|
| 56 |
_USAGE = """
|
| 57 |
Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
|
| 58 |
[--counting=total|toplevel|detailed] [--root=subdir]
|
| 59 |
[--linelength=digits]
|
| 60 |
<file> [file] ...
|
| 61 |
|
| 62 |
The style guidelines this tries to follow are those in
|
| 63 |
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
|
| 64 |
|
| 65 |
Every problem is given a confidence score from 1-5, with 5 meaning we are
|
| 66 |
certain of the problem, and 1 meaning it could be a legitimate construct.
|
| 67 |
This will miss some errors, and is not a substitute for a code review.
|
| 68 |
|
| 69 |
To suppress false-positive errors of a certain category, add a
|
| 70 |
'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
|
| 71 |
suppresses errors of all categories on that line.
|
| 72 |
|
| 73 |
The files passed in will be linted; at least one file must be provided.
|
| 74 |
Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
|
| 75 |
extensions with the --extensions flag.
|
| 76 |
|
| 77 |
Flags:
|
| 78 |
|
| 79 |
output=vs7
|
| 80 |
By default, the output is formatted to ease emacs parsing. Visual Studio
|
| 81 |
compatible output (vs7) may also be used. Other formats are unsupported.
|
| 82 |
|
| 83 |
verbose=#
|
| 84 |
Specify a number 0-5 to restrict errors to certain verbosity levels.
|
| 85 |
|
| 86 |
filter=-x,+y,...
|
| 87 |
Specify a comma-separated list of category-filters to apply: only
|
| 88 |
error messages whose category names pass the filters will be printed.
|
| 89 |
(Category names are printed with the message and look like
|
| 90 |
"[whitespace/indent]".) Filters are evaluated left to right.
|
| 91 |
"-FOO" and "FOO" means "do not print categories that start with FOO".
|
| 92 |
"+FOO" means "do print categories that start with FOO".
|
| 93 |
|
| 94 |
Examples: --filter=-whitespace,+whitespace/braces
|
| 95 |
--filter=whitespace,runtime/printf,+runtime/printf_format
|
| 96 |
--filter=-,+build/include_what_you_use
|
| 97 |
|
| 98 |
To see a list of all the categories used in cpplint, pass no arg:
|
| 99 |
--filter=
|
| 100 |
|
| 101 |
counting=total|toplevel|detailed
|
| 102 |
The total number of errors found is always printed. If
|
| 103 |
'toplevel' is provided, then the count of errors in each of
|
| 104 |
the top-level categories like 'build' and 'whitespace' will
|
| 105 |
also be printed. If 'detailed' is provided, then a count
|
| 106 |
is provided for each category like 'build/class'.
|
| 107 |
|
| 108 |
root=subdir
|
| 109 |
The root directory used for deriving header guard CPP variable.
|
| 110 |
By default, the header guard CPP variable is calculated as the relative
|
| 111 |
path to the directory that contains .git, .hg, or .svn. When this flag
|
| 112 |
is specified, the relative path is calculated from the specified
|
| 113 |
directory. If the specified directory does not exist, this flag is
|
| 114 |
ignored.
|
| 115 |
|
| 116 |
Examples:
|
| 117 |
Assuming that src/.git exists, the header guard CPP variables for
|
| 118 |
src/chrome/browser/ui/browser.h are:
|
| 119 |
|
| 120 |
No flag => CHROME_BROWSER_UI_BROWSER_H_
|
| 121 |
--root=chrome => BROWSER_UI_BROWSER_H_
|
| 122 |
--root=chrome/browser => UI_BROWSER_H_
|
| 123 |
|
| 124 |
linelength=digits
|
| 125 |
This is the allowed line length for the project. The default value is
|
| 126 |
80 characters.
|
| 127 |
|
| 128 |
Examples:
|
| 129 |
--linelength=120
|
| 130 |
|
| 131 |
extensions=extension,extension,...
|
| 132 |
The allowed file extensions that cpplint will check
|
| 133 |
|
| 134 |
Examples:
|
| 135 |
--extensions=hpp,cpp
|
| 136 |
|
| 137 |
cpplint.py supports per-directory configurations specified in CPPLINT.cfg
|
| 138 |
files. CPPLINT.cfg file can contain a number of key=value pairs.
|
| 139 |
Currently the following options are supported:
|
| 140 |
|
| 141 |
set noparent
|
| 142 |
filter=+filter1,-filter2,...
|
| 143 |
exclude_files=regex
|
| 144 |
linelength=80
|
| 145 |
|
| 146 |
"set noparent" option prevents cpplint from traversing directory tree
|
| 147 |
upwards looking for more .cfg files in parent directories. This option
|
| 148 |
is usually placed in the top-level project directory.
|
| 149 |
|
| 150 |
The "filter" option is similar in function to --filter flag. It specifies
|
| 151 |
message filters in addition to the |_DEFAULT_FILTERS| and those specified
|
| 152 |
through --filter command-line flag.
|
| 153 |
|
| 154 |
"exclude_files" allows to specify a regular expression to be matched against
|
| 155 |
a file name. If the expression matches, the file is skipped and not run
|
| 156 |
through liner.
|
| 157 |
|
| 158 |
"linelength" allows to specify the allowed line length for the project.
|
| 159 |
|
| 160 |
CPPLINT.cfg has an effect on files in the same directory and all
|
| 161 |
sub-directories, unless overridden by a nested configuration file.
|
| 162 |
|
| 163 |
Example file:
|
| 164 |
filter=-build/include_order,+build/include_alpha
|
| 165 |
exclude_files=.*\.cc
|
| 166 |
|
| 167 |
The above example disables build/include_order warning and enables
|
| 168 |
build/include_alpha as well as excludes all .cc from being
|
| 169 |
processed by linter, in the current directory (where the .cfg
|
| 170 |
file is located) and all sub-directories.
|
| 171 |
"""
|
| 172 |
|
| 173 |
# We categorize each error message we print. Here are the categories.
|
| 174 |
# We want an explicit list so we can list them all in cpplint --filter=.
|
| 175 |
# If you add a new error message with a new category, add it to the list
|
| 176 |
# here! cpplint_unittest.py should tell you if you forget to do this.
|
| 177 |
_ERROR_CATEGORIES = [ |
| 178 |
'build/class',
|
| 179 |
'build/c++11',
|
| 180 |
'build/deprecated',
|
| 181 |
'build/endif_comment',
|
| 182 |
'build/explicit_make_pair',
|
| 183 |
'build/forward_decl',
|
| 184 |
'build/header_guard',
|
| 185 |
'build/include',
|
| 186 |
'build/include_alpha',
|
| 187 |
'build/include_order',
|
| 188 |
'build/include_what_you_use',
|
| 189 |
'build/namespaces',
|
| 190 |
'build/printf_format',
|
| 191 |
'build/storage_class',
|
| 192 |
'legal/copyright',
|
| 193 |
'readability/alt_tokens',
|
| 194 |
'readability/braces',
|
| 195 |
'readability/casting',
|
| 196 |
'readability/check',
|
| 197 |
'readability/constructors',
|
| 198 |
'readability/fn_size',
|
| 199 |
'readability/function',
|
| 200 |
'readability/inheritance',
|
| 201 |
'readability/multiline_comment',
|
| 202 |
'readability/multiline_string',
|
| 203 |
'readability/namespace',
|
| 204 |
'readability/nolint',
|
| 205 |
'readability/nul',
|
| 206 |
'readability/strings',
|
| 207 |
'readability/todo',
|
| 208 |
'readability/utf8',
|
| 209 |
'runtime/arrays',
|
| 210 |
'runtime/casting',
|
| 211 |
'runtime/explicit',
|
| 212 |
'runtime/int',
|
| 213 |
'runtime/init',
|
| 214 |
'runtime/invalid_increment',
|
| 215 |
'runtime/member_string_references',
|
| 216 |
'runtime/memset',
|
| 217 |
'runtime/indentation_namespace',
|
| 218 |
'runtime/operator',
|
| 219 |
'runtime/printf',
|
| 220 |
'runtime/printf_format',
|
| 221 |
'runtime/references',
|
| 222 |
'runtime/string',
|
| 223 |
'runtime/threadsafe_fn',
|
| 224 |
'runtime/vlog',
|
| 225 |
'whitespace/blank_line',
|
| 226 |
'whitespace/braces',
|
| 227 |
'whitespace/comma',
|
| 228 |
'whitespace/comments',
|
| 229 |
'whitespace/empty_conditional_body',
|
| 230 |
'whitespace/empty_loop_body',
|
| 231 |
'whitespace/end_of_line',
|
| 232 |
'whitespace/ending_newline',
|
| 233 |
'whitespace/forcolon',
|
| 234 |
'whitespace/indent',
|
| 235 |
'whitespace/line_length',
|
| 236 |
'whitespace/newline',
|
| 237 |
'whitespace/operators',
|
| 238 |
'whitespace/parens',
|
| 239 |
'whitespace/semicolon',
|
| 240 |
'whitespace/tab',
|
| 241 |
'whitespace/todo',
|
| 242 |
] |
| 243 |
|
| 244 |
# These error categories are no longer enforced by cpplint, but for backwards-
|
| 245 |
# compatibility they may still appear in NOLINT comments.
|
| 246 |
_LEGACY_ERROR_CATEGORIES = [ |
| 247 |
'readability/streams',
|
| 248 |
] |
| 249 |
|
| 250 |
# The default state of the category filter. This is overridden by the --filter=
|
| 251 |
# flag. By default all errors are on, so only add here categories that should be
|
| 252 |
# off by default (i.e., categories that must be enabled by the --filter= flags).
|
| 253 |
# All entries here should start with a '-' or '+', as in the --filter= flag.
|
| 254 |
_DEFAULT_FILTERS = ['-build/include_alpha']
|
| 255 |
|
| 256 |
# We used to check for high-bit characters, but after much discussion we
|
| 257 |
# decided those were OK, as long as they were in UTF-8 and didn't represent
|
| 258 |
# hard-coded international strings, which belong in a separate i18n file.
|
| 259 |
|
| 260 |
# C++ headers
|
| 261 |
_CPP_HEADERS = frozenset([
|
| 262 |
# Legacy
|
| 263 |
'algobase.h',
|
| 264 |
'algo.h',
|
| 265 |
'alloc.h',
|
| 266 |
'builtinbuf.h',
|
| 267 |
'bvector.h',
|
| 268 |
'complex.h',
|
| 269 |
'defalloc.h',
|
| 270 |
'deque.h',
|
| 271 |
'editbuf.h',
|
| 272 |
'fstream.h',
|
| 273 |
'function.h',
|
| 274 |
'hash_map',
|
| 275 |
'hash_map.h',
|
| 276 |
'hash_set',
|
| 277 |
'hash_set.h',
|
| 278 |
'hashtable.h',
|
| 279 |
'heap.h',
|
| 280 |
'indstream.h',
|
| 281 |
'iomanip.h',
|
| 282 |
'iostream.h',
|
| 283 |
'istream.h',
|
| 284 |
'iterator.h',
|
| 285 |
'list.h',
|
| 286 |
'map.h',
|
| 287 |
'multimap.h',
|
| 288 |
'multiset.h',
|
| 289 |
'ostream.h',
|
| 290 |
'pair.h',
|
| 291 |
'parsestream.h',
|
| 292 |
'pfstream.h',
|
| 293 |
'procbuf.h',
|
| 294 |
'pthread_alloc',
|
| 295 |
'pthread_alloc.h',
|
| 296 |
'rope',
|
| 297 |
'rope.h',
|
| 298 |
'ropeimpl.h',
|
| 299 |
'set.h',
|
| 300 |
'slist',
|
| 301 |
'slist.h',
|
| 302 |
'stack.h',
|
| 303 |
'stdiostream.h',
|
| 304 |
'stl_alloc.h',
|
| 305 |
'stl_relops.h',
|
| 306 |
'streambuf.h',
|
| 307 |
'stream.h',
|
| 308 |
'strfile.h',
|
| 309 |
'strstream.h',
|
| 310 |
'tempbuf.h',
|
| 311 |
'tree.h',
|
| 312 |
'type_traits.h',
|
| 313 |
'vector.h',
|
| 314 |
# 17.6.1.2 C++ library headers
|
| 315 |
'algorithm',
|
| 316 |
'array',
|
| 317 |
'atomic',
|
| 318 |
'bitset',
|
| 319 |
'chrono',
|
| 320 |
'codecvt',
|
| 321 |
'complex',
|
| 322 |
'condition_variable',
|
| 323 |
'deque',
|
| 324 |
'exception',
|
| 325 |
'forward_list',
|
| 326 |
'fstream',
|
| 327 |
'functional',
|
| 328 |
'future',
|
| 329 |
'initializer_list',
|
| 330 |
'iomanip',
|
| 331 |
'ios',
|
| 332 |
'iosfwd',
|
| 333 |
'iostream',
|
| 334 |
'istream',
|
| 335 |
'iterator',
|
| 336 |
'limits',
|
| 337 |
'list',
|
| 338 |
'locale',
|
| 339 |
'map',
|
| 340 |
'memory',
|
| 341 |
'mutex',
|
| 342 |
'new',
|
| 343 |
'numeric',
|
| 344 |
'ostream',
|
| 345 |
'queue',
|
| 346 |
'random',
|
| 347 |
'ratio',
|
| 348 |
'regex',
|
| 349 |
'set',
|
| 350 |
'sstream',
|
| 351 |
'stack',
|
| 352 |
'stdexcept',
|
| 353 |
'streambuf',
|
| 354 |
'string',
|
| 355 |
'strstream',
|
| 356 |
'system_error',
|
| 357 |
'thread',
|
| 358 |
'tuple',
|
| 359 |
'typeindex',
|
| 360 |
'typeinfo',
|
| 361 |
'type_traits',
|
| 362 |
'unordered_map',
|
| 363 |
'unordered_set',
|
| 364 |
'utility',
|
| 365 |
'valarray',
|
| 366 |
'vector',
|
| 367 |
# 17.6.1.2 C++ headers for C library facilities
|
| 368 |
'cassert',
|
| 369 |
'ccomplex',
|
| 370 |
'cctype',
|
| 371 |
'cerrno',
|
| 372 |
'cfenv',
|
| 373 |
'cfloat',
|
| 374 |
'cinttypes',
|
| 375 |
'ciso646',
|
| 376 |
'climits',
|
| 377 |
'clocale',
|
| 378 |
'cmath',
|
| 379 |
'csetjmp',
|
| 380 |
'csignal',
|
| 381 |
'cstdalign',
|
| 382 |
'cstdarg',
|
| 383 |
'cstdbool',
|
| 384 |
'cstddef',
|
| 385 |
'cstdint',
|
| 386 |
'cstdio',
|
| 387 |
'cstdlib',
|
| 388 |
'cstring',
|
| 389 |
'ctgmath',
|
| 390 |
'ctime',
|
| 391 |
'cuchar',
|
| 392 |
'cwchar',
|
| 393 |
'cwctype',
|
| 394 |
]) |
| 395 |
|
| 396 |
|
| 397 |
# These headers are excluded from [build/include] and [build/include_order]
|
| 398 |
# checks:
|
| 399 |
# - Anything not following google file name conventions (containing an
|
| 400 |
# uppercase character, such as Python.h or nsStringAPI.h, for example).
|
| 401 |
# - Lua headers.
|
| 402 |
_THIRD_PARTY_HEADERS_PATTERN = re.compile( |
| 403 |
r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$')
|
| 404 |
|
| 405 |
|
| 406 |
# Assertion macros. These are defined in base/logging.h and
|
| 407 |
# testing/base/gunit.h. Note that the _M versions need to come first
|
| 408 |
# for substring matching to work.
|
| 409 |
_CHECK_MACROS = [ |
| 410 |
'DCHECK', 'CHECK', |
| 411 |
'EXPECT_TRUE_M', 'EXPECT_TRUE', |
| 412 |
'ASSERT_TRUE_M', 'ASSERT_TRUE', |
| 413 |
'EXPECT_FALSE_M', 'EXPECT_FALSE', |
| 414 |
'ASSERT_FALSE_M', 'ASSERT_FALSE', |
| 415 |
] |
| 416 |
|
| 417 |
# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
|
| 418 |
_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) |
| 419 |
|
| 420 |
for op, replacement in [('==', 'EQ'), ('!=', 'NE'), |
| 421 |
('>=', 'GE'), ('>', 'GT'), |
| 422 |
('<=', 'LE'), ('<', 'LT')]: |
| 423 |
_CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement |
| 424 |
_CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement |
| 425 |
_CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement |
| 426 |
_CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement |
| 427 |
_CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement |
| 428 |
_CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement |
| 429 |
|
| 430 |
for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), |
| 431 |
('>=', 'LT'), ('>', 'LE'), |
| 432 |
('<=', 'GT'), ('<', 'GE')]: |
| 433 |
_CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement |
| 434 |
_CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement |
| 435 |
_CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement |
| 436 |
_CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement |
| 437 |
|
| 438 |
# Alternative tokens and their replacements. For full list, see section 2.5
|
| 439 |
# Alternative tokens [lex.digraph] in the C++ standard.
|
| 440 |
#
|
| 441 |
# Digraphs (such as '%:') are not included here since it's a mess to
|
| 442 |
# match those on a word boundary.
|
| 443 |
_ALT_TOKEN_REPLACEMENT = {
|
| 444 |
'and': '&&', |
| 445 |
'bitor': '|', |
| 446 |
'or': '||', |
| 447 |
'xor': '^', |
| 448 |
'compl': '~', |
| 449 |
'bitand': '&', |
| 450 |
'and_eq': '&=', |
| 451 |
'or_eq': '|=', |
| 452 |
'xor_eq': '^=', |
| 453 |
'not': '!', |
| 454 |
'not_eq': '!=' |
| 455 |
} |
| 456 |
|
| 457 |
# Compile regular expression that matches all the above keywords. The "[ =()]"
|
| 458 |
# bit is meant to avoid matching these keywords outside of boolean expressions.
|
| 459 |
#
|
| 460 |
# False positives include C-style multi-line comments and multi-line strings
|
| 461 |
# but those have always been troublesome for cpplint.
|
| 462 |
_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( |
| 463 |
r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') |
| 464 |
|
| 465 |
|
| 466 |
# These constants define types of headers for use with
|
| 467 |
# _IncludeState.CheckNextIncludeOrder().
|
| 468 |
_C_SYS_HEADER = 1
|
| 469 |
_CPP_SYS_HEADER = 2
|
| 470 |
_LIKELY_MY_HEADER = 3
|
| 471 |
_POSSIBLE_MY_HEADER = 4
|
| 472 |
_OTHER_HEADER = 5
|
| 473 |
|
| 474 |
# These constants define the current inline assembly state
|
| 475 |
_NO_ASM = 0 # Outside of inline assembly block |
| 476 |
_INSIDE_ASM = 1 # Inside inline assembly block |
| 477 |
_END_ASM = 2 # Last line of inline assembly block |
| 478 |
_BLOCK_ASM = 3 # The whole block is an inline assembly block |
| 479 |
|
| 480 |
# Match start of assembly blocks
|
| 481 |
_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
|
| 482 |
r'(?:\s+(volatile|__volatile__))?'
|
| 483 |
r'\s*[{(]')
|
| 484 |
|
| 485 |
|
| 486 |
_regexp_compile_cache = {}
|
| 487 |
|
| 488 |
# {str, set(int)}: a map from error categories to sets of linenumbers
|
| 489 |
# on which those errors are expected and should be suppressed.
|
| 490 |
_error_suppressions = {}
|
| 491 |
|
| 492 |
# The root directory used for deriving header guard CPP variable.
|
| 493 |
# This is set by --root flag.
|
| 494 |
_root = None
|
| 495 |
|
| 496 |
# The allowed line length of files.
|
| 497 |
# This is set by --linelength flag.
|
| 498 |
_line_length = 80
|
| 499 |
|
| 500 |
# The allowed extensions for file names
|
| 501 |
# This is set by --extensions flag.
|
| 502 |
_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) |
| 503 |
|
| 504 |
def ParseNolintSuppressions(filename, raw_line, linenum, error): |
| 505 |
"""Updates the global list of error-suppressions.
|
| 506 |
|
| 507 |
Parses any NOLINT comments on the current line, updating the global
|
| 508 |
error_suppressions store. Reports an error if the NOLINT comment
|
| 509 |
was malformed.
|
| 510 |
|
| 511 |
Args:
|
| 512 |
filename: str, the name of the input file.
|
| 513 |
raw_line: str, the line of input text, with comments.
|
| 514 |
linenum: int, the number of the current line.
|
| 515 |
error: function, an error handler.
|
| 516 |
"""
|
| 517 |
matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line)
|
| 518 |
if matched:
|
| 519 |
if matched.group(1): |
| 520 |
suppressed_line = linenum + 1
|
| 521 |
else:
|
| 522 |
suppressed_line = linenum |
| 523 |
category = matched.group(2)
|
| 524 |
if category in (None, '(*)'): # => "suppress all" |
| 525 |
_error_suppressions.setdefault(None, set()).add(suppressed_line) |
| 526 |
else:
|
| 527 |
if category.startswith('(') and category.endswith(')'): |
| 528 |
category = category[1:-1] |
| 529 |
if category in _ERROR_CATEGORIES: |
| 530 |
_error_suppressions.setdefault(category, set()).add(suppressed_line)
|
| 531 |
elif category not in _LEGACY_ERROR_CATEGORIES: |
| 532 |
error(filename, linenum, 'readability/nolint', 5, |
| 533 |
'Unknown NOLINT error category: %s' % category)
|
| 534 |
|
| 535 |
|
| 536 |
def ResetNolintSuppressions(): |
| 537 |
"""Resets the set of NOLINT suppressions to empty."""
|
| 538 |
_error_suppressions.clear() |
| 539 |
|
| 540 |
|
| 541 |
def IsErrorSuppressedByNolint(category, linenum): |
| 542 |
"""Returns true if the specified error category is suppressed on this line.
|
| 543 |
|
| 544 |
Consults the global error_suppressions map populated by
|
| 545 |
ParseNolintSuppressions/ResetNolintSuppressions.
|
| 546 |
|
| 547 |
Args:
|
| 548 |
category: str, the category of the error.
|
| 549 |
linenum: int, the current line number.
|
| 550 |
Returns:
|
| 551 |
bool, True iff the error should be suppressed due to a NOLINT comment.
|
| 552 |
"""
|
| 553 |
return (linenum in _error_suppressions.get(category, set()) or |
| 554 |
linenum in _error_suppressions.get(None, set())) |
| 555 |
|
| 556 |
|
| 557 |
def Match(pattern, s): |
| 558 |
"""Matches the string with the pattern, caching the compiled regexp."""
|
| 559 |
# The regexp compilation caching is inlined in both Match and Search for
|
| 560 |
# performance reasons; factoring it out into a separate function turns out
|
| 561 |
# to be noticeably expensive.
|
| 562 |
if pattern not in _regexp_compile_cache: |
| 563 |
_regexp_compile_cache[pattern] = sre_compile.compile(pattern) |
| 564 |
return _regexp_compile_cache[pattern].match(s)
|
| 565 |
|
| 566 |
|
| 567 |
def ReplaceAll(pattern, rep, s): |
| 568 |
"""Replaces instances of pattern in a string with a replacement.
|
| 569 |
|
| 570 |
The compiled regex is kept in a cache shared by Match and Search.
|
| 571 |
|
| 572 |
Args:
|
| 573 |
pattern: regex pattern
|
| 574 |
rep: replacement text
|
| 575 |
s: search string
|
| 576 |
|
| 577 |
Returns:
|
| 578 |
string with replacements made (or original string if no replacements)
|
| 579 |
"""
|
| 580 |
if pattern not in _regexp_compile_cache: |
| 581 |
_regexp_compile_cache[pattern] = sre_compile.compile(pattern) |
| 582 |
return _regexp_compile_cache[pattern].sub(rep, s)
|
| 583 |
|
| 584 |
|
| 585 |
def Search(pattern, s): |
| 586 |
"""Searches the string for the pattern, caching the compiled regexp."""
|
| 587 |
if pattern not in _regexp_compile_cache: |
| 588 |
_regexp_compile_cache[pattern] = sre_compile.compile(pattern) |
| 589 |
return _regexp_compile_cache[pattern].search(s)
|
| 590 |
|
| 591 |
|
| 592 |
class _IncludeState(object): |
| 593 |
"""Tracks line numbers for includes, and the order in which includes appear.
|
| 594 |
|
| 595 |
include_list contains list of lists of (header, line number) pairs.
|
| 596 |
It's a lists of lists rather than just one flat list to make it
|
| 597 |
easier to update across preprocessor boundaries.
|
| 598 |
|
| 599 |
Call CheckNextIncludeOrder() once for each header in the file, passing
|
| 600 |
in the type constants defined above. Calls in an illegal order will
|
| 601 |
raise an _IncludeError with an appropriate error message.
|
| 602 |
|
| 603 |
"""
|
| 604 |
# self._section will move monotonically through this set. If it ever
|
| 605 |
# needs to move backwards, CheckNextIncludeOrder will raise an error.
|
| 606 |
_INITIAL_SECTION = 0
|
| 607 |
_MY_H_SECTION = 1
|
| 608 |
_C_SECTION = 2
|
| 609 |
_CPP_SECTION = 3
|
| 610 |
_OTHER_H_SECTION = 4
|
| 611 |
|
| 612 |
_TYPE_NAMES = {
|
| 613 |
_C_SYS_HEADER: 'C system header',
|
| 614 |
_CPP_SYS_HEADER: 'C++ system header',
|
| 615 |
_LIKELY_MY_HEADER: 'header this file implements',
|
| 616 |
_POSSIBLE_MY_HEADER: 'header this file may implement',
|
| 617 |
_OTHER_HEADER: 'other header',
|
| 618 |
} |
| 619 |
_SECTION_NAMES = {
|
| 620 |
_INITIAL_SECTION: "... nothing. (This can't be an error.)",
|
| 621 |
_MY_H_SECTION: 'a header this file implements',
|
| 622 |
_C_SECTION: 'C system header',
|
| 623 |
_CPP_SECTION: 'C++ system header',
|
| 624 |
_OTHER_H_SECTION: 'other header',
|
| 625 |
} |
| 626 |
|
| 627 |
def __init__(self): |
| 628 |
self.include_list = [[]]
|
| 629 |
self.ResetSection('') |
| 630 |
|
| 631 |
def FindHeader(self, header): |
| 632 |
"""Check if a header has already been included.
|
| 633 |
|
| 634 |
Args:
|
| 635 |
header: header to check.
|
| 636 |
Returns:
|
| 637 |
Line number of previous occurrence, or -1 if the header has not
|
| 638 |
been seen before.
|
| 639 |
"""
|
| 640 |
for section_list in self.include_list: |
| 641 |
for f in section_list: |
| 642 |
if f[0] == header: |
| 643 |
return f[1] |
| 644 |
return -1 |
| 645 |
|
| 646 |
def ResetSection(self, directive): |
| 647 |
"""Reset section checking for preprocessor directive.
|
| 648 |
|
| 649 |
Args:
|
| 650 |
directive: preprocessor directive (e.g. "if", "else").
|
| 651 |
"""
|
| 652 |
# The name of the current section.
|
| 653 |
self._section = self._INITIAL_SECTION |
| 654 |
# The path of last found header.
|
| 655 |
self._last_header = '' |
| 656 |
|
| 657 |
# Update list of includes. Note that we never pop from the
|
| 658 |
# include list.
|
| 659 |
if directive in ('if', 'ifdef', 'ifndef'): |
| 660 |
self.include_list.append([])
|
| 661 |
elif directive in ('else', 'elif'): |
| 662 |
self.include_list[-1] = [] |
| 663 |
|
| 664 |
def SetLastHeader(self, header_path): |
| 665 |
self._last_header = header_path
|
| 666 |
|
| 667 |
def CanonicalizeAlphabeticalOrder(self, header_path): |
| 668 |
"""Returns a path canonicalized for alphabetical comparison.
|
| 669 |
|
| 670 |
- replaces "-" with "_" so they both cmp the same.
|
| 671 |
- removes '-inl' since we don't require them to be after the main header.
|
| 672 |
- lowercase everything, just in case.
|
| 673 |
|
| 674 |
Args:
|
| 675 |
header_path: Path to be canonicalized.
|
| 676 |
|
| 677 |
Returns:
|
| 678 |
Canonicalized path.
|
| 679 |
"""
|
| 680 |
return header_path.replace('-inl.h', '.h').replace('-', '_').lower() |
| 681 |
|
| 682 |
def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): |
| 683 |
"""Check if a header is in alphabetical order with the previous header.
|
| 684 |
|
| 685 |
Args:
|
| 686 |
clean_lines: A CleansedLines instance containing the file.
|
| 687 |
linenum: The number of the line to check.
|
| 688 |
header_path: Canonicalized header to be checked.
|
| 689 |
|
| 690 |
Returns:
|
| 691 |
Returns true if the header is in alphabetical order.
|
| 692 |
"""
|
| 693 |
# If previous section is different from current section, _last_header will
|
| 694 |
# be reset to empty string, so it's always less than current header.
|
| 695 |
#
|
| 696 |
# If previous line was a blank line, assume that the headers are
|
| 697 |
# intentionally sorted the way they are.
|
| 698 |
if (self._last_header > header_path and |
| 699 |
Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): |
| 700 |
return False |
| 701 |
return True |
| 702 |
|
| 703 |
def CheckNextIncludeOrder(self, header_type): |
| 704 |
"""Returns a non-empty error message if the next header is out of order.
|
| 705 |
|
| 706 |
This function also updates the internal state to be ready to check
|
| 707 |
the next include.
|
| 708 |
|
| 709 |
Args:
|
| 710 |
header_type: One of the _XXX_HEADER constants defined above.
|
| 711 |
|
| 712 |
Returns:
|
| 713 |
The empty string if the header is in the right order, or an
|
| 714 |
error message describing what's wrong.
|
| 715 |
|
| 716 |
"""
|
| 717 |
error_message = ('Found %s after %s' %
|
| 718 |
(self._TYPE_NAMES[header_type],
|
| 719 |
self._SECTION_NAMES[self._section])) |
| 720 |
|
| 721 |
last_section = self._section
|
| 722 |
|
| 723 |
if header_type == _C_SYS_HEADER:
|
| 724 |
if self._section <= self._C_SECTION: |
| 725 |
self._section = self._C_SECTION |
| 726 |
else:
|
| 727 |
self._last_header = '' |
| 728 |
return error_message
|
| 729 |
elif header_type == _CPP_SYS_HEADER:
|
| 730 |
if self._section <= self._CPP_SECTION: |
| 731 |
self._section = self._CPP_SECTION |
| 732 |
else:
|
| 733 |
self._last_header = '' |
| 734 |
return error_message
|
| 735 |
elif header_type == _LIKELY_MY_HEADER:
|
| 736 |
if self._section <= self._MY_H_SECTION: |
| 737 |
self._section = self._MY_H_SECTION |
| 738 |
else:
|
| 739 |
self._section = self._OTHER_H_SECTION |
| 740 |
elif header_type == _POSSIBLE_MY_HEADER:
|
| 741 |
if self._section <= self._MY_H_SECTION: |
| 742 |
self._section = self._MY_H_SECTION |
| 743 |
else:
|
| 744 |
# This will always be the fallback because we're not sure
|
| 745 |
# enough that the header is associated with this file.
|
| 746 |
self._section = self._OTHER_H_SECTION |
| 747 |
else:
|
| 748 |
assert header_type == _OTHER_HEADER
|
| 749 |
self._section = self._OTHER_H_SECTION |
| 750 |
|
| 751 |
if last_section != self._section: |
| 752 |
self._last_header = '' |
| 753 |
|
| 754 |
return '' |
| 755 |
|
| 756 |
|
| 757 |
class _CppLintState(object): |
| 758 |
"""Maintains module-wide state.."""
|
| 759 |
|
| 760 |
def __init__(self): |
| 761 |
self.verbose_level = 1 # global setting. |
| 762 |
self.error_count = 0 # global count of reported errors |
| 763 |
# filters to apply when emitting error messages
|
| 764 |
self.filters = _DEFAULT_FILTERS[:]
|
| 765 |
# backup of filter list. Used to restore the state after each file.
|
| 766 |
self._filters_backup = self.filters[:] |
| 767 |
self.counting = 'total' # In what way are we counting errors? |
| 768 |
self.errors_by_category = {} # string to int dict storing error counts |
| 769 |
|
| 770 |
# output format:
|
| 771 |
# "emacs" - format that emacs can parse (default)
|
| 772 |
# "vs7" - format that Microsoft Visual Studio 7 can parse
|
| 773 |
self.output_format = 'emacs' |
| 774 |
|
| 775 |
def SetOutputFormat(self, output_format): |
| 776 |
"""Sets the output format for errors."""
|
| 777 |
self.output_format = output_format
|
| 778 |
|
| 779 |
def SetVerboseLevel(self, level): |
| 780 |
"""Sets the module's verbosity, and returns the previous setting."""
|
| 781 |
last_verbose_level = self.verbose_level
|
| 782 |
self.verbose_level = level
|
| 783 |
return last_verbose_level
|
| 784 |
|
| 785 |
def SetCountingStyle(self, counting_style): |
| 786 |
"""Sets the module's counting options."""
|
| 787 |
self.counting = counting_style
|
| 788 |
|
| 789 |
def SetFilters(self, filters): |
| 790 |
"""Sets the error-message filters.
|
| 791 |
|
| 792 |
These filters are applied when deciding whether to emit a given
|
| 793 |
error message.
|
| 794 |
|
| 795 |
Args:
|
| 796 |
filters: A string of comma-separated filters (eg "+whitespace/indent").
|
| 797 |
Each filter should start with + or -; else we die.
|
| 798 |
|
| 799 |
Raises:
|
| 800 |
ValueError: The comma-separated filters did not all start with '+' or '-'.
|
| 801 |
E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
|
| 802 |
"""
|
| 803 |
# Default filters always have less priority than the flag ones.
|
| 804 |
self.filters = _DEFAULT_FILTERS[:]
|
| 805 |
self.AddFilters(filters)
|
| 806 |
|
| 807 |
def AddFilters(self, filters): |
| 808 |
""" Adds more filters to the existing list of error-message filters. """
|
| 809 |
for filt in filters.split(','): |
| 810 |
clean_filt = filt.strip() |
| 811 |
if clean_filt:
|
| 812 |
self.filters.append(clean_filt)
|
| 813 |
for filt in self.filters: |
| 814 |
if not (filt.startswith('+') or filt.startswith('-')): |
| 815 |
raise ValueError('Every filter in --filters must start with + or -' |
| 816 |
' (%s does not)' % filt)
|
| 817 |
|
| 818 |
def BackupFilters(self): |
| 819 |
""" Saves the current filter list to backup storage."""
|
| 820 |
self._filters_backup = self.filters[:] |
| 821 |
|
| 822 |
def RestoreFilters(self): |
| 823 |
""" Restores filters previously backed up."""
|
| 824 |
self.filters = self._filters_backup[:] |
| 825 |
|
| 826 |
def ResetErrorCounts(self): |
| 827 |
"""Sets the module's error statistic back to zero."""
|
| 828 |
self.error_count = 0 |
| 829 |
self.errors_by_category = {}
|
| 830 |
|
| 831 |
def IncrementErrorCount(self, category): |
| 832 |
"""Bumps the module's error statistic."""
|
| 833 |
self.error_count += 1 |
| 834 |
if self.counting in ('toplevel', 'detailed'): |
| 835 |
if self.counting != 'detailed': |
| 836 |
category = category.split('/')[0] |
| 837 |
if category not in self.errors_by_category: |
| 838 |
self.errors_by_category[category] = 0 |
| 839 |
self.errors_by_category[category] += 1 |
| 840 |
|
| 841 |
def PrintErrorCounts(self): |
| 842 |
"""Print a summary of errors by category, and the total."""
|
| 843 |
for category, count in self.errors_by_category.iteritems(): |
| 844 |
sys.stderr.write('Category \'%s\' errors found: %d\n' %
|
| 845 |
(category, count)) |
| 846 |
sys.stderr.write('Total errors found: %d\n' % self.error_count) |
| 847 |
|
| 848 |
_cpplint_state = _CppLintState() |
| 849 |
|
| 850 |
|
| 851 |
def _OutputFormat(): |
| 852 |
"""Gets the module's output format."""
|
| 853 |
return _cpplint_state.output_format
|
| 854 |
|
| 855 |
|
| 856 |
def _SetOutputFormat(output_format): |
| 857 |
"""Sets the module's output format."""
|
| 858 |
_cpplint_state.SetOutputFormat(output_format) |
| 859 |
|
| 860 |
|
| 861 |
def _VerboseLevel(): |
| 862 |
"""Returns the module's verbosity setting."""
|
| 863 |
return _cpplint_state.verbose_level
|
| 864 |
|
| 865 |
|
| 866 |
def _SetVerboseLevel(level): |
| 867 |
"""Sets the module's verbosity, and returns the previous setting."""
|
| 868 |
return _cpplint_state.SetVerboseLevel(level)
|
| 869 |
|
| 870 |
|
| 871 |
def _SetCountingStyle(level): |
| 872 |
"""Sets the module's counting options."""
|
| 873 |
_cpplint_state.SetCountingStyle(level) |
| 874 |
|
| 875 |
|
| 876 |
def _Filters(): |
| 877 |
"""Returns the module's list of output filters, as a list."""
|
| 878 |
return _cpplint_state.filters
|
| 879 |
|
| 880 |
|
| 881 |
def _SetFilters(filters): |
| 882 |
"""Sets the module's error-message filters.
|
| 883 |
|
| 884 |
These filters are applied when deciding whether to emit a given
|
| 885 |
error message.
|
| 886 |
|
| 887 |
Args:
|
| 888 |
filters: A string of comma-separated filters (eg "whitespace/indent").
|
| 889 |
Each filter should start with + or -; else we die.
|
| 890 |
"""
|
| 891 |
_cpplint_state.SetFilters(filters) |
| 892 |
|
| 893 |
def _AddFilters(filters): |
| 894 |
"""Adds more filter overrides.
|
| 895 |
|
| 896 |
Unlike _SetFilters, this function does not reset the current list of filters
|
| 897 |
available.
|
| 898 |
|
| 899 |
Args:
|
| 900 |
filters: A string of comma-separated filters (eg "whitespace/indent").
|
| 901 |
Each filter should start with + or -; else we die.
|
| 902 |
"""
|
| 903 |
_cpplint_state.AddFilters(filters) |
| 904 |
|
| 905 |
def _BackupFilters(): |
| 906 |
""" Saves the current filter list to backup storage."""
|
| 907 |
_cpplint_state.BackupFilters() |
| 908 |
|
| 909 |
def _RestoreFilters(): |
| 910 |
""" Restores filters previously backed up."""
|
| 911 |
_cpplint_state.RestoreFilters() |
| 912 |
|
| 913 |
class _FunctionState(object): |
| 914 |
"""Tracks current function name and the number of lines in its body."""
|
| 915 |
|
| 916 |
_NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. |
| 917 |
_TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. |
| 918 |
|
| 919 |
def __init__(self): |
| 920 |
self.in_a_function = False |
| 921 |
self.lines_in_function = 0 |
| 922 |
self.current_function = '' |
| 923 |
|
| 924 |
def Begin(self, function_name): |
| 925 |
"""Start analyzing function body.
|
| 926 |
|
| 927 |
Args:
|
| 928 |
function_name: The name of the function being tracked.
|
| 929 |
"""
|
| 930 |
self.in_a_function = True |
| 931 |
self.lines_in_function = 0 |
| 932 |
self.current_function = function_name
|
| 933 |
|
| 934 |
def Count(self): |
| 935 |
"""Count line in current function body."""
|
| 936 |
if self.in_a_function: |
| 937 |
self.lines_in_function += 1 |
| 938 |
|
| 939 |
def Check(self, error, filename, linenum): |
| 940 |
"""Report if too many lines in function body.
|
| 941 |
|
| 942 |
Args:
|
| 943 |
error: The function to call with any errors found.
|
| 944 |
filename: The name of the current file.
|
| 945 |
linenum: The number of the line to check.
|
| 946 |
"""
|
| 947 |
if Match(r'T(EST|est)', self.current_function): |
| 948 |
base_trigger = self._TEST_TRIGGER
|
| 949 |
else:
|
| 950 |
base_trigger = self._NORMAL_TRIGGER
|
| 951 |
trigger = base_trigger * 2**_VerboseLevel()
|
| 952 |
|
| 953 |
if self.lines_in_function > trigger: |
| 954 |
error_level = int(math.log(self.lines_in_function / base_trigger, 2)) |
| 955 |
# 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
|
| 956 |
if error_level > 5: |
| 957 |
error_level = 5
|
| 958 |
error(filename, linenum, 'readability/fn_size', error_level,
|
| 959 |
'Small and focused functions are preferred:'
|
| 960 |
' %s has %d non-comment lines'
|
| 961 |
' (error triggered by exceeding %d lines).' % (
|
| 962 |
self.current_function, self.lines_in_function, trigger)) |
| 963 |
|
| 964 |
def End(self): |
| 965 |
"""Stop analyzing function body."""
|
| 966 |
self.in_a_function = False |
| 967 |
|
| 968 |
|
| 969 |
class _IncludeError(Exception): |
| 970 |
"""Indicates a problem with the include order in a file."""
|
| 971 |
pass
|
| 972 |
|
| 973 |
|
| 974 |
class FileInfo(object): |
| 975 |
"""Provides utility functions for filenames.
|
| 976 |
|
| 977 |
FileInfo provides easy access to the components of a file's path
|
| 978 |
relative to the project root.
|
| 979 |
"""
|
| 980 |
|
| 981 |
def __init__(self, filename): |
| 982 |
self._filename = filename
|
| 983 |
|
| 984 |
def FullName(self): |
| 985 |
"""Make Windows paths like Unix."""
|
| 986 |
return os.path.abspath(self._filename).replace('\\', '/') |
| 987 |
|
| 988 |
def RepositoryName(self): |
| 989 |
"""FullName after removing the local path to the repository.
|
| 990 |
|
| 991 |
If we have a real absolute path name here we can try to do something smart:
|
| 992 |
detecting the root of the checkout and truncating /path/to/checkout from
|
| 993 |
the name so that we get header guards that don't include things like
|
| 994 |
"C:\Documents and Settings\..." or "/home/username/..." in them and thus
|
| 995 |
people on different computers who have checked the source out to different
|
| 996 |
locations won't see bogus errors.
|
| 997 |
"""
|
| 998 |
fullname = self.FullName()
|
| 999 |
|
| 1000 |
if os.path.exists(fullname):
|
| 1001 |
project_dir = os.path.dirname(fullname) |
| 1002 |
|
| 1003 |
if os.path.exists(os.path.join(project_dir, ".svn")): |
| 1004 |
# If there's a .svn file in the current directory, we recursively look
|
| 1005 |
# up the directory tree for the top of the SVN checkout
|
| 1006 |
root_dir = project_dir |
| 1007 |
one_up_dir = os.path.dirname(root_dir) |
| 1008 |
while os.path.exists(os.path.join(one_up_dir, ".svn")): |
| 1009 |
root_dir = os.path.dirname(root_dir) |
| 1010 |
one_up_dir = os.path.dirname(one_up_dir) |
| 1011 |
|
| 1012 |
prefix = os.path.commonprefix([root_dir, project_dir]) |
| 1013 |
return fullname[len(prefix) + 1:] |
| 1014 |
|
| 1015 |
# Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
|
| 1016 |
# searching up from the current path.
|
| 1017 |
root_dir = os.path.dirname(fullname) |
| 1018 |
while (root_dir != os.path.dirname(root_dir) and |
| 1019 |
not os.path.exists(os.path.join(root_dir, ".git")) and |
| 1020 |
not os.path.exists(os.path.join(root_dir, ".hg")) and |
| 1021 |
not os.path.exists(os.path.join(root_dir, ".svn"))): |
| 1022 |
root_dir = os.path.dirname(root_dir) |
| 1023 |
|
| 1024 |
if (os.path.exists(os.path.join(root_dir, ".git")) or |
| 1025 |
os.path.exists(os.path.join(root_dir, ".hg")) or |
| 1026 |
os.path.exists(os.path.join(root_dir, ".svn"))):
|
| 1027 |
prefix = os.path.commonprefix([root_dir, project_dir]) |
| 1028 |
return fullname[len(prefix) + 1:] |
| 1029 |
|
| 1030 |
# Don't know what to do; header guard warnings may be wrong...
|
| 1031 |
return fullname
|
| 1032 |
|
| 1033 |
def Split(self): |
| 1034 |
"""Splits the file into the directory, basename, and extension.
|
| 1035 |
|
| 1036 |
For 'chrome/browser/browser.cc', Split() would
|
| 1037 |
return ('chrome/browser', 'browser', '.cc')
|
| 1038 |
|
| 1039 |
Returns:
|
| 1040 |
A tuple of (directory, basename, extension).
|
| 1041 |
"""
|
| 1042 |
|
| 1043 |
googlename = self.RepositoryName()
|
| 1044 |
project, rest = os.path.split(googlename) |
| 1045 |
return (project,) + os.path.splitext(rest)
|
| 1046 |
|
| 1047 |
def BaseName(self): |
| 1048 |
"""File base name - text after the final slash, before the final period."""
|
| 1049 |
return self.Split()[1] |
| 1050 |
|
| 1051 |
def Extension(self): |
| 1052 |
"""File extension - text following the final period."""
|
| 1053 |
return self.Split()[2] |
| 1054 |
|
| 1055 |
def NoExtension(self): |
| 1056 |
"""File has no source file extension."""
|
| 1057 |
return '/'.join(self.Split()[0:2]) |
| 1058 |
|
| 1059 |
def IsSource(self): |
| 1060 |
"""File has a source file extension."""
|
| 1061 |
return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') |
| 1062 |
|
| 1063 |
|
| 1064 |
def _ShouldPrintError(category, confidence, linenum): |
| 1065 |
"""If confidence >= verbose, category passes filter and is not suppressed."""
|
| 1066 |
|
| 1067 |
# There are three ways we might decide not to print an error message:
|
| 1068 |
# a "NOLINT(category)" comment appears in the source,
|
| 1069 |
# the verbosity level isn't high enough, or the filters filter it out.
|
| 1070 |
if IsErrorSuppressedByNolint(category, linenum):
|
| 1071 |
return False |
| 1072 |
|
| 1073 |
if confidence < _cpplint_state.verbose_level:
|
| 1074 |
return False |
| 1075 |
|
| 1076 |
is_filtered = False
|
| 1077 |
for one_filter in _Filters(): |
| 1078 |
if one_filter.startswith('-'): |
| 1079 |
if category.startswith(one_filter[1:]): |
| 1080 |
is_filtered = True
|
| 1081 |
elif one_filter.startswith('+'): |
| 1082 |
if category.startswith(one_filter[1:]): |
| 1083 |
is_filtered = False
|
| 1084 |
else:
|
| 1085 |
assert False # should have been checked for in SetFilter. |
| 1086 |
if is_filtered:
|
| 1087 |
return False |
| 1088 |
|
| 1089 |
return True |
| 1090 |
|
| 1091 |
|
| 1092 |
def Error(filename, linenum, category, confidence, message): |
| 1093 |
"""Logs the fact we've found a lint error.
|
| 1094 |
|
| 1095 |
We log where the error was found, and also our confidence in the error,
|
| 1096 |
that is, how certain we are this is a legitimate style regression, and
|
| 1097 |
not a misidentification or a use that's sometimes justified.
|
| 1098 |
|
| 1099 |
False positives can be suppressed by the use of
|
| 1100 |
"cpplint(category)" comments on the offending line. These are
|
| 1101 |
parsed into _error_suppressions.
|
| 1102 |
|
| 1103 |
Args:
|
| 1104 |
filename: The name of the file containing the error.
|
| 1105 |
linenum: The number of the line containing the error.
|
| 1106 |
category: A string used to describe the "category" this bug
|
| 1107 |
falls under: "whitespace", say, or "runtime". Categories
|
| 1108 |
may have a hierarchy separated by slashes: "whitespace/indent".
|
| 1109 |
confidence: A number from 1-5 representing a confidence score for
|
| 1110 |
the error, with 5 meaning that we are certain of the problem,
|
| 1111 |
and 1 meaning that it could be a legitimate construct.
|
| 1112 |
message: The error message.
|
| 1113 |
"""
|
| 1114 |
if _ShouldPrintError(category, confidence, linenum):
|
| 1115 |
_cpplint_state.IncrementErrorCount(category) |
| 1116 |
if _cpplint_state.output_format == 'vs7': |
| 1117 |
sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
|
| 1118 |
filename, linenum, message, category, confidence)) |
| 1119 |
elif _cpplint_state.output_format == 'eclipse': |
| 1120 |
sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
|
| 1121 |
filename, linenum, message, category, confidence)) |
| 1122 |
else:
|
| 1123 |
sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
|
| 1124 |
filename, linenum, message, category, confidence)) |
| 1125 |
|
| 1126 |
|
| 1127 |
# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
|
| 1128 |
_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( |
| 1129 |
r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
|
| 1130 |
# Match a single C style comment on the same line.
|
| 1131 |
_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/'
|
| 1132 |
# Matches multi-line C style comments.
|
| 1133 |
# This RE is a little bit more complicated than one might expect, because we
|
| 1134 |
# have to take care of space removals tools so we can handle comments inside
|
| 1135 |
# statements better.
|
| 1136 |
# The current rule is: We only clear spaces from both sides when we're at the
|
| 1137 |
# end of the line. Otherwise, we try to remove spaces from the right side,
|
| 1138 |
# if this doesn't work we try on left side but only if there's a non-character
|
| 1139 |
# on the right.
|
| 1140 |
_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( |
| 1141 |
r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + |
| 1142 |
_RE_PATTERN_C_COMMENTS + r'\s+|' +
|
| 1143 |
r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + |
| 1144 |
_RE_PATTERN_C_COMMENTS + r')')
|
| 1145 |
|
| 1146 |
|
| 1147 |
def IsCppString(line): |
| 1148 |
"""Does line terminate so, that the next symbol is in string constant.
|
| 1149 |
|
| 1150 |
This function does not consider single-line nor multi-line comments.
|
| 1151 |
|
| 1152 |
Args:
|
| 1153 |
line: is a partial line of code starting from the 0..n.
|
| 1154 |
|
| 1155 |
Returns:
|
| 1156 |
True, if next character appended to 'line' is inside a
|
| 1157 |
string constant.
|
| 1158 |
"""
|
| 1159 |
|
| 1160 |
line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" |
| 1161 |
return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 |
| 1162 |
|
| 1163 |
|
| 1164 |
def CleanseRawStrings(raw_lines): |
| 1165 |
"""Removes C++11 raw strings from lines.
|
| 1166 |
|
| 1167 |
Before:
|
| 1168 |
static const char kData[] = R"(
|
| 1169 |
multi-line string
|
| 1170 |
)";
|
| 1171 |
|
| 1172 |
After:
|
| 1173 |
static const char kData[] = ""
|
| 1174 |
(replaced by blank line)
|
| 1175 |
"";
|
| 1176 |
|
| 1177 |
Args:
|
| 1178 |
raw_lines: list of raw lines.
|
| 1179 |
|
| 1180 |
Returns:
|
| 1181 |
list of lines with C++11 raw strings replaced by empty strings.
|
| 1182 |
"""
|
| 1183 |
|
| 1184 |
delimiter = None
|
| 1185 |
lines_without_raw_strings = [] |
| 1186 |
for line in raw_lines: |
| 1187 |
if delimiter:
|
| 1188 |
# Inside a raw string, look for the end
|
| 1189 |
end = line.find(delimiter) |
| 1190 |
if end >= 0: |
| 1191 |
# Found the end of the string, match leading space for this
|
| 1192 |
# line and resume copying the original lines, and also insert
|
| 1193 |
# a "" on the last line.
|
| 1194 |
leading_space = Match(r'^(\s*)\S', line)
|
| 1195 |
line = leading_space.group(1) + '""' + line[end + len(delimiter):] |
| 1196 |
delimiter = None
|
| 1197 |
else:
|
| 1198 |
# Haven't found the end yet, append a blank line.
|
| 1199 |
line = '""'
|
| 1200 |
|
| 1201 |
# Look for beginning of a raw string, and replace them with
|
| 1202 |
# empty strings. This is done in a loop to handle multiple raw
|
| 1203 |
# strings on the same line.
|
| 1204 |
while delimiter is None: |
| 1205 |
# Look for beginning of a raw string.
|
| 1206 |
# See 2.14.15 [lex.string] for syntax.
|
| 1207 |
matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
|
| 1208 |
if matched:
|
| 1209 |
delimiter = ')' + matched.group(2) + '"' |
| 1210 |
|
| 1211 |
end = matched.group(3).find(delimiter)
|
| 1212 |
if end >= 0: |
| 1213 |
# Raw string ended on same line
|
| 1214 |
line = (matched.group(1) + '""' + |
| 1215 |
matched.group(3)[end + len(delimiter):]) |
| 1216 |
delimiter = None
|
| 1217 |
else:
|
| 1218 |
# Start of a multi-line raw string
|
| 1219 |
line = matched.group(1) + '""' |
| 1220 |
else:
|
| 1221 |
break
|
| 1222 |
|
| 1223 |
lines_without_raw_strings.append(line) |
| 1224 |
|
| 1225 |
# TODO(unknown): if delimiter is not None here, we might want to
|
| 1226 |
# emit a warning for unterminated string.
|
| 1227 |
return lines_without_raw_strings
|
| 1228 |
|
| 1229 |
|
| 1230 |
def FindNextMultiLineCommentStart(lines, lineix): |
| 1231 |
"""Find the beginning marker for a multiline comment."""
|
| 1232 |
while lineix < len(lines): |
| 1233 |
if lines[lineix].strip().startswith('/*'): |
| 1234 |
# Only return this marker if the comment goes beyond this line
|
| 1235 |
if lines[lineix].strip().find('*/', 2) < 0: |
| 1236 |
return lineix
|
| 1237 |
lineix += 1
|
| 1238 |
return len(lines) |
| 1239 |
|
| 1240 |
|
| 1241 |
def FindNextMultiLineCommentEnd(lines, lineix): |
| 1242 |
"""We are inside a comment, find the end marker."""
|
| 1243 |
while lineix < len(lines): |
| 1244 |
if lines[lineix].strip().endswith('*/'): |
| 1245 |
return lineix
|
| 1246 |
lineix += 1
|
| 1247 |
return len(lines) |
| 1248 |
|
| 1249 |
|
| 1250 |
def RemoveMultiLineCommentsFromRange(lines, begin, end): |
| 1251 |
"""Clears a range of lines for multi-line comments."""
|
| 1252 |
# Having // dummy comments makes the lines non-empty, so we will not get
|
| 1253 |
# unnecessary blank line warnings later in the code.
|
| 1254 |
for i in range(begin, end): |
| 1255 |
lines[i] = '/**/'
|
| 1256 |
|
| 1257 |
|
| 1258 |
def RemoveMultiLineComments(filename, lines, error): |
| 1259 |
"""Removes multiline (c-style) comments from lines."""
|
| 1260 |
lineix = 0
|
| 1261 |
while lineix < len(lines): |
| 1262 |
lineix_begin = FindNextMultiLineCommentStart(lines, lineix) |
| 1263 |
if lineix_begin >= len(lines): |
| 1264 |
return
|
| 1265 |
lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) |
| 1266 |
if lineix_end >= len(lines): |
| 1267 |
error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, |
| 1268 |
'Could not find end of multi-line comment')
|
| 1269 |
return
|
| 1270 |
RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
|
| 1271 |
lineix = lineix_end + 1
|
| 1272 |
|
| 1273 |
|
| 1274 |
def CleanseComments(line): |
| 1275 |
"""Removes //-comments and single-line C-style /* */ comments.
|
| 1276 |
|
| 1277 |
Args:
|
| 1278 |
line: A line of C++ source.
|
| 1279 |
|
| 1280 |
Returns:
|
| 1281 |
The line with single-line comments removed.
|
| 1282 |
"""
|
| 1283 |
commentpos = line.find('//')
|
| 1284 |
if commentpos != -1 and not IsCppString(line[:commentpos]): |
| 1285 |
line = line[:commentpos].rstrip() |
| 1286 |
# get rid of /* ... */
|
| 1287 |
return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) |
| 1288 |
|
| 1289 |
|
| 1290 |
class CleansedLines(object): |
| 1291 |
|