Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1 | # 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 | |
| 30 | See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
| 31 | for more details about the presubmit API built into gcl. |
| 32 | """ |
| 33 | |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 34 | import json |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 35 | import os |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 36 | import re |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 37 | import sys |
| 38 | |
| 39 | |
| 40 | _EXCLUDED_PATHS = ( |
| 41 | r"^test[\\\/].*", |
| 42 | r"^testing[\\\/].*", |
| 43 | r"^third_party[\\\/].*", |
| 44 | r"^tools[\\\/].*", |
| 45 | ) |
| 46 | |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 47 | _LICENSE_FILE = ( |
| 48 | r"LICENSE" |
| 49 | ) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 50 | |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 51 | # 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 Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 57 | # 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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 65 | # Runtime functions used for testing. |
| 66 | r'src[\\\/]runtime[\\\/]runtime-test\.cc', |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 67 | ) |
| 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 | |
| 77 | def _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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 82 | from v8_presubmit import CppLintProcessor |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 83 | from v8_presubmit import JSLintProcessor |
| 84 | from v8_presubmit import TorqueLintProcessor |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 85 | 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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 91 | 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 Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 103 | |
| 104 | results = [] |
Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 105 | if not CppLintProcessor().RunOnFiles( |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 106 | input_api.AffectedFiles(file_filter=FilterFile, include_deletes=False)): |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 107 | results.append(output_api.PresubmitError("C++ lint check failed")) |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 108 | 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 116 | if not SourceProcessor().RunOnFiles( |
| 117 | input_api.AffectedFiles(include_deletes=False)): |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 118 | results.append(output_api.PresubmitError( |
| 119 | "Copyright header, trailing whitespaces and two empty lines " \ |
| 120 | "between declarations check failed")) |
Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 121 | if not StatusFilesProcessor().RunOnFiles( |
| 122 | input_api.AffectedFiles(include_deletes=True)): |
Ben Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 123 | results.append(output_api.PresubmitError("Status file check failed")) |
Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 124 | results.extend(input_api.canned_checks.CheckAuthorizedAuthor( |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 125 | input_api, output_api, bot_allowlist=[ |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 126 | 'v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com' |
| 127 | ])) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 128 | return results |
| 129 | |
| 130 | |
| 131 | def _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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 150 | 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 Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 208 | added_includes = [] |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 209 | affected_files = input_api.AffectedFiles() |
| 210 | impacted_by_deps = _FilesImpactedByDepsChange(affected_files) |
| 211 | for f in _FilterDuplicates(impacted_by_deps, affected_files): |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 212 | 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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 244 | def _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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 249 | files_to_skip = _EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 250 | return input_api.FilterSourceFile( |
| 251 | affected_file, |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 252 | files_to_check=(file_inclusion_pattern, ), |
| 253 | files_to_skip=files_to_skip) |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 254 | |
| 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 Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 299 | def _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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 305 | 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 Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 308 | |
| 309 | def FilterFile(affected_file): |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 310 | files_to_skip = _EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP |
Ben Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 311 | return input_api.FilterSourceFile( |
| 312 | affected_file, |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 313 | files_to_check=(file_inclusion_pattern, ), |
| 314 | files_to_skip=files_to_skip) |
Ben Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 315 | |
| 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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 325 | return [output_api.PresubmitError(include_error, problems)] |
Ben Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 326 | else: |
| 327 | return [] |
| 328 | |
| 329 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 330 | def _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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 349 | files_to_skip = (_EXCLUDED_PATHS + |
| 350 | _TEST_CODE_EXCLUDED_PATHS + |
| 351 | input_api.DEFAULT_FILES_TO_SKIP) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 352 | return input_api.FilterSourceFile( |
| 353 | affected_file, |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 354 | files_to_check=(file_inclusion_pattern, ), |
| 355 | files_to_skip=files_to_skip) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 356 | |
| 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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 373 | def _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 | |
| 382 | def _RunTestsWithVPythonSpec(input_api, output_api): |
| 383 | return input_api.RunTests( |
| 384 | input_api.canned_checks.CheckVPythonSpec(input_api, output_api)) |
| 385 | |
| 386 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 387 | def _CommonChecks(input_api, output_api): |
| 388 | """Checks common to both upload and commit.""" |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 389 | # 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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 392 | 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 Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 409 | |
| 410 | |
| 411 | def _SkipTreeCheck(input_api, output_api): |
| 412 | """Check the env var whether we want to skip tree check. |
Ben Murdoch | 014dc51 | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 413 | Only skip if include/v8-version.h has been updated.""" |
| 414 | src_version = 'include/v8-version.h' |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 415 | 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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 421 | def _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 | |
| 447 | def _CheckJSONFiles(input_api, output_api): |
| 448 | def FilterFile(affected_file): |
| 449 | return input_api.FilterSourceFile( |
| 450 | affected_file, |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 451 | files_to_check=(r'.+\.json',)) |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 452 | |
| 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 Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 466 | def _CheckNoexceptAnnotations(input_api, output_api): |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 467 | """ |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 468 | Checks that all user-defined constructors and assignment operators are marked |
| 469 | V8_NOEXCEPT. |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 470 | |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 471 | 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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 478 | """ |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 479 | |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 480 | def FilterFile(affected_file): |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 481 | return input_api.FilterSourceFile( |
| 482 | affected_file, |
| 483 | files_to_check=(r'src/.*', r'test/.*')) |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 484 | |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 485 | |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 486 | # 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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 501 | errors = [] |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 502 | for f in input_api.AffectedFiles(file_filter=FilterFile, |
| 503 | include_deletes=False): |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 504 | with open(f.LocalPath()) as fh: |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 505 | for match in re.finditer(regexp, fh.read()): |
| 506 | errors.append('in {}: {}'.format(f.LocalPath(), |
| 507 | match.group().strip())) |
Rubin Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 508 | |
| 509 | if errors: |
| 510 | return [output_api.PresubmitPromptOrNotify( |
Rubin Xu | 7314a9f | 2021-02-10 00:04:48 +0000 | [diff] [blame] | 511 | '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 Xu | 2894c6a | 2019-02-07 16:01:35 +0000 | [diff] [blame] | 514 | errors)] |
| 515 | return [] |
| 516 | |
| 517 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 518 | def CheckChangeOnUpload(input_api, output_api): |
| 519 | results = [] |
| 520 | results.extend(_CommonChecks(input_api, output_api)) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 521 | return results |
| 522 | |
| 523 | |
| 524 | def CheckChangeOnCommit(input_api, output_api): |
| 525 | results = [] |
| 526 | results.extend(_CommonChecks(input_api, output_api)) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 527 | 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 |