Thanks to visit codestin.com
Credit goes to chromium.googlesource.com

blob: 666d60f5b6e2f63e5f0a08673e40c868f54fa128 [file] [log] [blame]
Avi Drissman24976592022-09-12 15:24:311# Copyright 2012 The Chromium Authors
[email protected]ca8d19842009-02-19 16:33:122# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Top-level presubmit script for Chromium.
6
Daniel Chengd88244472022-05-16 09:08:477See https://www.chromium.org/developers/how-tos/depottools/presubmit-scripts/
tfarina78bb92f42015-01-31 00:20:488for more details about the presubmit API built into depot_tools.
[email protected]ca8d19842009-02-19 16:33:129"""
Daniel Chenga44a1bcd2022-03-15 20:00:1510
Daniel Chenga37c03db2022-05-12 17:20:3411from typing import Callable
Daniel Chenga44a1bcd2022-03-15 20:00:1512from typing import Optional
13from typing import Sequence
14from dataclasses import dataclass
15
Saagar Sanghavifceeaae2020-08-12 16:40:3616PRESUBMIT_VERSION = '2.0.0'
[email protected]eea609a2011-11-18 13:10:1217
Dirk Prankee3c9c62d2021-05-18 18:35:5918# This line is 'magic' in that git-cl looks for it to decide whether to
19# use Python3 instead of Python2 when running the code in this file.
20USE_PYTHON3 = True
21
[email protected]379e7dd2010-01-28 17:39:2122_EXCLUDED_PATHS = (
Bruce Dawson7f8566b2022-05-06 16:22:1823 # Generated file
Bruce Dawson40fece62022-09-16 19:58:3124 (r"chrome/android/webapk/shell_apk/src/org/chromium"
25 r"/webapk/lib/runtime_library/IWebApkApi.java"),
Mila Greene3aa7222021-09-07 16:34:0826 # File needs to write to stdout to emulate a tool it's replacing.
Bruce Dawson40fece62022-09-16 19:58:3127 r"chrome/updater/mac/keystone/ksadmin.mm",
Ilya Shermane8a7d2d2020-07-25 04:33:4728 # Generated file.
Bruce Dawson40fece62022-09-16 19:58:3129 (r"^components/variations/proto/devtools/"
Ilya Shermanc167a962020-08-18 18:40:2630 r"client_variations.js"),
Bruce Dawson3bd976c2022-05-06 22:47:5231 # These are video files, not typescript.
Bruce Dawson40fece62022-09-16 19:58:3132 r"^media/test/data/.*.ts",
33 r"^native_client_sdksrc/build_tools/make_rules.py",
34 r"^native_client_sdk/src/build_tools/make_simple.py",
35 r"^native_client_sdk/src/tools/.*.mk",
36 r"^net/tools/spdyshark/.*",
37 r"^skia/.*",
38 r"^third_party/blink/.*",
39 r"^third_party/breakpad/.*",
Darwin Huangd74a9d32019-07-17 17:58:4640 # sqlite is an imported third party dependency.
Bruce Dawson40fece62022-09-16 19:58:3141 r"^third_party/sqlite/.*",
42 r"^v8/.*",
[email protected]3e4eb112011-01-18 03:29:5443 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5344 r".+_autogen\.h$",
Yue Shecf1380552022-08-23 20:59:2045 r".+_pb2(_grpc)?\.py$",
Bruce Dawson40fece62022-09-16 19:58:3146 r".+/pnacl_shim\.c$",
47 r"^gpu/config/.*_list_json\.cc$",
48 r"tools/md_browser/.*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1449 # Test pages for Maps telemetry tests.
Bruce Dawson40fece62022-09-16 19:58:3150 r"tools/perf/page_sets/maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5451 # Test pages for WebRTC telemetry tests.
Bruce Dawson40fece62022-09-16 19:58:3152 r"tools/perf/page_sets/webrtc_cases.*",
[email protected]4306417642009-06-11 00:33:4053)
[email protected]ca8d19842009-02-19 16:33:1254
John Abd-El-Malek759fea62021-03-13 03:41:1455_EXCLUDED_SET_NO_PARENT_PATHS = (
56 # It's for historical reasons that blink isn't a top level directory, where
57 # it would be allowed to have "set noparent" to avoid top level owners
58 # accidentally +1ing changes.
59 'third_party/blink/OWNERS',
60)
61
wnwenbdc444e2016-05-25 13:44:1562
[email protected]06e6d0ff2012-12-11 01:36:4463# Fragment of a regular expression that matches C++ and Objective-C++
64# implementation files.
65_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
66
wnwenbdc444e2016-05-25 13:44:1567
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:1968# Fragment of a regular expression that matches C++ and Objective-C++
69# header files.
70_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
71
72
Aleksey Khoroshilov9b28c032022-06-03 16:35:3273# Paths with sources that don't use //base.
74_NON_BASE_DEPENDENT_PATHS = (
Bruce Dawson40fece62022-09-16 19:58:3175 r"^chrome/browser/browser_switcher/bho/",
76 r"^tools/win/",
Aleksey Khoroshilov9b28c032022-06-03 16:35:3277)
78
79
[email protected]06e6d0ff2012-12-11 01:36:4480# Regular expression that matches code only used for test binaries
81# (best effort).
82_TEST_CODE_EXCLUDED_PATHS = (
Bruce Dawson40fece62022-09-16 19:58:3183 r'.*/(fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4484 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
James Cook1b4dc132021-03-09 22:45:1385 # Test suite files, like:
86 # foo_browsertest.cc
87 # bar_unittest_mac.cc (suffix)
88 # baz_unittests.cc (plural)
89 r'.+_(api|browser|eg|int|perf|pixel|unit|ui)?test(s)?(_[a-z]+)?%s' %
[email protected]e2d7e6f2013-04-23 12:57:1290 _IMPLEMENTATION_EXTENSIONS,
Matthew Denton63ea1e62019-03-25 20:39:1891 r'.+_(fuzz|fuzzer)(_[a-z]+)?%s' % _IMPLEMENTATION_EXTENSIONS,
Victor Hugo Vianna Silvac22e0202021-06-09 19:46:2192 r'.+sync_service_impl_harness%s' % _IMPLEMENTATION_EXTENSIONS,
Bruce Dawson40fece62022-09-16 19:58:3193 r'.*/(test|tool(s)?)/.*',
danakj89f47082020-09-02 17:53:4394 # content_shell is used for running content_browsertests.
Bruce Dawson40fece62022-09-16 19:58:3195 r'content/shell/.*',
danakj89f47082020-09-02 17:53:4396 # Web test harness.
Bruce Dawson40fece62022-09-16 19:58:3197 r'content/web_test/.*',
[email protected]7b054982013-11-27 00:44:4798 # Non-production example code.
Bruce Dawson40fece62022-09-16 19:58:3199 r'mojo/examples/.*',
[email protected]8176de12014-06-20 19:07:08100 # Launcher for running iOS tests on the simulator.
Bruce Dawson40fece62022-09-16 19:58:31101 r'testing/iossim/iossim\.mm$',
Olivier Robinbcea0fa2019-11-12 08:56:41102 # EarlGrey app side code for tests.
Bruce Dawson40fece62022-09-16 19:58:31103 r'ios/.*_app_interface\.mm$',
Allen Bauer0678d772020-05-11 22:25:17104 # Views Examples code
Bruce Dawson40fece62022-09-16 19:58:31105 r'ui/views/examples/.*',
Austin Sullivan33da70a2020-10-07 15:39:41106 # Chromium Codelab
Bruce Dawson40fece62022-09-16 19:58:31107 r'codelabs/*'
[email protected]06e6d0ff2012-12-11 01:36:44108)
[email protected]ca8d19842009-02-19 16:33:12109
Daniel Bratell609102be2019-03-27 20:53:21110_THIRD_PARTY_EXCEPT_BLINK = 'third_party/(?!blink/)'
wnwenbdc444e2016-05-25 13:44:15111
[email protected]eea609a2011-11-18 13:10:12112_TEST_ONLY_WARNING = (
113 'You might be calling functions intended only for testing from\n'
danakj5f6e3b82020-09-10 13:52:55114 'production code. If you are doing this from inside another method\n'
115 'named as *ForTesting(), then consider exposing things to have tests\n'
116 'make that same call directly.\n'
117 'If that is not possible, you may put a comment on the same line with\n'
118 ' // IN-TEST \n'
119 'to tell the PRESUBMIT script that the code is inside a *ForTesting()\n'
120 'method and can be ignored. Do not do this inside production code.\n'
121 'The android-binary-size trybot will block if the method exists in the\n'
122 'release apk.')
[email protected]eea609a2011-11-18 13:10:12123
124
Daniel Chenga44a1bcd2022-03-15 20:00:15125@dataclass
126class BanRule:
Daniel Chenga37c03db2022-05-12 17:20:34127 # String pattern. If the pattern begins with a slash, the pattern will be
128 # treated as a regular expression instead.
129 pattern: str
130 # Explanation as a sequence of strings. Each string in the sequence will be
131 # printed on its own line.
132 explanation: Sequence[str]
133 # Whether or not to treat this ban as a fatal error. If unspecified,
134 # defaults to true.
135 treat_as_error: Optional[bool] = None
136 # Paths that should be excluded from the ban check. Each string is a regular
137 # expression that will be matched against the path of the file being checked
138 # relative to the root of the source tree.
139 excluded_paths: Optional[Sequence[str]] = None
[email protected]cf9b78f2012-11-14 11:40:28140
Daniel Chenga44a1bcd2022-03-15 20:00:15141
Daniel Cheng917ce542022-03-15 20:46:57142_BANNED_JAVA_IMPORTS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15143 BanRule(
144 'import java.net.URI;',
145 (
146 'Use org.chromium.url.GURL instead of java.net.URI, where possible.',
147 ),
148 excluded_paths=(
149 (r'net/android/javatests/src/org/chromium/net/'
150 'AndroidProxySelectorTest\.java'),
151 r'components/cronet/',
152 r'third_party/robolectric/local/',
153 ),
Michael Thiessen44457642020-02-06 00:24:15154 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15155 BanRule(
156 'import android.annotation.TargetApi;',
157 (
158 'Do not use TargetApi, use @androidx.annotation.RequiresApi instead. '
159 'RequiresApi ensures that any calls are guarded by the appropriate '
160 'SDK_INT check. See https://crbug.com/1116486.',
161 ),
162 ),
163 BanRule(
164 'import android.support.test.rule.UiThreadTestRule;',
165 (
166 'Do not use UiThreadTestRule, just use '
167 '@org.chromium.base.test.UiThreadTest on test methods that should run '
168 'on the UI thread. See https://crbug.com/1111893.',
169 ),
170 ),
171 BanRule(
172 'import android.support.test.annotation.UiThreadTest;',
173 ('Do not use android.support.test.annotation.UiThreadTest, use '
174 'org.chromium.base.test.UiThreadTest instead. See '
175 'https://crbug.com/1111893.',
176 ),
177 ),
178 BanRule(
179 'import android.support.test.rule.ActivityTestRule;',
180 (
181 'Do not use ActivityTestRule, use '
182 'org.chromium.base.test.BaseActivityTestRule instead.',
183 ),
184 excluded_paths=(
185 'components/cronet/',
186 ),
187 ),
188)
wnwenbdc444e2016-05-25 13:44:15189
Daniel Cheng917ce542022-03-15 20:46:57190_BANNED_JAVA_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15191 BanRule(
Eric Stevensona9a980972017-09-23 00:04:41192 'StrictMode.allowThreadDiskReads()',
193 (
194 'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
195 'directly.',
196 ),
197 False,
198 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15199 BanRule(
Eric Stevensona9a980972017-09-23 00:04:41200 'StrictMode.allowThreadDiskWrites()',
201 (
202 'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
203 'directly.',
204 ),
205 False,
206 ),
Daniel Cheng917ce542022-03-15 20:46:57207 BanRule(
Michael Thiessen0f2547e32020-07-27 21:55:36208 '.waitForIdleSync()',
209 (
210 'Do not use waitForIdleSync as it masks underlying issues. There is '
211 'almost always something else you should wait on instead.',
212 ),
213 False,
214 ),
Eric Stevensona9a980972017-09-23 00:04:41215)
216
Daniel Cheng917ce542022-03-15 20:46:57217_BANNED_OBJC_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15218 BanRule(
[email protected]127f18ec2012-06-16 05:05:59219 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:20220 (
221 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:59222 'prohibited. Please use CrTrackingArea instead.',
223 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
224 ),
225 False,
226 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15227 BanRule(
[email protected]eaae1972014-04-16 04:17:26228 r'/NSTrackingArea\W',
[email protected]23e6cbc2012-06-16 18:51:20229 (
230 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:59231 'instead.',
232 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
233 ),
234 False,
235 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15236 BanRule(
[email protected]127f18ec2012-06-16 05:05:59237 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20238 (
239 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59240 'Please use |convertPoint:(point) fromView:nil| instead.',
241 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
242 ),
243 True,
244 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15245 BanRule(
[email protected]127f18ec2012-06-16 05:05:59246 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:20247 (
248 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59249 'Please use |convertPoint:(point) toView:nil| instead.',
250 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
251 ),
252 True,
253 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15254 BanRule(
[email protected]127f18ec2012-06-16 05:05:59255 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20256 (
257 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59258 'Please use |convertRect:(point) fromView:nil| instead.',
259 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
260 ),
261 True,
262 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15263 BanRule(
[email protected]127f18ec2012-06-16 05:05:59264 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20265 (
266 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59267 'Please use |convertRect:(point) toView:nil| instead.',
268 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
269 ),
270 True,
271 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15272 BanRule(
[email protected]127f18ec2012-06-16 05:05:59273 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20274 (
275 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59276 'Please use |convertSize:(point) fromView:nil| instead.',
277 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
278 ),
279 True,
280 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15281 BanRule(
[email protected]127f18ec2012-06-16 05:05:59282 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20283 (
284 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59285 'Please use |convertSize:(point) toView:nil| instead.',
286 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
287 ),
288 True,
289 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15290 BanRule(
jif65398702016-10-27 10:19:48291 r"/\s+UTF8String\s*]",
292 (
293 'The use of -[NSString UTF8String] is dangerous as it can return null',
294 'even if |canBeConvertedToEncoding:NSUTF8StringEncoding| returns YES.',
295 'Please use |SysNSStringToUTF8| instead.',
296 ),
297 True,
298 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15299 BanRule(
Sylvain Defresne4cf1d182017-09-18 14:16:34300 r'__unsafe_unretained',
301 (
302 'The use of __unsafe_unretained is almost certainly wrong, unless',
303 'when interacting with NSFastEnumeration or NSInvocation.',
304 'Please use __weak in files build with ARC, nothing otherwise.',
305 ),
306 False,
307 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15308 BanRule(
Avi Drissman7382afa02019-04-29 23:27:13309 'freeWhenDone:NO',
310 (
311 'The use of "freeWhenDone:NO" with the NoCopy creation of ',
312 'Foundation types is prohibited.',
313 ),
314 True,
315 ),
[email protected]127f18ec2012-06-16 05:05:59316)
317
Sylvain Defresnea8b73d252018-02-28 15:45:54318_BANNED_IOS_OBJC_FUNCTIONS = (
Daniel Chenga44a1bcd2022-03-15 20:00:15319 BanRule(
Sylvain Defresnea8b73d252018-02-28 15:45:54320 r'/\bTEST[(]',
321 (
322 'TEST() macro should not be used in Objective-C++ code as it does not ',
323 'drain the autorelease pool at the end of the test. Use TEST_F() ',
324 'macro instead with a fixture inheriting from PlatformTest (or a ',
325 'typedef).'
326 ),
327 True,
328 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15329 BanRule(
Sylvain Defresnea8b73d252018-02-28 15:45:54330 r'/\btesting::Test\b',
331 (
332 'testing::Test should not be used in Objective-C++ code as it does ',
333 'not drain the autorelease pool at the end of the test. Use ',
334 'PlatformTest instead.'
335 ),
336 True,
337 ),
Ewann2ecc8d72022-07-18 07:41:23338 BanRule(
339 ' systemImageNamed:',
340 (
341 '+[UIImage systemImageNamed:] should not be used to create symbols.',
342 'Instead use a wrapper defined in:',
343 'ios/chrome/browser/ui/icons/chrome_symbol.h'
344 ),
345 True,
Ewann450a2ef2022-07-19 14:38:23346 excluded_paths=(
347 'ios/chrome/browser/ui/icons/chrome_symbol.mm',
348 ),
Ewann2ecc8d72022-07-18 07:41:23349 ),
Sylvain Defresnea8b73d252018-02-28 15:45:54350)
351
Daniel Cheng917ce542022-03-15 20:46:57352_BANNED_IOS_EGTEST_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15353 BanRule(
Peter K. Lee6c03ccff2019-07-15 14:40:05354 r'/\bEXPECT_OCMOCK_VERIFY\b',
355 (
356 'EXPECT_OCMOCK_VERIFY should not be used in EarlGrey tests because ',
357 'it is meant for GTests. Use [mock verify] instead.'
358 ),
359 True,
360 ),
361)
362
Daniel Cheng917ce542022-03-15 20:46:57363_BANNED_CPP_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15364 BanRule(
Peter Kasting94a56c42019-10-25 21:54:04365 r'/\busing namespace ',
366 (
367 'Using directives ("using namespace x") are banned by the Google Style',
368 'Guide ( http://google.github.io/styleguide/cppguide.html#Namespaces ).',
369 'Explicitly qualify symbols or use using declarations ("using x::foo").',
370 ),
371 True,
372 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
373 ),
Antonio Gomes07300d02019-03-13 20:59:57374 # Make sure that gtest's FRIEND_TEST() macro is not used; the
375 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
376 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
Daniel Chenga44a1bcd2022-03-15 20:00:15377 BanRule(
[email protected]23e6cbc2012-06-16 18:51:20378 'FRIEND_TEST(',
379 (
[email protected]e3c945502012-06-26 20:01:49380 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20381 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
382 ),
383 False,
[email protected]7345da02012-11-27 14:31:49384 (),
[email protected]23e6cbc2012-06-16 18:51:20385 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15386 BanRule(
tomhudsone2c14d552016-05-26 17:07:46387 'setMatrixClip',
388 (
389 'Overriding setMatrixClip() is prohibited; ',
390 'the base function is deprecated. ',
391 ),
392 True,
393 (),
394 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15395 BanRule(
[email protected]52657f62013-05-20 05:30:31396 'SkRefPtr',
397 (
398 'The use of SkRefPtr is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22399 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31400 ),
401 True,
402 (),
403 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15404 BanRule(
[email protected]52657f62013-05-20 05:30:31405 'SkAutoRef',
406 (
407 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22408 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31409 ),
410 True,
411 (),
412 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15413 BanRule(
[email protected]52657f62013-05-20 05:30:31414 'SkAutoTUnref',
415 (
416 'The use of SkAutoTUnref is dangerous because it implicitly ',
tomhudson7e6e0512016-04-19 19:27:22417 'converts to a raw pointer. Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31418 ),
419 True,
420 (),
421 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15422 BanRule(
[email protected]52657f62013-05-20 05:30:31423 'SkAutoUnref',
424 (
425 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
426 'because it implicitly converts to a raw pointer. ',
tomhudson7e6e0512016-04-19 19:27:22427 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31428 ),
429 True,
430 (),
431 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15432 BanRule(
[email protected]d89eec82013-12-03 14:10:59433 r'/HANDLE_EINTR\(.*close',
434 (
435 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
436 'descriptor will be closed, and it is incorrect to retry the close.',
437 'Either call close directly and ignore its return value, or wrap close',
438 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
439 ),
440 True,
441 (),
442 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15443 BanRule(
[email protected]d89eec82013-12-03 14:10:59444 r'/IGNORE_EINTR\((?!.*close)',
445 (
446 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
447 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
448 ),
449 True,
450 (
451 # Files that #define IGNORE_EINTR.
Bruce Dawson40fece62022-09-16 19:58:31452 r'^base/posix/eintr_wrapper\.h$',
453 r'^ppapi/tests/test_broker\.cc$',
[email protected]d89eec82013-12-03 14:10:59454 ),
455 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15456 BanRule(
[email protected]ec5b3f02014-04-04 18:43:43457 r'/v8::Extension\(',
458 (
459 'Do not introduce new v8::Extensions into the code base, use',
460 'gin::Wrappable instead. See http://crbug.com/334679',
461 ),
462 True,
[email protected]f55c90ee62014-04-12 00:50:03463 (
Bruce Dawson40fece62022-09-16 19:58:31464 r'extensions/renderer/safe_builtins\.*',
[email protected]f55c90ee62014-04-12 00:50:03465 ),
[email protected]ec5b3f02014-04-04 18:43:43466 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15467 BanRule(
jame2d1a952016-04-02 00:27:10468 '#pragma comment(lib,',
469 (
470 'Specify libraries to link with in build files and not in the source.',
471 ),
472 True,
Mirko Bonadeif4f0f0e2018-04-12 09:29:41473 (
Bruce Dawson40fece62022-09-16 19:58:31474 r'^base/third_party/symbolize/.*',
475 r'^third_party/abseil-cpp/.*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41476 ),
jame2d1a952016-04-02 00:27:10477 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15478 BanRule(
Gabriel Charette7cc6c432018-04-25 20:52:02479 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59480 (
481 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
482 ),
483 False,
484 (),
485 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15486 BanRule(
Gabriel Charette7cc6c432018-04-25 20:52:02487 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59488 (
489 'Consider using THREAD_CHECKER macros instead of the class directly.',
490 ),
491 False,
492 (),
493 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15494 BanRule(
Sean Maher03efef12022-09-23 22:43:13495 r'/\b(?!(Sequenced|SingleThread))\w*TaskRunner::(GetCurrentDefault|CurrentDefaultHandle)',
496 (
497 'It is not allowed to call these methods from the subclasses ',
498 'of Sequenced or SingleThread task runners.',
499 ),
500 True,
501 (),
502 ),
503 BanRule(
Yuri Wiitala2f8de5c2017-07-21 00:11:06504 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
505 (
506 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
507 'deprecated (http://crbug.com/634507). Please avoid converting away',
508 'from the Time types in Chromium code, especially if any math is',
509 'being done on time values. For interfacing with platform/library',
510 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
511 'type converter methods instead. For faking TimeXXX values (for unit',
Peter Kasting53fd6ee2021-10-05 20:40:48512 'testing only), use TimeXXX() + Microseconds(N). For',
Yuri Wiitala2f8de5c2017-07-21 00:11:06513 'other use cases, please contact base/time/OWNERS.',
514 ),
515 False,
516 (),
517 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15518 BanRule(
dbeamb6f4fde2017-06-15 04:03:06519 'CallJavascriptFunctionUnsafe',
520 (
521 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
522 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
523 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
524 ),
525 False,
526 (
Bruce Dawson40fece62022-09-16 19:58:31527 r'^content/browser/webui/web_ui_impl\.(cc|h)$',
528 r'^content/public/browser/web_ui\.h$',
529 r'^content/public/test/test_web_ui\.(cc|h)$',
dbeamb6f4fde2017-06-15 04:03:06530 ),
531 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15532 BanRule(
dskiba1474c2bfd62017-07-20 02:19:24533 'leveldb::DB::Open',
534 (
535 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
536 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
537 "Chrome's tracing, making their memory usage visible.",
538 ),
539 True,
540 (
541 r'^third_party/leveldatabase/.*\.(cc|h)$',
542 ),
Gabriel Charette0592c3a2017-07-26 12:02:04543 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15544 BanRule(
Chris Mumfordc38afb62017-10-09 17:55:08545 'leveldb::NewMemEnv',
546 (
547 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58548 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
549 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08550 ),
551 True,
552 (
553 r'^third_party/leveldatabase/.*\.(cc|h)$',
554 ),
555 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15556 BanRule(
Gabriel Charetted9839bc2017-07-29 14:17:47557 'RunLoop::QuitCurrent',
558 (
Robert Liao64b7ab22017-08-04 23:03:43559 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
560 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47561 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41562 False,
Gabriel Charetted9839bc2017-07-29 14:17:47563 (),
Gabriel Charettea44975052017-08-21 23:14:04564 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15565 BanRule(
Gabriel Charettea44975052017-08-21 23:14:04566 'base::ScopedMockTimeMessageLoopTaskRunner',
567 (
Gabriel Charette87cc1af2018-04-25 20:52:51568 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
Gabriel Charettedfa36042019-08-19 17:30:11569 'TaskEnvironment::TimeSource::MOCK_TIME. There are still a',
Gabriel Charette87cc1af2018-04-25 20:52:51570 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
571 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
572 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04573 ),
Gabriel Charette87cc1af2018-04-25 20:52:51574 False,
Gabriel Charettea44975052017-08-21 23:14:04575 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57576 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15577 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44578 'std::regex',
Eric Stevenson6b47b44c2017-08-30 20:41:57579 (
580 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02581 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57582 ),
583 True,
Danil Chapovalov7bc42a72020-12-09 18:20:16584 # Abseil's benchmarks never linked into chrome.
585 ['third_party/abseil-cpp/.*_benchmark.cc'],
Francois Doray43670e32017-09-27 12:40:38586 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15587 BanRule(
Peter Kasting991618a62019-06-17 22:00:09588 r'/\bstd::stoi\b',
589 (
590 'std::stoi uses exceptions to communicate results. ',
591 'Use base::StringToInt() instead.',
592 ),
593 True,
594 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
595 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15596 BanRule(
Peter Kasting991618a62019-06-17 22:00:09597 r'/\bstd::stol\b',
598 (
599 'std::stol uses exceptions to communicate results. ',
600 'Use base::StringToInt() instead.',
601 ),
602 True,
603 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
604 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15605 BanRule(
Peter Kasting991618a62019-06-17 22:00:09606 r'/\bstd::stoul\b',
607 (
608 'std::stoul uses exceptions to communicate results. ',
609 'Use base::StringToUint() instead.',
610 ),
611 True,
612 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
613 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15614 BanRule(
Peter Kasting991618a62019-06-17 22:00:09615 r'/\bstd::stoll\b',
616 (
617 'std::stoll uses exceptions to communicate results. ',
618 'Use base::StringToInt64() instead.',
619 ),
620 True,
621 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
622 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15623 BanRule(
Peter Kasting991618a62019-06-17 22:00:09624 r'/\bstd::stoull\b',
625 (
626 'std::stoull uses exceptions to communicate results. ',
627 'Use base::StringToUint64() instead.',
628 ),
629 True,
630 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
631 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15632 BanRule(
Peter Kasting991618a62019-06-17 22:00:09633 r'/\bstd::stof\b',
634 (
635 'std::stof uses exceptions to communicate results. ',
636 'For locale-independent values, e.g. reading numbers from disk',
637 'profiles, use base::StringToDouble().',
638 'For user-visible values, parse using ICU.',
639 ),
640 True,
641 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
642 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15643 BanRule(
Peter Kasting991618a62019-06-17 22:00:09644 r'/\bstd::stod\b',
645 (
646 'std::stod uses exceptions to communicate results. ',
647 'For locale-independent values, e.g. reading numbers from disk',
648 'profiles, use base::StringToDouble().',
649 'For user-visible values, parse using ICU.',
650 ),
651 True,
652 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
653 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15654 BanRule(
Peter Kasting991618a62019-06-17 22:00:09655 r'/\bstd::stold\b',
656 (
657 'std::stold uses exceptions to communicate results. ',
658 'For locale-independent values, e.g. reading numbers from disk',
659 'profiles, use base::StringToDouble().',
660 'For user-visible values, parse using ICU.',
661 ),
662 True,
663 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
664 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15665 BanRule(
Daniel Bratell69334cc2019-03-26 11:07:45666 r'/\bstd::to_string\b',
667 (
668 'std::to_string is locale dependent and slower than alternatives.',
Peter Kasting991618a62019-06-17 22:00:09669 'For locale-independent strings, e.g. writing numbers to disk',
670 'profiles, use base::NumberToString().',
Daniel Bratell69334cc2019-03-26 11:07:45671 'For user-visible strings, use base::FormatNumber() and',
672 'the related functions in base/i18n/number_formatting.h.',
673 ),
Peter Kasting991618a62019-06-17 22:00:09674 False, # Only a warning since it is already used.
Daniel Bratell609102be2019-03-27 20:53:21675 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45676 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15677 BanRule(
Daniel Bratell69334cc2019-03-26 11:07:45678 r'/\bstd::shared_ptr\b',
679 (
680 'std::shared_ptr should not be used. Use scoped_refptr instead.',
681 ),
682 True,
Ulan Degenbaev947043882021-02-10 14:02:31683 [
684 # Needed for interop with third-party library.
685 '^third_party/blink/renderer/core/typed_arrays/array_buffer/' +
Alex Chau9eb03cdd52020-07-13 21:04:57686 'array_buffer_contents\.(cc|h)',
Ben Kelly39bf6bef2021-10-04 22:54:58687 '^third_party/blink/renderer/bindings/core/v8/' +
688 'v8_wasm_response_extensions.cc',
Wez5f56be52021-05-04 09:30:58689 '^gin/array_buffer\.(cc|h)',
690 '^chrome/services/sharing/nearby/',
Stephen Nuskoe09c8ef22022-09-29 00:47:28691 # Needed for interop with third-party library libunwindstack.
Stephen Nuskoe51c1382022-09-26 15:49:03692 '^base/profiler/libunwindstack_unwinder_android\.(cc|h)',
Meilin Wang00efc7c2021-05-13 01:12:42693 # gRPC provides some C++ libraries that use std::shared_ptr<>.
Yeunjoo Choi1b644402022-08-25 02:36:10694 '^chromeos/ash/services/libassistant/grpc/',
Vigen Issahhanjanfdf9de52021-12-22 21:13:59695 '^chromecast/cast_core/grpc',
696 '^chromecast/cast_core/runtime/browser',
Yue Shef83d95202022-09-26 20:23:45697 '^ios/chrome/test/earl_grey/chrome_egtest_plugin_client\.(mm|h)',
Wez5f56be52021-05-04 09:30:58698 # Fuchsia provides C++ libraries that use std::shared_ptr<>.
Fabrice de Gans3b875422022-04-19 19:40:26699 '^base/fuchsia/filtered_service_directory\.(cc|h)',
700 '^base/fuchsia/service_directory_test_base\.h',
Wez5f56be52021-05-04 09:30:58701 '.*fuchsia.*test\.(cc|h)',
Will Cassella64da6c52022-01-06 18:13:57702 # Needed for clang plugin tests
703 '^tools/clang/plugins/tests/',
Alex Chau9eb03cdd52020-07-13 21:04:57704 _THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell609102be2019-03-27 20:53:21705 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15706 BanRule(
Peter Kasting991618a62019-06-17 22:00:09707 r'/\bstd::weak_ptr\b',
708 (
709 'std::weak_ptr should not be used. Use base::WeakPtr instead.',
710 ),
711 True,
712 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
713 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15714 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21715 r'/\blong long\b',
716 (
717 'long long is banned. Use stdint.h if you need a 64 bit number.',
718 ),
719 False, # Only a warning since it is already used.
720 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
721 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15722 BanRule(
Daniel Chengc05fcc62022-01-12 16:54:29723 r'\b(absl|std)::any\b',
724 (
Daniel Chenga44a1bcd2022-03-15 20:00:15725 'absl::any / std::any are not safe to use in a component build.',
Daniel Chengc05fcc62022-01-12 16:54:29726 ),
727 True,
728 # Not an error in third party folders, though it probably should be :)
729 [_THIRD_PARTY_EXCEPT_BLINK],
730 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15731 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21732 r'/\bstd::bind\b',
733 (
734 'std::bind is banned because of lifetime risks.',
735 'Use base::BindOnce or base::BindRepeating instead.',
736 ),
737 True,
738 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
739 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15740 BanRule(
Avi Drissman48ee39e2022-02-16 16:31:03741 r'/\bstd::optional\b',
742 (
743 'std::optional is banned. Use absl::optional instead.',
744 ),
745 True,
746 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
747 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15748 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21749 r'/\b#include <chrono>\b',
750 (
751 '<chrono> overlaps with Time APIs in base. Keep using',
752 'base classes.',
753 ),
754 True,
755 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
756 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15757 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21758 r'/\b#include <exception>\b',
759 (
760 'Exceptions are banned and disabled in Chromium.',
761 ),
762 True,
763 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
764 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15765 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21766 r'/\bstd::function\b',
767 (
Colin Blundellea615d422021-05-12 09:35:41768 'std::function is banned. Instead use base::OnceCallback or ',
769 'base::RepeatingCallback, which directly support Chromium\'s weak ',
770 'pointers, ref counting and more.',
Daniel Bratell609102be2019-03-27 20:53:21771 ),
Daniel Chenge5583e3c2022-09-22 00:19:41772 True,
Daniel Chengcd23b8b2022-09-16 17:16:24773 [
774 # Has tests that template trait helpers don't unintentionally match
775 # std::function.
Daniel Chenge5583e3c2022-09-22 00:19:41776 r'base/functional/callback_helpers_unittest\.cc',
777 # Required to implement interfaces from the third-party perfetto
778 # library.
779 r'base/tracing/perfetto_task_runner\.cc',
780 r'base/tracing/perfetto_task_runner\.h',
781 # Needed for interop with the third-party nearby library type
782 # location::nearby::connections::ResultCallback.
783 'chrome/services/sharing/nearby/nearby_connections_conversions\.cc'
784 # Needed for interop with the internal libassistant library.
785 'chromeos/ash/services/libassistant/callback_utils\.h',
786 # Needed for interop with Fuchsia fidl APIs.
787 'fuchsia_web/webengine/browser/context_impl_browsertest\.cc',
788 'fuchsia_web/webengine/browser/cookie_manager_impl_unittest\.cc',
789 'fuchsia_web/webengine/browser/media_player_impl_unittest\.cc',
790 # Required to interop with interfaces from the third-party perfetto
791 # library.
792 'services/tracing/public/cpp/perfetto/custom_event_recorder\.cc',
793 'services/tracing/public/cpp/perfetto/perfetto_traced_process\.cc',
794 'services/tracing/public/cpp/perfetto/perfetto_traced_process\.h',
795 'services/tracing/public/cpp/perfetto/perfetto_tracing_backend\.cc',
796 'services/tracing/public/cpp/perfetto/producer_client\.cc',
797 'services/tracing/public/cpp/perfetto/producer_client\.h',
798 'services/tracing/public/cpp/perfetto/producer_test_utils\.cc',
799 'services/tracing/public/cpp/perfetto/producer_test_utils\.h',
800 # Required for interop with the third-party webrtc library.
801 'third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl\.cc',
802 'third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl\.h',
803
804 # TODO(https://crbug.com/1364577): Various uses that should be
805 # migrated to something else.
806 # Should use base::OnceCallback or base::RepeatingCallback.
807 'base/allocator/dispatcher/initializer_unittest\.cc',
808 'chrome/browser/ash/accessibility/speech_monitor\.cc',
809 'chrome/browser/ash/accessibility/speech_monitor\.h',
810 'chrome/browser/ash/login/ash_hud_login_browsertest\.cc',
811 'chromecast/base/observer_unittest\.cc',
812 'chromecast/browser/cast_web_view\.h',
813 'chromecast/public/cast_media_shlib\.h',
814 'device/bluetooth/floss/exported_callback_manager\.h',
815 'device/bluetooth/floss/floss_dbus_client\.h',
816 'device/fido/cable/v2_handshake_unittest\.cc',
817 'device/fido/pin\.cc',
818 'services/tracing/perfetto/test_utils\.h',
819 # Should use base::FunctionRef.
820 'chrome/browser/media/webrtc/test_stats_dictionary\.cc',
821 'chrome/browser/media/webrtc/test_stats_dictionary\.h',
822 'chromeos/ash/services/libassistant/device_settings_controller\.cc',
823 'components/browser_ui/client_certificate/android/ssl_client_certificate_request\.cc',
824 'components/gwp_asan/client/sampling_malloc_shims_unittest\.cc',
825 'content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest\.cc',
826 # Does not need std::function at all.
827 'components/omnibox/browser/autocomplete_result\.cc',
828 'device/fido/win/webauthn_api\.cc',
829 'media/audio/alsa/alsa_util\.cc',
830 'media/remoting/stream_provider\.h',
831 'sql/vfs_wrapper\.cc',
832 # TODO(https://crbug.com/1364585): Remove usage and exception list
833 # entries.
834 'extensions/renderer/api/automation/automation_internal_custom_bindings\.cc',
835 'extensions/renderer/api/automation/automation_internal_custom_bindings\.h',
836 # TODO(https://crbug.com/1364579): Remove usage and exception list
837 # entry.
838 'ui/views/controls/focus_ring\.h',
839
840 # Various pre-existing uses in //tools that is low-priority to fix.
841 'tools/binary_size/libsupersize/viewer/caspian/diff\.cc',
842 'tools/binary_size/libsupersize/viewer/caspian/model\.cc',
843 'tools/binary_size/libsupersize/viewer/caspian/model\.h',
844 'tools/binary_size/libsupersize/viewer/caspian/tree_builder\.h',
845 'tools/clang/base_bind_rewriters/BaseBindRewriters\.cpp',
846
Daniel Chengcd23b8b2022-09-16 17:16:24847 # Not an error in third_party folders.
848 _THIRD_PARTY_EXCEPT_BLINK
849 ],
Daniel Bratell609102be2019-03-27 20:53:21850 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15851 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21852 r'/\b#include <random>\b',
853 (
854 'Do not use any random number engines from <random>. Instead',
855 'use base::RandomBitGenerator.',
856 ),
857 True,
858 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
859 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15860 BanRule(
Tom Andersona95e12042020-09-09 23:08:00861 r'/\b#include <X11/',
862 (
863 'Do not use Xlib. Use xproto (from //ui/gfx/x:xproto) instead.',
864 ),
865 True,
866 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
867 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15868 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21869 r'/\bstd::ratio\b',
870 (
871 'std::ratio is banned by the Google Style Guide.',
872 ),
873 True,
874 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45875 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15876 BanRule(
Gabriel Charetted90bcc92021-09-21 00:23:10877 ('base::ThreadRestrictions::ScopedAllowIO'),
Francois Doray43670e32017-09-27 12:40:38878 (
Gabriel Charetted90bcc92021-09-21 00:23:10879 'ScopedAllowIO is deprecated, use ScopedAllowBlocking instead.',
Francois Doray43670e32017-09-27 12:40:38880 ),
Gabriel Charette04b138f2018-08-06 00:03:22881 False,
Francois Doray43670e32017-09-27 12:40:38882 (),
883 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15884 BanRule(
Michael Giuffrida7f93d6922019-04-19 14:39:58885 r'/\bRunMessageLoop\b',
Gabriel Charette147335ea2018-03-22 15:59:19886 (
887 'RunMessageLoop is deprecated, use RunLoop instead.',
888 ),
889 False,
890 (),
891 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15892 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44893 'RunThisRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19894 (
895 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
896 ),
897 False,
898 (),
899 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15900 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44901 'RunAllPendingInMessageLoop()',
Gabriel Charette147335ea2018-03-22 15:59:19902 (
903 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
904 "if you're convinced you need this.",
905 ),
906 False,
907 (),
908 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15909 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44910 'RunAllPendingInMessageLoop(BrowserThread',
Gabriel Charette147335ea2018-03-22 15:59:19911 (
912 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
Gabriel Charette798fde72019-08-20 22:24:04913 'BrowserThread::UI, BrowserTaskEnvironment::RunIOThreadUntilIdle',
Gabriel Charette147335ea2018-03-22 15:59:19914 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
915 'async events instead of flushing threads.',
916 ),
917 False,
918 (),
919 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15920 BanRule(
Gabriel Charette147335ea2018-03-22 15:59:19921 r'MessageLoopRunner',
922 (
923 'MessageLoopRunner is deprecated, use RunLoop instead.',
924 ),
925 False,
926 (),
927 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15928 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44929 'GetDeferredQuitTaskForRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19930 (
931 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
932 "gab@ if you found a use case where this is the only solution.",
933 ),
934 False,
935 (),
936 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15937 BanRule(
Victor Costane48a2e82019-03-15 22:02:34938 'sqlite3_initialize(',
Victor Costan3653df62018-02-08 21:38:16939 (
Victor Costane48a2e82019-03-15 22:02:34940 'Instead of calling sqlite3_initialize(), depend on //sql, ',
Victor Costan3653df62018-02-08 21:38:16941 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
942 ),
943 True,
944 (
945 r'^sql/initialization\.(cc|h)$',
946 r'^third_party/sqlite/.*\.(c|cc|h)$',
947 ),
948 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15949 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44950 'std::random_shuffle',
tzik5de2157f2018-05-08 03:42:47951 (
952 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
953 'base::RandomShuffle instead.'
954 ),
955 True,
956 (),
957 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15958 BanRule(
Javier Ernesto Flores Robles749e6c22018-10-08 09:36:24959 'ios/web/public/test/http_server',
960 (
961 'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
962 ),
963 False,
964 (),
965 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15966 BanRule(
Robert Liao764c9492019-01-24 18:46:28967 'GetAddressOf',
968 (
969 'Improper use of Microsoft::WRL::ComPtr<T>::GetAddressOf() has been ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53970 'implicated in a few leaks. ReleaseAndGetAddressOf() is safe but ',
Joshua Berenhaus8b972ec2020-09-11 20:00:11971 'operator& is generally recommended. So always use operator& instead. ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53972 'See http://crbug.com/914910 for more conversion guidance.'
Robert Liao764c9492019-01-24 18:46:28973 ),
974 True,
975 (),
976 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15977 BanRule(
Ben Lewisa9514602019-04-29 17:53:05978 'SHFileOperation',
979 (
980 'SHFileOperation was deprecated in Windows Vista, and there are less ',
981 'complex functions to achieve the same goals. Use IFileOperation for ',
982 'any esoteric actions instead.'
983 ),
984 True,
985 (),
986 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15987 BanRule(
Cliff Smolinsky81951642019-04-30 21:39:51988 'StringFromGUID2',
989 (
990 'StringFromGUID2 introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:24991 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:51992 ),
993 True,
994 (
Daniel Chenga44a1bcd2022-03-15 20:00:15995 r'/base/win/win_util_unittest.cc',
Cliff Smolinsky81951642019-04-30 21:39:51996 ),
997 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15998 BanRule(
Cliff Smolinsky81951642019-04-30 21:39:51999 'StringFromCLSID',
1000 (
1001 'StringFromCLSID introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:241002 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:511003 ),
1004 True,
1005 (
Daniel Chenga44a1bcd2022-03-15 20:00:151006 r'/base/win/win_util_unittest.cc',
Cliff Smolinsky81951642019-04-30 21:39:511007 ),
1008 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151009 BanRule(
Avi Drissman7382afa02019-04-29 23:27:131010 'kCFAllocatorNull',
1011 (
1012 'The use of kCFAllocatorNull with the NoCopy creation of ',
1013 'CoreFoundation types is prohibited.',
1014 ),
1015 True,
1016 (),
1017 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151018 BanRule(
Oksana Zhuravlovafd247772019-05-16 16:57:291019 'mojo::ConvertTo',
1020 (
1021 'mojo::ConvertTo and TypeConverter are deprecated. Please consider',
1022 'StructTraits / UnionTraits / EnumTraits / ArrayTraits / MapTraits /',
1023 'StringTraits if you would like to convert between custom types and',
1024 'the wire format of mojom types.'
1025 ),
Oksana Zhuravlova1d3b59de2019-05-17 00:08:221026 False,
Oksana Zhuravlovafd247772019-05-16 16:57:291027 (
David Dorwin13dc48b2022-06-03 21:18:421028 r'^fuchsia_web/webengine/browser/url_request_rewrite_rules_manager\.cc$',
1029 r'^fuchsia_web/webengine/url_request_rewrite_type_converters\.cc$',
Oksana Zhuravlovafd247772019-05-16 16:57:291030 r'^third_party/blink/.*\.(cc|h)$',
1031 r'^content/renderer/.*\.(cc|h)$',
1032 ),
1033 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151034 BanRule(
Oksana Zhuravlovac8222d22019-12-19 19:21:161035 'GetInterfaceProvider',
1036 (
1037 'InterfaceProvider is deprecated.',
1038 'Please use ExecutionContext::GetBrowserInterfaceBroker and overrides',
1039 'or Platform::GetBrowserInterfaceBroker.'
1040 ),
1041 False,
1042 (),
1043 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151044 BanRule(
Robert Liao1d78df52019-11-11 20:02:011045 'CComPtr',
1046 (
1047 'New code should use Microsoft::WRL::ComPtr from wrl/client.h as a ',
1048 'replacement for CComPtr from ATL. See http://crbug.com/5027 for more ',
1049 'details.'
1050 ),
1051 False,
1052 (),
1053 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151054 BanRule(
Xiaohan Wang72bd2ba2020-02-18 21:38:201055 r'/\b(IFACE|STD)METHOD_?\(',
1056 (
1057 'IFACEMETHOD() and STDMETHOD() make code harder to format and read.',
1058 'Instead, always use IFACEMETHODIMP in the declaration.'
1059 ),
1060 False,
1061 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
1062 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151063 BanRule(
Allen Bauer53b43fb12020-03-12 17:21:471064 'set_owned_by_client',
1065 (
1066 'set_owned_by_client is deprecated.',
1067 'views::View already owns the child views by default. This introduces ',
1068 'a competing ownership model which makes the code difficult to reason ',
1069 'about. See http://crbug.com/1044687 for more details.'
1070 ),
1071 False,
1072 (),
1073 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151074 BanRule(
Peter Boström7ff41522021-07-29 03:43:271075 'RemoveAllChildViewsWithoutDeleting',
1076 (
1077 'RemoveAllChildViewsWithoutDeleting is deprecated.',
1078 'This method is deemed dangerous as, unless raw pointers are re-added,',
1079 'calls to this method introduce memory leaks.'
1080 ),
1081 False,
1082 (),
1083 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151084 BanRule(
Eric Secklerbe6f48d2020-05-06 18:09:121085 r'/\bTRACE_EVENT_ASYNC_',
1086 (
1087 'Please use TRACE_EVENT_NESTABLE_ASYNC_.. macros instead',
1088 'of TRACE_EVENT_ASYNC_.. (crbug.com/1038710).',
1089 ),
1090 False,
1091 (
1092 r'^base/trace_event/.*',
1093 r'^base/tracing/.*',
1094 ),
1095 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151096 BanRule(
Aditya Kushwah5a286b72022-02-10 04:54:431097 r'/\bbase::debug::DumpWithoutCrashingUnthrottled[(][)]',
1098 (
1099 'base::debug::DumpWithoutCrashingUnthrottled() does not throttle',
1100 'dumps and may spam crash reports. Consider if the throttled',
1101 'variants suffice instead.',
1102 ),
1103 False,
1104 (),
1105 ),
Daniel Chenga44a1bcd2022-03-15 20:00:151106 BanRule(
Robert Liao22f66a52021-04-10 00:57:521107 'RoInitialize',
1108 (
Robert Liao48018922021-04-16 23:03:021109 'Improper use of [base::win]::RoInitialize() has been implicated in a ',
Robert Liao22f66a52021-04-10 00:57:521110 'few COM initialization leaks. Use base::win::ScopedWinrtInitializer ',
1111 'instead. See http://crbug.com/1197722 for more information.'
1112 ),
1113 True,
Robert Liao48018922021-04-16 23:03:021114 (
Bruce Dawson40fece62022-09-16 19:58:311115 r'^base/win/scoped_winrt_initializer\.cc$',
Robert Liao48018922021-04-16 23:03:021116 ),
Robert Liao22f66a52021-04-10 00:57:521117 ),
Patrick Monettec343bb982022-06-01 17:18:451118 BanRule(
1119 r'base::Watchdog',
1120 (
1121 'base::Watchdog is deprecated because it creates its own thread.',
1122 'Instead, manually start a timer on a SequencedTaskRunner.',
1123 ),
1124 False,
1125 (),
1126 ),
Andrew Rayskiy04a51ce2022-06-07 11:47:091127 BanRule(
1128 'base::Passed',
1129 (
1130 'Do not use base::Passed. It is a legacy helper for capturing ',
1131 'move-only types with base::BindRepeating, but invoking the ',
1132 'resulting RepeatingCallback moves the captured value out of ',
1133 'the callback storage, and subsequent invocations may pass the ',
1134 'value in a valid but undefined state. Prefer base::BindOnce().',
1135 'See http://crbug.com/1326449 for context.'
1136 ),
1137 False,
Daniel Cheng91f6fbaf2022-09-16 12:07:481138 (
1139 # False positive, but it is also fine to let bind internals reference
1140 # base::Passed.
Daniel Chengcd23b8b2022-09-16 17:16:241141 r'^base[\\/]functional[\\/]bind\.h',
Daniel Cheng91f6fbaf2022-09-16 12:07:481142 r'^base[\\/]functional[\\/]bind_internal\.h',
1143 ),
Andrew Rayskiy04a51ce2022-06-07 11:47:091144 ),
Daniel Cheng2248b332022-07-27 06:16:591145 BanRule(
1146 r'/\babsl::FunctionRef\b',
1147 (
1148 'absl::FunctionRef is banned. Use base::FunctionRef instead.',
1149 ),
Daniel Cheng4dd164d2022-07-27 17:39:001150 True,
Daniel Cheng2248b332022-07-27 06:16:591151 [
1152 # base::Bind{Once,Repeating} references absl::FunctionRef to disallow
1153 # interoperability.
Bruce Dawson40fece62022-09-16 19:58:311154 r'^base/functional/bind_internal\.h',
Daniel Cheng2248b332022-07-27 06:16:591155 # base::FunctionRef is implemented on top of absl::FunctionRef.
Bruce Dawson40fece62022-09-16 19:58:311156 r'^base/functional/function_ref.*\..+',
Daniel Cheng2248b332022-07-27 06:16:591157 # Not an error in third_party folders.
1158 _THIRD_PARTY_EXCEPT_BLINK,
1159 ],
1160 ),
[email protected]127f18ec2012-06-16 05:05:591161)
1162
Daniel Cheng92c15e32022-03-16 17:48:221163_BANNED_MOJOM_PATTERNS : Sequence[BanRule] = (
1164 BanRule(
1165 'handle<shared_buffer>',
1166 (
1167 'Please use one of the more specific shared memory types instead:',
1168 ' mojo_base.mojom.ReadOnlySharedMemoryRegion',
1169 ' mojo_base.mojom.WritableSharedMemoryRegion',
1170 ' mojo_base.mojom.UnsafeSharedMemoryRegion',
1171 ),
1172 True,
1173 ),
1174)
1175
mlamouria82272622014-09-16 18:45:041176_IPC_ENUM_TRAITS_DEPRECATED = (
1177 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501178 'See http://www.chromium.org/Home/chromium-security/education/'
1179 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:041180
Stephen Martinis97a394142018-06-07 23:06:051181_LONG_PATH_ERROR = (
1182 'Some files included in this CL have file names that are too long (> 200'
1183 ' characters). If committed, these files will cause issues on Windows. See'
1184 ' https://crbug.com/612667 for more details.'
1185)
1186
Shenghua Zhangbfaa38b82017-11-16 21:58:021187_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
Bruce Dawson40fece62022-09-16 19:58:311188 r".*/AppHooksImpl\.java",
1189 r".*/BuildHooksAndroidImpl\.java",
1190 r".*/LicenseContentProvider\.java",
1191 r".*/PlatformServiceBridgeImpl.java",
1192 r".*chrome/android/feed/dummy/.*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:021193]
[email protected]127f18ec2012-06-16 05:05:591194
Mohamed Heikald048240a2019-11-12 16:57:371195# List of image extensions that are used as resources in chromium.
1196_IMAGE_EXTENSIONS = ['.svg', '.png', '.webp']
1197
Sean Kau46e29bc2017-08-28 16:31:161198# These paths contain test data and other known invalid JSON files.
Erik Staab2dd72b12020-04-16 15:03:401199_KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS = [
Bruce Dawson40fece62022-09-16 19:58:311200 r'test/data/',
1201 r'testing/buildbot/',
1202 r'^components/policy/resources/policy_templates\.json$',
1203 r'^third_party/protobuf/',
1204 r'^third_party/blink/perf_tests/speedometer/resources/todomvc/learn.json',
1205 r'^third_party/blink/renderer/devtools/protocol\.json$',
1206 r'^third_party/blink/web_tests/external/wpt/',
1207 r'^tools/perf/',
1208 r'^tools/traceline/svgui/startup-release.json',
Daniel Cheng2d4c2d192022-07-01 01:38:311209 # vscode configuration files allow comments
Bruce Dawson40fece62022-09-16 19:58:311210 r'^tools/vscode/',
Sean Kau46e29bc2017-08-28 16:31:161211]
1212
Andrew Grieveb773bad2020-06-05 18:00:381213# These are not checked on the public chromium-presubmit trybot.
1214# Add files here that rely on .py files that exists only for target_os="android"
Samuel Huangc2f5d6bb2020-08-17 23:46:041215# checkouts.
agrievef32bcc72016-04-04 14:57:401216_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Grieveb773bad2020-06-05 18:00:381217 'chrome/android/features/create_stripped_java_factory.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381218]
1219
1220
1221_GENERIC_PYDEPS_FILES = [
Bruce Dawson853b739e62022-05-03 23:03:101222 'android_webview/test/components/run_webview_component_smoketest.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041223 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361224 'base/android/jni_generator/jni_generator.pydeps',
1225 'base/android/jni_generator/jni_registration_generator.pydeps',
Andrew Grieve4c4cede2020-11-20 22:09:361226 'build/android/apk_operations.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041227 'build/android/devil_chromium.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361228 'build/android/gyp/aar.pydeps',
1229 'build/android/gyp/aidl.pydeps',
Tibor Goldschwendt0bef2d7a2019-10-24 21:19:271230 'build/android/gyp/allot_native_libraries.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361231 'build/android/gyp/apkbuilder.pydeps',
Andrew Grievea417ad302019-02-06 19:54:381232 'build/android/gyp/assert_static_initializers.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361233 'build/android/gyp/bytecode_processor.pydeps',
Robbie McElrath360e54d2020-11-12 20:38:021234 'build/android/gyp/bytecode_rewriter.pydeps',
Mohamed Heikal6305bcc2021-03-15 15:34:221235 'build/android/gyp/check_flag_expectations.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111236 'build/android/gyp/compile_java.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361237 'build/android/gyp/compile_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361238 'build/android/gyp/copy_ex.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361239 'build/android/gyp/create_apk_operations_script.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111240 'build/android/gyp/create_app_bundle.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041241 'build/android/gyp/create_app_bundle_apks.pydeps',
1242 'build/android/gyp/create_bundle_wrapper_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361243 'build/android/gyp/create_java_binary_script.pydeps',
Mohamed Heikaladbe4e482020-07-09 19:25:121244 'build/android/gyp/create_r_java.pydeps',
Mohamed Heikal8cd763a52021-02-01 23:32:091245 'build/android/gyp/create_r_txt.pydeps',
Andrew Grieveb838d832019-02-11 16:55:221246 'build/android/gyp/create_size_info_files.pydeps',
Peter Wene6e017e2022-07-27 21:40:401247 'build/android/gyp/create_test_apk_wrapper_script.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001248 'build/android/gyp/create_ui_locale_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361249 'build/android/gyp/dex.pydeps',
Andrew Grieve723c1502020-04-23 16:27:421250 'build/android/gyp/dex_jdk_libs.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041251 'build/android/gyp/dexsplitter.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361252 'build/android/gyp/dist_aar.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361253 'build/android/gyp/filter_zip.pydeps',
Mohamed Heikal21e1994b2021-11-12 21:37:211254 'build/android/gyp/flatc_java.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361255 'build/android/gyp/gcc_preprocess.pydeps',
Christopher Grant99e0e20062018-11-21 21:22:361256 'build/android/gyp/generate_linker_version_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361257 'build/android/gyp/ijar.pydeps',
Yun Liueb4075ddf2019-05-13 19:47:581258 'build/android/gyp/jacoco_instr.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361259 'build/android/gyp/java_cpp_enum.pydeps',
Nate Fischerac07b2622020-10-01 20:20:141260 'build/android/gyp/java_cpp_features.pydeps',
Ian Vollickb99472e2019-03-07 21:35:261261 'build/android/gyp/java_cpp_strings.pydeps',
Andrew Grieve09457912021-04-27 15:22:471262 'build/android/gyp/java_google_api_keys.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041263 'build/android/gyp/jinja_template.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361264 'build/android/gyp/lint.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361265 'build/android/gyp/merge_manifest.pydeps',
Bruce Dawson853b739e62022-05-03 23:03:101266 'build/android/gyp/optimize_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361267 'build/android/gyp/prepare_resources.pydeps',
Mohamed Heikalf85138b2020-10-06 15:43:221268 'build/android/gyp/process_native_prebuilt.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361269 'build/android/gyp/proguard.pydeps',
Andrew Grievee3a775ab2022-05-16 15:59:221270 'build/android/gyp/system_image_apks.pydeps',
Bruce Dawson853b739e62022-05-03 23:03:101271 'build/android/gyp/trace_event_bytecode_rewriter.pydeps',
Peter Wen578730b2020-03-19 19:55:461272 'build/android/gyp/turbine.pydeps',
Mohamed Heikal246710c2021-06-14 15:34:301273 'build/android/gyp/unused_resources.pydeps',
Eric Stevensona82cf6082019-07-24 14:35:241274 'build/android/gyp/validate_static_library_dex_references.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361275 'build/android/gyp/write_build_config.pydeps',
Tibor Goldschwendtc4caae92019-07-12 00:33:461276 'build/android/gyp/write_native_libraries_java.pydeps',
Andrew Grieve9ff17792018-11-30 04:55:561277 'build/android/gyp/zip.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361278 'build/android/incremental_install/generate_android_manifest.pydeps',
1279 'build/android/incremental_install/write_installer_json.pydeps',
Stephanie Kim392913b452022-06-15 17:25:321280 'build/android/pylib/results/presentation/test_results_presentation.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041281 'build/android/resource_sizes.pydeps',
1282 'build/android/test_runner.pydeps',
1283 'build/android/test_wrapper/logdog_wrapper.pydeps',
Samuel Huange65eb3f12020-08-14 19:04:361284 'build/lacros/lacros_resource_sizes.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361285 'build/protoc_java.pydeps',
Peter Kotwicz64667b02020-10-18 06:43:321286 'chrome/android/monochrome/scripts/monochrome_python_tests.pydeps',
Peter Wenefb56c72020-06-04 15:12:271287 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
1288 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Junbo Kedcd3a452021-03-19 17:55:041289 'chromecast/resource_sizes/chromecast_resource_sizes.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001290 'components/cronet/tools/generate_javadoc.pydeps',
1291 'components/cronet/tools/jar_src.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381292 'components/module_installer/android/module_desc_java.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001293 'content/public/android/generate_child_service.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381294 'net/tools/testserver/testserver.pydeps',
Peter Kotwicz3c339f32020-10-19 19:59:181295 'testing/scripts/run_isolated_script_test.pydeps',
Stephanie Kimc94072c2022-03-22 22:31:411296 'testing/merge_scripts/standard_isolated_script_merge.pydeps',
1297 'testing/merge_scripts/standard_gtest_merge.pydeps',
1298 'testing/merge_scripts/code_coverage/merge_results.pydeps',
1299 'testing/merge_scripts/code_coverage/merge_steps.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041300 'third_party/android_platform/development/scripts/stack.pydeps',
Hitoshi Yoshida0f228c42019-08-07 09:37:421301 'third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps',
Yuki Shiino38eeaad12022-08-11 06:40:251302 'third_party/blink/renderer/bindings/scripts/check_generated_file_list.pydeps',
Hitoshi Yoshida0f228c42019-08-07 09:37:421303 'third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps',
Yuki Shiinoe7827aa2019-09-13 12:26:131304 'third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps',
Canon Mukaif32f8f592021-04-23 18:56:501305 'third_party/blink/renderer/bindings/scripts/validate_web_idl.pydeps',
Stephanie Kimc94072c2022-03-22 22:31:411306 'third_party/blink/tools/blinkpy/web_tests/merge_results.pydeps',
1307 'third_party/blink/tools/merge_web_test_results.pydeps',
John Budorickbc3571aa2019-04-25 02:20:061308 'tools/binary_size/sizes.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:221309 'tools/binary_size/supersize.pydeps',
Ben Pastene028104a2022-08-10 19:17:451310 'tools/perf/process_perf_results.pydeps',
agrievef32bcc72016-04-04 14:57:401311]
1312
wnwenbdc444e2016-05-25 13:44:151313
agrievef32bcc72016-04-04 14:57:401314_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
1315
1316
Eric Boren6fd2b932018-01-25 15:05:081317# Bypass the AUTHORS check for these accounts.
1318_KNOWN_ROBOTS = set(
Sergiy Byelozyorov47158a52018-06-13 22:38:591319 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:451320 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:591321 ) | set('%[email protected]' % s
smutde797052019-12-04 02:03:521322 for s in ('bling-autoroll-builder', 'v8-ci-autoroll-builder',
Sven Zhengf7abd31d2021-08-09 19:06:231323 'wpt-autoroller', 'chrome-weblayer-builder',
Garrett Beaty4d4fcf62021-11-24 17:57:471324 'lacros-version-skew-roller', 'skylab-test-cros-roller',
Sven Zheng722960ba2022-07-18 16:40:461325 'infra-try-recipes-tester', 'lacros-tracking-roller',
1326 'lacros-sdk-version-roller')
Eric Boren835d71f2018-09-07 21:09:041327 ) | set('%[email protected]' % s
Eric Boren66150e52020-01-08 11:20:271328 for s in ('chromium-autoroll', 'chromium-release-autoroll')
Eric Boren835d71f2018-09-07 21:09:041329 ) | set('%[email protected]' % s
Yulan Lineb0cfba2021-04-09 18:43:161330 for s in ('chromium-internal-autoroll',)
1331 ) | set('%[email protected]' % s
1332 for s in ('swarming-tasks',))
Eric Boren6fd2b932018-01-25 15:05:081333
Matt Stark6ef08872021-07-29 01:21:461334_INVALID_GRD_FILE_LINE = [
1335 (r'<file lang=.* path=.*', 'Path should come before lang in GRD files.')
1336]
Eric Boren6fd2b932018-01-25 15:05:081337
Daniel Bratell65b033262019-04-23 08:17:061338def _IsCPlusPlusFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501339 """Returns True if this file contains C++-like code (and not Python,
1340 Go, Java, MarkDown, ...)"""
Daniel Bratell65b033262019-04-23 08:17:061341
Sam Maiera6e76d72022-02-11 21:43:501342 ext = input_api.os_path.splitext(file_path)[1]
1343 # This list is compatible with CppChecker.IsCppFile but we should
1344 # consider adding ".c" to it. If we do that we can use this function
1345 # at more places in the code.
1346 return ext in (
1347 '.h',
1348 '.cc',
1349 '.cpp',
1350 '.m',
1351 '.mm',
1352 )
1353
Daniel Bratell65b033262019-04-23 08:17:061354
1355def _IsCPlusPlusHeaderFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501356 return input_api.os_path.splitext(file_path)[1] == ".h"
Daniel Bratell65b033262019-04-23 08:17:061357
1358
1359def _IsJavaFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501360 return input_api.os_path.splitext(file_path)[1] == ".java"
Daniel Bratell65b033262019-04-23 08:17:061361
1362
1363def _IsProtoFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501364 return input_api.os_path.splitext(file_path)[1] == ".proto"
Daniel Bratell65b033262019-04-23 08:17:061365
Mohamed Heikal5e5b7922020-10-29 18:57:591366
Erik Staabc734cd7a2021-11-23 03:11:521367def _IsXmlOrGrdFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501368 ext = input_api.os_path.splitext(file_path)[1]
1369 return ext in ('.grd', '.xml')
Erik Staabc734cd7a2021-11-23 03:11:521370
1371
Mohamed Heikal5e5b7922020-10-29 18:57:591372def CheckNoUpstreamDepsOnClank(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501373 """Prevent additions of dependencies from the upstream repo on //clank."""
1374 # clank can depend on clank
1375 if input_api.change.RepositoryRoot().endswith('clank'):
1376 return []
1377 build_file_patterns = [
1378 r'(.+/)?BUILD\.gn',
1379 r'.+\.gni',
1380 ]
1381 excluded_files = [r'build[/\\]config[/\\]android[/\\]config\.gni']
1382 bad_pattern = input_api.re.compile(r'^[^#]*//clank')
Mohamed Heikal5e5b7922020-10-29 18:57:591383
Sam Maiera6e76d72022-02-11 21:43:501384 error_message = 'Disallowed import on //clank in an upstream build file:'
Mohamed Heikal5e5b7922020-10-29 18:57:591385
Sam Maiera6e76d72022-02-11 21:43:501386 def FilterFile(affected_file):
1387 return input_api.FilterSourceFile(affected_file,
1388 files_to_check=build_file_patterns,
1389 files_to_skip=excluded_files)
Mohamed Heikal5e5b7922020-10-29 18:57:591390
Sam Maiera6e76d72022-02-11 21:43:501391 problems = []
1392 for f in input_api.AffectedSourceFiles(FilterFile):
1393 local_path = f.LocalPath()
1394 for line_number, line in f.ChangedContents():
1395 if (bad_pattern.search(line)):
1396 problems.append('%s:%d\n %s' %
1397 (local_path, line_number, line.strip()))
1398 if problems:
1399 return [output_api.PresubmitPromptOrNotify(error_message, problems)]
1400 else:
1401 return []
Mohamed Heikal5e5b7922020-10-29 18:57:591402
1403
Saagar Sanghavifceeaae2020-08-12 16:40:361404def CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501405 """Attempts to prevent use of functions intended only for testing in
1406 non-testing code. For now this is just a best-effort implementation
1407 that ignores header files and may have some false positives. A
1408 better implementation would probably need a proper C++ parser.
1409 """
1410 # We only scan .cc files and the like, as the declaration of
1411 # for-testing functions in header files are hard to distinguish from
1412 # calls to such functions without a proper C++ parser.
1413 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:191414
Sam Maiera6e76d72022-02-11 21:43:501415 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
1416 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' %
1417 base_function_pattern)
1418 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
1419 allowlist_pattern = input_api.re.compile(r'// IN-TEST$')
1420 exclusion_pattern = input_api.re.compile(
1421 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' %
1422 (base_function_pattern, base_function_pattern))
1423 # Avoid a false positive in this case, where the method name, the ::, and
1424 # the closing { are all on different lines due to line wrapping.
1425 # HelperClassForTesting::
1426 # HelperClassForTesting(
1427 # args)
1428 # : member(0) {}
1429 method_defn_pattern = input_api.re.compile(r'[A-Za-z0-9_]+::$')
[email protected]55459852011-08-10 15:17:191430
Sam Maiera6e76d72022-02-11 21:43:501431 def FilterFile(affected_file):
1432 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1433 input_api.DEFAULT_FILES_TO_SKIP)
1434 return input_api.FilterSourceFile(
1435 affected_file,
1436 files_to_check=file_inclusion_pattern,
1437 files_to_skip=files_to_skip)
[email protected]55459852011-08-10 15:17:191438
Sam Maiera6e76d72022-02-11 21:43:501439 problems = []
1440 for f in input_api.AffectedSourceFiles(FilterFile):
1441 local_path = f.LocalPath()
1442 in_method_defn = False
1443 for line_number, line in f.ChangedContents():
1444 if (inclusion_pattern.search(line)
1445 and not comment_pattern.search(line)
1446 and not exclusion_pattern.search(line)
1447 and not allowlist_pattern.search(line)
1448 and not in_method_defn):
1449 problems.append('%s:%d\n %s' %
1450 (local_path, line_number, line.strip()))
1451 in_method_defn = method_defn_pattern.search(line)
[email protected]55459852011-08-10 15:17:191452
Sam Maiera6e76d72022-02-11 21:43:501453 if problems:
1454 return [
1455 output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)
1456 ]
1457 else:
1458 return []
[email protected]55459852011-08-10 15:17:191459
1460
Saagar Sanghavifceeaae2020-08-12 16:40:361461def CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501462 """This is a simplified version of
1463 CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
1464 """
1465 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
1466 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
1467 name_pattern = r'ForTest(s|ing)?'
1468 # Describes an occurrence of "ForTest*" inside a // comment.
1469 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
1470 # Describes @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
1471 annotation_re = input_api.re.compile(r'@VisibleForTesting\(')
1472 # Catch calls.
1473 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
1474 # Ignore definitions. (Comments are ignored separately.)
1475 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
Vaclav Brozek7dbc28c2018-03-27 08:35:231476
Sam Maiera6e76d72022-02-11 21:43:501477 problems = []
1478 sources = lambda x: input_api.FilterSourceFile(
1479 x,
1480 files_to_skip=(('(?i).*test', r'.*\/junit\/') + input_api.
1481 DEFAULT_FILES_TO_SKIP),
1482 files_to_check=[r'.*\.java$'])
1483 for f in input_api.AffectedFiles(include_deletes=False,
1484 file_filter=sources):
1485 local_path = f.LocalPath()
Vaclav Brozek7dbc28c2018-03-27 08:35:231486 is_inside_javadoc = False
Sam Maiera6e76d72022-02-11 21:43:501487 for line_number, line in f.ChangedContents():
1488 if is_inside_javadoc and javadoc_end_re.search(line):
1489 is_inside_javadoc = False
1490 if not is_inside_javadoc and javadoc_start_re.search(line):
1491 is_inside_javadoc = True
1492 if is_inside_javadoc:
1493 continue
1494 if (inclusion_re.search(line) and not comment_re.search(line)
1495 and not annotation_re.search(line)
1496 and not exclusion_re.search(line)):
1497 problems.append('%s:%d\n %s' %
1498 (local_path, line_number, line.strip()))
Vaclav Brozek7dbc28c2018-03-27 08:35:231499
Sam Maiera6e76d72022-02-11 21:43:501500 if problems:
1501 return [
1502 output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)
1503 ]
1504 else:
1505 return []
Vaclav Brozek7dbc28c2018-03-27 08:35:231506
1507
Saagar Sanghavifceeaae2020-08-12 16:40:361508def CheckNoIOStreamInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501509 """Checks to make sure no .h files include <iostream>."""
1510 files = []
1511 pattern = input_api.re.compile(r'^#include\s*<iostream>',
1512 input_api.re.MULTILINE)
1513 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1514 if not f.LocalPath().endswith('.h'):
1515 continue
1516 contents = input_api.ReadFile(f)
1517 if pattern.search(contents):
1518 files.append(f)
[email protected]10689ca2011-09-02 02:31:541519
Sam Maiera6e76d72022-02-11 21:43:501520 if len(files):
1521 return [
1522 output_api.PresubmitError(
1523 'Do not #include <iostream> in header files, since it inserts static '
1524 'initialization into every file including the header. Instead, '
1525 '#include <ostream>. See http://crbug.com/94794', files)
1526 ]
1527 return []
1528
[email protected]10689ca2011-09-02 02:31:541529
Aleksey Khoroshilov9b28c032022-06-03 16:35:321530def CheckNoStrCatRedefines(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501531 """Checks no windows headers with StrCat redefined are included directly."""
1532 files = []
Aleksey Khoroshilov9b28c032022-06-03 16:35:321533 files_to_check = (r'.+%s' % _HEADER_EXTENSIONS,
1534 r'.+%s' % _IMPLEMENTATION_EXTENSIONS)
1535 files_to_skip = (input_api.DEFAULT_FILES_TO_SKIP +
1536 _NON_BASE_DEPENDENT_PATHS)
1537 sources_filter = lambda f: input_api.FilterSourceFile(
1538 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
1539
Sam Maiera6e76d72022-02-11 21:43:501540 pattern_deny = input_api.re.compile(
1541 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
1542 input_api.re.MULTILINE)
1543 pattern_allow = input_api.re.compile(
1544 r'^#include\s"base/win/windows_defines.inc"', input_api.re.MULTILINE)
Aleksey Khoroshilov9b28c032022-06-03 16:35:321545 for f in input_api.AffectedSourceFiles(sources_filter):
Sam Maiera6e76d72022-02-11 21:43:501546 contents = input_api.ReadFile(f)
1547 if pattern_deny.search(
1548 contents) and not pattern_allow.search(contents):
1549 files.append(f.LocalPath())
Danil Chapovalov3518f362018-08-11 16:13:431550
Sam Maiera6e76d72022-02-11 21:43:501551 if len(files):
1552 return [
1553 output_api.PresubmitError(
1554 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
1555 'directly since they pollute code with StrCat macro. Instead, '
1556 'include matching header from base/win. See http://crbug.com/856536',
1557 files)
1558 ]
1559 return []
Danil Chapovalov3518f362018-08-11 16:13:431560
[email protected]10689ca2011-09-02 02:31:541561
Saagar Sanghavifceeaae2020-08-12 16:40:361562def CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501563 """Checks to make sure no source files use UNIT_TEST."""
1564 problems = []
1565 for f in input_api.AffectedFiles():
1566 if (not f.LocalPath().endswith(('.cc', '.mm'))):
1567 continue
[email protected]72df4e782012-06-21 16:28:181568
Sam Maiera6e76d72022-02-11 21:43:501569 for line_num, line in f.ChangedContents():
1570 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
1571 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]72df4e782012-06-21 16:28:181572
Sam Maiera6e76d72022-02-11 21:43:501573 if not problems:
1574 return []
1575 return [
1576 output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
1577 '\n'.join(problems))
1578 ]
1579
[email protected]72df4e782012-06-21 16:28:181580
Saagar Sanghavifceeaae2020-08-12 16:40:361581def CheckNoDISABLETypoInTests(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501582 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
Dominic Battre033531052018-09-24 15:45:341583
Sam Maiera6e76d72022-02-11 21:43:501584 This test warns if somebody tries to disable a test with the DISABLE_ prefix
1585 instead of DISABLED_. To filter false positives, reports are only generated
1586 if a corresponding MAYBE_ line exists.
1587 """
1588 problems = []
Dominic Battre033531052018-09-24 15:45:341589
Sam Maiera6e76d72022-02-11 21:43:501590 # The following two patterns are looked for in tandem - is a test labeled
1591 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
1592 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
1593 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
Dominic Battre033531052018-09-24 15:45:341594
Sam Maiera6e76d72022-02-11 21:43:501595 # This is for the case that a test is disabled on all platforms.
1596 full_disable_pattern = input_api.re.compile(
1597 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
1598 input_api.re.MULTILINE)
Dominic Battre033531052018-09-24 15:45:341599
Sam Maiera6e76d72022-02-11 21:43:501600 for f in input_api.AffectedFiles(False):
1601 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1602 continue
Dominic Battre033531052018-09-24 15:45:341603
Sam Maiera6e76d72022-02-11 21:43:501604 # Search for MABYE_, DISABLE_ pairs.
1605 disable_lines = {} # Maps of test name to line number.
1606 maybe_lines = {}
1607 for line_num, line in f.ChangedContents():
1608 disable_match = disable_pattern.search(line)
1609 if disable_match:
1610 disable_lines[disable_match.group(1)] = line_num
1611 maybe_match = maybe_pattern.search(line)
1612 if maybe_match:
1613 maybe_lines[maybe_match.group(1)] = line_num
Dominic Battre033531052018-09-24 15:45:341614
Sam Maiera6e76d72022-02-11 21:43:501615 # Search for DISABLE_ occurrences within a TEST() macro.
1616 disable_tests = set(disable_lines.keys())
1617 maybe_tests = set(maybe_lines.keys())
1618 for test in disable_tests.intersection(maybe_tests):
1619 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
Dominic Battre033531052018-09-24 15:45:341620
Sam Maiera6e76d72022-02-11 21:43:501621 contents = input_api.ReadFile(f)
1622 full_disable_match = full_disable_pattern.search(contents)
1623 if full_disable_match:
1624 problems.append(' %s' % f.LocalPath())
Dominic Battre033531052018-09-24 15:45:341625
Sam Maiera6e76d72022-02-11 21:43:501626 if not problems:
1627 return []
1628 return [
1629 output_api.PresubmitPromptWarning(
1630 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
1631 '\n'.join(problems))
1632 ]
1633
Dominic Battre033531052018-09-24 15:45:341634
Nina Satragnof7660532021-09-20 18:03:351635def CheckForgettingMAYBEInTests(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501636 """Checks to make sure tests disabled conditionally are not missing a
1637 corresponding MAYBE_ prefix.
1638 """
1639 # Expect at least a lowercase character in the test name. This helps rule out
1640 # false positives with macros wrapping the actual tests name.
1641 define_maybe_pattern = input_api.re.compile(
1642 r'^\#define MAYBE_(?P<test_name>\w*[a-z]\w*)')
Bruce Dawsonffc55292022-04-20 04:18:191643 # The test_maybe_pattern needs to handle all of these forms. The standard:
1644 # IN_PROC_TEST_F(SyncTest, MAYBE_Start) {
1645 # With a wrapper macro around the test name:
1646 # IN_PROC_TEST_F(SyncTest, E2E_ENABLED(MAYBE_Start)) {
1647 # And the odd-ball NACL_BROWSER_TEST_f format:
1648 # NACL_BROWSER_TEST_F(NaClBrowserTest, SimpleLoad, {
1649 # The optional E2E_ENABLED-style is handled with (\w*\()?
1650 # The NACL_BROWSER_TEST_F pattern is handled by allowing a trailing comma or
1651 # trailing ')'.
1652 test_maybe_pattern = (
1653 r'^\s*\w*TEST[^(]*\(\s*\w+,\s*(\w*\()?MAYBE_{test_name}[\),]')
Sam Maiera6e76d72022-02-11 21:43:501654 suite_maybe_pattern = r'^\s*\w*TEST[^(]*\(\s*MAYBE_{test_name}[\),]'
1655 warnings = []
Nina Satragnof7660532021-09-20 18:03:351656
Sam Maiera6e76d72022-02-11 21:43:501657 # Read the entire files. We can't just read the affected lines, forgetting to
1658 # add MAYBE_ on a change would not show up otherwise.
1659 for f in input_api.AffectedFiles(False):
1660 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1661 continue
1662 contents = input_api.ReadFile(f)
1663 lines = contents.splitlines(True)
1664 current_position = 0
1665 warning_test_names = set()
1666 for line_num, line in enumerate(lines, start=1):
1667 current_position += len(line)
1668 maybe_match = define_maybe_pattern.search(line)
1669 if maybe_match:
1670 test_name = maybe_match.group('test_name')
1671 # Do not warn twice for the same test.
1672 if (test_name in warning_test_names):
1673 continue
1674 warning_test_names.add(test_name)
Nina Satragnof7660532021-09-20 18:03:351675
Sam Maiera6e76d72022-02-11 21:43:501676 # Attempt to find the corresponding MAYBE_ test or suite, starting from
1677 # the current position.
1678 test_match = input_api.re.compile(
1679 test_maybe_pattern.format(test_name=test_name),
1680 input_api.re.MULTILINE).search(contents, current_position)
1681 suite_match = input_api.re.compile(
1682 suite_maybe_pattern.format(test_name=test_name),
1683 input_api.re.MULTILINE).search(contents, current_position)
1684 if not test_match and not suite_match:
1685 warnings.append(
1686 output_api.PresubmitPromptWarning(
1687 '%s:%d found MAYBE_ defined without corresponding test %s'
1688 % (f.LocalPath(), line_num, test_name)))
1689 return warnings
1690
[email protected]72df4e782012-06-21 16:28:181691
Saagar Sanghavifceeaae2020-08-12 16:40:361692def CheckDCHECK_IS_ONHasBraces(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501693 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
1694 errors = []
Kalvin Lee4a3b79de2022-05-26 16:00:161695 pattern = input_api.re.compile(r'\bDCHECK_IS_ON\b(?!\(\))',
Sam Maiera6e76d72022-02-11 21:43:501696 input_api.re.MULTILINE)
1697 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1698 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
1699 continue
1700 for lnum, line in f.ChangedContents():
1701 if input_api.re.search(pattern, line):
1702 errors.append(
1703 output_api.PresubmitError((
1704 '%s:%d: Use of DCHECK_IS_ON() must be written as "#if '
1705 + 'DCHECK_IS_ON()", not forgetting the parentheses.') %
1706 (f.LocalPath(), lnum)))
1707 return errors
danakj61c1aa22015-10-26 19:55:521708
1709
Weilun Shia487fad2020-10-28 00:10:341710# TODO(crbug/1138055): Reimplement CheckUmaHistogramChangesOnUpload check in a
1711# more reliable way. See
1712# https://chromium-review.googlesource.com/c/chromium/src/+/2500269
mcasasb7440c282015-02-04 14:52:191713
wnwenbdc444e2016-05-25 13:44:151714
Saagar Sanghavifceeaae2020-08-12 16:40:361715def CheckFlakyTestUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501716 """Check that FlakyTest annotation is our own instead of the android one"""
1717 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1718 files = []
1719 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1720 if f.LocalPath().endswith('Test.java'):
1721 if pattern.search(input_api.ReadFile(f)):
1722 files.append(f)
1723 if len(files):
1724 return [
1725 output_api.PresubmitError(
1726 'Use org.chromium.base.test.util.FlakyTest instead of '
1727 'android.test.FlakyTest', files)
1728 ]
1729 return []
mcasasb7440c282015-02-04 14:52:191730
wnwenbdc444e2016-05-25 13:44:151731
Saagar Sanghavifceeaae2020-08-12 16:40:361732def CheckNoDEPSGIT(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501733 """Make sure .DEPS.git is never modified manually."""
1734 if any(f.LocalPath().endswith('.DEPS.git')
1735 for f in input_api.AffectedFiles()):
1736 return [
1737 output_api.PresubmitError(
1738 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1739 'automated system based on what\'s in DEPS and your changes will be\n'
1740 'overwritten.\n'
1741 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1742 'get-the-code#Rolling_DEPS\n'
1743 'for more information')
1744 ]
1745 return []
[email protected]2a8ac9c2011-10-19 17:20:441746
1747
Saagar Sanghavifceeaae2020-08-12 16:40:361748def CheckValidHostsInDEPSOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501749 """Checks that DEPS file deps are from allowed_hosts."""
1750 # Run only if DEPS file has been modified to annoy fewer bystanders.
1751 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1752 return []
1753 # Outsource work to gclient verify
1754 try:
1755 gclient_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
1756 'third_party', 'depot_tools',
1757 'gclient.py')
1758 input_api.subprocess.check_output(
Bruce Dawson8a43cf72022-05-13 17:10:321759 [input_api.python3_executable, gclient_path, 'verify'],
Sam Maiera6e76d72022-02-11 21:43:501760 stderr=input_api.subprocess.STDOUT)
1761 return []
1762 except input_api.subprocess.CalledProcessError as error:
1763 return [
1764 output_api.PresubmitError(
1765 'DEPS file must have only git dependencies.',
1766 long_text=error.output)
1767 ]
tandriief664692014-09-23 14:51:471768
1769
Mario Sanchez Prada2472cab2019-09-18 10:58:311770def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
Daniel Chenga44a1bcd2022-03-15 20:00:151771 ban_rule):
Allen Bauer84778682022-09-22 16:28:561772 """Helper method for checking for banned constructs.
Mario Sanchez Prada2472cab2019-09-18 10:58:311773
Sam Maiera6e76d72022-02-11 21:43:501774 Returns an string composed of the name of the file, the line number where the
1775 match has been found and the additional text passed as |message| in case the
1776 target type name matches the text inside the line passed as parameter.
1777 """
1778 result = []
Peng Huang9c5949a02020-06-11 19:20:541779
Daniel Chenga44a1bcd2022-03-15 20:00:151780 # Ignore comments about banned types.
1781 if input_api.re.search(r"^ *//", line):
Sam Maiera6e76d72022-02-11 21:43:501782 return result
Daniel Chenga44a1bcd2022-03-15 20:00:151783 # A // nocheck comment will bypass this error.
1784 if line.endswith(" nocheck"):
Sam Maiera6e76d72022-02-11 21:43:501785 return result
1786
1787 matched = False
Daniel Chenga44a1bcd2022-03-15 20:00:151788 if ban_rule.pattern[0:1] == '/':
1789 regex = ban_rule.pattern[1:]
Sam Maiera6e76d72022-02-11 21:43:501790 if input_api.re.search(regex, line):
1791 matched = True
Daniel Chenga44a1bcd2022-03-15 20:00:151792 elif ban_rule.pattern in line:
Sam Maiera6e76d72022-02-11 21:43:501793 matched = True
1794
1795 if matched:
1796 result.append(' %s:%d:' % (affected_file.LocalPath(), line_number))
Daniel Chenga44a1bcd2022-03-15 20:00:151797 for line in ban_rule.explanation:
1798 result.append(' %s' % line)
Sam Maiera6e76d72022-02-11 21:43:501799
danakjd18e8892020-12-17 17:42:011800 return result
Mario Sanchez Prada2472cab2019-09-18 10:58:311801
1802
Saagar Sanghavifceeaae2020-08-12 16:40:361803def CheckNoBannedFunctions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501804 """Make sure that banned functions are not used."""
1805 warnings = []
1806 errors = []
[email protected]127f18ec2012-06-16 05:05:591807
Sam Maiera6e76d72022-02-11 21:43:501808 def IsExcludedFile(affected_file, excluded_paths):
Daniel Chenga44a1bcd2022-03-15 20:00:151809 if not excluded_paths:
1810 return False
1811
Sam Maiera6e76d72022-02-11 21:43:501812 local_path = affected_file.LocalPath()
Bruce Dawson40fece62022-09-16 19:58:311813 # Consistently use / as path separator to simplify the writing of regex
1814 # expressions.
1815 local_path = local_path.replace(input_api.os_path.sep, '/')
Sam Maiera6e76d72022-02-11 21:43:501816 for item in excluded_paths:
1817 if input_api.re.match(item, local_path):
1818 return True
1819 return False
wnwenbdc444e2016-05-25 13:44:151820
Sam Maiera6e76d72022-02-11 21:43:501821 def IsIosObjcFile(affected_file):
1822 local_path = affected_file.LocalPath()
1823 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m',
1824 '.h'):
1825 return False
1826 basename = input_api.os_path.basename(local_path)
1827 if 'ios' in basename.split('_'):
1828 return True
1829 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1830 if sep and 'ios' in local_path.split(sep):
1831 return True
1832 return False
Sylvain Defresnea8b73d252018-02-28 15:45:541833
Daniel Chenga44a1bcd2022-03-15 20:00:151834 def CheckForMatch(affected_file, line_num: int, line: str,
1835 ban_rule: BanRule):
1836 if IsExcludedFile(affected_file, ban_rule.excluded_paths):
1837 return
1838
Sam Maiera6e76d72022-02-11 21:43:501839 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
Daniel Chenga44a1bcd2022-03-15 20:00:151840 ban_rule)
Sam Maiera6e76d72022-02-11 21:43:501841 if problems:
Daniel Chenga44a1bcd2022-03-15 20:00:151842 if ban_rule.treat_as_error is not None and ban_rule.treat_as_error:
Sam Maiera6e76d72022-02-11 21:43:501843 errors.extend(problems)
1844 else:
1845 warnings.extend(problems)
wnwenbdc444e2016-05-25 13:44:151846
Sam Maiera6e76d72022-02-11 21:43:501847 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1848 for f in input_api.AffectedFiles(file_filter=file_filter):
1849 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151850 for ban_rule in _BANNED_JAVA_FUNCTIONS:
1851 CheckForMatch(f, line_num, line, ban_rule)
Eric Stevensona9a980972017-09-23 00:04:411852
Sam Maiera6e76d72022-02-11 21:43:501853 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1854 for f in input_api.AffectedFiles(file_filter=file_filter):
1855 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151856 for ban_rule in _BANNED_OBJC_FUNCTIONS:
1857 CheckForMatch(f, line_num, line, ban_rule)
[email protected]127f18ec2012-06-16 05:05:591858
Sam Maiera6e76d72022-02-11 21:43:501859 for f in input_api.AffectedFiles(file_filter=IsIosObjcFile):
1860 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151861 for ban_rule in _BANNED_IOS_OBJC_FUNCTIONS:
1862 CheckForMatch(f, line_num, line, ban_rule)
Sylvain Defresnea8b73d252018-02-28 15:45:541863
Sam Maiera6e76d72022-02-11 21:43:501864 egtest_filter = lambda f: f.LocalPath().endswith(('_egtest.mm'))
1865 for f in input_api.AffectedFiles(file_filter=egtest_filter):
1866 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151867 for ban_rule in _BANNED_IOS_EGTEST_FUNCTIONS:
1868 CheckForMatch(f, line_num, line, ban_rule)
Peter K. Lee6c03ccff2019-07-15 14:40:051869
Sam Maiera6e76d72022-02-11 21:43:501870 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1871 for f in input_api.AffectedFiles(file_filter=file_filter):
1872 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151873 for ban_rule in _BANNED_CPP_FUNCTIONS:
1874 CheckForMatch(f, line_num, line, ban_rule)
[email protected]127f18ec2012-06-16 05:05:591875
Daniel Cheng92c15e32022-03-16 17:48:221876 file_filter = lambda f: f.LocalPath().endswith(('.mojom'))
1877 for f in input_api.AffectedFiles(file_filter=file_filter):
1878 for line_num, line in f.ChangedContents():
1879 for ban_rule in _BANNED_MOJOM_PATTERNS:
1880 CheckForMatch(f, line_num, line, ban_rule)
1881
1882
Sam Maiera6e76d72022-02-11 21:43:501883 result = []
1884 if (warnings):
1885 result.append(
1886 output_api.PresubmitPromptWarning('Banned functions were used.\n' +
1887 '\n'.join(warnings)))
1888 if (errors):
1889 result.append(
1890 output_api.PresubmitError('Banned functions were used.\n' +
1891 '\n'.join(errors)))
1892 return result
[email protected]127f18ec2012-06-16 05:05:591893
Allen Bauer84778682022-09-22 16:28:561894def CheckNoLayoutCallsInTests(input_api, output_api):
1895 """Make sure there are no explicit calls to View::Layout() in tests"""
1896 warnings = []
1897 ban_rule = BanRule(
1898 r'/(\.|->)Layout\(\);',
1899 (
1900 'Direct calls to View::Layout() are not allowed in tests. '
1901 'If the view must be laid out here, use RunScheduledLayout(view). It '
1902 'is found in //ui/views/test/views_test_utils.h. '
1903 'See http://crbug.com/1350521 for more details.',
1904 ),
1905 False,
1906 )
1907 file_filter = lambda f: input_api.re.search(
1908 r'_(unittest|browsertest|ui_test).*\.(cc|mm)$', f.LocalPath())
1909 for f in input_api.AffectedFiles(file_filter = file_filter):
1910 for line_num, line in f.ChangedContents():
1911 problems = _GetMessageForMatchingType(input_api, f,
1912 line_num, line,
1913 ban_rule)
1914 if problems:
1915 warnings.extend(problems)
1916 result = []
1917 if (warnings):
1918 result.append(
1919 output_api.PresubmitPromptWarning(
1920 'Banned call to View::Layout() in tests.\n\n'.join(warnings)))
1921 return result
[email protected]127f18ec2012-06-16 05:05:591922
Michael Thiessen44457642020-02-06 00:24:151923def _CheckAndroidNoBannedImports(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501924 """Make sure that banned java imports are not used."""
1925 errors = []
Michael Thiessen44457642020-02-06 00:24:151926
Sam Maiera6e76d72022-02-11 21:43:501927 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1928 for f in input_api.AffectedFiles(file_filter=file_filter):
1929 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151930 for ban_rule in _BANNED_JAVA_IMPORTS:
1931 # Consider merging this into the above function. There is no
1932 # real difference anymore other than helping with a little
1933 # bit of boilerplate text. Doing so means things like
1934 # `treat_as_error` will also be uniformly handled.
Sam Maiera6e76d72022-02-11 21:43:501935 problems = _GetMessageForMatchingType(input_api, f, line_num,
Daniel Chenga44a1bcd2022-03-15 20:00:151936 line, ban_rule)
Sam Maiera6e76d72022-02-11 21:43:501937 if problems:
1938 errors.extend(problems)
1939 result = []
1940 if (errors):
1941 result.append(
1942 output_api.PresubmitError('Banned imports were used.\n' +
1943 '\n'.join(errors)))
1944 return result
Michael Thiessen44457642020-02-06 00:24:151945
1946
Saagar Sanghavifceeaae2020-08-12 16:40:361947def CheckNoPragmaOnce(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501948 """Make sure that banned functions are not used."""
1949 files = []
1950 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
1951 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1952 if not f.LocalPath().endswith('.h'):
1953 continue
Bruce Dawson4c4c2922022-05-02 18:07:331954 if f.LocalPath().endswith('com_imported_mstscax.h'):
1955 continue
Sam Maiera6e76d72022-02-11 21:43:501956 contents = input_api.ReadFile(f)
1957 if pattern.search(contents):
1958 files.append(f)
[email protected]6c063c62012-07-11 19:11:061959
Sam Maiera6e76d72022-02-11 21:43:501960 if files:
1961 return [
1962 output_api.PresubmitError(
1963 'Do not use #pragma once in header files.\n'
1964 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1965 files)
1966 ]
1967 return []
[email protected]6c063c62012-07-11 19:11:061968
[email protected]127f18ec2012-06-16 05:05:591969
Saagar Sanghavifceeaae2020-08-12 16:40:361970def CheckNoTrinaryTrueFalse(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501971 """Checks to make sure we don't introduce use of foo ? true : false."""
1972 problems = []
1973 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1974 for f in input_api.AffectedFiles():
1975 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1976 continue
[email protected]e7479052012-09-19 00:26:121977
Sam Maiera6e76d72022-02-11 21:43:501978 for line_num, line in f.ChangedContents():
1979 if pattern.match(line):
1980 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]e7479052012-09-19 00:26:121981
Sam Maiera6e76d72022-02-11 21:43:501982 if not problems:
1983 return []
1984 return [
1985 output_api.PresubmitPromptWarning(
1986 'Please consider avoiding the "? true : false" pattern if possible.\n'
1987 + '\n'.join(problems))
1988 ]
[email protected]e7479052012-09-19 00:26:121989
1990
Saagar Sanghavifceeaae2020-08-12 16:40:361991def CheckUnwantedDependencies(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501992 """Runs checkdeps on #include and import statements added in this
1993 change. Breaking - rules is an error, breaking ! rules is a
1994 warning.
1995 """
1996 # Return early if no relevant file types were modified.
1997 for f in input_api.AffectedFiles():
1998 path = f.LocalPath()
1999 if (_IsCPlusPlusFile(input_api, path) or _IsProtoFile(input_api, path)
2000 or _IsJavaFile(input_api, path)):
2001 break
[email protected]55f9f382012-07-31 11:02:182002 else:
Sam Maiera6e76d72022-02-11 21:43:502003 return []
rhalavati08acd232017-04-03 07:23:282004
Sam Maiera6e76d72022-02-11 21:43:502005 import sys
2006 # We need to wait until we have an input_api object and use this
2007 # roundabout construct to import checkdeps because this file is
2008 # eval-ed and thus doesn't have __file__.
2009 original_sys_path = sys.path
2010 try:
2011 sys.path = sys.path + [
2012 input_api.os_path.join(input_api.PresubmitLocalPath(),
2013 'buildtools', 'checkdeps')
2014 ]
2015 import checkdeps
2016 from rules import Rule
2017 finally:
2018 # Restore sys.path to what it was before.
2019 sys.path = original_sys_path
[email protected]55f9f382012-07-31 11:02:182020
Sam Maiera6e76d72022-02-11 21:43:502021 added_includes = []
2022 added_imports = []
2023 added_java_imports = []
2024 for f in input_api.AffectedFiles():
2025 if _IsCPlusPlusFile(input_api, f.LocalPath()):
2026 changed_lines = [line for _, line in f.ChangedContents()]
2027 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
2028 elif _IsProtoFile(input_api, f.LocalPath()):
2029 changed_lines = [line for _, line in f.ChangedContents()]
2030 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
2031 elif _IsJavaFile(input_api, f.LocalPath()):
2032 changed_lines = [line for _, line in f.ChangedContents()]
2033 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:242034
Sam Maiera6e76d72022-02-11 21:43:502035 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
2036
2037 error_descriptions = []
2038 warning_descriptions = []
2039 error_subjects = set()
2040 warning_subjects = set()
2041
2042 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
2043 added_includes):
2044 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
2045 description_with_path = '%s\n %s' % (path, rule_description)
2046 if rule_type == Rule.DISALLOW:
2047 error_descriptions.append(description_with_path)
2048 error_subjects.add("#includes")
2049 else:
2050 warning_descriptions.append(description_with_path)
2051 warning_subjects.add("#includes")
2052
2053 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
2054 added_imports):
2055 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
2056 description_with_path = '%s\n %s' % (path, rule_description)
2057 if rule_type == Rule.DISALLOW:
2058 error_descriptions.append(description_with_path)
2059 error_subjects.add("imports")
2060 else:
2061 warning_descriptions.append(description_with_path)
2062 warning_subjects.add("imports")
2063
2064 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
2065 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
2066 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
2067 description_with_path = '%s\n %s' % (path, rule_description)
2068 if rule_type == Rule.DISALLOW:
2069 error_descriptions.append(description_with_path)
2070 error_subjects.add("imports")
2071 else:
2072 warning_descriptions.append(description_with_path)
2073 warning_subjects.add("imports")
2074
2075 results = []
2076 if error_descriptions:
2077 results.append(
2078 output_api.PresubmitError(
2079 'You added one or more %s that violate checkdeps rules.' %
2080 " and ".join(error_subjects), error_descriptions))
2081 if warning_descriptions:
2082 results.append(
2083 output_api.PresubmitPromptOrNotify(
2084 'You added one or more %s of files that are temporarily\n'
2085 'allowed but being removed. Can you avoid introducing the\n'
2086 '%s? See relevant DEPS file(s) for details and contacts.' %
2087 (" and ".join(warning_subjects), "/".join(warning_subjects)),
2088 warning_descriptions))
2089 return results
[email protected]55f9f382012-07-31 11:02:182090
2091
Saagar Sanghavifceeaae2020-08-12 16:40:362092def CheckFilePermissions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502093 """Check that all files have their permissions properly set."""
2094 if input_api.platform == 'win32':
2095 return []
2096 checkperms_tool = input_api.os_path.join(input_api.PresubmitLocalPath(),
2097 'tools', 'checkperms',
2098 'checkperms.py')
2099 args = [
Bruce Dawson8a43cf72022-05-13 17:10:322100 input_api.python3_executable, checkperms_tool, '--root',
Sam Maiera6e76d72022-02-11 21:43:502101 input_api.change.RepositoryRoot()
2102 ]
2103 with input_api.CreateTemporaryFile() as file_list:
2104 for f in input_api.AffectedFiles():
2105 # checkperms.py file/directory arguments must be relative to the
2106 # repository.
2107 file_list.write((f.LocalPath() + '\n').encode('utf8'))
2108 file_list.close()
2109 args += ['--file-list', file_list.name]
2110 try:
2111 input_api.subprocess.check_output(args)
2112 return []
2113 except input_api.subprocess.CalledProcessError as error:
2114 return [
2115 output_api.PresubmitError('checkperms.py failed:',
2116 long_text=error.output.decode(
2117 'utf-8', 'ignore'))
2118 ]
[email protected]fbcafe5a2012-08-08 15:31:222119
2120
Saagar Sanghavifceeaae2020-08-12 16:40:362121def CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502122 """Makes sure we don't include ui/aura/window_property.h
2123 in header files.
2124 """
2125 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
2126 errors = []
2127 for f in input_api.AffectedFiles():
2128 if not f.LocalPath().endswith('.h'):
2129 continue
2130 for line_num, line in f.ChangedContents():
2131 if pattern.match(line):
2132 errors.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]c8278b32012-10-30 20:35:492133
Sam Maiera6e76d72022-02-11 21:43:502134 results = []
2135 if errors:
2136 results.append(
2137 output_api.PresubmitError(
2138 'Header files should not include ui/aura/window_property.h',
2139 errors))
2140 return results
[email protected]c8278b32012-10-30 20:35:492141
2142
Omer Katzcc77ea92021-04-26 10:23:282143def CheckNoInternalHeapIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502144 """Makes sure we don't include any headers from
2145 third_party/blink/renderer/platform/heap/impl or
2146 third_party/blink/renderer/platform/heap/v8_wrapper from files outside of
2147 third_party/blink/renderer/platform/heap
2148 """
2149 impl_pattern = input_api.re.compile(
2150 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/impl/.*"')
2151 v8_wrapper_pattern = input_api.re.compile(
2152 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/v8_wrapper/.*"'
2153 )
Bruce Dawson40fece62022-09-16 19:58:312154 # Consistently use / as path separator to simplify the writing of regex
2155 # expressions.
Sam Maiera6e76d72022-02-11 21:43:502156 file_filter = lambda f: not input_api.re.match(
Bruce Dawson40fece62022-09-16 19:58:312157 r"^third_party/blink/renderer/platform/heap/.*",
2158 f.LocalPath().replace(input_api.os_path.sep, '/'))
Sam Maiera6e76d72022-02-11 21:43:502159 errors = []
Omer Katzcc77ea92021-04-26 10:23:282160
Sam Maiera6e76d72022-02-11 21:43:502161 for f in input_api.AffectedFiles(file_filter=file_filter):
2162 for line_num, line in f.ChangedContents():
2163 if impl_pattern.match(line) or v8_wrapper_pattern.match(line):
2164 errors.append(' %s:%d' % (f.LocalPath(), line_num))
Omer Katzcc77ea92021-04-26 10:23:282165
Sam Maiera6e76d72022-02-11 21:43:502166 results = []
2167 if errors:
2168 results.append(
2169 output_api.PresubmitError(
2170 'Do not include files from third_party/blink/renderer/platform/heap/impl'
2171 ' or third_party/blink/renderer/platform/heap/v8_wrapper. Use the '
2172 'relevant counterparts from third_party/blink/renderer/platform/heap',
2173 errors))
2174 return results
Omer Katzcc77ea92021-04-26 10:23:282175
2176
[email protected]70ca77752012-11-20 03:45:032177def _CheckForVersionControlConflictsInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:502178 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
2179 errors = []
2180 for line_num, line in f.ChangedContents():
2181 if f.LocalPath().endswith(('.md', '.rst', '.txt')):
2182 # First-level headers in markdown look a lot like version control
2183 # conflict markers. http://daringfireball.net/projects/markdown/basics
2184 continue
2185 if pattern.match(line):
2186 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
2187 return errors
[email protected]70ca77752012-11-20 03:45:032188
2189
Saagar Sanghavifceeaae2020-08-12 16:40:362190def CheckForVersionControlConflicts(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502191 """Usually this is not intentional and will cause a compile failure."""
2192 errors = []
2193 for f in input_api.AffectedFiles():
2194 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
[email protected]70ca77752012-11-20 03:45:032195
Sam Maiera6e76d72022-02-11 21:43:502196 results = []
2197 if errors:
2198 results.append(
2199 output_api.PresubmitError(
2200 'Version control conflict markers found, please resolve.',
2201 errors))
2202 return results
[email protected]70ca77752012-11-20 03:45:032203
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202204
Saagar Sanghavifceeaae2020-08-12 16:40:362205def CheckGoogleSupportAnswerUrlOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502206 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
2207 errors = []
2208 for f in input_api.AffectedFiles():
2209 for line_num, line in f.ChangedContents():
2210 if pattern.search(line):
2211 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
estadee17314a02017-01-12 16:22:162212
Sam Maiera6e76d72022-02-11 21:43:502213 results = []
2214 if errors:
2215 results.append(
2216 output_api.PresubmitPromptWarning(
2217 'Found Google support URL addressed by answer number. Please replace '
2218 'with a p= identifier instead. See crbug.com/679462\n',
2219 errors))
2220 return results
estadee17314a02017-01-12 16:22:162221
[email protected]70ca77752012-11-20 03:45:032222
Saagar Sanghavifceeaae2020-08-12 16:40:362223def CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502224 def FilterFile(affected_file):
2225 """Filter function for use with input_api.AffectedSourceFiles,
2226 below. This filters out everything except non-test files from
2227 top-level directories that generally speaking should not hard-code
2228 service URLs (e.g. src/android_webview/, src/content/ and others).
2229 """
2230 return input_api.FilterSourceFile(
2231 affected_file,
Bruce Dawson40fece62022-09-16 19:58:312232 files_to_check=[r'^(android_webview|base|content|net)/.*'],
Sam Maiera6e76d72022-02-11 21:43:502233 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2234 input_api.DEFAULT_FILES_TO_SKIP))
[email protected]06e6d0ff2012-12-11 01:36:442235
Sam Maiera6e76d72022-02-11 21:43:502236 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
2237 '\.(com|net)[^"]*"')
2238 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
2239 pattern = input_api.re.compile(base_pattern)
2240 problems = [] # items are (filename, line_number, line)
2241 for f in input_api.AffectedSourceFiles(FilterFile):
2242 for line_num, line in f.ChangedContents():
2243 if not comment_pattern.search(line) and pattern.search(line):
2244 problems.append((f.LocalPath(), line_num, line))
[email protected]06e6d0ff2012-12-11 01:36:442245
Sam Maiera6e76d72022-02-11 21:43:502246 if problems:
2247 return [
2248 output_api.PresubmitPromptOrNotify(
2249 'Most layers below src/chrome/ should not hardcode service URLs.\n'
2250 'Are you sure this is correct?', [
2251 ' %s:%d: %s' % (problem[0], problem[1], problem[2])
2252 for problem in problems
2253 ])
2254 ]
2255 else:
2256 return []
[email protected]06e6d0ff2012-12-11 01:36:442257
2258
Saagar Sanghavifceeaae2020-08-12 16:40:362259def CheckChromeOsSyncedPrefRegistration(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502260 """Warns if Chrome OS C++ files register syncable prefs as browser prefs."""
James Cook6b6597c2019-11-06 22:05:292261
Sam Maiera6e76d72022-02-11 21:43:502262 def FileFilter(affected_file):
2263 """Includes directories known to be Chrome OS only."""
2264 return input_api.FilterSourceFile(
2265 affected_file,
2266 files_to_check=(
2267 '^ash/',
2268 '^chromeos/', # Top-level src/chromeos.
2269 '.*/chromeos/', # Any path component.
2270 '^components/arc',
2271 '^components/exo'),
2272 files_to_skip=(input_api.DEFAULT_FILES_TO_SKIP))
James Cook6b6597c2019-11-06 22:05:292273
Sam Maiera6e76d72022-02-11 21:43:502274 prefs = []
2275 priority_prefs = []
2276 for f in input_api.AffectedFiles(file_filter=FileFilter):
2277 for line_num, line in f.ChangedContents():
2278 if input_api.re.search('PrefRegistrySyncable::SYNCABLE_PREF',
2279 line):
2280 prefs.append(' %s:%d:' % (f.LocalPath(), line_num))
2281 prefs.append(' %s' % line)
2282 if input_api.re.search(
2283 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF', line):
2284 priority_prefs.append(' %s:%d' % (f.LocalPath(), line_num))
2285 priority_prefs.append(' %s' % line)
2286
2287 results = []
2288 if (prefs):
2289 results.append(
2290 output_api.PresubmitPromptWarning(
2291 'Preferences were registered as SYNCABLE_PREF and will be controlled '
2292 'by browser sync settings. If these prefs should be controlled by OS '
2293 'sync settings use SYNCABLE_OS_PREF instead.\n' +
2294 '\n'.join(prefs)))
2295 if (priority_prefs):
2296 results.append(
2297 output_api.PresubmitPromptWarning(
2298 'Preferences were registered as SYNCABLE_PRIORITY_PREF and will be '
2299 'controlled by browser sync settings. If these prefs should be '
2300 'controlled by OS sync settings use SYNCABLE_OS_PRIORITY_PREF '
2301 'instead.\n' + '\n'.join(prefs)))
2302 return results
James Cook6b6597c2019-11-06 22:05:292303
2304
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492305# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362306def CheckNoAbbreviationInPngFileName(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502307 """Makes sure there are no abbreviations in the name of PNG files.
2308 The native_client_sdk directory is excluded because it has auto-generated PNG
2309 files for documentation.
2310 """
2311 errors = []
2312 files_to_check = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
Bruce Dawson40fece62022-09-16 19:58:312313 files_to_skip = [r'^native_client_sdk/',
2314 r'^services/test/',
2315 r'^third_party/blink/web_tests/',
Bruce Dawson3db456212022-05-02 05:34:182316 ]
Sam Maiera6e76d72022-02-11 21:43:502317 file_filter = lambda f: input_api.FilterSourceFile(
2318 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
2319 for f in input_api.AffectedFiles(include_deletes=False,
2320 file_filter=file_filter):
2321 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:272322
Sam Maiera6e76d72022-02-11 21:43:502323 results = []
2324 if errors:
2325 results.append(
2326 output_api.PresubmitError(
2327 'The name of PNG files should not have abbreviations. \n'
2328 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
2329 'Contact [email protected] if you have questions.', errors))
2330 return results
[email protected]d2530012013-01-25 16:39:272331
Evan Stade7cd4a2c2022-08-04 23:37:252332def CheckNoProductIconsAddedToPublicRepo(input_api, output_api):
2333 """Heuristically identifies product icons based on their file name and reminds
2334 contributors not to add them to the Chromium repository.
2335 """
2336 errors = []
2337 files_to_check = [r'.*google.*\.png$|.*google.*\.svg$|.*google.*\.icon$']
2338 file_filter = lambda f: input_api.FilterSourceFile(
2339 f, files_to_check=files_to_check)
2340 for f in input_api.AffectedFiles(include_deletes=False,
2341 file_filter=file_filter):
2342 errors.append(' %s' % f.LocalPath())
2343
2344 results = []
2345 if errors:
Bruce Dawson3bcf0c92022-08-12 00:03:082346 # Give warnings instead of errors on presubmit --all and presubmit
2347 # --files.
2348 message_type = (output_api.PresubmitNotifyResult if input_api.no_diffs
2349 else output_api.PresubmitError)
Evan Stade7cd4a2c2022-08-04 23:37:252350 results.append(
Bruce Dawson3bcf0c92022-08-12 00:03:082351 message_type(
Evan Stade7cd4a2c2022-08-04 23:37:252352 'Trademarked images should not be added to the public repo. '
2353 'See crbug.com/944754', errors))
2354 return results
2355
[email protected]d2530012013-01-25 16:39:272356
Daniel Cheng4dcdb6b2017-04-13 08:30:172357def _ExtractAddRulesFromParsedDeps(parsed_deps):
Sam Maiera6e76d72022-02-11 21:43:502358 """Extract the rules that add dependencies from a parsed DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:172359
Sam Maiera6e76d72022-02-11 21:43:502360 Args:
2361 parsed_deps: the locals dictionary from evaluating the DEPS file."""
2362 add_rules = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:172363 add_rules.update([
Sam Maiera6e76d72022-02-11 21:43:502364 rule[1:] for rule in parsed_deps.get('include_rules', [])
Daniel Cheng4dcdb6b2017-04-13 08:30:172365 if rule.startswith('+') or rule.startswith('!')
2366 ])
Sam Maiera6e76d72022-02-11 21:43:502367 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
2368 add_rules.update([
2369 rule[1:] for rule in rules
2370 if rule.startswith('+') or rule.startswith('!')
2371 ])
2372 return add_rules
Daniel Cheng4dcdb6b2017-04-13 08:30:172373
2374
2375def _ParseDeps(contents):
Sam Maiera6e76d72022-02-11 21:43:502376 """Simple helper for parsing DEPS files."""
Daniel Cheng4dcdb6b2017-04-13 08:30:172377
Sam Maiera6e76d72022-02-11 21:43:502378 # Stubs for handling special syntax in the root DEPS file.
2379 class _VarImpl:
2380 def __init__(self, local_scope):
2381 self._local_scope = local_scope
Daniel Cheng4dcdb6b2017-04-13 08:30:172382
Sam Maiera6e76d72022-02-11 21:43:502383 def Lookup(self, var_name):
2384 """Implements the Var syntax."""
2385 try:
2386 return self._local_scope['vars'][var_name]
2387 except KeyError:
2388 raise Exception('Var is not defined: %s' % var_name)
Daniel Cheng4dcdb6b2017-04-13 08:30:172389
Sam Maiera6e76d72022-02-11 21:43:502390 local_scope = {}
2391 global_scope = {
2392 'Var': _VarImpl(local_scope).Lookup,
2393 'Str': str,
2394 }
Dirk Pranke1b9e06382021-05-14 01:16:222395
Sam Maiera6e76d72022-02-11 21:43:502396 exec(contents, global_scope, local_scope)
2397 return local_scope
Daniel Cheng4dcdb6b2017-04-13 08:30:172398
2399
2400def _CalculateAddedDeps(os_path, old_contents, new_contents):
Sam Maiera6e76d72022-02-11 21:43:502401 """Helper method for CheckAddedDepsHaveTargetApprovals. Returns
2402 a set of DEPS entries that we should look up.
[email protected]14a6131c2014-01-08 01:15:412403
Sam Maiera6e76d72022-02-11 21:43:502404 For a directory (rather than a specific filename) we fake a path to
2405 a specific filename by adding /DEPS. This is chosen as a file that
2406 will seldom or never be subject to per-file include_rules.
2407 """
2408 # We ignore deps entries on auto-generated directories.
2409 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:082410
Sam Maiera6e76d72022-02-11 21:43:502411 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
2412 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Daniel Cheng4dcdb6b2017-04-13 08:30:172413
Sam Maiera6e76d72022-02-11 21:43:502414 added_deps = new_deps.difference(old_deps)
Daniel Cheng4dcdb6b2017-04-13 08:30:172415
Sam Maiera6e76d72022-02-11 21:43:502416 results = set()
2417 for added_dep in added_deps:
2418 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
2419 continue
2420 # Assume that a rule that ends in .h is a rule for a specific file.
2421 if added_dep.endswith('.h'):
2422 results.add(added_dep)
2423 else:
2424 results.add(os_path.join(added_dep, 'DEPS'))
2425 return results
[email protected]f32e2d1e2013-07-26 21:39:082426
2427
Saagar Sanghavifceeaae2020-08-12 16:40:362428def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502429 """When a dependency prefixed with + is added to a DEPS file, we
2430 want to make sure that the change is reviewed by an OWNER of the
2431 target file or directory, to avoid layering violations from being
2432 introduced. This check verifies that this happens.
2433 """
2434 # We rely on Gerrit's code-owners to check approvals.
2435 # input_api.gerrit is always set for Chromium, but other projects
2436 # might not use Gerrit.
Bruce Dawson344ab262022-06-04 11:35:102437 if not input_api.gerrit or input_api.no_diffs:
Sam Maiera6e76d72022-02-11 21:43:502438 return []
Bruce Dawsonb357aeb2022-08-09 15:38:302439 if 'PRESUBMIT_SKIP_NETWORK' in input_api.environ:
Sam Maiera6e76d72022-02-11 21:43:502440 return []
Bruce Dawsonb357aeb2022-08-09 15:38:302441 try:
2442 if (input_api.change.issue and
2443 input_api.gerrit.IsOwnersOverrideApproved(
2444 input_api.change.issue)):
2445 # Skip OWNERS check when Owners-Override label is approved. This is
2446 # intended for global owners, trusted bots, and on-call sheriffs.
2447 # Review is still required for these changes.
2448 return []
2449 except Exception as e:
2450 return [output_api.PresubmitPromptWarning(
2451 'Failed to retrieve owner override status - %s' % str(e))]
Edward Lesmes6fba51082021-01-20 04:20:232452
Sam Maiera6e76d72022-02-11 21:43:502453 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:242454
Bruce Dawson40fece62022-09-16 19:58:312455 # Consistently use / as path separator to simplify the writing of regex
2456 # expressions.
Sam Maiera6e76d72022-02-11 21:43:502457 file_filter = lambda f: not input_api.re.match(
Bruce Dawson40fece62022-09-16 19:58:312458 r"^third_party/blink/.*",
2459 f.LocalPath().replace(input_api.os_path.sep, '/'))
Sam Maiera6e76d72022-02-11 21:43:502460 for f in input_api.AffectedFiles(include_deletes=False,
2461 file_filter=file_filter):
2462 filename = input_api.os_path.basename(f.LocalPath())
2463 if filename == 'DEPS':
2464 virtual_depended_on_files.update(
2465 _CalculateAddedDeps(input_api.os_path,
2466 '\n'.join(f.OldContents()),
2467 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:552468
Sam Maiera6e76d72022-02-11 21:43:502469 if not virtual_depended_on_files:
2470 return []
[email protected]e871964c2013-05-13 14:14:552471
Sam Maiera6e76d72022-02-11 21:43:502472 if input_api.is_committing:
2473 if input_api.tbr:
2474 return [
2475 output_api.PresubmitNotifyResult(
2476 '--tbr was specified, skipping OWNERS check for DEPS additions'
2477 )
2478 ]
Daniel Cheng3008dc12022-05-13 04:02:112479 # TODO(dcheng): Make this generate an error on dry runs if the reviewer
2480 # is not added, to prevent review serialization.
Sam Maiera6e76d72022-02-11 21:43:502481 if input_api.dry_run:
2482 return [
2483 output_api.PresubmitNotifyResult(
2484 'This is a dry run, skipping OWNERS check for DEPS additions'
2485 )
2486 ]
2487 if not input_api.change.issue:
2488 return [
2489 output_api.PresubmitError(
2490 "DEPS approval by OWNERS check failed: this change has "
2491 "no change number, so we can't check it for approvals.")
2492 ]
2493 output = output_api.PresubmitError
[email protected]14a6131c2014-01-08 01:15:412494 else:
Sam Maiera6e76d72022-02-11 21:43:502495 output = output_api.PresubmitNotifyResult
[email protected]e871964c2013-05-13 14:14:552496
Sam Maiera6e76d72022-02-11 21:43:502497 owner_email, reviewers = (
2498 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
2499 input_api, None, approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:552500
Sam Maiera6e76d72022-02-11 21:43:502501 owner_email = owner_email or input_api.change.author_email
2502
2503 approval_status = input_api.owners_client.GetFilesApprovalStatus(
2504 virtual_depended_on_files, reviewers.union([owner_email]), [])
2505 missing_files = [
2506 f for f in virtual_depended_on_files
2507 if approval_status[f] != input_api.owners_client.APPROVED
2508 ]
2509
2510 # We strip the /DEPS part that was added by
2511 # _FilesToCheckForIncomingDeps to fake a path to a file in a
2512 # directory.
2513 def StripDeps(path):
2514 start_deps = path.rfind('/DEPS')
2515 if start_deps != -1:
2516 return path[:start_deps]
2517 else:
2518 return path
2519
2520 unapproved_dependencies = [
2521 "'+%s'," % StripDeps(path) for path in missing_files
2522 ]
2523
2524 if unapproved_dependencies:
2525 output_list = [
2526 output(
2527 'You need LGTM from owners of depends-on paths in DEPS that were '
2528 'modified in this CL:\n %s' %
2529 '\n '.join(sorted(unapproved_dependencies)))
2530 ]
2531 suggested_owners = input_api.owners_client.SuggestOwners(
2532 missing_files, exclude=[owner_email])
2533 output_list.append(
2534 output('Suggested missing target path OWNERS:\n %s' %
2535 '\n '.join(suggested_owners or [])))
2536 return output_list
2537
2538 return []
[email protected]e871964c2013-05-13 14:14:552539
2540
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492541# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362542def CheckSpamLogging(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502543 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
2544 files_to_skip = (
2545 _EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2546 input_api.DEFAULT_FILES_TO_SKIP + (
Bruce Dawson40fece62022-09-16 19:58:312547 r"^base/logging\.h$",
2548 r"^base/logging\.cc$",
2549 r"^base/task/thread_pool/task_tracker\.cc$",
2550 r"^chrome/app/chrome_main_delegate\.cc$",
2551 r"^chrome/browser/chrome_browser_main\.cc$",
2552 r"^chrome/browser/ui/startup/startup_browser_creator\.cc$",
2553 r"^chrome/browser/browser_switcher/bho/.*",
2554 r"^chrome/browser/diagnostics/diagnostics_writer\.cc$",
2555 r"^chrome/chrome_cleaner/.*",
2556 r"^chrome/chrome_elf/dll_hash/dll_hash_main\.cc$",
2557 r"^chrome/installer/setup/.*",
2558 r"^chromecast/",
2559 r"^components/browser_watcher/dump_stability_report_main_win\.cc$",
2560 r"^components/media_control/renderer/media_playback_options\.cc$",
2561 r"^components/viz/service/display/"
Sam Maiera6e76d72022-02-11 21:43:502562 r"overlay_strategy_underlay_cast\.cc$",
Bruce Dawson40fece62022-09-16 19:58:312563 r"^components/zucchini/.*",
Sam Maiera6e76d72022-02-11 21:43:502564 # TODO(peter): Remove exception. https://crbug.com/534537
Bruce Dawson40fece62022-09-16 19:58:312565 r"^content/browser/notifications/"
Sam Maiera6e76d72022-02-11 21:43:502566 r"notification_event_dispatcher_impl\.cc$",
Bruce Dawson40fece62022-09-16 19:58:312567 r"^content/common/gpu/client/gl_helper_benchmark\.cc$",
2568 r"^courgette/courgette_minimal_tool\.cc$",
2569 r"^courgette/courgette_tool\.cc$",
2570 r"^extensions/renderer/logging_native_handler\.cc$",
2571 r"^fuchsia_web/common/init_logging\.cc$",
2572 r"^fuchsia_web/runners/common/web_component\.cc$",
2573 r"^fuchsia_web/shell/.*_shell\.cc$",
2574 r"^headless/app/headless_shell\.cc$",
2575 r"^ipc/ipc_logging\.cc$",
2576 r"^native_client_sdk/",
2577 r"^remoting/base/logging\.h$",
2578 r"^remoting/host/.*",
2579 r"^sandbox/linux/.*",
2580 r"^storage/browser/file_system/dump_file_system\.cc$",
2581 r"^tools/",
2582 r"^ui/base/resource/data_pack\.cc$",
2583 r"^ui/aura/bench/bench_main\.cc$",
2584 r"^ui/ozone/platform/cast/",
2585 r"^ui/base/x/xwmstartupcheck/"
Sam Maiera6e76d72022-02-11 21:43:502586 r"xwmstartupcheck\.cc$"))
2587 source_file_filter = lambda x: input_api.FilterSourceFile(
2588 x, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
[email protected]85218562013-11-22 07:41:402589
Sam Maiera6e76d72022-02-11 21:43:502590 log_info = set([])
2591 printf = set([])
[email protected]85218562013-11-22 07:41:402592
Sam Maiera6e76d72022-02-11 21:43:502593 for f in input_api.AffectedSourceFiles(source_file_filter):
2594 for _, line in f.ChangedContents():
2595 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
2596 log_info.add(f.LocalPath())
2597 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
2598 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:372599
Sam Maiera6e76d72022-02-11 21:43:502600 if input_api.re.search(r"\bprintf\(", line):
2601 printf.add(f.LocalPath())
2602 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
2603 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:402604
Sam Maiera6e76d72022-02-11 21:43:502605 if log_info:
2606 return [
2607 output_api.PresubmitError(
2608 'These files spam the console log with LOG(INFO):',
2609 items=log_info)
2610 ]
2611 if printf:
2612 return [
2613 output_api.PresubmitError(
2614 'These files spam the console log with printf/fprintf:',
2615 items=printf)
2616 ]
2617 return []
[email protected]85218562013-11-22 07:41:402618
2619
Saagar Sanghavifceeaae2020-08-12 16:40:362620def CheckForAnonymousVariables(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502621 """These types are all expected to hold locks while in scope and
2622 so should never be anonymous (which causes them to be immediately
2623 destroyed)."""
2624 they_who_must_be_named = [
2625 'base::AutoLock',
2626 'base::AutoReset',
2627 'base::AutoUnlock',
2628 'SkAutoAlphaRestore',
2629 'SkAutoBitmapShaderInstall',
2630 'SkAutoBlitterChoose',
2631 'SkAutoBounderCommit',
2632 'SkAutoCallProc',
2633 'SkAutoCanvasRestore',
2634 'SkAutoCommentBlock',
2635 'SkAutoDescriptor',
2636 'SkAutoDisableDirectionCheck',
2637 'SkAutoDisableOvalCheck',
2638 'SkAutoFree',
2639 'SkAutoGlyphCache',
2640 'SkAutoHDC',
2641 'SkAutoLockColors',
2642 'SkAutoLockPixels',
2643 'SkAutoMalloc',
2644 'SkAutoMaskFreeImage',
2645 'SkAutoMutexAcquire',
2646 'SkAutoPathBoundsUpdate',
2647 'SkAutoPDFRelease',
2648 'SkAutoRasterClipValidate',
2649 'SkAutoRef',
2650 'SkAutoTime',
2651 'SkAutoTrace',
2652 'SkAutoUnref',
2653 ]
2654 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
2655 # bad: base::AutoLock(lock.get());
2656 # not bad: base::AutoLock lock(lock.get());
2657 bad_pattern = input_api.re.compile(anonymous)
2658 # good: new base::AutoLock(lock.get())
2659 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
2660 errors = []
[email protected]49aa76a2013-12-04 06:59:162661
Sam Maiera6e76d72022-02-11 21:43:502662 for f in input_api.AffectedFiles():
2663 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
2664 continue
2665 for linenum, line in f.ChangedContents():
2666 if bad_pattern.search(line) and not good_pattern.search(line):
2667 errors.append('%s:%d' % (f.LocalPath(), linenum))
[email protected]49aa76a2013-12-04 06:59:162668
Sam Maiera6e76d72022-02-11 21:43:502669 if errors:
2670 return [
2671 output_api.PresubmitError(
2672 'These lines create anonymous variables that need to be named:',
2673 items=errors)
2674 ]
2675 return []
[email protected]49aa76a2013-12-04 06:59:162676
2677
Saagar Sanghavifceeaae2020-08-12 16:40:362678def CheckUniquePtrOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502679 # Returns whether |template_str| is of the form <T, U...> for some types T
2680 # and U. Assumes that |template_str| is already in the form <...>.
2681 def HasMoreThanOneArg(template_str):
2682 # Level of <...> nesting.
2683 nesting = 0
2684 for c in template_str:
2685 if c == '<':
2686 nesting += 1
2687 elif c == '>':
2688 nesting -= 1
2689 elif c == ',' and nesting == 1:
2690 return True
2691 return False
Vaclav Brozekb7fadb692018-08-30 06:39:532692
Sam Maiera6e76d72022-02-11 21:43:502693 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
2694 sources = lambda affected_file: input_api.FilterSourceFile(
2695 affected_file,
2696 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
2697 DEFAULT_FILES_TO_SKIP),
2698 files_to_check=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:552699
Sam Maiera6e76d72022-02-11 21:43:502700 # Pattern to capture a single "<...>" block of template arguments. It can
2701 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
2702 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
2703 # latter would likely require counting that < and > match, which is not
2704 # expressible in regular languages. Should the need arise, one can introduce
2705 # limited counting (matching up to a total number of nesting depth), which
2706 # should cover all practical cases for already a low nesting limit.
2707 template_arg_pattern = (
2708 r'<[^>]*' # Opening block of <.
2709 r'>([^<]*>)?') # Closing block of >.
2710 # Prefix expressing that whatever follows is not already inside a <...>
2711 # block.
2712 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
2713 null_construct_pattern = input_api.re.compile(
2714 not_inside_template_arg_pattern + r'\bstd::unique_ptr' +
2715 template_arg_pattern + r'\(\)')
Vaclav Brozeka54c528b2018-04-06 19:23:552716
Sam Maiera6e76d72022-02-11 21:43:502717 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
2718 template_arg_no_array_pattern = (
2719 r'<[^>]*[^]]' # Opening block of <.
2720 r'>([^(<]*[^]]>)?') # Closing block of >.
2721 # Prefix saying that what follows is the start of an expression.
2722 start_of_expr_pattern = r'(=|\breturn|^)\s*'
2723 # Suffix saying that what follows are call parentheses with a non-empty list
2724 # of arguments.
2725 nonempty_arg_list_pattern = r'\(([^)]|$)'
2726 # Put the template argument into a capture group for deeper examination later.
2727 return_construct_pattern = input_api.re.compile(
2728 start_of_expr_pattern + r'std::unique_ptr' + '(?P<template_arg>' +
2729 template_arg_no_array_pattern + ')' + nonempty_arg_list_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:552730
Sam Maiera6e76d72022-02-11 21:43:502731 problems_constructor = []
2732 problems_nullptr = []
2733 for f in input_api.AffectedSourceFiles(sources):
2734 for line_number, line in f.ChangedContents():
2735 # Disallow:
2736 # return std::unique_ptr<T>(foo);
2737 # bar = std::unique_ptr<T>(foo);
2738 # But allow:
2739 # return std::unique_ptr<T[]>(foo);
2740 # bar = std::unique_ptr<T[]>(foo);
2741 # And also allow cases when the second template argument is present. Those
2742 # cases cannot be handled by std::make_unique:
2743 # return std::unique_ptr<T, U>(foo);
2744 # bar = std::unique_ptr<T, U>(foo);
2745 local_path = f.LocalPath()
2746 return_construct_result = return_construct_pattern.search(line)
2747 if return_construct_result and not HasMoreThanOneArg(
2748 return_construct_result.group('template_arg')):
2749 problems_constructor.append(
2750 '%s:%d\n %s' % (local_path, line_number, line.strip()))
2751 # Disallow:
2752 # std::unique_ptr<T>()
2753 if null_construct_pattern.search(line):
2754 problems_nullptr.append(
2755 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Vaclav Brozek851d9602018-04-04 16:13:052756
Sam Maiera6e76d72022-02-11 21:43:502757 errors = []
2758 if problems_nullptr:
2759 errors.append(
2760 output_api.PresubmitPromptWarning(
2761 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
2762 problems_nullptr))
2763 if problems_constructor:
2764 errors.append(
2765 output_api.PresubmitError(
2766 'The following files use explicit std::unique_ptr constructor. '
2767 'Use std::make_unique<T>() instead, or use base::WrapUnique if '
2768 'std::make_unique is not an option.', problems_constructor))
2769 return errors
Peter Kasting4844e46e2018-02-23 07:27:102770
2771
Saagar Sanghavifceeaae2020-08-12 16:40:362772def CheckUserActionUpdate(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502773 """Checks if any new user action has been added."""
2774 if any('actions.xml' == input_api.os_path.basename(f)
2775 for f in input_api.LocalPaths()):
2776 # If actions.xml is already included in the changelist, the PRESUBMIT
2777 # for actions.xml will do a more complete presubmit check.
2778 return []
2779
2780 file_inclusion_pattern = [r'.*\.(cc|mm)$']
2781 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2782 input_api.DEFAULT_FILES_TO_SKIP)
2783 file_filter = lambda f: input_api.FilterSourceFile(
2784 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
2785
2786 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
2787 current_actions = None
2788 for f in input_api.AffectedFiles(file_filter=file_filter):
2789 for line_num, line in f.ChangedContents():
2790 match = input_api.re.search(action_re, line)
2791 if match:
2792 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
2793 # loaded only once.
2794 if not current_actions:
2795 with open(
2796 'tools/metrics/actions/actions.xml') as actions_f:
2797 current_actions = actions_f.read()
2798 # Search for the matched user action name in |current_actions|.
2799 for action_name in match.groups():
2800 action = 'name="{0}"'.format(action_name)
2801 if action not in current_actions:
2802 return [
2803 output_api.PresubmitPromptWarning(
2804 'File %s line %d: %s is missing in '
2805 'tools/metrics/actions/actions.xml. Please run '
2806 'tools/metrics/actions/extract_actions.py to update.'
2807 % (f.LocalPath(), line_num, action_name))
2808 ]
[email protected]999261d2014-03-03 20:08:082809 return []
2810
[email protected]999261d2014-03-03 20:08:082811
Daniel Cheng13ca61a882017-08-25 15:11:252812def _ImportJSONCommentEater(input_api):
Sam Maiera6e76d72022-02-11 21:43:502813 import sys
2814 sys.path = sys.path + [
2815 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
2816 'json_comment_eater')
2817 ]
2818 import json_comment_eater
2819 return json_comment_eater
Daniel Cheng13ca61a882017-08-25 15:11:252820
2821
[email protected]99171a92014-06-03 08:44:472822def _GetJSONParseError(input_api, filename, eat_comments=True):
dchenge07de812016-06-20 19:27:172823 try:
Sam Maiera6e76d72022-02-11 21:43:502824 contents = input_api.ReadFile(filename)
2825 if eat_comments:
2826 json_comment_eater = _ImportJSONCommentEater(input_api)
2827 contents = json_comment_eater.Nom(contents)
dchenge07de812016-06-20 19:27:172828
Sam Maiera6e76d72022-02-11 21:43:502829 input_api.json.loads(contents)
2830 except ValueError as e:
2831 return e
Andrew Grieve4deedb12022-02-03 21:34:502832 return None
2833
2834
Sam Maiera6e76d72022-02-11 21:43:502835def _GetIDLParseError(input_api, filename):
2836 try:
2837 contents = input_api.ReadFile(filename)
Devlin Croninf7582a12022-04-21 21:14:282838 for i, char in enumerate(contents):
Daniel Chenga37c03db2022-05-12 17:20:342839 if not char.isascii():
2840 return (
2841 'Non-ascii character "%s" (ord %d) found at offset %d.' %
2842 (char, ord(char), i))
Sam Maiera6e76d72022-02-11 21:43:502843 idl_schema = input_api.os_path.join(input_api.PresubmitLocalPath(),
2844 'tools', 'json_schema_compiler',
2845 'idl_schema.py')
2846 process = input_api.subprocess.Popen(
Bruce Dawson679fb082022-04-14 00:47:282847 [input_api.python3_executable, idl_schema],
Sam Maiera6e76d72022-02-11 21:43:502848 stdin=input_api.subprocess.PIPE,
2849 stdout=input_api.subprocess.PIPE,
2850 stderr=input_api.subprocess.PIPE,
2851 universal_newlines=True)
2852 (_, error) = process.communicate(input=contents)
2853 return error or None
2854 except ValueError as e:
2855 return e
agrievef32bcc72016-04-04 14:57:402856
agrievef32bcc72016-04-04 14:57:402857
Sam Maiera6e76d72022-02-11 21:43:502858def CheckParseErrors(input_api, output_api):
2859 """Check that IDL and JSON files do not contain syntax errors."""
2860 actions = {
2861 '.idl': _GetIDLParseError,
2862 '.json': _GetJSONParseError,
2863 }
2864 # Most JSON files are preprocessed and support comments, but these do not.
2865 json_no_comments_patterns = [
Bruce Dawson40fece62022-09-16 19:58:312866 r'^testing/',
Sam Maiera6e76d72022-02-11 21:43:502867 ]
2868 # Only run IDL checker on files in these directories.
2869 idl_included_patterns = [
Bruce Dawson40fece62022-09-16 19:58:312870 r'^chrome/common/extensions/api/',
2871 r'^extensions/common/api/',
Sam Maiera6e76d72022-02-11 21:43:502872 ]
agrievef32bcc72016-04-04 14:57:402873
Sam Maiera6e76d72022-02-11 21:43:502874 def get_action(affected_file):
2875 filename = affected_file.LocalPath()
2876 return actions.get(input_api.os_path.splitext(filename)[1])
agrievef32bcc72016-04-04 14:57:402877
Sam Maiera6e76d72022-02-11 21:43:502878 def FilterFile(affected_file):
2879 action = get_action(affected_file)
2880 if not action:
2881 return False
2882 path = affected_file.LocalPath()
agrievef32bcc72016-04-04 14:57:402883
Sam Maiera6e76d72022-02-11 21:43:502884 if _MatchesFile(input_api,
2885 _KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS, path):
2886 return False
2887
2888 if (action == _GetIDLParseError
2889 and not _MatchesFile(input_api, idl_included_patterns, path)):
2890 return False
2891 return True
2892
2893 results = []
2894 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
2895 include_deletes=False):
2896 action = get_action(affected_file)
2897 kwargs = {}
2898 if (action == _GetJSONParseError
2899 and _MatchesFile(input_api, json_no_comments_patterns,
2900 affected_file.LocalPath())):
2901 kwargs['eat_comments'] = False
2902 parse_error = action(input_api, affected_file.AbsoluteLocalPath(),
2903 **kwargs)
2904 if parse_error:
2905 results.append(
2906 output_api.PresubmitError(
2907 '%s could not be parsed: %s' %
2908 (affected_file.LocalPath(), parse_error)))
2909 return results
2910
2911
2912def CheckJavaStyle(input_api, output_api):
2913 """Runs checkstyle on changed java files and returns errors if any exist."""
2914
2915 # Return early if no java files were modified.
2916 if not any(
2917 _IsJavaFile(input_api, f.LocalPath())
2918 for f in input_api.AffectedFiles()):
2919 return []
2920
2921 import sys
2922 original_sys_path = sys.path
2923 try:
2924 sys.path = sys.path + [
2925 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
2926 'android', 'checkstyle')
2927 ]
2928 import checkstyle
2929 finally:
2930 # Restore sys.path to what it was before.
2931 sys.path = original_sys_path
2932
2933 return checkstyle.RunCheckstyle(
2934 input_api,
2935 output_api,
2936 'tools/android/checkstyle/chromium-style-5.0.xml',
2937 files_to_skip=_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP)
2938
2939
2940def CheckPythonDevilInit(input_api, output_api):
2941 """Checks to make sure devil is initialized correctly in python scripts."""
2942 script_common_initialize_pattern = input_api.re.compile(
2943 r'script_common\.InitializeEnvironment\(')
2944 devil_env_config_initialize = input_api.re.compile(
2945 r'devil_env\.config\.Initialize\(')
2946
2947 errors = []
2948
2949 sources = lambda affected_file: input_api.FilterSourceFile(
2950 affected_file,
2951 files_to_skip=(_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP + (
Bruce Dawson40fece62022-09-16 19:58:312952 r'^build/android/devil_chromium\.py',
2953 r'^third_party/.*',
Sam Maiera6e76d72022-02-11 21:43:502954 )),
2955 files_to_check=[r'.*\.py$'])
2956
2957 for f in input_api.AffectedSourceFiles(sources):
2958 for line_num, line in f.ChangedContents():
2959 if (script_common_initialize_pattern.search(line)
2960 or devil_env_config_initialize.search(line)):
2961 errors.append("%s:%d" % (f.LocalPath(), line_num))
2962
2963 results = []
2964
2965 if errors:
2966 results.append(
2967 output_api.PresubmitError(
2968 'Devil initialization should always be done using '
2969 'devil_chromium.Initialize() in the chromium project, to use better '
2970 'defaults for dependencies (ex. up-to-date version of adb).',
2971 errors))
2972
2973 return results
2974
2975
2976def _MatchesFile(input_api, patterns, path):
Bruce Dawson40fece62022-09-16 19:58:312977 # Consistently use / as path separator to simplify the writing of regex
2978 # expressions.
2979 path = path.replace(input_api.os_path.sep, '/')
Sam Maiera6e76d72022-02-11 21:43:502980 for pattern in patterns:
2981 if input_api.re.search(pattern, path):
2982 return True
2983 return False
2984
2985
Daniel Chenga37c03db2022-05-12 17:20:342986def _ChangeHasSecurityReviewer(input_api, owners_file):
2987 """Returns True iff the CL has a reviewer from SECURITY_OWNERS.
Sam Maiera6e76d72022-02-11 21:43:502988
Daniel Chenga37c03db2022-05-12 17:20:342989 Args:
2990 input_api: The presubmit input API.
2991 owners_file: OWNERS file with required reviewers. Typically, this is
2992 something like ipc/SECURITY_OWNERS.
2993
2994 Note: if the presubmit is running for commit rather than for upload, this
2995 only returns True if a security reviewer has also approved the CL.
Sam Maiera6e76d72022-02-11 21:43:502996 """
Daniel Chengd88244472022-05-16 09:08:472997 # Owners-Override should bypass all additional OWNERS enforcement checks.
2998 # A CR+1 vote will still be required to land this change.
2999 if (input_api.change.issue and input_api.gerrit.IsOwnersOverrideApproved(
3000 input_api.change.issue)):
3001 return True
3002
Daniel Chenga37c03db2022-05-12 17:20:343003 owner_email, reviewers = (
3004 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
Daniel Cheng3008dc12022-05-13 04:02:113005 input_api,
3006 None,
3007 approval_needed=input_api.is_committing and not input_api.dry_run))
Sam Maiera6e76d72022-02-11 21:43:503008
Daniel Chenga37c03db2022-05-12 17:20:343009 security_owners = input_api.owners_client.ListOwners(owners_file)
3010 return any(owner in reviewers for owner in security_owners)
Sam Maiera6e76d72022-02-11 21:43:503011
Daniel Chenga37c03db2022-05-12 17:20:343012
3013@dataclass
Daniel Cheng171dad8d2022-05-21 00:40:253014class _SecurityProblemWithItems:
3015 problem: str
3016 items: Sequence[str]
3017
3018
3019@dataclass
Daniel Chenga37c03db2022-05-12 17:20:343020class _MissingSecurityOwnersResult:
Daniel Cheng171dad8d2022-05-21 00:40:253021 owners_file_problems: Sequence[_SecurityProblemWithItems]
Daniel Chenga37c03db2022-05-12 17:20:343022 has_security_sensitive_files: bool
Daniel Cheng171dad8d2022-05-21 00:40:253023 missing_reviewer_problem: Optional[_SecurityProblemWithItems]
Daniel Chenga37c03db2022-05-12 17:20:343024
3025
3026def _FindMissingSecurityOwners(input_api,
3027 output_api,
3028 file_patterns: Sequence[str],
3029 excluded_patterns: Sequence[str],
3030 required_owners_file: str,
3031 custom_rule_function: Optional[Callable] = None
3032 ) -> _MissingSecurityOwnersResult:
3033 """Find OWNERS files missing per-file rules for security-sensitive files.
3034
3035 Args:
3036 input_api: the PRESUBMIT input API object.
3037 output_api: the PRESUBMIT output API object.
3038 file_patterns: basename patterns that require a corresponding per-file
3039 security restriction.
3040 excluded_patterns: path patterns that should be exempted from
3041 requiring a security restriction.
3042 required_owners_file: path to the required OWNERS file, e.g.
3043 ipc/SECURITY_OWNERS
3044 cc_alias: If not None, email that will be CCed automatically if the
3045 change contains security-sensitive files, as determined by
3046 `file_patterns` and `excluded_patterns`.
3047 custom_rule_function: If not None, will be called with `input_api` and
3048 the current file under consideration. Returning True will add an
3049 exact match per-file rule check for the current file.
3050 """
3051
3052 # `to_check` is a mapping of an OWNERS file path to Patterns.
3053 #
3054 # Patterns is a dictionary mapping glob patterns (suitable for use in
3055 # per-file rules) to a PatternEntry.
3056 #
Sam Maiera6e76d72022-02-11 21:43:503057 # PatternEntry is a dictionary with two keys:
3058 # - 'files': the files that are matched by this pattern
3059 # - 'rules': the per-file rules needed for this pattern
Daniel Chenga37c03db2022-05-12 17:20:343060 #
Sam Maiera6e76d72022-02-11 21:43:503061 # For example, if we expect OWNERS file to contain rules for *.mojom and
3062 # *_struct_traits*.*, Patterns might look like this:
3063 # {
3064 # '*.mojom': {
3065 # 'files': ...,
3066 # 'rules': [
3067 # 'per-file *.mojom=set noparent',
3068 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
3069 # ],
3070 # },
3071 # '*_struct_traits*.*': {
3072 # 'files': ...,
3073 # 'rules': [
3074 # 'per-file *_struct_traits*.*=set noparent',
3075 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
3076 # ],
3077 # },
3078 # }
3079 to_check = {}
Daniel Chenga37c03db2022-05-12 17:20:343080 files_to_review = []
Sam Maiera6e76d72022-02-11 21:43:503081
Daniel Chenga37c03db2022-05-12 17:20:343082 def AddPatternToCheck(file, pattern):
Sam Maiera6e76d72022-02-11 21:43:503083 owners_file = input_api.os_path.join(
Daniel Chengd88244472022-05-16 09:08:473084 input_api.os_path.dirname(file.LocalPath()), 'OWNERS')
Sam Maiera6e76d72022-02-11 21:43:503085 if owners_file not in to_check:
3086 to_check[owners_file] = {}
3087 if pattern not in to_check[owners_file]:
3088 to_check[owners_file][pattern] = {
3089 'files': [],
3090 'rules': [
Daniel Chenga37c03db2022-05-12 17:20:343091 f'per-file {pattern}=set noparent',
3092 f'per-file {pattern}=file://{required_owners_file}',
Sam Maiera6e76d72022-02-11 21:43:503093 ]
3094 }
Daniel Chenged57a162022-05-25 02:56:343095 to_check[owners_file][pattern]['files'].append(file.LocalPath())
Daniel Chenga37c03db2022-05-12 17:20:343096 files_to_review.append(file.LocalPath())
Sam Maiera6e76d72022-02-11 21:43:503097
Daniel Chenga37c03db2022-05-12 17:20:343098 # Only enforce security OWNERS rules for a directory if that directory has a
3099 # file that matches `file_patterns`. For example, if a directory only
3100 # contains *.mojom files and no *_messages*.h files, the check should only
3101 # ensure that rules for *.mojom files are present.
3102 for file in input_api.AffectedFiles(include_deletes=False):
3103 file_basename = input_api.os_path.basename(file.LocalPath())
3104 if custom_rule_function is not None and custom_rule_function(
3105 input_api, file):
3106 AddPatternToCheck(file, file_basename)
3107 continue
Sam Maiera6e76d72022-02-11 21:43:503108
Daniel Chenga37c03db2022-05-12 17:20:343109 if any(
3110 input_api.fnmatch.fnmatch(file.LocalPath(), pattern)
3111 for pattern in excluded_patterns):
Sam Maiera6e76d72022-02-11 21:43:503112 continue
3113
3114 for pattern in file_patterns:
Daniel Chenga37c03db2022-05-12 17:20:343115 # Unlike `excluded_patterns`, `file_patterns` is checked only against the
3116 # file's basename.
3117 if input_api.fnmatch.fnmatch(file_basename, pattern):
3118 AddPatternToCheck(file, pattern)
Sam Maiera6e76d72022-02-11 21:43:503119 break
3120
Daniel Chenga37c03db2022-05-12 17:20:343121 has_security_sensitive_files = bool(to_check)
Daniel Cheng171dad8d2022-05-21 00:40:253122
3123 # Check if any newly added lines in OWNERS files intersect with required
3124 # per-file OWNERS lines. If so, ensure that a security reviewer is included.
3125 # This is a hack, but is needed because the OWNERS check (by design) ignores
3126 # new OWNERS entries; otherwise, a non-owner could add someone as a new
3127 # OWNER and have that newly-added OWNER self-approve their own addition.
3128 newly_covered_files = []
3129 for file in input_api.AffectedFiles(include_deletes=False):
3130 if not file.LocalPath() in to_check:
3131 continue
3132 for _, line in file.ChangedContents():
3133 for _, entry in to_check[file.LocalPath()].items():
3134 if line in entry['rules']:
3135 newly_covered_files.extend(entry['files'])
3136
3137 missing_reviewer_problems = None
3138 if newly_covered_files and not _ChangeHasSecurityReviewer(
Daniel Chenga37c03db2022-05-12 17:20:343139 input_api, required_owners_file):
Daniel Cheng171dad8d2022-05-21 00:40:253140 missing_reviewer_problems = _SecurityProblemWithItems(
3141 f'Review from an owner in {required_owners_file} is required for '
3142 'the following newly-added files:',
3143 [f'{file}' for file in sorted(set(newly_covered_files))])
Sam Maiera6e76d72022-02-11 21:43:503144
3145 # Go through the OWNERS files to check, filtering out rules that are already
3146 # present in that OWNERS file.
3147 for owners_file, patterns in to_check.items():
3148 try:
Daniel Cheng171dad8d2022-05-21 00:40:253149 lines = set(
3150 input_api.ReadFile(
3151 input_api.os_path.join(input_api.change.RepositoryRoot(),
3152 owners_file)).splitlines())
3153 for entry in patterns.values():
3154 entry['rules'] = [
3155 rule for rule in entry['rules'] if rule not in lines
3156 ]
Sam Maiera6e76d72022-02-11 21:43:503157 except IOError:
3158 # No OWNERS file, so all the rules are definitely missing.
3159 continue
3160
3161 # All the remaining lines weren't found in OWNERS files, so emit an error.
Daniel Cheng171dad8d2022-05-21 00:40:253162 owners_file_problems = []
Daniel Chenga37c03db2022-05-12 17:20:343163
Sam Maiera6e76d72022-02-11 21:43:503164 for owners_file, patterns in to_check.items():
3165 missing_lines = []
3166 files = []
3167 for _, entry in patterns.items():
Daniel Chenged57a162022-05-25 02:56:343168 files.extend(entry['files'])
Sam Maiera6e76d72022-02-11 21:43:503169 missing_lines.extend(entry['rules'])
Sam Maiera6e76d72022-02-11 21:43:503170 if missing_lines:
Daniel Cheng171dad8d2022-05-21 00:40:253171 joined_missing_lines = '\n'.join(line for line in missing_lines)
3172 owners_file_problems.append(
3173 _SecurityProblemWithItems(
3174 'Found missing OWNERS lines for security-sensitive files. '
3175 f'Please add the following lines to {owners_file}:\n'
3176 f'{joined_missing_lines}\n\nTo ensure security review for:',
3177 files))
Daniel Chenga37c03db2022-05-12 17:20:343178
Daniel Cheng171dad8d2022-05-21 00:40:253179 return _MissingSecurityOwnersResult(owners_file_problems,
Daniel Chenga37c03db2022-05-12 17:20:343180 has_security_sensitive_files,
Daniel Cheng171dad8d2022-05-21 00:40:253181 missing_reviewer_problems)
Daniel Chenga37c03db2022-05-12 17:20:343182
3183
3184def _CheckChangeForIpcSecurityOwners(input_api, output_api):
3185 # Whether or not a file affects IPC is (mostly) determined by a simple list
3186 # of filename patterns.
3187 file_patterns = [
3188 # Legacy IPC:
3189 '*_messages.cc',
3190 '*_messages*.h',
3191 '*_param_traits*.*',
3192 # Mojo IPC:
3193 '*.mojom',
3194 '*_mojom_traits*.*',
3195 '*_type_converter*.*',
3196 # Android native IPC:
3197 '*.aidl',
3198 ]
3199
Daniel Chenga37c03db2022-05-12 17:20:343200 excluded_patterns = [
Daniel Cheng518943f2022-05-12 22:15:463201 # These third_party directories do not contain IPCs, but contain files
3202 # matching the above patterns, which trigger false positives.
Daniel Chenga37c03db2022-05-12 17:20:343203 'third_party/crashpad/*',
3204 'third_party/blink/renderer/platform/bindings/*',
3205 'third_party/protobuf/benchmarks/python/*',
3206 'third_party/win_build_output/*',
Daniel Chengd88244472022-05-16 09:08:473207 # Enum-only mojoms used for web metrics, so no security review needed.
3208 'third_party/blink/public/mojom/use_counter/metrics/*',
Daniel Chenga37c03db2022-05-12 17:20:343209 # These files are just used to communicate between class loaders running
3210 # in the same process.
3211 'weblayer/browser/java/org/chromium/weblayer_private/interfaces/*',
3212 'weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/*',
3213 ]
3214
3215 def IsMojoServiceManifestFile(input_api, file):
3216 manifest_pattern = input_api.re.compile('manifests?\.(cc|h)$')
3217 test_manifest_pattern = input_api.re.compile('test_manifests?\.(cc|h)')
3218 if not manifest_pattern.search(file.LocalPath()):
3219 return False
3220
3221 if test_manifest_pattern.search(file.LocalPath()):
3222 return False
3223
3224 # All actual service manifest files should contain at least one
3225 # qualified reference to service_manager::Manifest.
3226 return any('service_manager::Manifest' in line
3227 for line in file.NewContents())
3228
3229 return _FindMissingSecurityOwners(
3230 input_api,
3231 output_api,
3232 file_patterns,
3233 excluded_patterns,
3234 'ipc/SECURITY_OWNERS',
3235 custom_rule_function=IsMojoServiceManifestFile)
3236
3237
3238def _CheckChangeForFuchsiaSecurityOwners(input_api, output_api):
3239 file_patterns = [
3240 # Component specifications.
3241 '*.cml', # Component Framework v2.
3242 '*.cmx', # Component Framework v1.
3243
3244 # Fuchsia IDL protocol specifications.
3245 '*.fidl',
3246 ]
3247
3248 # Don't check for owners files for changes in these directories.
3249 excluded_patterns = [
3250 'third_party/crashpad/*',
3251 ]
3252
3253 return _FindMissingSecurityOwners(input_api, output_api, file_patterns,
3254 excluded_patterns,
3255 'build/fuchsia/SECURITY_OWNERS')
3256
3257
3258def CheckSecurityOwners(input_api, output_api):
3259 """Checks that various security-sensitive files have an IPC OWNERS rule."""
3260 ipc_results = _CheckChangeForIpcSecurityOwners(input_api, output_api)
3261 fuchsia_results = _CheckChangeForFuchsiaSecurityOwners(
3262 input_api, output_api)
3263
3264 if ipc_results.has_security_sensitive_files:
3265 output_api.AppendCC('[email protected]')
Sam Maiera6e76d72022-02-11 21:43:503266
3267 results = []
Daniel Chenga37c03db2022-05-12 17:20:343268
Daniel Cheng171dad8d2022-05-21 00:40:253269 missing_reviewer_problems = []
3270 if ipc_results.missing_reviewer_problem:
3271 missing_reviewer_problems.append(ipc_results.missing_reviewer_problem)
3272 if fuchsia_results.missing_reviewer_problem:
3273 missing_reviewer_problems.append(
3274 fuchsia_results.missing_reviewer_problem)
Daniel Chenga37c03db2022-05-12 17:20:343275
Daniel Cheng171dad8d2022-05-21 00:40:253276 # Missing reviewers are an error unless there's no issue number
3277 # associated with this branch; in that case, the presubmit is being run
3278 # with --all or --files.
3279 #
3280 # Note that upload should never be an error; otherwise, it would be
3281 # impossible to upload changes at all.
3282 if input_api.is_committing and input_api.change.issue:
3283 make_presubmit_message = output_api.PresubmitError
3284 else:
3285 make_presubmit_message = output_api.PresubmitNotifyResult
3286 for problem in missing_reviewer_problems:
Sam Maiera6e76d72022-02-11 21:43:503287 results.append(
Daniel Cheng171dad8d2022-05-21 00:40:253288 make_presubmit_message(problem.problem, items=problem.items))
Daniel Chenga37c03db2022-05-12 17:20:343289
Daniel Cheng171dad8d2022-05-21 00:40:253290 owners_file_problems = []
3291 owners_file_problems.extend(ipc_results.owners_file_problems)
3292 owners_file_problems.extend(fuchsia_results.owners_file_problems)
Daniel Chenga37c03db2022-05-12 17:20:343293
Daniel Cheng171dad8d2022-05-21 00:40:253294 for problem in owners_file_problems:
Daniel Cheng3008dc12022-05-13 04:02:113295 # Missing per-file rules are always an error. While swarming and caching
3296 # means that uploading a patchset with updated OWNERS files and sending
3297 # it to the CQ again should not have a large incremental cost, it is
3298 # still frustrating to discover the error only after the change has
3299 # already been uploaded.
Daniel Chenga37c03db2022-05-12 17:20:343300 results.append(
Daniel Cheng171dad8d2022-05-21 00:40:253301 output_api.PresubmitError(problem.problem, items=problem.items))
Sam Maiera6e76d72022-02-11 21:43:503302
3303 return results
3304
3305
3306def _GetFilesUsingSecurityCriticalFunctions(input_api):
3307 """Checks affected files for changes to security-critical calls. This
3308 function checks the full change diff, to catch both additions/changes
3309 and removals.
3310
3311 Returns a dict keyed by file name, and the value is a set of detected
3312 functions.
3313 """
3314 # Map of function pretty name (displayed in an error) to the pattern to
3315 # match it with.
3316 _PATTERNS_TO_CHECK = {
3317 'content::GetServiceSandboxType<>()': 'GetServiceSandboxType\\<'
3318 }
3319 _PATTERNS_TO_CHECK = {
3320 k: input_api.re.compile(v)
3321 for k, v in _PATTERNS_TO_CHECK.items()
3322 }
3323
Sam Maiera6e76d72022-02-11 21:43:503324 # We don't want to trigger on strings within this file.
3325 def presubmit_file_filter(f):
Daniel Chenga37c03db2022-05-12 17:20:343326 return 'PRESUBMIT.py' != input_api.os_path.split(f.LocalPath())[1]
Sam Maiera6e76d72022-02-11 21:43:503327
3328 # Scan all affected files for changes touching _FUNCTIONS_TO_CHECK.
3329 files_to_functions = {}
3330 for f in input_api.AffectedFiles(file_filter=presubmit_file_filter):
3331 diff = f.GenerateScmDiff()
3332 for line in diff.split('\n'):
3333 # Not using just RightHandSideLines() because removing a
3334 # call to a security-critical function can be just as important
3335 # as adding or changing the arguments.
3336 if line.startswith('-') or (line.startswith('+')
3337 and not line.startswith('++')):
3338 for name, pattern in _PATTERNS_TO_CHECK.items():
3339 if pattern.search(line):
3340 path = f.LocalPath()
3341 if not path in files_to_functions:
3342 files_to_functions[path] = set()
3343 files_to_functions[path].add(name)
3344 return files_to_functions
3345
3346
3347def CheckSecurityChanges(input_api, output_api):
3348 """Checks that changes involving security-critical functions are reviewed
3349 by the security team.
3350 """
3351 files_to_functions = _GetFilesUsingSecurityCriticalFunctions(input_api)
3352 if not len(files_to_functions):
3353 return []
3354
Sam Maiera6e76d72022-02-11 21:43:503355 owners_file = 'ipc/SECURITY_OWNERS'
Daniel Chenga37c03db2022-05-12 17:20:343356 if _ChangeHasSecurityReviewer(input_api, owners_file):
Sam Maiera6e76d72022-02-11 21:43:503357 return []
3358
Daniel Chenga37c03db2022-05-12 17:20:343359 msg = 'The following files change calls to security-sensitive functions\n' \
Sam Maiera6e76d72022-02-11 21:43:503360 'that need to be reviewed by {}.\n'.format(owners_file)
3361 for path, names in files_to_functions.items():
3362 msg += ' {}\n'.format(path)
3363 for name in names:
3364 msg += ' {}\n'.format(name)
3365 msg += '\n'
3366
3367 if input_api.is_committing:
3368 output = output_api.PresubmitError
Mohamed Heikale217fc852020-07-06 19:44:033369 else:
Sam Maiera6e76d72022-02-11 21:43:503370 output = output_api.PresubmitNotifyResult
3371 return [output(msg)]
3372
3373
3374def CheckSetNoParent(input_api, output_api):
3375 """Checks that set noparent is only used together with an OWNERS file in
3376 //build/OWNERS.setnoparent (see also
3377 //docs/code_reviews.md#owners-files-details)
3378 """
3379 # Return early if no OWNERS files were modified.
3380 if not any(f.LocalPath().endswith('OWNERS')
3381 for f in input_api.AffectedFiles(include_deletes=False)):
3382 return []
3383
3384 errors = []
3385
3386 allowed_owners_files_file = 'build/OWNERS.setnoparent'
3387 allowed_owners_files = set()
3388 with open(allowed_owners_files_file, 'r') as f:
3389 for line in f:
3390 line = line.strip()
3391 if not line or line.startswith('#'):
3392 continue
3393 allowed_owners_files.add(line)
3394
3395 per_file_pattern = input_api.re.compile('per-file (.+)=(.+)')
3396
3397 for f in input_api.AffectedFiles(include_deletes=False):
3398 if not f.LocalPath().endswith('OWNERS'):
3399 continue
3400
3401 found_owners_files = set()
3402 found_set_noparent_lines = dict()
3403
3404 # Parse the OWNERS file.
3405 for lineno, line in enumerate(f.NewContents(), 1):
3406 line = line.strip()
3407 if line.startswith('set noparent'):
3408 found_set_noparent_lines[''] = lineno
3409 if line.startswith('file://'):
3410 if line in allowed_owners_files:
3411 found_owners_files.add('')
3412 if line.startswith('per-file'):
3413 match = per_file_pattern.match(line)
3414 if match:
3415 glob = match.group(1).strip()
3416 directive = match.group(2).strip()
3417 if directive == 'set noparent':
3418 found_set_noparent_lines[glob] = lineno
3419 if directive.startswith('file://'):
3420 if directive in allowed_owners_files:
3421 found_owners_files.add(glob)
3422
3423 # Check that every set noparent line has a corresponding file:// line
3424 # listed in build/OWNERS.setnoparent. An exception is made for top level
3425 # directories since src/OWNERS shouldn't review them.
Bruce Dawson6bb0d672022-04-06 15:13:493426 linux_path = f.LocalPath().replace(input_api.os_path.sep, '/')
3427 if (linux_path.count('/') != 1
3428 and (not linux_path in _EXCLUDED_SET_NO_PARENT_PATHS)):
Sam Maiera6e76d72022-02-11 21:43:503429 for set_noparent_line in found_set_noparent_lines:
3430 if set_noparent_line in found_owners_files:
3431 continue
3432 errors.append(' %s:%d' %
Bruce Dawson6bb0d672022-04-06 15:13:493433 (linux_path,
Sam Maiera6e76d72022-02-11 21:43:503434 found_set_noparent_lines[set_noparent_line]))
3435
3436 results = []
3437 if errors:
3438 if input_api.is_committing:
3439 output = output_api.PresubmitError
3440 else:
3441 output = output_api.PresubmitPromptWarning
3442 results.append(
3443 output(
3444 'Found the following "set noparent" restrictions in OWNERS files that '
3445 'do not include owners from build/OWNERS.setnoparent:',
3446 long_text='\n\n'.join(errors)))
3447 return results
3448
3449
3450def CheckUselessForwardDeclarations(input_api, output_api):
3451 """Checks that added or removed lines in non third party affected
3452 header files do not lead to new useless class or struct forward
3453 declaration.
3454 """
3455 results = []
3456 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
3457 input_api.re.MULTILINE)
3458 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
3459 input_api.re.MULTILINE)
3460 for f in input_api.AffectedFiles(include_deletes=False):
3461 if (f.LocalPath().startswith('third_party')
3462 and not f.LocalPath().startswith('third_party/blink')
3463 and not f.LocalPath().startswith('third_party\\blink')):
3464 continue
3465
3466 if not f.LocalPath().endswith('.h'):
3467 continue
3468
3469 contents = input_api.ReadFile(f)
3470 fwd_decls = input_api.re.findall(class_pattern, contents)
3471 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
3472
3473 useless_fwd_decls = []
3474 for decl in fwd_decls:
3475 count = sum(1 for _ in input_api.re.finditer(
3476 r'\b%s\b' % input_api.re.escape(decl), contents))
3477 if count == 1:
3478 useless_fwd_decls.append(decl)
3479
3480 if not useless_fwd_decls:
3481 continue
3482
3483 for line in f.GenerateScmDiff().splitlines():
3484 if (line.startswith('-') and not line.startswith('--')
3485 or line.startswith('+') and not line.startswith('++')):
3486 for decl in useless_fwd_decls:
3487 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
3488 results.append(
3489 output_api.PresubmitPromptWarning(
3490 '%s: %s forward declaration is no longer needed'
3491 % (f.LocalPath(), decl)))
3492 useless_fwd_decls.remove(decl)
3493
3494 return results
3495
3496
3497def _CheckAndroidDebuggableBuild(input_api, output_api):
3498 """Checks that code uses BuildInfo.isDebugAndroid() instead of
3499 Build.TYPE.equals('') or ''.equals(Build.TYPE) to check if
3500 this is a debuggable build of Android.
3501 """
3502 build_type_check_pattern = input_api.re.compile(
3503 r'\bBuild\.TYPE\.equals\(|\.equals\(\s*\bBuild\.TYPE\)')
3504
3505 errors = []
3506
3507 sources = lambda affected_file: input_api.FilterSourceFile(
3508 affected_file,
3509 files_to_skip=(
3510 _EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
3511 DEFAULT_FILES_TO_SKIP + (
Bruce Dawson40fece62022-09-16 19:58:313512 r"^android_webview/support_library/boundary_interfaces/",
3513 r"^chrome/android/webapk/.*",
3514 r'^third_party/.*',
3515 r"tools/android/customtabs_benchmark/.*",
3516 r"webview/chromium/License.*",
Sam Maiera6e76d72022-02-11 21:43:503517 )),
3518 files_to_check=[r'.*\.java$'])
3519
3520 for f in input_api.AffectedSourceFiles(sources):
3521 for line_num, line in f.ChangedContents():
3522 if build_type_check_pattern.search(line):
3523 errors.append("%s:%d" % (f.LocalPath(), line_num))
3524
3525 results = []
3526
3527 if errors:
3528 results.append(
3529 output_api.PresubmitPromptWarning(
3530 'Build.TYPE.equals or .equals(Build.TYPE) usage is detected.'
3531 ' Please use BuildInfo.isDebugAndroid() instead.', errors))
3532
3533 return results
3534
3535# TODO: add unit tests
3536def _CheckAndroidToastUsage(input_api, output_api):
3537 """Checks that code uses org.chromium.ui.widget.Toast instead of
3538 android.widget.Toast (Chromium Toast doesn't force hardware
3539 acceleration on low-end devices, saving memory).
3540 """
3541 toast_import_pattern = input_api.re.compile(
3542 r'^import android\.widget\.Toast;$')
3543
3544 errors = []
3545
3546 sources = lambda affected_file: input_api.FilterSourceFile(
3547 affected_file,
3548 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
Bruce Dawson40fece62022-09-16 19:58:313549 DEFAULT_FILES_TO_SKIP + (r'^chromecast/.*',
3550 r'^remoting/.*')),
Sam Maiera6e76d72022-02-11 21:43:503551 files_to_check=[r'.*\.java$'])
3552
3553 for f in input_api.AffectedSourceFiles(sources):
3554 for line_num, line in f.ChangedContents():
3555 if toast_import_pattern.search(line):
3556 errors.append("%s:%d" % (f.LocalPath(), line_num))
3557
3558 results = []
3559
3560 if errors:
3561 results.append(
3562 output_api.PresubmitError(
3563 'android.widget.Toast usage is detected. Android toasts use hardware'
3564 ' acceleration, and can be\ncostly on low-end devices. Please use'
3565 ' org.chromium.ui.widget.Toast instead.\n'
3566 'Contact [email protected] if you have any questions.',
3567 errors))
3568
3569 return results
3570
3571
3572def _CheckAndroidCrLogUsage(input_api, output_api):
3573 """Checks that new logs using org.chromium.base.Log:
3574 - Are using 'TAG' as variable name for the tags (warn)
3575 - Are using a tag that is shorter than 20 characters (error)
3576 """
3577
3578 # Do not check format of logs in the given files
3579 cr_log_check_excluded_paths = [
3580 # //chrome/android/webapk cannot depend on //base
Bruce Dawson40fece62022-09-16 19:58:313581 r"^chrome/android/webapk/.*",
Sam Maiera6e76d72022-02-11 21:43:503582 # WebView license viewer code cannot depend on //base; used in stub APK.
Bruce Dawson40fece62022-09-16 19:58:313583 r"^android_webview/glue/java/src/com/android/"
3584 r"webview/chromium/License.*",
Sam Maiera6e76d72022-02-11 21:43:503585 # The customtabs_benchmark is a small app that does not depend on Chromium
3586 # java pieces.
Bruce Dawson40fece62022-09-16 19:58:313587 r"tools/android/customtabs_benchmark/.*",
Sam Maiera6e76d72022-02-11 21:43:503588 ]
3589
3590 cr_log_import_pattern = input_api.re.compile(
3591 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
3592 class_in_base_pattern = input_api.re.compile(
3593 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
3594 has_some_log_import_pattern = input_api.re.compile(r'^import .*\.Log;$',
3595 input_api.re.MULTILINE)
3596 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
3597 log_call_pattern = input_api.re.compile(r'\bLog\.\w\((?P<tag>\"?\w+)')
3598 log_decl_pattern = input_api.re.compile(
3599 r'static final String TAG = "(?P<name>(.*))"')
3600 rough_log_decl_pattern = input_api.re.compile(r'\bString TAG\s*=')
3601
3602 REF_MSG = ('See docs/android_logging.md for more info.')
3603 sources = lambda x: input_api.FilterSourceFile(
3604 x,
3605 files_to_check=[r'.*\.java$'],
3606 files_to_skip=cr_log_check_excluded_paths)
3607
3608 tag_decl_errors = []
3609 tag_length_errors = []
3610 tag_errors = []
3611 tag_with_dot_errors = []
3612 util_log_errors = []
3613
3614 for f in input_api.AffectedSourceFiles(sources):
3615 file_content = input_api.ReadFile(f)
3616 has_modified_logs = False
3617 # Per line checks
3618 if (cr_log_import_pattern.search(file_content)
3619 or (class_in_base_pattern.search(file_content)
3620 and not has_some_log_import_pattern.search(file_content))):
3621 # Checks to run for files using cr log
3622 for line_num, line in f.ChangedContents():
3623 if rough_log_decl_pattern.search(line):
3624 has_modified_logs = True
3625
3626 # Check if the new line is doing some logging
3627 match = log_call_pattern.search(line)
3628 if match:
3629 has_modified_logs = True
3630
3631 # Make sure it uses "TAG"
3632 if not match.group('tag') == 'TAG':
3633 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
3634 else:
3635 # Report non cr Log function calls in changed lines
3636 for line_num, line in f.ChangedContents():
3637 if log_call_pattern.search(line):
3638 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
3639
3640 # Per file checks
3641 if has_modified_logs:
3642 # Make sure the tag is using the "cr" prefix and is not too long
3643 match = log_decl_pattern.search(file_content)
3644 tag_name = match.group('name') if match else None
3645 if not tag_name:
3646 tag_decl_errors.append(f.LocalPath())
3647 elif len(tag_name) > 20:
3648 tag_length_errors.append(f.LocalPath())
3649 elif '.' in tag_name:
3650 tag_with_dot_errors.append(f.LocalPath())
3651
3652 results = []
3653 if tag_decl_errors:
3654 results.append(
3655 output_api.PresubmitPromptWarning(
3656 'Please define your tags using the suggested format: .\n'
3657 '"private static final String TAG = "<package tag>".\n'
3658 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
3659 tag_decl_errors))
3660
3661 if tag_length_errors:
3662 results.append(
3663 output_api.PresubmitError(
3664 'The tag length is restricted by the system to be at most '
3665 '20 characters.\n' + REF_MSG, tag_length_errors))
3666
3667 if tag_errors:
3668 results.append(
3669 output_api.PresubmitPromptWarning(
3670 'Please use a variable named "TAG" for your log tags.\n' +
3671 REF_MSG, tag_errors))
3672
3673 if util_log_errors:
3674 results.append(
3675 output_api.PresubmitPromptWarning(
3676 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
3677 util_log_errors))
3678
3679 if tag_with_dot_errors:
3680 results.append(
3681 output_api.PresubmitPromptWarning(
3682 'Dot in log tags cause them to be elided in crash reports.\n' +
3683 REF_MSG, tag_with_dot_errors))
3684
3685 return results
3686
3687
3688def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
3689 """Checks that junit.framework.* is no longer used."""
3690 deprecated_junit_framework_pattern = input_api.re.compile(
3691 r'^import junit\.framework\..*;', input_api.re.MULTILINE)
3692 sources = lambda x: input_api.FilterSourceFile(
3693 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
3694 errors = []
3695 for f in input_api.AffectedFiles(file_filter=sources):
3696 for line_num, line in f.ChangedContents():
3697 if deprecated_junit_framework_pattern.search(line):
3698 errors.append("%s:%d" % (f.LocalPath(), line_num))
3699
3700 results = []
3701 if errors:
3702 results.append(
3703 output_api.PresubmitError(
3704 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
3705 '(org.junit.*) from //third_party/junit. Contact [email protected]'
3706 ' if you have any question.', errors))
3707 return results
3708
3709
3710def _CheckAndroidTestJUnitInheritance(input_api, output_api):
3711 """Checks that if new Java test classes have inheritance.
3712 Either the new test class is JUnit3 test or it is a JUnit4 test class
3713 with a base class, either case is undesirable.
3714 """
3715 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
3716
3717 sources = lambda x: input_api.FilterSourceFile(
3718 x, files_to_check=[r'.*Test\.java$'], files_to_skip=None)
3719 errors = []
3720 for f in input_api.AffectedFiles(file_filter=sources):
3721 if not f.OldContents():
3722 class_declaration_start_flag = False
3723 for line_num, line in f.ChangedContents():
3724 if class_declaration_pattern.search(line):
3725 class_declaration_start_flag = True
3726 if class_declaration_start_flag and ' extends ' in line:
3727 errors.append('%s:%d' % (f.LocalPath(), line_num))
3728 if '{' in line:
3729 class_declaration_start_flag = False
3730
3731 results = []
3732 if errors:
3733 results.append(
3734 output_api.PresubmitPromptWarning(
3735 'The newly created files include Test classes that inherits from base'
3736 ' class. Please do not use inheritance in JUnit4 tests or add new'
3737 ' JUnit3 tests. Contact [email protected] if you have any'
3738 ' questions.', errors))
3739 return results
3740
3741
3742def _CheckAndroidTestAnnotationUsage(input_api, output_api):
3743 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
3744 deprecated_annotation_import_pattern = input_api.re.compile(
3745 r'^import android\.test\.suitebuilder\.annotation\..*;',
3746 input_api.re.MULTILINE)
3747 sources = lambda x: input_api.FilterSourceFile(
3748 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
3749 errors = []
3750 for f in input_api.AffectedFiles(file_filter=sources):
3751 for line_num, line in f.ChangedContents():
3752 if deprecated_annotation_import_pattern.search(line):
3753 errors.append("%s:%d" % (f.LocalPath(), line_num))
3754
3755 results = []
3756 if errors:
3757 results.append(
3758 output_api.PresubmitError(
3759 'Annotations in android.test.suitebuilder.annotation have been'
3760 ' deprecated since API level 24. Please use android.support.test.filters'
3761 ' from //third_party/android_support_test_runner:runner_java instead.'
3762 ' Contact [email protected] if you have any questions.',
3763 errors))
3764 return results
3765
3766
3767def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
3768 """Checks if MDPI assets are placed in a correct directory."""
Bruce Dawson6c05e852022-07-21 15:48:513769 file_filter = lambda f: (f.LocalPath().endswith(
3770 '.png') and ('/res/drawable/'.replace('/', input_api.os_path.sep) in f.
3771 LocalPath() or '/res/drawable-ldrtl/'.replace(
3772 '/', input_api.os_path.sep) in f.LocalPath()))
Sam Maiera6e76d72022-02-11 21:43:503773 errors = []
3774 for f in input_api.AffectedFiles(include_deletes=False,
3775 file_filter=file_filter):
3776 errors.append(' %s' % f.LocalPath())
3777
3778 results = []
3779 if errors:
3780 results.append(
3781 output_api.PresubmitError(
3782 'MDPI assets should be placed in /res/drawable-mdpi/ or '
3783 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
3784 '/res/drawable-ldrtl/.\n'
3785 'Contact [email protected] if you have questions.', errors))
3786 return results
3787
3788
3789def _CheckAndroidWebkitImports(input_api, output_api):
3790 """Checks that code uses org.chromium.base.Callback instead of
3791 android.webview.ValueCallback except in the WebView glue layer
3792 and WebLayer.
3793 """
3794 valuecallback_import_pattern = input_api.re.compile(
3795 r'^import android\.webkit\.ValueCallback;$')
3796
3797 errors = []
3798
3799 sources = lambda affected_file: input_api.FilterSourceFile(
3800 affected_file,
3801 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
3802 DEFAULT_FILES_TO_SKIP + (
Bruce Dawson40fece62022-09-16 19:58:313803 r'^android_webview/glue/.*',
3804 r'^weblayer/.*',
Sam Maiera6e76d72022-02-11 21:43:503805 )),
3806 files_to_check=[r'.*\.java$'])
3807
3808 for f in input_api.AffectedSourceFiles(sources):
3809 for line_num, line in f.ChangedContents():
3810 if valuecallback_import_pattern.search(line):
3811 errors.append("%s:%d" % (f.LocalPath(), line_num))
3812
3813 results = []
3814
3815 if errors:
3816 results.append(
3817 output_api.PresubmitError(
3818 'android.webkit.ValueCallback usage is detected outside of the glue'
3819 ' layer. To stay compatible with the support library, android.webkit.*'
3820 ' classes should only be used inside the glue layer and'
3821 ' org.chromium.base.Callback should be used instead.', errors))
3822
3823 return results
3824
3825
3826def _CheckAndroidXmlStyle(input_api, output_api, is_check_on_upload):
3827 """Checks Android XML styles """
3828
3829 # Return early if no relevant files were modified.
3830 if not any(
3831 _IsXmlOrGrdFile(input_api, f.LocalPath())
3832 for f in input_api.AffectedFiles(include_deletes=False)):
3833 return []
3834
3835 import sys
3836 original_sys_path = sys.path
3837 try:
3838 sys.path = sys.path + [
3839 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
3840 'android', 'checkxmlstyle')
3841 ]
3842 import checkxmlstyle
3843 finally:
3844 # Restore sys.path to what it was before.
3845 sys.path = original_sys_path
3846
3847 if is_check_on_upload:
3848 return checkxmlstyle.CheckStyleOnUpload(input_api, output_api)
3849 else:
3850 return checkxmlstyle.CheckStyleOnCommit(input_api, output_api)
3851
3852
3853def _CheckAndroidInfoBarDeprecation(input_api, output_api):
3854 """Checks Android Infobar Deprecation """
3855
3856 import sys
3857 original_sys_path = sys.path
3858 try:
3859 sys.path = sys.path + [
3860 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
3861 'android', 'infobar_deprecation')
3862 ]
3863 import infobar_deprecation
3864 finally:
3865 # Restore sys.path to what it was before.
3866 sys.path = original_sys_path
3867
3868 return infobar_deprecation.CheckDeprecationOnUpload(input_api, output_api)
3869
3870
3871class _PydepsCheckerResult:
3872 def __init__(self, cmd, pydeps_path, process, old_contents):
3873 self._cmd = cmd
3874 self._pydeps_path = pydeps_path
3875 self._process = process
3876 self._old_contents = old_contents
3877
3878 def GetError(self):
3879 """Returns an error message, or None."""
3880 import difflib
3881 if self._process.wait() != 0:
3882 # STDERR should already be printed.
3883 return 'Command failed: ' + self._cmd
3884 new_contents = self._process.stdout.read().splitlines()[2:]
3885 if self._old_contents != new_contents:
3886 diff = '\n'.join(
3887 difflib.context_diff(self._old_contents, new_contents))
3888 return ('File is stale: {}\n'
3889 'Diff (apply to fix):\n'
3890 '{}\n'
3891 'To regenerate, run:\n\n'
3892 ' {}').format(self._pydeps_path, diff, self._cmd)
3893 return None
3894
3895
3896class PydepsChecker:
3897 def __init__(self, input_api, pydeps_files):
3898 self._file_cache = {}
3899 self._input_api = input_api
3900 self._pydeps_files = pydeps_files
3901
3902 def _LoadFile(self, path):
3903 """Returns the list of paths within a .pydeps file relative to //."""
3904 if path not in self._file_cache:
3905 with open(path, encoding='utf-8') as f:
3906 self._file_cache[path] = f.read()
3907 return self._file_cache[path]
3908
3909 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
Gao Shenga79ebd42022-08-08 17:25:593910 """Returns an iterable of paths within the .pydep, relativized to //."""
Sam Maiera6e76d72022-02-11 21:43:503911 pydeps_data = self._LoadFile(pydeps_path)
3912 uses_gn_paths = '--gn-paths' in pydeps_data
3913 entries = (l for l in pydeps_data.splitlines()
3914 if not l.startswith('#'))
3915 if uses_gn_paths:
3916 # Paths look like: //foo/bar/baz
3917 return (e[2:] for e in entries)
3918 else:
3919 # Paths look like: path/relative/to/file.pydeps
3920 os_path = self._input_api.os_path
3921 pydeps_dir = os_path.dirname(pydeps_path)
3922 return (os_path.normpath(os_path.join(pydeps_dir, e))
3923 for e in entries)
3924
3925 def _CreateFilesToPydepsMap(self):
3926 """Returns a map of local_path -> list_of_pydeps."""
3927 ret = {}
3928 for pydep_local_path in self._pydeps_files:
3929 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
3930 ret.setdefault(path, []).append(pydep_local_path)
3931 return ret
3932
3933 def ComputeAffectedPydeps(self):
3934 """Returns an iterable of .pydeps files that might need regenerating."""
3935 affected_pydeps = set()
3936 file_to_pydeps_map = None
3937 for f in self._input_api.AffectedFiles(include_deletes=True):
3938 local_path = f.LocalPath()
3939 # Changes to DEPS can lead to .pydeps changes if any .py files are in
3940 # subrepositories. We can't figure out which files change, so re-check
3941 # all files.
3942 # Changes to print_python_deps.py affect all .pydeps.
3943 if local_path in ('DEPS', 'PRESUBMIT.py'
3944 ) or local_path.endswith('print_python_deps.py'):
3945 return self._pydeps_files
3946 elif local_path.endswith('.pydeps'):
3947 if local_path in self._pydeps_files:
3948 affected_pydeps.add(local_path)
3949 elif local_path.endswith('.py'):
3950 if file_to_pydeps_map is None:
3951 file_to_pydeps_map = self._CreateFilesToPydepsMap()
3952 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
3953 return affected_pydeps
3954
3955 def DetermineIfStaleAsync(self, pydeps_path):
3956 """Runs print_python_deps.py to see if the files is stale."""
3957 import os
3958
3959 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
3960 if old_pydeps_data:
3961 cmd = old_pydeps_data[1][1:].strip()
3962 if '--output' not in cmd:
3963 cmd += ' --output ' + pydeps_path
3964 old_contents = old_pydeps_data[2:]
3965 else:
3966 # A default cmd that should work in most cases (as long as pydeps filename
3967 # matches the script name) so that PRESUBMIT.py does not crash if pydeps
3968 # file is empty/new.
3969 cmd = 'build/print_python_deps.py {} --root={} --output={}'.format(
3970 pydeps_path[:-4], os.path.dirname(pydeps_path), pydeps_path)
3971 old_contents = []
3972 env = dict(os.environ)
3973 env['PYTHONDONTWRITEBYTECODE'] = '1'
3974 process = self._input_api.subprocess.Popen(
3975 cmd + ' --output ""',
3976 shell=True,
3977 env=env,
3978 stdout=self._input_api.subprocess.PIPE,
3979 encoding='utf-8')
3980 return _PydepsCheckerResult(cmd, pydeps_path, process, old_contents)
agrievef32bcc72016-04-04 14:57:403981
3982
Tibor Goldschwendt360793f72019-06-25 18:23:493983def _ParseGclientArgs():
Sam Maiera6e76d72022-02-11 21:43:503984 args = {}
3985 with open('build/config/gclient_args.gni', 'r') as f:
3986 for line in f:
3987 line = line.strip()
3988 if not line or line.startswith('#'):
3989 continue
3990 attribute, value = line.split('=')
3991 args[attribute.strip()] = value.strip()
3992 return args
Tibor Goldschwendt360793f72019-06-25 18:23:493993
3994
Saagar Sanghavifceeaae2020-08-12 16:40:363995def CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
Sam Maiera6e76d72022-02-11 21:43:503996 """Checks if a .pydeps file needs to be regenerated."""
3997 # This check is for Python dependency lists (.pydeps files), and involves
3998 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
3999 # doesn't work on Windows and Mac, so skip it on other platforms.
4000 if not input_api.platform.startswith('linux'):
4001 return []
Erik Staabc734cd7a2021-11-23 03:11:524002
Sam Maiera6e76d72022-02-11 21:43:504003 results = []
4004 # First, check for new / deleted .pydeps.
4005 for f in input_api.AffectedFiles(include_deletes=True):
4006 # Check whether we are running the presubmit check for a file in src.
4007 # f.LocalPath is relative to repo (src, or internal repo).
4008 # os_path.exists is relative to src repo.
4009 # Therefore if os_path.exists is true, it means f.LocalPath is relative
4010 # to src and we can conclude that the pydeps is in src.
4011 if f.LocalPath().endswith('.pydeps'):
4012 if input_api.os_path.exists(f.LocalPath()):
4013 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
4014 results.append(
4015 output_api.PresubmitError(
4016 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
4017 'remove %s' % f.LocalPath()))
4018 elif f.Action() != 'D' and f.LocalPath(
4019 ) not in _ALL_PYDEPS_FILES:
4020 results.append(
4021 output_api.PresubmitError(
4022 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
4023 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:404024
Sam Maiera6e76d72022-02-11 21:43:504025 if results:
4026 return results
4027
4028 is_android = _ParseGclientArgs().get('checkout_android', 'false') == 'true'
4029 checker = checker_for_tests or PydepsChecker(input_api, _ALL_PYDEPS_FILES)
4030 affected_pydeps = set(checker.ComputeAffectedPydeps())
4031 affected_android_pydeps = affected_pydeps.intersection(
4032 set(_ANDROID_SPECIFIC_PYDEPS_FILES))
4033 if affected_android_pydeps and not is_android:
4034 results.append(
4035 output_api.PresubmitPromptOrNotify(
4036 'You have changed python files that may affect pydeps for android\n'
Gao Shenga79ebd42022-08-08 17:25:594037 'specific scripts. However, the relevant presubmit check cannot be\n'
Sam Maiera6e76d72022-02-11 21:43:504038 'run because you are not using an Android checkout. To validate that\n'
4039 'the .pydeps are correct, re-run presubmit in an Android checkout, or\n'
4040 'use the android-internal-presubmit optional trybot.\n'
4041 'Possibly stale pydeps files:\n{}'.format(
4042 '\n'.join(affected_android_pydeps))))
4043
4044 all_pydeps = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
4045 pydeps_to_check = affected_pydeps.intersection(all_pydeps)
4046 # Process these concurrently, as each one takes 1-2 seconds.
4047 pydep_results = [checker.DetermineIfStaleAsync(p) for p in pydeps_to_check]
4048 for result in pydep_results:
4049 error_msg = result.GetError()
4050 if error_msg:
4051 results.append(output_api.PresubmitError(error_msg))
4052
agrievef32bcc72016-04-04 14:57:404053 return results
4054
agrievef32bcc72016-04-04 14:57:404055
Saagar Sanghavifceeaae2020-08-12 16:40:364056def CheckSingletonInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504057 """Checks to make sure no header files have |Singleton<|."""
4058
4059 def FileFilter(affected_file):
4060 # It's ok for base/memory/singleton.h to have |Singleton<|.
4061 files_to_skip = (_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP +
Bruce Dawson40fece62022-09-16 19:58:314062 (r"^base/memory/singleton\.h$",
4063 r"^net/quic/platform/impl/quic_singleton_impl\.h$"))
Sam Maiera6e76d72022-02-11 21:43:504064 return input_api.FilterSourceFile(affected_file,
4065 files_to_skip=files_to_skip)
glidere61efad2015-02-18 17:39:434066
Sam Maiera6e76d72022-02-11 21:43:504067 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
4068 files = []
4069 for f in input_api.AffectedSourceFiles(FileFilter):
4070 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx')
4071 or f.LocalPath().endswith('.hpp')
4072 or f.LocalPath().endswith('.inl')):
4073 contents = input_api.ReadFile(f)
4074 for line in contents.splitlines(False):
4075 if (not line.lstrip().startswith('//')
4076 and # Strip C++ comment.
4077 pattern.search(line)):
4078 files.append(f)
4079 break
glidere61efad2015-02-18 17:39:434080
Sam Maiera6e76d72022-02-11 21:43:504081 if files:
4082 return [
4083 output_api.PresubmitError(
4084 'Found base::Singleton<T> in the following header files.\n' +
4085 'Please move them to an appropriate source file so that the ' +
4086 'template gets instantiated in a single compilation unit.',
4087 files)
4088 ]
4089 return []
glidere61efad2015-02-18 17:39:434090
4091
[email protected]fd20b902014-05-09 02:14:534092_DEPRECATED_CSS = [
4093 # Values
4094 ( "-webkit-box", "flex" ),
4095 ( "-webkit-inline-box", "inline-flex" ),
4096 ( "-webkit-flex", "flex" ),
4097 ( "-webkit-inline-flex", "inline-flex" ),
4098 ( "-webkit-min-content", "min-content" ),
4099 ( "-webkit-max-content", "max-content" ),
4100
4101 # Properties
4102 ( "-webkit-background-clip", "background-clip" ),
4103 ( "-webkit-background-origin", "background-origin" ),
4104 ( "-webkit-background-size", "background-size" ),
4105 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:444106 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:534107
4108 # Functions
4109 ( "-webkit-gradient", "gradient" ),
4110 ( "-webkit-repeating-gradient", "repeating-gradient" ),
4111 ( "-webkit-linear-gradient", "linear-gradient" ),
4112 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
4113 ( "-webkit-radial-gradient", "radial-gradient" ),
4114 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
4115]
4116
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:204117
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:494118# TODO: add unit tests
Saagar Sanghavifceeaae2020-08-12 16:40:364119def CheckNoDeprecatedCss(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504120 """ Make sure that we don't use deprecated CSS
4121 properties, functions or values. Our external
4122 documentation and iOS CSS for dom distiller
4123 (reader mode) are ignored by the hooks as it
4124 needs to be consumed by WebKit. """
4125 results = []
4126 file_inclusion_pattern = [r".+\.css$"]
4127 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
4128 input_api.DEFAULT_FILES_TO_SKIP +
4129 (r"^chrome/common/extensions/docs", r"^chrome/docs",
4130 r"^native_client_sdk"))
4131 file_filter = lambda f: input_api.FilterSourceFile(
4132 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
4133 for fpath in input_api.AffectedFiles(file_filter=file_filter):
4134 for line_num, line in fpath.ChangedContents():
4135 for (deprecated_value, value) in _DEPRECATED_CSS:
4136 if deprecated_value in line:
4137 results.append(
4138 output_api.PresubmitError(
4139 "%s:%d: Use of deprecated CSS %s, use %s instead" %
4140 (fpath.LocalPath(), line_num, deprecated_value,
4141 value)))
4142 return results
[email protected]fd20b902014-05-09 02:14:534143
mohan.reddyf21db962014-10-16 12:26:474144
Saagar Sanghavifceeaae2020-08-12 16:40:364145def CheckForRelativeIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504146 bad_files = {}
4147 for f in input_api.AffectedFiles(include_deletes=False):
4148 if (f.LocalPath().startswith('third_party')
4149 and not f.LocalPath().startswith('third_party/blink')
4150 and not f.LocalPath().startswith('third_party\\blink')):
4151 continue
rlanday6802cf632017-05-30 17:48:364152
Sam Maiera6e76d72022-02-11 21:43:504153 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
4154 continue
rlanday6802cf632017-05-30 17:48:364155
Sam Maiera6e76d72022-02-11 21:43:504156 relative_includes = [
4157 line for _, line in f.ChangedContents()
4158 if "#include" in line and "../" in line
4159 ]
4160 if not relative_includes:
4161 continue
4162 bad_files[f.LocalPath()] = relative_includes
rlanday6802cf632017-05-30 17:48:364163
Sam Maiera6e76d72022-02-11 21:43:504164 if not bad_files:
4165 return []
rlanday6802cf632017-05-30 17:48:364166
Sam Maiera6e76d72022-02-11 21:43:504167 error_descriptions = []
4168 for file_path, bad_lines in bad_files.items():
4169 error_description = file_path
4170 for line in bad_lines:
4171 error_description += '\n ' + line
4172 error_descriptions.append(error_description)
rlanday6802cf632017-05-30 17:48:364173
Sam Maiera6e76d72022-02-11 21:43:504174 results = []
4175 results.append(
4176 output_api.PresubmitError(
4177 'You added one or more relative #include paths (including "../").\n'
4178 'These shouldn\'t be used because they can be used to include headers\n'
4179 'from code that\'s not correctly specified as a dependency in the\n'
4180 'relevant BUILD.gn file(s).', error_descriptions))
rlanday6802cf632017-05-30 17:48:364181
Sam Maiera6e76d72022-02-11 21:43:504182 return results
rlanday6802cf632017-05-30 17:48:364183
Takeshi Yoshinoe387aa32017-08-02 13:16:134184
Saagar Sanghavifceeaae2020-08-12 16:40:364185def CheckForCcIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504186 """Check that nobody tries to include a cc file. It's a relatively
4187 common error which results in duplicate symbols in object
4188 files. This may not always break the build until someone later gets
4189 very confusing linking errors."""
4190 results = []
4191 for f in input_api.AffectedFiles(include_deletes=False):
4192 # We let third_party code do whatever it wants
4193 if (f.LocalPath().startswith('third_party')
4194 and not f.LocalPath().startswith('third_party/blink')
4195 and not f.LocalPath().startswith('third_party\\blink')):
4196 continue
Daniel Bratell65b033262019-04-23 08:17:064197
Sam Maiera6e76d72022-02-11 21:43:504198 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
4199 continue
Daniel Bratell65b033262019-04-23 08:17:064200
Sam Maiera6e76d72022-02-11 21:43:504201 for _, line in f.ChangedContents():
4202 if line.startswith('#include "'):
4203 included_file = line.split('"')[1]
4204 if _IsCPlusPlusFile(input_api, included_file):
4205 # The most common naming for external files with C++ code,
4206 # apart from standard headers, is to call them foo.inc, but
4207 # Chromium sometimes uses foo-inc.cc so allow that as well.
4208 if not included_file.endswith(('.h', '-inc.cc')):
4209 results.append(
4210 output_api.PresubmitError(
4211 'Only header files or .inc files should be included in other\n'
4212 'C++ files. Compiling the contents of a cc file more than once\n'
4213 'will cause duplicate information in the build which may later\n'
4214 'result in strange link_errors.\n' +
4215 f.LocalPath() + ':\n ' + line))
Daniel Bratell65b033262019-04-23 08:17:064216
Sam Maiera6e76d72022-02-11 21:43:504217 return results
Daniel Bratell65b033262019-04-23 08:17:064218
4219
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204220def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
Sam Maiera6e76d72022-02-11 21:43:504221 if not isinstance(key, ast.Str):
4222 return 'Key at line %d must be a string literal' % key.lineno
4223 if not isinstance(value, ast.Dict):
4224 return 'Value at line %d must be a dict' % value.lineno
4225 if len(value.keys) != 1:
4226 return 'Dict at line %d must have single entry' % value.lineno
4227 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
4228 return (
4229 'Entry at line %d must have a string literal \'filepath\' as key' %
4230 value.lineno)
4231 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:134232
Takeshi Yoshinoe387aa32017-08-02 13:16:134233
Sergey Ulanov4af16052018-11-08 02:41:464234def _CheckWatchlistsEntrySyntax(key, value, ast, email_regex):
Sam Maiera6e76d72022-02-11 21:43:504235 if not isinstance(key, ast.Str):
4236 return 'Key at line %d must be a string literal' % key.lineno
4237 if not isinstance(value, ast.List):
4238 return 'Value at line %d must be a list' % value.lineno
4239 for element in value.elts:
4240 if not isinstance(element, ast.Str):
4241 return 'Watchlist elements on line %d is not a string' % key.lineno
4242 if not email_regex.match(element.s):
4243 return ('Watchlist element on line %d doesn\'t look like a valid '
4244 + 'email: %s') % (key.lineno, element.s)
4245 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:134246
Takeshi Yoshinoe387aa32017-08-02 13:16:134247
Sergey Ulanov4af16052018-11-08 02:41:464248def _CheckWATCHLISTSEntries(wd_dict, w_dict, input_api):
Sam Maiera6e76d72022-02-11 21:43:504249 mismatch_template = (
4250 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
4251 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:134252
Sam Maiera6e76d72022-02-11 21:43:504253 email_regex = input_api.re.compile(
4254 r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$")
Sergey Ulanov4af16052018-11-08 02:41:464255
Sam Maiera6e76d72022-02-11 21:43:504256 ast = input_api.ast
4257 i = 0
4258 last_key = ''
4259 while True:
4260 if i >= len(wd_dict.keys):
4261 if i >= len(w_dict.keys):
4262 return None
4263 return mismatch_template % ('missing',
4264 'line %d' % w_dict.keys[i].lineno)
4265 elif i >= len(w_dict.keys):
4266 return (mismatch_template %
4267 ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:134268
Sam Maiera6e76d72022-02-11 21:43:504269 wd_key = wd_dict.keys[i]
4270 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:134271
Sam Maiera6e76d72022-02-11 21:43:504272 result = _CheckWatchlistDefinitionsEntrySyntax(wd_key,
4273 wd_dict.values[i], ast)
4274 if result is not None:
4275 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:134276
Sam Maiera6e76d72022-02-11 21:43:504277 result = _CheckWatchlistsEntrySyntax(w_key, w_dict.values[i], ast,
4278 email_regex)
4279 if result is not None:
4280 return 'Bad entry in WATCHLISTS dict: %s' % result
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204281
Sam Maiera6e76d72022-02-11 21:43:504282 if wd_key.s != w_key.s:
4283 return mismatch_template % ('%s at line %d' %
4284 (wd_key.s, wd_key.lineno),
4285 '%s at line %d' %
4286 (w_key.s, w_key.lineno))
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204287
Sam Maiera6e76d72022-02-11 21:43:504288 if wd_key.s < last_key:
4289 return (
4290 'WATCHLISTS dict is not sorted lexicographically at line %d and %d'
4291 % (wd_key.lineno, w_key.lineno))
4292 last_key = wd_key.s
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204293
Sam Maiera6e76d72022-02-11 21:43:504294 i = i + 1
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204295
4296
Sergey Ulanov4af16052018-11-08 02:41:464297def _CheckWATCHLISTSSyntax(expression, input_api):
Sam Maiera6e76d72022-02-11 21:43:504298 ast = input_api.ast
4299 if not isinstance(expression, ast.Expression):
4300 return 'WATCHLISTS file must contain a valid expression'
4301 dictionary = expression.body
4302 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
4303 return 'WATCHLISTS file must have single dict with exactly two entries'
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204304
Sam Maiera6e76d72022-02-11 21:43:504305 first_key = dictionary.keys[0]
4306 first_value = dictionary.values[0]
4307 second_key = dictionary.keys[1]
4308 second_value = dictionary.values[1]
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204309
Sam Maiera6e76d72022-02-11 21:43:504310 if (not isinstance(first_key, ast.Str)
4311 or first_key.s != 'WATCHLIST_DEFINITIONS'
4312 or not isinstance(first_value, ast.Dict)):
4313 return ('The first entry of the dict in WATCHLISTS file must be '
4314 'WATCHLIST_DEFINITIONS dict')
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204315
Sam Maiera6e76d72022-02-11 21:43:504316 if (not isinstance(second_key, ast.Str) or second_key.s != 'WATCHLISTS'
4317 or not isinstance(second_value, ast.Dict)):
4318 return ('The second entry of the dict in WATCHLISTS file must be '
4319 'WATCHLISTS dict')
Takeshi Yoshino3a8f9cb52017-08-10 11:32:204320
Sam Maiera6e76d72022-02-11 21:43:504321 return _CheckWATCHLISTSEntries(first_value, second_value, input_api)
Takeshi Yoshinoe387aa32017-08-02 13:16:134322
4323
Saagar Sanghavifceeaae2020-08-12 16:40:364324def CheckWATCHLISTS(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504325 for f in input_api.AffectedFiles(include_deletes=False):
4326 if f.LocalPath() == 'WATCHLISTS':
4327 contents = input_api.ReadFile(f, 'r')
Takeshi Yoshinoe387aa32017-08-02 13:16:134328
Sam Maiera6e76d72022-02-11 21:43:504329 try:
4330 # First, make sure that it can be evaluated.
4331 input_api.ast.literal_eval(contents)
4332 # Get an AST tree for it and scan the tree for detailed style checking.
4333 expression = input_api.ast.parse(contents,
4334 filename='WATCHLISTS',
4335 mode='eval')
4336 except ValueError as e:
4337 return [
4338 output_api.PresubmitError('Cannot parse WATCHLISTS file',
4339 long_text=repr(e))
4340 ]
4341 except SyntaxError as e:
4342 return [
4343 output_api.PresubmitError('Cannot parse WATCHLISTS file',
4344 long_text=repr(e))
4345 ]
4346 except TypeError as e:
4347 return [
4348 output_api.PresubmitError('Cannot parse WATCHLISTS file',
4349 long_text=repr(e))
4350 ]
Takeshi Yoshinoe387aa32017-08-02 13:16:134351
Sam Maiera6e76d72022-02-11 21:43:504352 result = _CheckWATCHLISTSSyntax(expression, input_api)
4353 if result is not None:
4354 return [output_api.PresubmitError(result)]
4355 break
Takeshi Yoshinoe387aa32017-08-02 13:16:134356
Sam Maiera6e76d72022-02-11 21:43:504357 return []
Takeshi Yoshinoe387aa32017-08-02 13:16:134358
4359
Andrew Grieve1b290e4a22020-11-24 20:07:014360def CheckGnGlobForward(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504361 """Checks that forward_variables_from(invoker, "*") follows best practices.
Andrew Grieve1b290e4a22020-11-24 20:07:014362
Sam Maiera6e76d72022-02-11 21:43:504363 As documented at //build/docs/writing_gn_templates.md
4364 """
Andrew Grieve1b290e4a22020-11-24 20:07:014365
Sam Maiera6e76d72022-02-11 21:43:504366 def gn_files(f):
4367 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gni', ))
Andrew Grieve1b290e4a22020-11-24 20:07:014368
Sam Maiera6e76d72022-02-11 21:43:504369 problems = []
4370 for f in input_api.AffectedSourceFiles(gn_files):
4371 for line_num, line in f.ChangedContents():
4372 if 'forward_variables_from(invoker, "*")' in line:
4373 problems.append(
4374 'Bare forward_variables_from(invoker, "*") in %s:%d' %
4375 (f.LocalPath(), line_num))
4376
4377 if problems:
4378 return [
4379 output_api.PresubmitPromptWarning(
4380 'forward_variables_from("*") without exclusions',
4381 items=sorted(problems),
4382 long_text=(
Gao Shenga79ebd42022-08-08 17:25:594383 'The variables "visibility" and "test_only" should be '
Sam Maiera6e76d72022-02-11 21:43:504384 'explicitly listed in forward_variables_from(). For more '
4385 'details, see:\n'
4386 'https://chromium.googlesource.com/chromium/src/+/HEAD/'
4387 'build/docs/writing_gn_templates.md'
4388 '#Using-forward_variables_from'))
4389 ]
4390 return []
Andrew Grieve1b290e4a22020-11-24 20:07:014391
4392
Saagar Sanghavifceeaae2020-08-12 16:40:364393def CheckNewHeaderWithoutGnChangeOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504394 """Checks that newly added header files have corresponding GN changes.
4395 Note that this is only a heuristic. To be precise, run script:
4396 build/check_gn_headers.py.
4397 """
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194398
Sam Maiera6e76d72022-02-11 21:43:504399 def headers(f):
4400 return input_api.FilterSourceFile(
4401 f, files_to_check=(r'.+%s' % _HEADER_EXTENSIONS, ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194402
Sam Maiera6e76d72022-02-11 21:43:504403 new_headers = []
4404 for f in input_api.AffectedSourceFiles(headers):
4405 if f.Action() != 'A':
4406 continue
4407 new_headers.append(f.LocalPath())
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194408
Sam Maiera6e76d72022-02-11 21:43:504409 def gn_files(f):
4410 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gn', ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194411
Sam Maiera6e76d72022-02-11 21:43:504412 all_gn_changed_contents = ''
4413 for f in input_api.AffectedSourceFiles(gn_files):
4414 for _, line in f.ChangedContents():
4415 all_gn_changed_contents += line
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194416
Sam Maiera6e76d72022-02-11 21:43:504417 problems = []
4418 for header in new_headers:
4419 basename = input_api.os_path.basename(header)
4420 if basename not in all_gn_changed_contents:
4421 problems.append(header)
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194422
Sam Maiera6e76d72022-02-11 21:43:504423 if problems:
4424 return [
4425 output_api.PresubmitPromptWarning(
4426 'Missing GN changes for new header files',
4427 items=sorted(problems),
4428 long_text=
4429 'Please double check whether newly added header files need '
4430 'corresponding changes in gn or gni files.\nThis checking is only a '
4431 'heuristic. Run build/check_gn_headers.py to be precise.\n'
4432 'Read https://crbug.com/661774 for more info.')
4433 ]
4434 return []
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194435
4436
Saagar Sanghavifceeaae2020-08-12 16:40:364437def CheckCorrectProductNameInMessages(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504438 """Check that Chromium-branded strings don't include "Chrome" or vice versa.
Michael Giuffridad3bc8672018-10-25 22:48:024439
Sam Maiera6e76d72022-02-11 21:43:504440 This assumes we won't intentionally reference one product from the other
4441 product.
4442 """
4443 all_problems = []
4444 test_cases = [{
4445 "filename_postfix": "google_chrome_strings.grd",
4446 "correct_name": "Chrome",
4447 "incorrect_name": "Chromium",
4448 }, {
4449 "filename_postfix": "chromium_strings.grd",
4450 "correct_name": "Chromium",
4451 "incorrect_name": "Chrome",
4452 }]
Michael Giuffridad3bc8672018-10-25 22:48:024453
Sam Maiera6e76d72022-02-11 21:43:504454 for test_case in test_cases:
4455 problems = []
4456 filename_filter = lambda x: x.LocalPath().endswith(test_case[
4457 "filename_postfix"])
Michael Giuffridad3bc8672018-10-25 22:48:024458
Sam Maiera6e76d72022-02-11 21:43:504459 # Check each new line. Can yield false positives in multiline comments, but
4460 # easier than trying to parse the XML because messages can have nested
4461 # children, and associating message elements with affected lines is hard.
4462 for f in input_api.AffectedSourceFiles(filename_filter):
4463 for line_num, line in f.ChangedContents():
4464 if "<message" in line or "<!--" in line or "-->" in line:
4465 continue
4466 if test_case["incorrect_name"] in line:
4467 problems.append("Incorrect product name in %s:%d" %
4468 (f.LocalPath(), line_num))
Michael Giuffridad3bc8672018-10-25 22:48:024469
Sam Maiera6e76d72022-02-11 21:43:504470 if problems:
4471 message = (
4472 "Strings in %s-branded string files should reference \"%s\", not \"%s\""
4473 % (test_case["correct_name"], test_case["correct_name"],
4474 test_case["incorrect_name"]))
4475 all_problems.append(
4476 output_api.PresubmitPromptWarning(message, items=problems))
Michael Giuffridad3bc8672018-10-25 22:48:024477
Sam Maiera6e76d72022-02-11 21:43:504478 return all_problems
Michael Giuffridad3bc8672018-10-25 22:48:024479
4480
Saagar Sanghavifceeaae2020-08-12 16:40:364481def CheckForTooLargeFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504482 """Avoid large files, especially binary files, in the repository since
4483 git doesn't scale well for those. They will be in everyone's repo
4484 clones forever, forever making Chromium slower to clone and work
4485 with."""
Daniel Bratell93eb6c62019-04-29 20:13:364486
Sam Maiera6e76d72022-02-11 21:43:504487 # Uploading files to cloud storage is not trivial so we don't want
4488 # to set the limit too low, but the upper limit for "normal" large
4489 # files seems to be 1-2 MB, with a handful around 5-8 MB, so
4490 # anything over 20 MB is exceptional.
4491 TOO_LARGE_FILE_SIZE_LIMIT = 20 * 1024 * 1024 # 10 MB
Daniel Bratell93eb6c62019-04-29 20:13:364492
Sam Maiera6e76d72022-02-11 21:43:504493 too_large_files = []
4494 for f in input_api.AffectedFiles():
4495 # Check both added and modified files (but not deleted files).
4496 if f.Action() in ('A', 'M'):
4497 size = input_api.os_path.getsize(f.AbsoluteLocalPath())
4498 if size > TOO_LARGE_FILE_SIZE_LIMIT:
4499 too_large_files.append("%s: %d bytes" % (f.LocalPath(), size))
Daniel Bratell93eb6c62019-04-29 20:13:364500
Sam Maiera6e76d72022-02-11 21:43:504501 if too_large_files:
4502 message = (
4503 'Do not commit large files to git since git scales badly for those.\n'
4504 +
4505 'Instead put the large files in cloud storage and use DEPS to\n' +
4506 'fetch them.\n' + '\n'.join(too_large_files))
4507 return [
4508 output_api.PresubmitError('Too large files found in commit',
4509 long_text=message + '\n')
4510 ]
4511 else:
4512 return []
Daniel Bratell93eb6c62019-04-29 20:13:364513
Max Morozb47503b2019-08-08 21:03:274514
Saagar Sanghavifceeaae2020-08-12 16:40:364515def CheckFuzzTargetsOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504516 """Checks specific for fuzz target sources."""
4517 EXPORTED_SYMBOLS = [
4518 'LLVMFuzzerInitialize',
4519 'LLVMFuzzerCustomMutator',
4520 'LLVMFuzzerCustomCrossOver',
4521 'LLVMFuzzerMutate',
4522 ]
Max Morozb47503b2019-08-08 21:03:274523
Sam Maiera6e76d72022-02-11 21:43:504524 REQUIRED_HEADER = '#include "testing/libfuzzer/libfuzzer_exports.h"'
Max Morozb47503b2019-08-08 21:03:274525
Sam Maiera6e76d72022-02-11 21:43:504526 def FilterFile(affected_file):
4527 """Ignore libFuzzer source code."""
4528 files_to_check = r'.*fuzz.*\.(h|hpp|hcc|cc|cpp|cxx)$'
Bruce Dawson40fece62022-09-16 19:58:314529 files_to_skip = r"^third_party/libFuzzer"
Max Morozb47503b2019-08-08 21:03:274530
Sam Maiera6e76d72022-02-11 21:43:504531 return input_api.FilterSourceFile(affected_file,
4532 files_to_check=[files_to_check],
4533 files_to_skip=[files_to_skip])
Max Morozb47503b2019-08-08 21:03:274534
Sam Maiera6e76d72022-02-11 21:43:504535 files_with_missing_header = []
4536 for f in input_api.AffectedSourceFiles(FilterFile):
4537 contents = input_api.ReadFile(f, 'r')
4538 if REQUIRED_HEADER in contents:
4539 continue
Max Morozb47503b2019-08-08 21:03:274540
Sam Maiera6e76d72022-02-11 21:43:504541 if any(symbol in contents for symbol in EXPORTED_SYMBOLS):
4542 files_with_missing_header.append(f.LocalPath())
Max Morozb47503b2019-08-08 21:03:274543
Sam Maiera6e76d72022-02-11 21:43:504544 if not files_with_missing_header:
4545 return []
Max Morozb47503b2019-08-08 21:03:274546
Sam Maiera6e76d72022-02-11 21:43:504547 long_text = (
4548 'If you define any of the libFuzzer optional functions (%s), it is '
4549 'recommended to add \'%s\' directive. Otherwise, the fuzz target may '
4550 'work incorrectly on Mac (crbug.com/687076).\nNote that '
4551 'LLVMFuzzerInitialize should not be used, unless your fuzz target needs '
4552 'to access command line arguments passed to the fuzzer. Instead, prefer '
4553 'static initialization and shared resources as documented in '
4554 'https://chromium.googlesource.com/chromium/src/+/main/testing/'
4555 'libfuzzer/efficient_fuzzing.md#simplifying-initialization_cleanup.\n'
4556 % (', '.join(EXPORTED_SYMBOLS), REQUIRED_HEADER))
Max Morozb47503b2019-08-08 21:03:274557
Sam Maiera6e76d72022-02-11 21:43:504558 return [
4559 output_api.PresubmitPromptWarning(message="Missing '%s' in:" %
4560 REQUIRED_HEADER,
4561 items=files_with_missing_header,
4562 long_text=long_text)
4563 ]
Max Morozb47503b2019-08-08 21:03:274564
4565
Mohamed Heikald048240a2019-11-12 16:57:374566def _CheckNewImagesWarning(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504567 """
4568 Warns authors who add images into the repo to make sure their images are
4569 optimized before committing.
4570 """
4571 images_added = False
4572 image_paths = []
4573 errors = []
4574 filter_lambda = lambda x: input_api.FilterSourceFile(
4575 x,
4576 files_to_skip=(('(?i).*test', r'.*\/junit\/') + input_api.
4577 DEFAULT_FILES_TO_SKIP),
4578 files_to_check=[r'.*\/(drawable|mipmap)'])
4579 for f in input_api.AffectedFiles(include_deletes=False,
4580 file_filter=filter_lambda):
4581 local_path = f.LocalPath().lower()
4582 if any(
4583 local_path.endswith(extension)
4584 for extension in _IMAGE_EXTENSIONS):
4585 images_added = True
4586 image_paths.append(f)
4587 if images_added:
4588 errors.append(
4589 output_api.PresubmitPromptWarning(
4590 'It looks like you are trying to commit some images. If these are '
4591 'non-test-only images, please make sure to read and apply the tips in '
4592 'https://chromium.googlesource.com/chromium/src/+/HEAD/docs/speed/'
4593 'binary_size/optimization_advice.md#optimizing-images\nThis check is '
4594 'FYI only and will not block your CL on the CQ.', image_paths))
4595 return errors
Mohamed Heikald048240a2019-11-12 16:57:374596
4597
Saagar Sanghavifceeaae2020-08-12 16:40:364598def ChecksAndroidSpecificOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504599 """Groups upload checks that target android code."""
4600 results = []
4601 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
4602 results.extend(_CheckAndroidDebuggableBuild(input_api, output_api))
4603 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
4604 results.extend(_CheckAndroidToastUsage(input_api, output_api))
4605 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
4606 results.extend(_CheckAndroidTestJUnitFrameworkImport(
4607 input_api, output_api))
4608 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
4609 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
4610 results.extend(_CheckAndroidXmlStyle(input_api, output_api, True))
4611 results.extend(_CheckNewImagesWarning(input_api, output_api))
4612 results.extend(_CheckAndroidNoBannedImports(input_api, output_api))
4613 results.extend(_CheckAndroidInfoBarDeprecation(input_api, output_api))
4614 return results
4615
Becky Zhou7c69b50992018-12-10 19:37:574616
Saagar Sanghavifceeaae2020-08-12 16:40:364617def ChecksAndroidSpecificOnCommit(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504618 """Groups commit checks that target android code."""
4619 results = []
4620 results.extend(_CheckAndroidXmlStyle(input_api, output_api, False))
4621 return results
dgnaa68d5e2015-06-10 10:08:224622
Chris Hall59f8d0c72020-05-01 07:31:194623# TODO(chrishall): could we additionally match on any path owned by
4624# ui/accessibility/OWNERS ?
4625_ACCESSIBILITY_PATHS = (
Bruce Dawson40fece62022-09-16 19:58:314626 r"^chrome/browser.*/accessibility/",
4627 r"^chrome/browser/extensions/api/automation.*/",
4628 r"^chrome/renderer/extensions/accessibility_.*",
4629 r"^chrome/tests/data/accessibility/",
Katie Dektar58ef07b2022-09-27 13:19:174630 r"^components/services/screen_ai/",
Bruce Dawson40fece62022-09-16 19:58:314631 r"^content/browser/accessibility/",
4632 r"^content/renderer/accessibility/",
4633 r"^content/tests/data/accessibility/",
4634 r"^extensions/renderer/api/automation/",
Katie Dektar58ef07b2022-09-27 13:19:174635 r"^services/accessibility/",
Bruce Dawson40fece62022-09-16 19:58:314636 r"^ui/accessibility/",
4637 r"^ui/views/accessibility/",
Chris Hall59f8d0c72020-05-01 07:31:194638)
4639
Saagar Sanghavifceeaae2020-08-12 16:40:364640def CheckAccessibilityRelnotesField(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504641 """Checks that commits to accessibility code contain an AX-Relnotes field in
4642 their commit message."""
Chris Hall59f8d0c72020-05-01 07:31:194643
Sam Maiera6e76d72022-02-11 21:43:504644 def FileFilter(affected_file):
4645 paths = _ACCESSIBILITY_PATHS
4646 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Chris Hall59f8d0c72020-05-01 07:31:194647
Sam Maiera6e76d72022-02-11 21:43:504648 # Only consider changes affecting accessibility paths.
4649 if not any(input_api.AffectedFiles(file_filter=FileFilter)):
4650 return []
Akihiro Ota08108e542020-05-20 15:30:534651
Sam Maiera6e76d72022-02-11 21:43:504652 # AX-Relnotes can appear in either the description or the footer.
4653 # When searching the description, require 'AX-Relnotes:' to appear at the
4654 # beginning of a line.
4655 ax_regex = input_api.re.compile('ax-relnotes[:=]')
4656 description_has_relnotes = any(
4657 ax_regex.match(line)
4658 for line in input_api.change.DescriptionText().lower().splitlines())
Chris Hall59f8d0c72020-05-01 07:31:194659
Sam Maiera6e76d72022-02-11 21:43:504660 footer_relnotes = input_api.change.GitFootersFromDescription().get(
4661 'AX-Relnotes', [])
4662 if description_has_relnotes or footer_relnotes:
4663 return []
Chris Hall59f8d0c72020-05-01 07:31:194664
Sam Maiera6e76d72022-02-11 21:43:504665 # TODO(chrishall): link to Relnotes documentation in message.
4666 message = (
4667 "Missing 'AX-Relnotes:' field required for accessibility changes"
4668 "\n please add 'AX-Relnotes: [release notes].' to describe any "
4669 "user-facing changes"
4670 "\n otherwise add 'AX-Relnotes: n/a.' if this change has no "
4671 "user-facing effects"
4672 "\n if this is confusing or annoying then please contact members "
4673 "of ui/accessibility/OWNERS.")
4674
4675 return [output_api.PresubmitNotifyResult(message)]
dgnaa68d5e2015-06-10 10:08:224676
Mark Schillacie5a0be22022-01-19 00:38:394677
4678_ACCESSIBILITY_EVENTS_TEST_PATH = (
Bruce Dawson40fece62022-09-16 19:58:314679 r"^content/test/data/accessibility/event/.*\.html",
Mark Schillacie5a0be22022-01-19 00:38:394680)
4681
4682_ACCESSIBILITY_TREE_TEST_PATH = (
Bruce Dawson40fece62022-09-16 19:58:314683 r"^content/test/data/accessibility/accname/.*\.html",
4684 r"^content/test/data/accessibility/aria/.*\.html",
4685 r"^content/test/data/accessibility/css/.*\.html",
4686 r"^content/test/data/accessibility/html/.*\.html",
Mark Schillacie5a0be22022-01-19 00:38:394687)
4688
4689_ACCESSIBILITY_ANDROID_EVENTS_TEST_PATH = (
Bruce Dawson40fece62022-09-16 19:58:314690 r"^.*/WebContentsAccessibilityEventsTest\.java",
Mark Schillacie5a0be22022-01-19 00:38:394691)
4692
4693_ACCESSIBILITY_ANDROID_TREE_TEST_PATH = (
Bruce Dawson40fece62022-09-16 19:58:314694 r"^.*/WebContentsAccessibilityTreeTest\.java",
Mark Schillacie5a0be22022-01-19 00:38:394695)
4696
4697def CheckAccessibilityEventsTestsAreIncludedForAndroid(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504698 """Checks that commits that include a newly added, renamed/moved, or deleted
4699 test in the DumpAccessibilityEventsTest suite also includes a corresponding
4700 change to the Android test."""
Mark Schillacie5a0be22022-01-19 00:38:394701
Sam Maiera6e76d72022-02-11 21:43:504702 def FilePathFilter(affected_file):
4703 paths = _ACCESSIBILITY_EVENTS_TEST_PATH
4704 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394705
Sam Maiera6e76d72022-02-11 21:43:504706 def AndroidFilePathFilter(affected_file):
4707 paths = _ACCESSIBILITY_ANDROID_EVENTS_TEST_PATH
4708 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394709
Sam Maiera6e76d72022-02-11 21:43:504710 # Only consider changes in the events test data path with html type.
4711 if not any(
4712 input_api.AffectedFiles(include_deletes=True,
4713 file_filter=FilePathFilter)):
4714 return []
Mark Schillacie5a0be22022-01-19 00:38:394715
Sam Maiera6e76d72022-02-11 21:43:504716 # If the commit contains any change to the Android test file, ignore.
4717 if any(
4718 input_api.AffectedFiles(include_deletes=True,
4719 file_filter=AndroidFilePathFilter)):
4720 return []
Mark Schillacie5a0be22022-01-19 00:38:394721
Sam Maiera6e76d72022-02-11 21:43:504722 # Only consider changes that are adding/renaming or deleting a file
4723 message = []
4724 for f in input_api.AffectedFiles(include_deletes=True,
4725 file_filter=FilePathFilter):
4726 if f.Action() == 'A' or f.Action() == 'D':
4727 message = (
4728 "It appears that you are adding, renaming or deleting"
4729 "\na dump_accessibility_events* test, but have not included"
4730 "\na corresponding change for Android."
4731 "\nPlease include (or remove) the test from:"
4732 "\n content/public/android/javatests/src/org/chromium/"
4733 "content/browser/accessibility/"
4734 "WebContentsAccessibilityEventsTest.java"
4735 "\nIf this message is confusing or annoying, please contact"
4736 "\nmembers of ui/accessibility/OWNERS.")
Mark Schillacie5a0be22022-01-19 00:38:394737
Sam Maiera6e76d72022-02-11 21:43:504738 # If no message was set, return empty.
4739 if not len(message):
4740 return []
4741
4742 return [output_api.PresubmitPromptWarning(message)]
4743
Mark Schillacie5a0be22022-01-19 00:38:394744
4745def CheckAccessibilityTreeTestsAreIncludedForAndroid(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504746 """Checks that commits that include a newly added, renamed/moved, or deleted
4747 test in the DumpAccessibilityTreeTest suite also includes a corresponding
4748 change to the Android test."""
Mark Schillacie5a0be22022-01-19 00:38:394749
Sam Maiera6e76d72022-02-11 21:43:504750 def FilePathFilter(affected_file):
4751 paths = _ACCESSIBILITY_TREE_TEST_PATH
4752 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394753
Sam Maiera6e76d72022-02-11 21:43:504754 def AndroidFilePathFilter(affected_file):
4755 paths = _ACCESSIBILITY_ANDROID_TREE_TEST_PATH
4756 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394757
Sam Maiera6e76d72022-02-11 21:43:504758 # Only consider changes in the various tree test data paths with html type.
4759 if not any(
4760 input_api.AffectedFiles(include_deletes=True,
4761 file_filter=FilePathFilter)):
4762 return []
Mark Schillacie5a0be22022-01-19 00:38:394763
Sam Maiera6e76d72022-02-11 21:43:504764 # If the commit contains any change to the Android test file, ignore.
4765 if any(
4766 input_api.AffectedFiles(include_deletes=True,
4767 file_filter=AndroidFilePathFilter)):
4768 return []
Mark Schillacie5a0be22022-01-19 00:38:394769
Sam Maiera6e76d72022-02-11 21:43:504770 # Only consider changes that are adding/renaming or deleting a file
4771 message = []
4772 for f in input_api.AffectedFiles(include_deletes=True,
4773 file_filter=FilePathFilter):
4774 if f.Action() == 'A' or f.Action() == 'D':
4775 message = (
4776 "It appears that you are adding, renaming or deleting"
4777 "\na dump_accessibility_tree* test, but have not included"
4778 "\na corresponding change for Android."
4779 "\nPlease include (or remove) the test from:"
4780 "\n content/public/android/javatests/src/org/chromium/"
4781 "content/browser/accessibility/"
4782 "WebContentsAccessibilityTreeTest.java"
4783 "\nIf this message is confusing or annoying, please contact"
4784 "\nmembers of ui/accessibility/OWNERS.")
Mark Schillacie5a0be22022-01-19 00:38:394785
Sam Maiera6e76d72022-02-11 21:43:504786 # If no message was set, return empty.
4787 if not len(message):
4788 return []
4789
4790 return [output_api.PresubmitPromptWarning(message)]
Mark Schillacie5a0be22022-01-19 00:38:394791
4792
seanmccullough4a9356252021-04-08 19:54:094793# string pattern, sequence of strings to show when pattern matches,
4794# error flag. True if match is a presubmit error, otherwise it's a warning.
4795_NON_INCLUSIVE_TERMS = (
4796 (
4797 # Note that \b pattern in python re is pretty particular. In this
4798 # regexp, 'class WhiteList ...' will match, but 'class FooWhiteList
4799 # ...' will not. This may require some tweaking to catch these cases
4800 # without triggering a lot of false positives. Leaving it naive and
4801 # less matchy for now.
seanmccullough56d1e3cf2021-12-03 18:18:324802 r'/\b(?i)((black|white)list|master|slave)\b', # nocheck
seanmccullough4a9356252021-04-08 19:54:094803 (
4804 'Please don\'t use blacklist, whitelist, ' # nocheck
4805 'or slave in your', # nocheck
4806 'code and make every effort to use other terms. Using "// nocheck"',
4807 '"# nocheck" or "<!-- nocheck -->"',
4808 'at the end of the offending line will bypass this PRESUBMIT error',
4809 'but avoid using this whenever possible. Reach out to',
4810 '[email protected] if you have questions'),
4811 True),)
4812
Saagar Sanghavifceeaae2020-08-12 16:40:364813def ChecksCommon(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504814 """Checks common to both upload and commit."""
4815 results = []
Eric Boren6fd2b932018-01-25 15:05:084816 results.extend(
Sam Maiera6e76d72022-02-11 21:43:504817 input_api.canned_checks.PanProjectChecks(
4818 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:084819
Sam Maiera6e76d72022-02-11 21:43:504820 author = input_api.change.author_email
4821 if author and author not in _KNOWN_ROBOTS:
4822 results.extend(
4823 input_api.canned_checks.CheckAuthorizedAuthor(
4824 input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:244825
Sam Maiera6e76d72022-02-11 21:43:504826 results.extend(
4827 input_api.canned_checks.CheckChangeHasNoTabs(
4828 input_api,
4829 output_api,
4830 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
4831 results.extend(
4832 input_api.RunTests(
4833 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Edward Lesmesce51df52020-08-04 22:10:174834
Bruce Dawsonc8054482022-03-28 15:33:374835 dirmd = 'dirmd.bat' if input_api.is_windows else 'dirmd'
Sam Maiera6e76d72022-02-11 21:43:504836 dirmd_bin = input_api.os_path.join(input_api.PresubmitLocalPath(),
Bruce Dawsonc8054482022-03-28 15:33:374837 'third_party', 'depot_tools', dirmd)
Sam Maiera6e76d72022-02-11 21:43:504838 results.extend(
4839 input_api.RunTests(
4840 input_api.canned_checks.CheckDirMetadataFormat(
4841 input_api, output_api, dirmd_bin)))
4842 results.extend(
4843 input_api.canned_checks.CheckOwnersDirMetadataExclusive(
4844 input_api, output_api))
4845 results.extend(
4846 input_api.canned_checks.CheckNoNewMetadataInOwners(
4847 input_api, output_api))
4848 results.extend(
4849 input_api.canned_checks.CheckInclusiveLanguage(
4850 input_api,
4851 output_api,
4852 excluded_directories_relative_path=[
4853 'infra', 'inclusive_language_presubmit_exempt_dirs.txt'
4854 ],
4855 non_inclusive_terms=_NON_INCLUSIVE_TERMS))
Dirk Prankee3c9c62d2021-05-18 18:35:594856
Aleksey Khoroshilov2978c942022-06-13 16:14:124857 presubmit_py_filter = lambda f: input_api.FilterSourceFile(
Bruce Dawson696963f2022-09-13 01:15:474858 f, files_to_check=[r'.*PRESUBMIT\.py$'])
Aleksey Khoroshilov2978c942022-06-13 16:14:124859 for f in input_api.AffectedFiles(include_deletes=False,
4860 file_filter=presubmit_py_filter):
4861 full_path = input_api.os_path.dirname(f.AbsoluteLocalPath())
4862 test_file = input_api.os_path.join(full_path, 'PRESUBMIT_test.py')
4863 # The PRESUBMIT.py file (and the directory containing it) might have
4864 # been affected by being moved or removed, so only try to run the tests
4865 # if they still exist.
4866 if not input_api.os_path.exists(test_file):
4867 continue
Sam Maiera6e76d72022-02-11 21:43:504868
Aleksey Khoroshilov2978c942022-06-13 16:14:124869 use_python3 = False
4870 with open(f.LocalPath()) as fp:
4871 use_python3 = any(
4872 line.startswith('USE_PYTHON3 = True')
4873 for line in fp.readlines())
4874
4875 results.extend(
4876 input_api.canned_checks.RunUnitTestsInDirectory(
4877 input_api,
4878 output_api,
4879 full_path,
4880 files_to_check=[r'^PRESUBMIT_test\.py$'],
4881 run_on_python2=not use_python3,
4882 run_on_python3=use_python3,
4883 skip_shebang_check=True))
Sam Maiera6e76d72022-02-11 21:43:504884 return results
[email protected]1f7b4172010-01-28 01:17:344885
[email protected]b337cb5b2011-01-23 21:24:054886
Saagar Sanghavifceeaae2020-08-12 16:40:364887def CheckPatchFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504888 problems = [
4889 f.LocalPath() for f in input_api.AffectedFiles()
4890 if f.LocalPath().endswith(('.orig', '.rej'))
4891 ]
4892 # Cargo.toml.orig files are part of third-party crates downloaded from
4893 # crates.io and should be included.
4894 problems = [f for f in problems if not f.endswith('Cargo.toml.orig')]
4895 if problems:
4896 return [
4897 output_api.PresubmitError("Don't commit .rej and .orig files.",
4898 problems)
4899 ]
4900 else:
4901 return []
[email protected]b8079ae4a2012-12-05 19:56:494902
4903
Saagar Sanghavifceeaae2020-08-12 16:40:364904def CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504905 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
4906 macro_re = input_api.re.compile(
4907 r'^\s*#(el)?if.*\bdefined\(((COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
4908 include_re = input_api.re.compile(r'^#include\s+"build/build_config.h"',
4909 input_api.re.MULTILINE)
4910 extension_re = input_api.re.compile(r'\.[a-z]+$')
4911 errors = []
Bruce Dawsonf7679202022-08-09 20:24:004912 config_h_file = input_api.os_path.join('build', 'build_config.h')
Sam Maiera6e76d72022-02-11 21:43:504913 for f in input_api.AffectedFiles(include_deletes=False):
Bruce Dawsonf7679202022-08-09 20:24:004914 # The build-config macros are allowed to be used in build_config.h
4915 # without including itself.
4916 if f.LocalPath() == config_h_file:
4917 continue
Sam Maiera6e76d72022-02-11 21:43:504918 if not f.LocalPath().endswith(
4919 ('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
4920 continue
4921 found_line_number = None
4922 found_macro = None
4923 all_lines = input_api.ReadFile(f, 'r').splitlines()
4924 for line_num, line in enumerate(all_lines):
4925 match = macro_re.search(line)
4926 if match:
4927 found_line_number = line_num
4928 found_macro = match.group(2)
4929 break
4930 if not found_line_number:
4931 continue
Kent Tamura5a8755d2017-06-29 23:37:074932
Sam Maiera6e76d72022-02-11 21:43:504933 found_include_line = -1
4934 for line_num, line in enumerate(all_lines):
4935 if include_re.search(line):
4936 found_include_line = line_num
4937 break
4938 if found_include_line >= 0 and found_include_line < found_line_number:
4939 continue
Kent Tamura5a8755d2017-06-29 23:37:074940
Sam Maiera6e76d72022-02-11 21:43:504941 if not f.LocalPath().endswith('.h'):
4942 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
4943 try:
4944 content = input_api.ReadFile(primary_header_path, 'r')
4945 if include_re.search(content):
4946 continue
4947 except IOError:
4948 pass
4949 errors.append('%s:%d %s macro is used without first including build/'
4950 'build_config.h.' %
4951 (f.LocalPath(), found_line_number, found_macro))
4952 if errors:
4953 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4954 return []
Kent Tamura5a8755d2017-06-29 23:37:074955
4956
Lei Zhang1c12a22f2021-05-12 11:28:454957def CheckForSuperfluousStlIncludesInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504958 stl_include_re = input_api.re.compile(r'^#include\s+<('
4959 r'algorithm|'
4960 r'array|'
4961 r'limits|'
4962 r'list|'
4963 r'map|'
4964 r'memory|'
4965 r'queue|'
4966 r'set|'
4967 r'string|'
4968 r'unordered_map|'
4969 r'unordered_set|'
4970 r'utility|'
4971 r'vector)>')
4972 std_namespace_re = input_api.re.compile(r'std::')
4973 errors = []
4974 for f in input_api.AffectedFiles():
4975 if not _IsCPlusPlusHeaderFile(input_api, f.LocalPath()):
4976 continue
Lei Zhang1c12a22f2021-05-12 11:28:454977
Sam Maiera6e76d72022-02-11 21:43:504978 uses_std_namespace = False
4979 has_stl_include = False
4980 for line in f.NewContents():
4981 if has_stl_include and uses_std_namespace:
4982 break
Lei Zhang1c12a22f2021-05-12 11:28:454983
Sam Maiera6e76d72022-02-11 21:43:504984 if not has_stl_include and stl_include_re.search(line):
4985 has_stl_include = True
4986 continue
Lei Zhang1c12a22f2021-05-12 11:28:454987
Bruce Dawson4a5579a2022-04-08 17:11:364988 if not uses_std_namespace and (std_namespace_re.search(line)
4989 or 'no-std-usage-because-pch-file' in line):
Sam Maiera6e76d72022-02-11 21:43:504990 uses_std_namespace = True
4991 continue
Lei Zhang1c12a22f2021-05-12 11:28:454992
Sam Maiera6e76d72022-02-11 21:43:504993 if has_stl_include and not uses_std_namespace:
4994 errors.append(
4995 '%s: Includes STL header(s) but does not reference std::' %
4996 f.LocalPath())
4997 if errors:
4998 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4999 return []
Lei Zhang1c12a22f2021-05-12 11:28:455000
5001
Xiaohan Wang42d96c22022-01-20 17:23:115002def _CheckForDeprecatedOSMacrosInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:505003 """Check for sensible looking, totally invalid OS macros."""
5004 preprocessor_statement = input_api.re.compile(r'^\s*#')
5005 os_macro = input_api.re.compile(r'defined\(OS_([^)]+)\)')
5006 results = []
5007 for lnum, line in f.ChangedContents():
5008 if preprocessor_statement.search(line):
5009 for match in os_macro.finditer(line):
5010 results.append(
5011 ' %s:%d: %s' %
5012 (f.LocalPath(), lnum, 'defined(OS_' + match.group(1) +
5013 ') -> BUILDFLAG(IS_' + match.group(1) + ')'))
5014 return results
[email protected]b00342e7f2013-03-26 16:21:545015
5016
Xiaohan Wang42d96c22022-01-20 17:23:115017def CheckForDeprecatedOSMacros(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505018 """Check all affected files for invalid OS macros."""
5019 bad_macros = []
Bruce Dawsonf7679202022-08-09 20:24:005020 # The OS_ macros are allowed to be used in build/build_config.h.
5021 config_h_file = input_api.os_path.join('build', 'build_config.h')
Sam Maiera6e76d72022-02-11 21:43:505022 for f in input_api.AffectedSourceFiles(None):
Bruce Dawsonf7679202022-08-09 20:24:005023 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')) \
5024 and f.LocalPath() != config_h_file:
Sam Maiera6e76d72022-02-11 21:43:505025 bad_macros.extend(_CheckForDeprecatedOSMacrosInFile(input_api, f))
[email protected]b00342e7f2013-03-26 16:21:545026
Sam Maiera6e76d72022-02-11 21:43:505027 if not bad_macros:
5028 return []
[email protected]b00342e7f2013-03-26 16:21:545029
Sam Maiera6e76d72022-02-11 21:43:505030 return [
5031 output_api.PresubmitError(
5032 'OS macros have been deprecated. Please use BUILDFLAGs instead (still '
5033 'defined in build_config.h):', bad_macros)
5034 ]
[email protected]b00342e7f2013-03-26 16:21:545035
lliabraa35bab3932014-10-01 12:16:445036
5037def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:505038 """Check all affected files for invalid "if defined" macros."""
5039 ALWAYS_DEFINED_MACROS = (
5040 "TARGET_CPU_PPC",
5041 "TARGET_CPU_PPC64",
5042 "TARGET_CPU_68K",
5043 "TARGET_CPU_X86",
5044 "TARGET_CPU_ARM",
5045 "TARGET_CPU_MIPS",
5046 "TARGET_CPU_SPARC",
5047 "TARGET_CPU_ALPHA",
5048 "TARGET_IPHONE_SIMULATOR",
5049 "TARGET_OS_EMBEDDED",
5050 "TARGET_OS_IPHONE",
5051 "TARGET_OS_MAC",
5052 "TARGET_OS_UNIX",
5053 "TARGET_OS_WIN32",
5054 )
5055 ifdef_macro = input_api.re.compile(
5056 r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
5057 results = []
5058 for lnum, line in f.ChangedContents():
5059 for match in ifdef_macro.finditer(line):
5060 if match.group(1) in ALWAYS_DEFINED_MACROS:
5061 always_defined = ' %s is always defined. ' % match.group(1)
5062 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
5063 results.append(
5064 ' %s:%d %s\n\t%s' %
5065 (f.LocalPath(), lnum, always_defined, did_you_mean))
5066 return results
lliabraa35bab3932014-10-01 12:16:445067
5068
Saagar Sanghavifceeaae2020-08-12 16:40:365069def CheckForInvalidIfDefinedMacros(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505070 """Check all affected files for invalid "if defined" macros."""
5071 bad_macros = []
5072 skipped_paths = ['third_party/sqlite/', 'third_party/abseil-cpp/']
5073 for f in input_api.AffectedFiles():
5074 if any([f.LocalPath().startswith(path) for path in skipped_paths]):
5075 continue
5076 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
5077 bad_macros.extend(
5078 _CheckForInvalidIfDefinedMacrosInFile(input_api, f))
lliabraa35bab3932014-10-01 12:16:445079
Sam Maiera6e76d72022-02-11 21:43:505080 if not bad_macros:
5081 return []
lliabraa35bab3932014-10-01 12:16:445082
Sam Maiera6e76d72022-02-11 21:43:505083 return [
5084 output_api.PresubmitError(
5085 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
5086 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
5087 bad_macros)
5088 ]
lliabraa35bab3932014-10-01 12:16:445089
5090
Saagar Sanghavifceeaae2020-08-12 16:40:365091def CheckForIPCRules(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505092 """Check for same IPC rules described in
5093 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
5094 """
5095 base_pattern = r'IPC_ENUM_TRAITS\('
5096 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
5097 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
mlamouria82272622014-09-16 18:45:045098
Sam Maiera6e76d72022-02-11 21:43:505099 problems = []
5100 for f in input_api.AffectedSourceFiles(None):
5101 local_path = f.LocalPath()
5102 if not local_path.endswith('.h'):
5103 continue
5104 for line_number, line in f.ChangedContents():
5105 if inclusion_pattern.search(
5106 line) and not comment_pattern.search(line):
5107 problems.append('%s:%d\n %s' %
5108 (local_path, line_number, line.strip()))
mlamouria82272622014-09-16 18:45:045109
Sam Maiera6e76d72022-02-11 21:43:505110 if problems:
5111 return [
5112 output_api.PresubmitPromptWarning(_IPC_ENUM_TRAITS_DEPRECATED,
5113 problems)
5114 ]
5115 else:
5116 return []
mlamouria82272622014-09-16 18:45:045117
[email protected]b00342e7f2013-03-26 16:21:545118
Saagar Sanghavifceeaae2020-08-12 16:40:365119def CheckForLongPathnames(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505120 """Check to make sure no files being submitted have long paths.
5121 This causes issues on Windows.
5122 """
5123 problems = []
5124 for f in input_api.AffectedTestableFiles():
5125 local_path = f.LocalPath()
5126 # Windows has a path limit of 260 characters. Limit path length to 200 so
5127 # that we have some extra for the prefix on dev machines and the bots.
5128 if len(local_path) > 200:
5129 problems.append(local_path)
Stephen Martinis97a394142018-06-07 23:06:055130
Sam Maiera6e76d72022-02-11 21:43:505131 if problems:
5132 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
5133 else:
5134 return []
Stephen Martinis97a394142018-06-07 23:06:055135
5136
Saagar Sanghavifceeaae2020-08-12 16:40:365137def CheckForIncludeGuards(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505138 """Check that header files have proper guards against multiple inclusion.
5139 If a file should not have such guards (and it probably should) then it
Bruce Dawson4a5579a2022-04-08 17:11:365140 should include the string "no-include-guard-because-multiply-included" or
5141 "no-include-guard-because-pch-file".
Sam Maiera6e76d72022-02-11 21:43:505142 """
Daniel Bratell8ba52722018-03-02 16:06:145143
Sam Maiera6e76d72022-02-11 21:43:505144 def is_chromium_header_file(f):
5145 # We only check header files under the control of the Chromium
5146 # project. That is, those outside third_party apart from
5147 # third_party/blink.
5148 # We also exclude *_message_generator.h headers as they use
5149 # include guards in a special, non-typical way.
5150 file_with_path = input_api.os_path.normpath(f.LocalPath())
5151 return (file_with_path.endswith('.h')
5152 and not file_with_path.endswith('_message_generator.h')
Bruce Dawson4c4c2922022-05-02 18:07:335153 and not file_with_path.endswith('com_imported_mstscax.h')
Sam Maiera6e76d72022-02-11 21:43:505154 and (not file_with_path.startswith('third_party')
5155 or file_with_path.startswith(
5156 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:145157
Sam Maiera6e76d72022-02-11 21:43:505158 def replace_special_with_underscore(string):
5159 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:145160
Sam Maiera6e76d72022-02-11 21:43:505161 errors = []
Daniel Bratell8ba52722018-03-02 16:06:145162
Sam Maiera6e76d72022-02-11 21:43:505163 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
5164 guard_name = None
5165 guard_line_number = None
5166 seen_guard_end = False
Daniel Bratell8ba52722018-03-02 16:06:145167
Sam Maiera6e76d72022-02-11 21:43:505168 file_with_path = input_api.os_path.normpath(f.LocalPath())
5169 base_file_name = input_api.os_path.splitext(
5170 input_api.os_path.basename(file_with_path))[0]
5171 upper_base_file_name = base_file_name.upper()
Daniel Bratell8ba52722018-03-02 16:06:145172
Sam Maiera6e76d72022-02-11 21:43:505173 expected_guard = replace_special_with_underscore(
5174 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:145175
Sam Maiera6e76d72022-02-11 21:43:505176 # For "path/elem/file_name.h" we should really only accept
5177 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
5178 # are too many (1000+) files with slight deviations from the
5179 # coding style. The most important part is that the include guard
5180 # is there, and that it's unique, not the name so this check is
5181 # forgiving for existing files.
5182 #
5183 # As code becomes more uniform, this could be made stricter.
Daniel Bratell8ba52722018-03-02 16:06:145184
Sam Maiera6e76d72022-02-11 21:43:505185 guard_name_pattern_list = [
5186 # Anything with the right suffix (maybe with an extra _).
5187 r'\w+_H__?',
Daniel Bratell8ba52722018-03-02 16:06:145188
Sam Maiera6e76d72022-02-11 21:43:505189 # To cover include guards with old Blink style.
5190 r'\w+_h',
Daniel Bratell8ba52722018-03-02 16:06:145191
Sam Maiera6e76d72022-02-11 21:43:505192 # Anything including the uppercase name of the file.
5193 r'\w*' + input_api.re.escape(
5194 replace_special_with_underscore(upper_base_file_name)) +
5195 r'\w*',
5196 ]
5197 guard_name_pattern = '|'.join(guard_name_pattern_list)
5198 guard_pattern = input_api.re.compile(r'#ifndef\s+(' +
5199 guard_name_pattern + ')')
Daniel Bratell8ba52722018-03-02 16:06:145200
Sam Maiera6e76d72022-02-11 21:43:505201 for line_number, line in enumerate(f.NewContents()):
Bruce Dawson4a5579a2022-04-08 17:11:365202 if ('no-include-guard-because-multiply-included' in line
5203 or 'no-include-guard-because-pch-file' in line):
Sam Maiera6e76d72022-02-11 21:43:505204 guard_name = 'DUMMY' # To not trigger check outside the loop.
5205 break
Daniel Bratell8ba52722018-03-02 16:06:145206
Sam Maiera6e76d72022-02-11 21:43:505207 if guard_name is None:
5208 match = guard_pattern.match(line)
5209 if match:
5210 guard_name = match.group(1)
5211 guard_line_number = line_number
Daniel Bratell8ba52722018-03-02 16:06:145212
Sam Maiera6e76d72022-02-11 21:43:505213 # We allow existing files to use include guards whose names
5214 # don't match the chromium style guide, but new files should
5215 # get it right.
Bruce Dawson6cc154e2022-04-12 20:39:495216 if guard_name != expected_guard:
Bruce Dawson95eb7562022-09-14 15:27:165217 if f.Action() == 'A': # If file was just 'A'dded
Sam Maiera6e76d72022-02-11 21:43:505218 errors.append(
5219 output_api.PresubmitPromptWarning(
5220 'Header using the wrong include guard name %s'
5221 % guard_name, [
5222 '%s:%d' %
5223 (f.LocalPath(), line_number + 1)
5224 ], 'Expected: %r\nFound: %r' %
5225 (expected_guard, guard_name)))
5226 else:
5227 # The line after #ifndef should have a #define of the same name.
5228 if line_number == guard_line_number + 1:
5229 expected_line = '#define %s' % guard_name
5230 if line != expected_line:
5231 errors.append(
5232 output_api.PresubmitPromptWarning(
5233 'Missing "%s" for include guard' %
5234 expected_line,
5235 ['%s:%d' % (f.LocalPath(), line_number + 1)],
5236 'Expected: %r\nGot: %r' %
5237 (expected_line, line)))
Daniel Bratell8ba52722018-03-02 16:06:145238
Sam Maiera6e76d72022-02-11 21:43:505239 if not seen_guard_end and line == '#endif // %s' % guard_name:
5240 seen_guard_end = True
5241 elif seen_guard_end:
5242 if line.strip() != '':
5243 errors.append(
5244 output_api.PresubmitPromptWarning(
5245 'Include guard %s not covering the whole file'
5246 % (guard_name), [f.LocalPath()]))
5247 break # Nothing else to check and enough to warn once.
Daniel Bratell8ba52722018-03-02 16:06:145248
Sam Maiera6e76d72022-02-11 21:43:505249 if guard_name is None:
5250 errors.append(
5251 output_api.PresubmitPromptWarning(
Bruce Dawson32114b62022-04-11 16:45:495252 'Missing include guard in %s\n'
Sam Maiera6e76d72022-02-11 21:43:505253 'Recommended name: %s\n'
5254 'This check can be disabled by having the string\n'
Bruce Dawson4a5579a2022-04-08 17:11:365255 '"no-include-guard-because-multiply-included" or\n'
5256 '"no-include-guard-because-pch-file" in the header.'
Sam Maiera6e76d72022-02-11 21:43:505257 % (f.LocalPath(), expected_guard)))
5258
5259 return errors
Daniel Bratell8ba52722018-03-02 16:06:145260
5261
Saagar Sanghavifceeaae2020-08-12 16:40:365262def CheckForWindowsLineEndings(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505263 """Check source code and known ascii text files for Windows style line
5264 endings.
5265 """
Bruce Dawson5efbdc652022-04-11 19:29:515266 known_text_files = r'.*\.(txt|html|htm|py|gyp|gypi|gn|isolate|icon)$'
mostynbb639aca52015-01-07 20:31:235267
Sam Maiera6e76d72022-02-11 21:43:505268 file_inclusion_pattern = (known_text_files,
5269 r'.+%s' % _IMPLEMENTATION_EXTENSIONS,
5270 r'.+%s' % _HEADER_EXTENSIONS)
mostynbb639aca52015-01-07 20:31:235271
Sam Maiera6e76d72022-02-11 21:43:505272 problems = []
5273 source_file_filter = lambda f: input_api.FilterSourceFile(
5274 f, files_to_check=file_inclusion_pattern, files_to_skip=None)
5275 for f in input_api.AffectedSourceFiles(source_file_filter):
Bruce Dawson5efbdc652022-04-11 19:29:515276 # Ignore test files that contain crlf intentionally.
5277 if f.LocalPath().endswith('crlf.txt'):
Daniel Chenga37c03db2022-05-12 17:20:345278 continue
Sam Maiera6e76d72022-02-11 21:43:505279 include_file = False
5280 for line in input_api.ReadFile(f, 'r').splitlines(True):
5281 if line.endswith('\r\n'):
5282 include_file = True
5283 if include_file:
5284 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:235285
Sam Maiera6e76d72022-02-11 21:43:505286 if problems:
5287 return [
5288 output_api.PresubmitPromptWarning(
5289 'Are you sure that you want '
5290 'these files to contain Windows style line endings?\n' +
5291 '\n'.join(problems))
5292 ]
mostynbb639aca52015-01-07 20:31:235293
Sam Maiera6e76d72022-02-11 21:43:505294 return []
5295
mostynbb639aca52015-01-07 20:31:235296
Evan Stade6cfc964c12021-05-18 20:21:165297def CheckIconFilesForLicenseHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505298 """Check that .icon files (which are fragments of C++) have license headers.
5299 """
Evan Stade6cfc964c12021-05-18 20:21:165300
Sam Maiera6e76d72022-02-11 21:43:505301 icon_files = (r'.*\.icon$', )
Evan Stade6cfc964c12021-05-18 20:21:165302
Sam Maiera6e76d72022-02-11 21:43:505303 icons = lambda x: input_api.FilterSourceFile(x, files_to_check=icon_files)
5304 return input_api.canned_checks.CheckLicense(input_api,
5305 output_api,
5306 source_file_filter=icons)
5307
Evan Stade6cfc964c12021-05-18 20:21:165308
Jose Magana2b456f22021-03-09 23:26:405309def CheckForUseOfChromeAppsDeprecations(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505310 """Check source code for use of Chrome App technologies being
5311 deprecated.
5312 """
Jose Magana2b456f22021-03-09 23:26:405313
Sam Maiera6e76d72022-02-11 21:43:505314 def _CheckForDeprecatedTech(input_api,
5315 output_api,
5316 detection_list,
5317 files_to_check=None,
5318 files_to_skip=None):
Jose Magana2b456f22021-03-09 23:26:405319
Sam Maiera6e76d72022-02-11 21:43:505320 if (files_to_check or files_to_skip):
5321 source_file_filter = lambda f: input_api.FilterSourceFile(
5322 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
5323 else:
5324 source_file_filter = None
5325
5326 problems = []
5327
5328 for f in input_api.AffectedSourceFiles(source_file_filter):
5329 if f.Action() == 'D':
5330 continue
5331 for _, line in f.ChangedContents():
5332 if any(detect in line for detect in detection_list):
5333 problems.append(f.LocalPath())
5334
5335 return problems
5336
5337 # to avoid this presubmit script triggering warnings
5338 files_to_skip = ['PRESUBMIT.py', 'PRESUBMIT_test.py']
Jose Magana2b456f22021-03-09 23:26:405339
5340 problems = []
5341
Sam Maiera6e76d72022-02-11 21:43:505342 # NMF: any files with extensions .nmf or NMF
5343 _NMF_FILES = r'\.(nmf|NMF)$'
5344 problems += _CheckForDeprecatedTech(
5345 input_api,
5346 output_api,
5347 detection_list=[''], # any change to the file will trigger warning
5348 files_to_check=[r'.+%s' % _NMF_FILES])
Jose Magana2b456f22021-03-09 23:26:405349
Sam Maiera6e76d72022-02-11 21:43:505350 # MANIFEST: any manifest.json that in its diff includes "app":
5351 _MANIFEST_FILES = r'(manifest\.json)$'
5352 problems += _CheckForDeprecatedTech(
5353 input_api,
5354 output_api,
5355 detection_list=['"app":'],
5356 files_to_check=[r'.*%s' % _MANIFEST_FILES])
Jose Magana2b456f22021-03-09 23:26:405357
Sam Maiera6e76d72022-02-11 21:43:505358 # NaCl / PNaCl: any file that in its diff contains the strings in the list
5359 problems += _CheckForDeprecatedTech(
5360 input_api,
5361 output_api,
5362 detection_list=['config=nacl', 'enable-nacl', 'cpu=pnacl', 'nacl_io'],
Bruce Dawson40fece62022-09-16 19:58:315363 files_to_skip=files_to_skip + [r"^native_client_sdk/"])
Jose Magana2b456f22021-03-09 23:26:405364
Gao Shenga79ebd42022-08-08 17:25:595365 # PPAPI: any C/C++ file that in its diff includes a ppapi library
Sam Maiera6e76d72022-02-11 21:43:505366 problems += _CheckForDeprecatedTech(
5367 input_api,
5368 output_api,
5369 detection_list=['#include "ppapi', '#include <ppapi'],
5370 files_to_check=(r'.+%s' % _HEADER_EXTENSIONS,
5371 r'.+%s' % _IMPLEMENTATION_EXTENSIONS),
Bruce Dawson40fece62022-09-16 19:58:315372 files_to_skip=[r"^ppapi/"])
Jose Magana2b456f22021-03-09 23:26:405373
Sam Maiera6e76d72022-02-11 21:43:505374 if problems:
5375 return [
5376 output_api.PresubmitPromptWarning(
5377 'You are adding/modifying code'
5378 'related to technologies which will soon be deprecated (Chrome Apps, NaCl,'
5379 ' PNaCl, PPAPI). See this blog post for more details:\n'
5380 'https://blog.chromium.org/2020/08/changes-to-chrome-app-support-timeline.html\n'
5381 'and this documentation for options to replace these technologies:\n'
5382 'https://developer.chrome.com/docs/apps/migration/\n' +
5383 '\n'.join(problems))
5384 ]
Jose Magana2b456f22021-03-09 23:26:405385
Sam Maiera6e76d72022-02-11 21:43:505386 return []
Jose Magana2b456f22021-03-09 23:26:405387
mostynbb639aca52015-01-07 20:31:235388
Saagar Sanghavifceeaae2020-08-12 16:40:365389def CheckSyslogUseWarningOnUpload(input_api, output_api, src_file_filter=None):
Sam Maiera6e76d72022-02-11 21:43:505390 """Checks that all source files use SYSLOG properly."""
5391 syslog_files = []
5392 for f in input_api.AffectedSourceFiles(src_file_filter):
5393 for line_number, line in f.ChangedContents():
5394 if 'SYSLOG' in line:
5395 syslog_files.append(f.LocalPath() + ':' + str(line_number))
pastarmovj032ba5bc2017-01-12 10:41:565396
Sam Maiera6e76d72022-02-11 21:43:505397 if syslog_files:
5398 return [
5399 output_api.PresubmitPromptWarning(
5400 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
5401 ' calls.\nFiles to check:\n',
5402 items=syslog_files)
5403 ]
5404 return []
pastarmovj89f7ee12016-09-20 14:58:135405
5406
[email protected]1f7b4172010-01-28 01:17:345407def CheckChangeOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505408 if input_api.version < [2, 0, 0]:
5409 return [
5410 output_api.PresubmitError(
5411 "Your depot_tools is out of date. "
5412 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
5413 "but your version is %d.%d.%d" % tuple(input_api.version))
5414 ]
5415 results = []
5416 results.extend(
5417 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
5418 return results
[email protected]ca8d19842009-02-19 16:33:125419
5420
5421def CheckChangeOnCommit(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505422 if input_api.version < [2, 0, 0]:
5423 return [
5424 output_api.PresubmitError(
5425 "Your depot_tools is out of date. "
5426 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
5427 "but your version is %d.%d.%d" % tuple(input_api.version))
5428 ]
Saagar Sanghavifceeaae2020-08-12 16:40:365429
Sam Maiera6e76d72022-02-11 21:43:505430 results = []
5431 # Make sure the tree is 'open'.
5432 results.extend(
5433 input_api.canned_checks.CheckTreeIsOpen(
5434 input_api,
5435 output_api,
5436 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:275437
Sam Maiera6e76d72022-02-11 21:43:505438 results.extend(
5439 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
5440 results.extend(
5441 input_api.canned_checks.CheckChangeHasBugField(input_api, output_api))
5442 results.extend(
5443 input_api.canned_checks.CheckChangeHasNoUnwantedTags(
5444 input_api, output_api))
Sam Maiera6e76d72022-02-11 21:43:505445 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145446
5447
Saagar Sanghavifceeaae2020-08-12 16:40:365448def CheckStrings(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505449 """Check string ICU syntax validity and if translation screenshots exist."""
5450 # Skip translation screenshots check if a SkipTranslationScreenshotsCheck
5451 # footer is set to true.
5452 git_footers = input_api.change.GitFootersFromDescription()
5453 skip_screenshot_check_footer = [
5454 footer.lower() for footer in git_footers.get(
5455 u'Skip-Translation-Screenshots-Check', [])
5456 ]
5457 run_screenshot_check = u'true' not in skip_screenshot_check_footer
Edward Lesmesf7c5c6d2020-05-14 23:30:025458
Sam Maiera6e76d72022-02-11 21:43:505459 import os
5460 import re
5461 import sys
5462 from io import StringIO
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145463
Sam Maiera6e76d72022-02-11 21:43:505464 new_or_added_paths = set(f.LocalPath() for f in input_api.AffectedFiles()
5465 if (f.Action() == 'A' or f.Action() == 'M'))
5466 removed_paths = set(f.LocalPath()
5467 for f in input_api.AffectedFiles(include_deletes=True)
5468 if f.Action() == 'D')
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145469
Sam Maiera6e76d72022-02-11 21:43:505470 affected_grds = [
5471 f for f in input_api.AffectedFiles()
5472 if f.LocalPath().endswith(('.grd', '.grdp'))
5473 ]
5474 affected_grds = [
5475 f for f in affected_grds if not 'testdata' in f.LocalPath()
5476 ]
5477 if not affected_grds:
5478 return []
meacer8c0d3832019-12-26 21:46:165479
Sam Maiera6e76d72022-02-11 21:43:505480 affected_png_paths = [
5481 f.AbsoluteLocalPath() for f in input_api.AffectedFiles()
5482 if (f.LocalPath().endswith('.png'))
5483 ]
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145484
Sam Maiera6e76d72022-02-11 21:43:505485 # Check for screenshots. Developers can upload screenshots using
5486 # tools/translation/upload_screenshots.py which finds and uploads
5487 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
5488 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
5489 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
5490 #
5491 # The logic here is as follows:
5492 #
5493 # - If the CL has a .png file under the screenshots directory for a grd
5494 # file, warn the developer. Actual images should never be checked into the
5495 # Chrome repo.
5496 #
5497 # - If the CL contains modified or new messages in grd files and doesn't
5498 # contain the corresponding .sha1 files, warn the developer to add images
5499 # and upload them via tools/translation/upload_screenshots.py.
5500 #
5501 # - If the CL contains modified or new messages in grd files and the
5502 # corresponding .sha1 files, everything looks good.
5503 #
5504 # - If the CL contains removed messages in grd files but the corresponding
5505 # .sha1 files aren't removed, warn the developer to remove them.
5506 unnecessary_screenshots = []
5507 missing_sha1 = []
5508 unnecessary_sha1_files = []
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145509
Sam Maiera6e76d72022-02-11 21:43:505510 # This checks verifies that the ICU syntax of messages this CL touched is
5511 # valid, and reports any found syntax errors.
5512 # Without this presubmit check, ICU syntax errors in Chromium strings can land
5513 # without developers being aware of them. Later on, such ICU syntax errors
5514 # break message extraction for translation, hence would block Chromium
5515 # translations until they are fixed.
5516 icu_syntax_errors = []
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145517
Sam Maiera6e76d72022-02-11 21:43:505518 def _CheckScreenshotAdded(screenshots_dir, message_id):
5519 sha1_path = input_api.os_path.join(screenshots_dir,
5520 message_id + '.png.sha1')
5521 if sha1_path not in new_or_added_paths:
5522 missing_sha1.append(sha1_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145523
Sam Maiera6e76d72022-02-11 21:43:505524 def _CheckScreenshotRemoved(screenshots_dir, message_id):
5525 sha1_path = input_api.os_path.join(screenshots_dir,
5526 message_id + '.png.sha1')
5527 if input_api.os_path.exists(
5528 sha1_path) and sha1_path not in removed_paths:
5529 unnecessary_sha1_files.append(sha1_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145530
Sam Maiera6e76d72022-02-11 21:43:505531 def _ValidateIcuSyntax(text, level, signatures):
5532 """Validates ICU syntax of a text string.
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145533
Sam Maiera6e76d72022-02-11 21:43:505534 Check if text looks similar to ICU and checks for ICU syntax correctness
5535 in this case. Reports various issues with ICU syntax and values of
5536 variants. Supports checking of nested messages. Accumulate information of
5537 each ICU messages found in the text for further checking.
Rainhard Findlingfc31844c52020-05-15 09:58:265538
Sam Maiera6e76d72022-02-11 21:43:505539 Args:
5540 text: a string to check.
5541 level: a number of current nesting level.
5542 signatures: an accumulator, a list of tuple of (level, variable,
5543 kind, variants).
Rainhard Findlingfc31844c52020-05-15 09:58:265544
Sam Maiera6e76d72022-02-11 21:43:505545 Returns:
5546 None if a string is not ICU or no issue detected.
5547 A tuple of (message, start index, end index) if an issue detected.
5548 """
5549 valid_types = {
5550 'plural': (frozenset(
5551 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many',
5552 'other']), frozenset(['=1', 'other'])),
5553 'selectordinal': (frozenset(
5554 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many',
5555 'other']), frozenset(['one', 'other'])),
5556 'select': (frozenset(), frozenset(['other'])),
5557 }
Rainhard Findlingfc31844c52020-05-15 09:58:265558
Sam Maiera6e76d72022-02-11 21:43:505559 # Check if the message looks like an attempt to use ICU
5560 # plural. If yes - check if its syntax strictly matches ICU format.
5561 like = re.match(r'^[^{]*\{[^{]*\b(plural|selectordinal|select)\b',
5562 text)
5563 if not like:
5564 signatures.append((level, None, None, None))
5565 return
Rainhard Findlingfc31844c52020-05-15 09:58:265566
Sam Maiera6e76d72022-02-11 21:43:505567 # Check for valid prefix and suffix
5568 m = re.match(
5569 r'^([^{]*\{)([a-zA-Z0-9_]+),\s*'
5570 r'(plural|selectordinal|select),\s*'
5571 r'(?:offset:\d+)?\s*(.*)', text, re.DOTALL)
5572 if not m:
5573 return (('This message looks like an ICU plural, '
5574 'but does not follow ICU syntax.'), like.start(),
5575 like.end())
5576 starting, variable, kind, variant_pairs = m.groups()
5577 variants, depth, last_pos = _ParseIcuVariants(variant_pairs,
5578 m.start(4))
5579 if depth:
5580 return ('Invalid ICU format. Unbalanced opening bracket', last_pos,
5581 len(text))
5582 first = text[0]
5583 ending = text[last_pos:]
5584 if not starting:
5585 return ('Invalid ICU format. No initial opening bracket',
5586 last_pos - 1, last_pos)
5587 if not ending or '}' not in ending:
5588 return ('Invalid ICU format. No final closing bracket',
5589 last_pos - 1, last_pos)
5590 elif first != '{':
5591 return ((
5592 'Invalid ICU format. Extra characters at the start of a complex '
5593 'message (go/icu-message-migration): "%s"') % starting, 0,
5594 len(starting))
5595 elif ending != '}':
5596 return ((
5597 'Invalid ICU format. Extra characters at the end of a complex '
5598 'message (go/icu-message-migration): "%s"') % ending,
5599 last_pos - 1, len(text) - 1)
5600 if kind not in valid_types:
5601 return (('Unknown ICU message type %s. '
5602 'Valid types are: plural, select, selectordinal') % kind,
5603 0, 0)
5604 known, required = valid_types[kind]
5605 defined_variants = set()
5606 for variant, variant_range, value, value_range in variants:
5607 start, end = variant_range
5608 if variant in defined_variants:
5609 return ('Variant "%s" is defined more than once' % variant,
5610 start, end)
5611 elif known and variant not in known:
5612 return ('Variant "%s" is not valid for %s message' %
5613 (variant, kind), start, end)
5614 defined_variants.add(variant)
5615 # Check for nested structure
5616 res = _ValidateIcuSyntax(value[1:-1], level + 1, signatures)
5617 if res:
5618 return (res[0], res[1] + value_range[0] + 1,
5619 res[2] + value_range[0] + 1)
5620 missing = required - defined_variants
5621 if missing:
5622 return ('Required variants missing: %s' % ', '.join(missing), 0,
5623 len(text))
5624 signatures.append((level, variable, kind, defined_variants))
Rainhard Findlingfc31844c52020-05-15 09:58:265625
Sam Maiera6e76d72022-02-11 21:43:505626 def _ParseIcuVariants(text, offset=0):
5627 """Parse variants part of ICU complex message.
Rainhard Findlingfc31844c52020-05-15 09:58:265628
Sam Maiera6e76d72022-02-11 21:43:505629 Builds a tuple of variant names and values, as well as
5630 their offsets in the input string.
Rainhard Findlingfc31844c52020-05-15 09:58:265631
Sam Maiera6e76d72022-02-11 21:43:505632 Args:
5633 text: a string to parse
5634 offset: additional offset to add to positions in the text to get correct
5635 position in the complete ICU string.
Rainhard Findlingfc31844c52020-05-15 09:58:265636
Sam Maiera6e76d72022-02-11 21:43:505637 Returns:
5638 List of tuples, each tuple consist of four fields: variant name,
5639 variant name span (tuple of two integers), variant value, value
5640 span (tuple of two integers).
5641 """
5642 depth, start, end = 0, -1, -1
5643 variants = []
5644 key = None
5645 for idx, char in enumerate(text):
5646 if char == '{':
5647 if not depth:
5648 start = idx
5649 chunk = text[end + 1:start]
5650 key = chunk.strip()
5651 pos = offset + end + 1 + chunk.find(key)
5652 span = (pos, pos + len(key))
5653 depth += 1
5654 elif char == '}':
5655 if not depth:
5656 return variants, depth, offset + idx
5657 depth -= 1
5658 if not depth:
5659 end = idx
5660 variants.append((key, span, text[start:end + 1],
5661 (offset + start, offset + end + 1)))
5662 return variants, depth, offset + end + 1
Rainhard Findlingfc31844c52020-05-15 09:58:265663
Sam Maiera6e76d72022-02-11 21:43:505664 try:
5665 old_sys_path = sys.path
5666 sys.path = sys.path + [
5667 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
5668 'translation')
5669 ]
5670 from helper import grd_helper
5671 finally:
5672 sys.path = old_sys_path
Rainhard Findlingfc31844c52020-05-15 09:58:265673
Sam Maiera6e76d72022-02-11 21:43:505674 for f in affected_grds:
5675 file_path = f.LocalPath()
5676 old_id_to_msg_map = {}
5677 new_id_to_msg_map = {}
5678 # Note that this code doesn't check if the file has been deleted. This is
5679 # OK because it only uses the old and new file contents and doesn't load
5680 # the file via its path.
5681 # It's also possible that a file's content refers to a renamed or deleted
5682 # file via a <part> tag, such as <part file="now-deleted-file.grdp">. This
5683 # is OK as well, because grd_helper ignores <part> tags when loading .grd or
5684 # .grdp files.
5685 if file_path.endswith('.grdp'):
5686 if f.OldContents():
5687 old_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
5688 '\n'.join(f.OldContents()))
5689 if f.NewContents():
5690 new_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
5691 '\n'.join(f.NewContents()))
5692 else:
5693 file_dir = input_api.os_path.dirname(file_path) or '.'
5694 if f.OldContents():
5695 old_id_to_msg_map = grd_helper.GetGrdMessages(
5696 StringIO('\n'.join(f.OldContents())), file_dir)
5697 if f.NewContents():
5698 new_id_to_msg_map = grd_helper.GetGrdMessages(
5699 StringIO('\n'.join(f.NewContents())), file_dir)
Rainhard Findlingfc31844c52020-05-15 09:58:265700
Sam Maiera6e76d72022-02-11 21:43:505701 grd_name, ext = input_api.os_path.splitext(
5702 input_api.os_path.basename(file_path))
5703 screenshots_dir = input_api.os_path.join(
5704 input_api.os_path.dirname(file_path),
5705 grd_name + ext.replace('.', '_'))
Rainhard Findlingfc31844c52020-05-15 09:58:265706
Sam Maiera6e76d72022-02-11 21:43:505707 # Compute added, removed and modified message IDs.
5708 old_ids = set(old_id_to_msg_map)
5709 new_ids = set(new_id_to_msg_map)
5710 added_ids = new_ids - old_ids
5711 removed_ids = old_ids - new_ids
5712 modified_ids = set([])
5713 for key in old_ids.intersection(new_ids):
5714 if (old_id_to_msg_map[key].ContentsAsXml('', True) !=
5715 new_id_to_msg_map[key].ContentsAsXml('', True)):
5716 # The message content itself changed. Require an updated screenshot.
5717 modified_ids.add(key)
5718 elif old_id_to_msg_map[key].attrs['meaning'] != \
5719 new_id_to_msg_map[key].attrs['meaning']:
5720 # The message meaning changed. Ensure there is a screenshot for it.
5721 sha1_path = input_api.os_path.join(screenshots_dir,
5722 key + '.png.sha1')
5723 if sha1_path not in new_or_added_paths and not \
5724 input_api.os_path.exists(sha1_path):
5725 # There is neither a previous screenshot nor is a new one added now.
5726 # Require a screenshot.
5727 modified_ids.add(key)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145728
Sam Maiera6e76d72022-02-11 21:43:505729 if run_screenshot_check:
5730 # Check the screenshot directory for .png files. Warn if there is any.
5731 for png_path in affected_png_paths:
5732 if png_path.startswith(screenshots_dir):
5733 unnecessary_screenshots.append(png_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145734
Sam Maiera6e76d72022-02-11 21:43:505735 for added_id in added_ids:
5736 _CheckScreenshotAdded(screenshots_dir, added_id)
Rainhard Findlingd8d04372020-08-13 13:30:095737
Sam Maiera6e76d72022-02-11 21:43:505738 for modified_id in modified_ids:
5739 _CheckScreenshotAdded(screenshots_dir, modified_id)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145740
Sam Maiera6e76d72022-02-11 21:43:505741 for removed_id in removed_ids:
5742 _CheckScreenshotRemoved(screenshots_dir, removed_id)
5743
5744 # Check new and changed strings for ICU syntax errors.
5745 for key in added_ids.union(modified_ids):
5746 msg = new_id_to_msg_map[key].ContentsAsXml('', True)
5747 err = _ValidateIcuSyntax(msg, 0, [])
5748 if err is not None:
5749 icu_syntax_errors.append(str(key) + ': ' + str(err[0]))
5750
5751 results = []
Rainhard Findlingfc31844c52020-05-15 09:58:265752 if run_screenshot_check:
Sam Maiera6e76d72022-02-11 21:43:505753 if unnecessary_screenshots:
5754 results.append(
5755 output_api.PresubmitError(
5756 'Do not include actual screenshots in the changelist. Run '
5757 'tools/translate/upload_screenshots.py to upload them instead:',
5758 sorted(unnecessary_screenshots)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145759
Sam Maiera6e76d72022-02-11 21:43:505760 if missing_sha1:
5761 results.append(
5762 output_api.PresubmitError(
5763 'You are adding or modifying UI strings.\n'
5764 'To ensure the best translations, take screenshots of the relevant UI '
5765 '(https://g.co/chrome/translation) and add these files to your '
5766 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145767
Sam Maiera6e76d72022-02-11 21:43:505768 if unnecessary_sha1_files:
5769 results.append(
5770 output_api.PresubmitError(
5771 'You removed strings associated with these files. Remove:',
5772 sorted(unnecessary_sha1_files)))
5773 else:
5774 results.append(
5775 output_api.PresubmitPromptOrNotify('Skipping translation '
5776 'screenshots check.'))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145777
Sam Maiera6e76d72022-02-11 21:43:505778 if icu_syntax_errors:
5779 results.append(
5780 output_api.PresubmitPromptWarning(
5781 'ICU syntax errors were found in the following strings (problems or '
5782 'feedback? Contact [email protected]):',
5783 items=icu_syntax_errors))
Rainhard Findlingfc31844c52020-05-15 09:58:265784
Sam Maiera6e76d72022-02-11 21:43:505785 return results
Mustafa Emre Acer51f2f742020-03-09 19:41:125786
5787
Saagar Sanghavifceeaae2020-08-12 16:40:365788def CheckTranslationExpectations(input_api, output_api,
Mustafa Emre Acer51f2f742020-03-09 19:41:125789 repo_root=None,
5790 translation_expectations_path=None,
5791 grd_files=None):
Sam Maiera6e76d72022-02-11 21:43:505792 import sys
5793 affected_grds = [
5794 f for f in input_api.AffectedFiles()
5795 if (f.LocalPath().endswith('.grd') or f.LocalPath().endswith('.grdp'))
5796 ]
5797 if not affected_grds:
5798 return []
5799
5800 try:
5801 old_sys_path = sys.path
5802 sys.path = sys.path + [
5803 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
5804 'translation')
5805 ]
5806 from helper import git_helper
5807 from helper import translation_helper
5808 finally:
5809 sys.path = old_sys_path
5810
5811 # Check that translation expectations can be parsed and we can get a list of
5812 # translatable grd files. |repo_root| and |translation_expectations_path| are
5813 # only passed by tests.
5814 if not repo_root:
5815 repo_root = input_api.PresubmitLocalPath()
5816 if not translation_expectations_path:
5817 translation_expectations_path = input_api.os_path.join(
5818 repo_root, 'tools', 'gritsettings', 'translation_expectations.pyl')
5819 if not grd_files:
5820 grd_files = git_helper.list_grds_in_repository(repo_root)
5821
5822 # Ignore bogus grd files used only for testing
Gao Shenga79ebd42022-08-08 17:25:595823 # ui/webui/resources/tools/generate_grd.py.
Sam Maiera6e76d72022-02-11 21:43:505824 ignore_path = input_api.os_path.join('ui', 'webui', 'resources', 'tools',
5825 'tests')
5826 grd_files = [p for p in grd_files if ignore_path not in p]
5827
5828 try:
5829 translation_helper.get_translatable_grds(
5830 repo_root, grd_files, translation_expectations_path)
5831 except Exception as e:
5832 return [
5833 output_api.PresubmitNotifyResult(
5834 'Failed to get a list of translatable grd files. This happens when:\n'
5835 ' - One of the modified grd or grdp files cannot be parsed or\n'
5836 ' - %s is not updated.\n'
5837 'Stack:\n%s' % (translation_expectations_path, str(e)))
5838 ]
Mustafa Emre Acer51f2f742020-03-09 19:41:125839 return []
5840
Ken Rockotc31f4832020-05-29 18:58:515841
Saagar Sanghavifceeaae2020-08-12 16:40:365842def CheckStableMojomChanges(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505843 """Changes to [Stable] mojom types must preserve backward-compatibility."""
5844 changed_mojoms = input_api.AffectedFiles(
5845 include_deletes=True,
5846 file_filter=lambda f: f.LocalPath().endswith(('.mojom')))
Erik Staabc734cd7a2021-11-23 03:11:525847
Bruce Dawson344ab262022-06-04 11:35:105848 if not changed_mojoms or input_api.no_diffs:
Sam Maiera6e76d72022-02-11 21:43:505849 return []
5850
5851 delta = []
5852 for mojom in changed_mojoms:
Sam Maiera6e76d72022-02-11 21:43:505853 delta.append({
5854 'filename': mojom.LocalPath(),
5855 'old': '\n'.join(mojom.OldContents()) or None,
5856 'new': '\n'.join(mojom.NewContents()) or None,
5857 })
5858
5859 process = input_api.subprocess.Popen([
Takuto Ikutadca10222022-04-13 02:51:215860 input_api.python3_executable,
Sam Maiera6e76d72022-02-11 21:43:505861 input_api.os_path.join(
5862 input_api.PresubmitLocalPath(), 'mojo', 'public', 'tools', 'mojom',
5863 'check_stable_mojom_compatibility.py'), '--src-root',
5864 input_api.PresubmitLocalPath()
5865 ],
5866 stdin=input_api.subprocess.PIPE,
5867 stdout=input_api.subprocess.PIPE,
5868 stderr=input_api.subprocess.PIPE,
5869 universal_newlines=True)
5870 (x, error) = process.communicate(input=input_api.json.dumps(delta))
5871 if process.returncode:
5872 return [
5873 output_api.PresubmitError(
5874 'One or more [Stable] mojom definitions appears to have been changed '
5875 'in a way that is not backward-compatible.',
5876 long_text=error)
5877 ]
Erik Staabc734cd7a2021-11-23 03:11:525878 return []
5879
Dominic Battre645d42342020-12-04 16:14:105880def CheckDeprecationOfPreferences(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505881 """Removing a preference should come with a deprecation."""
Dominic Battre645d42342020-12-04 16:14:105882
Sam Maiera6e76d72022-02-11 21:43:505883 def FilterFile(affected_file):
5884 """Accept only .cc files and the like."""
5885 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
5886 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
5887 input_api.DEFAULT_FILES_TO_SKIP)
5888 return input_api.FilterSourceFile(
5889 affected_file,
5890 files_to_check=file_inclusion_pattern,
5891 files_to_skip=files_to_skip)
Dominic Battre645d42342020-12-04 16:14:105892
Sam Maiera6e76d72022-02-11 21:43:505893 def ModifiedLines(affected_file):
5894 """Returns a list of tuples (line number, line text) of added and removed
5895 lines.
Dominic Battre645d42342020-12-04 16:14:105896
Sam Maiera6e76d72022-02-11 21:43:505897 Deleted lines share the same line number as the previous line.
Dominic Battre645d42342020-12-04 16:14:105898
Sam Maiera6e76d72022-02-11 21:43:505899 This relies on the scm diff output describing each changed code section
5900 with a line of the form
Dominic Battre645d42342020-12-04 16:14:105901
Sam Maiera6e76d72022-02-11 21:43:505902 ^@@ <old line num>,<old size> <new line num>,<new size> @@$
5903 """
5904 line_num = 0
5905 modified_lines = []
5906 for line in affected_file.GenerateScmDiff().splitlines():
5907 # Extract <new line num> of the patch fragment (see format above).
5908 m = input_api.re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@',
5909 line)
5910 if m:
5911 line_num = int(m.groups(1)[0])
5912 continue
5913 if ((line.startswith('+') and not line.startswith('++'))
5914 or (line.startswith('-') and not line.startswith('--'))):
5915 modified_lines.append((line_num, line))
Dominic Battre645d42342020-12-04 16:14:105916
Sam Maiera6e76d72022-02-11 21:43:505917 if not line.startswith('-'):
5918 line_num += 1
5919 return modified_lines
Dominic Battre645d42342020-12-04 16:14:105920
Sam Maiera6e76d72022-02-11 21:43:505921 def FindLineWith(lines, needle):
5922 """Returns the line number (i.e. index + 1) in `lines` containing `needle`.
Dominic Battre645d42342020-12-04 16:14:105923
Sam Maiera6e76d72022-02-11 21:43:505924 If 0 or >1 lines contain `needle`, -1 is returned.
5925 """
5926 matching_line_numbers = [
5927 # + 1 for 1-based counting of line numbers.
5928 i + 1 for i, line in enumerate(lines) if needle in line
5929 ]
5930 return matching_line_numbers[0] if len(
5931 matching_line_numbers) == 1 else -1
Dominic Battre645d42342020-12-04 16:14:105932
Sam Maiera6e76d72022-02-11 21:43:505933 def ModifiedPrefMigration(affected_file):
5934 """Returns whether the MigrateObsolete.*Pref functions were modified."""
5935 # Determine first and last lines of MigrateObsolete.*Pref functions.
5936 new_contents = affected_file.NewContents()
5937 range_1 = (FindLineWith(new_contents,
5938 'BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'),
5939 FindLineWith(new_contents,
5940 'END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'))
5941 range_2 = (FindLineWith(new_contents,
5942 'BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS'),
5943 FindLineWith(new_contents,
5944 'END_MIGRATE_OBSOLETE_PROFILE_PREFS'))
5945 if (-1 in range_1 + range_2):
5946 raise Exception(
5947 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.'
5948 )
Dominic Battre645d42342020-12-04 16:14:105949
Sam Maiera6e76d72022-02-11 21:43:505950 # Check whether any of the modified lines are part of the
5951 # MigrateObsolete.*Pref functions.
5952 for line_nr, line in ModifiedLines(affected_file):
5953 if (range_1[0] <= line_nr <= range_1[1]
5954 or range_2[0] <= line_nr <= range_2[1]):
5955 return True
5956 return False
Dominic Battre645d42342020-12-04 16:14:105957
Sam Maiera6e76d72022-02-11 21:43:505958 register_pref_pattern = input_api.re.compile(r'Register.+Pref')
5959 browser_prefs_file_pattern = input_api.re.compile(
5960 r'chrome/browser/prefs/browser_prefs.cc')
Dominic Battre645d42342020-12-04 16:14:105961
Sam Maiera6e76d72022-02-11 21:43:505962 changes = input_api.AffectedFiles(include_deletes=True,
5963 file_filter=FilterFile)
5964 potential_problems = []
5965 for f in changes:
5966 for line in f.GenerateScmDiff().splitlines():
5967 # Check deleted lines for pref registrations.
5968 if (line.startswith('-') and not line.startswith('--')
5969 and register_pref_pattern.search(line)):
5970 potential_problems.append('%s: %s' % (f.LocalPath(), line))
Dominic Battre645d42342020-12-04 16:14:105971
Sam Maiera6e76d72022-02-11 21:43:505972 if browser_prefs_file_pattern.search(f.LocalPath()):
5973 # If the developer modified the MigrateObsolete.*Prefs() functions, we
5974 # assume that they knew that they have to deprecate preferences and don't
5975 # warn.
5976 try:
5977 if ModifiedPrefMigration(f):
5978 return []
5979 except Exception as e:
5980 return [output_api.PresubmitError(str(e))]
Dominic Battre645d42342020-12-04 16:14:105981
Sam Maiera6e76d72022-02-11 21:43:505982 if potential_problems:
5983 return [
5984 output_api.PresubmitPromptWarning(
5985 'Discovered possible removal of preference registrations.\n\n'
5986 'Please make sure to properly deprecate preferences by clearing their\n'
5987 'value for a couple of milestones before finally removing the code.\n'
5988 'Otherwise data may stay in the preferences files forever. See\n'
5989 'Migrate*Prefs() in chrome/browser/prefs/browser_prefs.cc and\n'
5990 'chrome/browser/prefs/README.md for examples.\n'
5991 'This may be a false positive warning (e.g. if you move preference\n'
5992 'registrations to a different place).\n', potential_problems)
5993 ]
5994 return []
5995
Matt Stark6ef08872021-07-29 01:21:465996
5997def CheckConsistentGrdChanges(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505998 """Changes to GRD files must be consistent for tools to read them."""
5999 changed_grds = input_api.AffectedFiles(
6000 include_deletes=False,
6001 file_filter=lambda f: f.LocalPath().endswith(('.grd')))
6002 errors = []
6003 invalid_file_regexes = [(input_api.re.compile(matcher), msg)
6004 for matcher, msg in _INVALID_GRD_FILE_LINE]
6005 for grd in changed_grds:
6006 for i, line in enumerate(grd.NewContents()):
6007 for matcher, msg in invalid_file_regexes:
6008 if matcher.search(line):
6009 errors.append(
6010 output_api.PresubmitError(
6011 'Problem on {grd}:{i} - {msg}'.format(
6012 grd=grd.LocalPath(), i=i + 1, msg=msg)))
6013 return errors
6014
Kevin McNee967dd2d22021-11-15 16:09:296015
6016def CheckMPArchApiUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:506017 """CC the MPArch watchlist if the CL uses an API that is ambiguous in the
6018 presence of MPArch features such as bfcache, prerendering, and fenced frames.
6019 """
Kevin McNee967dd2d22021-11-15 16:09:296020
Ian Vollickdba956c2022-04-20 23:53:456021 # Only consider top-level directories that (1) can use content APIs or
6022 # problematic blink APIs, (2) apply to desktop or android chrome, and (3)
6023 # are known to have a significant number of uses of the APIs of concern.
Sam Maiera6e76d72022-02-11 21:43:506024 files_to_check = (
Bruce Dawson40fece62022-09-16 19:58:316025 r'^(chrome|components|content|extensions|third_party/blink/renderer)/.+%s' %
Kevin McNee967dd2d22021-11-15 16:09:296026 _IMPLEMENTATION_EXTENSIONS,
Bruce Dawson40fece62022-09-16 19:58:316027 r'^(chrome|components|content|extensions|third_party/blink/renderer)/.+%s' %
Sam Maiera6e76d72022-02-11 21:43:506028 _HEADER_EXTENSIONS,
6029 )
6030 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
6031 input_api.DEFAULT_FILES_TO_SKIP)
6032 source_file_filter = lambda f: input_api.FilterSourceFile(
6033 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
Kevin McNee967dd2d22021-11-15 16:09:296034
Kevin McNee29c0e8232022-08-05 15:36:096035 # Here we list the classes/methods we're monitoring. For the "fyi" cases,
6036 # we add the CL to the watchlist, but we don't omit a warning or have it be
6037 # included in the triage rotation.
Sam Maiera6e76d72022-02-11 21:43:506038 # Note that since these are are just regular expressions and we don't have
6039 # the compiler's AST, we could have spurious matches (e.g. an unrelated class
6040 # could have a method named IsInMainFrame).
Kevin McNee29c0e8232022-08-05 15:36:096041 fyi_concerning_class_pattern = input_api.re.compile(
Sam Maiera6e76d72022-02-11 21:43:506042 r'WebContentsObserver|WebContentsUserData')
6043 # A subset of WebContentsObserver overrides where there's particular risk for
6044 # confusing tab and page level operations and data (e.g. incorrectly
6045 # resetting page state in DidFinishNavigation).
Kevin McNee29c0e8232022-08-05 15:36:096046 fyi_concerning_wco_methods = [
Sam Maiera6e76d72022-02-11 21:43:506047 'DidStartNavigation',
6048 'ReadyToCommitNavigation',
6049 'DidFinishNavigation',
6050 'RenderViewReady',
6051 'RenderViewDeleted',
6052 'RenderViewHostChanged',
Sam Maiera6e76d72022-02-11 21:43:506053 'DOMContentLoaded',
6054 'DidFinishLoad',
6055 ]
6056 concerning_nav_handle_methods = [
6057 'IsInMainFrame',
6058 ]
6059 concerning_web_contents_methods = [
Sam Maiera6e76d72022-02-11 21:43:506060 'FromRenderFrameHost',
6061 'FromRenderViewHost',
Kevin McNee29c0e8232022-08-05 15:36:096062 ]
6063 fyi_concerning_web_contents_methods = [
Sam Maiera6e76d72022-02-11 21:43:506064 'GetRenderViewHost',
6065 ]
6066 concerning_rfh_methods = [
6067 'GetParent',
6068 'GetMainFrame',
Kevin McNee29c0e8232022-08-05 15:36:096069 ]
6070 fyi_concerning_rfh_methods = [
Sam Maiera6e76d72022-02-11 21:43:506071 'GetFrameTreeNodeId',
6072 ]
Ian Vollickc825b1f2022-04-19 14:30:156073 concerning_rfhi_methods = [
6074 'is_main_frame',
6075 ]
Ian Vollicka77a73ea2022-04-06 18:08:016076 concerning_ftn_methods = [
6077 'IsMainFrame',
6078 ]
Ian Vollickdba956c2022-04-20 23:53:456079 concerning_blink_frame_methods = [
Ian Vollick4d785d22022-06-18 00:10:026080 'IsCrossOriginToNearestMainFrame',
Ian Vollickdba956c2022-04-20 23:53:456081 ]
Sam Maiera6e76d72022-02-11 21:43:506082 concerning_method_pattern = input_api.re.compile(r'(' + r'|'.join(
6083 item for sublist in [
Kevin McNee29c0e8232022-08-05 15:36:096084 concerning_nav_handle_methods,
Ian Vollicka77a73ea2022-04-06 18:08:016085 concerning_web_contents_methods, concerning_rfh_methods,
Ian Vollickc825b1f2022-04-19 14:30:156086 concerning_rfhi_methods, concerning_ftn_methods,
Ian Vollickdba956c2022-04-20 23:53:456087 concerning_blink_frame_methods,
Sam Maiera6e76d72022-02-11 21:43:506088 ] for item in sublist) + r')\(')
Kevin McNee29c0e8232022-08-05 15:36:096089 fyi_concerning_method_pattern = input_api.re.compile(r'(' + r'|'.join(
6090 item for sublist in [
6091 fyi_concerning_wco_methods, fyi_concerning_web_contents_methods,
6092 fyi_concerning_rfh_methods,
6093 ] for item in sublist) + r')\(')
Kevin McNee967dd2d22021-11-15 16:09:296094
Kevin McNee4eeec792022-02-14 20:02:046095 used_apis = set()
Kevin McNee29c0e8232022-08-05 15:36:096096 used_fyi_methods = False
Sam Maiera6e76d72022-02-11 21:43:506097 for f in input_api.AffectedFiles(include_deletes=False,
6098 file_filter=source_file_filter):
6099 for line_num, line in f.ChangedContents():
Kevin McNee29c0e8232022-08-05 15:36:096100 fyi_class_match = fyi_concerning_class_pattern.search(line)
6101 if fyi_class_match:
6102 used_fyi_methods = True
6103 fyi_method_match = fyi_concerning_method_pattern.search(line)
6104 if fyi_method_match:
6105 used_fyi_methods = True
Kevin McNee4eeec792022-02-14 20:02:046106 method_match = concerning_method_pattern.search(line)
6107 if method_match:
6108 used_apis.add(method_match[1])
Sam Maiera6e76d72022-02-11 21:43:506109
Kevin McNee4eeec792022-02-14 20:02:046110 if not used_apis:
Kevin McNee29c0e8232022-08-05 15:36:096111 if used_fyi_methods:
6112 output_api.AppendCC('[email protected]')
6113
Kevin McNee4eeec792022-02-14 20:02:046114 return []
Kevin McNee967dd2d22021-11-15 16:09:296115
Kevin McNee4eeec792022-02-14 20:02:046116 output_api.AppendCC('[email protected]')
6117 message = ('This change uses API(s) that are ambiguous in the presence of '
6118 'MPArch features such as bfcache, prerendering, and fenced '
6119 'frames.')
Kevin McNee29c0e8232022-08-05 15:36:096120 explanation = (
Kevin McNee4eeec792022-02-14 20:02:046121 'Please double check whether new code assumes that a WebContents only '
Kevin McNee29c0e8232022-08-05 15:36:096122 'contains a single page at a time. Notably, checking whether a frame '
6123 'is the \"main frame\" is not specific enough to determine whether it '
6124 'corresponds to the document reflected in the omnibox. A WebContents '
6125 'may have additional main frames for prerendered pages, bfcached '
6126 'pages, fenced frames, etc. '
6127 'See this doc [1] and the comments on the individual APIs '
Kevin McNee4eeec792022-02-14 20:02:046128 'for guidance and this doc [2] for context. The MPArch review '
6129 'watchlist has been CC\'d on this change to help identify any issues.\n'
6130 '[1] https://docs.google.com/document/d/13l16rWTal3o5wce4i0RwdpMP5ESELLKr439Faj2BBRo/edit?usp=sharing\n'
6131 '[2] https://docs.google.com/document/d/1NginQ8k0w3znuwTiJ5qjYmBKgZDekvEPC22q0I4swxQ/edit?usp=sharing'
6132 )
6133 return [
6134 output_api.PresubmitNotifyResult(message,
6135 items=list(used_apis),
Kevin McNee29c0e8232022-08-05 15:36:096136 long_text=explanation)
Kevin McNee4eeec792022-02-14 20:02:046137 ]
Henrique Ferreiro2a4b55942021-11-29 23:45:366138
6139
6140def CheckAssertAshOnlyCode(input_api, output_api):
6141 """Errors if a BUILD.gn file in an ash/ directory doesn't include
6142 assert(is_chromeos_ash).
6143 """
6144
6145 def FileFilter(affected_file):
6146 """Includes directories known to be Ash only."""
6147 return input_api.FilterSourceFile(
6148 affected_file,
6149 files_to_check=(
6150 r'^ash/.*BUILD\.gn', # Top-level src/ash/.
6151 r'.*/ash/.*BUILD\.gn'), # Any path component.
6152 files_to_skip=(input_api.DEFAULT_FILES_TO_SKIP))
6153
6154 errors = []
6155 pattern = input_api.re.compile(r'assert\(is_chromeos_ash')
Jameson Thies0ce669f2021-12-09 15:56:566156 for f in input_api.AffectedFiles(include_deletes=False,
6157 file_filter=FileFilter):
Henrique Ferreiro2a4b55942021-11-29 23:45:366158 if (not pattern.search(input_api.ReadFile(f))):
6159 errors.append(
6160 output_api.PresubmitError(
6161 'Please add assert(is_chromeos_ash) to %s. If that\'s not '
6162 'possible, please create and issue and add a comment such '
6163 'as:\n # TODO(https://crbug.com/XXX): add '
6164 'assert(is_chromeos_ash) when ...' % f.LocalPath()))
6165 return errors
Lukasz Anforowicz7016d05e2021-11-30 03:56:276166
6167
6168def _IsRendererOnlyCppFile(input_api, affected_file):
Sam Maiera6e76d72022-02-11 21:43:506169 path = affected_file.LocalPath()
6170 if not _IsCPlusPlusFile(input_api, path):
6171 return False
6172
6173 # Any code under a "renderer" subdirectory is assumed to be Renderer-only.
6174 if "/renderer/" in path:
6175 return True
6176
6177 # Blink's public/web API is only used/included by Renderer-only code. Note
6178 # that public/platform API may be used in non-Renderer processes (e.g. there
6179 # are some includes in code used by Utility, PDF, or Plugin processes).
6180 if "/blink/public/web/" in path:
6181 return True
6182
6183 # We assume that everything else may be used outside of Renderer processes.
Lukasz Anforowicz7016d05e2021-11-30 03:56:276184 return False
6185
Lukasz Anforowicz7016d05e2021-11-30 03:56:276186# TODO(https://crbug.com/1273182): Remove these checks, once they are replaced
6187# by the Chromium Clang Plugin (which will be preferable because it will
6188# 1) report errors earlier - at compile-time and 2) cover more rules).
6189def CheckRawPtrUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:506190 """Rough checks that raw_ptr<T> usage guidelines are followed."""
6191 errors = []
6192 # The regex below matches "raw_ptr<" following a word boundary, but not in a
6193 # C++ comment.
6194 raw_ptr_matcher = input_api.re.compile(r'^((?!//).)*\braw_ptr<')
6195 file_filter = lambda f: _IsRendererOnlyCppFile(input_api, f)
6196 for f, line_num, line in input_api.RightHandSideLines(file_filter):
6197 if raw_ptr_matcher.search(line):
6198 errors.append(
6199 output_api.PresubmitError(
6200 'Problem on {path}:{line} - '\
6201 'raw_ptr<T> should not be used in Renderer-only code '\
6202 '(as documented in the "Pointers to unprotected memory" '\
6203 'section in //base/memory/raw_ptr.md)'.format(
6204 path=f.LocalPath(), line=line_num)))
6205 return errors
Henrique Ferreirof9819f2e32021-11-30 13:31:566206
6207
6208def CheckPythonShebang(input_api, output_api):
6209 """Checks that python scripts use #!/usr/bin/env instead of hardcoding a
6210 system-wide python.
6211 """
6212 errors = []
6213 sources = lambda affected_file: input_api.FilterSourceFile(
6214 affected_file,
6215 files_to_skip=((_THIRD_PARTY_EXCEPT_BLINK,
6216 r'third_party/blink/web_tests/external/') + input_api.
6217 DEFAULT_FILES_TO_SKIP),
6218 files_to_check=[r'.*\.py$'])
6219 for f in input_api.AffectedSourceFiles(sources):
Takuto Ikuta36976512021-11-30 23:15:276220 for line_num, line in f.ChangedContents():
6221 if line_num == 1 and line.startswith('#!/usr/bin/python'):
6222 errors.append(f.LocalPath())
6223 break
Henrique Ferreirof9819f2e32021-11-30 13:31:566224
6225 result = []
6226 for file in errors:
6227 result.append(
6228 output_api.PresubmitError(
6229 "Please use '#!/usr/bin/env python/2/3' as the shebang of %s" %
6230 file))
6231 return result
James Shen81cc0e22022-06-15 21:10:456232
6233
6234def CheckBatchAnnotation(input_api, output_api):
6235 """Checks that tests have either @Batch or @DoNotBatch annotation. If this
6236 is not an instrumentation test, disregard."""
6237
6238 batch_annotation = input_api.re.compile(r'^\s*@Batch')
6239 do_not_batch_annotation = input_api.re.compile(r'^\s*@DoNotBatch')
6240 robolectric_test = input_api.re.compile(r'[rR]obolectric')
6241 test_class_declaration = input_api.re.compile(r'^\s*public\sclass.*Test')
6242 uiautomator_test = input_api.re.compile(r'[uU]i[aA]utomator')
6243
ckitagawae8fd23b2022-06-17 15:29:386244 missing_annotation_errors = []
6245 extra_annotation_errors = []
James Shen81cc0e22022-06-15 21:10:456246
6247 def _FilterFile(affected_file):
6248 return input_api.FilterSourceFile(
6249 affected_file,
6250 files_to_skip=input_api.DEFAULT_FILES_TO_SKIP,
6251 files_to_check=[r'.*Test\.java$'])
6252
6253 for f in input_api.AffectedSourceFiles(_FilterFile):
6254 batch_matched = None
6255 do_not_batch_matched = None
6256 is_instrumentation_test = True
6257 for line in f.NewContents():
6258 if robolectric_test.search(line) or uiautomator_test.search(line):
6259 # Skip Robolectric and UiAutomator tests.
6260 is_instrumentation_test = False
6261 break
6262 if not batch_matched:
6263 batch_matched = batch_annotation.search(line)
6264 if not do_not_batch_matched:
6265 do_not_batch_matched = do_not_batch_annotation.search(line)
6266 test_class_declaration_matched = test_class_declaration.search(
6267 line)
6268 if test_class_declaration_matched:
6269 break
6270 if (is_instrumentation_test and
6271 not batch_matched and
6272 not do_not_batch_matched):
ckitagawae8fd23b2022-06-17 15:29:386273 missing_annotation_errors.append(str(f.LocalPath()))
6274 if (not is_instrumentation_test and
6275 (batch_matched or
6276 do_not_batch_matched)):
6277 extra_annotation_errors.append(str(f.LocalPath()))
James Shen81cc0e22022-06-15 21:10:456278
6279 results = []
6280
ckitagawae8fd23b2022-06-17 15:29:386281 if missing_annotation_errors:
James Shen81cc0e22022-06-15 21:10:456282 results.append(
6283 output_api.PresubmitPromptWarning(
6284 """
6285Instrumentation tests should use either @Batch or @DoNotBatch. If tests are not
6286safe to run in batch, please use @DoNotBatch with reasons.
ckitagawae8fd23b2022-06-17 15:29:386287""", missing_annotation_errors))
6288 if extra_annotation_errors:
6289 results.append(
6290 output_api.PresubmitPromptWarning(
6291 """
6292Robolectric tests do not need a @Batch or @DoNotBatch annotations.
6293""", extra_annotation_errors))
James Shen81cc0e22022-06-15 21:10:456294
6295 return results