blob: 113ed2fd61fa69373ce448907c615499d3c8ed94 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001# Copyright 2012 the V8 project authors. All rights reserved.
2# Redistribution and use in source and binary forms, with or without
3# modification, are permitted provided that the following conditions are
4# met:
5#
6# * Redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer.
8# * Redistributions in binary form must reproduce the above
9# copyright notice, this list of conditions and the following
10# disclaimer in the documentation and/or other materials provided
11# with the distribution.
12# * Neither the name of Google Inc. nor the names of its
13# contributors may be used to endorse or promote products derived
14# from this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28"""Top-level presubmit script for V8.
29
30See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
31for more details about the presubmit API built into gcl.
32"""
33
Rubin Xu2894c6a2019-02-07 16:01:35 +000034import json
Rubin Xu7314a9f2021-02-10 00:04:48 +000035import os
Rubin Xu2894c6a2019-02-07 16:01:35 +000036import re
Ben Murdochb8a8cc12014-11-26 15:28:44 +000037import sys
38
39
40_EXCLUDED_PATHS = (
41 r"^test[\\\/].*",
42 r"^testing[\\\/].*",
43 r"^third_party[\\\/].*",
44 r"^tools[\\\/].*",
45)
46
Rubin Xu7314a9f2021-02-10 00:04:48 +000047_LICENSE_FILE = (
48 r"LICENSE"
49)
Ben Murdochb8a8cc12014-11-26 15:28:44 +000050
Rubin Xu2894c6a2019-02-07 16:01:35 +000051# Regular expression that matches code which should not be run through cpplint.
52_NO_LINT_PATHS = (
53 r'src[\\\/]base[\\\/]export-template\.h',
54)
55
56
Ben Murdochb8a8cc12014-11-26 15:28:44 +000057# Regular expression that matches code only used for test binaries
58# (best effort).
59_TEST_CODE_EXCLUDED_PATHS = (
60 r'.+-unittest\.cc',
61 # Has a method VisitForTest().
62 r'src[\\\/]compiler[\\\/]ast-graph-builder\.cc',
63 # Test extension.
64 r'src[\\\/]extensions[\\\/]gc-extension\.cc',
Rubin Xu7314a9f2021-02-10 00:04:48 +000065 # Runtime functions used for testing.
66 r'src[\\\/]runtime[\\\/]runtime-test\.cc',
Ben Murdochb8a8cc12014-11-26 15:28:44 +000067)
68
69
70_TEST_ONLY_WARNING = (
71 'You might be calling functions intended only for testing from\n'
72 'production code. It is OK to ignore this warning if you know what\n'
73 'you are doing, as the heuristics used to detect the situation are\n'
74 'not perfect. The commit queue will not block on this warning.')
75
76
77def _V8PresubmitChecks(input_api, output_api):
78 """Runs the V8 presubmit checks."""
79 import sys
80 sys.path.append(input_api.os_path.join(
81 input_api.PresubmitLocalPath(), 'tools'))
Rubin Xu2894c6a2019-02-07 16:01:35 +000082 from v8_presubmit import CppLintProcessor
Rubin Xu7314a9f2021-02-10 00:04:48 +000083 from v8_presubmit import JSLintProcessor
84 from v8_presubmit import TorqueLintProcessor
Rubin Xu2894c6a2019-02-07 16:01:35 +000085 from v8_presubmit import SourceProcessor
86 from v8_presubmit import StatusFilesProcessor
87
88 def FilterFile(affected_file):
89 return input_api.FilterSourceFile(
90 affected_file,
Rubin Xu7314a9f2021-02-10 00:04:48 +000091 files_to_check=None,
92 files_to_skip=_NO_LINT_PATHS)
93
94 def FilterTorqueFile(affected_file):
95 return input_api.FilterSourceFile(
96 affected_file,
97 files_to_check=(r'.+\.tq'))
98
99 def FilterJSFile(affected_file):
100 return input_api.FilterSourceFile(
101 affected_file,
102 files_to_check=(r'.+\.m?js'))
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000103
104 results = []
Ben Murdoch62ed6312017-06-06 11:06:27 +0100105 if not CppLintProcessor().RunOnFiles(
Rubin Xu2894c6a2019-02-07 16:01:35 +0000106 input_api.AffectedFiles(file_filter=FilterFile, include_deletes=False)):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000107 results.append(output_api.PresubmitError("C++ lint check failed"))
Rubin Xu7314a9f2021-02-10 00:04:48 +0000108 if not TorqueLintProcessor().RunOnFiles(
109 input_api.AffectedFiles(file_filter=FilterTorqueFile,
110 include_deletes=False)):
111 results.append(output_api.PresubmitError("Torque format check failed"))
112 if not JSLintProcessor().RunOnFiles(
113 input_api.AffectedFiles(file_filter=FilterJSFile,
114 include_deletes=False)):
115 results.append(output_api.PresubmitError("JS format check failed"))
Ben Murdoch62ed6312017-06-06 11:06:27 +0100116 if not SourceProcessor().RunOnFiles(
117 input_api.AffectedFiles(include_deletes=False)):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000118 results.append(output_api.PresubmitError(
119 "Copyright header, trailing whitespaces and two empty lines " \
120 "between declarations check failed"))
Ben Murdoch62ed6312017-06-06 11:06:27 +0100121 if not StatusFilesProcessor().RunOnFiles(
122 input_api.AffectedFiles(include_deletes=True)):
Ben Murdoch014dc512016-03-22 12:00:34 +0000123 results.append(output_api.PresubmitError("Status file check failed"))
Ben Murdoch62ed6312017-06-06 11:06:27 +0100124 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Rubin Xu7314a9f2021-02-10 00:04:48 +0000125 input_api, output_api, bot_allowlist=[
Rubin Xu2894c6a2019-02-07 16:01:35 +0000126 'v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com'
127 ]))
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000128 return results
129
130
131def _CheckUnwantedDependencies(input_api, output_api):
132 """Runs checkdeps on #include statements added in this
133 change. Breaking - rules is an error, breaking ! rules is a
134 warning.
135 """
136 # We need to wait until we have an input_api object and use this
137 # roundabout construct to import checkdeps because this file is
138 # eval-ed and thus doesn't have __file__.
139 original_sys_path = sys.path
140 try:
141 sys.path = sys.path + [input_api.os_path.join(
142 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
143 import checkdeps
144 from cpp_checker import CppChecker
145 from rules import Rule
146 finally:
147 # Restore sys.path to what it was before.
148 sys.path = original_sys_path
149
Rubin Xu7314a9f2021-02-10 00:04:48 +0000150 def _FilesImpactedByDepsChange(files):
151 all_files = [f.AbsoluteLocalPath() for f in files]
152 deps_files = [p for p in all_files if IsDepsFile(p)]
153 impacted_files = union([_CollectImpactedFiles(path) for path in deps_files])
154 impacted_file_objs = [ImpactedFile(path) for path in impacted_files]
155 return impacted_file_objs
156
157 def IsDepsFile(p):
158 return os.path.isfile(p) and os.path.basename(p) == 'DEPS'
159
160 def union(list_of_lists):
161 """Ensure no duplicates"""
162 return set(sum(list_of_lists, []))
163
164 def _CollectImpactedFiles(deps_file):
165 # TODO(liviurau): Do not walk paths twice. Then we have no duplicates.
166 # Higher level DEPS changes may dominate lower level DEPS changes.
167 # TODO(liviurau): Check if DEPS changed in the right way.
168 # 'include_rules' impact c++ files but 'vars' or 'deps' do not.
169 # Maybe we just eval both old and new DEPS content and check
170 # if the list are the same.
171 result = []
172 parent_dir = os.path.dirname(deps_file)
173 for relative_f in input_api.change.AllFiles(parent_dir):
174 abs_f = os.path.join(parent_dir, relative_f)
175 if CppChecker.IsCppFile(abs_f):
176 result.append(abs_f)
177 return result
178
179 class ImpactedFile(object):
180 """Duck type version of AffectedFile needed to check files under directories
181 where a DEPS file changed. Extend the interface along the line of
182 AffectedFile if you need it for other checks."""
183
184 def __init__(self, path):
185 self._path = path
186
187 def LocalPath(self):
188 path = self._path.replace(os.sep, '/')
189 return os.path.normpath(path)
190
191 def ChangedContents(self):
192 with open(self._path) as f:
193 # TODO(liviurau): read only '#include' lines
194 lines = f.readlines()
195 return enumerate(lines, start=1)
196
197 def _FilterDuplicates(impacted_files, affected_files):
198 """"We include all impacted files but exclude affected files that are also
199 impacted. Files impacted by DEPS changes take precedence before files
200 affected by direct changes."""
201 result = impacted_files[:]
202 only_paths = set([imf.LocalPath() for imf in impacted_files])
203 for af in affected_files:
204 if not af.LocalPath() in only_paths:
205 result.append(af)
206 return result
207
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000208 added_includes = []
Rubin Xu7314a9f2021-02-10 00:04:48 +0000209 affected_files = input_api.AffectedFiles()
210 impacted_by_deps = _FilesImpactedByDepsChange(affected_files)
211 for f in _FilterDuplicates(impacted_by_deps, affected_files):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000212 if not CppChecker.IsCppFile(f.LocalPath()):
213 continue
214
215 changed_lines = [line for line_num, line in f.ChangedContents()]
216 added_includes.append([f.LocalPath(), changed_lines])
217
218 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
219
220 error_descriptions = []
221 warning_descriptions = []
222 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
223 added_includes):
224 description_with_path = '%s\n %s' % (path, rule_description)
225 if rule_type == Rule.DISALLOW:
226 error_descriptions.append(description_with_path)
227 else:
228 warning_descriptions.append(description_with_path)
229
230 results = []
231 if error_descriptions:
232 results.append(output_api.PresubmitError(
233 'You added one or more #includes that violate checkdeps rules.',
234 error_descriptions))
235 if warning_descriptions:
236 results.append(output_api.PresubmitPromptOrNotify(
237 'You added one or more #includes of files that are temporarily\n'
238 'allowed but being removed. Can you avoid introducing the\n'
239 '#include? See relevant DEPS file(s) for details and contacts.',
240 warning_descriptions))
241 return results
242
243
Rubin Xu2894c6a2019-02-07 16:01:35 +0000244def _CheckHeadersHaveIncludeGuards(input_api, output_api):
245 """Ensures that all header files have include guards."""
246 file_inclusion_pattern = r'src/.+\.h'
247
248 def FilterFile(affected_file):
Rubin Xu7314a9f2021-02-10 00:04:48 +0000249 files_to_skip = _EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP
Rubin Xu2894c6a2019-02-07 16:01:35 +0000250 return input_api.FilterSourceFile(
251 affected_file,
Rubin Xu7314a9f2021-02-10 00:04:48 +0000252 files_to_check=(file_inclusion_pattern, ),
253 files_to_skip=files_to_skip)
Rubin Xu2894c6a2019-02-07 16:01:35 +0000254
255 leading_src_pattern = input_api.re.compile(r'^src/')
256 dash_dot_slash_pattern = input_api.re.compile(r'[-./]')
257 def PathToGuardMacro(path):
258 """Guards should be of the form V8_PATH_TO_FILE_WITHOUT_SRC_H_."""
259 x = input_api.re.sub(leading_src_pattern, 'v8_', path)
260 x = input_api.re.sub(dash_dot_slash_pattern, '_', x)
261 x = x.upper() + "_"
262 return x
263
264 problems = []
265 for f in input_api.AffectedSourceFiles(FilterFile):
266 local_path = f.LocalPath()
267 guard_macro = PathToGuardMacro(local_path)
268 guard_patterns = [
269 input_api.re.compile(r'^#ifndef ' + guard_macro + '$'),
270 input_api.re.compile(r'^#define ' + guard_macro + '$'),
271 input_api.re.compile(r'^#endif // ' + guard_macro + '$')]
272 skip_check_pattern = input_api.re.compile(
273 r'^// PRESUBMIT_INTENTIONALLY_MISSING_INCLUDE_GUARD')
274 found_patterns = [ False, False, False ]
275 file_omitted = False
276
277 for line in f.NewContents():
278 for i in range(len(guard_patterns)):
279 if guard_patterns[i].match(line):
280 found_patterns[i] = True
281 if skip_check_pattern.match(line):
282 file_omitted = True
283 break
284
285 if not file_omitted and not all(found_patterns):
286 problems.append(
287 '%s: Missing include guard \'%s\'' % (local_path, guard_macro))
288
289 if problems:
290 return [output_api.PresubmitError(
291 'You added one or more header files without an appropriate\n'
292 'include guard. Add the include guard {#ifndef,#define,#endif}\n'
293 'triplet or omit the check entirely through the magic comment:\n'
294 '"// PRESUBMIT_INTENTIONALLY_MISSING_INCLUDE_GUARD".', problems)]
295 else:
296 return []
297
298
Ben Murdoch014dc512016-03-22 12:00:34 +0000299def _CheckNoInlineHeaderIncludesInNormalHeaders(input_api, output_api):
300 """Attempts to prevent inclusion of inline headers into normal header
301 files. This tries to establish a layering where inline headers can be
302 included by other inline headers or compilation units only."""
303 file_inclusion_pattern = r'(?!.+-inl\.h).+\.h'
304 include_directive_pattern = input_api.re.compile(r'#include ".+-inl.h"')
Rubin Xu2894c6a2019-02-07 16:01:35 +0000305 include_error = (
306 'You are including an inline header (e.g. foo-inl.h) within a normal\n'
307 'header (e.g. bar.h) file. This violates layering of dependencies.')
Ben Murdoch014dc512016-03-22 12:00:34 +0000308
309 def FilterFile(affected_file):
Rubin Xu7314a9f2021-02-10 00:04:48 +0000310 files_to_skip = _EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP
Ben Murdoch014dc512016-03-22 12:00:34 +0000311 return input_api.FilterSourceFile(
312 affected_file,
Rubin Xu7314a9f2021-02-10 00:04:48 +0000313 files_to_check=(file_inclusion_pattern, ),
314 files_to_skip=files_to_skip)
Ben Murdoch014dc512016-03-22 12:00:34 +0000315
316 problems = []
317 for f in input_api.AffectedSourceFiles(FilterFile):
318 local_path = f.LocalPath()
319 for line_number, line in f.ChangedContents():
320 if (include_directive_pattern.search(line)):
321 problems.append(
322 '%s:%d\n %s' % (local_path, line_number, line.strip()))
323
324 if problems:
Rubin Xu2894c6a2019-02-07 16:01:35 +0000325 return [output_api.PresubmitError(include_error, problems)]
Ben Murdoch014dc512016-03-22 12:00:34 +0000326 else:
327 return []
328
329
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000330def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
331 """Attempts to prevent use of functions intended only for testing in
332 non-testing code. For now this is just a best-effort implementation
333 that ignores header files and may have some false positives. A
334 better implementation would probably need a proper C++ parser.
335 """
336 # We only scan .cc files, as the declaration of for-testing functions in
337 # header files are hard to distinguish from calls to such functions without a
338 # proper C++ parser.
339 file_inclusion_pattern = r'.+\.cc'
340
341 base_function_pattern = r'[ :]test::[^\s]+|ForTest(ing)?|for_test(ing)?'
342 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
343 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
344 exclusion_pattern = input_api.re.compile(
345 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
346 base_function_pattern, base_function_pattern))
347
348 def FilterFile(affected_file):
Rubin Xu7314a9f2021-02-10 00:04:48 +0000349 files_to_skip = (_EXCLUDED_PATHS +
350 _TEST_CODE_EXCLUDED_PATHS +
351 input_api.DEFAULT_FILES_TO_SKIP)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000352 return input_api.FilterSourceFile(
353 affected_file,
Rubin Xu7314a9f2021-02-10 00:04:48 +0000354 files_to_check=(file_inclusion_pattern, ),
355 files_to_skip=files_to_skip)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000356
357 problems = []
358 for f in input_api.AffectedSourceFiles(FilterFile):
359 local_path = f.LocalPath()
360 for line_number, line in f.ChangedContents():
361 if (inclusion_pattern.search(line) and
362 not comment_pattern.search(line) and
363 not exclusion_pattern.search(line)):
364 problems.append(
365 '%s:%d\n %s' % (local_path, line_number, line.strip()))
366
367 if problems:
368 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
369 else:
370 return []
371
372
Rubin Xu7314a9f2021-02-10 00:04:48 +0000373def _CheckGenderNeutralInLicenses(input_api, output_api):
374 # License files are taken as is, even if they include gendered pronouns.
375 def LicenseFilter(path):
376 input_api.FilterSourceFile(path, files_to_skip=_LICENSE_FILE)
377
378 return input_api.canned_checks.CheckGenderNeutral(
379 input_api, output_api, source_file_filter=LicenseFilter)
380
381
382def _RunTestsWithVPythonSpec(input_api, output_api):
383 return input_api.RunTests(
384 input_api.canned_checks.CheckVPythonSpec(input_api, output_api))
385
386
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000387def _CommonChecks(input_api, output_api):
388 """Checks common to both upload and commit."""
Rubin Xu2894c6a2019-02-07 16:01:35 +0000389 # TODO(machenbach): Replace some of those checks, e.g. owners and copyright,
390 # with the canned PanProjectChecks. Need to make sure that the checks all
391 # pass on all existing files.
Rubin Xu7314a9f2021-02-10 00:04:48 +0000392 checks = [
393 input_api.canned_checks.CheckOwnersFormat,
394 input_api.canned_checks.CheckOwners,
395 _CheckCommitMessageBugEntry,
396 input_api.canned_checks.CheckPatchFormatted,
397 _CheckGenderNeutralInLicenses,
398 _V8PresubmitChecks,
399 _CheckUnwantedDependencies,
400 _CheckNoProductionCodeUsingTestOnlyFunctions,
401 _CheckHeadersHaveIncludeGuards,
402 _CheckNoInlineHeaderIncludesInNormalHeaders,
403 _CheckJSONFiles,
404 _CheckNoexceptAnnotations,
405 _RunTestsWithVPythonSpec,
406 ]
407
408 return sum([check(input_api, output_api) for check in checks], [])
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000409
410
411def _SkipTreeCheck(input_api, output_api):
412 """Check the env var whether we want to skip tree check.
Ben Murdoch014dc512016-03-22 12:00:34 +0000413 Only skip if include/v8-version.h has been updated."""
414 src_version = 'include/v8-version.h'
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000415 if not input_api.AffectedSourceFiles(
416 lambda file: file.LocalPath() == src_version):
417 return False
418 return input_api.environ.get('PRESUBMIT_TREE_CHECK') == 'skip'
419
420
Rubin Xu2894c6a2019-02-07 16:01:35 +0000421def _CheckCommitMessageBugEntry(input_api, output_api):
422 """Check that bug entries are well-formed in commit message."""
423 bogus_bug_msg = (
424 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
425 'issue number, separated by a colon, e.g. v8:123 or chromium:12345.')
426 results = []
427 for bug in (input_api.change.BUG or '').split(','):
428 bug = bug.strip()
429 if 'none'.startswith(bug.lower()):
430 continue
431 if ':' not in bug:
432 try:
433 if int(bug) > 100000:
434 # Rough indicator for current chromium bugs.
435 prefix_guess = 'chromium'
436 else:
437 prefix_guess = 'v8'
438 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
439 (prefix_guess, bug))
440 except ValueError:
441 results.append(bogus_bug_msg % bug)
442 elif not re.match(r'\w+:\d+', bug):
443 results.append(bogus_bug_msg % bug)
444 return [output_api.PresubmitError(r) for r in results]
445
446
447def _CheckJSONFiles(input_api, output_api):
448 def FilterFile(affected_file):
449 return input_api.FilterSourceFile(
450 affected_file,
Rubin Xu7314a9f2021-02-10 00:04:48 +0000451 files_to_check=(r'.+\.json',))
Rubin Xu2894c6a2019-02-07 16:01:35 +0000452
453 results = []
454 for f in input_api.AffectedFiles(
455 file_filter=FilterFile, include_deletes=False):
456 with open(f.LocalPath()) as j:
457 try:
458 json.load(j)
459 except Exception as e:
460 results.append(
461 'JSON validation failed for %s. Error:\n%s' % (f.LocalPath(), e))
462
463 return [output_api.PresubmitError(r) for r in results]
464
465
Rubin Xu7314a9f2021-02-10 00:04:48 +0000466def _CheckNoexceptAnnotations(input_api, output_api):
Rubin Xu2894c6a2019-02-07 16:01:35 +0000467 """
Rubin Xu7314a9f2021-02-10 00:04:48 +0000468 Checks that all user-defined constructors and assignment operators are marked
469 V8_NOEXCEPT.
Rubin Xu2894c6a2019-02-07 16:01:35 +0000470
Rubin Xu7314a9f2021-02-10 00:04:48 +0000471 This is required for standard containers to pick the right constructors. Our
472 macros (like MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS) add this automatically.
473 Omitting it at some places can result in weird compiler errors if this is
474 mixed with other classes that have the annotation.
475
476 TODO(clemensb): This check should eventually be enabled for all files via
477 tools/presubmit.py (https://crbug.com/v8/8616).
Rubin Xu2894c6a2019-02-07 16:01:35 +0000478 """
Rubin Xu7314a9f2021-02-10 00:04:48 +0000479
Rubin Xu2894c6a2019-02-07 16:01:35 +0000480 def FilterFile(affected_file):
Rubin Xu7314a9f2021-02-10 00:04:48 +0000481 return input_api.FilterSourceFile(
482 affected_file,
483 files_to_check=(r'src/.*', r'test/.*'))
Rubin Xu2894c6a2019-02-07 16:01:35 +0000484
Rubin Xu2894c6a2019-02-07 16:01:35 +0000485
Rubin Xu7314a9f2021-02-10 00:04:48 +0000486 # matches any class name.
487 class_name = r'\b([A-Z][A-Za-z0-9_:]*)(?:::\1)?'
488 # initial class name is potentially followed by this to declare an assignment
489 # operator.
490 potential_assignment = r'(?:&\s+(?:\1::)?operator=)?\s*'
491 # matches an argument list that contains only a reference to a class named
492 # like the first capture group, potentially const.
493 single_class_ref_arg = r'\(\s*(?:const\s+)?\1(?:::\1)?&&?[^,;)]*\)'
494 # matches anything but a sequence of whitespaces followed by either
495 # V8_NOEXCEPT or "= delete".
496 not_followed_by_noexcept = r'(?!\s+(?:V8_NOEXCEPT|=\s+delete)\b)'
497 full_pattern = r'^.*?' + class_name + potential_assignment + \
498 single_class_ref_arg + not_followed_by_noexcept + '.*?$'
499 regexp = input_api.re.compile(full_pattern, re.MULTILINE)
500
Rubin Xu2894c6a2019-02-07 16:01:35 +0000501 errors = []
Rubin Xu7314a9f2021-02-10 00:04:48 +0000502 for f in input_api.AffectedFiles(file_filter=FilterFile,
503 include_deletes=False):
Rubin Xu2894c6a2019-02-07 16:01:35 +0000504 with open(f.LocalPath()) as fh:
Rubin Xu7314a9f2021-02-10 00:04:48 +0000505 for match in re.finditer(regexp, fh.read()):
506 errors.append('in {}: {}'.format(f.LocalPath(),
507 match.group().strip()))
Rubin Xu2894c6a2019-02-07 16:01:35 +0000508
509 if errors:
510 return [output_api.PresubmitPromptOrNotify(
Rubin Xu7314a9f2021-02-10 00:04:48 +0000511 'Copy constructors, move constructors, copy assignment operators and '
512 'move assignment operators should be marked V8_NOEXCEPT.\n'
513 'Please report false positives on https://crbug.com/v8/8616.',
Rubin Xu2894c6a2019-02-07 16:01:35 +0000514 errors)]
515 return []
516
517
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000518def CheckChangeOnUpload(input_api, output_api):
519 results = []
520 results.extend(_CommonChecks(input_api, output_api))
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000521 return results
522
523
524def CheckChangeOnCommit(input_api, output_api):
525 results = []
526 results.extend(_CommonChecks(input_api, output_api))
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000527 results.extend(input_api.canned_checks.CheckChangeHasDescription(
528 input_api, output_api))
529 if not _SkipTreeCheck(input_api, output_api):
530 results.extend(input_api.canned_checks.CheckTreeIsOpen(
531 input_api, output_api,
532 json_url='http://v8-status.appspot.com/current?format=json'))
533 return results