blob: ed7da8e8813c55e5b7b63aaeafea4d55d6c93525 [file] [log] [blame]
Jordan Bayles5d694412023-02-23 01:30:051# Copyright 2018 The Chromium Authors
Ryan Tseng85ec17e2018-09-07 00:10:052# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Jordan Bayles5d694412023-02-23 01:30:054# pragma pylint: disable=invalid-name, import-error
5"""
6This file acts as a git pre-submission checker, and uses the support tooling
7from depot_tools to check a variety of style and programming requirements.
8"""
Ryan Tseng85ec17e2018-09-07 00:10:059
Jordan Bayles5d694412023-02-23 01:30:0510from collections import namedtuple
Jordan Baylese50a1d62020-11-30 20:31:4711import os
mark a. foltz01490a42020-11-19 18:33:3612import re
Jordan Baylese50a1d62020-11-30 20:31:4713import sys
14
Jordan Bayles5d694412023-02-23 01:30:0515
Jordan Baylese50a1d62020-11-30 20:31:4716_REPO_PATH = os.path.dirname(os.path.realpath('__file__'))
17_IMPORT_SUBFOLDERS = ['tools', os.path.join('buildtools', 'checkdeps')]
18
19# git-cl upload is not compatible with __init__.py based subfolder imports, so
20# we extend the system path instead.
mark a. foltz1cc7ba92023-11-15 18:23:1621#
22# TODO(crbug.com/1502599): Migrate these modules to depot_tools and import from
23# there.
Jordan Baylese50a1d62020-11-30 20:31:4724sys.path.extend(os.path.join(_REPO_PATH, p) for p in _IMPORT_SUBFOLDERS)
25
mark a. foltz1cc7ba92023-11-15 18:23:1626
Jordan Bayles5d694412023-02-23 01:30:0527from checkdeps import DepsChecker # pylint: disable=wrong-import-position
28import licenses # pylint: disable=wrong-import-position
Jordan Bayles6e279a02021-05-27 21:24:1229
Jordan Baylese50a1d62020-11-30 20:31:4730def _CheckLicenses(input_api, output_api):
31 """Checks third party licenses and returns a list of violations."""
Jordan Bayles5d694412023-02-23 01:30:0532 # NOTE: the licenses check is confused by the fact that we don't actually
33 # check ou the libraries in buildtools/third_party, so explicitly exclude
34 # that folder. See https://crbug.com/1215335 for more info.
Jordan Bayles3adadc62024-02-01 06:29:4135 licenses.PRUNE_PATHS.update([
36 os.path.join('buildtools', 'third_party'),
37 os.path.join('third_party', 'libc++'),
38 os.path.join('third_party', 'libc++abi'),
mark a. foltz105a2e52025-01-07 00:10:0539 os.path.join('third_party', 'rust-toolchain'),
40 os.path.join('third_party', 'depot_tools')
Jordan Bayles3adadc62024-02-01 06:29:4141 ])
Jordan Bayles5d694412023-02-23 01:30:0542
43 if any(s.LocalPath().startswith('third_party')
44 for s in input_api.change.AffectedFiles()):
45 return [
46 output_api.PresubmitError(v)
47 for v in licenses.ScanThirdPartyDirs()
48 ]
49 return []
btolsch9ba23712019-04-18 23:36:5550
Jordan Baylese50a1d62020-11-30 20:31:4751
52def _CheckDeps(input_api, output_api):
53 """Checks DEPS rules and returns a list of violations."""
54 deps_checker = DepsChecker(input_api.PresubmitLocalPath())
55 deps_checker.CheckDirectory(input_api.PresubmitLocalPath())
56 deps_results = deps_checker.results_formatter.GetResults()
57 return [output_api.PresubmitError(v) for v in deps_results]
btolsch9ba23712019-04-18 23:36:5558
59
Jordan Bayles5d694412023-02-23 01:30:0560# Arguments passed to methods by cpplint.
61CpplintArgs = namedtuple("CpplintArgs", "filename clean_lines linenum error")
Jordan Bayles963b0a62020-12-02 21:23:5962
Jordan Bayles5d694412023-02-23 01:30:0563# A defined error to return to cpplint.
64Error = namedtuple("Error", "type message")
mark a. foltz01490a42020-11-19 18:33:3665
66
Jordan Bayles5d694412023-02-23 01:30:0567def _CheckNoRegexMatches(regex, cpplint_args, error, include_cpp_files=True):
Jordan Bayles963b0a62020-12-02 21:23:5968 """Checks that there are no matches for a specific regex.
69
70 Args:
Jordan Bayles5d694412023-02-23 01:30:0571 regex: The regex to use for matching.
72 cpplint_args: The arguments passed to us by cpplint.
73 error: The error to return if we find an issue.
74 include_cpp_files: Whether C++ files should be checked.
75 """
76 if not include_cpp_files and not cpplint_args.filename.endswith('.h'):
Jordan Bayles963b0a62020-12-02 21:23:5977 return
78
Jordan Bayles5d694412023-02-23 01:30:0579 line = cpplint_args.clean_lines.elided[cpplint_args.linenum]
Jordan Bayles963b0a62020-12-02 21:23:5980 matched = regex.match(line)
81 if matched:
Jordan Bayles5d694412023-02-23 01:30:0582 cpplint_args.error(
83 cpplint_args.filename, cpplint_args.linenum, error.type, 4,
84 f'Error: {error.message} at {matched.group(0).strip()}')
85
86
87# Matches OSP_CHECK(foo.is_value()) or OSP_DCHECK(foo.is_value())
88_RE_PATTERN_VALUE_CHECK = re.compile(
89 r'\s*OSP_D?CHECK\([^)]*\.is_value\(\)\);\s*')
Jordan Bayles963b0a62020-12-02 21:23:5990
91
92def _CheckNoValueDchecks(filename, clean_lines, linenum, error):
93 """Checks that there are no OSP_DCHECK(foo.is_value()) instances.
94
95 filename: The name of the current file.
96 clean_lines: A CleansedLines instance containing the file.
97 linenum: The number of the line to check.
98 error: The function to call with any errors found.
99 """
Jordan Bayles5d694412023-02-23 01:30:05100 cpplint_args = CpplintArgs(filename, clean_lines, linenum, error)
101 error_to_return = Error('runtime/is_value_dchecks',
102 'Unnecessary CHECK for ErrorOr::is_value()')
103
104 _CheckNoRegexMatches(_RE_PATTERN_VALUE_CHECK, cpplint_args,
105 error_to_return)
106
107
108# Matches Foo(Foo&&) when not followed by noexcept.
109_RE_PATTERN_MOVE_WITHOUT_NOEXCEPT = re.compile(
110 r'\s*(?P<classname>\w+)\((?P=classname)&&[^)]*\)\s*(?!noexcept)\s*[{;=]')
Jordan Bayles963b0a62020-12-02 21:23:59111
112
mark a. foltz01490a42020-11-19 18:33:36113def _CheckNoexceptOnMove(filename, clean_lines, linenum, error):
Jordan Baylese50a1d62020-11-30 20:31:47114 """Checks that move constructors are declared with 'noexcept'.
mark a. foltz01490a42020-11-19 18:33:36115
mark a. foltz01490a42020-11-19 18:33:36116 filename: The name of the current file.
117 clean_lines: A CleansedLines instance containing the file.
118 linenum: The number of the line to check.
119 error: The function to call with any errors found.
Jordan Bayles5d694412023-02-23 01:30:05120 """
121 cpplint_args = CpplintArgs(filename, clean_lines, linenum, error)
122 error_to_return = Error('runtime/noexcept',
123 'Move constructor not declared \'noexcept\'')
124
Jordan Baylese50a1d62020-11-30 20:31:47125 # We only check headers as noexcept is meaningful on declarations, not
126 # definitions. This may skip some definitions in .cc files though.
Jordan Bayles5d694412023-02-23 01:30:05127 _CheckNoRegexMatches(_RE_PATTERN_MOVE_WITHOUT_NOEXCEPT, cpplint_args,
128 error_to_return, False)
129
130
Jordan Baylesb926e152023-10-02 16:53:58131# Matches "namespace <foo> {". Since we only check one line at a time, we
132# need to call this twice.
133_RE_PATTERN_UNNESTED_NAMESPACE = re.compile(
134 r'namespace +\w+ +\{')
135
136
137def _CheckUnnestedNamespaces(filename, clean_lines, linenum, error):
138 """Checks that nestable namespaces are nested.
139
140 filename: The name of the current file.
141 clean_lines: A CleansedLines instance containing the file.
142 linenum: The number of the line to check.
143 error: The function to call with any errors found.
144 """
145
146 # If we have a match for a namespace on this line, check the next line for
147 # an nestable namespace declaration.
148 re = _RE_PATTERN_UNNESTED_NAMESPACE
149 if re.match(clean_lines.elided[linenum]):
150 cpplint_args = CpplintArgs(filename, clean_lines, linenum + 1, error)
151 error_to_return = Error('runtime/nested_namespace',
152 'Please nest namespaces when possible.')
153 _CheckNoRegexMatches(re, cpplint_args, error_to_return)
154
155
Jordan Bayles5d694412023-02-23 01:30:05156# Gives additional debug information whenever a linting error occurs.
157_CPPLINT_VERBOSE_LEVEL = 4
mark a. foltz01490a42020-11-19 18:33:36158
Jordan Baylesb926e152023-10-02 16:53:58159
mark a. foltz01490a42020-11-19 18:33:36160# - We disable c++11 header checks since Open Screen allows them.
161# - We disable whitespace/braces because of various false positives.
162# - There are some false positives with 'explicit' checks, but it's useful
163# enough to keep.
164# - We add a custom check for 'noexcept' usage.
165def _CheckChangeLintsClean(input_api, output_api):
Jordan Baylese50a1d62020-11-30 20:31:47166 """Checks that all '.cc' and '.h' files pass cpplint.py."""
167 cpplint = input_api.cpplint
Jordan Bayles425c1362021-05-27 21:33:11168 # Directive that allows access to a protected member _XX of a client class.
Jordan Baylese50a1d62020-11-30 20:31:47169 # pylint: disable=protected-access
170 cpplint._cpplint_state.ResetErrorCounts()
mark a. foltz01490a42020-11-19 18:33:36171
Jordan Baylese50a1d62020-11-30 20:31:47172 cpplint._SetFilters('-build/c++11,-whitespace/braces')
173 files = [
174 f.AbsoluteLocalPath() for f in input_api.AffectedSourceFiles(None)
175 ]
Jordan Bayles5d694412023-02-23 01:30:05176
Jordan Baylese50a1d62020-11-30 20:31:47177 for file_name in files:
Jordan Baylesb926e152023-10-02 16:53:58178 cpplint.ProcessFile(file_name, _CPPLINT_VERBOSE_LEVEL, [
179 _CheckNoexceptOnMove, _CheckNoValueDchecks,
180 _CheckUnnestedNamespaces
181 ])
mark a. foltz01490a42020-11-19 18:33:36182
Jordan Baylese50a1d62020-11-30 20:31:47183 if cpplint._cpplint_state.error_count:
184 if input_api.is_committing:
185 res_type = output_api.PresubmitError
186 else:
187 res_type = output_api.PresubmitPromptWarning
188 return [res_type('Changelist failed cpplint.py check.')]
mark a. foltz01490a42020-11-19 18:33:36189
Jordan Baylese50a1d62020-11-30 20:31:47190 return []
mark a. foltz01490a42020-11-19 18:33:36191
192
Jordan Bayles4d9a1ed2023-02-22 19:26:01193def _CheckLuciCfgLint(input_api, output_api):
194 """Check that the luci configs pass the linter."""
195 path = os.path.join('infra', 'config', 'global', 'main.star')
Jordan Baylesb926e152023-10-02 16:53:58196 if not input_api.AffectedSourceFiles(
197 lambda f: os.path.samefile(f.AbsoluteLocalPath(), path)):
Jordan Bayles4d9a1ed2023-02-22 19:26:01198 return []
199
200 result = []
201 result.extend(input_api.RunTests([input_api.Command(
202 'lucicfg lint',
203 [
204 'lucicfg' if not input_api.is_windows else 'lucicfg.bat', 'lint',
205 path, '--log-level', 'debug' if input_api.verbose else 'warning'
206 ],
207 {
208 'stderr': input_api.subprocess.STDOUT,
209 'shell': input_api.is_windows, # to resolve *.bat
210 'cwd': input_api.PresubmitLocalPath(),
211 },
212 output_api.PresubmitError)]))
213 return result
Jordan Bayles0a682462021-09-02 18:44:48214
215
btolsch333aecd2019-04-18 23:21:23216def _CommonChecks(input_api, output_api):
Jordan Bayles5d694412023-02-23 01:30:05217 """Performs a list of checks that should be used for both presubmission and
218 upload validation.
219 """
Jordan Baylese50a1d62020-11-30 20:31:47220 # PanProjectChecks include:
221 # CheckLongLines (@ 80 cols)
222 # CheckChangeHasNoTabs
223 # CheckChangeHasNoStrayWhitespace
Jordan Baylese50a1d62020-11-30 20:31:47224 # CheckChangeWasUploaded (if committing)
225 # CheckChangeHasDescription
226 # CheckDoNotSubmitInDescription
227 # CheckDoNotSubmitInFiles
Jordan Bayles5d694412023-02-23 01:30:05228 # CheckLicenses
Jordan Baylese50a1d62020-11-30 20:31:47229 results = input_api.canned_checks.PanProjectChecks(input_api,
230 output_api,
231 owners_check=False)
mark a. foltz4410e8e2020-05-08 00:10:17232
Jordan Baylese50a1d62020-11-30 20:31:47233 # No carriage return characters, files end with one EOL (\n).
234 results.extend(
235 input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
236 input_api, output_api))
mark a. foltz4410e8e2020-05-08 00:10:17237
Jordan Bayles425c1362021-05-27 21:33:11238 # Ensure code change is gender inclusive.
Jordan Baylese50a1d62020-11-30 20:31:47239 results.extend(
240 input_api.canned_checks.CheckGenderNeutral(input_api, output_api))
mark a. foltz4410e8e2020-05-08 00:10:17241
Jordan Bayles425c1362021-05-27 21:33:11242 # Ensure code change to do items uses TODO(bug) or TODO(user) format.
243 # TODO(bug) is generally preferred.
Jordan Baylese50a1d62020-11-30 20:31:47244 results.extend(
245 input_api.canned_checks.CheckChangeTodoHasOwner(input_api, output_api))
mark a. foltz4410e8e2020-05-08 00:10:17246
Jordan Bayles425c1362021-05-27 21:33:11247 # Ensure code change passes linter cleanly.
Jordan Baylese50a1d62020-11-30 20:31:47248 results.extend(_CheckChangeLintsClean(input_api, output_api))
mark a. foltz4410e8e2020-05-08 00:10:17249
Jordan Bayles425c1362021-05-27 21:33:11250 # Ensure code change has already had clang-format ran.
Jordan Baylese50a1d62020-11-30 20:31:47251 results.extend(
252 input_api.canned_checks.CheckPatchFormatted(input_api,
253 output_api,
254 bypass_warnings=False))
mark a. foltz4410e8e2020-05-08 00:10:17255
Jordan Bayles425c1362021-05-27 21:33:11256 # Ensure code change has had GN formatting ran.
Jordan Baylese50a1d62020-11-30 20:31:47257 results.extend(
258 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
mark a. foltz4410e8e2020-05-08 00:10:17259
Jordan Bayles425c1362021-05-27 21:33:11260 # Run buildtools/checkdeps on code change.
Jordan Baylese50a1d62020-11-30 20:31:47261 results.extend(_CheckDeps(input_api, output_api))
262
Jordan Bayles4d9a1ed2023-02-22 19:26:01263 # Ensure the LUCI configs pass the linter.
264 results.extend(_CheckLuciCfgLint(input_api, output_api))
265
Jordan Bayles425c1362021-05-27 21:33:11266 # Run tools/licenses on code change.
Jordan Bayles5d694412023-02-23 01:30:05267 results.extend(_CheckLicenses(input_api, output_api))
Jordan Baylese50a1d62020-11-30 20:31:47268
269 return results
btolsch333aecd2019-04-18 23:21:23270
271
Ryan Tseng85ec17e2018-09-07 00:10:05272def CheckChangeOnUpload(input_api, output_api):
Jordan Bayles5d694412023-02-23 01:30:05273 """Checks the changelist whenever there is an upload (`git cl upload`)"""
Jordan Baylese50a1d62020-11-30 20:31:47274 # We always run the OnCommit checks, as well as some additional checks.
275 results = CheckChangeOnCommit(input_api, output_api)
Josip Sokcevic401815e2022-02-10 18:33:13276 results.extend(
277 input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api))
Jordan Baylese50a1d62020-11-30 20:31:47278 return results
btolsch333aecd2019-04-18 23:21:23279
280
281def CheckChangeOnCommit(input_api, output_api):
Jordan Bayles5d694412023-02-23 01:30:05282 """Checks the changelist whenever there is commit (`git cl commit`)"""
Jordan Baylese50a1d62020-11-30 20:31:47283 return _CommonChecks(input_api, output_api)