1fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#!/usr/bin/python
2fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
3fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Copyright (c) 2009 Google Inc. All rights reserved.
4fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
5fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Redistribution and use in source and binary forms, with or without
6fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# modification, are permitted provided that the following conditions are
7fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# met:
8fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
9fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    * Redistributions of source code must retain the above copyright
10fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# notice, this list of conditions and the following disclaimer.
11fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    * Redistributions in binary form must reproduce the above
12fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# copyright notice, this list of conditions and the following disclaimer
13fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# in the documentation and/or other materials provided with the
14fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# distribution.
15fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    * Neither the name of Google Inc. nor the names of its
16fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# contributors may be used to endorse or promote products derived from
17fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# this software without specific prior written permission.
18fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
19fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
31fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Here are some issues that I've had people identify in my code during reviews,
32fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# that I think are possible to flag automatically in a lint tool.  If these were
33fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# caught by lint, it would save time both for myself and that of my reviewers.
34fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Most likely, some of these are beyond the scope of the current lint framework,
35fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# but I think it is valuable to retain these wish-list items even if they cannot
36fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# be immediately implemented.
37fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
38fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  Suggestions
39fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  -----------
40fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for no 'explicit' for multi-arg ctor
41fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for boolean assign RHS in parens
42fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for ctor initializer-list colon position and spacing
43fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check that if there's a ctor, there should be a dtor
44fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check accessors that return non-pointer member variables are
45fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    declared const
46fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check accessors that return non-const pointer member vars are
47fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    *not* declared const
48fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for using public includes for testing
49fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for spaces between brackets in one-line inline method
50fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for no assert()
51fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for spaces surrounding operators
52fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for 0 in pointer context (should be NULL)
53fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for 0 in char context (should be '\0')
54fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for camel-case method name conventions for methods
55fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    that are not simple inline getters and setters
56fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Do not indent namespace contents
57fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Avoid inlining non-trivial constructors in header files
58fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for old-school (void) cast for call-sites of functions
59fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    ignored return value
60fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check gUnit usage of anonymous namespace
61fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#  - Check for class declaration order (typedefs, consts, enums,
62fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#    ctor(s?), dtor, friend declarations, methods, member vars)
63fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
64fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
65fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov"""Does google-lint on c++ files.
66fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
67fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey SamsonovThe goal of this script is to identify places in the code that *may*
68fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovbe in non-compliance with google style.  It does not attempt to fix
69fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovup these problems -- the point is to educate.  It does also not
70fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovattempt to find all problems, or to ensure that everything it does
71fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovfind is legitimately a problem.
72fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
73fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey SamsonovIn particular, we can get very confused by /* and // inside strings!
74fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey SamsonovWe do a small hack, which is to ignore //'s with "'s after them on the
75fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovsame line, but it is far from perfect (in either direction).
76fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov"""
77fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
78fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport codecs
79fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport copy
80fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport getopt
81fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport math  # for log
82fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport os
83fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport re
84fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport sre_compile
85fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport string
86fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport sys
87fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovimport unicodedata
88fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
89fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
90fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_USAGE = """
91fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey SamsonovSyntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
92fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                   [--counting=total|toplevel|detailed]
93fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        <file> [file] ...
94fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
95fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  The style guidelines this tries to follow are those in
96fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
97fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
98fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Every problem is given a confidence score from 1-5, with 5 meaning we are
99fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  certain of the problem, and 1 meaning it could be a legitimate construct.
100fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  This will miss some errors, and is not a substitute for a code review.
101fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
102fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  To suppress false-positive errors of a certain category, add a
103fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'NOLINT(category)' comment to the line.  NOLINT or NOLINT(*)
104fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  suppresses errors of all categories on that line.
105fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
106fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  The files passed in will be linted; at least one file must be provided.
107fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Linted extensions are .cc, .cpp, and .h.  Other file types will be ignored.
108fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
109fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Flags:
110fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
111fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    output=vs7
112fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      By default, the output is formatted to ease emacs parsing.  Visual Studio
113fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      compatible output (vs7) may also be used.  Other formats are unsupported.
114fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
115fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    verbose=#
116fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Specify a number 0-5 to restrict errors to certain verbosity levels.
117fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
118fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    filter=-x,+y,...
119fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Specify a comma-separated list of category-filters to apply: only
120fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error messages whose category names pass the filters will be printed.
121fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      (Category names are printed with the message and look like
122fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      "[whitespace/indent]".)  Filters are evaluated left to right.
123fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      "-FOO" and "FOO" means "do not print categories that start with FOO".
124fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      "+FOO" means "do print categories that start with FOO".
125fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
126fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Examples: --filter=-whitespace,+whitespace/braces
127fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                --filter=whitespace,runtime/printf,+runtime/printf_format
128fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                --filter=-,+build/include_what_you_use
129fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
130fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      To see a list of all the categories used in cpplint, pass no arg:
131fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov         --filter=
132fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
133fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    counting=total|toplevel|detailed
134fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      The total number of errors found is always printed. If
135fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      'toplevel' is provided, then the count of errors in each of
136fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      the top-level categories like 'build' and 'whitespace' will
137fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      also be printed. If 'detailed' is provided, then a count
138fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      is provided for each category like 'build/class'.
139fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
140fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    root=subdir
141fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      The root directory used for deriving header guard CPP variable.
142fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      By default, the header guard CPP variable is calculated as the relative
143fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      path to the directory that contains .git, .hg, or .svn.  When this flag
144fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      is specified, the relative path is calculated from the specified
145fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      directory. If the specified directory does not exist, this flag is
146fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      ignored.
147fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
148fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Examples:
149fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        Assuing that src/.git exists, the header guard CPP variables for
150fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        src/chrome/browser/ui/browser.h are:
151fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
152fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        No flag => CHROME_BROWSER_UI_BROWSER_H_
153fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        --root=chrome => BROWSER_UI_BROWSER_H_
154fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        --root=chrome/browser => UI_BROWSER_H_
155fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov"""
156fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
157fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# We categorize each error message we print.  Here are the categories.
158fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# We want an explicit list so we can list them all in cpplint --filter=.
159fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# If you add a new error message with a new category, add it to the list
160fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# here!  cpplint_unittest.py should tell you if you forget to do this.
161fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# \ used for clearer layout -- pylint: disable-msg=C6013
162fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_ERROR_CATEGORIES = [
163fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/class',
164fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/deprecated',
165fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/endif_comment',
166fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/explicit_make_pair',
167fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/forward_decl',
168fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/header_guard',
169fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/include',
170fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/include_alpha',
171fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/include_order',
172fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/include_what_you_use',
173fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/namespaces',
174fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/printf_format',
175fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'build/storage_class',
176fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'legal/copyright',
177fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/alt_tokens',
178fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/braces',
179fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/casting',
180fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/check',
181fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/constructors',
182fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/fn_size',
183fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/function',
184fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/multiline_comment',
185fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/multiline_string',
186fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/namespace',
187fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/nolint',
188fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/streams',
189fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/todo',
190fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'readability/utf8',
191fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/arrays',
192fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/casting',
193fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/explicit',
194fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/int',
195fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/init',
196fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/invalid_increment',
197fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/member_string_references',
198fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/memset',
199fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/operator',
200fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/printf',
201fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/printf_format',
202fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/references',
203fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/rtti',
204fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/sizeof',
205fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/string',
206fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'runtime/threadsafe_fn',
207fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/blank_line',
208fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/braces',
209fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/comma',
210fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/comments',
211fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/empty_loop_body',
212fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/end_of_line',
213fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/ending_newline',
214fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/forcolon',
215fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/indent',
216fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/labels',
217fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/line_length',
218fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/newline',
219fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/operators',
220fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/parens',
221fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/semicolon',
222fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/tab',
223fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  'whitespace/todo'
224fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  ]
225fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
226fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# The default state of the category filter. This is overrided by the --filter=
227fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# flag. By default all errors are on, so only add here categories that should be
228fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# off by default (i.e., categories that must be enabled by the --filter= flags).
229fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# All entries here should start with a '-' or '+', as in the --filter= flag.
230fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_DEFAULT_FILTERS = ['-build/include_alpha']
231fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
232fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# We used to check for high-bit characters, but after much discussion we
233fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# decided those were OK, as long as they were in UTF-8 and didn't represent
234fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# hard-coded international strings, which belong in a separate i18n file.
235fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
236fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Headers that we consider STL headers.
237fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_STL_HEADERS = frozenset([
238fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception',
239fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set',
240fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new',
241fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack',
242fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'stl_alloc.h', 'stl_relops.h', 'type_traits.h',
243fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'utility', 'vector', 'vector.h',
244fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    ])
245fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
246fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
247fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Non-STL C++ system headers.
248fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_CPP_HEADERS = frozenset([
249fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype',
250fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath',
251fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef',
252fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype',
253fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream',
254fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip',
255fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream',
256fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h',
257fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h',
258fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h',
259fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept',
260fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h',
261fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo',
262fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'valarray',
263fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    ])
264fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
265fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
266fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Assertion macros.  These are defined in base/logging.h and
267fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# testing/base/gunit.h.  Note that the _M versions need to come first
268fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# for substring matching to work.
269fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_CHECK_MACROS = [
270fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'DCHECK', 'CHECK',
271fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'EXPECT_TRUE_M', 'EXPECT_TRUE',
272fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'ASSERT_TRUE_M', 'ASSERT_TRUE',
273fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'EXPECT_FALSE_M', 'EXPECT_FALSE',
274fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'ASSERT_FALSE_M', 'ASSERT_FALSE',
275fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    ]
276fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
277fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
278fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
279fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
280fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovfor op, replacement in [('==', 'EQ'), ('!=', 'NE'),
281fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                        ('>=', 'GE'), ('>', 'GT'),
282fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                        ('<=', 'LE'), ('<', 'LT')]:
283fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
284fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
285fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
286fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
287fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement
288fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement
289fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
290fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovfor op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
291fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                            ('>=', 'LT'), ('>', 'LE'),
292fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                            ('<=', 'GT'), ('<', 'GE')]:
293fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
294fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
295fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement
296fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement
297fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
298fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Alternative tokens and their replacements.  For full list, see section 2.5
299fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Alternative tokens [lex.digraph] in the C++ standard.
300fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
301fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Digraphs (such as '%:') are not included here since it's a mess to
302fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# match those on a word boundary.
303fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_ALT_TOKEN_REPLACEMENT = {
304fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'and': '&&',
305fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'bitor': '|',
306fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'or': '||',
307fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'xor': '^',
308fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'compl': '~',
309fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'bitand': '&',
310fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'and_eq': '&=',
311fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'or_eq': '|=',
312fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'xor_eq': '^=',
313fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'not': '!',
314fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'not_eq': '!='
315fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    }
316fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
317fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Compile regular expression that matches all the above keywords.  The "[ =()]"
318fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# bit is meant to avoid matching these keywords outside of boolean expressions.
319fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov#
320fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# False positives include C-style multi-line comments (http://go/nsiut )
321fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# and multi-line strings (http://go/beujw ), but those have always been
322fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# troublesome for cpplint.
323fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
324fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
325fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
326fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
327fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# These constants define types of headers for use with
328fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# _IncludeState.CheckNextIncludeOrder().
329fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_C_SYS_HEADER = 1
330fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_CPP_SYS_HEADER = 2
331fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_LIKELY_MY_HEADER = 3
332fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_POSSIBLE_MY_HEADER = 4
333fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_OTHER_HEADER = 5
334fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
335fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# These constants define the current inline assembly state
336fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_NO_ASM = 0       # Outside of inline assembly block
337fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_INSIDE_ASM = 1   # Inside inline assembly block
338fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_END_ASM = 2      # Last line of inline assembly block
339fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_BLOCK_ASM = 3    # The whole block is an inline assembly block
340fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
341fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Match start of assembly blocks
342fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
343fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                        r'(?:\s+(volatile|__volatile__))?'
344fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                        r'\s*[{(]')
345fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
346fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
347fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_regexp_compile_cache = {}
348fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
349fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Finds occurrences of NOLINT or NOLINT(...).
350fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?')
351fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
352fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# {str, set(int)}: a map from error categories to sets of linenumbers
353fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# on which those errors are expected and should be suppressed.
354fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_error_suppressions = {}
355fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
356fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# The root directory used for deriving header guard CPP variable.
357fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# This is set by --root flag.
358fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_root = None
359fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
360fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef ParseNolintSuppressions(filename, raw_line, linenum, error):
361fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Updates the global list of error-suppressions.
362fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
363fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Parses any NOLINT comments on the current line, updating the global
364fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  error_suppressions store.  Reports an error if the NOLINT comment
365fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  was malformed.
366fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
367fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
368fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    filename: str, the name of the input file.
369fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    raw_line: str, the line of input text, with comments.
370fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    linenum: int, the number of the current line.
371fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    error: function, an error handler.
372fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
373fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*).
374fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  matched = _RE_SUPPRESSION.search(raw_line)
375fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if matched:
376fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    category = matched.group(1)
377fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if category in (None, '(*)'):  # => "suppress all"
378fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _error_suppressions.setdefault(None, set()).add(linenum)
379fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    else:
380fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if category.startswith('(') and category.endswith(')'):
381fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        category = category[1:-1]
382fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        if category in _ERROR_CATEGORIES:
383fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          _error_suppressions.setdefault(category, set()).add(linenum)
384fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        else:
385fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          error(filename, linenum, 'readability/nolint', 5,
386fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                'Unknown NOLINT error category: %s' % category)
387fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
388fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
389fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef ResetNolintSuppressions():
390fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  "Resets the set of NOLINT suppressions to empty."
391fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _error_suppressions.clear()
392fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
393fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
394fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef IsErrorSuppressedByNolint(category, linenum):
395fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Returns true if the specified error category is suppressed on this line.
396fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
397fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Consults the global error_suppressions map populated by
398fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  ParseNolintSuppressions/ResetNolintSuppressions.
399fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
400fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
401fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    category: str, the category of the error.
402fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    linenum: int, the current line number.
403fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
404fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    bool, True iff the error should be suppressed due to a NOLINT comment.
405fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
406fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return (linenum in _error_suppressions.get(category, set()) or
407fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          linenum in _error_suppressions.get(None, set()))
408fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
409fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef Match(pattern, s):
410fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Matches the string with the pattern, caching the compiled regexp."""
411fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # The regexp compilation caching is inlined in both Match and Search for
412fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # performance reasons; factoring it out into a separate function turns out
413fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # to be noticeably expensive.
414fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if not pattern in _regexp_compile_cache:
415fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
416fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _regexp_compile_cache[pattern].match(s)
417fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
418fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
419fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef Search(pattern, s):
420fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Searches the string for the pattern, caching the compiled regexp."""
421fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if not pattern in _regexp_compile_cache:
422fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
423fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _regexp_compile_cache[pattern].search(s)
424fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
425fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
426fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass _IncludeState(dict):
427fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Tracks line numbers for includes, and the order in which includes appear.
428fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
429fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  As a dict, an _IncludeState object serves as a mapping between include
430fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  filename and line number on which that file was included.
431fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
432fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Call CheckNextIncludeOrder() once for each header in the file, passing
433fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  in the type constants defined above. Calls in an illegal order will
434fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  raise an _IncludeError with an appropriate error message.
435fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
436fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
437fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # self._section will move monotonically through this set. If it ever
438fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # needs to move backwards, CheckNextIncludeOrder will raise an error.
439fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _INITIAL_SECTION = 0
440fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _MY_H_SECTION = 1
441fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _C_SECTION = 2
442fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _CPP_SECTION = 3
443fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _OTHER_H_SECTION = 4
444fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
445fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _TYPE_NAMES = {
446fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _C_SYS_HEADER: 'C system header',
447fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _CPP_SYS_HEADER: 'C++ system header',
448fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _LIKELY_MY_HEADER: 'header this file implements',
449fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _POSSIBLE_MY_HEADER: 'header this file may implement',
450fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _OTHER_HEADER: 'other header',
451fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      }
452fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _SECTION_NAMES = {
453fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _INITIAL_SECTION: "... nothing. (This can't be an error.)",
454fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _MY_H_SECTION: 'a header this file implements',
455fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _C_SECTION: 'C system header',
456fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _CPP_SECTION: 'C++ system header',
457fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      _OTHER_H_SECTION: 'other header',
458fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      }
459fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
460fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def __init__(self):
461fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    dict.__init__(self)
462fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # The name of the current section.
463fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self._section = self._INITIAL_SECTION
464fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # The path of last found header.
465fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self._last_header = ''
466fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
467fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def CanonicalizeAlphabeticalOrder(self, header_path):
468fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Returns a path canonicalized for alphabetical comparison.
469fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
470fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    - replaces "-" with "_" so they both cmp the same.
471fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    - removes '-inl' since we don't require them to be after the main header.
472fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    - lowercase everything, just in case.
473fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
474fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
475fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      header_path: Path to be canonicalized.
476fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
477fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Returns:
478fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Canonicalized path.
479fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
480fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
481fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
482fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def IsInAlphabeticalOrder(self, header_path):
483fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Check if a header is in alphabetical order with the previous header.
484fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
485fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
486fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      header_path: Header to be checked.
487fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
488fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Returns:
489fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      Returns true if the header is in alphabetical order.
490fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
491fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    canonical_header = self.CanonicalizeAlphabeticalOrder(header_path)
492fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if self._last_header > canonical_header:
493fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      return False
494fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self._last_header = canonical_header
495fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return True
496fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
497fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def CheckNextIncludeOrder(self, header_type):
498fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Returns a non-empty error message if the next header is out of order.
499fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
500fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    This function also updates the internal state to be ready to check
501fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    the next include.
502fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
503fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
504fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      header_type: One of the _XXX_HEADER constants defined above.
505fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
506fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Returns:
507fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      The empty string if the header is in the right order, or an
508fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error message describing what's wrong.
509fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
510fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
511fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    error_message = ('Found %s after %s' %
512fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                     (self._TYPE_NAMES[header_type],
513fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                      self._SECTION_NAMES[self._section]))
514fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
515fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    last_section = self._section
516fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
517fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if header_type == _C_SYS_HEADER:
518fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if self._section <= self._C_SECTION:
519fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._C_SECTION
520fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      else:
521fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._last_header = ''
522fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return error_message
523fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif header_type == _CPP_SYS_HEADER:
524fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if self._section <= self._CPP_SECTION:
525fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._CPP_SECTION
526fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      else:
527fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._last_header = ''
528fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return error_message
529fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif header_type == _LIKELY_MY_HEADER:
530fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if self._section <= self._MY_H_SECTION:
531fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._MY_H_SECTION
532fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      else:
533fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._OTHER_H_SECTION
534fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif header_type == _POSSIBLE_MY_HEADER:
535fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if self._section <= self._MY_H_SECTION:
536fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._MY_H_SECTION
537fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      else:
538fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        # This will always be the fallback because we're not sure
539fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        # enough that the header is associated with this file.
540fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self._section = self._OTHER_H_SECTION
541fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    else:
542fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      assert header_type == _OTHER_HEADER
543fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self._section = self._OTHER_H_SECTION
544fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
545fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if last_section != self._section:
546fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self._last_header = ''
547fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
548fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return ''
549fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
550fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
551fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass _CppLintState(object):
552fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Maintains module-wide state.."""
553fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
554fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def __init__(self):
555fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.verbose_level = 1  # global setting.
556fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.error_count = 0    # global count of reported errors
557fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # filters to apply when emitting error messages
558fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.filters = _DEFAULT_FILTERS[:]
559fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.counting = 'total'  # In what way are we counting errors?
560fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.errors_by_category = {}  # string to int dict storing error counts
561fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
562fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # output format:
563fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # "emacs" - format that emacs can parse (default)
564fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # "vs7" - format that Microsoft Visual Studio 7 can parse
565fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.output_format = 'emacs'
566fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
567fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def SetOutputFormat(self, output_format):
568fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Sets the output format for errors."""
569fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.output_format = output_format
570fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
571fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def SetVerboseLevel(self, level):
572fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Sets the module's verbosity, and returns the previous setting."""
573fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    last_verbose_level = self.verbose_level
574fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.verbose_level = level
575fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return last_verbose_level
576fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
577fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def SetCountingStyle(self, counting_style):
578fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Sets the module's counting options."""
579fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.counting = counting_style
580fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
581fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def SetFilters(self, filters):
582fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Sets the error-message filters.
583fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
584fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    These filters are applied when deciding whether to emit a given
585fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    error message.
586fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
587fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
588fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      filters: A string of comma-separated filters (eg "+whitespace/indent").
589fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov               Each filter should start with + or -; else we die.
590fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
591fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Raises:
592fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      ValueError: The comma-separated filters did not all start with '+' or '-'.
593fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                  E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
594fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
595fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # Default filters always have less priority than the flag ones.
596fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.filters = _DEFAULT_FILTERS[:]
597fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    for filt in filters.split(','):
598fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      clean_filt = filt.strip()
599fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if clean_filt:
600fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self.filters.append(clean_filt)
601fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    for filt in self.filters:
602fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if not (filt.startswith('+') or filt.startswith('-')):
603fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        raise ValueError('Every filter in --filters must start with + or -'
604fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                         ' (%s does not)' % filt)
605fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
606fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def ResetErrorCounts(self):
607fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Sets the module's error statistic back to zero."""
608fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.error_count = 0
609fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.errors_by_category = {}
610fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
611fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def IncrementErrorCount(self, category):
612fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Bumps the module's error statistic."""
613fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.error_count += 1
614fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if self.counting in ('toplevel', 'detailed'):
615fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if self.counting != 'detailed':
616fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        category = category.split('/')[0]
617fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if category not in self.errors_by_category:
618fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        self.errors_by_category[category] = 0
619fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self.errors_by_category[category] += 1
620fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
621fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def PrintErrorCounts(self):
622fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Print a summary of errors by category, and the total."""
623fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    for category, count in self.errors_by_category.iteritems():
624fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      sys.stderr.write('Category \'%s\' errors found: %d\n' %
625fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                       (category, count))
626fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    sys.stderr.write('Total errors found: %d\n' % self.error_count)
627fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
628fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_cpplint_state = _CppLintState()
629fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
630fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
631fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _OutputFormat():
632fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Gets the module's output format."""
633fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _cpplint_state.output_format
634fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
635fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
636fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _SetOutputFormat(output_format):
637fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Sets the module's output format."""
638fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _cpplint_state.SetOutputFormat(output_format)
639fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
640fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
641fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _VerboseLevel():
642fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Returns the module's verbosity setting."""
643fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _cpplint_state.verbose_level
644fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
645fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
646fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _SetVerboseLevel(level):
647fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Sets the module's verbosity, and returns the previous setting."""
648fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _cpplint_state.SetVerboseLevel(level)
649fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
650fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
651fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _SetCountingStyle(level):
652fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Sets the module's counting options."""
653fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _cpplint_state.SetCountingStyle(level)
654fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
655fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
656fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _Filters():
657fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Returns the module's list of output filters, as a list."""
658fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _cpplint_state.filters
659fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
660fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
661fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _SetFilters(filters):
662fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Sets the module's error-message filters.
663fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
664fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  These filters are applied when deciding whether to emit a given
665fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  error message.
666fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
667fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
668fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    filters: A string of comma-separated filters (eg "whitespace/indent").
669fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov             Each filter should start with + or -; else we die.
670fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
671fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _cpplint_state.SetFilters(filters)
672fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
673fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
674fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass _FunctionState(object):
675fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Tracks current function name and the number of lines in its body."""
676fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
677fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _NORMAL_TRIGGER = 250  # for --v=0, 500 for --v=1, etc.
678fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  _TEST_TRIGGER = 400    # about 50% more than _NORMAL_TRIGGER.
679fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
680fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def __init__(self):
681fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.in_a_function = False
682fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.lines_in_function = 0
683fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.current_function = ''
684fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
685fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def Begin(self, function_name):
686fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Start analyzing function body.
687fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
688fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
689fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      function_name: The name of the function being tracked.
690fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
691fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.in_a_function = True
692fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.lines_in_function = 0
693fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.current_function = function_name
694fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
695fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def Count(self):
696fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Count line in current function body."""
697fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if self.in_a_function:
698fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self.lines_in_function += 1
699fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
700fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def Check(self, error, filename, linenum):
701fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Report if too many lines in function body.
702fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
703fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
704fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error: The function to call with any errors found.
705fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      filename: The name of the current file.
706fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      linenum: The number of the line to check.
707fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
708fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if Match(r'T(EST|est)', self.current_function):
709fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      base_trigger = self._TEST_TRIGGER
710fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    else:
711fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      base_trigger = self._NORMAL_TRIGGER
712fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    trigger = base_trigger * 2**_VerboseLevel()
713fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
714fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if self.lines_in_function > trigger:
715fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error_level = int(math.log(self.lines_in_function / base_trigger, 2))
716fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
717fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if error_level > 5:
718fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        error_level = 5
719fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error(filename, linenum, 'readability/fn_size', error_level,
720fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            'Small and focused functions are preferred:'
721fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            ' %s has %d non-comment lines'
722fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            ' (error triggered by exceeding %d lines).'  % (
723fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov                self.current_function, self.lines_in_function, trigger))
724fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
725fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def End(self):
726fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Stop analyzing function body."""
727fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.in_a_function = False
728fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
729fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
730fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass _IncludeError(Exception):
731fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Indicates a problem with the include order in a file."""
732fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  pass
733fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
734fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
735fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass FileInfo:
736fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Provides utility functions for filenames.
737fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
738fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  FileInfo provides easy access to the components of a file's path
739fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  relative to the project root.
740fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
741fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
742fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def __init__(self, filename):
743fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self._filename = filename
744fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
745fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def FullName(self):
746fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Make Windows paths like Unix."""
747fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return os.path.abspath(self._filename).replace('\\', '/')
748fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
749fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def RepositoryName(self):
750fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """FullName after removing the local path to the repository.
751fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
752fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    If we have a real absolute path name here we can try to do something smart:
753fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    detecting the root of the checkout and truncating /path/to/checkout from
754fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    the name so that we get header guards that don't include things like
755fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    "C:\Documents and Settings\..." or "/home/username/..." in them and thus
756fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    people on different computers who have checked the source out to different
757fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    locations won't see bogus errors.
758fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
759fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    fullname = self.FullName()
760fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
761fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if os.path.exists(fullname):
762fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      project_dir = os.path.dirname(fullname)
763fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
764fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if os.path.exists(os.path.join(project_dir, ".svn")):
765fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        # If there's a .svn file in the current directory, we recursively look
766fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        # up the directory tree for the top of the SVN checkout
767fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        root_dir = project_dir
768fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        one_up_dir = os.path.dirname(root_dir)
769fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        while os.path.exists(os.path.join(one_up_dir, ".svn")):
770fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          root_dir = os.path.dirname(root_dir)
771fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          one_up_dir = os.path.dirname(one_up_dir)
772fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
773fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        prefix = os.path.commonprefix([root_dir, project_dir])
774fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return fullname[len(prefix) + 1:]
775fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
776fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
777fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # searching up from the current path.
778fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      root_dir = os.path.dirname(fullname)
779fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      while (root_dir != os.path.dirname(root_dir) and
780fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov             not os.path.exists(os.path.join(root_dir, ".git")) and
781fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov             not os.path.exists(os.path.join(root_dir, ".hg")) and
782fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov             not os.path.exists(os.path.join(root_dir, ".svn"))):
783fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        root_dir = os.path.dirname(root_dir)
784fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
785fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if (os.path.exists(os.path.join(root_dir, ".git")) or
786fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          os.path.exists(os.path.join(root_dir, ".hg")) or
787fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          os.path.exists(os.path.join(root_dir, ".svn"))):
788fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        prefix = os.path.commonprefix([root_dir, project_dir])
789fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return fullname[len(prefix) + 1:]
790fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
791fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    # Don't know what to do; header guard warnings may be wrong...
792fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return fullname
793fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
794fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def Split(self):
795fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Splits the file into the directory, basename, and extension.
796fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
797fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    For 'chrome/browser/browser.cc', Split() would
798fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return ('chrome/browser', 'browser', '.cc')
799fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
800fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Returns:
801fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      A tuple of (directory, basename, extension).
802fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
803fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
804fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    googlename = self.RepositoryName()
805fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    project, rest = os.path.split(googlename)
806fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return (project,) + os.path.splitext(rest)
807fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
808fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def BaseName(self):
809fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """File base name - text after the final slash, before the final period."""
810fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return self.Split()[1]
811fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
812fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def Extension(self):
813fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """File extension - text following the final period."""
814fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return self.Split()[2]
815fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
816fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def NoExtension(self):
817fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """File has no source file extension."""
818fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return '/'.join(self.Split()[0:2])
819fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
820fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def IsSource(self):
821fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """File has a source file extension."""
822fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
823fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
824fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
825fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef _ShouldPrintError(category, confidence, linenum):
826fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """If confidence >= verbose, category passes filter and is not suppressed."""
827fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
828fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # There are three ways we might decide not to print an error message:
829fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # a "NOLINT(category)" comment appears in the source,
830fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # the verbosity level isn't high enough, or the filters filter it out.
831fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if IsErrorSuppressedByNolint(category, linenum):
832fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return False
833fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if confidence < _cpplint_state.verbose_level:
834fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return False
835fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
836fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  is_filtered = False
837fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  for one_filter in _Filters():
838fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if one_filter.startswith('-'):
839fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if category.startswith(one_filter[1:]):
840fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        is_filtered = True
841fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif one_filter.startswith('+'):
842fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if category.startswith(one_filter[1:]):
843fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        is_filtered = False
844fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    else:
845fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      assert False  # should have been checked for in SetFilter.
846fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if is_filtered:
847fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return False
848fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
849fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return True
850fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
851fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
852fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef Error(filename, linenum, category, confidence, message):
853fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Logs the fact we've found a lint error.
854fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
855fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  We log where the error was found, and also our confidence in the error,
856fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  that is, how certain we are this is a legitimate style regression, and
857fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  not a misidentification or a use that's sometimes justified.
858fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
859fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  False positives can be suppressed by the use of
860fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  "cpplint(category)"  comments on the offending line.  These are
861fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  parsed into _error_suppressions.
862fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
863fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
864fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    filename: The name of the file containing the error.
865fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    linenum: The number of the line containing the error.
866fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    category: A string used to describe the "category" this bug
867fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      falls under: "whitespace", say, or "runtime".  Categories
868fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      may have a hierarchy separated by slashes: "whitespace/indent".
869fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    confidence: A number from 1-5 representing a confidence score for
870fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      the error, with 5 meaning that we are certain of the problem,
871fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      and 1 meaning that it could be a legitimate construct.
872fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    message: The error message.
873fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
874fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if _ShouldPrintError(category, confidence, linenum):
875fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    _cpplint_state.IncrementErrorCount(category)
876fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if _cpplint_state.output_format == 'vs7':
877fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      sys.stderr.write('%s(%s):  %s  [%s] [%d]\n' % (
878fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          filename, linenum, message, category, confidence))
879fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif _cpplint_state.output_format == 'eclipse':
880fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      sys.stderr.write('%s:%s: warning: %s  [%s] [%d]\n' % (
881fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          filename, linenum, message, category, confidence))
882fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    else:
883fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      sys.stderr.write('%s:%s:  %s  [%s] [%d]\n' % (
884fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          filename, linenum, message, category, confidence))
885fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
886fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
887fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard.
888fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
889fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
890fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Matches strings.  Escape codes should already be removed by ESCAPES.
891fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"')
892fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Matches characters.  Escape codes should already be removed by ESCAPES.
893fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'")
894fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# Matches multi-line C++ comments.
895fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# This RE is a little bit more complicated than one might expect, because we
896fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# have to take care of space removals tools so we can handle comments inside
897fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# statements better.
898fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# The current rule is: We only clear spaces from both sides when we're at the
899fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# end of the line. Otherwise, we try to remove spaces from the right side,
900fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# if this doesn't work we try on left side but only if there's a non-character
901fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov# on the right.
902fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
903fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    r"""(\s*/\*.*\*/\s*$|
904fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            /\*.*\*/\s+|
905fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov         \s+/\*.*\*/(?=\W)|
906fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            /\*.*\*/)""", re.VERBOSE)
907fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
908fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
909fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef IsCppString(line):
910fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Does line terminate so, that the next symbol is in string constant.
911fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
912fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  This function does not consider single-line nor multi-line comments.
913fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
914fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
915fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    line: is a partial line of code starting from the 0..n.
916fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
917fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
918fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    True, if next character appended to 'line' is inside a
919fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    string constant.
920fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
921fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
922fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  line = line.replace(r'\\', 'XX')  # after this, \\" does not match to \"
923fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
924fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
925fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
926fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef FindNextMultiLineCommentStart(lines, lineix):
927fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Find the beginning marker for a multiline comment."""
928fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  while lineix < len(lines):
929fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if lines[lineix].strip().startswith('/*'):
930fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # Only return this marker if the comment goes beyond this line
931fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if lines[lineix].strip().find('*/', 2) < 0:
932fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return lineix
933fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lineix += 1
934fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return len(lines)
935fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
936fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
937fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef FindNextMultiLineCommentEnd(lines, lineix):
938fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """We are inside a comment, find the end marker."""
939fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  while lineix < len(lines):
940fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if lines[lineix].strip().endswith('*/'):
941fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      return lineix
942fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lineix += 1
943fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return len(lines)
944fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
945fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
946fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef RemoveMultiLineCommentsFromRange(lines, begin, end):
947fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Clears a range of lines for multi-line comments."""
948fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # Having // dummy comments makes the lines non-empty, so we will not get
949fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # unnecessary blank line warnings later in the code.
950fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  for i in range(begin, end):
951fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lines[i] = '// dummy'
952fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
953fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
954fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef RemoveMultiLineComments(filename, lines, error):
955fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Removes multiline (c-style) comments from lines."""
956fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  lineix = 0
957fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  while lineix < len(lines):
958fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
959fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if lineix_begin >= len(lines):
960fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      return
961fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
962fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if lineix_end >= len(lines):
963fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
964fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov            'Could not find end of multi-line comment')
965fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      return
966fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
967fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    lineix = lineix_end + 1
968fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
969fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
970fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef CleanseComments(line):
971fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Removes //-comments and single-line C-style /* */ comments.
972fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
973fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
974fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    line: A line of C++ source.
975fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
976fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
977fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    The line with single-line comments removed.
978fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
979fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  commentpos = line.find('//')
980fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if commentpos != -1 and not IsCppString(line[:commentpos]):
981fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    line = line[:commentpos].rstrip()
982fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # get rid of /* ... */
983fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
984fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
985fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
986fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovclass CleansedLines(object):
987fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Holds 3 copies of all lines with different preprocessing applied to them.
988fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
989fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  1) elided member contains lines without strings and comments,
990fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  2) lines member contains lines without comments, and
991fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  3) raw_lines member contains all the lines without processing.
992fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  All these three members are of <type 'list'>, and of the same length.
993fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
994fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
995fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def __init__(self, lines):
996fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.elided = []
997fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.lines = []
998fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.raw_lines = lines
999fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    self.num_lines = len(lines)
1000fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    for linenum in range(len(lines)):
1001fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self.lines.append(CleanseComments(lines[linenum]))
1002fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      elided = self._CollapseStrings(lines[linenum])
1003fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      self.elided.append(CleanseComments(elided))
1004fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1005fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def NumLines(self):
1006fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Returns the number of lines represented."""
1007fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return self.num_lines
1008fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1009fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  @staticmethod
1010fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  def _CollapseStrings(elided):
1011fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """Collapses strings and chars on a line to simple "" or '' blocks.
1012fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1013fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    We nix strings first so we're not fooled by text like '"http://"'
1014fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1015fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Args:
1016fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      elided: The line being processed.
1017fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1018fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Returns:
1019fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      The line with collapsed strings.
1020fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    """
1021fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if not _RE_PATTERN_INCLUDE.match(elided):
1022fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # Remove escaped characters first to make quote/single quote collapsing
1023fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # basic.  Things that look like escaped characters shouldn't occur
1024fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      # outside of strings and chars.
1025fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
1026fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided)
1027fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided)
1028fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return elided
1029fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1030fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1031fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
1032fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Find the position just after the matching endchar.
1033fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1034fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
1035fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    line: a CleansedLines line.
1036fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    startpos: start searching at this position.
1037fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    depth: nesting level at startpos.
1038fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    startchar: expression opening character.
1039fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    endchar: expression closing character.
1040fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1041fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
1042fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    Index just after endchar.
1043fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
1044fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  for i in xrange(startpos, len(line)):
1045fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if line[i] == startchar:
1046fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      depth += 1
1047fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    elif line[i] == endchar:
1048fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      depth -= 1
1049fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      if depth == 0:
1050fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov        return i + 1
1051fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return -1
1052fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1053fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1054fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef CloseExpression(clean_lines, linenum, pos):
1055fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """If input points to ( or { or [, finds the position that closes it.
1056fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1057fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  If lines[linenum][pos] points to a '(' or '{' or '[', finds the
1058fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  linenum/pos that correspond to the closing of the expression.
1059fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1060fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
1061fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    clean_lines: A CleansedLines instance containing the file.
1062fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    linenum: The number of the line to check.
1063fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    pos: A position on the line.
1064fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1065fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
1066fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    A tuple (line, linenum, pos) pointer *past* the closing brace, or
1067fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    (line, len(lines), -1) if we never find a close.  Note we ignore
1068fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    strings and comments when matching; and the line we return is the
1069fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    'cleansed' line at linenum.
1070fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
1071fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1072fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  line = clean_lines.elided[linenum]
1073fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  startchar = line[pos]
1074fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if startchar not in '({[':
1075fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return (line, clean_lines.NumLines(), -1)
1076fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if startchar == '(': endchar = ')'
1077fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if startchar == '[': endchar = ']'
1078fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if startchar == '{': endchar = '}'
1079fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1080fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # Check first line
1081fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar)
1082fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if end_pos > -1:
1083fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    return (line, linenum, end_pos)
1084fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  tail = line[pos:]
1085fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  num_open = tail.count(startchar) - tail.count(endchar)
1086fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  while linenum < clean_lines.NumLines() - 1:
1087fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    linenum += 1
1088fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    line = clean_lines.elided[linenum]
1089fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    delta = line.count(startchar) - line.count(endchar)
1090fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if num_open + delta <= 0:
1091fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov      return (line, linenum,
1092fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov              FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar))
1093fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    num_open += delta
1094fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1095fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # Did not find endchar before end of file, give up
1096fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  return (line, clean_lines.NumLines(), -1)
1097fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1098fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef CheckForCopyright(filename, lines, error):
1099fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Logs an error if no Copyright message appears at the top of the file."""
1100fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1101fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # We'll say it should occur by line 10. Don't forget there's a
1102fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # dummy line at the front.
1103fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  for line in xrange(1, min(len(lines), 11)):
1104fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    if re.search(r'Copyright', lines[line], re.I): break
1105fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  else:                       # means no copyright line was found
1106fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    error(filename, 0, 'legal/copyright', 5,
1107fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          'No copyright message found.  '
1108fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov          'You should have a line: "Copyright [year] <Copyright Owner>"')
1109fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1110fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1111fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonovdef GetHeaderGuardCPPVariable(filename):
1112fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """Returns the CPP variable that should be used as a header guard.
1113fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1114fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Args:
1115fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    filename: The name of a C++ header file.
1116fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1117fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  Returns:
1118fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    The CPP variable that should be used as a header guard in the
1119fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    named file.
1120fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1121fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  """
1122fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1123fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # Restores original filename in case that cpplint is invoked from Emacs's
1124fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  # flymake.
1125fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  filename = re.sub(r'_flymake\.h$', '.h', filename)
1126fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
1127fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov
1128fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  fileinfo = FileInfo(filename)
1129fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  file_path_from_root = fileinfo.RepositoryName()
1130fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov  if _root:
1131fafab2f5e27eecbfaaee727f66ecd068dafa41feAlexey Samsonov    file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root)
1132