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