• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Copyright (C) 2009 Google Inc. All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#    * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#    * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#    * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Script to run the linter for source code of WebKit."""
32
33import codecs
34import os
35import os.path
36import sys
37
38import modules.cpp_style as cpp_style
39from modules.diff_parser import DiffParser
40from modules.scm import detect_scm_system
41
42
43# Override the usage of the lint tool.
44cpp_style._USAGE = """
45Syntax: %(program_name)s [--verbose=#] [--git-commit=<SingleCommit>] [--output=vs7]
46        [--filter=-x,+y,...] [file] ...
47
48  The style guidelines this tries to follow are those in
49    http://webkit.org/coding/coding-style.html
50
51  Every problem is given a confidence score from 1-5, with 5 meaning we are
52  certain of the problem, and 1 meaning it could be a legitimate construct.
53  This will miss some errors, and is not a substitute for a code review.
54
55  To prevent specific lines from being linted, add a '// NOLINT' comment to the
56  end of the line.
57
58  Linted extensions are .cpp, .c and .h.  Other file types will be ignored.
59
60  The file parameter is optional and multiple files to scan be passed in.
61  Leaving out the file parameter will apply the check to the files changed
62  according to the scm system.
63
64  Flags:
65
66    verbose=#
67      Specify a number 0-5 to restrict errors to certain verbosity levels.
68
69    git-commit=<SingleCommit>
70      Checks the style of everything from the given commit to the local tree.
71
72    output=vs7
73      By default, the output is formatted to ease emacs parsing.  Visual Studio
74      compatible output (vs7) may also be used.  Other formats are unsupported.
75
76    filter=-x,+y,...
77      Specify a comma-separated list of category-filters to apply: only
78      error messages whose category names pass the filters will be printed.
79      (Category names are printed with the message and look like
80      "[whitespace/indent]".)  Filters are evaluated left to right.
81      "-FOO" and "FOO" means "do not print categories that start with FOO".
82      "+FOO" means "do print categories that start with FOO".
83
84      Examples: --filter=-whitespace,+whitespace/braces
85                --filter=whitespace,runtime/printf,+runtime/printf_format
86                --filter=-,+build/include_what_you_use
87
88      To see a list of all the categories used in %(program_name)s, pass no arg:
89         --filter=
90""" % {'program_name': os.path.basename(sys.argv[0])}
91
92
93def process_patch(patch_string):
94    """Does lint on a single patch.
95
96    Args:
97      patch_string: A string of a patch.
98    """
99    patch = DiffParser(patch_string.splitlines())
100    for filename, diff in patch.files.iteritems():
101        file_extension = os.path.splitext(filename)[1]
102
103        if file_extension in ['.cpp', '.c', '.h']:
104            line_numbers = set()
105
106            def error_for_patch(filename, line_number, category, confidence, message):
107                """Wrapper function of cpp_style.error for patches.
108
109                This function outputs errors only if the line number
110                corresponds to lines which are modified or added.
111                """
112                if not line_numbers:
113                    for line in diff.lines:
114                        # When deleted line is not set, it means that
115                        # the line is newly added.
116                        if not line[0]:
117                            line_numbers.add(line[1])
118
119                if line_number in line_numbers:
120                    cpp_style.error(filename, line_number, category, confidence, message)
121
122            cpp_style.process_file(filename, error=error_for_patch)
123
124
125def main():
126    cpp_style.use_webkit_styles()
127
128    (files, flags) = cpp_style.parse_arguments(sys.argv[1:], ["git-commit="])
129
130    # Change stderr to write with replacement characters so we don't die
131    # if we try to print something containing non-ASCII characters.
132    sys.stderr = codecs.StreamReaderWriter(sys.stderr,
133                                           codecs.getreader('utf8'),
134                                           codecs.getwriter('utf8'),
135                                           'replace')
136
137    if files and "--git-commit" in flags:
138        sys.stderr.write("ERROR: It is not possible to check files "
139                          "and a specific commit at the same time.\n" + cpp_style._USAGE)
140        sys.exit(1)
141
142    if files:
143        for filename in files:
144            cpp_style.process_file(filename)
145
146    else:
147        cwd = os.path.abspath('.')
148        scm = detect_scm_system(cwd)
149
150        if "--git-commit" in flags:
151            commit = flags["--git-commit"]
152            if '..' in commit:
153                # FIXME: If the range is a "...", the code should find the common ancestor and
154                # start there (see git diff --help for information about how ... usually works).
155                commit = commit[:commit.find('..')]
156                print >> sys.stderr, "Warning: Ranges are not supported for --git-commit. Checking all changes since %s.\n" % commit
157            process_patch(scm.create_patch_since_local_commit(commit))
158        else:
159            process_patch(scm.create_patch())
160
161    sys.stderr.write('Total errors found: %d\n' % cpp_style.error_count())
162    sys.exit(cpp_style.error_count() > 0)
163
164
165if __name__ == "__main__":
166    main()
167