blob: 8bd4392208e966daacc436cd168b9ec3b9d99e14 [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d1982009-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
[email protected]f1293792009-07-31 18:09:567See http://dev.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]ca8d1982009-02-19 16:33:129"""
Daniel Chenga44a1bcd2022-03-15 20:00:1510
11from typing import Optional
12from typing import Sequence
13from dataclasses import dataclass
14
Saagar Sanghavifceeaae2020-08-12 16:40:3615PRESUBMIT_VERSION = '2.0.0'
[email protected]eea609a2011-11-18 13:10:1216
Dirk Prankee3c9c62d2021-05-18 18:35:5917# This line is 'magic' in that git-cl looks for it to decide whether to
18# use Python3 instead of Python2 when running the code in this file.
19USE_PYTHON3 = True
20
[email protected]379e7dd2010-01-28 17:39:2121_EXCLUDED_PATHS = (
Mila Greene3aa7222021-09-07 16:34:0822 # File needs to write to stdout to emulate a tool it's replacing.
Mila Greend3fc6a42021-09-10 17:38:2323 r"chrome[\\/]updater[\\/]mac[\\/]keystone[\\/]ksadmin.mm",
Ilya Shermane8a7d2d2020-07-25 04:33:4724 # Generated file.
25 (r"^components[\\/]variations[\\/]proto[\\/]devtools[\\/]"
Ilya Shermanc167a962020-08-18 18:40:2626 r"client_variations.js"),
Mila Greene3aa7222021-09-07 16:34:0827 r"^native_client_sdksrc[\\/]build_tools[\\/]make_rules.py",
Egor Paskoce145c42018-09-28 19:31:0428 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_simple.py",
29 r"^native_client_sdk[\\/]src[\\/]tools[\\/].*.mk",
30 r"^net[\\/]tools[\\/]spdyshark[\\/].*",
31 r"^skia[\\/].*",
Kent Tamura32dbbcb2018-11-30 12:28:4932 r"^third_party[\\/]blink[\\/].*",
Egor Paskoce145c42018-09-28 19:31:0433 r"^third_party[\\/]breakpad[\\/].*",
Darwin Huangd74a9d32019-07-17 17:58:4634 # sqlite is an imported third party dependency.
35 r"^third_party[\\/]sqlite[\\/].*",
Egor Paskoce145c42018-09-28 19:31:0436 r"^v8[\\/].*",
[email protected]3e4eb112011-01-18 03:29:5437 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5338 r".+_autogen\.h$",
John Budorick1e701d322019-09-11 23:35:1239 r".+_pb2\.py$",
Egor Paskoce145c42018-09-28 19:31:0440 r".+[\\/]pnacl_shim\.c$",
41 r"^gpu[\\/]config[\\/].*_list_json\.cc$",
Egor Paskoce145c42018-09-28 19:31:0442 r"tools[\\/]md_browser[\\/].*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1443 # Test pages for Maps telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0444 r"tools[\\/]perf[\\/]page_sets[\\/]maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5445 # Test pages for WebRTC telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0446 r"tools[\\/]perf[\\/]page_sets[\\/]webrtc_cases.*",
[email protected]4306417642009-06-11 00:33:4047)
[email protected]ca8d1982009-02-19 16:33:1248
John Abd-El-Malek759fea62021-03-13 03:41:1449_EXCLUDED_SET_NO_PARENT_PATHS = (
50 # It's for historical reasons that blink isn't a top level directory, where
51 # it would be allowed to have "set noparent" to avoid top level owners
52 # accidentally +1ing changes.
53 'third_party/blink/OWNERS',
54)
55
wnwenbdc444e2016-05-25 13:44:1556
[email protected]06e6d0ff2012-12-11 01:36:4457# Fragment of a regular expression that matches C++ and Objective-C++
58# implementation files.
59_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
60
wnwenbdc444e2016-05-25 13:44:1561
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:1962# Fragment of a regular expression that matches C++ and Objective-C++
63# header files.
64_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
65
66
[email protected]06e6d0ff2012-12-11 01:36:4467# Regular expression that matches code only used for test binaries
68# (best effort).
69_TEST_CODE_EXCLUDED_PATHS = (
Egor Paskoce145c42018-09-28 19:31:0470 r'.*[\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4471 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
James Cook1b4dc132021-03-09 22:45:1372 # Test suite files, like:
73 # foo_browsertest.cc
74 # bar_unittest_mac.cc (suffix)
75 # baz_unittests.cc (plural)
76 r'.+_(api|browser|eg|int|perf|pixel|unit|ui)?test(s)?(_[a-z]+)?%s' %
[email protected]e2d7e6f2013-04-23 12:57:1277 _IMPLEMENTATION_EXTENSIONS,
Matthew Denton63ea1e62019-03-25 20:39:1878 r'.+_(fuzz|fuzzer)(_[a-z]+)?%s' % _IMPLEMENTATION_EXTENSIONS,
Victor Hugo Vianna Silvac22e0202021-06-09 19:46:2179 r'.+sync_service_impl_harness%s' % _IMPLEMENTATION_EXTENSIONS,
Egor Paskoce145c42018-09-28 19:31:0480 r'.*[\\/](test|tool(s)?)[\\/].*',
danakj89f47082020-09-02 17:53:4381 # content_shell is used for running content_browsertests.
Egor Paskoce145c42018-09-28 19:31:0482 r'content[\\/]shell[\\/].*',
danakj89f47082020-09-02 17:53:4383 # Web test harness.
84 r'content[\\/]web_test[\\/].*',
[email protected]7b054982013-11-27 00:44:4785 # Non-production example code.
Egor Paskoce145c42018-09-28 19:31:0486 r'mojo[\\/]examples[\\/].*',
[email protected]8176de12014-06-20 19:07:0887 # Launcher for running iOS tests on the simulator.
Egor Paskoce145c42018-09-28 19:31:0488 r'testing[\\/]iossim[\\/]iossim\.mm$',
Olivier Robinbcea0fa2019-11-12 08:56:4189 # EarlGrey app side code for tests.
90 r'ios[\\/].*_app_interface\.mm$',
Allen Bauer0678d772020-05-11 22:25:1791 # Views Examples code
92 r'ui[\\/]views[\\/]examples[\\/].*',
Austin Sullivan33da70a2020-10-07 15:39:4193 # Chromium Codelab
94 r'codelabs[\\/]*'
[email protected]06e6d0ff2012-12-11 01:36:4495)
[email protected]ca8d1982009-02-19 16:33:1296
Daniel Bratell609102be2019-03-27 20:53:2197_THIRD_PARTY_EXCEPT_BLINK = 'third_party/(?!blink/)'
wnwenbdc444e2016-05-25 13:44:1598
[email protected]eea609a2011-11-18 13:10:1299_TEST_ONLY_WARNING = (
100 'You might be calling functions intended only for testing from\n'
danakj5f6e3b82020-09-10 13:52:55101 'production code. If you are doing this from inside another method\n'
102 'named as *ForTesting(), then consider exposing things to have tests\n'
103 'make that same call directly.\n'
104 'If that is not possible, you may put a comment on the same line with\n'
105 ' // IN-TEST \n'
106 'to tell the PRESUBMIT script that the code is inside a *ForTesting()\n'
107 'method and can be ignored. Do not do this inside production code.\n'
108 'The android-binary-size trybot will block if the method exists in the\n'
109 'release apk.')
[email protected]eea609a2011-11-18 13:10:12110
111
Daniel Chenga44a1bcd2022-03-15 20:00:15112@dataclass
113class BanRule:
114 # String pattern. If the pattern begins with a slash, the pattern will be
115 # treated as a regular expression instead.
116 pattern: str
117 # Explanation as a sequence of strings. Each string in the sequence will be
118 # printed on its own line.
119 explanation: Sequence[str]
120 # Whether or not to treat this ban as a fatal error. If unspecified, defaults
121 # to true.
122 treat_as_error: Optional[bool] = None
123 # Paths that should be excluded from the ban check. Each string is a regular
124 # expression that will be matched against the path of the file being checked
125 # relative to the root of the source tree.
126 excluded_paths: Optional[Sequence[str]] = None
[email protected]cf9b78f2012-11-14 11:40:28127
Daniel Chenga44a1bcd2022-03-15 20:00:15128
Daniel Cheng917ce542022-03-15 20:46:57129_BANNED_JAVA_IMPORTS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15130 BanRule(
131 'import java.net.URI;',
132 (
133 'Use org.chromium.url.GURL instead of java.net.URI, where possible.',
134 ),
135 excluded_paths=(
136 (r'net/android/javatests/src/org/chromium/net/'
137 'AndroidProxySelectorTest\.java'),
138 r'components/cronet/',
139 r'third_party/robolectric/local/',
140 ),
Michael Thiessen44457642020-02-06 00:24:15141 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15142 BanRule(
143 'import android.annotation.TargetApi;',
144 (
145 'Do not use TargetApi, use @androidx.annotation.RequiresApi instead. '
146 'RequiresApi ensures that any calls are guarded by the appropriate '
147 'SDK_INT check. See https://crbug.com/1116486.',
148 ),
149 ),
150 BanRule(
151 'import android.support.test.rule.UiThreadTestRule;',
152 (
153 'Do not use UiThreadTestRule, just use '
154 '@org.chromium.base.test.UiThreadTest on test methods that should run '
155 'on the UI thread. See https://crbug.com/1111893.',
156 ),
157 ),
158 BanRule(
159 'import android.support.test.annotation.UiThreadTest;',
160 ('Do not use android.support.test.annotation.UiThreadTest, use '
161 'org.chromium.base.test.UiThreadTest instead. See '
162 'https://crbug.com/1111893.',
163 ),
164 ),
165 BanRule(
166 'import android.support.test.rule.ActivityTestRule;',
167 (
168 'Do not use ActivityTestRule, use '
169 'org.chromium.base.test.BaseActivityTestRule instead.',
170 ),
171 excluded_paths=(
172 'components/cronet/',
173 ),
174 ),
175)
wnwenbdc444e2016-05-25 13:44:15176
Daniel Cheng917ce542022-03-15 20:46:57177_BANNED_JAVA_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15178 BanRule(
Eric Stevensona9a980972017-09-23 00:04:41179 'StrictMode.allowThreadDiskReads()',
180 (
181 'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
182 'directly.',
183 ),
184 False,
185 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15186 BanRule(
Eric Stevensona9a980972017-09-23 00:04:41187 'StrictMode.allowThreadDiskWrites()',
188 (
189 'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
190 'directly.',
191 ),
192 False,
193 ),
Daniel Cheng917ce542022-03-15 20:46:57194 BanRule(
Michael Thiessen0f2547e2020-07-27 21:55:36195 '.waitForIdleSync()',
196 (
197 'Do not use waitForIdleSync as it masks underlying issues. There is '
198 'almost always something else you should wait on instead.',
199 ),
200 False,
201 ),
Eric Stevensona9a980972017-09-23 00:04:41202)
203
Daniel Cheng917ce542022-03-15 20:46:57204_BANNED_OBJC_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15205 BanRule(
[email protected]127f18ec2012-06-16 05:05:59206 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:20207 (
208 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:59209 'prohibited. Please use CrTrackingArea instead.',
210 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
211 ),
212 False,
213 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15214 BanRule(
[email protected]eaae1972014-04-16 04:17:26215 r'/NSTrackingArea\W',
[email protected]23e6cbc2012-06-16 18:51:20216 (
217 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:59218 'instead.',
219 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
220 ),
221 False,
222 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15223 BanRule(
[email protected]127f18ec2012-06-16 05:05:59224 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20225 (
226 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59227 'Please use |convertPoint:(point) fromView:nil| instead.',
228 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
229 ),
230 True,
231 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15232 BanRule(
[email protected]127f18ec2012-06-16 05:05:59233 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:20234 (
235 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59236 'Please use |convertPoint:(point) toView:nil| instead.',
237 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
238 ),
239 True,
240 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15241 BanRule(
[email protected]127f18ec2012-06-16 05:05:59242 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20243 (
244 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59245 'Please use |convertRect:(point) fromView:nil| instead.',
246 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
247 ),
248 True,
249 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15250 BanRule(
[email protected]127f18ec2012-06-16 05:05:59251 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20252 (
253 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59254 'Please use |convertRect:(point) toView:nil| instead.',
255 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
256 ),
257 True,
258 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15259 BanRule(
[email protected]127f18ec2012-06-16 05:05:59260 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20261 (
262 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59263 'Please use |convertSize:(point) fromView:nil| instead.',
264 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
265 ),
266 True,
267 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15268 BanRule(
[email protected]127f18ec2012-06-16 05:05:59269 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20270 (
271 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59272 'Please use |convertSize:(point) toView:nil| instead.',
273 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
274 ),
275 True,
276 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15277 BanRule(
jif65398702016-10-27 10:19:48278 r"/\s+UTF8String\s*]",
279 (
280 'The use of -[NSString UTF8String] is dangerous as it can return null',
281 'even if |canBeConvertedToEncoding:NSUTF8StringEncoding| returns YES.',
282 'Please use |SysNSStringToUTF8| instead.',
283 ),
284 True,
285 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15286 BanRule(
Sylvain Defresne4cf1d182017-09-18 14:16:34287 r'__unsafe_unretained',
288 (
289 'The use of __unsafe_unretained is almost certainly wrong, unless',
290 'when interacting with NSFastEnumeration or NSInvocation.',
291 'Please use __weak in files build with ARC, nothing otherwise.',
292 ),
293 False,
294 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15295 BanRule(
Avi Drissman7382afa02019-04-29 23:27:13296 'freeWhenDone:NO',
297 (
298 'The use of "freeWhenDone:NO" with the NoCopy creation of ',
299 'Foundation types is prohibited.',
300 ),
301 True,
302 ),
[email protected]127f18ec2012-06-16 05:05:59303)
304
Sylvain Defresnea8b73d252018-02-28 15:45:54305_BANNED_IOS_OBJC_FUNCTIONS = (
Daniel Chenga44a1bcd2022-03-15 20:00:15306 BanRule(
Sylvain Defresnea8b73d252018-02-28 15:45:54307 r'/\bTEST[(]',
308 (
309 'TEST() macro should not be used in Objective-C++ code as it does not ',
310 'drain the autorelease pool at the end of the test. Use TEST_F() ',
311 'macro instead with a fixture inheriting from PlatformTest (or a ',
312 'typedef).'
313 ),
314 True,
315 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15316 BanRule(
Sylvain Defresnea8b73d252018-02-28 15:45:54317 r'/\btesting::Test\b',
318 (
319 'testing::Test should not be used in Objective-C++ code as it does ',
320 'not drain the autorelease pool at the end of the test. Use ',
321 'PlatformTest instead.'
322 ),
323 True,
324 ),
325)
326
Daniel Cheng917ce542022-03-15 20:46:57327_BANNED_IOS_EGTEST_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15328 BanRule(
Peter K. Lee6c03ccff2019-07-15 14:40:05329 r'/\bEXPECT_OCMOCK_VERIFY\b',
330 (
331 'EXPECT_OCMOCK_VERIFY should not be used in EarlGrey tests because ',
332 'it is meant for GTests. Use [mock verify] instead.'
333 ),
334 True,
335 ),
336)
337
Daniel Cheng917ce542022-03-15 20:46:57338_BANNED_CPP_FUNCTIONS : Sequence[BanRule] = (
Daniel Chenga44a1bcd2022-03-15 20:00:15339 BanRule(
Peter Kasting94a56c42019-10-25 21:54:04340 r'/\busing namespace ',
341 (
342 'Using directives ("using namespace x") are banned by the Google Style',
343 'Guide ( http://google.github.io/styleguide/cppguide.html#Namespaces ).',
344 'Explicitly qualify symbols or use using declarations ("using x::foo").',
345 ),
346 True,
347 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
348 ),
Antonio Gomes07300d02019-03-13 20:59:57349 # Make sure that gtest's FRIEND_TEST() macro is not used; the
350 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
351 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
Daniel Chenga44a1bcd2022-03-15 20:00:15352 BanRule(
[email protected]23e6cbc2012-06-16 18:51:20353 'FRIEND_TEST(',
354 (
[email protected]e3c945502012-06-26 20:01:49355 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20356 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
357 ),
358 False,
[email protected]7345da02012-11-27 14:31:49359 (),
[email protected]23e6cbc2012-06-16 18:51:20360 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15361 BanRule(
tomhudsone2c14d552016-05-26 17:07:46362 'setMatrixClip',
363 (
364 'Overriding setMatrixClip() is prohibited; ',
365 'the base function is deprecated. ',
366 ),
367 True,
368 (),
369 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15370 BanRule(
[email protected]52657f62013-05-20 05:30:31371 'SkRefPtr',
372 (
373 'The use of SkRefPtr is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22374 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31375 ),
376 True,
377 (),
378 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15379 BanRule(
[email protected]52657f62013-05-20 05:30:31380 'SkAutoRef',
381 (
382 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22383 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31384 ),
385 True,
386 (),
387 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15388 BanRule(
[email protected]52657f62013-05-20 05:30:31389 'SkAutoTUnref',
390 (
391 'The use of SkAutoTUnref is dangerous because it implicitly ',
tomhudson7e6e0512016-04-19 19:27:22392 'converts to a raw pointer. Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31393 ),
394 True,
395 (),
396 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15397 BanRule(
[email protected]52657f62013-05-20 05:30:31398 'SkAutoUnref',
399 (
400 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
401 'because it implicitly converts to a raw pointer. ',
tomhudson7e6e0512016-04-19 19:27:22402 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31403 ),
404 True,
405 (),
406 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15407 BanRule(
[email protected]d89eec82013-12-03 14:10:59408 r'/HANDLE_EINTR\(.*close',
409 (
410 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
411 'descriptor will be closed, and it is incorrect to retry the close.',
412 'Either call close directly and ignore its return value, or wrap close',
413 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
414 ),
415 True,
416 (),
417 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15418 BanRule(
[email protected]d89eec82013-12-03 14:10:59419 r'/IGNORE_EINTR\((?!.*close)',
420 (
421 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
422 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
423 ),
424 True,
425 (
426 # Files that #define IGNORE_EINTR.
Egor Paskoce145c42018-09-28 19:31:04427 r'^base[\\/]posix[\\/]eintr_wrapper\.h$',
428 r'^ppapi[\\/]tests[\\/]test_broker\.cc$',
[email protected]d89eec82013-12-03 14:10:59429 ),
430 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15431 BanRule(
[email protected]ec5b3f02014-04-04 18:43:43432 r'/v8::Extension\(',
433 (
434 'Do not introduce new v8::Extensions into the code base, use',
435 'gin::Wrappable instead. See http://crbug.com/334679',
436 ),
437 True,
[email protected]f55c90ee62014-04-12 00:50:03438 (
Egor Paskoce145c42018-09-28 19:31:04439 r'extensions[\\/]renderer[\\/]safe_builtins\.*',
[email protected]f55c90ee62014-04-12 00:50:03440 ),
[email protected]ec5b3f02014-04-04 18:43:43441 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15442 BanRule(
jame2d1a952016-04-02 00:27:10443 '#pragma comment(lib,',
444 (
445 'Specify libraries to link with in build files and not in the source.',
446 ),
447 True,
Mirko Bonadeif4f0f0e2018-04-12 09:29:41448 (
tzik3f295992018-12-04 20:32:23449 r'^base[\\/]third_party[\\/]symbolize[\\/].*',
Egor Paskoce145c42018-09-28 19:31:04450 r'^third_party[\\/]abseil-cpp[\\/].*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41451 ),
jame2d1a952016-04-02 00:27:10452 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15453 BanRule(
Gabriel Charette7cc6c432018-04-25 20:52:02454 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59455 (
456 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
457 ),
458 False,
459 (),
460 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15461 BanRule(
Gabriel Charette7cc6c432018-04-25 20:52:02462 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59463 (
464 'Consider using THREAD_CHECKER macros instead of the class directly.',
465 ),
466 False,
467 (),
468 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15469 BanRule(
Yuri Wiitala2f8de5c2017-07-21 00:11:06470 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
471 (
472 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
473 'deprecated (http://crbug.com/634507). Please avoid converting away',
474 'from the Time types in Chromium code, especially if any math is',
475 'being done on time values. For interfacing with platform/library',
476 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
477 'type converter methods instead. For faking TimeXXX values (for unit',
Peter Kasting53fd6ee2021-10-05 20:40:48478 'testing only), use TimeXXX() + Microseconds(N). For',
Yuri Wiitala2f8de5c2017-07-21 00:11:06479 'other use cases, please contact base/time/OWNERS.',
480 ),
481 False,
482 (),
483 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15484 BanRule(
dbeamb6f4fde2017-06-15 04:03:06485 'CallJavascriptFunctionUnsafe',
486 (
487 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
488 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
489 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
490 ),
491 False,
492 (
Egor Paskoce145c42018-09-28 19:31:04493 r'^content[\\/]browser[\\/]webui[\\/]web_ui_impl\.(cc|h)$',
494 r'^content[\\/]public[\\/]browser[\\/]web_ui\.h$',
495 r'^content[\\/]public[\\/]test[\\/]test_web_ui\.(cc|h)$',
dbeamb6f4fde2017-06-15 04:03:06496 ),
497 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15498 BanRule(
dskiba1474c2bfd62017-07-20 02:19:24499 'leveldb::DB::Open',
500 (
501 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
502 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
503 "Chrome's tracing, making their memory usage visible.",
504 ),
505 True,
506 (
507 r'^third_party/leveldatabase/.*\.(cc|h)$',
508 ),
Gabriel Charette0592c3a2017-07-26 12:02:04509 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15510 BanRule(
Chris Mumfordc38afb62017-10-09 17:55:08511 'leveldb::NewMemEnv',
512 (
513 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58514 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
515 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08516 ),
517 True,
518 (
519 r'^third_party/leveldatabase/.*\.(cc|h)$',
520 ),
521 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15522 BanRule(
Gabriel Charetted9839bc2017-07-29 14:17:47523 'RunLoop::QuitCurrent',
524 (
Robert Liao64b7ab22017-08-04 23:03:43525 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
526 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47527 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41528 False,
Gabriel Charetted9839bc2017-07-29 14:17:47529 (),
Gabriel Charettea44975052017-08-21 23:14:04530 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15531 BanRule(
Gabriel Charettea44975052017-08-21 23:14:04532 'base::ScopedMockTimeMessageLoopTaskRunner',
533 (
Gabriel Charette87cc1af2018-04-25 20:52:51534 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
Gabriel Charettedfa36042019-08-19 17:30:11535 'TaskEnvironment::TimeSource::MOCK_TIME. There are still a',
Gabriel Charette87cc1af2018-04-25 20:52:51536 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
537 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
538 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04539 ),
Gabriel Charette87cc1af2018-04-25 20:52:51540 False,
Gabriel Charettea44975052017-08-21 23:14:04541 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57542 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15543 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44544 'std::regex',
Eric Stevenson6b47b44c2017-08-30 20:41:57545 (
546 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02547 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57548 ),
549 True,
Danil Chapovalov7bc42a72020-12-09 18:20:16550 # Abseil's benchmarks never linked into chrome.
551 ['third_party/abseil-cpp/.*_benchmark.cc'],
Francois Doray43670e32017-09-27 12:40:38552 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15553 BanRule(
Peter Kasting991618a62019-06-17 22:00:09554 r'/\bstd::stoi\b',
555 (
556 'std::stoi uses exceptions to communicate results. ',
557 'Use base::StringToInt() instead.',
558 ),
559 True,
560 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
561 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15562 BanRule(
Peter Kasting991618a62019-06-17 22:00:09563 r'/\bstd::stol\b',
564 (
565 'std::stol uses exceptions to communicate results. ',
566 'Use base::StringToInt() instead.',
567 ),
568 True,
569 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
570 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15571 BanRule(
Peter Kasting991618a62019-06-17 22:00:09572 r'/\bstd::stoul\b',
573 (
574 'std::stoul uses exceptions to communicate results. ',
575 'Use base::StringToUint() instead.',
576 ),
577 True,
578 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
579 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15580 BanRule(
Peter Kasting991618a62019-06-17 22:00:09581 r'/\bstd::stoll\b',
582 (
583 'std::stoll uses exceptions to communicate results. ',
584 'Use base::StringToInt64() instead.',
585 ),
586 True,
587 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
588 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15589 BanRule(
Peter Kasting991618a62019-06-17 22:00:09590 r'/\bstd::stoull\b',
591 (
592 'std::stoull uses exceptions to communicate results. ',
593 'Use base::StringToUint64() instead.',
594 ),
595 True,
596 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
597 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15598 BanRule(
Peter Kasting991618a62019-06-17 22:00:09599 r'/\bstd::stof\b',
600 (
601 'std::stof uses exceptions to communicate results. ',
602 'For locale-independent values, e.g. reading numbers from disk',
603 'profiles, use base::StringToDouble().',
604 'For user-visible values, parse using ICU.',
605 ),
606 True,
607 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
608 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15609 BanRule(
Peter Kasting991618a62019-06-17 22:00:09610 r'/\bstd::stod\b',
611 (
612 'std::stod uses exceptions to communicate results. ',
613 'For locale-independent values, e.g. reading numbers from disk',
614 'profiles, use base::StringToDouble().',
615 'For user-visible values, parse using ICU.',
616 ),
617 True,
618 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
619 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15620 BanRule(
Peter Kasting991618a62019-06-17 22:00:09621 r'/\bstd::stold\b',
622 (
623 'std::stold uses exceptions to communicate results. ',
624 'For locale-independent values, e.g. reading numbers from disk',
625 'profiles, use base::StringToDouble().',
626 'For user-visible values, parse using ICU.',
627 ),
628 True,
629 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
630 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15631 BanRule(
Daniel Bratell69334cc2019-03-26 11:07:45632 r'/\bstd::to_string\b',
633 (
634 'std::to_string is locale dependent and slower than alternatives.',
Peter Kasting991618a62019-06-17 22:00:09635 'For locale-independent strings, e.g. writing numbers to disk',
636 'profiles, use base::NumberToString().',
Daniel Bratell69334cc2019-03-26 11:07:45637 'For user-visible strings, use base::FormatNumber() and',
638 'the related functions in base/i18n/number_formatting.h.',
639 ),
Peter Kasting991618a62019-06-17 22:00:09640 False, # Only a warning since it is already used.
Daniel Bratell609102be2019-03-27 20:53:21641 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45642 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15643 BanRule(
Daniel Bratell69334cc2019-03-26 11:07:45644 r'/\bstd::shared_ptr\b',
645 (
646 'std::shared_ptr should not be used. Use scoped_refptr instead.',
647 ),
648 True,
Ulan Degenbaev947043882021-02-10 14:02:31649 [
650 # Needed for interop with third-party library.
651 '^third_party/blink/renderer/core/typed_arrays/array_buffer/' +
Alex Chau9eb03cdd52020-07-13 21:04:57652 'array_buffer_contents\.(cc|h)',
Ben Kelly39bf6bef2021-10-04 22:54:58653 '^third_party/blink/renderer/bindings/core/v8/' +
654 'v8_wasm_response_extensions.cc',
Wez5f56be52021-05-04 09:30:58655 '^gin/array_buffer\.(cc|h)',
656 '^chrome/services/sharing/nearby/',
Meilin Wang00efc7c2021-05-13 01:12:42657 # gRPC provides some C++ libraries that use std::shared_ptr<>.
658 '^chromeos/services/libassistant/grpc/',
Vigen Issahhanjanfdf9de52021-12-22 21:13:59659 '^chromecast/cast_core/grpc',
660 '^chromecast/cast_core/runtime/browser',
Wez5f56be52021-05-04 09:30:58661 # Fuchsia provides C++ libraries that use std::shared_ptr<>.
662 '.*fuchsia.*test\.(cc|h)',
Will Cassella64da6c52022-01-06 18:13:57663 # Needed for clang plugin tests
664 '^tools/clang/plugins/tests/',
Alex Chau9eb03cdd52020-07-13 21:04:57665 _THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell609102be2019-03-27 20:53:21666 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15667 BanRule(
Peter Kasting991618a62019-06-17 22:00:09668 r'/\bstd::weak_ptr\b',
669 (
670 'std::weak_ptr should not be used. Use base::WeakPtr instead.',
671 ),
672 True,
673 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
674 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15675 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21676 r'/\blong long\b',
677 (
678 'long long is banned. Use stdint.h if you need a 64 bit number.',
679 ),
680 False, # Only a warning since it is already used.
681 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
682 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15683 BanRule(
Daniel Chengc05fcc62022-01-12 16:54:29684 r'\b(absl|std)::any\b',
685 (
Daniel Chenga44a1bcd2022-03-15 20:00:15686 'absl::any / std::any are not safe to use in a component build.',
Daniel Chengc05fcc62022-01-12 16:54:29687 ),
688 True,
689 # Not an error in third party folders, though it probably should be :)
690 [_THIRD_PARTY_EXCEPT_BLINK],
691 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15692 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21693 r'/\bstd::bind\b',
694 (
695 'std::bind is banned because of lifetime risks.',
696 'Use base::BindOnce or base::BindRepeating instead.',
697 ),
698 True,
699 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
700 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15701 BanRule(
Avi Drissman48ee39e2022-02-16 16:31:03702 r'/\bstd::optional\b',
703 (
704 'std::optional is banned. Use absl::optional instead.',
705 ),
706 True,
707 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
708 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15709 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21710 r'/\b#include <chrono>\b',
711 (
712 '<chrono> overlaps with Time APIs in base. Keep using',
713 'base classes.',
714 ),
715 True,
716 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
717 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15718 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21719 r'/\b#include <exception>\b',
720 (
721 'Exceptions are banned and disabled in Chromium.',
722 ),
723 True,
724 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
725 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15726 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21727 r'/\bstd::function\b',
728 (
Colin Blundellea615d422021-05-12 09:35:41729 'std::function is banned. Instead use base::OnceCallback or ',
730 'base::RepeatingCallback, which directly support Chromium\'s weak ',
731 'pointers, ref counting and more.',
Daniel Bratell609102be2019-03-27 20:53:21732 ),
Peter Kasting991618a62019-06-17 22:00:09733 False, # Only a warning since it is already used.
Daniel Bratell609102be2019-03-27 20:53:21734 [_THIRD_PARTY_EXCEPT_BLINK], # Do not warn in third_party folders.
735 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15736 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21737 r'/\b#include <random>\b',
738 (
739 'Do not use any random number engines from <random>. Instead',
740 'use base::RandomBitGenerator.',
741 ),
742 True,
743 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
744 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15745 BanRule(
Tom Andersona95e12042020-09-09 23:08:00746 r'/\b#include <X11/',
747 (
748 'Do not use Xlib. Use xproto (from //ui/gfx/x:xproto) instead.',
749 ),
750 True,
751 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
752 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15753 BanRule(
Daniel Bratell609102be2019-03-27 20:53:21754 r'/\bstd::ratio\b',
755 (
756 'std::ratio is banned by the Google Style Guide.',
757 ),
758 True,
759 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45760 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15761 BanRule(
Gabriel Charetted90bcc92021-09-21 00:23:10762 ('base::ThreadRestrictions::ScopedAllowIO'),
Francois Doray43670e32017-09-27 12:40:38763 (
Gabriel Charetted90bcc92021-09-21 00:23:10764 'ScopedAllowIO is deprecated, use ScopedAllowBlocking instead.',
Francois Doray43670e32017-09-27 12:40:38765 ),
Gabriel Charette04b138f2018-08-06 00:03:22766 False,
Francois Doray43670e32017-09-27 12:40:38767 (),
768 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15769 BanRule(
Michael Giuffrida7f93d6922019-04-19 14:39:58770 r'/\bRunMessageLoop\b',
Gabriel Charette147335ea2018-03-22 15:59:19771 (
772 'RunMessageLoop is deprecated, use RunLoop instead.',
773 ),
774 False,
775 (),
776 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15777 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44778 'RunThisRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19779 (
780 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
781 ),
782 False,
783 (),
784 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15785 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44786 'RunAllPendingInMessageLoop()',
Gabriel Charette147335ea2018-03-22 15:59:19787 (
788 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
789 "if you're convinced you need this.",
790 ),
791 False,
792 (),
793 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15794 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44795 'RunAllPendingInMessageLoop(BrowserThread',
Gabriel Charette147335ea2018-03-22 15:59:19796 (
797 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
Gabriel Charette798fde72019-08-20 22:24:04798 'BrowserThread::UI, BrowserTaskEnvironment::RunIOThreadUntilIdle',
Gabriel Charette147335ea2018-03-22 15:59:19799 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
800 'async events instead of flushing threads.',
801 ),
802 False,
803 (),
804 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15805 BanRule(
Gabriel Charette147335ea2018-03-22 15:59:19806 r'MessageLoopRunner',
807 (
808 'MessageLoopRunner is deprecated, use RunLoop instead.',
809 ),
810 False,
811 (),
812 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15813 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44814 'GetDeferredQuitTaskForRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19815 (
816 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
817 "gab@ if you found a use case where this is the only solution.",
818 ),
819 False,
820 (),
821 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15822 BanRule(
Victor Costane48a2e82019-03-15 22:02:34823 'sqlite3_initialize(',
Victor Costan3653df62018-02-08 21:38:16824 (
Victor Costane48a2e82019-03-15 22:02:34825 'Instead of calling sqlite3_initialize(), depend on //sql, ',
Victor Costan3653df62018-02-08 21:38:16826 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
827 ),
828 True,
829 (
830 r'^sql/initialization\.(cc|h)$',
831 r'^third_party/sqlite/.*\.(c|cc|h)$',
832 ),
833 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15834 BanRule(
Dave Tapuska98199b612019-07-10 13:30:44835 'std::random_shuffle',
tzik5de2157f2018-05-08 03:42:47836 (
837 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
838 'base::RandomShuffle instead.'
839 ),
840 True,
841 (),
842 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15843 BanRule(
Javier Ernesto Flores Robles749e6c22018-10-08 09:36:24844 'ios/web/public/test/http_server',
845 (
846 'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
847 ),
848 False,
849 (),
850 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15851 BanRule(
Robert Liao764c9492019-01-24 18:46:28852 'GetAddressOf',
853 (
854 'Improper use of Microsoft::WRL::ComPtr<T>::GetAddressOf() has been ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53855 'implicated in a few leaks. ReleaseAndGetAddressOf() is safe but ',
Joshua Berenhaus8b972ec2020-09-11 20:00:11856 'operator& is generally recommended. So always use operator& instead. ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53857 'See http://crbug.com/914910 for more conversion guidance.'
Robert Liao764c9492019-01-24 18:46:28858 ),
859 True,
860 (),
861 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15862 BanRule(
Ben Lewisa9514602019-04-29 17:53:05863 'SHFileOperation',
864 (
865 'SHFileOperation was deprecated in Windows Vista, and there are less ',
866 'complex functions to achieve the same goals. Use IFileOperation for ',
867 'any esoteric actions instead.'
868 ),
869 True,
870 (),
871 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15872 BanRule(
Cliff Smolinsky81951642019-04-30 21:39:51873 'StringFromGUID2',
874 (
875 'StringFromGUID2 introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:24876 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:51877 ),
878 True,
879 (
Daniel Chenga44a1bcd2022-03-15 20:00:15880 r'/base/win/win_util_unittest.cc',
Cliff Smolinsky81951642019-04-30 21:39:51881 ),
882 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15883 BanRule(
Cliff Smolinsky81951642019-04-30 21:39:51884 'StringFromCLSID',
885 (
886 'StringFromCLSID introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:24887 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:51888 ),
889 True,
890 (
Daniel Chenga44a1bcd2022-03-15 20:00:15891 r'/base/win/win_util_unittest.cc',
Cliff Smolinsky81951642019-04-30 21:39:51892 ),
893 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15894 BanRule(
Avi Drissman7382afa02019-04-29 23:27:13895 'kCFAllocatorNull',
896 (
897 'The use of kCFAllocatorNull with the NoCopy creation of ',
898 'CoreFoundation types is prohibited.',
899 ),
900 True,
901 (),
902 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15903 BanRule(
Oksana Zhuravlovafd247772019-05-16 16:57:29904 'mojo::ConvertTo',
905 (
906 'mojo::ConvertTo and TypeConverter are deprecated. Please consider',
907 'StructTraits / UnionTraits / EnumTraits / ArrayTraits / MapTraits /',
908 'StringTraits if you would like to convert between custom types and',
909 'the wire format of mojom types.'
910 ),
Oksana Zhuravlova1d3b59de2019-05-17 00:08:22911 False,
Oksana Zhuravlovafd247772019-05-16 16:57:29912 (
Wezf89dec092019-09-11 19:38:33913 r'^fuchsia/engine/browser/url_request_rewrite_rules_manager\.cc$',
914 r'^fuchsia/engine/url_request_rewrite_type_converters\.cc$',
Oksana Zhuravlovafd247772019-05-16 16:57:29915 r'^third_party/blink/.*\.(cc|h)$',
916 r'^content/renderer/.*\.(cc|h)$',
917 ),
918 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15919 BanRule(
Oksana Zhuravlovac8222d22019-12-19 19:21:16920 'GetInterfaceProvider',
921 (
922 'InterfaceProvider is deprecated.',
923 'Please use ExecutionContext::GetBrowserInterfaceBroker and overrides',
924 'or Platform::GetBrowserInterfaceBroker.'
925 ),
926 False,
927 (),
928 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15929 BanRule(
Robert Liao1d78df52019-11-11 20:02:01930 'CComPtr',
931 (
932 'New code should use Microsoft::WRL::ComPtr from wrl/client.h as a ',
933 'replacement for CComPtr from ATL. See http://crbug.com/5027 for more ',
934 'details.'
935 ),
936 False,
937 (),
938 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15939 BanRule(
Xiaohan Wang72bd2ba2020-02-18 21:38:20940 r'/\b(IFACE|STD)METHOD_?\(',
941 (
942 'IFACEMETHOD() and STDMETHOD() make code harder to format and read.',
943 'Instead, always use IFACEMETHODIMP in the declaration.'
944 ),
945 False,
946 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
947 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15948 BanRule(
Allen Bauer53b43fb12020-03-12 17:21:47949 'set_owned_by_client',
950 (
951 'set_owned_by_client is deprecated.',
952 'views::View already owns the child views by default. This introduces ',
953 'a competing ownership model which makes the code difficult to reason ',
954 'about. See http://crbug.com/1044687 for more details.'
955 ),
956 False,
957 (),
958 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15959 BanRule(
Peter Boström7ff41522021-07-29 03:43:27960 'RemoveAllChildViewsWithoutDeleting',
961 (
962 'RemoveAllChildViewsWithoutDeleting is deprecated.',
963 'This method is deemed dangerous as, unless raw pointers are re-added,',
964 'calls to this method introduce memory leaks.'
965 ),
966 False,
967 (),
968 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15969 BanRule(
Eric Secklerbe6f48d2020-05-06 18:09:12970 r'/\bTRACE_EVENT_ASYNC_',
971 (
972 'Please use TRACE_EVENT_NESTABLE_ASYNC_.. macros instead',
973 'of TRACE_EVENT_ASYNC_.. (crbug.com/1038710).',
974 ),
975 False,
976 (
977 r'^base/trace_event/.*',
978 r'^base/tracing/.*',
979 ),
980 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15981 BanRule(
Aditya Kushwah5a286b72022-02-10 04:54:43982 r'/\bbase::debug::DumpWithoutCrashingUnthrottled[(][)]',
983 (
984 'base::debug::DumpWithoutCrashingUnthrottled() does not throttle',
985 'dumps and may spam crash reports. Consider if the throttled',
986 'variants suffice instead.',
987 ),
988 False,
989 (),
990 ),
Daniel Chenga44a1bcd2022-03-15 20:00:15991 BanRule(
Robert Liao22f66a52021-04-10 00:57:52992 'RoInitialize',
993 (
Robert Liao48018922021-04-16 23:03:02994 'Improper use of [base::win]::RoInitialize() has been implicated in a ',
Robert Liao22f66a52021-04-10 00:57:52995 'few COM initialization leaks. Use base::win::ScopedWinrtInitializer ',
996 'instead. See http://crbug.com/1197722 for more information.'
997 ),
998 True,
Robert Liao48018922021-04-16 23:03:02999 (
Daniel Chenga44a1bcd2022-03-15 20:00:151000 r'^base[\\/]win[\\/]scoped_winrt_initializer\.cc$',
Robert Liao48018922021-04-16 23:03:021001 ),
Robert Liao22f66a52021-04-10 00:57:521002 ),
[email protected]127f18ec2012-06-16 05:05:591003)
1004
Daniel Cheng92c15e32022-03-16 17:48:221005_BANNED_MOJOM_PATTERNS : Sequence[BanRule] = (
1006 BanRule(
1007 'handle<shared_buffer>',
1008 (
1009 'Please use one of the more specific shared memory types instead:',
1010 ' mojo_base.mojom.ReadOnlySharedMemoryRegion',
1011 ' mojo_base.mojom.WritableSharedMemoryRegion',
1012 ' mojo_base.mojom.UnsafeSharedMemoryRegion',
1013 ),
1014 True,
1015 ),
1016)
1017
mlamouria82272622014-09-16 18:45:041018_IPC_ENUM_TRAITS_DEPRECATED = (
1019 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501020 'See http://www.chromium.org/Home/chromium-security/education/'
1021 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:041022
Stephen Martinis97a394142018-06-07 23:06:051023_LONG_PATH_ERROR = (
1024 'Some files included in this CL have file names that are too long (> 200'
1025 ' characters). If committed, these files will cause issues on Windows. See'
1026 ' https://crbug.com/612667 for more details.'
1027)
1028
Shenghua Zhangbfaa38b82017-11-16 21:58:021029_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
Scott Violet1dbd37e12021-05-14 16:35:041030 r".*[\\/]AppHooksImpl\.java",
Egor Paskoce145c42018-09-28 19:31:041031 r".*[\\/]BuildHooksAndroidImpl\.java",
1032 r".*[\\/]LicenseContentProvider\.java",
1033 r".*[\\/]PlatformServiceBridgeImpl.java",
Patrick Noland5475bc0d2018-10-01 20:04:281034 r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:021035]
[email protected]127f18ec2012-06-16 05:05:591036
Mohamed Heikald048240a2019-11-12 16:57:371037# List of image extensions that are used as resources in chromium.
1038_IMAGE_EXTENSIONS = ['.svg', '.png', '.webp']
1039
Sean Kau46e29bc2017-08-28 16:31:161040# These paths contain test data and other known invalid JSON files.
Erik Staab2dd72b12020-04-16 15:03:401041_KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS = [
Egor Paskoce145c42018-09-28 19:31:041042 r'test[\\/]data[\\/]',
Erik Staab2dd72b12020-04-16 15:03:401043 r'testing[\\/]buildbot[\\/]',
Egor Paskoce145c42018-09-28 19:31:041044 r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
1045 r'^third_party[\\/]protobuf[\\/]',
Egor Paskoce145c42018-09-28 19:31:041046 r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
Kent Tamura77578cc2018-11-25 22:33:431047 r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]',
Sean Kau46e29bc2017-08-28 16:31:161048]
1049
Andrew Grieveb773bad2020-06-05 18:00:381050# These are not checked on the public chromium-presubmit trybot.
1051# Add files here that rely on .py files that exists only for target_os="android"
Samuel Huangc2f5d6bb2020-08-17 23:46:041052# checkouts.
agrievef32bcc72016-04-04 14:57:401053_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Grieveb773bad2020-06-05 18:00:381054 'chrome/android/features/create_stripped_java_factory.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381055]
1056
1057
1058_GENERIC_PYDEPS_FILES = [
Samuel Huangc2f5d6bb2020-08-17 23:46:041059 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361060 'base/android/jni_generator/jni_generator.pydeps',
1061 'base/android/jni_generator/jni_registration_generator.pydeps',
Andrew Grieve4c4cede2020-11-20 22:09:361062 'build/android/apk_operations.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041063 'build/android/devil_chromium.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361064 'build/android/gyp/aar.pydeps',
1065 'build/android/gyp/aidl.pydeps',
Tibor Goldschwendt0bef2d7a2019-10-24 21:19:271066 'build/android/gyp/allot_native_libraries.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361067 'build/android/gyp/apkbuilder.pydeps',
Andrew Grievea417ad302019-02-06 19:54:381068 'build/android/gyp/assert_static_initializers.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361069 'build/android/gyp/bytecode_processor.pydeps',
Robbie McElrath360e54d2020-11-12 20:38:021070 'build/android/gyp/bytecode_rewriter.pydeps',
Mohamed Heikal6305bcc2021-03-15 15:34:221071 'build/android/gyp/check_flag_expectations.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111072 'build/android/gyp/compile_java.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361073 'build/android/gyp/compile_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361074 'build/android/gyp/copy_ex.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361075 'build/android/gyp/create_apk_operations_script.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111076 'build/android/gyp/create_app_bundle.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041077 'build/android/gyp/create_app_bundle_apks.pydeps',
1078 'build/android/gyp/create_bundle_wrapper_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361079 'build/android/gyp/create_java_binary_script.pydeps',
Mohamed Heikaladbe4e482020-07-09 19:25:121080 'build/android/gyp/create_r_java.pydeps',
Mohamed Heikal8cd763a52021-02-01 23:32:091081 'build/android/gyp/create_r_txt.pydeps',
Andrew Grieveb838d832019-02-11 16:55:221082 'build/android/gyp/create_size_info_files.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001083 'build/android/gyp/create_ui_locale_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361084 'build/android/gyp/dex.pydeps',
Andrew Grieve723c1502020-04-23 16:27:421085 'build/android/gyp/dex_jdk_libs.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041086 'build/android/gyp/dexsplitter.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361087 'build/android/gyp/dist_aar.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361088 'build/android/gyp/filter_zip.pydeps',
Mohamed Heikal21e1994b2021-11-12 21:37:211089 'build/android/gyp/flatc_java.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361090 'build/android/gyp/gcc_preprocess.pydeps',
Christopher Grant99e0e20062018-11-21 21:22:361091 'build/android/gyp/generate_linker_version_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361092 'build/android/gyp/ijar.pydeps',
Yun Liueb4075ddf2019-05-13 19:47:581093 'build/android/gyp/jacoco_instr.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361094 'build/android/gyp/java_cpp_enum.pydeps',
Nate Fischerac07b2622020-10-01 20:20:141095 'build/android/gyp/java_cpp_features.pydeps',
Ian Vollickb99472e2019-03-07 21:35:261096 'build/android/gyp/java_cpp_strings.pydeps',
Andrew Grieve09457912021-04-27 15:22:471097 'build/android/gyp/java_google_api_keys.pydeps',
Andrew Grieve5853fbd2020-02-20 17:26:011098 'build/android/gyp/jetify_jar.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041099 'build/android/gyp/jinja_template.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361100 'build/android/gyp/lint.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361101 'build/android/gyp/merge_manifest.pydeps',
1102 'build/android/gyp/prepare_resources.pydeps',
Mohamed Heikalf85138b2020-10-06 15:43:221103 'build/android/gyp/process_native_prebuilt.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361104 'build/android/gyp/proguard.pydeps',
Peter Wen578730b2020-03-19 19:55:461105 'build/android/gyp/turbine.pydeps',
Mohamed Heikal246710c2021-06-14 15:34:301106 'build/android/gyp/unused_resources.pydeps',
Eric Stevensona82cf6082019-07-24 14:35:241107 'build/android/gyp/validate_static_library_dex_references.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361108 'build/android/gyp/write_build_config.pydeps',
Tibor Goldschwendtc4caae92019-07-12 00:33:461109 'build/android/gyp/write_native_libraries_java.pydeps',
Andrew Grieve9ff17792018-11-30 04:55:561110 'build/android/gyp/zip.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361111 'build/android/incremental_install/generate_android_manifest.pydeps',
1112 'build/android/incremental_install/write_installer_json.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041113 'build/android/resource_sizes.pydeps',
1114 'build/android/test_runner.pydeps',
1115 'build/android/test_wrapper/logdog_wrapper.pydeps',
Samuel Huange65eb3f12020-08-14 19:04:361116 'build/lacros/lacros_resource_sizes.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361117 'build/protoc_java.pydeps',
Peter Kotwicz64667b02020-10-18 06:43:321118 'chrome/android/monochrome/scripts/monochrome_python_tests.pydeps',
Peter Wenefb56c72020-06-04 15:12:271119 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
1120 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Junbo Kedcd3a452021-03-19 17:55:041121 'chromecast/resource_sizes/chromecast_resource_sizes.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001122 'components/cronet/tools/generate_javadoc.pydeps',
1123 'components/cronet/tools/jar_src.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381124 'components/module_installer/android/module_desc_java.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001125 'content/public/android/generate_child_service.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381126 'net/tools/testserver/testserver.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041127 'testing/scripts/run_android_wpt.pydeps',
Peter Kotwicz3c339f32020-10-19 19:59:181128 'testing/scripts/run_isolated_script_test.pydeps',
Stephanie Kimc94072c2022-03-22 22:31:411129 'testing/merge_scripts/standard_isolated_script_merge.pydeps',
1130 'testing/merge_scripts/standard_gtest_merge.pydeps',
1131 'testing/merge_scripts/code_coverage/merge_results.pydeps',
1132 'testing/merge_scripts/code_coverage/merge_steps.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041133 'third_party/android_platform/development/scripts/stack.pydeps',
Hitoshi Yoshida0f228c42019-08-07 09:37:421134 'third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps',
1135 'third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps',
Yuki Shiinoe7827aa2019-09-13 12:26:131136 'third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps',
Canon Mukaif32f8f592021-04-23 18:56:501137 'third_party/blink/renderer/bindings/scripts/validate_web_idl.pydeps',
Stephanie Kimc94072c2022-03-22 22:31:411138 'third_party/blink/tools/blinkpy/web_tests/merge_results.pydeps',
1139 'third_party/blink/tools/merge_web_test_results.pydeps',
John Budorickbc3571aa2019-04-25 02:20:061140 'tools/binary_size/sizes.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:221141 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:401142]
1143
wnwenbdc444e2016-05-25 13:44:151144
agrievef32bcc72016-04-04 14:57:401145_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
1146
1147
Eric Boren6fd2b932018-01-25 15:05:081148# Bypass the AUTHORS check for these accounts.
1149_KNOWN_ROBOTS = set(
Sergiy Byelozyorov47158a52018-06-13 22:38:591150 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:451151 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:591152 ) | set('%[email protected]' % s
smutde797052019-12-04 02:03:521153 for s in ('bling-autoroll-builder', 'v8-ci-autoroll-builder',
Sven Zhengf7abd31d2021-08-09 19:06:231154 'wpt-autoroller', 'chrome-weblayer-builder',
Garrett Beaty4d4fcf62021-11-24 17:57:471155 'lacros-version-skew-roller', 'skylab-test-cros-roller',
Jieting Yang668bde92022-01-27 18:40:431156 'infra-try-recipes-tester', 'lacros-tracking-roller')
Eric Boren835d71f2018-09-07 21:09:041157 ) | set('%[email protected]' % s
Eric Boren66150e52020-01-08 11:20:271158 for s in ('chromium-autoroll', 'chromium-release-autoroll')
Eric Boren835d71f2018-09-07 21:09:041159 ) | set('%[email protected]' % s
Yulan Lineb0cfba2021-04-09 18:43:161160 for s in ('chromium-internal-autoroll',)
1161 ) | set('%[email protected]' % s
1162 for s in ('swarming-tasks',))
Eric Boren6fd2b932018-01-25 15:05:081163
Matt Stark6ef08872021-07-29 01:21:461164_INVALID_GRD_FILE_LINE = [
1165 (r'<file lang=.* path=.*', 'Path should come before lang in GRD files.')
1166]
Eric Boren6fd2b932018-01-25 15:05:081167
Daniel Bratell65b033262019-04-23 08:17:061168def _IsCPlusPlusFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501169 """Returns True if this file contains C++-like code (and not Python,
1170 Go, Java, MarkDown, ...)"""
Daniel Bratell65b033262019-04-23 08:17:061171
Sam Maiera6e76d72022-02-11 21:43:501172 ext = input_api.os_path.splitext(file_path)[1]
1173 # This list is compatible with CppChecker.IsCppFile but we should
1174 # consider adding ".c" to it. If we do that we can use this function
1175 # at more places in the code.
1176 return ext in (
1177 '.h',
1178 '.cc',
1179 '.cpp',
1180 '.m',
1181 '.mm',
1182 )
1183
Daniel Bratell65b033262019-04-23 08:17:061184
1185def _IsCPlusPlusHeaderFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501186 return input_api.os_path.splitext(file_path)[1] == ".h"
Daniel Bratell65b033262019-04-23 08:17:061187
1188
1189def _IsJavaFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501190 return input_api.os_path.splitext(file_path)[1] == ".java"
Daniel Bratell65b033262019-04-23 08:17:061191
1192
1193def _IsProtoFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501194 return input_api.os_path.splitext(file_path)[1] == ".proto"
Daniel Bratell65b033262019-04-23 08:17:061195
Mohamed Heikal5e5b7922020-10-29 18:57:591196
Erik Staabc734cd7a2021-11-23 03:11:521197def _IsXmlOrGrdFile(input_api, file_path):
Sam Maiera6e76d72022-02-11 21:43:501198 ext = input_api.os_path.splitext(file_path)[1]
1199 return ext in ('.grd', '.xml')
Erik Staabc734cd7a2021-11-23 03:11:521200
1201
Mohamed Heikal5e5b7922020-10-29 18:57:591202def CheckNoUpstreamDepsOnClank(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501203 """Prevent additions of dependencies from the upstream repo on //clank."""
1204 # clank can depend on clank
1205 if input_api.change.RepositoryRoot().endswith('clank'):
1206 return []
1207 build_file_patterns = [
1208 r'(.+/)?BUILD\.gn',
1209 r'.+\.gni',
1210 ]
1211 excluded_files = [r'build[/\\]config[/\\]android[/\\]config\.gni']
1212 bad_pattern = input_api.re.compile(r'^[^#]*//clank')
Mohamed Heikal5e5b7922020-10-29 18:57:591213
Sam Maiera6e76d72022-02-11 21:43:501214 error_message = 'Disallowed import on //clank in an upstream build file:'
Mohamed Heikal5e5b7922020-10-29 18:57:591215
Sam Maiera6e76d72022-02-11 21:43:501216 def FilterFile(affected_file):
1217 return input_api.FilterSourceFile(affected_file,
1218 files_to_check=build_file_patterns,
1219 files_to_skip=excluded_files)
Mohamed Heikal5e5b7922020-10-29 18:57:591220
Sam Maiera6e76d72022-02-11 21:43:501221 problems = []
1222 for f in input_api.AffectedSourceFiles(FilterFile):
1223 local_path = f.LocalPath()
1224 for line_number, line in f.ChangedContents():
1225 if (bad_pattern.search(line)):
1226 problems.append('%s:%d\n %s' %
1227 (local_path, line_number, line.strip()))
1228 if problems:
1229 return [output_api.PresubmitPromptOrNotify(error_message, problems)]
1230 else:
1231 return []
Mohamed Heikal5e5b7922020-10-29 18:57:591232
1233
Saagar Sanghavifceeaae2020-08-12 16:40:361234def CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501235 """Attempts to prevent use of functions intended only for testing in
1236 non-testing code. For now this is just a best-effort implementation
1237 that ignores header files and may have some false positives. A
1238 better implementation would probably need a proper C++ parser.
1239 """
1240 # We only scan .cc files and the like, as the declaration of
1241 # for-testing functions in header files are hard to distinguish from
1242 # calls to such functions without a proper C++ parser.
1243 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:191244
Sam Maiera6e76d72022-02-11 21:43:501245 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
1246 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' %
1247 base_function_pattern)
1248 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
1249 allowlist_pattern = input_api.re.compile(r'// IN-TEST$')
1250 exclusion_pattern = input_api.re.compile(
1251 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' %
1252 (base_function_pattern, base_function_pattern))
1253 # Avoid a false positive in this case, where the method name, the ::, and
1254 # the closing { are all on different lines due to line wrapping.
1255 # HelperClassForTesting::
1256 # HelperClassForTesting(
1257 # args)
1258 # : member(0) {}
1259 method_defn_pattern = input_api.re.compile(r'[A-Za-z0-9_]+::$')
[email protected]55459852011-08-10 15:17:191260
Sam Maiera6e76d72022-02-11 21:43:501261 def FilterFile(affected_file):
1262 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1263 input_api.DEFAULT_FILES_TO_SKIP)
1264 return input_api.FilterSourceFile(
1265 affected_file,
1266 files_to_check=file_inclusion_pattern,
1267 files_to_skip=files_to_skip)
[email protected]55459852011-08-10 15:17:191268
Sam Maiera6e76d72022-02-11 21:43:501269 problems = []
1270 for f in input_api.AffectedSourceFiles(FilterFile):
1271 local_path = f.LocalPath()
1272 in_method_defn = False
1273 for line_number, line in f.ChangedContents():
1274 if (inclusion_pattern.search(line)
1275 and not comment_pattern.search(line)
1276 and not exclusion_pattern.search(line)
1277 and not allowlist_pattern.search(line)
1278 and not in_method_defn):
1279 problems.append('%s:%d\n %s' %
1280 (local_path, line_number, line.strip()))
1281 in_method_defn = method_defn_pattern.search(line)
[email protected]55459852011-08-10 15:17:191282
Sam Maiera6e76d72022-02-11 21:43:501283 if problems:
1284 return [
1285 output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)
1286 ]
1287 else:
1288 return []
[email protected]55459852011-08-10 15:17:191289
1290
Saagar Sanghavifceeaae2020-08-12 16:40:361291def CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501292 """This is a simplified version of
1293 CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
1294 """
1295 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
1296 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
1297 name_pattern = r'ForTest(s|ing)?'
1298 # Describes an occurrence of "ForTest*" inside a // comment.
1299 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
1300 # Describes @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
1301 annotation_re = input_api.re.compile(r'@VisibleForTesting\(')
1302 # Catch calls.
1303 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
1304 # Ignore definitions. (Comments are ignored separately.)
1305 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
Vaclav Brozek7dbc28c2018-03-27 08:35:231306
Sam Maiera6e76d72022-02-11 21:43:501307 problems = []
1308 sources = lambda x: input_api.FilterSourceFile(
1309 x,
1310 files_to_skip=(('(?i).*test', r'.*\/junit\/') + input_api.
1311 DEFAULT_FILES_TO_SKIP),
1312 files_to_check=[r'.*\.java$'])
1313 for f in input_api.AffectedFiles(include_deletes=False,
1314 file_filter=sources):
1315 local_path = f.LocalPath()
Vaclav Brozek7dbc28c2018-03-27 08:35:231316 is_inside_javadoc = False
Sam Maiera6e76d72022-02-11 21:43:501317 for line_number, line in f.ChangedContents():
1318 if is_inside_javadoc and javadoc_end_re.search(line):
1319 is_inside_javadoc = False
1320 if not is_inside_javadoc and javadoc_start_re.search(line):
1321 is_inside_javadoc = True
1322 if is_inside_javadoc:
1323 continue
1324 if (inclusion_re.search(line) and not comment_re.search(line)
1325 and not annotation_re.search(line)
1326 and not exclusion_re.search(line)):
1327 problems.append('%s:%d\n %s' %
1328 (local_path, line_number, line.strip()))
Vaclav Brozek7dbc28c2018-03-27 08:35:231329
Sam Maiera6e76d72022-02-11 21:43:501330 if problems:
1331 return [
1332 output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)
1333 ]
1334 else:
1335 return []
Vaclav Brozek7dbc28c2018-03-27 08:35:231336
1337
Saagar Sanghavifceeaae2020-08-12 16:40:361338def CheckNoIOStreamInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501339 """Checks to make sure no .h files include <iostream>."""
1340 files = []
1341 pattern = input_api.re.compile(r'^#include\s*<iostream>',
1342 input_api.re.MULTILINE)
1343 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1344 if not f.LocalPath().endswith('.h'):
1345 continue
1346 contents = input_api.ReadFile(f)
1347 if pattern.search(contents):
1348 files.append(f)
[email protected]10689ca2011-09-02 02:31:541349
Sam Maiera6e76d72022-02-11 21:43:501350 if len(files):
1351 return [
1352 output_api.PresubmitError(
1353 'Do not #include <iostream> in header files, since it inserts static '
1354 'initialization into every file including the header. Instead, '
1355 '#include <ostream>. See http://crbug.com/94794', files)
1356 ]
1357 return []
1358
[email protected]10689ca2011-09-02 02:31:541359
Danil Chapovalov3518f362018-08-11 16:13:431360def _CheckNoStrCatRedefines(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501361 """Checks no windows headers with StrCat redefined are included directly."""
1362 files = []
1363 pattern_deny = input_api.re.compile(
1364 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
1365 input_api.re.MULTILINE)
1366 pattern_allow = input_api.re.compile(
1367 r'^#include\s"base/win/windows_defines.inc"', input_api.re.MULTILINE)
1368 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1369 contents = input_api.ReadFile(f)
1370 if pattern_deny.search(
1371 contents) and not pattern_allow.search(contents):
1372 files.append(f.LocalPath())
Danil Chapovalov3518f362018-08-11 16:13:431373
Sam Maiera6e76d72022-02-11 21:43:501374 if len(files):
1375 return [
1376 output_api.PresubmitError(
1377 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
1378 'directly since they pollute code with StrCat macro. Instead, '
1379 'include matching header from base/win. See http://crbug.com/856536',
1380 files)
1381 ]
1382 return []
Danil Chapovalov3518f362018-08-11 16:13:431383
[email protected]10689ca2011-09-02 02:31:541384
Saagar Sanghavifceeaae2020-08-12 16:40:361385def CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501386 """Checks to make sure no source files use UNIT_TEST."""
1387 problems = []
1388 for f in input_api.AffectedFiles():
1389 if (not f.LocalPath().endswith(('.cc', '.mm'))):
1390 continue
[email protected]72df4e782012-06-21 16:28:181391
Sam Maiera6e76d72022-02-11 21:43:501392 for line_num, line in f.ChangedContents():
1393 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
1394 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]72df4e782012-06-21 16:28:181395
Sam Maiera6e76d72022-02-11 21:43:501396 if not problems:
1397 return []
1398 return [
1399 output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
1400 '\n'.join(problems))
1401 ]
1402
[email protected]72df4e782012-06-21 16:28:181403
Saagar Sanghavifceeaae2020-08-12 16:40:361404def CheckNoDISABLETypoInTests(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501405 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
Dominic Battre033531052018-09-24 15:45:341406
Sam Maiera6e76d72022-02-11 21:43:501407 This test warns if somebody tries to disable a test with the DISABLE_ prefix
1408 instead of DISABLED_. To filter false positives, reports are only generated
1409 if a corresponding MAYBE_ line exists.
1410 """
1411 problems = []
Dominic Battre033531052018-09-24 15:45:341412
Sam Maiera6e76d72022-02-11 21:43:501413 # The following two patterns are looked for in tandem - is a test labeled
1414 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
1415 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
1416 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
Dominic Battre033531052018-09-24 15:45:341417
Sam Maiera6e76d72022-02-11 21:43:501418 # This is for the case that a test is disabled on all platforms.
1419 full_disable_pattern = input_api.re.compile(
1420 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
1421 input_api.re.MULTILINE)
Dominic Battre033531052018-09-24 15:45:341422
Sam Maiera6e76d72022-02-11 21:43:501423 for f in input_api.AffectedFiles(False):
1424 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1425 continue
Dominic Battre033531052018-09-24 15:45:341426
Sam Maiera6e76d72022-02-11 21:43:501427 # Search for MABYE_, DISABLE_ pairs.
1428 disable_lines = {} # Maps of test name to line number.
1429 maybe_lines = {}
1430 for line_num, line in f.ChangedContents():
1431 disable_match = disable_pattern.search(line)
1432 if disable_match:
1433 disable_lines[disable_match.group(1)] = line_num
1434 maybe_match = maybe_pattern.search(line)
1435 if maybe_match:
1436 maybe_lines[maybe_match.group(1)] = line_num
Dominic Battre033531052018-09-24 15:45:341437
Sam Maiera6e76d72022-02-11 21:43:501438 # Search for DISABLE_ occurrences within a TEST() macro.
1439 disable_tests = set(disable_lines.keys())
1440 maybe_tests = set(maybe_lines.keys())
1441 for test in disable_tests.intersection(maybe_tests):
1442 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
Dominic Battre033531052018-09-24 15:45:341443
Sam Maiera6e76d72022-02-11 21:43:501444 contents = input_api.ReadFile(f)
1445 full_disable_match = full_disable_pattern.search(contents)
1446 if full_disable_match:
1447 problems.append(' %s' % f.LocalPath())
Dominic Battre033531052018-09-24 15:45:341448
Sam Maiera6e76d72022-02-11 21:43:501449 if not problems:
1450 return []
1451 return [
1452 output_api.PresubmitPromptWarning(
1453 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
1454 '\n'.join(problems))
1455 ]
1456
Dominic Battre033531052018-09-24 15:45:341457
Nina Satragnof7660532021-09-20 18:03:351458def CheckForgettingMAYBEInTests(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501459 """Checks to make sure tests disabled conditionally are not missing a
1460 corresponding MAYBE_ prefix.
1461 """
1462 # Expect at least a lowercase character in the test name. This helps rule out
1463 # false positives with macros wrapping the actual tests name.
1464 define_maybe_pattern = input_api.re.compile(
1465 r'^\#define MAYBE_(?P<test_name>\w*[a-z]\w*)')
1466 test_maybe_pattern = r'^\s*\w*TEST[^(]*\(\s*\w+,\s*MAYBE_{test_name}\)'
1467 suite_maybe_pattern = r'^\s*\w*TEST[^(]*\(\s*MAYBE_{test_name}[\),]'
1468 warnings = []
Nina Satragnof7660532021-09-20 18:03:351469
Sam Maiera6e76d72022-02-11 21:43:501470 # Read the entire files. We can't just read the affected lines, forgetting to
1471 # add MAYBE_ on a change would not show up otherwise.
1472 for f in input_api.AffectedFiles(False):
1473 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1474 continue
1475 contents = input_api.ReadFile(f)
1476 lines = contents.splitlines(True)
1477 current_position = 0
1478 warning_test_names = set()
1479 for line_num, line in enumerate(lines, start=1):
1480 current_position += len(line)
1481 maybe_match = define_maybe_pattern.search(line)
1482 if maybe_match:
1483 test_name = maybe_match.group('test_name')
1484 # Do not warn twice for the same test.
1485 if (test_name in warning_test_names):
1486 continue
1487 warning_test_names.add(test_name)
Nina Satragnof7660532021-09-20 18:03:351488
Sam Maiera6e76d72022-02-11 21:43:501489 # Attempt to find the corresponding MAYBE_ test or suite, starting from
1490 # the current position.
1491 test_match = input_api.re.compile(
1492 test_maybe_pattern.format(test_name=test_name),
1493 input_api.re.MULTILINE).search(contents, current_position)
1494 suite_match = input_api.re.compile(
1495 suite_maybe_pattern.format(test_name=test_name),
1496 input_api.re.MULTILINE).search(contents, current_position)
1497 if not test_match and not suite_match:
1498 warnings.append(
1499 output_api.PresubmitPromptWarning(
1500 '%s:%d found MAYBE_ defined without corresponding test %s'
1501 % (f.LocalPath(), line_num, test_name)))
1502 return warnings
1503
[email protected]72df4e782012-06-21 16:28:181504
Saagar Sanghavifceeaae2020-08-12 16:40:361505def CheckDCHECK_IS_ONHasBraces(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501506 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
1507 errors = []
1508 pattern = input_api.re.compile(r'DCHECK_IS_ON\b(?!\(\))',
1509 input_api.re.MULTILINE)
1510 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1511 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
1512 continue
1513 for lnum, line in f.ChangedContents():
1514 if input_api.re.search(pattern, line):
1515 errors.append(
1516 output_api.PresubmitError((
1517 '%s:%d: Use of DCHECK_IS_ON() must be written as "#if '
1518 + 'DCHECK_IS_ON()", not forgetting the parentheses.') %
1519 (f.LocalPath(), lnum)))
1520 return errors
danakj61c1aa22015-10-26 19:55:521521
1522
Weilun Shia487fad2020-10-28 00:10:341523# TODO(crbug/1138055): Reimplement CheckUmaHistogramChangesOnUpload check in a
1524# more reliable way. See
1525# https://chromium-review.googlesource.com/c/chromium/src/+/2500269
mcasasb7440c282015-02-04 14:52:191526
wnwenbdc444e2016-05-25 13:44:151527
Saagar Sanghavifceeaae2020-08-12 16:40:361528def CheckFlakyTestUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501529 """Check that FlakyTest annotation is our own instead of the android one"""
1530 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1531 files = []
1532 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1533 if f.LocalPath().endswith('Test.java'):
1534 if pattern.search(input_api.ReadFile(f)):
1535 files.append(f)
1536 if len(files):
1537 return [
1538 output_api.PresubmitError(
1539 'Use org.chromium.base.test.util.FlakyTest instead of '
1540 'android.test.FlakyTest', files)
1541 ]
1542 return []
mcasasb7440c282015-02-04 14:52:191543
wnwenbdc444e2016-05-25 13:44:151544
Saagar Sanghavifceeaae2020-08-12 16:40:361545def CheckNoDEPSGIT(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501546 """Make sure .DEPS.git is never modified manually."""
1547 if any(f.LocalPath().endswith('.DEPS.git')
1548 for f in input_api.AffectedFiles()):
1549 return [
1550 output_api.PresubmitError(
1551 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1552 'automated system based on what\'s in DEPS and your changes will be\n'
1553 'overwritten.\n'
1554 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1555 'get-the-code#Rolling_DEPS\n'
1556 'for more information')
1557 ]
1558 return []
[email protected]2a8ac9c2011-10-19 17:20:441559
1560
Saagar Sanghavifceeaae2020-08-12 16:40:361561def CheckValidHostsInDEPSOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501562 """Checks that DEPS file deps are from allowed_hosts."""
1563 # Run only if DEPS file has been modified to annoy fewer bystanders.
1564 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1565 return []
1566 # Outsource work to gclient verify
1567 try:
1568 gclient_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
1569 'third_party', 'depot_tools',
1570 'gclient.py')
1571 input_api.subprocess.check_output(
1572 [input_api.python_executable, gclient_path, 'verify'],
1573 stderr=input_api.subprocess.STDOUT)
1574 return []
1575 except input_api.subprocess.CalledProcessError as error:
1576 return [
1577 output_api.PresubmitError(
1578 'DEPS file must have only git dependencies.',
1579 long_text=error.output)
1580 ]
tandriief664692014-09-23 14:51:471581
1582
Mario Sanchez Prada2472cab2019-09-18 10:58:311583def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
Daniel Chenga44a1bcd2022-03-15 20:00:151584 ban_rule):
Sam Maiera6e76d72022-02-11 21:43:501585 """Helper method for CheckNoBannedFunctions and CheckNoDeprecatedMojoTypes.
Mario Sanchez Prada2472cab2019-09-18 10:58:311586
Sam Maiera6e76d72022-02-11 21:43:501587 Returns an string composed of the name of the file, the line number where the
1588 match has been found and the additional text passed as |message| in case the
1589 target type name matches the text inside the line passed as parameter.
1590 """
1591 result = []
Peng Huang9c5949a02020-06-11 19:20:541592
Daniel Chenga44a1bcd2022-03-15 20:00:151593 # Ignore comments about banned types.
1594 if input_api.re.search(r"^ *//", line):
Sam Maiera6e76d72022-02-11 21:43:501595 return result
Daniel Chenga44a1bcd2022-03-15 20:00:151596 # A // nocheck comment will bypass this error.
1597 if line.endswith(" nocheck"):
Sam Maiera6e76d72022-02-11 21:43:501598 return result
1599
1600 matched = False
Daniel Chenga44a1bcd2022-03-15 20:00:151601 if ban_rule.pattern[0:1] == '/':
1602 regex = ban_rule.pattern[1:]
Sam Maiera6e76d72022-02-11 21:43:501603 if input_api.re.search(regex, line):
1604 matched = True
Daniel Chenga44a1bcd2022-03-15 20:00:151605 elif ban_rule.pattern in line:
Sam Maiera6e76d72022-02-11 21:43:501606 matched = True
1607
1608 if matched:
1609 result.append(' %s:%d:' % (affected_file.LocalPath(), line_number))
Daniel Chenga44a1bcd2022-03-15 20:00:151610 for line in ban_rule.explanation:
1611 result.append(' %s' % line)
Sam Maiera6e76d72022-02-11 21:43:501612
danakjd18e8892020-12-17 17:42:011613 return result
Mario Sanchez Prada2472cab2019-09-18 10:58:311614
1615
Saagar Sanghavifceeaae2020-08-12 16:40:361616def CheckNoBannedFunctions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501617 """Make sure that banned functions are not used."""
1618 warnings = []
1619 errors = []
[email protected]127f18ec2012-06-16 05:05:591620
Sam Maiera6e76d72022-02-11 21:43:501621 def IsExcludedFile(affected_file, excluded_paths):
Daniel Chenga44a1bcd2022-03-15 20:00:151622 if not excluded_paths:
1623 return False
1624
Sam Maiera6e76d72022-02-11 21:43:501625 local_path = affected_file.LocalPath()
1626 for item in excluded_paths:
1627 if input_api.re.match(item, local_path):
1628 return True
1629 return False
wnwenbdc444e2016-05-25 13:44:151630
Sam Maiera6e76d72022-02-11 21:43:501631 def IsIosObjcFile(affected_file):
1632 local_path = affected_file.LocalPath()
1633 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m',
1634 '.h'):
1635 return False
1636 basename = input_api.os_path.basename(local_path)
1637 if 'ios' in basename.split('_'):
1638 return True
1639 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1640 if sep and 'ios' in local_path.split(sep):
1641 return True
1642 return False
Sylvain Defresnea8b73d252018-02-28 15:45:541643
Daniel Chenga44a1bcd2022-03-15 20:00:151644 def CheckForMatch(affected_file, line_num: int, line: str,
1645 ban_rule: BanRule):
1646 if IsExcludedFile(affected_file, ban_rule.excluded_paths):
1647 return
1648
Sam Maiera6e76d72022-02-11 21:43:501649 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
Daniel Chenga44a1bcd2022-03-15 20:00:151650 ban_rule)
Sam Maiera6e76d72022-02-11 21:43:501651 if problems:
Daniel Chenga44a1bcd2022-03-15 20:00:151652 if ban_rule.treat_as_error is not None and ban_rule.treat_as_error:
Sam Maiera6e76d72022-02-11 21:43:501653 errors.extend(problems)
1654 else:
1655 warnings.extend(problems)
wnwenbdc444e2016-05-25 13:44:151656
Sam Maiera6e76d72022-02-11 21:43:501657 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1658 for f in input_api.AffectedFiles(file_filter=file_filter):
1659 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151660 for ban_rule in _BANNED_JAVA_FUNCTIONS:
1661 CheckForMatch(f, line_num, line, ban_rule)
Eric Stevensona9a980972017-09-23 00:04:411662
Sam Maiera6e76d72022-02-11 21:43:501663 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1664 for f in input_api.AffectedFiles(file_filter=file_filter):
1665 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151666 for ban_rule in _BANNED_OBJC_FUNCTIONS:
1667 CheckForMatch(f, line_num, line, ban_rule)
[email protected]127f18ec2012-06-16 05:05:591668
Sam Maiera6e76d72022-02-11 21:43:501669 for f in input_api.AffectedFiles(file_filter=IsIosObjcFile):
1670 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151671 for ban_rule in _BANNED_IOS_OBJC_FUNCTIONS:
1672 CheckForMatch(f, line_num, line, ban_rule)
Sylvain Defresnea8b73d252018-02-28 15:45:541673
Sam Maiera6e76d72022-02-11 21:43:501674 egtest_filter = lambda f: f.LocalPath().endswith(('_egtest.mm'))
1675 for f in input_api.AffectedFiles(file_filter=egtest_filter):
1676 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151677 for ban_rule in _BANNED_IOS_EGTEST_FUNCTIONS:
1678 CheckForMatch(f, line_num, line, ban_rule)
Peter K. Lee6c03ccff2019-07-15 14:40:051679
Sam Maiera6e76d72022-02-11 21:43:501680 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1681 for f in input_api.AffectedFiles(file_filter=file_filter):
1682 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151683 for ban_rule in _BANNED_CPP_FUNCTIONS:
1684 CheckForMatch(f, line_num, line, ban_rule)
[email protected]127f18ec2012-06-16 05:05:591685
Daniel Cheng92c15e32022-03-16 17:48:221686 file_filter = lambda f: f.LocalPath().endswith(('.mojom'))
1687 for f in input_api.AffectedFiles(file_filter=file_filter):
1688 for line_num, line in f.ChangedContents():
1689 for ban_rule in _BANNED_MOJOM_PATTERNS:
1690 CheckForMatch(f, line_num, line, ban_rule)
1691
1692
Sam Maiera6e76d72022-02-11 21:43:501693 result = []
1694 if (warnings):
1695 result.append(
1696 output_api.PresubmitPromptWarning('Banned functions were used.\n' +
1697 '\n'.join(warnings)))
1698 if (errors):
1699 result.append(
1700 output_api.PresubmitError('Banned functions were used.\n' +
1701 '\n'.join(errors)))
1702 return result
[email protected]127f18ec2012-06-16 05:05:591703
1704
Michael Thiessen44457642020-02-06 00:24:151705def _CheckAndroidNoBannedImports(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501706 """Make sure that banned java imports are not used."""
1707 errors = []
Michael Thiessen44457642020-02-06 00:24:151708
Sam Maiera6e76d72022-02-11 21:43:501709 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1710 for f in input_api.AffectedFiles(file_filter=file_filter):
1711 for line_num, line in f.ChangedContents():
Daniel Chenga44a1bcd2022-03-15 20:00:151712 for ban_rule in _BANNED_JAVA_IMPORTS:
1713 # Consider merging this into the above function. There is no
1714 # real difference anymore other than helping with a little
1715 # bit of boilerplate text. Doing so means things like
1716 # `treat_as_error` will also be uniformly handled.
Sam Maiera6e76d72022-02-11 21:43:501717 problems = _GetMessageForMatchingType(input_api, f, line_num,
Daniel Chenga44a1bcd2022-03-15 20:00:151718 line, ban_rule)
Sam Maiera6e76d72022-02-11 21:43:501719 if problems:
1720 errors.extend(problems)
1721 result = []
1722 if (errors):
1723 result.append(
1724 output_api.PresubmitError('Banned imports were used.\n' +
1725 '\n'.join(errors)))
1726 return result
Michael Thiessen44457642020-02-06 00:24:151727
1728
Saagar Sanghavifceeaae2020-08-12 16:40:361729def CheckNoPragmaOnce(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501730 """Make sure that banned functions are not used."""
1731 files = []
1732 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
1733 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1734 if not f.LocalPath().endswith('.h'):
1735 continue
1736 contents = input_api.ReadFile(f)
1737 if pattern.search(contents):
1738 files.append(f)
[email protected]6c063c62012-07-11 19:11:061739
Sam Maiera6e76d72022-02-11 21:43:501740 if files:
1741 return [
1742 output_api.PresubmitError(
1743 'Do not use #pragma once in header files.\n'
1744 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1745 files)
1746 ]
1747 return []
[email protected]6c063c62012-07-11 19:11:061748
[email protected]127f18ec2012-06-16 05:05:591749
Saagar Sanghavifceeaae2020-08-12 16:40:361750def CheckNoTrinaryTrueFalse(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501751 """Checks to make sure we don't introduce use of foo ? true : false."""
1752 problems = []
1753 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1754 for f in input_api.AffectedFiles():
1755 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1756 continue
[email protected]e7479052012-09-19 00:26:121757
Sam Maiera6e76d72022-02-11 21:43:501758 for line_num, line in f.ChangedContents():
1759 if pattern.match(line):
1760 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]e7479052012-09-19 00:26:121761
Sam Maiera6e76d72022-02-11 21:43:501762 if not problems:
1763 return []
1764 return [
1765 output_api.PresubmitPromptWarning(
1766 'Please consider avoiding the "? true : false" pattern if possible.\n'
1767 + '\n'.join(problems))
1768 ]
[email protected]e7479052012-09-19 00:26:121769
1770
Saagar Sanghavifceeaae2020-08-12 16:40:361771def CheckUnwantedDependencies(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501772 """Runs checkdeps on #include and import statements added in this
1773 change. Breaking - rules is an error, breaking ! rules is a
1774 warning.
1775 """
1776 # Return early if no relevant file types were modified.
1777 for f in input_api.AffectedFiles():
1778 path = f.LocalPath()
1779 if (_IsCPlusPlusFile(input_api, path) or _IsProtoFile(input_api, path)
1780 or _IsJavaFile(input_api, path)):
1781 break
[email protected]55f9f382012-07-31 11:02:181782 else:
Sam Maiera6e76d72022-02-11 21:43:501783 return []
rhalavati08acd232017-04-03 07:23:281784
Sam Maiera6e76d72022-02-11 21:43:501785 import sys
1786 # We need to wait until we have an input_api object and use this
1787 # roundabout construct to import checkdeps because this file is
1788 # eval-ed and thus doesn't have __file__.
1789 original_sys_path = sys.path
1790 try:
1791 sys.path = sys.path + [
1792 input_api.os_path.join(input_api.PresubmitLocalPath(),
1793 'buildtools', 'checkdeps')
1794 ]
1795 import checkdeps
1796 from rules import Rule
1797 finally:
1798 # Restore sys.path to what it was before.
1799 sys.path = original_sys_path
[email protected]55f9f382012-07-31 11:02:181800
Sam Maiera6e76d72022-02-11 21:43:501801 added_includes = []
1802 added_imports = []
1803 added_java_imports = []
1804 for f in input_api.AffectedFiles():
1805 if _IsCPlusPlusFile(input_api, f.LocalPath()):
1806 changed_lines = [line for _, line in f.ChangedContents()]
1807 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
1808 elif _IsProtoFile(input_api, f.LocalPath()):
1809 changed_lines = [line for _, line in f.ChangedContents()]
1810 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
1811 elif _IsJavaFile(input_api, f.LocalPath()):
1812 changed_lines = [line for _, line in f.ChangedContents()]
1813 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:241814
Sam Maiera6e76d72022-02-11 21:43:501815 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
1816
1817 error_descriptions = []
1818 warning_descriptions = []
1819 error_subjects = set()
1820 warning_subjects = set()
1821
1822 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1823 added_includes):
1824 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
1825 description_with_path = '%s\n %s' % (path, rule_description)
1826 if rule_type == Rule.DISALLOW:
1827 error_descriptions.append(description_with_path)
1828 error_subjects.add("#includes")
1829 else:
1830 warning_descriptions.append(description_with_path)
1831 warning_subjects.add("#includes")
1832
1833 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1834 added_imports):
1835 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
1836 description_with_path = '%s\n %s' % (path, rule_description)
1837 if rule_type == Rule.DISALLOW:
1838 error_descriptions.append(description_with_path)
1839 error_subjects.add("imports")
1840 else:
1841 warning_descriptions.append(description_with_path)
1842 warning_subjects.add("imports")
1843
1844 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
1845 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
1846 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
1847 description_with_path = '%s\n %s' % (path, rule_description)
1848 if rule_type == Rule.DISALLOW:
1849 error_descriptions.append(description_with_path)
1850 error_subjects.add("imports")
1851 else:
1852 warning_descriptions.append(description_with_path)
1853 warning_subjects.add("imports")
1854
1855 results = []
1856 if error_descriptions:
1857 results.append(
1858 output_api.PresubmitError(
1859 'You added one or more %s that violate checkdeps rules.' %
1860 " and ".join(error_subjects), error_descriptions))
1861 if warning_descriptions:
1862 results.append(
1863 output_api.PresubmitPromptOrNotify(
1864 'You added one or more %s of files that are temporarily\n'
1865 'allowed but being removed. Can you avoid introducing the\n'
1866 '%s? See relevant DEPS file(s) for details and contacts.' %
1867 (" and ".join(warning_subjects), "/".join(warning_subjects)),
1868 warning_descriptions))
1869 return results
[email protected]55f9f382012-07-31 11:02:181870
1871
Saagar Sanghavifceeaae2020-08-12 16:40:361872def CheckFilePermissions(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501873 """Check that all files have their permissions properly set."""
1874 if input_api.platform == 'win32':
1875 return []
1876 checkperms_tool = input_api.os_path.join(input_api.PresubmitLocalPath(),
1877 'tools', 'checkperms',
1878 'checkperms.py')
1879 args = [
1880 input_api.python_executable, checkperms_tool, '--root',
1881 input_api.change.RepositoryRoot()
1882 ]
1883 with input_api.CreateTemporaryFile() as file_list:
1884 for f in input_api.AffectedFiles():
1885 # checkperms.py file/directory arguments must be relative to the
1886 # repository.
1887 file_list.write((f.LocalPath() + '\n').encode('utf8'))
1888 file_list.close()
1889 args += ['--file-list', file_list.name]
1890 try:
1891 input_api.subprocess.check_output(args)
1892 return []
1893 except input_api.subprocess.CalledProcessError as error:
1894 return [
1895 output_api.PresubmitError('checkperms.py failed:',
1896 long_text=error.output.decode(
1897 'utf-8', 'ignore'))
1898 ]
[email protected]fbcafe5a2012-08-08 15:31:221899
1900
Saagar Sanghavifceeaae2020-08-12 16:40:361901def CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501902 """Makes sure we don't include ui/aura/window_property.h
1903 in header files.
1904 """
1905 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1906 errors = []
1907 for f in input_api.AffectedFiles():
1908 if not f.LocalPath().endswith('.h'):
1909 continue
1910 for line_num, line in f.ChangedContents():
1911 if pattern.match(line):
1912 errors.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]c8278b32012-10-30 20:35:491913
Sam Maiera6e76d72022-02-11 21:43:501914 results = []
1915 if errors:
1916 results.append(
1917 output_api.PresubmitError(
1918 'Header files should not include ui/aura/window_property.h',
1919 errors))
1920 return results
[email protected]c8278b32012-10-30 20:35:491921
1922
Omer Katzcc77ea92021-04-26 10:23:281923def CheckNoInternalHeapIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501924 """Makes sure we don't include any headers from
1925 third_party/blink/renderer/platform/heap/impl or
1926 third_party/blink/renderer/platform/heap/v8_wrapper from files outside of
1927 third_party/blink/renderer/platform/heap
1928 """
1929 impl_pattern = input_api.re.compile(
1930 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/impl/.*"')
1931 v8_wrapper_pattern = input_api.re.compile(
1932 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/v8_wrapper/.*"'
1933 )
1934 file_filter = lambda f: not input_api.re.match(
1935 r"^third_party[\\/]blink[\\/]renderer[\\/]platform[\\/]heap[\\/].*",
1936 f.LocalPath())
1937 errors = []
Omer Katzcc77ea92021-04-26 10:23:281938
Sam Maiera6e76d72022-02-11 21:43:501939 for f in input_api.AffectedFiles(file_filter=file_filter):
1940 for line_num, line in f.ChangedContents():
1941 if impl_pattern.match(line) or v8_wrapper_pattern.match(line):
1942 errors.append(' %s:%d' % (f.LocalPath(), line_num))
Omer Katzcc77ea92021-04-26 10:23:281943
Sam Maiera6e76d72022-02-11 21:43:501944 results = []
1945 if errors:
1946 results.append(
1947 output_api.PresubmitError(
1948 'Do not include files from third_party/blink/renderer/platform/heap/impl'
1949 ' or third_party/blink/renderer/platform/heap/v8_wrapper. Use the '
1950 'relevant counterparts from third_party/blink/renderer/platform/heap',
1951 errors))
1952 return results
Omer Katzcc77ea92021-04-26 10:23:281953
1954
[email protected]70ca77752012-11-20 03:45:031955def _CheckForVersionControlConflictsInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:501956 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1957 errors = []
1958 for line_num, line in f.ChangedContents():
1959 if f.LocalPath().endswith(('.md', '.rst', '.txt')):
1960 # First-level headers in markdown look a lot like version control
1961 # conflict markers. http://daringfireball.net/projects/markdown/basics
1962 continue
1963 if pattern.match(line):
1964 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1965 return errors
[email protected]70ca77752012-11-20 03:45:031966
1967
Saagar Sanghavifceeaae2020-08-12 16:40:361968def CheckForVersionControlConflicts(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501969 """Usually this is not intentional and will cause a compile failure."""
1970 errors = []
1971 for f in input_api.AffectedFiles():
1972 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
[email protected]70ca77752012-11-20 03:45:031973
Sam Maiera6e76d72022-02-11 21:43:501974 results = []
1975 if errors:
1976 results.append(
1977 output_api.PresubmitError(
1978 'Version control conflict markers found, please resolve.',
1979 errors))
1980 return results
[email protected]70ca77752012-11-20 03:45:031981
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201982
Saagar Sanghavifceeaae2020-08-12 16:40:361983def CheckGoogleSupportAnswerUrlOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:501984 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1985 errors = []
1986 for f in input_api.AffectedFiles():
1987 for line_num, line in f.ChangedContents():
1988 if pattern.search(line):
1989 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
estadee17314a02017-01-12 16:22:161990
Sam Maiera6e76d72022-02-11 21:43:501991 results = []
1992 if errors:
1993 results.append(
1994 output_api.PresubmitPromptWarning(
1995 'Found Google support URL addressed by answer number. Please replace '
1996 'with a p= identifier instead. See crbug.com/679462\n',
1997 errors))
1998 return results
estadee17314a02017-01-12 16:22:161999
[email protected]70ca77752012-11-20 03:45:032000
Saagar Sanghavifceeaae2020-08-12 16:40:362001def CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502002 def FilterFile(affected_file):
2003 """Filter function for use with input_api.AffectedSourceFiles,
2004 below. This filters out everything except non-test files from
2005 top-level directories that generally speaking should not hard-code
2006 service URLs (e.g. src/android_webview/, src/content/ and others).
2007 """
2008 return input_api.FilterSourceFile(
2009 affected_file,
2010 files_to_check=[r'^(android_webview|base|content|net)[\\/].*'],
2011 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2012 input_api.DEFAULT_FILES_TO_SKIP))
[email protected]06e6d0ff2012-12-11 01:36:442013
Sam Maiera6e76d72022-02-11 21:43:502014 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
2015 '\.(com|net)[^"]*"')
2016 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
2017 pattern = input_api.re.compile(base_pattern)
2018 problems = [] # items are (filename, line_number, line)
2019 for f in input_api.AffectedSourceFiles(FilterFile):
2020 for line_num, line in f.ChangedContents():
2021 if not comment_pattern.search(line) and pattern.search(line):
2022 problems.append((f.LocalPath(), line_num, line))
[email protected]06e6d0ff2012-12-11 01:36:442023
Sam Maiera6e76d72022-02-11 21:43:502024 if problems:
2025 return [
2026 output_api.PresubmitPromptOrNotify(
2027 'Most layers below src/chrome/ should not hardcode service URLs.\n'
2028 'Are you sure this is correct?', [
2029 ' %s:%d: %s' % (problem[0], problem[1], problem[2])
2030 for problem in problems
2031 ])
2032 ]
2033 else:
2034 return []
[email protected]06e6d0ff2012-12-11 01:36:442035
2036
Saagar Sanghavifceeaae2020-08-12 16:40:362037def CheckChromeOsSyncedPrefRegistration(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502038 """Warns if Chrome OS C++ files register syncable prefs as browser prefs."""
James Cook6b6597c2019-11-06 22:05:292039
Sam Maiera6e76d72022-02-11 21:43:502040 def FileFilter(affected_file):
2041 """Includes directories known to be Chrome OS only."""
2042 return input_api.FilterSourceFile(
2043 affected_file,
2044 files_to_check=(
2045 '^ash/',
2046 '^chromeos/', # Top-level src/chromeos.
2047 '.*/chromeos/', # Any path component.
2048 '^components/arc',
2049 '^components/exo'),
2050 files_to_skip=(input_api.DEFAULT_FILES_TO_SKIP))
James Cook6b6597c2019-11-06 22:05:292051
Sam Maiera6e76d72022-02-11 21:43:502052 prefs = []
2053 priority_prefs = []
2054 for f in input_api.AffectedFiles(file_filter=FileFilter):
2055 for line_num, line in f.ChangedContents():
2056 if input_api.re.search('PrefRegistrySyncable::SYNCABLE_PREF',
2057 line):
2058 prefs.append(' %s:%d:' % (f.LocalPath(), line_num))
2059 prefs.append(' %s' % line)
2060 if input_api.re.search(
2061 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF', line):
2062 priority_prefs.append(' %s:%d' % (f.LocalPath(), line_num))
2063 priority_prefs.append(' %s' % line)
2064
2065 results = []
2066 if (prefs):
2067 results.append(
2068 output_api.PresubmitPromptWarning(
2069 'Preferences were registered as SYNCABLE_PREF and will be controlled '
2070 'by browser sync settings. If these prefs should be controlled by OS '
2071 'sync settings use SYNCABLE_OS_PREF instead.\n' +
2072 '\n'.join(prefs)))
2073 if (priority_prefs):
2074 results.append(
2075 output_api.PresubmitPromptWarning(
2076 'Preferences were registered as SYNCABLE_PRIORITY_PREF and will be '
2077 'controlled by browser sync settings. If these prefs should be '
2078 'controlled by OS sync settings use SYNCABLE_OS_PRIORITY_PREF '
2079 'instead.\n' + '\n'.join(prefs)))
2080 return results
James Cook6b6597c2019-11-06 22:05:292081
2082
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492083# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362084def CheckNoAbbreviationInPngFileName(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502085 """Makes sure there are no abbreviations in the name of PNG files.
2086 The native_client_sdk directory is excluded because it has auto-generated PNG
2087 files for documentation.
2088 """
2089 errors = []
2090 files_to_check = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
2091 files_to_skip = [r'^native_client_sdk[\\/]']
2092 file_filter = lambda f: input_api.FilterSourceFile(
2093 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
2094 for f in input_api.AffectedFiles(include_deletes=False,
2095 file_filter=file_filter):
2096 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:272097
Sam Maiera6e76d72022-02-11 21:43:502098 results = []
2099 if errors:
2100 results.append(
2101 output_api.PresubmitError(
2102 'The name of PNG files should not have abbreviations. \n'
2103 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
2104 'Contact [email protected] if you have questions.', errors))
2105 return results
[email protected]d2530012013-01-25 16:39:272106
2107
Daniel Cheng4dcdb6b2017-04-13 08:30:172108def _ExtractAddRulesFromParsedDeps(parsed_deps):
Sam Maiera6e76d72022-02-11 21:43:502109 """Extract the rules that add dependencies from a parsed DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:172110
Sam Maiera6e76d72022-02-11 21:43:502111 Args:
2112 parsed_deps: the locals dictionary from evaluating the DEPS file."""
2113 add_rules = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:172114 add_rules.update([
Sam Maiera6e76d72022-02-11 21:43:502115 rule[1:] for rule in parsed_deps.get('include_rules', [])
Daniel Cheng4dcdb6b2017-04-13 08:30:172116 if rule.startswith('+') or rule.startswith('!')
2117 ])
Sam Maiera6e76d72022-02-11 21:43:502118 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
2119 add_rules.update([
2120 rule[1:] for rule in rules
2121 if rule.startswith('+') or rule.startswith('!')
2122 ])
2123 return add_rules
Daniel Cheng4dcdb6b2017-04-13 08:30:172124
2125
2126def _ParseDeps(contents):
Sam Maiera6e76d72022-02-11 21:43:502127 """Simple helper for parsing DEPS files."""
Daniel Cheng4dcdb6b2017-04-13 08:30:172128
Sam Maiera6e76d72022-02-11 21:43:502129 # Stubs for handling special syntax in the root DEPS file.
2130 class _VarImpl:
2131 def __init__(self, local_scope):
2132 self._local_scope = local_scope
Daniel Cheng4dcdb6b2017-04-13 08:30:172133
Sam Maiera6e76d72022-02-11 21:43:502134 def Lookup(self, var_name):
2135 """Implements the Var syntax."""
2136 try:
2137 return self._local_scope['vars'][var_name]
2138 except KeyError:
2139 raise Exception('Var is not defined: %s' % var_name)
Daniel Cheng4dcdb6b2017-04-13 08:30:172140
Sam Maiera6e76d72022-02-11 21:43:502141 local_scope = {}
2142 global_scope = {
2143 'Var': _VarImpl(local_scope).Lookup,
2144 'Str': str,
2145 }
Dirk Pranke1b9e06382021-05-14 01:16:222146
Sam Maiera6e76d72022-02-11 21:43:502147 exec(contents, global_scope, local_scope)
2148 return local_scope
Daniel Cheng4dcdb6b2017-04-13 08:30:172149
2150
2151def _CalculateAddedDeps(os_path, old_contents, new_contents):
Sam Maiera6e76d72022-02-11 21:43:502152 """Helper method for CheckAddedDepsHaveTargetApprovals. Returns
2153 a set of DEPS entries that we should look up.
[email protected]14a6131c2014-01-08 01:15:412154
Sam Maiera6e76d72022-02-11 21:43:502155 For a directory (rather than a specific filename) we fake a path to
2156 a specific filename by adding /DEPS. This is chosen as a file that
2157 will seldom or never be subject to per-file include_rules.
2158 """
2159 # We ignore deps entries on auto-generated directories.
2160 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:082161
Sam Maiera6e76d72022-02-11 21:43:502162 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
2163 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Daniel Cheng4dcdb6b2017-04-13 08:30:172164
Sam Maiera6e76d72022-02-11 21:43:502165 added_deps = new_deps.difference(old_deps)
Daniel Cheng4dcdb6b2017-04-13 08:30:172166
Sam Maiera6e76d72022-02-11 21:43:502167 results = set()
2168 for added_dep in added_deps:
2169 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
2170 continue
2171 # Assume that a rule that ends in .h is a rule for a specific file.
2172 if added_dep.endswith('.h'):
2173 results.add(added_dep)
2174 else:
2175 results.add(os_path.join(added_dep, 'DEPS'))
2176 return results
[email protected]f32e2d1e2013-07-26 21:39:082177
2178
Saagar Sanghavifceeaae2020-08-12 16:40:362179def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502180 """When a dependency prefixed with + is added to a DEPS file, we
2181 want to make sure that the change is reviewed by an OWNER of the
2182 target file or directory, to avoid layering violations from being
2183 introduced. This check verifies that this happens.
2184 """
2185 # We rely on Gerrit's code-owners to check approvals.
2186 # input_api.gerrit is always set for Chromium, but other projects
2187 # might not use Gerrit.
2188 if not input_api.gerrit:
2189 return []
2190 if (input_api.change.issue and input_api.gerrit.IsOwnersOverrideApproved(
2191 input_api.change.issue)):
2192 # Skip OWNERS check when Owners-Override label is approved. This is intended
2193 # for global owners, trusted bots, and on-call sheriffs. Review is still
2194 # required for these changes.
2195 return []
Edward Lesmes6fba51082021-01-20 04:20:232196
Sam Maiera6e76d72022-02-11 21:43:502197 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:242198
Sam Maiera6e76d72022-02-11 21:43:502199 file_filter = lambda f: not input_api.re.match(
2200 r"^third_party[\\/]blink[\\/].*", f.LocalPath())
2201 for f in input_api.AffectedFiles(include_deletes=False,
2202 file_filter=file_filter):
2203 filename = input_api.os_path.basename(f.LocalPath())
2204 if filename == 'DEPS':
2205 virtual_depended_on_files.update(
2206 _CalculateAddedDeps(input_api.os_path,
2207 '\n'.join(f.OldContents()),
2208 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:552209
Sam Maiera6e76d72022-02-11 21:43:502210 if not virtual_depended_on_files:
2211 return []
[email protected]e871964c2013-05-13 14:14:552212
Sam Maiera6e76d72022-02-11 21:43:502213 if input_api.is_committing:
2214 if input_api.tbr:
2215 return [
2216 output_api.PresubmitNotifyResult(
2217 '--tbr was specified, skipping OWNERS check for DEPS additions'
2218 )
2219 ]
2220 if input_api.dry_run:
2221 return [
2222 output_api.PresubmitNotifyResult(
2223 'This is a dry run, skipping OWNERS check for DEPS additions'
2224 )
2225 ]
2226 if not input_api.change.issue:
2227 return [
2228 output_api.PresubmitError(
2229 "DEPS approval by OWNERS check failed: this change has "
2230 "no change number, so we can't check it for approvals.")
2231 ]
2232 output = output_api.PresubmitError
[email protected]14a6131c2014-01-08 01:15:412233 else:
Sam Maiera6e76d72022-02-11 21:43:502234 output = output_api.PresubmitNotifyResult
[email protected]e871964c2013-05-13 14:14:552235
Sam Maiera6e76d72022-02-11 21:43:502236 owner_email, reviewers = (
2237 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
2238 input_api, None, approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:552239
Sam Maiera6e76d72022-02-11 21:43:502240 owner_email = owner_email or input_api.change.author_email
2241
2242 approval_status = input_api.owners_client.GetFilesApprovalStatus(
2243 virtual_depended_on_files, reviewers.union([owner_email]), [])
2244 missing_files = [
2245 f for f in virtual_depended_on_files
2246 if approval_status[f] != input_api.owners_client.APPROVED
2247 ]
2248
2249 # We strip the /DEPS part that was added by
2250 # _FilesToCheckForIncomingDeps to fake a path to a file in a
2251 # directory.
2252 def StripDeps(path):
2253 start_deps = path.rfind('/DEPS')
2254 if start_deps != -1:
2255 return path[:start_deps]
2256 else:
2257 return path
2258
2259 unapproved_dependencies = [
2260 "'+%s'," % StripDeps(path) for path in missing_files
2261 ]
2262
2263 if unapproved_dependencies:
2264 output_list = [
2265 output(
2266 'You need LGTM from owners of depends-on paths in DEPS that were '
2267 'modified in this CL:\n %s' %
2268 '\n '.join(sorted(unapproved_dependencies)))
2269 ]
2270 suggested_owners = input_api.owners_client.SuggestOwners(
2271 missing_files, exclude=[owner_email])
2272 output_list.append(
2273 output('Suggested missing target path OWNERS:\n %s' %
2274 '\n '.join(suggested_owners or [])))
2275 return output_list
2276
2277 return []
[email protected]e871964c2013-05-13 14:14:552278
2279
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492280# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362281def CheckSpamLogging(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502282 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
2283 files_to_skip = (
2284 _EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2285 input_api.DEFAULT_FILES_TO_SKIP + (
2286 r"^base[\\/]logging\.h$",
2287 r"^base[\\/]logging\.cc$",
2288 r"^base[\\/]task[\\/]thread_pool[\\/]task_tracker\.cc$",
2289 r"^chrome[\\/]app[\\/]chrome_main_delegate\.cc$",
2290 r"^chrome[\\/]browser[\\/]chrome_browser_main\.cc$",
2291 r"^chrome[\\/]browser[\\/]ui[\\/]startup[\\/]"
2292 r"startup_browser_creator\.cc$",
2293 r"^chrome[\\/]browser[\\/]browser_switcher[\\/]bho[\\/].*",
2294 r"^chrome[\\/]browser[\\/]diagnostics[\\/]" +
2295 r"diagnostics_writer\.cc$",
2296 r"^chrome[\\/]chrome_cleaner[\\/].*",
2297 r"^chrome[\\/]chrome_elf[\\/]dll_hash[\\/]" +
2298 r"dll_hash_main\.cc$",
2299 r"^chrome[\\/]installer[\\/]setup[\\/].*",
2300 r"^chromecast[\\/]",
Sam Maiera6e76d72022-02-11 21:43:502301 r"^components[\\/]browser_watcher[\\/]"
2302 r"dump_stability_report_main_win.cc$",
2303 r"^components[\\/]media_control[\\/]renderer[\\/]"
2304 r"media_playback_options\.cc$",
2305 r"^components[\\/]viz[\\/]service[\\/]display[\\/]"
2306 r"overlay_strategy_underlay_cast\.cc$",
2307 r"^components[\\/]zucchini[\\/].*",
2308 # TODO(peter): Remove exception. https://crbug.com/534537
2309 r"^content[\\/]browser[\\/]notifications[\\/]"
2310 r"notification_event_dispatcher_impl\.cc$",
2311 r"^content[\\/]common[\\/]gpu[\\/]client[\\/]"
2312 r"gl_helper_benchmark\.cc$",
2313 r"^courgette[\\/]courgette_minimal_tool\.cc$",
2314 r"^courgette[\\/]courgette_tool\.cc$",
2315 r"^extensions[\\/]renderer[\\/]logging_native_handler\.cc$",
2316 r"^fuchsia[\\/]base[\\/]init_logging.cc$",
2317 r"^fuchsia[\\/]engine[\\/]browser[\\/]frame_impl.cc$",
2318 r"^fuchsia[\\/]runners[\\/]common[\\/]web_component.cc$",
2319 r"^headless[\\/]app[\\/]headless_shell\.cc$",
2320 r"^ipc[\\/]ipc_logging\.cc$",
2321 r"^native_client_sdk[\\/]",
2322 r"^remoting[\\/]base[\\/]logging\.h$",
2323 r"^remoting[\\/]host[\\/].*",
2324 r"^sandbox[\\/]linux[\\/].*",
2325 r"^storage[\\/]browser[\\/]file_system[\\/]" +
2326 r"dump_file_system.cc$",
2327 r"^tools[\\/]",
2328 r"^ui[\\/]base[\\/]resource[\\/]data_pack.cc$",
2329 r"^ui[\\/]aura[\\/]bench[\\/]bench_main\.cc$",
2330 r"^ui[\\/]ozone[\\/]platform[\\/]cast[\\/]",
2331 r"^ui[\\/]base[\\/]x[\\/]xwmstartupcheck[\\/]"
2332 r"xwmstartupcheck\.cc$"))
2333 source_file_filter = lambda x: input_api.FilterSourceFile(
2334 x, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
[email protected]85218562013-11-22 07:41:402335
Sam Maiera6e76d72022-02-11 21:43:502336 log_info = set([])
2337 printf = set([])
[email protected]85218562013-11-22 07:41:402338
Sam Maiera6e76d72022-02-11 21:43:502339 for f in input_api.AffectedSourceFiles(source_file_filter):
2340 for _, line in f.ChangedContents():
2341 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
2342 log_info.add(f.LocalPath())
2343 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
2344 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:372345
Sam Maiera6e76d72022-02-11 21:43:502346 if input_api.re.search(r"\bprintf\(", line):
2347 printf.add(f.LocalPath())
2348 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
2349 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:402350
Sam Maiera6e76d72022-02-11 21:43:502351 if log_info:
2352 return [
2353 output_api.PresubmitError(
2354 'These files spam the console log with LOG(INFO):',
2355 items=log_info)
2356 ]
2357 if printf:
2358 return [
2359 output_api.PresubmitError(
2360 'These files spam the console log with printf/fprintf:',
2361 items=printf)
2362 ]
2363 return []
[email protected]85218562013-11-22 07:41:402364
2365
Saagar Sanghavifceeaae2020-08-12 16:40:362366def CheckForAnonymousVariables(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502367 """These types are all expected to hold locks while in scope and
2368 so should never be anonymous (which causes them to be immediately
2369 destroyed)."""
2370 they_who_must_be_named = [
2371 'base::AutoLock',
2372 'base::AutoReset',
2373 'base::AutoUnlock',
2374 'SkAutoAlphaRestore',
2375 'SkAutoBitmapShaderInstall',
2376 'SkAutoBlitterChoose',
2377 'SkAutoBounderCommit',
2378 'SkAutoCallProc',
2379 'SkAutoCanvasRestore',
2380 'SkAutoCommentBlock',
2381 'SkAutoDescriptor',
2382 'SkAutoDisableDirectionCheck',
2383 'SkAutoDisableOvalCheck',
2384 'SkAutoFree',
2385 'SkAutoGlyphCache',
2386 'SkAutoHDC',
2387 'SkAutoLockColors',
2388 'SkAutoLockPixels',
2389 'SkAutoMalloc',
2390 'SkAutoMaskFreeImage',
2391 'SkAutoMutexAcquire',
2392 'SkAutoPathBoundsUpdate',
2393 'SkAutoPDFRelease',
2394 'SkAutoRasterClipValidate',
2395 'SkAutoRef',
2396 'SkAutoTime',
2397 'SkAutoTrace',
2398 'SkAutoUnref',
2399 ]
2400 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
2401 # bad: base::AutoLock(lock.get());
2402 # not bad: base::AutoLock lock(lock.get());
2403 bad_pattern = input_api.re.compile(anonymous)
2404 # good: new base::AutoLock(lock.get())
2405 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
2406 errors = []
[email protected]49aa76a2013-12-04 06:59:162407
Sam Maiera6e76d72022-02-11 21:43:502408 for f in input_api.AffectedFiles():
2409 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
2410 continue
2411 for linenum, line in f.ChangedContents():
2412 if bad_pattern.search(line) and not good_pattern.search(line):
2413 errors.append('%s:%d' % (f.LocalPath(), linenum))
[email protected]49aa76a2013-12-04 06:59:162414
Sam Maiera6e76d72022-02-11 21:43:502415 if errors:
2416 return [
2417 output_api.PresubmitError(
2418 'These lines create anonymous variables that need to be named:',
2419 items=errors)
2420 ]
2421 return []
[email protected]49aa76a2013-12-04 06:59:162422
2423
Saagar Sanghavifceeaae2020-08-12 16:40:362424def CheckUniquePtrOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502425 # Returns whether |template_str| is of the form <T, U...> for some types T
2426 # and U. Assumes that |template_str| is already in the form <...>.
2427 def HasMoreThanOneArg(template_str):
2428 # Level of <...> nesting.
2429 nesting = 0
2430 for c in template_str:
2431 if c == '<':
2432 nesting += 1
2433 elif c == '>':
2434 nesting -= 1
2435 elif c == ',' and nesting == 1:
2436 return True
2437 return False
Vaclav Brozekb7fadb692018-08-30 06:39:532438
Sam Maiera6e76d72022-02-11 21:43:502439 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
2440 sources = lambda affected_file: input_api.FilterSourceFile(
2441 affected_file,
2442 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
2443 DEFAULT_FILES_TO_SKIP),
2444 files_to_check=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:552445
Sam Maiera6e76d72022-02-11 21:43:502446 # Pattern to capture a single "<...>" block of template arguments. It can
2447 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
2448 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
2449 # latter would likely require counting that < and > match, which is not
2450 # expressible in regular languages. Should the need arise, one can introduce
2451 # limited counting (matching up to a total number of nesting depth), which
2452 # should cover all practical cases for already a low nesting limit.
2453 template_arg_pattern = (
2454 r'<[^>]*' # Opening block of <.
2455 r'>([^<]*>)?') # Closing block of >.
2456 # Prefix expressing that whatever follows is not already inside a <...>
2457 # block.
2458 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
2459 null_construct_pattern = input_api.re.compile(
2460 not_inside_template_arg_pattern + r'\bstd::unique_ptr' +
2461 template_arg_pattern + r'\(\)')
Vaclav Brozeka54c528b2018-04-06 19:23:552462
Sam Maiera6e76d72022-02-11 21:43:502463 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
2464 template_arg_no_array_pattern = (
2465 r'<[^>]*[^]]' # Opening block of <.
2466 r'>([^(<]*[^]]>)?') # Closing block of >.
2467 # Prefix saying that what follows is the start of an expression.
2468 start_of_expr_pattern = r'(=|\breturn|^)\s*'
2469 # Suffix saying that what follows are call parentheses with a non-empty list
2470 # of arguments.
2471 nonempty_arg_list_pattern = r'\(([^)]|$)'
2472 # Put the template argument into a capture group for deeper examination later.
2473 return_construct_pattern = input_api.re.compile(
2474 start_of_expr_pattern + r'std::unique_ptr' + '(?P<template_arg>' +
2475 template_arg_no_array_pattern + ')' + nonempty_arg_list_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:552476
Sam Maiera6e76d72022-02-11 21:43:502477 problems_constructor = []
2478 problems_nullptr = []
2479 for f in input_api.AffectedSourceFiles(sources):
2480 for line_number, line in f.ChangedContents():
2481 # Disallow:
2482 # return std::unique_ptr<T>(foo);
2483 # bar = std::unique_ptr<T>(foo);
2484 # But allow:
2485 # return std::unique_ptr<T[]>(foo);
2486 # bar = std::unique_ptr<T[]>(foo);
2487 # And also allow cases when the second template argument is present. Those
2488 # cases cannot be handled by std::make_unique:
2489 # return std::unique_ptr<T, U>(foo);
2490 # bar = std::unique_ptr<T, U>(foo);
2491 local_path = f.LocalPath()
2492 return_construct_result = return_construct_pattern.search(line)
2493 if return_construct_result and not HasMoreThanOneArg(
2494 return_construct_result.group('template_arg')):
2495 problems_constructor.append(
2496 '%s:%d\n %s' % (local_path, line_number, line.strip()))
2497 # Disallow:
2498 # std::unique_ptr<T>()
2499 if null_construct_pattern.search(line):
2500 problems_nullptr.append(
2501 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Vaclav Brozek851d9602018-04-04 16:13:052502
Sam Maiera6e76d72022-02-11 21:43:502503 errors = []
2504 if problems_nullptr:
2505 errors.append(
2506 output_api.PresubmitPromptWarning(
2507 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
2508 problems_nullptr))
2509 if problems_constructor:
2510 errors.append(
2511 output_api.PresubmitError(
2512 'The following files use explicit std::unique_ptr constructor. '
2513 'Use std::make_unique<T>() instead, or use base::WrapUnique if '
2514 'std::make_unique is not an option.', problems_constructor))
2515 return errors
Peter Kasting4844e46e2018-02-23 07:27:102516
2517
Saagar Sanghavifceeaae2020-08-12 16:40:362518def CheckUserActionUpdate(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:502519 """Checks if any new user action has been added."""
2520 if any('actions.xml' == input_api.os_path.basename(f)
2521 for f in input_api.LocalPaths()):
2522 # If actions.xml is already included in the changelist, the PRESUBMIT
2523 # for actions.xml will do a more complete presubmit check.
2524 return []
2525
2526 file_inclusion_pattern = [r'.*\.(cc|mm)$']
2527 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2528 input_api.DEFAULT_FILES_TO_SKIP)
2529 file_filter = lambda f: input_api.FilterSourceFile(
2530 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
2531
2532 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
2533 current_actions = None
2534 for f in input_api.AffectedFiles(file_filter=file_filter):
2535 for line_num, line in f.ChangedContents():
2536 match = input_api.re.search(action_re, line)
2537 if match:
2538 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
2539 # loaded only once.
2540 if not current_actions:
2541 with open(
2542 'tools/metrics/actions/actions.xml') as actions_f:
2543 current_actions = actions_f.read()
2544 # Search for the matched user action name in |current_actions|.
2545 for action_name in match.groups():
2546 action = 'name="{0}"'.format(action_name)
2547 if action not in current_actions:
2548 return [
2549 output_api.PresubmitPromptWarning(
2550 'File %s line %d: %s is missing in '
2551 'tools/metrics/actions/actions.xml. Please run '
2552 'tools/metrics/actions/extract_actions.py to update.'
2553 % (f.LocalPath(), line_num, action_name))
2554 ]
[email protected]999261d2014-03-03 20:08:082555 return []
2556
[email protected]999261d2014-03-03 20:08:082557
Daniel Cheng13ca61a882017-08-25 15:11:252558def _ImportJSONCommentEater(input_api):
Sam Maiera6e76d72022-02-11 21:43:502559 import sys
2560 sys.path = sys.path + [
2561 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
2562 'json_comment_eater')
2563 ]
2564 import json_comment_eater
2565 return json_comment_eater
Daniel Cheng13ca61a882017-08-25 15:11:252566
2567
[email protected]99171a92014-06-03 08:44:472568def _GetJSONParseError(input_api, filename, eat_comments=True):
dchenge07de812016-06-20 19:27:172569 try:
Sam Maiera6e76d72022-02-11 21:43:502570 contents = input_api.ReadFile(filename)
2571 if eat_comments:
2572 json_comment_eater = _ImportJSONCommentEater(input_api)
2573 contents = json_comment_eater.Nom(contents)
dchenge07de812016-06-20 19:27:172574
Sam Maiera6e76d72022-02-11 21:43:502575 input_api.json.loads(contents)
2576 except ValueError as e:
2577 return e
Andrew Grieve4deedb12022-02-03 21:34:502578 return None
2579
2580
Sam Maiera6e76d72022-02-11 21:43:502581def _GetIDLParseError(input_api, filename):
2582 try:
2583 contents = input_api.ReadFile(filename)
2584 idl_schema = input_api.os_path.join(input_api.PresubmitLocalPath(),
2585 'tools', 'json_schema_compiler',
2586 'idl_schema.py')
2587 process = input_api.subprocess.Popen(
2588 [input_api.python_executable, idl_schema],
2589 stdin=input_api.subprocess.PIPE,
2590 stdout=input_api.subprocess.PIPE,
2591 stderr=input_api.subprocess.PIPE,
2592 universal_newlines=True)
2593 (_, error) = process.communicate(input=contents)
2594 return error or None
2595 except ValueError as e:
2596 return e
agrievef32bcc72016-04-04 14:57:402597
agrievef32bcc72016-04-04 14:57:402598
Sam Maiera6e76d72022-02-11 21:43:502599def CheckParseErrors(input_api, output_api):
2600 """Check that IDL and JSON files do not contain syntax errors."""
2601 actions = {
2602 '.idl': _GetIDLParseError,
2603 '.json': _GetJSONParseError,
2604 }
2605 # Most JSON files are preprocessed and support comments, but these do not.
2606 json_no_comments_patterns = [
2607 r'^testing[\\/]',
2608 ]
2609 # Only run IDL checker on files in these directories.
2610 idl_included_patterns = [
2611 r'^chrome[\\/]common[\\/]extensions[\\/]api[\\/]',
2612 r'^extensions[\\/]common[\\/]api[\\/]',
2613 ]
agrievef32bcc72016-04-04 14:57:402614
Sam Maiera6e76d72022-02-11 21:43:502615 def get_action(affected_file):
2616 filename = affected_file.LocalPath()
2617 return actions.get(input_api.os_path.splitext(filename)[1])
agrievef32bcc72016-04-04 14:57:402618
Sam Maiera6e76d72022-02-11 21:43:502619 def FilterFile(affected_file):
2620 action = get_action(affected_file)
2621 if not action:
2622 return False
2623 path = affected_file.LocalPath()
agrievef32bcc72016-04-04 14:57:402624
Sam Maiera6e76d72022-02-11 21:43:502625 if _MatchesFile(input_api,
2626 _KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS, path):
2627 return False
2628
2629 if (action == _GetIDLParseError
2630 and not _MatchesFile(input_api, idl_included_patterns, path)):
2631 return False
2632 return True
2633
2634 results = []
2635 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
2636 include_deletes=False):
2637 action = get_action(affected_file)
2638 kwargs = {}
2639 if (action == _GetJSONParseError
2640 and _MatchesFile(input_api, json_no_comments_patterns,
2641 affected_file.LocalPath())):
2642 kwargs['eat_comments'] = False
2643 parse_error = action(input_api, affected_file.AbsoluteLocalPath(),
2644 **kwargs)
2645 if parse_error:
2646 results.append(
2647 output_api.PresubmitError(
2648 '%s could not be parsed: %s' %
2649 (affected_file.LocalPath(), parse_error)))
2650 return results
2651
2652
2653def CheckJavaStyle(input_api, output_api):
2654 """Runs checkstyle on changed java files and returns errors if any exist."""
2655
2656 # Return early if no java files were modified.
2657 if not any(
2658 _IsJavaFile(input_api, f.LocalPath())
2659 for f in input_api.AffectedFiles()):
2660 return []
2661
2662 import sys
2663 original_sys_path = sys.path
2664 try:
2665 sys.path = sys.path + [
2666 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
2667 'android', 'checkstyle')
2668 ]
2669 import checkstyle
2670 finally:
2671 # Restore sys.path to what it was before.
2672 sys.path = original_sys_path
2673
2674 return checkstyle.RunCheckstyle(
2675 input_api,
2676 output_api,
2677 'tools/android/checkstyle/chromium-style-5.0.xml',
2678 files_to_skip=_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP)
2679
2680
2681def CheckPythonDevilInit(input_api, output_api):
2682 """Checks to make sure devil is initialized correctly in python scripts."""
2683 script_common_initialize_pattern = input_api.re.compile(
2684 r'script_common\.InitializeEnvironment\(')
2685 devil_env_config_initialize = input_api.re.compile(
2686 r'devil_env\.config\.Initialize\(')
2687
2688 errors = []
2689
2690 sources = lambda affected_file: input_api.FilterSourceFile(
2691 affected_file,
2692 files_to_skip=(_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP + (
2693 r'^build[\\/]android[\\/]devil_chromium\.py',
2694 r'^third_party[\\/].*',
2695 )),
2696 files_to_check=[r'.*\.py$'])
2697
2698 for f in input_api.AffectedSourceFiles(sources):
2699 for line_num, line in f.ChangedContents():
2700 if (script_common_initialize_pattern.search(line)
2701 or devil_env_config_initialize.search(line)):
2702 errors.append("%s:%d" % (f.LocalPath(), line_num))
2703
2704 results = []
2705
2706 if errors:
2707 results.append(
2708 output_api.PresubmitError(
2709 'Devil initialization should always be done using '
2710 'devil_chromium.Initialize() in the chromium project, to use better '
2711 'defaults for dependencies (ex. up-to-date version of adb).',
2712 errors))
2713
2714 return results
2715
2716
2717def _MatchesFile(input_api, patterns, path):
2718 for pattern in patterns:
2719 if input_api.re.search(pattern, path):
2720 return True
2721 return False
2722
2723
2724def _GetOwnersFilesToCheckForIpcOwners(input_api):
2725 """Gets a list of OWNERS files to check for correct security owners.
2726
2727 Returns:
2728 A dictionary mapping an OWNER file to the list of OWNERS rules it must
2729 contain to cover IPC-related files with noparent reviewer rules.
2730 """
2731 # Whether or not a file affects IPC is (mostly) determined by a simple list
2732 # of filename patterns.
2733 file_patterns = [
2734 # Legacy IPC:
2735 '*_messages.cc',
2736 '*_messages*.h',
2737 '*_param_traits*.*',
2738 # Mojo IPC:
2739 '*.mojom',
2740 '*_mojom_traits*.*',
2741 '*_struct_traits*.*',
2742 '*_type_converter*.*',
2743 '*.typemap',
2744 # Android native IPC:
2745 '*.aidl',
2746 # Blink uses a different file naming convention:
2747 '*EnumTraits*.*',
2748 "*MojomTraits*.*",
2749 '*StructTraits*.*',
2750 '*TypeConverter*.*',
2751 ]
2752
2753 # These third_party directories do not contain IPCs, but contain files
2754 # matching the above patterns, which trigger false positives.
2755 exclude_paths = [
2756 'third_party/crashpad/*',
2757 'third_party/blink/renderer/platform/bindings/*',
2758 'third_party/protobuf/benchmarks/python/*',
2759 'third_party/win_build_output/*',
2760 # These files are just used to communicate between class loaders running
2761 # in the same process.
2762 'weblayer/browser/java/org/chromium/weblayer_private/interfaces/*',
2763 'weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/*',
2764 ]
2765
2766 # Dictionary mapping an OWNERS file path to Patterns.
2767 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2768 # rules ) to a PatternEntry.
2769 # PatternEntry is a dictionary with two keys:
2770 # - 'files': the files that are matched by this pattern
2771 # - 'rules': the per-file rules needed for this pattern
2772 # For example, if we expect OWNERS file to contain rules for *.mojom and
2773 # *_struct_traits*.*, Patterns might look like this:
2774 # {
2775 # '*.mojom': {
2776 # 'files': ...,
2777 # 'rules': [
2778 # 'per-file *.mojom=set noparent',
2779 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2780 # ],
2781 # },
2782 # '*_struct_traits*.*': {
2783 # 'files': ...,
2784 # 'rules': [
2785 # 'per-file *_struct_traits*.*=set noparent',
2786 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2787 # ],
2788 # },
2789 # }
2790 to_check = {}
2791
2792 def AddPatternToCheck(input_file, pattern):
2793 owners_file = input_api.os_path.join(
2794 input_api.os_path.dirname(input_file.AbsoluteLocalPath()),
2795 'OWNERS')
2796 if owners_file not in to_check:
2797 to_check[owners_file] = {}
2798 if pattern not in to_check[owners_file]:
2799 to_check[owners_file][pattern] = {
2800 'files': [],
2801 'rules': [
2802 'per-file %s=set noparent' % pattern,
2803 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2804 ]
2805 }
2806 to_check[owners_file][pattern]['files'].append(input_file)
2807
2808 # Iterate through the affected files to see what we actually need to check
2809 # for. We should only nag patch authors about per-file rules if a file in that
2810 # directory would match that pattern. If a directory only contains *.mojom
2811 # files and no *_messages*.h files, we should only nag about rules for
2812 # *.mojom files.
2813 for f in input_api.AffectedFiles(include_deletes=False):
2814 # Manifest files don't have a strong naming convention. Instead, try to find
2815 # affected .cc and .h files which look like they contain a manifest
2816 # definition.
2817 manifest_pattern = input_api.re.compile('manifests?\.(cc|h)$')
2818 test_manifest_pattern = input_api.re.compile('test_manifests?\.(cc|h)')
2819 if (manifest_pattern.search(f.LocalPath())
2820 and not test_manifest_pattern.search(f.LocalPath())):
2821 # We expect all actual service manifest files to contain at least one
2822 # qualified reference to service_manager::Manifest.
2823 if 'service_manager::Manifest' in '\n'.join(f.NewContents()):
2824 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
2825 for pattern in file_patterns:
2826 if input_api.fnmatch.fnmatch(
2827 input_api.os_path.basename(f.LocalPath()), pattern):
2828 skip = False
2829 for exclude in exclude_paths:
2830 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2831 skip = True
2832 break
2833 if skip:
2834 continue
2835 AddPatternToCheck(f, pattern)
2836 break
2837
2838 return to_check
2839
2840
2841def _AddOwnersFilesToCheckForFuchsiaSecurityOwners(input_api, to_check):
2842 """Adds OWNERS files to check for correct Fuchsia security owners."""
2843
2844 file_patterns = [
2845 # Component specifications.
2846 '*.cml', # Component Framework v2.
2847 '*.cmx', # Component Framework v1.
2848
2849 # Fuchsia IDL protocol specifications.
2850 '*.fidl',
2851 ]
2852
2853 # Don't check for owners files for changes in these directories.
2854 exclude_paths = [
2855 'third_party/crashpad/*',
2856 ]
2857
2858 def AddPatternToCheck(input_file, pattern):
2859 owners_file = input_api.os_path.join(
2860 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2861 if owners_file not in to_check:
2862 to_check[owners_file] = {}
2863 if pattern not in to_check[owners_file]:
2864 to_check[owners_file][pattern] = {
2865 'files': [],
2866 'rules': [
2867 'per-file %s=set noparent' % pattern,
2868 'per-file %s=file://fuchsia/SECURITY_OWNERS' % pattern,
2869 ]
2870 }
2871 to_check[owners_file][pattern]['files'].append(input_file)
2872
2873 # Iterate through the affected files to see what we actually need to check
2874 # for. We should only nag patch authors about per-file rules if a file in that
2875 # directory would match that pattern.
2876 for f in input_api.AffectedFiles(include_deletes=False):
2877 skip = False
2878 for exclude in exclude_paths:
2879 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2880 skip = True
2881 if skip:
2882 continue
2883
2884 for pattern in file_patterns:
2885 if input_api.fnmatch.fnmatch(
2886 input_api.os_path.basename(f.LocalPath()), pattern):
2887 AddPatternToCheck(f, pattern)
2888 break
2889
2890 return to_check
2891
2892
2893def CheckSecurityOwners(input_api, output_api):
2894 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2895 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
2896 _AddOwnersFilesToCheckForFuchsiaSecurityOwners(input_api, to_check)
2897
2898 if to_check:
2899 # If there are any OWNERS files to check, there are IPC-related changes in
2900 # this CL. Auto-CC the review list.
2901 output_api.AppendCC('[email protected]')
2902
2903 # Go through the OWNERS files to check, filtering out rules that are already
2904 # present in that OWNERS file.
2905 for owners_file, patterns in to_check.items():
2906 try:
2907 with open(owners_file) as f:
2908 lines = set(f.read().splitlines())
2909 for entry in patterns.values():
2910 entry['rules'] = [
2911 rule for rule in entry['rules'] if rule not in lines
2912 ]
2913 except IOError:
2914 # No OWNERS file, so all the rules are definitely missing.
2915 continue
2916
2917 # All the remaining lines weren't found in OWNERS files, so emit an error.
2918 errors = []
2919 for owners_file, patterns in to_check.items():
2920 missing_lines = []
2921 files = []
2922 for _, entry in patterns.items():
2923 missing_lines.extend(entry['rules'])
2924 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2925 if missing_lines:
2926 errors.append('Because of the presence of files:\n%s\n\n'
2927 '%s needs the following %d lines added:\n\n%s' %
2928 ('\n'.join(files), owners_file, len(missing_lines),
2929 '\n'.join(missing_lines)))
2930
2931 results = []
2932 if errors:
2933 if input_api.is_committing:
2934 output = output_api.PresubmitError
2935 else:
2936 output = output_api.PresubmitPromptWarning
2937 results.append(
2938 output(
2939 'Found OWNERS files that need to be updated for IPC security '
2940 + 'review coverage.\nPlease update the OWNERS files below:',
2941 long_text='\n\n'.join(errors)))
2942
2943 return results
2944
2945
2946def _GetFilesUsingSecurityCriticalFunctions(input_api):
2947 """Checks affected files for changes to security-critical calls. This
2948 function checks the full change diff, to catch both additions/changes
2949 and removals.
2950
2951 Returns a dict keyed by file name, and the value is a set of detected
2952 functions.
2953 """
2954 # Map of function pretty name (displayed in an error) to the pattern to
2955 # match it with.
2956 _PATTERNS_TO_CHECK = {
2957 'content::GetServiceSandboxType<>()': 'GetServiceSandboxType\\<'
2958 }
2959 _PATTERNS_TO_CHECK = {
2960 k: input_api.re.compile(v)
2961 for k, v in _PATTERNS_TO_CHECK.items()
2962 }
2963
John Budorick47ca3fe2018-02-10 00:53:102964 import os
2965
Sam Maiera6e76d72022-02-11 21:43:502966 # We don't want to trigger on strings within this file.
2967 def presubmit_file_filter(f):
2968 return 'PRESUBMIT.py' != os.path.split(f.LocalPath())[1]
2969
2970 # Scan all affected files for changes touching _FUNCTIONS_TO_CHECK.
2971 files_to_functions = {}
2972 for f in input_api.AffectedFiles(file_filter=presubmit_file_filter):
2973 diff = f.GenerateScmDiff()
2974 for line in diff.split('\n'):
2975 # Not using just RightHandSideLines() because removing a
2976 # call to a security-critical function can be just as important
2977 # as adding or changing the arguments.
2978 if line.startswith('-') or (line.startswith('+')
2979 and not line.startswith('++')):
2980 for name, pattern in _PATTERNS_TO_CHECK.items():
2981 if pattern.search(line):
2982 path = f.LocalPath()
2983 if not path in files_to_functions:
2984 files_to_functions[path] = set()
2985 files_to_functions[path].add(name)
2986 return files_to_functions
2987
2988
2989def CheckSecurityChanges(input_api, output_api):
2990 """Checks that changes involving security-critical functions are reviewed
2991 by the security team.
2992 """
2993 files_to_functions = _GetFilesUsingSecurityCriticalFunctions(input_api)
2994 if not len(files_to_functions):
2995 return []
2996
2997 owner_email, reviewers = (
2998 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
2999 input_api, None, approval_needed=input_api.is_committing))
3000
3001 # Load the OWNERS file for security changes.
3002 owners_file = 'ipc/SECURITY_OWNERS'
3003 security_owners = input_api.owners_client.ListOwners(owners_file)
3004 has_security_owner = any([owner in reviewers for owner in security_owners])
3005 if has_security_owner:
3006 return []
3007
3008 msg = 'The following files change calls to security-sensive functions\n' \
3009 'that need to be reviewed by {}.\n'.format(owners_file)
3010 for path, names in files_to_functions.items():
3011 msg += ' {}\n'.format(path)
3012 for name in names:
3013 msg += ' {}\n'.format(name)
3014 msg += '\n'
3015
3016 if input_api.is_committing:
3017 output = output_api.PresubmitError
Mohamed Heikale217fc852020-07-06 19:44:033018 else:
Sam Maiera6e76d72022-02-11 21:43:503019 output = output_api.PresubmitNotifyResult
3020 return [output(msg)]
3021
3022
3023def CheckSetNoParent(input_api, output_api):
3024 """Checks that set noparent is only used together with an OWNERS file in
3025 //build/OWNERS.setnoparent (see also
3026 //docs/code_reviews.md#owners-files-details)
3027 """
3028 # Return early if no OWNERS files were modified.
3029 if not any(f.LocalPath().endswith('OWNERS')
3030 for f in input_api.AffectedFiles(include_deletes=False)):
3031 return []
3032
3033 errors = []
3034
3035 allowed_owners_files_file = 'build/OWNERS.setnoparent'
3036 allowed_owners_files = set()
3037 with open(allowed_owners_files_file, 'r') as f:
3038 for line in f:
3039 line = line.strip()
3040 if not line or line.startswith('#'):
3041 continue
3042 allowed_owners_files.add(line)
3043
3044 per_file_pattern = input_api.re.compile('per-file (.+)=(.+)')
3045
3046 for f in input_api.AffectedFiles(include_deletes=False):
3047 if not f.LocalPath().endswith('OWNERS'):
3048 continue
3049
3050 found_owners_files = set()
3051 found_set_noparent_lines = dict()
3052
3053 # Parse the OWNERS file.
3054 for lineno, line in enumerate(f.NewContents(), 1):
3055 line = line.strip()
3056 if line.startswith('set noparent'):
3057 found_set_noparent_lines[''] = lineno
3058 if line.startswith('file://'):
3059 if line in allowed_owners_files:
3060 found_owners_files.add('')
3061 if line.startswith('per-file'):
3062 match = per_file_pattern.match(line)
3063 if match:
3064 glob = match.group(1).strip()
3065 directive = match.group(2).strip()
3066 if directive == 'set noparent':
3067 found_set_noparent_lines[glob] = lineno
3068 if directive.startswith('file://'):
3069 if directive in allowed_owners_files:
3070 found_owners_files.add(glob)
3071
3072 # Check that every set noparent line has a corresponding file:// line
3073 # listed in build/OWNERS.setnoparent. An exception is made for top level
3074 # directories since src/OWNERS shouldn't review them.
3075 if (f.LocalPath().count('/') != 1
3076 and (not f.LocalPath() in _EXCLUDED_SET_NO_PARENT_PATHS)):
3077 for set_noparent_line in found_set_noparent_lines:
3078 if set_noparent_line in found_owners_files:
3079 continue
3080 errors.append(' %s:%d' %
3081 (f.LocalPath(),
3082 found_set_noparent_lines[set_noparent_line]))
3083
3084 results = []
3085 if errors:
3086 if input_api.is_committing:
3087 output = output_api.PresubmitError
3088 else:
3089 output = output_api.PresubmitPromptWarning
3090 results.append(
3091 output(
3092 'Found the following "set noparent" restrictions in OWNERS files that '
3093 'do not include owners from build/OWNERS.setnoparent:',
3094 long_text='\n\n'.join(errors)))
3095 return results
3096
3097
3098def CheckUselessForwardDeclarations(input_api, output_api):
3099 """Checks that added or removed lines in non third party affected
3100 header files do not lead to new useless class or struct forward
3101 declaration.
3102 """
3103 results = []
3104 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
3105 input_api.re.MULTILINE)
3106 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
3107 input_api.re.MULTILINE)
3108 for f in input_api.AffectedFiles(include_deletes=False):
3109 if (f.LocalPath().startswith('third_party')
3110 and not f.LocalPath().startswith('third_party/blink')
3111 and not f.LocalPath().startswith('third_party\\blink')):
3112 continue
3113
3114 if not f.LocalPath().endswith('.h'):
3115 continue
3116
3117 contents = input_api.ReadFile(f)
3118 fwd_decls = input_api.re.findall(class_pattern, contents)
3119 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
3120
3121 useless_fwd_decls = []
3122 for decl in fwd_decls:
3123 count = sum(1 for _ in input_api.re.finditer(
3124 r'\b%s\b' % input_api.re.escape(decl), contents))
3125 if count == 1:
3126 useless_fwd_decls.append(decl)
3127
3128 if not useless_fwd_decls:
3129 continue
3130
3131 for line in f.GenerateScmDiff().splitlines():
3132 if (line.startswith('-') and not line.startswith('--')
3133 or line.startswith('+') and not line.startswith('++')):
3134 for decl in useless_fwd_decls:
3135 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
3136 results.append(
3137 output_api.PresubmitPromptWarning(
3138 '%s: %s forward declaration is no longer needed'
3139 % (f.LocalPath(), decl)))
3140 useless_fwd_decls.remove(decl)
3141
3142 return results
3143
3144
3145def _CheckAndroidDebuggableBuild(input_api, output_api):
3146 """Checks that code uses BuildInfo.isDebugAndroid() instead of
3147 Build.TYPE.equals('') or ''.equals(Build.TYPE) to check if
3148 this is a debuggable build of Android.
3149 """
3150 build_type_check_pattern = input_api.re.compile(
3151 r'\bBuild\.TYPE\.equals\(|\.equals\(\s*\bBuild\.TYPE\)')
3152
3153 errors = []
3154
3155 sources = lambda affected_file: input_api.FilterSourceFile(
3156 affected_file,
3157 files_to_skip=(
3158 _EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
3159 DEFAULT_FILES_TO_SKIP + (
3160 r"^android_webview[\\/]support_library[\\/]"
3161 "boundary_interfaces[\\/]",
3162 r"^chrome[\\/]android[\\/]webapk[\\/].*",
3163 r'^third_party[\\/].*',
3164 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
3165 r"webview[\\/]chromium[\\/]License.*",
3166 )),
3167 files_to_check=[r'.*\.java$'])
3168
3169 for f in input_api.AffectedSourceFiles(sources):
3170 for line_num, line in f.ChangedContents():
3171 if build_type_check_pattern.search(line):
3172 errors.append("%s:%d" % (f.LocalPath(), line_num))
3173
3174 results = []
3175
3176 if errors:
3177 results.append(
3178 output_api.PresubmitPromptWarning(
3179 'Build.TYPE.equals or .equals(Build.TYPE) usage is detected.'
3180 ' Please use BuildInfo.isDebugAndroid() instead.', errors))
3181
3182 return results
3183
3184# TODO: add unit tests
3185def _CheckAndroidToastUsage(input_api, output_api):
3186 """Checks that code uses org.chromium.ui.widget.Toast instead of
3187 android.widget.Toast (Chromium Toast doesn't force hardware
3188 acceleration on low-end devices, saving memory).
3189 """
3190 toast_import_pattern = input_api.re.compile(
3191 r'^import android\.widget\.Toast;$')
3192
3193 errors = []
3194
3195 sources = lambda affected_file: input_api.FilterSourceFile(
3196 affected_file,
3197 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
3198 DEFAULT_FILES_TO_SKIP + (r'^chromecast[\\/].*',
3199 r'^remoting[\\/].*')),
3200 files_to_check=[r'.*\.java$'])
3201
3202 for f in input_api.AffectedSourceFiles(sources):
3203 for line_num, line in f.ChangedContents():
3204 if toast_import_pattern.search(line):
3205 errors.append("%s:%d" % (f.LocalPath(), line_num))
3206
3207 results = []
3208
3209 if errors:
3210 results.append(
3211 output_api.PresubmitError(
3212 'android.widget.Toast usage is detected. Android toasts use hardware'
3213 ' acceleration, and can be\ncostly on low-end devices. Please use'
3214 ' org.chromium.ui.widget.Toast instead.\n'
3215 'Contact [email protected] if you have any questions.',
3216 errors))
3217
3218 return results
3219
3220
3221def _CheckAndroidCrLogUsage(input_api, output_api):
3222 """Checks that new logs using org.chromium.base.Log:
3223 - Are using 'TAG' as variable name for the tags (warn)
3224 - Are using a tag that is shorter than 20 characters (error)
3225 """
3226
3227 # Do not check format of logs in the given files
3228 cr_log_check_excluded_paths = [
3229 # //chrome/android/webapk cannot depend on //base
3230 r"^chrome[\\/]android[\\/]webapk[\\/].*",
3231 # WebView license viewer code cannot depend on //base; used in stub APK.
3232 r"^android_webview[\\/]glue[\\/]java[\\/]src[\\/]com[\\/]android[\\/]"
3233 r"webview[\\/]chromium[\\/]License.*",
3234 # The customtabs_benchmark is a small app that does not depend on Chromium
3235 # java pieces.
3236 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
3237 ]
3238
3239 cr_log_import_pattern = input_api.re.compile(
3240 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
3241 class_in_base_pattern = input_api.re.compile(
3242 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
3243 has_some_log_import_pattern = input_api.re.compile(r'^import .*\.Log;$',
3244 input_api.re.MULTILINE)
3245 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
3246 log_call_pattern = input_api.re.compile(r'\bLog\.\w\((?P<tag>\"?\w+)')
3247 log_decl_pattern = input_api.re.compile(
3248 r'static final String TAG = "(?P<name>(.*))"')
3249 rough_log_decl_pattern = input_api.re.compile(r'\bString TAG\s*=')
3250
3251 REF_MSG = ('See docs/android_logging.md for more info.')
3252 sources = lambda x: input_api.FilterSourceFile(
3253 x,
3254 files_to_check=[r'.*\.java$'],
3255 files_to_skip=cr_log_check_excluded_paths)
3256
3257 tag_decl_errors = []
3258 tag_length_errors = []
3259 tag_errors = []
3260 tag_with_dot_errors = []
3261 util_log_errors = []
3262
3263 for f in input_api.AffectedSourceFiles(sources):
3264 file_content = input_api.ReadFile(f)
3265 has_modified_logs = False
3266 # Per line checks
3267 if (cr_log_import_pattern.search(file_content)
3268 or (class_in_base_pattern.search(file_content)
3269 and not has_some_log_import_pattern.search(file_content))):
3270 # Checks to run for files using cr log
3271 for line_num, line in f.ChangedContents():
3272 if rough_log_decl_pattern.search(line):
3273 has_modified_logs = True
3274
3275 # Check if the new line is doing some logging
3276 match = log_call_pattern.search(line)
3277 if match:
3278 has_modified_logs = True
3279
3280 # Make sure it uses "TAG"
3281 if not match.group('tag') == 'TAG':
3282 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
3283 else:
3284 # Report non cr Log function calls in changed lines
3285 for line_num, line in f.ChangedContents():
3286 if log_call_pattern.search(line):
3287 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
3288
3289 # Per file checks
3290 if has_modified_logs:
3291 # Make sure the tag is using the "cr" prefix and is not too long
3292 match = log_decl_pattern.search(file_content)
3293 tag_name = match.group('name') if match else None
3294 if not tag_name:
3295 tag_decl_errors.append(f.LocalPath())
3296 elif len(tag_name) > 20:
3297 tag_length_errors.append(f.LocalPath())
3298 elif '.' in tag_name:
3299 tag_with_dot_errors.append(f.LocalPath())
3300
3301 results = []
3302 if tag_decl_errors:
3303 results.append(
3304 output_api.PresubmitPromptWarning(
3305 'Please define your tags using the suggested format: .\n'
3306 '"private static final String TAG = "<package tag>".\n'
3307 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
3308 tag_decl_errors))
3309
3310 if tag_length_errors:
3311 results.append(
3312 output_api.PresubmitError(
3313 'The tag length is restricted by the system to be at most '
3314 '20 characters.\n' + REF_MSG, tag_length_errors))
3315
3316 if tag_errors:
3317 results.append(
3318 output_api.PresubmitPromptWarning(
3319 'Please use a variable named "TAG" for your log tags.\n' +
3320 REF_MSG, tag_errors))
3321
3322 if util_log_errors:
3323 results.append(
3324 output_api.PresubmitPromptWarning(
3325 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
3326 util_log_errors))
3327
3328 if tag_with_dot_errors:
3329 results.append(
3330 output_api.PresubmitPromptWarning(
3331 'Dot in log tags cause them to be elided in crash reports.\n' +
3332 REF_MSG, tag_with_dot_errors))
3333
3334 return results
3335
3336
3337def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
3338 """Checks that junit.framework.* is no longer used."""
3339 deprecated_junit_framework_pattern = input_api.re.compile(
3340 r'^import junit\.framework\..*;', input_api.re.MULTILINE)
3341 sources = lambda x: input_api.FilterSourceFile(
3342 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
3343 errors = []
3344 for f in input_api.AffectedFiles(file_filter=sources):
3345 for line_num, line in f.ChangedContents():
3346 if deprecated_junit_framework_pattern.search(line):
3347 errors.append("%s:%d" % (f.LocalPath(), line_num))
3348
3349 results = []
3350 if errors:
3351 results.append(
3352 output_api.PresubmitError(
3353 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
3354 '(org.junit.*) from //third_party/junit. Contact [email protected]'
3355 ' if you have any question.', errors))
3356 return results
3357
3358
3359def _CheckAndroidTestJUnitInheritance(input_api, output_api):
3360 """Checks that if new Java test classes have inheritance.
3361 Either the new test class is JUnit3 test or it is a JUnit4 test class
3362 with a base class, either case is undesirable.
3363 """
3364 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
3365
3366 sources = lambda x: input_api.FilterSourceFile(
3367 x, files_to_check=[r'.*Test\.java$'], files_to_skip=None)
3368 errors = []
3369 for f in input_api.AffectedFiles(file_filter=sources):
3370 if not f.OldContents():
3371 class_declaration_start_flag = False
3372 for line_num, line in f.ChangedContents():
3373 if class_declaration_pattern.search(line):
3374 class_declaration_start_flag = True
3375 if class_declaration_start_flag and ' extends ' in line:
3376 errors.append('%s:%d' % (f.LocalPath(), line_num))
3377 if '{' in line:
3378 class_declaration_start_flag = False
3379
3380 results = []
3381 if errors:
3382 results.append(
3383 output_api.PresubmitPromptWarning(
3384 'The newly created files include Test classes that inherits from base'
3385 ' class. Please do not use inheritance in JUnit4 tests or add new'
3386 ' JUnit3 tests. Contact [email protected] if you have any'
3387 ' questions.', errors))
3388 return results
3389
3390
3391def _CheckAndroidTestAnnotationUsage(input_api, output_api):
3392 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
3393 deprecated_annotation_import_pattern = input_api.re.compile(
3394 r'^import android\.test\.suitebuilder\.annotation\..*;',
3395 input_api.re.MULTILINE)
3396 sources = lambda x: input_api.FilterSourceFile(
3397 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
3398 errors = []
3399 for f in input_api.AffectedFiles(file_filter=sources):
3400 for line_num, line in f.ChangedContents():
3401 if deprecated_annotation_import_pattern.search(line):
3402 errors.append("%s:%d" % (f.LocalPath(), line_num))
3403
3404 results = []
3405 if errors:
3406 results.append(
3407 output_api.PresubmitError(
3408 'Annotations in android.test.suitebuilder.annotation have been'
3409 ' deprecated since API level 24. Please use android.support.test.filters'
3410 ' from //third_party/android_support_test_runner:runner_java instead.'
3411 ' Contact [email protected] if you have any questions.',
3412 errors))
3413 return results
3414
3415
3416def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
3417 """Checks if MDPI assets are placed in a correct directory."""
3418 file_filter = lambda f: (f.LocalPath().endswith('.png') and
3419 ('/res/drawable/' in f.LocalPath() or
3420 '/res/drawable-ldrtl/' in f.LocalPath()))
3421 errors = []
3422 for f in input_api.AffectedFiles(include_deletes=False,
3423 file_filter=file_filter):
3424 errors.append(' %s' % f.LocalPath())
3425
3426 results = []
3427 if errors:
3428 results.append(
3429 output_api.PresubmitError(
3430 'MDPI assets should be placed in /res/drawable-mdpi/ or '
3431 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
3432 '/res/drawable-ldrtl/.\n'
3433 'Contact [email protected] if you have questions.', errors))
3434 return results
3435
3436
3437def _CheckAndroidWebkitImports(input_api, output_api):
3438 """Checks that code uses org.chromium.base.Callback instead of
3439 android.webview.ValueCallback except in the WebView glue layer
3440 and WebLayer.
3441 """
3442 valuecallback_import_pattern = input_api.re.compile(
3443 r'^import android\.webkit\.ValueCallback;$')
3444
3445 errors = []
3446
3447 sources = lambda affected_file: input_api.FilterSourceFile(
3448 affected_file,
3449 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS + input_api.
3450 DEFAULT_FILES_TO_SKIP + (
3451 r'^android_webview[\\/]glue[\\/].*',
3452 r'^weblayer[\\/].*',
3453 )),
3454 files_to_check=[r'.*\.java$'])
3455
3456 for f in input_api.AffectedSourceFiles(sources):
3457 for line_num, line in f.ChangedContents():
3458 if valuecallback_import_pattern.search(line):
3459 errors.append("%s:%d" % (f.LocalPath(), line_num))
3460
3461 results = []
3462
3463 if errors:
3464 results.append(
3465 output_api.PresubmitError(
3466 'android.webkit.ValueCallback usage is detected outside of the glue'
3467 ' layer. To stay compatible with the support library, android.webkit.*'
3468 ' classes should only be used inside the glue layer and'
3469 ' org.chromium.base.Callback should be used instead.', errors))
3470
3471 return results
3472
3473
3474def _CheckAndroidXmlStyle(input_api, output_api, is_check_on_upload):
3475 """Checks Android XML styles """
3476
3477 # Return early if no relevant files were modified.
3478 if not any(
3479 _IsXmlOrGrdFile(input_api, f.LocalPath())
3480 for f in input_api.AffectedFiles(include_deletes=False)):
3481 return []
3482
3483 import sys
3484 original_sys_path = sys.path
3485 try:
3486 sys.path = sys.path + [
3487 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
3488 'android', 'checkxmlstyle')
3489 ]
3490 import checkxmlstyle
3491 finally:
3492 # Restore sys.path to what it was before.
3493 sys.path = original_sys_path
3494
3495 if is_check_on_upload:
3496 return checkxmlstyle.CheckStyleOnUpload(input_api, output_api)
3497 else:
3498 return checkxmlstyle.CheckStyleOnCommit(input_api, output_api)
3499
3500
3501def _CheckAndroidInfoBarDeprecation(input_api, output_api):
3502 """Checks Android Infobar Deprecation """
3503
3504 import sys
3505 original_sys_path = sys.path
3506 try:
3507 sys.path = sys.path + [
3508 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
3509 'android', 'infobar_deprecation')
3510 ]
3511 import infobar_deprecation
3512 finally:
3513 # Restore sys.path to what it was before.
3514 sys.path = original_sys_path
3515
3516 return infobar_deprecation.CheckDeprecationOnUpload(input_api, output_api)
3517
3518
3519class _PydepsCheckerResult:
3520 def __init__(self, cmd, pydeps_path, process, old_contents):
3521 self._cmd = cmd
3522 self._pydeps_path = pydeps_path
3523 self._process = process
3524 self._old_contents = old_contents
3525
3526 def GetError(self):
3527 """Returns an error message, or None."""
3528 import difflib
3529 if self._process.wait() != 0:
3530 # STDERR should already be printed.
3531 return 'Command failed: ' + self._cmd
3532 new_contents = self._process.stdout.read().splitlines()[2:]
3533 if self._old_contents != new_contents:
3534 diff = '\n'.join(
3535 difflib.context_diff(self._old_contents, new_contents))
3536 return ('File is stale: {}\n'
3537 'Diff (apply to fix):\n'
3538 '{}\n'
3539 'To regenerate, run:\n\n'
3540 ' {}').format(self._pydeps_path, diff, self._cmd)
3541 return None
3542
3543
3544class PydepsChecker:
3545 def __init__(self, input_api, pydeps_files):
3546 self._file_cache = {}
3547 self._input_api = input_api
3548 self._pydeps_files = pydeps_files
3549
3550 def _LoadFile(self, path):
3551 """Returns the list of paths within a .pydeps file relative to //."""
3552 if path not in self._file_cache:
3553 with open(path, encoding='utf-8') as f:
3554 self._file_cache[path] = f.read()
3555 return self._file_cache[path]
3556
3557 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
3558 """Returns an interable of paths within the .pydep, relativized to //."""
3559 pydeps_data = self._LoadFile(pydeps_path)
3560 uses_gn_paths = '--gn-paths' in pydeps_data
3561 entries = (l for l in pydeps_data.splitlines()
3562 if not l.startswith('#'))
3563 if uses_gn_paths:
3564 # Paths look like: //foo/bar/baz
3565 return (e[2:] for e in entries)
3566 else:
3567 # Paths look like: path/relative/to/file.pydeps
3568 os_path = self._input_api.os_path
3569 pydeps_dir = os_path.dirname(pydeps_path)
3570 return (os_path.normpath(os_path.join(pydeps_dir, e))
3571 for e in entries)
3572
3573 def _CreateFilesToPydepsMap(self):
3574 """Returns a map of local_path -> list_of_pydeps."""
3575 ret = {}
3576 for pydep_local_path in self._pydeps_files:
3577 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
3578 ret.setdefault(path, []).append(pydep_local_path)
3579 return ret
3580
3581 def ComputeAffectedPydeps(self):
3582 """Returns an iterable of .pydeps files that might need regenerating."""
3583 affected_pydeps = set()
3584 file_to_pydeps_map = None
3585 for f in self._input_api.AffectedFiles(include_deletes=True):
3586 local_path = f.LocalPath()
3587 # Changes to DEPS can lead to .pydeps changes if any .py files are in
3588 # subrepositories. We can't figure out which files change, so re-check
3589 # all files.
3590 # Changes to print_python_deps.py affect all .pydeps.
3591 if local_path in ('DEPS', 'PRESUBMIT.py'
3592 ) or local_path.endswith('print_python_deps.py'):
3593 return self._pydeps_files
3594 elif local_path.endswith('.pydeps'):
3595 if local_path in self._pydeps_files:
3596 affected_pydeps.add(local_path)
3597 elif local_path.endswith('.py'):
3598 if file_to_pydeps_map is None:
3599 file_to_pydeps_map = self._CreateFilesToPydepsMap()
3600 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
3601 return affected_pydeps
3602
3603 def DetermineIfStaleAsync(self, pydeps_path):
3604 """Runs print_python_deps.py to see if the files is stale."""
3605 import os
3606
3607 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
3608 if old_pydeps_data:
3609 cmd = old_pydeps_data[1][1:].strip()
3610 if '--output' not in cmd:
3611 cmd += ' --output ' + pydeps_path
3612 old_contents = old_pydeps_data[2:]
3613 else:
3614 # A default cmd that should work in most cases (as long as pydeps filename
3615 # matches the script name) so that PRESUBMIT.py does not crash if pydeps
3616 # file is empty/new.
3617 cmd = 'build/print_python_deps.py {} --root={} --output={}'.format(
3618 pydeps_path[:-4], os.path.dirname(pydeps_path), pydeps_path)
3619 old_contents = []
3620 env = dict(os.environ)
3621 env['PYTHONDONTWRITEBYTECODE'] = '1'
3622 process = self._input_api.subprocess.Popen(
3623 cmd + ' --output ""',
3624 shell=True,
3625 env=env,
3626 stdout=self._input_api.subprocess.PIPE,
3627 encoding='utf-8')
3628 return _PydepsCheckerResult(cmd, pydeps_path, process, old_contents)
agrievef32bcc72016-04-04 14:57:403629
3630
Tibor Goldschwendt360793f72019-06-25 18:23:493631def _ParseGclientArgs():
Sam Maiera6e76d72022-02-11 21:43:503632 args = {}
3633 with open('build/config/gclient_args.gni', 'r') as f:
3634 for line in f:
3635 line = line.strip()
3636 if not line or line.startswith('#'):
3637 continue
3638 attribute, value = line.split('=')
3639 args[attribute.strip()] = value.strip()
3640 return args
Tibor Goldschwendt360793f72019-06-25 18:23:493641
3642
Saagar Sanghavifceeaae2020-08-12 16:40:363643def CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
Sam Maiera6e76d72022-02-11 21:43:503644 """Checks if a .pydeps file needs to be regenerated."""
3645 # This check is for Python dependency lists (.pydeps files), and involves
3646 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
3647 # doesn't work on Windows and Mac, so skip it on other platforms.
3648 if not input_api.platform.startswith('linux'):
3649 return []
Erik Staabc734cd7a2021-11-23 03:11:523650
Sam Maiera6e76d72022-02-11 21:43:503651 results = []
3652 # First, check for new / deleted .pydeps.
3653 for f in input_api.AffectedFiles(include_deletes=True):
3654 # Check whether we are running the presubmit check for a file in src.
3655 # f.LocalPath is relative to repo (src, or internal repo).
3656 # os_path.exists is relative to src repo.
3657 # Therefore if os_path.exists is true, it means f.LocalPath is relative
3658 # to src and we can conclude that the pydeps is in src.
3659 if f.LocalPath().endswith('.pydeps'):
3660 if input_api.os_path.exists(f.LocalPath()):
3661 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
3662 results.append(
3663 output_api.PresubmitError(
3664 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
3665 'remove %s' % f.LocalPath()))
3666 elif f.Action() != 'D' and f.LocalPath(
3667 ) not in _ALL_PYDEPS_FILES:
3668 results.append(
3669 output_api.PresubmitError(
3670 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
3671 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:403672
Sam Maiera6e76d72022-02-11 21:43:503673 if results:
3674 return results
3675
3676 is_android = _ParseGclientArgs().get('checkout_android', 'false') == 'true'
3677 checker = checker_for_tests or PydepsChecker(input_api, _ALL_PYDEPS_FILES)
3678 affected_pydeps = set(checker.ComputeAffectedPydeps())
3679 affected_android_pydeps = affected_pydeps.intersection(
3680 set(_ANDROID_SPECIFIC_PYDEPS_FILES))
3681 if affected_android_pydeps and not is_android:
3682 results.append(
3683 output_api.PresubmitPromptOrNotify(
3684 'You have changed python files that may affect pydeps for android\n'
3685 'specific scripts. However, the relevant presumbit check cannot be\n'
3686 'run because you are not using an Android checkout. To validate that\n'
3687 'the .pydeps are correct, re-run presubmit in an Android checkout, or\n'
3688 'use the android-internal-presubmit optional trybot.\n'
3689 'Possibly stale pydeps files:\n{}'.format(
3690 '\n'.join(affected_android_pydeps))))
3691
3692 all_pydeps = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
3693 pydeps_to_check = affected_pydeps.intersection(all_pydeps)
3694 # Process these concurrently, as each one takes 1-2 seconds.
3695 pydep_results = [checker.DetermineIfStaleAsync(p) for p in pydeps_to_check]
3696 for result in pydep_results:
3697 error_msg = result.GetError()
3698 if error_msg:
3699 results.append(output_api.PresubmitError(error_msg))
3700
agrievef32bcc72016-04-04 14:57:403701 return results
3702
agrievef32bcc72016-04-04 14:57:403703
Saagar Sanghavifceeaae2020-08-12 16:40:363704def CheckSingletonInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:503705 """Checks to make sure no header files have |Singleton<|."""
3706
3707 def FileFilter(affected_file):
3708 # It's ok for base/memory/singleton.h to have |Singleton<|.
3709 files_to_skip = (_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP +
3710 (r"^base[\\/]memory[\\/]singleton\.h$",
3711 r"^net[\\/]quic[\\/]platform[\\/]impl[\\/]"
James Cook24a504192020-07-23 00:08:443712 r"quic_singleton_impl\.h$"))
Sam Maiera6e76d72022-02-11 21:43:503713 return input_api.FilterSourceFile(affected_file,
3714 files_to_skip=files_to_skip)
glidere61efad2015-02-18 17:39:433715
Sam Maiera6e76d72022-02-11 21:43:503716 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
3717 files = []
3718 for f in input_api.AffectedSourceFiles(FileFilter):
3719 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx')
3720 or f.LocalPath().endswith('.hpp')
3721 or f.LocalPath().endswith('.inl')):
3722 contents = input_api.ReadFile(f)
3723 for line in contents.splitlines(False):
3724 if (not line.lstrip().startswith('//')
3725 and # Strip C++ comment.
3726 pattern.search(line)):
3727 files.append(f)
3728 break
glidere61efad2015-02-18 17:39:433729
Sam Maiera6e76d72022-02-11 21:43:503730 if files:
3731 return [
3732 output_api.PresubmitError(
3733 'Found base::Singleton<T> in the following header files.\n' +
3734 'Please move them to an appropriate source file so that the ' +
3735 'template gets instantiated in a single compilation unit.',
3736 files)
3737 ]
3738 return []
glidere61efad2015-02-18 17:39:433739
3740
[email protected]fd20b902014-05-09 02:14:533741_DEPRECATED_CSS = [
3742 # Values
3743 ( "-webkit-box", "flex" ),
3744 ( "-webkit-inline-box", "inline-flex" ),
3745 ( "-webkit-flex", "flex" ),
3746 ( "-webkit-inline-flex", "inline-flex" ),
3747 ( "-webkit-min-content", "min-content" ),
3748 ( "-webkit-max-content", "max-content" ),
3749
3750 # Properties
3751 ( "-webkit-background-clip", "background-clip" ),
3752 ( "-webkit-background-origin", "background-origin" ),
3753 ( "-webkit-background-size", "background-size" ),
3754 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:443755 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:533756
3757 # Functions
3758 ( "-webkit-gradient", "gradient" ),
3759 ( "-webkit-repeating-gradient", "repeating-gradient" ),
3760 ( "-webkit-linear-gradient", "linear-gradient" ),
3761 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
3762 ( "-webkit-radial-gradient", "radial-gradient" ),
3763 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
3764]
3765
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:203766
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:493767# TODO: add unit tests
Saagar Sanghavifceeaae2020-08-12 16:40:363768def CheckNoDeprecatedCss(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:503769 """ Make sure that we don't use deprecated CSS
3770 properties, functions or values. Our external
3771 documentation and iOS CSS for dom distiller
3772 (reader mode) are ignored by the hooks as it
3773 needs to be consumed by WebKit. """
3774 results = []
3775 file_inclusion_pattern = [r".+\.css$"]
3776 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
3777 input_api.DEFAULT_FILES_TO_SKIP +
3778 (r"^chrome/common/extensions/docs", r"^chrome/docs",
3779 r"^native_client_sdk"))
3780 file_filter = lambda f: input_api.FilterSourceFile(
3781 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
3782 for fpath in input_api.AffectedFiles(file_filter=file_filter):
3783 for line_num, line in fpath.ChangedContents():
3784 for (deprecated_value, value) in _DEPRECATED_CSS:
3785 if deprecated_value in line:
3786 results.append(
3787 output_api.PresubmitError(
3788 "%s:%d: Use of deprecated CSS %s, use %s instead" %
3789 (fpath.LocalPath(), line_num, deprecated_value,
3790 value)))
3791 return results
[email protected]fd20b902014-05-09 02:14:533792
mohan.reddyf21db962014-10-16 12:26:473793
Saagar Sanghavifceeaae2020-08-12 16:40:363794def CheckForRelativeIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:503795 bad_files = {}
3796 for f in input_api.AffectedFiles(include_deletes=False):
3797 if (f.LocalPath().startswith('third_party')
3798 and not f.LocalPath().startswith('third_party/blink')
3799 and not f.LocalPath().startswith('third_party\\blink')):
3800 continue
rlanday6802cf632017-05-30 17:48:363801
Sam Maiera6e76d72022-02-11 21:43:503802 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
3803 continue
rlanday6802cf632017-05-30 17:48:363804
Sam Maiera6e76d72022-02-11 21:43:503805 relative_includes = [
3806 line for _, line in f.ChangedContents()
3807 if "#include" in line and "../" in line
3808 ]
3809 if not relative_includes:
3810 continue
3811 bad_files[f.LocalPath()] = relative_includes
rlanday6802cf632017-05-30 17:48:363812
Sam Maiera6e76d72022-02-11 21:43:503813 if not bad_files:
3814 return []
rlanday6802cf632017-05-30 17:48:363815
Sam Maiera6e76d72022-02-11 21:43:503816 error_descriptions = []
3817 for file_path, bad_lines in bad_files.items():
3818 error_description = file_path
3819 for line in bad_lines:
3820 error_description += '\n ' + line
3821 error_descriptions.append(error_description)
rlanday6802cf632017-05-30 17:48:363822
Sam Maiera6e76d72022-02-11 21:43:503823 results = []
3824 results.append(
3825 output_api.PresubmitError(
3826 'You added one or more relative #include paths (including "../").\n'
3827 'These shouldn\'t be used because they can be used to include headers\n'
3828 'from code that\'s not correctly specified as a dependency in the\n'
3829 'relevant BUILD.gn file(s).', error_descriptions))
rlanday6802cf632017-05-30 17:48:363830
Sam Maiera6e76d72022-02-11 21:43:503831 return results
rlanday6802cf632017-05-30 17:48:363832
Takeshi Yoshinoe387aa32017-08-02 13:16:133833
Saagar Sanghavifceeaae2020-08-12 16:40:363834def CheckForCcIncludes(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:503835 """Check that nobody tries to include a cc file. It's a relatively
3836 common error which results in duplicate symbols in object
3837 files. This may not always break the build until someone later gets
3838 very confusing linking errors."""
3839 results = []
3840 for f in input_api.AffectedFiles(include_deletes=False):
3841 # We let third_party code do whatever it wants
3842 if (f.LocalPath().startswith('third_party')
3843 and not f.LocalPath().startswith('third_party/blink')
3844 and not f.LocalPath().startswith('third_party\\blink')):
3845 continue
Daniel Bratell65b033262019-04-23 08:17:063846
Sam Maiera6e76d72022-02-11 21:43:503847 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
3848 continue
Daniel Bratell65b033262019-04-23 08:17:063849
Sam Maiera6e76d72022-02-11 21:43:503850 for _, line in f.ChangedContents():
3851 if line.startswith('#include "'):
3852 included_file = line.split('"')[1]
3853 if _IsCPlusPlusFile(input_api, included_file):
3854 # The most common naming for external files with C++ code,
3855 # apart from standard headers, is to call them foo.inc, but
3856 # Chromium sometimes uses foo-inc.cc so allow that as well.
3857 if not included_file.endswith(('.h', '-inc.cc')):
3858 results.append(
3859 output_api.PresubmitError(
3860 'Only header files or .inc files should be included in other\n'
3861 'C++ files. Compiling the contents of a cc file more than once\n'
3862 'will cause duplicate information in the build which may later\n'
3863 'result in strange link_errors.\n' +
3864 f.LocalPath() + ':\n ' + line))
Daniel Bratell65b033262019-04-23 08:17:063865
Sam Maiera6e76d72022-02-11 21:43:503866 return results
Daniel Bratell65b033262019-04-23 08:17:063867
3868
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203869def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
Sam Maiera6e76d72022-02-11 21:43:503870 if not isinstance(key, ast.Str):
3871 return 'Key at line %d must be a string literal' % key.lineno
3872 if not isinstance(value, ast.Dict):
3873 return 'Value at line %d must be a dict' % value.lineno
3874 if len(value.keys) != 1:
3875 return 'Dict at line %d must have single entry' % value.lineno
3876 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
3877 return (
3878 'Entry at line %d must have a string literal \'filepath\' as key' %
3879 value.lineno)
3880 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:133881
Takeshi Yoshinoe387aa32017-08-02 13:16:133882
Sergey Ulanov4af16052018-11-08 02:41:463883def _CheckWatchlistsEntrySyntax(key, value, ast, email_regex):
Sam Maiera6e76d72022-02-11 21:43:503884 if not isinstance(key, ast.Str):
3885 return 'Key at line %d must be a string literal' % key.lineno
3886 if not isinstance(value, ast.List):
3887 return 'Value at line %d must be a list' % value.lineno
3888 for element in value.elts:
3889 if not isinstance(element, ast.Str):
3890 return 'Watchlist elements on line %d is not a string' % key.lineno
3891 if not email_regex.match(element.s):
3892 return ('Watchlist element on line %d doesn\'t look like a valid '
3893 + 'email: %s') % (key.lineno, element.s)
3894 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:133895
Takeshi Yoshinoe387aa32017-08-02 13:16:133896
Sergey Ulanov4af16052018-11-08 02:41:463897def _CheckWATCHLISTSEntries(wd_dict, w_dict, input_api):
Sam Maiera6e76d72022-02-11 21:43:503898 mismatch_template = (
3899 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
3900 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:133901
Sam Maiera6e76d72022-02-11 21:43:503902 email_regex = input_api.re.compile(
3903 r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$")
Sergey Ulanov4af16052018-11-08 02:41:463904
Sam Maiera6e76d72022-02-11 21:43:503905 ast = input_api.ast
3906 i = 0
3907 last_key = ''
3908 while True:
3909 if i >= len(wd_dict.keys):
3910 if i >= len(w_dict.keys):
3911 return None
3912 return mismatch_template % ('missing',
3913 'line %d' % w_dict.keys[i].lineno)
3914 elif i >= len(w_dict.keys):
3915 return (mismatch_template %
3916 ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:133917
Sam Maiera6e76d72022-02-11 21:43:503918 wd_key = wd_dict.keys[i]
3919 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:133920
Sam Maiera6e76d72022-02-11 21:43:503921 result = _CheckWatchlistDefinitionsEntrySyntax(wd_key,
3922 wd_dict.values[i], ast)
3923 if result is not None:
3924 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:133925
Sam Maiera6e76d72022-02-11 21:43:503926 result = _CheckWatchlistsEntrySyntax(w_key, w_dict.values[i], ast,
3927 email_regex)
3928 if result is not None:
3929 return 'Bad entry in WATCHLISTS dict: %s' % result
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203930
Sam Maiera6e76d72022-02-11 21:43:503931 if wd_key.s != w_key.s:
3932 return mismatch_template % ('%s at line %d' %
3933 (wd_key.s, wd_key.lineno),
3934 '%s at line %d' %
3935 (w_key.s, w_key.lineno))
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203936
Sam Maiera6e76d72022-02-11 21:43:503937 if wd_key.s < last_key:
3938 return (
3939 'WATCHLISTS dict is not sorted lexicographically at line %d and %d'
3940 % (wd_key.lineno, w_key.lineno))
3941 last_key = wd_key.s
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203942
Sam Maiera6e76d72022-02-11 21:43:503943 i = i + 1
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203944
3945
Sergey Ulanov4af16052018-11-08 02:41:463946def _CheckWATCHLISTSSyntax(expression, input_api):
Sam Maiera6e76d72022-02-11 21:43:503947 ast = input_api.ast
3948 if not isinstance(expression, ast.Expression):
3949 return 'WATCHLISTS file must contain a valid expression'
3950 dictionary = expression.body
3951 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
3952 return 'WATCHLISTS file must have single dict with exactly two entries'
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203953
Sam Maiera6e76d72022-02-11 21:43:503954 first_key = dictionary.keys[0]
3955 first_value = dictionary.values[0]
3956 second_key = dictionary.keys[1]
3957 second_value = dictionary.values[1]
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203958
Sam Maiera6e76d72022-02-11 21:43:503959 if (not isinstance(first_key, ast.Str)
3960 or first_key.s != 'WATCHLIST_DEFINITIONS'
3961 or not isinstance(first_value, ast.Dict)):
3962 return ('The first entry of the dict in WATCHLISTS file must be '
3963 'WATCHLIST_DEFINITIONS dict')
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203964
Sam Maiera6e76d72022-02-11 21:43:503965 if (not isinstance(second_key, ast.Str) or second_key.s != 'WATCHLISTS'
3966 or not isinstance(second_value, ast.Dict)):
3967 return ('The second entry of the dict in WATCHLISTS file must be '
3968 'WATCHLISTS dict')
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203969
Sam Maiera6e76d72022-02-11 21:43:503970 return _CheckWATCHLISTSEntries(first_value, second_value, input_api)
Takeshi Yoshinoe387aa32017-08-02 13:16:133971
3972
Saagar Sanghavifceeaae2020-08-12 16:40:363973def CheckWATCHLISTS(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:503974 for f in input_api.AffectedFiles(include_deletes=False):
3975 if f.LocalPath() == 'WATCHLISTS':
3976 contents = input_api.ReadFile(f, 'r')
Takeshi Yoshinoe387aa32017-08-02 13:16:133977
Sam Maiera6e76d72022-02-11 21:43:503978 try:
3979 # First, make sure that it can be evaluated.
3980 input_api.ast.literal_eval(contents)
3981 # Get an AST tree for it and scan the tree for detailed style checking.
3982 expression = input_api.ast.parse(contents,
3983 filename='WATCHLISTS',
3984 mode='eval')
3985 except ValueError as e:
3986 return [
3987 output_api.PresubmitError('Cannot parse WATCHLISTS file',
3988 long_text=repr(e))
3989 ]
3990 except SyntaxError as e:
3991 return [
3992 output_api.PresubmitError('Cannot parse WATCHLISTS file',
3993 long_text=repr(e))
3994 ]
3995 except TypeError as e:
3996 return [
3997 output_api.PresubmitError('Cannot parse WATCHLISTS file',
3998 long_text=repr(e))
3999 ]
Takeshi Yoshinoe387aa32017-08-02 13:16:134000
Sam Maiera6e76d72022-02-11 21:43:504001 result = _CheckWATCHLISTSSyntax(expression, input_api)
4002 if result is not None:
4003 return [output_api.PresubmitError(result)]
4004 break
Takeshi Yoshinoe387aa32017-08-02 13:16:134005
Sam Maiera6e76d72022-02-11 21:43:504006 return []
Takeshi Yoshinoe387aa32017-08-02 13:16:134007
4008
Andrew Grieve1b290e4a22020-11-24 20:07:014009def CheckGnGlobForward(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504010 """Checks that forward_variables_from(invoker, "*") follows best practices.
Andrew Grieve1b290e4a22020-11-24 20:07:014011
Sam Maiera6e76d72022-02-11 21:43:504012 As documented at //build/docs/writing_gn_templates.md
4013 """
Andrew Grieve1b290e4a22020-11-24 20:07:014014
Sam Maiera6e76d72022-02-11 21:43:504015 def gn_files(f):
4016 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gni', ))
Andrew Grieve1b290e4a22020-11-24 20:07:014017
Sam Maiera6e76d72022-02-11 21:43:504018 problems = []
4019 for f in input_api.AffectedSourceFiles(gn_files):
4020 for line_num, line in f.ChangedContents():
4021 if 'forward_variables_from(invoker, "*")' in line:
4022 problems.append(
4023 'Bare forward_variables_from(invoker, "*") in %s:%d' %
4024 (f.LocalPath(), line_num))
4025
4026 if problems:
4027 return [
4028 output_api.PresubmitPromptWarning(
4029 'forward_variables_from("*") without exclusions',
4030 items=sorted(problems),
4031 long_text=(
4032 'The variables "visibilty" and "test_only" should be '
4033 'explicitly listed in forward_variables_from(). For more '
4034 'details, see:\n'
4035 'https://chromium.googlesource.com/chromium/src/+/HEAD/'
4036 'build/docs/writing_gn_templates.md'
4037 '#Using-forward_variables_from'))
4038 ]
4039 return []
Andrew Grieve1b290e4a22020-11-24 20:07:014040
4041
Saagar Sanghavifceeaae2020-08-12 16:40:364042def CheckNewHeaderWithoutGnChangeOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504043 """Checks that newly added header files have corresponding GN changes.
4044 Note that this is only a heuristic. To be precise, run script:
4045 build/check_gn_headers.py.
4046 """
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194047
Sam Maiera6e76d72022-02-11 21:43:504048 def headers(f):
4049 return input_api.FilterSourceFile(
4050 f, files_to_check=(r'.+%s' % _HEADER_EXTENSIONS, ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194051
Sam Maiera6e76d72022-02-11 21:43:504052 new_headers = []
4053 for f in input_api.AffectedSourceFiles(headers):
4054 if f.Action() != 'A':
4055 continue
4056 new_headers.append(f.LocalPath())
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194057
Sam Maiera6e76d72022-02-11 21:43:504058 def gn_files(f):
4059 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gn', ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194060
Sam Maiera6e76d72022-02-11 21:43:504061 all_gn_changed_contents = ''
4062 for f in input_api.AffectedSourceFiles(gn_files):
4063 for _, line in f.ChangedContents():
4064 all_gn_changed_contents += line
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194065
Sam Maiera6e76d72022-02-11 21:43:504066 problems = []
4067 for header in new_headers:
4068 basename = input_api.os_path.basename(header)
4069 if basename not in all_gn_changed_contents:
4070 problems.append(header)
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194071
Sam Maiera6e76d72022-02-11 21:43:504072 if problems:
4073 return [
4074 output_api.PresubmitPromptWarning(
4075 'Missing GN changes for new header files',
4076 items=sorted(problems),
4077 long_text=
4078 'Please double check whether newly added header files need '
4079 'corresponding changes in gn or gni files.\nThis checking is only a '
4080 'heuristic. Run build/check_gn_headers.py to be precise.\n'
4081 'Read https://crbug.com/661774 for more info.')
4082 ]
4083 return []
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:194084
4085
Saagar Sanghavifceeaae2020-08-12 16:40:364086def CheckCorrectProductNameInMessages(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504087 """Check that Chromium-branded strings don't include "Chrome" or vice versa.
Michael Giuffridad3bc8672018-10-25 22:48:024088
Sam Maiera6e76d72022-02-11 21:43:504089 This assumes we won't intentionally reference one product from the other
4090 product.
4091 """
4092 all_problems = []
4093 test_cases = [{
4094 "filename_postfix": "google_chrome_strings.grd",
4095 "correct_name": "Chrome",
4096 "incorrect_name": "Chromium",
4097 }, {
4098 "filename_postfix": "chromium_strings.grd",
4099 "correct_name": "Chromium",
4100 "incorrect_name": "Chrome",
4101 }]
Michael Giuffridad3bc8672018-10-25 22:48:024102
Sam Maiera6e76d72022-02-11 21:43:504103 for test_case in test_cases:
4104 problems = []
4105 filename_filter = lambda x: x.LocalPath().endswith(test_case[
4106 "filename_postfix"])
Michael Giuffridad3bc8672018-10-25 22:48:024107
Sam Maiera6e76d72022-02-11 21:43:504108 # Check each new line. Can yield false positives in multiline comments, but
4109 # easier than trying to parse the XML because messages can have nested
4110 # children, and associating message elements with affected lines is hard.
4111 for f in input_api.AffectedSourceFiles(filename_filter):
4112 for line_num, line in f.ChangedContents():
4113 if "<message" in line or "<!--" in line or "-->" in line:
4114 continue
4115 if test_case["incorrect_name"] in line:
4116 problems.append("Incorrect product name in %s:%d" %
4117 (f.LocalPath(), line_num))
Michael Giuffridad3bc8672018-10-25 22:48:024118
Sam Maiera6e76d72022-02-11 21:43:504119 if problems:
4120 message = (
4121 "Strings in %s-branded string files should reference \"%s\", not \"%s\""
4122 % (test_case["correct_name"], test_case["correct_name"],
4123 test_case["incorrect_name"]))
4124 all_problems.append(
4125 output_api.PresubmitPromptWarning(message, items=problems))
Michael Giuffridad3bc8672018-10-25 22:48:024126
Sam Maiera6e76d72022-02-11 21:43:504127 return all_problems
Michael Giuffridad3bc8672018-10-25 22:48:024128
4129
Saagar Sanghavifceeaae2020-08-12 16:40:364130def CheckForTooLargeFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504131 """Avoid large files, especially binary files, in the repository since
4132 git doesn't scale well for those. They will be in everyone's repo
4133 clones forever, forever making Chromium slower to clone and work
4134 with."""
Daniel Bratell93eb6c62019-04-29 20:13:364135
Sam Maiera6e76d72022-02-11 21:43:504136 # Uploading files to cloud storage is not trivial so we don't want
4137 # to set the limit too low, but the upper limit for "normal" large
4138 # files seems to be 1-2 MB, with a handful around 5-8 MB, so
4139 # anything over 20 MB is exceptional.
4140 TOO_LARGE_FILE_SIZE_LIMIT = 20 * 1024 * 1024 # 10 MB
Daniel Bratell93eb6c62019-04-29 20:13:364141
Sam Maiera6e76d72022-02-11 21:43:504142 too_large_files = []
4143 for f in input_api.AffectedFiles():
4144 # Check both added and modified files (but not deleted files).
4145 if f.Action() in ('A', 'M'):
4146 size = input_api.os_path.getsize(f.AbsoluteLocalPath())
4147 if size > TOO_LARGE_FILE_SIZE_LIMIT:
4148 too_large_files.append("%s: %d bytes" % (f.LocalPath(), size))
Daniel Bratell93eb6c62019-04-29 20:13:364149
Sam Maiera6e76d72022-02-11 21:43:504150 if too_large_files:
4151 message = (
4152 'Do not commit large files to git since git scales badly for those.\n'
4153 +
4154 'Instead put the large files in cloud storage and use DEPS to\n' +
4155 'fetch them.\n' + '\n'.join(too_large_files))
4156 return [
4157 output_api.PresubmitError('Too large files found in commit',
4158 long_text=message + '\n')
4159 ]
4160 else:
4161 return []
Daniel Bratell93eb6c62019-04-29 20:13:364162
Max Morozb47503b2019-08-08 21:03:274163
Saagar Sanghavifceeaae2020-08-12 16:40:364164def CheckFuzzTargetsOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504165 """Checks specific for fuzz target sources."""
4166 EXPORTED_SYMBOLS = [
4167 'LLVMFuzzerInitialize',
4168 'LLVMFuzzerCustomMutator',
4169 'LLVMFuzzerCustomCrossOver',
4170 'LLVMFuzzerMutate',
4171 ]
Max Morozb47503b2019-08-08 21:03:274172
Sam Maiera6e76d72022-02-11 21:43:504173 REQUIRED_HEADER = '#include "testing/libfuzzer/libfuzzer_exports.h"'
Max Morozb47503b2019-08-08 21:03:274174
Sam Maiera6e76d72022-02-11 21:43:504175 def FilterFile(affected_file):
4176 """Ignore libFuzzer source code."""
4177 files_to_check = r'.*fuzz.*\.(h|hpp|hcc|cc|cpp|cxx)$'
4178 files_to_skip = r"^third_party[\\/]libFuzzer"
Max Morozb47503b2019-08-08 21:03:274179
Sam Maiera6e76d72022-02-11 21:43:504180 return input_api.FilterSourceFile(affected_file,
4181 files_to_check=[files_to_check],
4182 files_to_skip=[files_to_skip])
Max Morozb47503b2019-08-08 21:03:274183
Sam Maiera6e76d72022-02-11 21:43:504184 files_with_missing_header = []
4185 for f in input_api.AffectedSourceFiles(FilterFile):
4186 contents = input_api.ReadFile(f, 'r')
4187 if REQUIRED_HEADER in contents:
4188 continue
Max Morozb47503b2019-08-08 21:03:274189
Sam Maiera6e76d72022-02-11 21:43:504190 if any(symbol in contents for symbol in EXPORTED_SYMBOLS):
4191 files_with_missing_header.append(f.LocalPath())
Max Morozb47503b2019-08-08 21:03:274192
Sam Maiera6e76d72022-02-11 21:43:504193 if not files_with_missing_header:
4194 return []
Max Morozb47503b2019-08-08 21:03:274195
Sam Maiera6e76d72022-02-11 21:43:504196 long_text = (
4197 'If you define any of the libFuzzer optional functions (%s), it is '
4198 'recommended to add \'%s\' directive. Otherwise, the fuzz target may '
4199 'work incorrectly on Mac (crbug.com/687076).\nNote that '
4200 'LLVMFuzzerInitialize should not be used, unless your fuzz target needs '
4201 'to access command line arguments passed to the fuzzer. Instead, prefer '
4202 'static initialization and shared resources as documented in '
4203 'https://chromium.googlesource.com/chromium/src/+/main/testing/'
4204 'libfuzzer/efficient_fuzzing.md#simplifying-initialization_cleanup.\n'
4205 % (', '.join(EXPORTED_SYMBOLS), REQUIRED_HEADER))
Max Morozb47503b2019-08-08 21:03:274206
Sam Maiera6e76d72022-02-11 21:43:504207 return [
4208 output_api.PresubmitPromptWarning(message="Missing '%s' in:" %
4209 REQUIRED_HEADER,
4210 items=files_with_missing_header,
4211 long_text=long_text)
4212 ]
Max Morozb47503b2019-08-08 21:03:274213
4214
Mohamed Heikald048240a2019-11-12 16:57:374215def _CheckNewImagesWarning(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504216 """
4217 Warns authors who add images into the repo to make sure their images are
4218 optimized before committing.
4219 """
4220 images_added = False
4221 image_paths = []
4222 errors = []
4223 filter_lambda = lambda x: input_api.FilterSourceFile(
4224 x,
4225 files_to_skip=(('(?i).*test', r'.*\/junit\/') + input_api.
4226 DEFAULT_FILES_TO_SKIP),
4227 files_to_check=[r'.*\/(drawable|mipmap)'])
4228 for f in input_api.AffectedFiles(include_deletes=False,
4229 file_filter=filter_lambda):
4230 local_path = f.LocalPath().lower()
4231 if any(
4232 local_path.endswith(extension)
4233 for extension in _IMAGE_EXTENSIONS):
4234 images_added = True
4235 image_paths.append(f)
4236 if images_added:
4237 errors.append(
4238 output_api.PresubmitPromptWarning(
4239 'It looks like you are trying to commit some images. If these are '
4240 'non-test-only images, please make sure to read and apply the tips in '
4241 'https://chromium.googlesource.com/chromium/src/+/HEAD/docs/speed/'
4242 'binary_size/optimization_advice.md#optimizing-images\nThis check is '
4243 'FYI only and will not block your CL on the CQ.', image_paths))
4244 return errors
Mohamed Heikald048240a2019-11-12 16:57:374245
4246
Saagar Sanghavifceeaae2020-08-12 16:40:364247def ChecksAndroidSpecificOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504248 """Groups upload checks that target android code."""
4249 results = []
4250 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
4251 results.extend(_CheckAndroidDebuggableBuild(input_api, output_api))
4252 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
4253 results.extend(_CheckAndroidToastUsage(input_api, output_api))
4254 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
4255 results.extend(_CheckAndroidTestJUnitFrameworkImport(
4256 input_api, output_api))
4257 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
4258 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
4259 results.extend(_CheckAndroidXmlStyle(input_api, output_api, True))
4260 results.extend(_CheckNewImagesWarning(input_api, output_api))
4261 results.extend(_CheckAndroidNoBannedImports(input_api, output_api))
4262 results.extend(_CheckAndroidInfoBarDeprecation(input_api, output_api))
4263 return results
4264
Becky Zhou7c69b50992018-12-10 19:37:574265
Saagar Sanghavifceeaae2020-08-12 16:40:364266def ChecksAndroidSpecificOnCommit(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504267 """Groups commit checks that target android code."""
4268 results = []
4269 results.extend(_CheckAndroidXmlStyle(input_api, output_api, False))
4270 return results
dgnaa68d5e2015-06-10 10:08:224271
Chris Hall59f8d0c72020-05-01 07:31:194272# TODO(chrishall): could we additionally match on any path owned by
4273# ui/accessibility/OWNERS ?
4274_ACCESSIBILITY_PATHS = (
4275 r"^chrome[\\/]browser.*[\\/]accessibility[\\/]",
4276 r"^chrome[\\/]browser[\\/]extensions[\\/]api[\\/]automation.*[\\/]",
4277 r"^chrome[\\/]renderer[\\/]extensions[\\/]accessibility_.*",
4278 r"^chrome[\\/]tests[\\/]data[\\/]accessibility[\\/]",
4279 r"^content[\\/]browser[\\/]accessibility[\\/]",
4280 r"^content[\\/]renderer[\\/]accessibility[\\/]",
4281 r"^content[\\/]tests[\\/]data[\\/]accessibility[\\/]",
4282 r"^extensions[\\/]renderer[\\/]api[\\/]automation[\\/]",
4283 r"^ui[\\/]accessibility[\\/]",
4284 r"^ui[\\/]views[\\/]accessibility[\\/]",
4285)
4286
Saagar Sanghavifceeaae2020-08-12 16:40:364287def CheckAccessibilityRelnotesField(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504288 """Checks that commits to accessibility code contain an AX-Relnotes field in
4289 their commit message."""
Chris Hall59f8d0c72020-05-01 07:31:194290
Sam Maiera6e76d72022-02-11 21:43:504291 def FileFilter(affected_file):
4292 paths = _ACCESSIBILITY_PATHS
4293 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Chris Hall59f8d0c72020-05-01 07:31:194294
Sam Maiera6e76d72022-02-11 21:43:504295 # Only consider changes affecting accessibility paths.
4296 if not any(input_api.AffectedFiles(file_filter=FileFilter)):
4297 return []
Akihiro Ota08108e542020-05-20 15:30:534298
Sam Maiera6e76d72022-02-11 21:43:504299 # AX-Relnotes can appear in either the description or the footer.
4300 # When searching the description, require 'AX-Relnotes:' to appear at the
4301 # beginning of a line.
4302 ax_regex = input_api.re.compile('ax-relnotes[:=]')
4303 description_has_relnotes = any(
4304 ax_regex.match(line)
4305 for line in input_api.change.DescriptionText().lower().splitlines())
Chris Hall59f8d0c72020-05-01 07:31:194306
Sam Maiera6e76d72022-02-11 21:43:504307 footer_relnotes = input_api.change.GitFootersFromDescription().get(
4308 'AX-Relnotes', [])
4309 if description_has_relnotes or footer_relnotes:
4310 return []
Chris Hall59f8d0c72020-05-01 07:31:194311
Sam Maiera6e76d72022-02-11 21:43:504312 # TODO(chrishall): link to Relnotes documentation in message.
4313 message = (
4314 "Missing 'AX-Relnotes:' field required for accessibility changes"
4315 "\n please add 'AX-Relnotes: [release notes].' to describe any "
4316 "user-facing changes"
4317 "\n otherwise add 'AX-Relnotes: n/a.' if this change has no "
4318 "user-facing effects"
4319 "\n if this is confusing or annoying then please contact members "
4320 "of ui/accessibility/OWNERS.")
4321
4322 return [output_api.PresubmitNotifyResult(message)]
dgnaa68d5e2015-06-10 10:08:224323
Mark Schillacie5a0be22022-01-19 00:38:394324
4325_ACCESSIBILITY_EVENTS_TEST_PATH = (
4326 r"^content[\\/]test[\\/]data[\\/]accessibility[\\/]event[\\/].*\.html",
4327)
4328
4329_ACCESSIBILITY_TREE_TEST_PATH = (
4330 r"^content[\\/]test[\\/]data[\\/]accessibility[\\/]accname[\\/].*\.html",
4331 r"^content[\\/]test[\\/]data[\\/]accessibility[\\/]aria[\\/].*\.html",
4332 r"^content[\\/]test[\\/]data[\\/]accessibility[\\/]css[\\/].*\.html",
4333 r"^content[\\/]test[\\/]data[\\/]accessibility[\\/]html[\\/].*\.html",
4334)
4335
4336_ACCESSIBILITY_ANDROID_EVENTS_TEST_PATH = (
4337 r"^.*[\\/]WebContentsAccessibilityEventsTest\.java",
4338)
4339
4340_ACCESSIBILITY_ANDROID_TREE_TEST_PATH = (
Mark Schillaci6f568a52022-02-17 18:41:444341 r"^.*[\\/]WebContentsAccessibilityTreeTest\.java",
Mark Schillacie5a0be22022-01-19 00:38:394342)
4343
4344def CheckAccessibilityEventsTestsAreIncludedForAndroid(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504345 """Checks that commits that include a newly added, renamed/moved, or deleted
4346 test in the DumpAccessibilityEventsTest suite also includes a corresponding
4347 change to the Android test."""
Mark Schillacie5a0be22022-01-19 00:38:394348
Sam Maiera6e76d72022-02-11 21:43:504349 def FilePathFilter(affected_file):
4350 paths = _ACCESSIBILITY_EVENTS_TEST_PATH
4351 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394352
Sam Maiera6e76d72022-02-11 21:43:504353 def AndroidFilePathFilter(affected_file):
4354 paths = _ACCESSIBILITY_ANDROID_EVENTS_TEST_PATH
4355 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394356
Sam Maiera6e76d72022-02-11 21:43:504357 # Only consider changes in the events test data path with html type.
4358 if not any(
4359 input_api.AffectedFiles(include_deletes=True,
4360 file_filter=FilePathFilter)):
4361 return []
Mark Schillacie5a0be22022-01-19 00:38:394362
Sam Maiera6e76d72022-02-11 21:43:504363 # If the commit contains any change to the Android test file, ignore.
4364 if any(
4365 input_api.AffectedFiles(include_deletes=True,
4366 file_filter=AndroidFilePathFilter)):
4367 return []
Mark Schillacie5a0be22022-01-19 00:38:394368
Sam Maiera6e76d72022-02-11 21:43:504369 # Only consider changes that are adding/renaming or deleting a file
4370 message = []
4371 for f in input_api.AffectedFiles(include_deletes=True,
4372 file_filter=FilePathFilter):
4373 if f.Action() == 'A' or f.Action() == 'D':
4374 message = (
4375 "It appears that you are adding, renaming or deleting"
4376 "\na dump_accessibility_events* test, but have not included"
4377 "\na corresponding change for Android."
4378 "\nPlease include (or remove) the test from:"
4379 "\n content/public/android/javatests/src/org/chromium/"
4380 "content/browser/accessibility/"
4381 "WebContentsAccessibilityEventsTest.java"
4382 "\nIf this message is confusing or annoying, please contact"
4383 "\nmembers of ui/accessibility/OWNERS.")
Mark Schillacie5a0be22022-01-19 00:38:394384
Sam Maiera6e76d72022-02-11 21:43:504385 # If no message was set, return empty.
4386 if not len(message):
4387 return []
4388
4389 return [output_api.PresubmitPromptWarning(message)]
4390
Mark Schillacie5a0be22022-01-19 00:38:394391
4392def CheckAccessibilityTreeTestsAreIncludedForAndroid(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504393 """Checks that commits that include a newly added, renamed/moved, or deleted
4394 test in the DumpAccessibilityTreeTest suite also includes a corresponding
4395 change to the Android test."""
Mark Schillacie5a0be22022-01-19 00:38:394396
Sam Maiera6e76d72022-02-11 21:43:504397 def FilePathFilter(affected_file):
4398 paths = _ACCESSIBILITY_TREE_TEST_PATH
4399 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394400
Sam Maiera6e76d72022-02-11 21:43:504401 def AndroidFilePathFilter(affected_file):
4402 paths = _ACCESSIBILITY_ANDROID_TREE_TEST_PATH
4403 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Mark Schillacie5a0be22022-01-19 00:38:394404
Sam Maiera6e76d72022-02-11 21:43:504405 # Only consider changes in the various tree test data paths with html type.
4406 if not any(
4407 input_api.AffectedFiles(include_deletes=True,
4408 file_filter=FilePathFilter)):
4409 return []
Mark Schillacie5a0be22022-01-19 00:38:394410
Sam Maiera6e76d72022-02-11 21:43:504411 # If the commit contains any change to the Android test file, ignore.
4412 if any(
4413 input_api.AffectedFiles(include_deletes=True,
4414 file_filter=AndroidFilePathFilter)):
4415 return []
Mark Schillacie5a0be22022-01-19 00:38:394416
Sam Maiera6e76d72022-02-11 21:43:504417 # Only consider changes that are adding/renaming or deleting a file
4418 message = []
4419 for f in input_api.AffectedFiles(include_deletes=True,
4420 file_filter=FilePathFilter):
4421 if f.Action() == 'A' or f.Action() == 'D':
4422 message = (
4423 "It appears that you are adding, renaming or deleting"
4424 "\na dump_accessibility_tree* test, but have not included"
4425 "\na corresponding change for Android."
4426 "\nPlease include (or remove) the test from:"
4427 "\n content/public/android/javatests/src/org/chromium/"
4428 "content/browser/accessibility/"
4429 "WebContentsAccessibilityTreeTest.java"
4430 "\nIf this message is confusing or annoying, please contact"
4431 "\nmembers of ui/accessibility/OWNERS.")
Mark Schillacie5a0be22022-01-19 00:38:394432
Sam Maiera6e76d72022-02-11 21:43:504433 # If no message was set, return empty.
4434 if not len(message):
4435 return []
4436
4437 return [output_api.PresubmitPromptWarning(message)]
Mark Schillacie5a0be22022-01-19 00:38:394438
4439
seanmccullough4a9356252021-04-08 19:54:094440# string pattern, sequence of strings to show when pattern matches,
4441# error flag. True if match is a presubmit error, otherwise it's a warning.
4442_NON_INCLUSIVE_TERMS = (
4443 (
4444 # Note that \b pattern in python re is pretty particular. In this
4445 # regexp, 'class WhiteList ...' will match, but 'class FooWhiteList
4446 # ...' will not. This may require some tweaking to catch these cases
4447 # without triggering a lot of false positives. Leaving it naive and
4448 # less matchy for now.
seanmccullough56d1e3cf2021-12-03 18:18:324449 r'/\b(?i)((black|white)list|master|slave)\b', # nocheck
seanmccullough4a9356252021-04-08 19:54:094450 (
4451 'Please don\'t use blacklist, whitelist, ' # nocheck
4452 'or slave in your', # nocheck
4453 'code and make every effort to use other terms. Using "// nocheck"',
4454 '"# nocheck" or "<!-- nocheck -->"',
4455 'at the end of the offending line will bypass this PRESUBMIT error',
4456 'but avoid using this whenever possible. Reach out to',
4457 '[email protected] if you have questions'),
4458 True),)
4459
Saagar Sanghavifceeaae2020-08-12 16:40:364460def ChecksCommon(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504461 """Checks common to both upload and commit."""
4462 results = []
Eric Boren6fd2b932018-01-25 15:05:084463 results.extend(
Sam Maiera6e76d72022-02-11 21:43:504464 input_api.canned_checks.PanProjectChecks(
4465 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:084466
Sam Maiera6e76d72022-02-11 21:43:504467 author = input_api.change.author_email
4468 if author and author not in _KNOWN_ROBOTS:
4469 results.extend(
4470 input_api.canned_checks.CheckAuthorizedAuthor(
4471 input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:244472
Sam Maiera6e76d72022-02-11 21:43:504473 results.extend(
4474 input_api.canned_checks.CheckChangeHasNoTabs(
4475 input_api,
4476 output_api,
4477 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
4478 results.extend(
4479 input_api.RunTests(
4480 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Edward Lesmesce51df52020-08-04 22:10:174481
Bruce Dawsonc8054482022-03-28 15:33:374482 dirmd = 'dirmd.bat' if input_api.is_windows else 'dirmd'
Sam Maiera6e76d72022-02-11 21:43:504483 dirmd_bin = input_api.os_path.join(input_api.PresubmitLocalPath(),
Bruce Dawsonc8054482022-03-28 15:33:374484 'third_party', 'depot_tools', dirmd)
Sam Maiera6e76d72022-02-11 21:43:504485 results.extend(
4486 input_api.RunTests(
4487 input_api.canned_checks.CheckDirMetadataFormat(
4488 input_api, output_api, dirmd_bin)))
4489 results.extend(
4490 input_api.canned_checks.CheckOwnersDirMetadataExclusive(
4491 input_api, output_api))
4492 results.extend(
4493 input_api.canned_checks.CheckNoNewMetadataInOwners(
4494 input_api, output_api))
4495 results.extend(
4496 input_api.canned_checks.CheckInclusiveLanguage(
4497 input_api,
4498 output_api,
4499 excluded_directories_relative_path=[
4500 'infra', 'inclusive_language_presubmit_exempt_dirs.txt'
4501 ],
4502 non_inclusive_terms=_NON_INCLUSIVE_TERMS))
Dirk Prankee3c9c62d2021-05-18 18:35:594503
Sam Maiera6e76d72022-02-11 21:43:504504 for f in input_api.AffectedFiles():
4505 path, name = input_api.os_path.split(f.LocalPath())
4506 if name == 'PRESUBMIT.py':
4507 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
4508 path)
4509 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
4510 if f.Action() != 'D' and input_api.os_path.exists(test_file):
4511 # The PRESUBMIT.py file (and the directory containing it) might
4512 # have been affected by being moved or removed, so only try to
4513 # run the tests if they still exist.
4514 use_python3 = False
4515 with open(f.LocalPath()) as fp:
4516 use_python3 = any(
4517 line.startswith('USE_PYTHON3 = True')
4518 for line in fp.readlines())
4519
4520 results.extend(
4521 input_api.canned_checks.RunUnitTestsInDirectory(
4522 input_api,
4523 output_api,
4524 full_path,
4525 files_to_check=[r'^PRESUBMIT_test\.py$'],
4526 run_on_python2=not use_python3,
4527 run_on_python3=use_python3,
4528 skip_shebang_check=True))
4529 return results
[email protected]1f7b4172010-01-28 01:17:344530
[email protected]b337cb5b2011-01-23 21:24:054531
Saagar Sanghavifceeaae2020-08-12 16:40:364532def CheckPatchFiles(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504533 problems = [
4534 f.LocalPath() for f in input_api.AffectedFiles()
4535 if f.LocalPath().endswith(('.orig', '.rej'))
4536 ]
4537 # Cargo.toml.orig files are part of third-party crates downloaded from
4538 # crates.io and should be included.
4539 problems = [f for f in problems if not f.endswith('Cargo.toml.orig')]
4540 if problems:
4541 return [
4542 output_api.PresubmitError("Don't commit .rej and .orig files.",
4543 problems)
4544 ]
4545 else:
4546 return []
[email protected]b8079ae4a2012-12-05 19:56:494547
4548
Saagar Sanghavifceeaae2020-08-12 16:40:364549def CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504550 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
4551 macro_re = input_api.re.compile(
4552 r'^\s*#(el)?if.*\bdefined\(((COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
4553 include_re = input_api.re.compile(r'^#include\s+"build/build_config.h"',
4554 input_api.re.MULTILINE)
4555 extension_re = input_api.re.compile(r'\.[a-z]+$')
4556 errors = []
4557 for f in input_api.AffectedFiles(include_deletes=False):
4558 if not f.LocalPath().endswith(
4559 ('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
4560 continue
4561 found_line_number = None
4562 found_macro = None
4563 all_lines = input_api.ReadFile(f, 'r').splitlines()
4564 for line_num, line in enumerate(all_lines):
4565 match = macro_re.search(line)
4566 if match:
4567 found_line_number = line_num
4568 found_macro = match.group(2)
4569 break
4570 if not found_line_number:
4571 continue
Kent Tamura5a8755d2017-06-29 23:37:074572
Sam Maiera6e76d72022-02-11 21:43:504573 found_include_line = -1
4574 for line_num, line in enumerate(all_lines):
4575 if include_re.search(line):
4576 found_include_line = line_num
4577 break
4578 if found_include_line >= 0 and found_include_line < found_line_number:
4579 continue
Kent Tamura5a8755d2017-06-29 23:37:074580
Sam Maiera6e76d72022-02-11 21:43:504581 if not f.LocalPath().endswith('.h'):
4582 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
4583 try:
4584 content = input_api.ReadFile(primary_header_path, 'r')
4585 if include_re.search(content):
4586 continue
4587 except IOError:
4588 pass
4589 errors.append('%s:%d %s macro is used without first including build/'
4590 'build_config.h.' %
4591 (f.LocalPath(), found_line_number, found_macro))
4592 if errors:
4593 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4594 return []
Kent Tamura5a8755d2017-06-29 23:37:074595
4596
Lei Zhang1c12a22f2021-05-12 11:28:454597def CheckForSuperfluousStlIncludesInHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504598 stl_include_re = input_api.re.compile(r'^#include\s+<('
4599 r'algorithm|'
4600 r'array|'
4601 r'limits|'
4602 r'list|'
4603 r'map|'
4604 r'memory|'
4605 r'queue|'
4606 r'set|'
4607 r'string|'
4608 r'unordered_map|'
4609 r'unordered_set|'
4610 r'utility|'
4611 r'vector)>')
4612 std_namespace_re = input_api.re.compile(r'std::')
4613 errors = []
4614 for f in input_api.AffectedFiles():
4615 if not _IsCPlusPlusHeaderFile(input_api, f.LocalPath()):
4616 continue
Lei Zhang1c12a22f2021-05-12 11:28:454617
Sam Maiera6e76d72022-02-11 21:43:504618 uses_std_namespace = False
4619 has_stl_include = False
4620 for line in f.NewContents():
4621 if has_stl_include and uses_std_namespace:
4622 break
Lei Zhang1c12a22f2021-05-12 11:28:454623
Sam Maiera6e76d72022-02-11 21:43:504624 if not has_stl_include and stl_include_re.search(line):
4625 has_stl_include = True
4626 continue
Lei Zhang1c12a22f2021-05-12 11:28:454627
Sam Maiera6e76d72022-02-11 21:43:504628 if not uses_std_namespace and std_namespace_re.search(line):
4629 uses_std_namespace = True
4630 continue
Lei Zhang1c12a22f2021-05-12 11:28:454631
Sam Maiera6e76d72022-02-11 21:43:504632 if has_stl_include and not uses_std_namespace:
4633 errors.append(
4634 '%s: Includes STL header(s) but does not reference std::' %
4635 f.LocalPath())
4636 if errors:
4637 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4638 return []
Lei Zhang1c12a22f2021-05-12 11:28:454639
4640
Xiaohan Wang42d96c22022-01-20 17:23:114641def _CheckForDeprecatedOSMacrosInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:504642 """Check for sensible looking, totally invalid OS macros."""
4643 preprocessor_statement = input_api.re.compile(r'^\s*#')
4644 os_macro = input_api.re.compile(r'defined\(OS_([^)]+)\)')
4645 results = []
4646 for lnum, line in f.ChangedContents():
4647 if preprocessor_statement.search(line):
4648 for match in os_macro.finditer(line):
4649 results.append(
4650 ' %s:%d: %s' %
4651 (f.LocalPath(), lnum, 'defined(OS_' + match.group(1) +
4652 ') -> BUILDFLAG(IS_' + match.group(1) + ')'))
4653 return results
[email protected]b00342e7f2013-03-26 16:21:544654
4655
Xiaohan Wang42d96c22022-01-20 17:23:114656def CheckForDeprecatedOSMacros(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504657 """Check all affected files for invalid OS macros."""
4658 bad_macros = []
4659 for f in input_api.AffectedSourceFiles(None):
4660 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
4661 bad_macros.extend(_CheckForDeprecatedOSMacrosInFile(input_api, f))
[email protected]b00342e7f2013-03-26 16:21:544662
Sam Maiera6e76d72022-02-11 21:43:504663 if not bad_macros:
4664 return []
[email protected]b00342e7f2013-03-26 16:21:544665
Sam Maiera6e76d72022-02-11 21:43:504666 return [
4667 output_api.PresubmitError(
4668 'OS macros have been deprecated. Please use BUILDFLAGs instead (still '
4669 'defined in build_config.h):', bad_macros)
4670 ]
[email protected]b00342e7f2013-03-26 16:21:544671
lliabraa35bab3932014-10-01 12:16:444672
4673def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
Sam Maiera6e76d72022-02-11 21:43:504674 """Check all affected files for invalid "if defined" macros."""
4675 ALWAYS_DEFINED_MACROS = (
4676 "TARGET_CPU_PPC",
4677 "TARGET_CPU_PPC64",
4678 "TARGET_CPU_68K",
4679 "TARGET_CPU_X86",
4680 "TARGET_CPU_ARM",
4681 "TARGET_CPU_MIPS",
4682 "TARGET_CPU_SPARC",
4683 "TARGET_CPU_ALPHA",
4684 "TARGET_IPHONE_SIMULATOR",
4685 "TARGET_OS_EMBEDDED",
4686 "TARGET_OS_IPHONE",
4687 "TARGET_OS_MAC",
4688 "TARGET_OS_UNIX",
4689 "TARGET_OS_WIN32",
4690 )
4691 ifdef_macro = input_api.re.compile(
4692 r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
4693 results = []
4694 for lnum, line in f.ChangedContents():
4695 for match in ifdef_macro.finditer(line):
4696 if match.group(1) in ALWAYS_DEFINED_MACROS:
4697 always_defined = ' %s is always defined. ' % match.group(1)
4698 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
4699 results.append(
4700 ' %s:%d %s\n\t%s' %
4701 (f.LocalPath(), lnum, always_defined, did_you_mean))
4702 return results
lliabraa35bab3932014-10-01 12:16:444703
4704
Saagar Sanghavifceeaae2020-08-12 16:40:364705def CheckForInvalidIfDefinedMacros(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504706 """Check all affected files for invalid "if defined" macros."""
4707 bad_macros = []
4708 skipped_paths = ['third_party/sqlite/', 'third_party/abseil-cpp/']
4709 for f in input_api.AffectedFiles():
4710 if any([f.LocalPath().startswith(path) for path in skipped_paths]):
4711 continue
4712 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
4713 bad_macros.extend(
4714 _CheckForInvalidIfDefinedMacrosInFile(input_api, f))
lliabraa35bab3932014-10-01 12:16:444715
Sam Maiera6e76d72022-02-11 21:43:504716 if not bad_macros:
4717 return []
lliabraa35bab3932014-10-01 12:16:444718
Sam Maiera6e76d72022-02-11 21:43:504719 return [
4720 output_api.PresubmitError(
4721 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
4722 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
4723 bad_macros)
4724 ]
lliabraa35bab3932014-10-01 12:16:444725
4726
Saagar Sanghavifceeaae2020-08-12 16:40:364727def CheckForIPCRules(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504728 """Check for same IPC rules described in
4729 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
4730 """
4731 base_pattern = r'IPC_ENUM_TRAITS\('
4732 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
4733 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
mlamouria82272622014-09-16 18:45:044734
Sam Maiera6e76d72022-02-11 21:43:504735 problems = []
4736 for f in input_api.AffectedSourceFiles(None):
4737 local_path = f.LocalPath()
4738 if not local_path.endswith('.h'):
4739 continue
4740 for line_number, line in f.ChangedContents():
4741 if inclusion_pattern.search(
4742 line) and not comment_pattern.search(line):
4743 problems.append('%s:%d\n %s' %
4744 (local_path, line_number, line.strip()))
mlamouria82272622014-09-16 18:45:044745
Sam Maiera6e76d72022-02-11 21:43:504746 if problems:
4747 return [
4748 output_api.PresubmitPromptWarning(_IPC_ENUM_TRAITS_DEPRECATED,
4749 problems)
4750 ]
4751 else:
4752 return []
mlamouria82272622014-09-16 18:45:044753
[email protected]b00342e7f2013-03-26 16:21:544754
Saagar Sanghavifceeaae2020-08-12 16:40:364755def CheckForLongPathnames(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504756 """Check to make sure no files being submitted have long paths.
4757 This causes issues on Windows.
4758 """
4759 problems = []
4760 for f in input_api.AffectedTestableFiles():
4761 local_path = f.LocalPath()
4762 # Windows has a path limit of 260 characters. Limit path length to 200 so
4763 # that we have some extra for the prefix on dev machines and the bots.
4764 if len(local_path) > 200:
4765 problems.append(local_path)
Stephen Martinis97a394142018-06-07 23:06:054766
Sam Maiera6e76d72022-02-11 21:43:504767 if problems:
4768 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
4769 else:
4770 return []
Stephen Martinis97a394142018-06-07 23:06:054771
4772
Saagar Sanghavifceeaae2020-08-12 16:40:364773def CheckForIncludeGuards(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504774 """Check that header files have proper guards against multiple inclusion.
4775 If a file should not have such guards (and it probably should) then it
4776 should include the string "no-include-guard-because-multiply-included".
4777 """
Daniel Bratell8ba52722018-03-02 16:06:144778
Sam Maiera6e76d72022-02-11 21:43:504779 def is_chromium_header_file(f):
4780 # We only check header files under the control of the Chromium
4781 # project. That is, those outside third_party apart from
4782 # third_party/blink.
4783 # We also exclude *_message_generator.h headers as they use
4784 # include guards in a special, non-typical way.
4785 file_with_path = input_api.os_path.normpath(f.LocalPath())
4786 return (file_with_path.endswith('.h')
4787 and not file_with_path.endswith('_message_generator.h')
4788 and (not file_with_path.startswith('third_party')
4789 or file_with_path.startswith(
4790 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:144791
Sam Maiera6e76d72022-02-11 21:43:504792 def replace_special_with_underscore(string):
4793 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:144794
Sam Maiera6e76d72022-02-11 21:43:504795 errors = []
Daniel Bratell8ba52722018-03-02 16:06:144796
Sam Maiera6e76d72022-02-11 21:43:504797 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
4798 guard_name = None
4799 guard_line_number = None
4800 seen_guard_end = False
Daniel Bratell8ba52722018-03-02 16:06:144801
Sam Maiera6e76d72022-02-11 21:43:504802 file_with_path = input_api.os_path.normpath(f.LocalPath())
4803 base_file_name = input_api.os_path.splitext(
4804 input_api.os_path.basename(file_with_path))[0]
4805 upper_base_file_name = base_file_name.upper()
Daniel Bratell8ba52722018-03-02 16:06:144806
Sam Maiera6e76d72022-02-11 21:43:504807 expected_guard = replace_special_with_underscore(
4808 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:144809
Sam Maiera6e76d72022-02-11 21:43:504810 # For "path/elem/file_name.h" we should really only accept
4811 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
4812 # are too many (1000+) files with slight deviations from the
4813 # coding style. The most important part is that the include guard
4814 # is there, and that it's unique, not the name so this check is
4815 # forgiving for existing files.
4816 #
4817 # As code becomes more uniform, this could be made stricter.
Daniel Bratell8ba52722018-03-02 16:06:144818
Sam Maiera6e76d72022-02-11 21:43:504819 guard_name_pattern_list = [
4820 # Anything with the right suffix (maybe with an extra _).
4821 r'\w+_H__?',
Daniel Bratell8ba52722018-03-02 16:06:144822
Sam Maiera6e76d72022-02-11 21:43:504823 # To cover include guards with old Blink style.
4824 r'\w+_h',
Daniel Bratell8ba52722018-03-02 16:06:144825
Sam Maiera6e76d72022-02-11 21:43:504826 # Anything including the uppercase name of the file.
4827 r'\w*' + input_api.re.escape(
4828 replace_special_with_underscore(upper_base_file_name)) +
4829 r'\w*',
4830 ]
4831 guard_name_pattern = '|'.join(guard_name_pattern_list)
4832 guard_pattern = input_api.re.compile(r'#ifndef\s+(' +
4833 guard_name_pattern + ')')
Daniel Bratell8ba52722018-03-02 16:06:144834
Sam Maiera6e76d72022-02-11 21:43:504835 for line_number, line in enumerate(f.NewContents()):
4836 if 'no-include-guard-because-multiply-included' in line:
4837 guard_name = 'DUMMY' # To not trigger check outside the loop.
4838 break
Daniel Bratell8ba52722018-03-02 16:06:144839
Sam Maiera6e76d72022-02-11 21:43:504840 if guard_name is None:
4841 match = guard_pattern.match(line)
4842 if match:
4843 guard_name = match.group(1)
4844 guard_line_number = line_number
Daniel Bratell8ba52722018-03-02 16:06:144845
Sam Maiera6e76d72022-02-11 21:43:504846 # We allow existing files to use include guards whose names
4847 # don't match the chromium style guide, but new files should
4848 # get it right.
4849 if not f.OldContents():
4850 if guard_name != expected_guard:
4851 errors.append(
4852 output_api.PresubmitPromptWarning(
4853 'Header using the wrong include guard name %s'
4854 % guard_name, [
4855 '%s:%d' %
4856 (f.LocalPath(), line_number + 1)
4857 ], 'Expected: %r\nFound: %r' %
4858 (expected_guard, guard_name)))
4859 else:
4860 # The line after #ifndef should have a #define of the same name.
4861 if line_number == guard_line_number + 1:
4862 expected_line = '#define %s' % guard_name
4863 if line != expected_line:
4864 errors.append(
4865 output_api.PresubmitPromptWarning(
4866 'Missing "%s" for include guard' %
4867 expected_line,
4868 ['%s:%d' % (f.LocalPath(), line_number + 1)],
4869 'Expected: %r\nGot: %r' %
4870 (expected_line, line)))
Daniel Bratell8ba52722018-03-02 16:06:144871
Sam Maiera6e76d72022-02-11 21:43:504872 if not seen_guard_end and line == '#endif // %s' % guard_name:
4873 seen_guard_end = True
4874 elif seen_guard_end:
4875 if line.strip() != '':
4876 errors.append(
4877 output_api.PresubmitPromptWarning(
4878 'Include guard %s not covering the whole file'
4879 % (guard_name), [f.LocalPath()]))
4880 break # Nothing else to check and enough to warn once.
Daniel Bratell8ba52722018-03-02 16:06:144881
Sam Maiera6e76d72022-02-11 21:43:504882 if guard_name is None:
4883 errors.append(
4884 output_api.PresubmitPromptWarning(
4885 'Missing include guard %s' % expected_guard,
4886 [f.LocalPath()], 'Missing include guard in %s\n'
4887 'Recommended name: %s\n'
4888 'This check can be disabled by having the string\n'
4889 'no-include-guard-because-multiply-included in the header.'
4890 % (f.LocalPath(), expected_guard)))
4891
4892 return errors
Daniel Bratell8ba52722018-03-02 16:06:144893
4894
Saagar Sanghavifceeaae2020-08-12 16:40:364895def CheckForWindowsLineEndings(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504896 """Check source code and known ascii text files for Windows style line
4897 endings.
4898 """
4899 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate|icon)$'
mostynbb639aca52015-01-07 20:31:234900
Sam Maiera6e76d72022-02-11 21:43:504901 file_inclusion_pattern = (known_text_files,
4902 r'.+%s' % _IMPLEMENTATION_EXTENSIONS,
4903 r'.+%s' % _HEADER_EXTENSIONS)
mostynbb639aca52015-01-07 20:31:234904
Sam Maiera6e76d72022-02-11 21:43:504905 problems = []
4906 source_file_filter = lambda f: input_api.FilterSourceFile(
4907 f, files_to_check=file_inclusion_pattern, files_to_skip=None)
4908 for f in input_api.AffectedSourceFiles(source_file_filter):
4909 include_file = False
4910 for line in input_api.ReadFile(f, 'r').splitlines(True):
4911 if line.endswith('\r\n'):
4912 include_file = True
4913 if include_file:
4914 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:234915
Sam Maiera6e76d72022-02-11 21:43:504916 if problems:
4917 return [
4918 output_api.PresubmitPromptWarning(
4919 'Are you sure that you want '
4920 'these files to contain Windows style line endings?\n' +
4921 '\n'.join(problems))
4922 ]
mostynbb639aca52015-01-07 20:31:234923
Sam Maiera6e76d72022-02-11 21:43:504924 return []
4925
mostynbb639aca52015-01-07 20:31:234926
Evan Stade6cfc964c12021-05-18 20:21:164927def CheckIconFilesForLicenseHeaders(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504928 """Check that .icon files (which are fragments of C++) have license headers.
4929 """
Evan Stade6cfc964c12021-05-18 20:21:164930
Sam Maiera6e76d72022-02-11 21:43:504931 icon_files = (r'.*\.icon$', )
Evan Stade6cfc964c12021-05-18 20:21:164932
Sam Maiera6e76d72022-02-11 21:43:504933 icons = lambda x: input_api.FilterSourceFile(x, files_to_check=icon_files)
4934 return input_api.canned_checks.CheckLicense(input_api,
4935 output_api,
4936 source_file_filter=icons)
4937
Evan Stade6cfc964c12021-05-18 20:21:164938
Jose Magana2b456f22021-03-09 23:26:404939def CheckForUseOfChromeAppsDeprecations(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:504940 """Check source code for use of Chrome App technologies being
4941 deprecated.
4942 """
Jose Magana2b456f22021-03-09 23:26:404943
Sam Maiera6e76d72022-02-11 21:43:504944 def _CheckForDeprecatedTech(input_api,
4945 output_api,
4946 detection_list,
4947 files_to_check=None,
4948 files_to_skip=None):
Jose Magana2b456f22021-03-09 23:26:404949
Sam Maiera6e76d72022-02-11 21:43:504950 if (files_to_check or files_to_skip):
4951 source_file_filter = lambda f: input_api.FilterSourceFile(
4952 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
4953 else:
4954 source_file_filter = None
4955
4956 problems = []
4957
4958 for f in input_api.AffectedSourceFiles(source_file_filter):
4959 if f.Action() == 'D':
4960 continue
4961 for _, line in f.ChangedContents():
4962 if any(detect in line for detect in detection_list):
4963 problems.append(f.LocalPath())
4964
4965 return problems
4966
4967 # to avoid this presubmit script triggering warnings
4968 files_to_skip = ['PRESUBMIT.py', 'PRESUBMIT_test.py']
Jose Magana2b456f22021-03-09 23:26:404969
4970 problems = []
4971
Sam Maiera6e76d72022-02-11 21:43:504972 # NMF: any files with extensions .nmf or NMF
4973 _NMF_FILES = r'\.(nmf|NMF)$'
4974 problems += _CheckForDeprecatedTech(
4975 input_api,
4976 output_api,
4977 detection_list=[''], # any change to the file will trigger warning
4978 files_to_check=[r'.+%s' % _NMF_FILES])
Jose Magana2b456f22021-03-09 23:26:404979
Sam Maiera6e76d72022-02-11 21:43:504980 # MANIFEST: any manifest.json that in its diff includes "app":
4981 _MANIFEST_FILES = r'(manifest\.json)$'
4982 problems += _CheckForDeprecatedTech(
4983 input_api,
4984 output_api,
4985 detection_list=['"app":'],
4986 files_to_check=[r'.*%s' % _MANIFEST_FILES])
Jose Magana2b456f22021-03-09 23:26:404987
Sam Maiera6e76d72022-02-11 21:43:504988 # NaCl / PNaCl: any file that in its diff contains the strings in the list
4989 problems += _CheckForDeprecatedTech(
4990 input_api,
4991 output_api,
4992 detection_list=['config=nacl', 'enable-nacl', 'cpu=pnacl', 'nacl_io'],
4993 files_to_skip=files_to_skip + [r"^native_client_sdk[\\/]"])
Jose Magana2b456f22021-03-09 23:26:404994
Sam Maiera6e76d72022-02-11 21:43:504995 # PPAPI: any C/C++ file that in its diff includes a ppappi library
4996 problems += _CheckForDeprecatedTech(
4997 input_api,
4998 output_api,
4999 detection_list=['#include "ppapi', '#include <ppapi'],
5000 files_to_check=(r'.+%s' % _HEADER_EXTENSIONS,
5001 r'.+%s' % _IMPLEMENTATION_EXTENSIONS),
5002 files_to_skip=[r"^ppapi[\\/]"])
Jose Magana2b456f22021-03-09 23:26:405003
Sam Maiera6e76d72022-02-11 21:43:505004 if problems:
5005 return [
5006 output_api.PresubmitPromptWarning(
5007 'You are adding/modifying code'
5008 'related to technologies which will soon be deprecated (Chrome Apps, NaCl,'
5009 ' PNaCl, PPAPI). See this blog post for more details:\n'
5010 'https://blog.chromium.org/2020/08/changes-to-chrome-app-support-timeline.html\n'
5011 'and this documentation for options to replace these technologies:\n'
5012 'https://developer.chrome.com/docs/apps/migration/\n' +
5013 '\n'.join(problems))
5014 ]
Jose Magana2b456f22021-03-09 23:26:405015
Sam Maiera6e76d72022-02-11 21:43:505016 return []
Jose Magana2b456f22021-03-09 23:26:405017
mostynbb639aca52015-01-07 20:31:235018
Saagar Sanghavifceeaae2020-08-12 16:40:365019def CheckSyslogUseWarningOnUpload(input_api, output_api, src_file_filter=None):
Sam Maiera6e76d72022-02-11 21:43:505020 """Checks that all source files use SYSLOG properly."""
5021 syslog_files = []
5022 for f in input_api.AffectedSourceFiles(src_file_filter):
5023 for line_number, line in f.ChangedContents():
5024 if 'SYSLOG' in line:
5025 syslog_files.append(f.LocalPath() + ':' + str(line_number))
pastarmovj032ba5bc2017-01-12 10:41:565026
Sam Maiera6e76d72022-02-11 21:43:505027 if syslog_files:
5028 return [
5029 output_api.PresubmitPromptWarning(
5030 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
5031 ' calls.\nFiles to check:\n',
5032 items=syslog_files)
5033 ]
5034 return []
pastarmovj89f7ee12016-09-20 14:58:135035
5036
[email protected]1f7b4172010-01-28 01:17:345037def CheckChangeOnUpload(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505038 if input_api.version < [2, 0, 0]:
5039 return [
5040 output_api.PresubmitError(
5041 "Your depot_tools is out of date. "
5042 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
5043 "but your version is %d.%d.%d" % tuple(input_api.version))
5044 ]
5045 results = []
5046 results.extend(
5047 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
5048 return results
[email protected]ca8d1982009-02-19 16:33:125049
5050
5051def CheckChangeOnCommit(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505052 if input_api.version < [2, 0, 0]:
5053 return [
5054 output_api.PresubmitError(
5055 "Your depot_tools is out of date. "
5056 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
5057 "but your version is %d.%d.%d" % tuple(input_api.version))
5058 ]
Saagar Sanghavifceeaae2020-08-12 16:40:365059
Sam Maiera6e76d72022-02-11 21:43:505060 results = []
5061 # Make sure the tree is 'open'.
5062 results.extend(
5063 input_api.canned_checks.CheckTreeIsOpen(
5064 input_api,
5065 output_api,
5066 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:275067
Sam Maiera6e76d72022-02-11 21:43:505068 results.extend(
5069 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
5070 results.extend(
5071 input_api.canned_checks.CheckChangeHasBugField(input_api, output_api))
5072 results.extend(
5073 input_api.canned_checks.CheckChangeHasNoUnwantedTags(
5074 input_api, output_api))
5075 results.extend(
5076 input_api.canned_checks.CheckChangeHasDescription(
5077 input_api, output_api))
5078 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145079
5080
Saagar Sanghavifceeaae2020-08-12 16:40:365081def CheckStrings(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505082 """Check string ICU syntax validity and if translation screenshots exist."""
5083 # Skip translation screenshots check if a SkipTranslationScreenshotsCheck
5084 # footer is set to true.
5085 git_footers = input_api.change.GitFootersFromDescription()
5086 skip_screenshot_check_footer = [
5087 footer.lower() for footer in git_footers.get(
5088 u'Skip-Translation-Screenshots-Check', [])
5089 ]
5090 run_screenshot_check = u'true' not in skip_screenshot_check_footer
Edward Lesmesf7c5c6d2020-05-14 23:30:025091
Sam Maiera6e76d72022-02-11 21:43:505092 import os
5093 import re
5094 import sys
5095 from io import StringIO
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145096
Sam Maiera6e76d72022-02-11 21:43:505097 new_or_added_paths = set(f.LocalPath() for f in input_api.AffectedFiles()
5098 if (f.Action() == 'A' or f.Action() == 'M'))
5099 removed_paths = set(f.LocalPath()
5100 for f in input_api.AffectedFiles(include_deletes=True)
5101 if f.Action() == 'D')
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145102
Sam Maiera6e76d72022-02-11 21:43:505103 affected_grds = [
5104 f for f in input_api.AffectedFiles()
5105 if f.LocalPath().endswith(('.grd', '.grdp'))
5106 ]
5107 affected_grds = [
5108 f for f in affected_grds if not 'testdata' in f.LocalPath()
5109 ]
5110 if not affected_grds:
5111 return []
meacer8c0d3832019-12-26 21:46:165112
Sam Maiera6e76d72022-02-11 21:43:505113 affected_png_paths = [
5114 f.AbsoluteLocalPath() for f in input_api.AffectedFiles()
5115 if (f.LocalPath().endswith('.png'))
5116 ]
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145117
Sam Maiera6e76d72022-02-11 21:43:505118 # Check for screenshots. Developers can upload screenshots using
5119 # tools/translation/upload_screenshots.py which finds and uploads
5120 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
5121 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
5122 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
5123 #
5124 # The logic here is as follows:
5125 #
5126 # - If the CL has a .png file under the screenshots directory for a grd
5127 # file, warn the developer. Actual images should never be checked into the
5128 # Chrome repo.
5129 #
5130 # - If the CL contains modified or new messages in grd files and doesn't
5131 # contain the corresponding .sha1 files, warn the developer to add images
5132 # and upload them via tools/translation/upload_screenshots.py.
5133 #
5134 # - If the CL contains modified or new messages in grd files and the
5135 # corresponding .sha1 files, everything looks good.
5136 #
5137 # - If the CL contains removed messages in grd files but the corresponding
5138 # .sha1 files aren't removed, warn the developer to remove them.
5139 unnecessary_screenshots = []
5140 missing_sha1 = []
5141 unnecessary_sha1_files = []
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145142
Sam Maiera6e76d72022-02-11 21:43:505143 # This checks verifies that the ICU syntax of messages this CL touched is
5144 # valid, and reports any found syntax errors.
5145 # Without this presubmit check, ICU syntax errors in Chromium strings can land
5146 # without developers being aware of them. Later on, such ICU syntax errors
5147 # break message extraction for translation, hence would block Chromium
5148 # translations until they are fixed.
5149 icu_syntax_errors = []
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145150
Sam Maiera6e76d72022-02-11 21:43:505151 def _CheckScreenshotAdded(screenshots_dir, message_id):
5152 sha1_path = input_api.os_path.join(screenshots_dir,
5153 message_id + '.png.sha1')
5154 if sha1_path not in new_or_added_paths:
5155 missing_sha1.append(sha1_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145156
Sam Maiera6e76d72022-02-11 21:43:505157 def _CheckScreenshotRemoved(screenshots_dir, message_id):
5158 sha1_path = input_api.os_path.join(screenshots_dir,
5159 message_id + '.png.sha1')
5160 if input_api.os_path.exists(
5161 sha1_path) and sha1_path not in removed_paths:
5162 unnecessary_sha1_files.append(sha1_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145163
Sam Maiera6e76d72022-02-11 21:43:505164 def _ValidateIcuSyntax(text, level, signatures):
5165 """Validates ICU syntax of a text string.
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145166
Sam Maiera6e76d72022-02-11 21:43:505167 Check if text looks similar to ICU and checks for ICU syntax correctness
5168 in this case. Reports various issues with ICU syntax and values of
5169 variants. Supports checking of nested messages. Accumulate information of
5170 each ICU messages found in the text for further checking.
Rainhard Findlingfc31844c52020-05-15 09:58:265171
Sam Maiera6e76d72022-02-11 21:43:505172 Args:
5173 text: a string to check.
5174 level: a number of current nesting level.
5175 signatures: an accumulator, a list of tuple of (level, variable,
5176 kind, variants).
Rainhard Findlingfc31844c52020-05-15 09:58:265177
Sam Maiera6e76d72022-02-11 21:43:505178 Returns:
5179 None if a string is not ICU or no issue detected.
5180 A tuple of (message, start index, end index) if an issue detected.
5181 """
5182 valid_types = {
5183 'plural': (frozenset(
5184 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many',
5185 'other']), frozenset(['=1', 'other'])),
5186 'selectordinal': (frozenset(
5187 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many',
5188 'other']), frozenset(['one', 'other'])),
5189 'select': (frozenset(), frozenset(['other'])),
5190 }
Rainhard Findlingfc31844c52020-05-15 09:58:265191
Sam Maiera6e76d72022-02-11 21:43:505192 # Check if the message looks like an attempt to use ICU
5193 # plural. If yes - check if its syntax strictly matches ICU format.
5194 like = re.match(r'^[^{]*\{[^{]*\b(plural|selectordinal|select)\b',
5195 text)
5196 if not like:
5197 signatures.append((level, None, None, None))
5198 return
Rainhard Findlingfc31844c52020-05-15 09:58:265199
Sam Maiera6e76d72022-02-11 21:43:505200 # Check for valid prefix and suffix
5201 m = re.match(
5202 r'^([^{]*\{)([a-zA-Z0-9_]+),\s*'
5203 r'(plural|selectordinal|select),\s*'
5204 r'(?:offset:\d+)?\s*(.*)', text, re.DOTALL)
5205 if not m:
5206 return (('This message looks like an ICU plural, '
5207 'but does not follow ICU syntax.'), like.start(),
5208 like.end())
5209 starting, variable, kind, variant_pairs = m.groups()
5210 variants, depth, last_pos = _ParseIcuVariants(variant_pairs,
5211 m.start(4))
5212 if depth:
5213 return ('Invalid ICU format. Unbalanced opening bracket', last_pos,
5214 len(text))
5215 first = text[0]
5216 ending = text[last_pos:]
5217 if not starting:
5218 return ('Invalid ICU format. No initial opening bracket',
5219 last_pos - 1, last_pos)
5220 if not ending or '}' not in ending:
5221 return ('Invalid ICU format. No final closing bracket',
5222 last_pos - 1, last_pos)
5223 elif first != '{':
5224 return ((
5225 'Invalid ICU format. Extra characters at the start of a complex '
5226 'message (go/icu-message-migration): "%s"') % starting, 0,
5227 len(starting))
5228 elif ending != '}':
5229 return ((
5230 'Invalid ICU format. Extra characters at the end of a complex '
5231 'message (go/icu-message-migration): "%s"') % ending,
5232 last_pos - 1, len(text) - 1)
5233 if kind not in valid_types:
5234 return (('Unknown ICU message type %s. '
5235 'Valid types are: plural, select, selectordinal') % kind,
5236 0, 0)
5237 known, required = valid_types[kind]
5238 defined_variants = set()
5239 for variant, variant_range, value, value_range in variants:
5240 start, end = variant_range
5241 if variant in defined_variants:
5242 return ('Variant "%s" is defined more than once' % variant,
5243 start, end)
5244 elif known and variant not in known:
5245 return ('Variant "%s" is not valid for %s message' %
5246 (variant, kind), start, end)
5247 defined_variants.add(variant)
5248 # Check for nested structure
5249 res = _ValidateIcuSyntax(value[1:-1], level + 1, signatures)
5250 if res:
5251 return (res[0], res[1] + value_range[0] + 1,
5252 res[2] + value_range[0] + 1)
5253 missing = required - defined_variants
5254 if missing:
5255 return ('Required variants missing: %s' % ', '.join(missing), 0,
5256 len(text))
5257 signatures.append((level, variable, kind, defined_variants))
Rainhard Findlingfc31844c52020-05-15 09:58:265258
Sam Maiera6e76d72022-02-11 21:43:505259 def _ParseIcuVariants(text, offset=0):
5260 """Parse variants part of ICU complex message.
Rainhard Findlingfc31844c52020-05-15 09:58:265261
Sam Maiera6e76d72022-02-11 21:43:505262 Builds a tuple of variant names and values, as well as
5263 their offsets in the input string.
Rainhard Findlingfc31844c52020-05-15 09:58:265264
Sam Maiera6e76d72022-02-11 21:43:505265 Args:
5266 text: a string to parse
5267 offset: additional offset to add to positions in the text to get correct
5268 position in the complete ICU string.
Rainhard Findlingfc31844c52020-05-15 09:58:265269
Sam Maiera6e76d72022-02-11 21:43:505270 Returns:
5271 List of tuples, each tuple consist of four fields: variant name,
5272 variant name span (tuple of two integers), variant value, value
5273 span (tuple of two integers).
5274 """
5275 depth, start, end = 0, -1, -1
5276 variants = []
5277 key = None
5278 for idx, char in enumerate(text):
5279 if char == '{':
5280 if not depth:
5281 start = idx
5282 chunk = text[end + 1:start]
5283 key = chunk.strip()
5284 pos = offset + end + 1 + chunk.find(key)
5285 span = (pos, pos + len(key))
5286 depth += 1
5287 elif char == '}':
5288 if not depth:
5289 return variants, depth, offset + idx
5290 depth -= 1
5291 if not depth:
5292 end = idx
5293 variants.append((key, span, text[start:end + 1],
5294 (offset + start, offset + end + 1)))
5295 return variants, depth, offset + end + 1
Rainhard Findlingfc31844c52020-05-15 09:58:265296
Sam Maiera6e76d72022-02-11 21:43:505297 try:
5298 old_sys_path = sys.path
5299 sys.path = sys.path + [
5300 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
5301 'translation')
5302 ]
5303 from helper import grd_helper
5304 finally:
5305 sys.path = old_sys_path
Rainhard Findlingfc31844c52020-05-15 09:58:265306
Sam Maiera6e76d72022-02-11 21:43:505307 for f in affected_grds:
5308 file_path = f.LocalPath()
5309 old_id_to_msg_map = {}
5310 new_id_to_msg_map = {}
5311 # Note that this code doesn't check if the file has been deleted. This is
5312 # OK because it only uses the old and new file contents and doesn't load
5313 # the file via its path.
5314 # It's also possible that a file's content refers to a renamed or deleted
5315 # file via a <part> tag, such as <part file="now-deleted-file.grdp">. This
5316 # is OK as well, because grd_helper ignores <part> tags when loading .grd or
5317 # .grdp files.
5318 if file_path.endswith('.grdp'):
5319 if f.OldContents():
5320 old_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
5321 '\n'.join(f.OldContents()))
5322 if f.NewContents():
5323 new_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
5324 '\n'.join(f.NewContents()))
5325 else:
5326 file_dir = input_api.os_path.dirname(file_path) or '.'
5327 if f.OldContents():
5328 old_id_to_msg_map = grd_helper.GetGrdMessages(
5329 StringIO('\n'.join(f.OldContents())), file_dir)
5330 if f.NewContents():
5331 new_id_to_msg_map = grd_helper.GetGrdMessages(
5332 StringIO('\n'.join(f.NewContents())), file_dir)
Rainhard Findlingfc31844c52020-05-15 09:58:265333
Sam Maiera6e76d72022-02-11 21:43:505334 grd_name, ext = input_api.os_path.splitext(
5335 input_api.os_path.basename(file_path))
5336 screenshots_dir = input_api.os_path.join(
5337 input_api.os_path.dirname(file_path),
5338 grd_name + ext.replace('.', '_'))
Rainhard Findlingfc31844c52020-05-15 09:58:265339
Sam Maiera6e76d72022-02-11 21:43:505340 # Compute added, removed and modified message IDs.
5341 old_ids = set(old_id_to_msg_map)
5342 new_ids = set(new_id_to_msg_map)
5343 added_ids = new_ids - old_ids
5344 removed_ids = old_ids - new_ids
5345 modified_ids = set([])
5346 for key in old_ids.intersection(new_ids):
5347 if (old_id_to_msg_map[key].ContentsAsXml('', True) !=
5348 new_id_to_msg_map[key].ContentsAsXml('', True)):
5349 # The message content itself changed. Require an updated screenshot.
5350 modified_ids.add(key)
5351 elif old_id_to_msg_map[key].attrs['meaning'] != \
5352 new_id_to_msg_map[key].attrs['meaning']:
5353 # The message meaning changed. Ensure there is a screenshot for it.
5354 sha1_path = input_api.os_path.join(screenshots_dir,
5355 key + '.png.sha1')
5356 if sha1_path not in new_or_added_paths and not \
5357 input_api.os_path.exists(sha1_path):
5358 # There is neither a previous screenshot nor is a new one added now.
5359 # Require a screenshot.
5360 modified_ids.add(key)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145361
Sam Maiera6e76d72022-02-11 21:43:505362 if run_screenshot_check:
5363 # Check the screenshot directory for .png files. Warn if there is any.
5364 for png_path in affected_png_paths:
5365 if png_path.startswith(screenshots_dir):
5366 unnecessary_screenshots.append(png_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145367
Sam Maiera6e76d72022-02-11 21:43:505368 for added_id in added_ids:
5369 _CheckScreenshotAdded(screenshots_dir, added_id)
Rainhard Findlingd8d04372020-08-13 13:30:095370
Sam Maiera6e76d72022-02-11 21:43:505371 for modified_id in modified_ids:
5372 _CheckScreenshotAdded(screenshots_dir, modified_id)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145373
Sam Maiera6e76d72022-02-11 21:43:505374 for removed_id in removed_ids:
5375 _CheckScreenshotRemoved(screenshots_dir, removed_id)
5376
5377 # Check new and changed strings for ICU syntax errors.
5378 for key in added_ids.union(modified_ids):
5379 msg = new_id_to_msg_map[key].ContentsAsXml('', True)
5380 err = _ValidateIcuSyntax(msg, 0, [])
5381 if err is not None:
5382 icu_syntax_errors.append(str(key) + ': ' + str(err[0]))
5383
5384 results = []
Rainhard Findlingfc31844c52020-05-15 09:58:265385 if run_screenshot_check:
Sam Maiera6e76d72022-02-11 21:43:505386 if unnecessary_screenshots:
5387 results.append(
5388 output_api.PresubmitError(
5389 'Do not include actual screenshots in the changelist. Run '
5390 'tools/translate/upload_screenshots.py to upload them instead:',
5391 sorted(unnecessary_screenshots)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145392
Sam Maiera6e76d72022-02-11 21:43:505393 if missing_sha1:
5394 results.append(
5395 output_api.PresubmitError(
5396 'You are adding or modifying UI strings.\n'
5397 'To ensure the best translations, take screenshots of the relevant UI '
5398 '(https://g.co/chrome/translation) and add these files to your '
5399 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145400
Sam Maiera6e76d72022-02-11 21:43:505401 if unnecessary_sha1_files:
5402 results.append(
5403 output_api.PresubmitError(
5404 'You removed strings associated with these files. Remove:',
5405 sorted(unnecessary_sha1_files)))
5406 else:
5407 results.append(
5408 output_api.PresubmitPromptOrNotify('Skipping translation '
5409 'screenshots check.'))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145410
Sam Maiera6e76d72022-02-11 21:43:505411 if icu_syntax_errors:
5412 results.append(
5413 output_api.PresubmitPromptWarning(
5414 'ICU syntax errors were found in the following strings (problems or '
5415 'feedback? Contact [email protected]):',
5416 items=icu_syntax_errors))
Rainhard Findlingfc31844c52020-05-15 09:58:265417
Sam Maiera6e76d72022-02-11 21:43:505418 return results
Mustafa Emre Acer51f2f742020-03-09 19:41:125419
5420
Saagar Sanghavifceeaae2020-08-12 16:40:365421def CheckTranslationExpectations(input_api, output_api,
Mustafa Emre Acer51f2f742020-03-09 19:41:125422 repo_root=None,
5423 translation_expectations_path=None,
5424 grd_files=None):
Sam Maiera6e76d72022-02-11 21:43:505425 import sys
5426 affected_grds = [
5427 f for f in input_api.AffectedFiles()
5428 if (f.LocalPath().endswith('.grd') or f.LocalPath().endswith('.grdp'))
5429 ]
5430 if not affected_grds:
5431 return []
5432
5433 try:
5434 old_sys_path = sys.path
5435 sys.path = sys.path + [
5436 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools',
5437 'translation')
5438 ]
5439 from helper import git_helper
5440 from helper import translation_helper
5441 finally:
5442 sys.path = old_sys_path
5443
5444 # Check that translation expectations can be parsed and we can get a list of
5445 # translatable grd files. |repo_root| and |translation_expectations_path| are
5446 # only passed by tests.
5447 if not repo_root:
5448 repo_root = input_api.PresubmitLocalPath()
5449 if not translation_expectations_path:
5450 translation_expectations_path = input_api.os_path.join(
5451 repo_root, 'tools', 'gritsettings', 'translation_expectations.pyl')
5452 if not grd_files:
5453 grd_files = git_helper.list_grds_in_repository(repo_root)
5454
5455 # Ignore bogus grd files used only for testing
5456 # ui/webui/resoucres/tools/generate_grd.py.
5457 ignore_path = input_api.os_path.join('ui', 'webui', 'resources', 'tools',
5458 'tests')
5459 grd_files = [p for p in grd_files if ignore_path not in p]
5460
5461 try:
5462 translation_helper.get_translatable_grds(
5463 repo_root, grd_files, translation_expectations_path)
5464 except Exception as e:
5465 return [
5466 output_api.PresubmitNotifyResult(
5467 'Failed to get a list of translatable grd files. This happens when:\n'
5468 ' - One of the modified grd or grdp files cannot be parsed or\n'
5469 ' - %s is not updated.\n'
5470 'Stack:\n%s' % (translation_expectations_path, str(e)))
5471 ]
Mustafa Emre Acer51f2f742020-03-09 19:41:125472 return []
5473
Ken Rockotc31f4832020-05-29 18:58:515474
Saagar Sanghavifceeaae2020-08-12 16:40:365475def CheckStableMojomChanges(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505476 """Changes to [Stable] mojom types must preserve backward-compatibility."""
5477 changed_mojoms = input_api.AffectedFiles(
5478 include_deletes=True,
5479 file_filter=lambda f: f.LocalPath().endswith(('.mojom')))
Erik Staabc734cd7a2021-11-23 03:11:525480
Sam Maiera6e76d72022-02-11 21:43:505481 if not changed_mojoms:
5482 return []
5483
5484 delta = []
5485 for mojom in changed_mojoms:
5486 old_contents = ''.join(mojom.OldContents()) or None
5487 new_contents = ''.join(mojom.NewContents()) or None
5488 delta.append({
5489 'filename': mojom.LocalPath(),
5490 'old': '\n'.join(mojom.OldContents()) or None,
5491 'new': '\n'.join(mojom.NewContents()) or None,
5492 })
5493
5494 process = input_api.subprocess.Popen([
5495 input_api.python_executable,
5496 input_api.os_path.join(
5497 input_api.PresubmitLocalPath(), 'mojo', 'public', 'tools', 'mojom',
5498 'check_stable_mojom_compatibility.py'), '--src-root',
5499 input_api.PresubmitLocalPath()
5500 ],
5501 stdin=input_api.subprocess.PIPE,
5502 stdout=input_api.subprocess.PIPE,
5503 stderr=input_api.subprocess.PIPE,
5504 universal_newlines=True)
5505 (x, error) = process.communicate(input=input_api.json.dumps(delta))
5506 if process.returncode:
5507 return [
5508 output_api.PresubmitError(
5509 'One or more [Stable] mojom definitions appears to have been changed '
5510 'in a way that is not backward-compatible.',
5511 long_text=error)
5512 ]
Erik Staabc734cd7a2021-11-23 03:11:525513 return []
5514
Dominic Battre645d42342020-12-04 16:14:105515def CheckDeprecationOfPreferences(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505516 """Removing a preference should come with a deprecation."""
Dominic Battre645d42342020-12-04 16:14:105517
Sam Maiera6e76d72022-02-11 21:43:505518 def FilterFile(affected_file):
5519 """Accept only .cc files and the like."""
5520 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
5521 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
5522 input_api.DEFAULT_FILES_TO_SKIP)
5523 return input_api.FilterSourceFile(
5524 affected_file,
5525 files_to_check=file_inclusion_pattern,
5526 files_to_skip=files_to_skip)
Dominic Battre645d42342020-12-04 16:14:105527
Sam Maiera6e76d72022-02-11 21:43:505528 def ModifiedLines(affected_file):
5529 """Returns a list of tuples (line number, line text) of added and removed
5530 lines.
Dominic Battre645d42342020-12-04 16:14:105531
Sam Maiera6e76d72022-02-11 21:43:505532 Deleted lines share the same line number as the previous line.
Dominic Battre645d42342020-12-04 16:14:105533
Sam Maiera6e76d72022-02-11 21:43:505534 This relies on the scm diff output describing each changed code section
5535 with a line of the form
Dominic Battre645d42342020-12-04 16:14:105536
Sam Maiera6e76d72022-02-11 21:43:505537 ^@@ <old line num>,<old size> <new line num>,<new size> @@$
5538 """
5539 line_num = 0
5540 modified_lines = []
5541 for line in affected_file.GenerateScmDiff().splitlines():
5542 # Extract <new line num> of the patch fragment (see format above).
5543 m = input_api.re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@',
5544 line)
5545 if m:
5546 line_num = int(m.groups(1)[0])
5547 continue
5548 if ((line.startswith('+') and not line.startswith('++'))
5549 or (line.startswith('-') and not line.startswith('--'))):
5550 modified_lines.append((line_num, line))
Dominic Battre645d42342020-12-04 16:14:105551
Sam Maiera6e76d72022-02-11 21:43:505552 if not line.startswith('-'):
5553 line_num += 1
5554 return modified_lines
Dominic Battre645d42342020-12-04 16:14:105555
Sam Maiera6e76d72022-02-11 21:43:505556 def FindLineWith(lines, needle):
5557 """Returns the line number (i.e. index + 1) in `lines` containing `needle`.
Dominic Battre645d42342020-12-04 16:14:105558
Sam Maiera6e76d72022-02-11 21:43:505559 If 0 or >1 lines contain `needle`, -1 is returned.
5560 """
5561 matching_line_numbers = [
5562 # + 1 for 1-based counting of line numbers.
5563 i + 1 for i, line in enumerate(lines) if needle in line
5564 ]
5565 return matching_line_numbers[0] if len(
5566 matching_line_numbers) == 1 else -1
Dominic Battre645d42342020-12-04 16:14:105567
Sam Maiera6e76d72022-02-11 21:43:505568 def ModifiedPrefMigration(affected_file):
5569 """Returns whether the MigrateObsolete.*Pref functions were modified."""
5570 # Determine first and last lines of MigrateObsolete.*Pref functions.
5571 new_contents = affected_file.NewContents()
5572 range_1 = (FindLineWith(new_contents,
5573 'BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'),
5574 FindLineWith(new_contents,
5575 'END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'))
5576 range_2 = (FindLineWith(new_contents,
5577 'BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS'),
5578 FindLineWith(new_contents,
5579 'END_MIGRATE_OBSOLETE_PROFILE_PREFS'))
5580 if (-1 in range_1 + range_2):
5581 raise Exception(
5582 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.'
5583 )
Dominic Battre645d42342020-12-04 16:14:105584
Sam Maiera6e76d72022-02-11 21:43:505585 # Check whether any of the modified lines are part of the
5586 # MigrateObsolete.*Pref functions.
5587 for line_nr, line in ModifiedLines(affected_file):
5588 if (range_1[0] <= line_nr <= range_1[1]
5589 or range_2[0] <= line_nr <= range_2[1]):
5590 return True
5591 return False
Dominic Battre645d42342020-12-04 16:14:105592
Sam Maiera6e76d72022-02-11 21:43:505593 register_pref_pattern = input_api.re.compile(r'Register.+Pref')
5594 browser_prefs_file_pattern = input_api.re.compile(
5595 r'chrome/browser/prefs/browser_prefs.cc')
Dominic Battre645d42342020-12-04 16:14:105596
Sam Maiera6e76d72022-02-11 21:43:505597 changes = input_api.AffectedFiles(include_deletes=True,
5598 file_filter=FilterFile)
5599 potential_problems = []
5600 for f in changes:
5601 for line in f.GenerateScmDiff().splitlines():
5602 # Check deleted lines for pref registrations.
5603 if (line.startswith('-') and not line.startswith('--')
5604 and register_pref_pattern.search(line)):
5605 potential_problems.append('%s: %s' % (f.LocalPath(), line))
Dominic Battre645d42342020-12-04 16:14:105606
Sam Maiera6e76d72022-02-11 21:43:505607 if browser_prefs_file_pattern.search(f.LocalPath()):
5608 # If the developer modified the MigrateObsolete.*Prefs() functions, we
5609 # assume that they knew that they have to deprecate preferences and don't
5610 # warn.
5611 try:
5612 if ModifiedPrefMigration(f):
5613 return []
5614 except Exception as e:
5615 return [output_api.PresubmitError(str(e))]
Dominic Battre645d42342020-12-04 16:14:105616
Sam Maiera6e76d72022-02-11 21:43:505617 if potential_problems:
5618 return [
5619 output_api.PresubmitPromptWarning(
5620 'Discovered possible removal of preference registrations.\n\n'
5621 'Please make sure to properly deprecate preferences by clearing their\n'
5622 'value for a couple of milestones before finally removing the code.\n'
5623 'Otherwise data may stay in the preferences files forever. See\n'
5624 'Migrate*Prefs() in chrome/browser/prefs/browser_prefs.cc and\n'
5625 'chrome/browser/prefs/README.md for examples.\n'
5626 'This may be a false positive warning (e.g. if you move preference\n'
5627 'registrations to a different place).\n', potential_problems)
5628 ]
5629 return []
5630
Matt Stark6ef08872021-07-29 01:21:465631
5632def CheckConsistentGrdChanges(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505633 """Changes to GRD files must be consistent for tools to read them."""
5634 changed_grds = input_api.AffectedFiles(
5635 include_deletes=False,
5636 file_filter=lambda f: f.LocalPath().endswith(('.grd')))
5637 errors = []
5638 invalid_file_regexes = [(input_api.re.compile(matcher), msg)
5639 for matcher, msg in _INVALID_GRD_FILE_LINE]
5640 for grd in changed_grds:
5641 for i, line in enumerate(grd.NewContents()):
5642 for matcher, msg in invalid_file_regexes:
5643 if matcher.search(line):
5644 errors.append(
5645 output_api.PresubmitError(
5646 'Problem on {grd}:{i} - {msg}'.format(
5647 grd=grd.LocalPath(), i=i + 1, msg=msg)))
5648 return errors
5649
Kevin McNee967dd2d22021-11-15 16:09:295650
5651def CheckMPArchApiUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505652 """CC the MPArch watchlist if the CL uses an API that is ambiguous in the
5653 presence of MPArch features such as bfcache, prerendering, and fenced frames.
5654 """
Kevin McNee967dd2d22021-11-15 16:09:295655
Sam Maiera6e76d72022-02-11 21:43:505656 # Only consider top-level directories that (1) can use content APIs, (2)
5657 # apply to desktop or android chrome, and (3) are known to have a significant
5658 # number of uses of the APIs of concern.
5659 files_to_check = (
5660 r'^(chrome|components|content|extensions)[\\/].+%s' %
Kevin McNee967dd2d22021-11-15 16:09:295661 _IMPLEMENTATION_EXTENSIONS,
Sam Maiera6e76d72022-02-11 21:43:505662 r'^(chrome|components|content|extensions)[\\/].+%s' %
5663 _HEADER_EXTENSIONS,
5664 )
5665 files_to_skip = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
5666 input_api.DEFAULT_FILES_TO_SKIP)
5667 source_file_filter = lambda f: input_api.FilterSourceFile(
5668 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
Kevin McNee967dd2d22021-11-15 16:09:295669
Sam Maiera6e76d72022-02-11 21:43:505670 # Note that since these are are just regular expressions and we don't have
5671 # the compiler's AST, we could have spurious matches (e.g. an unrelated class
5672 # could have a method named IsInMainFrame).
5673 concerning_class_pattern = input_api.re.compile(
5674 r'WebContentsObserver|WebContentsUserData')
5675 # A subset of WebContentsObserver overrides where there's particular risk for
5676 # confusing tab and page level operations and data (e.g. incorrectly
5677 # resetting page state in DidFinishNavigation).
5678 concerning_wco_methods = [
5679 'DidStartNavigation',
5680 'ReadyToCommitNavigation',
5681 'DidFinishNavigation',
5682 'RenderViewReady',
5683 'RenderViewDeleted',
5684 'RenderViewHostChanged',
5685 'PrimaryMainDocumentElementAvailable',
5686 'DocumentOnLoadCompletedInPrimaryMainFrame',
5687 'DOMContentLoaded',
5688 'DidFinishLoad',
5689 ]
5690 concerning_nav_handle_methods = [
5691 'IsInMainFrame',
5692 ]
5693 concerning_web_contents_methods = [
5694 'ForEachFrame',
5695 'GetAllFrames',
5696 'FromRenderFrameHost',
5697 'FromRenderViewHost',
5698 'GetMainFrame',
5699 'GetRenderViewHost',
5700 ]
5701 concerning_rfh_methods = [
5702 'GetParent',
5703 'GetMainFrame',
5704 'GetFrameTreeNodeId',
5705 ]
5706 concerning_method_pattern = input_api.re.compile(r'(' + r'|'.join(
5707 item for sublist in [
5708 concerning_wco_methods, concerning_nav_handle_methods,
5709 concerning_web_contents_methods, concerning_rfh_methods
5710 ] for item in sublist) + r')\(')
Kevin McNee967dd2d22021-11-15 16:09:295711
Kevin McNee4eeec792022-02-14 20:02:045712 used_apis = set()
Sam Maiera6e76d72022-02-11 21:43:505713 for f in input_api.AffectedFiles(include_deletes=False,
5714 file_filter=source_file_filter):
5715 for line_num, line in f.ChangedContents():
Kevin McNee4eeec792022-02-14 20:02:045716 class_match = concerning_class_pattern.search(line)
5717 if class_match:
5718 used_apis.add(class_match[0])
5719 method_match = concerning_method_pattern.search(line)
5720 if method_match:
5721 used_apis.add(method_match[1])
Sam Maiera6e76d72022-02-11 21:43:505722
Kevin McNee4eeec792022-02-14 20:02:045723 if not used_apis:
5724 return []
Kevin McNee967dd2d22021-11-15 16:09:295725
Kevin McNee4eeec792022-02-14 20:02:045726 output_api.AppendCC('[email protected]')
5727 message = ('This change uses API(s) that are ambiguous in the presence of '
5728 'MPArch features such as bfcache, prerendering, and fenced '
5729 'frames.')
5730 explaination = (
5731 'Please double check whether new code assumes that a WebContents only '
5732 'contains a single page at a time. For example, it is discouraged to '
5733 'reset per-document state in response to the observation of a '
5734 'navigation. See this doc [1] and the comments on the individual APIs '
5735 'for guidance and this doc [2] for context. The MPArch review '
5736 'watchlist has been CC\'d on this change to help identify any issues.\n'
5737 '[1] https://docs.google.com/document/d/13l16rWTal3o5wce4i0RwdpMP5ESELLKr439Faj2BBRo/edit?usp=sharing\n'
5738 '[2] https://docs.google.com/document/d/1NginQ8k0w3znuwTiJ5qjYmBKgZDekvEPC22q0I4swxQ/edit?usp=sharing'
5739 )
5740 return [
5741 output_api.PresubmitNotifyResult(message,
5742 items=list(used_apis),
5743 long_text=explaination)
5744 ]
Henrique Ferreiro2a4b55942021-11-29 23:45:365745
5746
5747def CheckAssertAshOnlyCode(input_api, output_api):
5748 """Errors if a BUILD.gn file in an ash/ directory doesn't include
5749 assert(is_chromeos_ash).
5750 """
5751
5752 def FileFilter(affected_file):
5753 """Includes directories known to be Ash only."""
5754 return input_api.FilterSourceFile(
5755 affected_file,
5756 files_to_check=(
5757 r'^ash/.*BUILD\.gn', # Top-level src/ash/.
5758 r'.*/ash/.*BUILD\.gn'), # Any path component.
5759 files_to_skip=(input_api.DEFAULT_FILES_TO_SKIP))
5760
5761 errors = []
5762 pattern = input_api.re.compile(r'assert\(is_chromeos_ash')
Jameson Thies0ce669f2021-12-09 15:56:565763 for f in input_api.AffectedFiles(include_deletes=False,
5764 file_filter=FileFilter):
Henrique Ferreiro2a4b55942021-11-29 23:45:365765 if (not pattern.search(input_api.ReadFile(f))):
5766 errors.append(
5767 output_api.PresubmitError(
5768 'Please add assert(is_chromeos_ash) to %s. If that\'s not '
5769 'possible, please create and issue and add a comment such '
5770 'as:\n # TODO(https://crbug.com/XXX): add '
5771 'assert(is_chromeos_ash) when ...' % f.LocalPath()))
5772 return errors
Lukasz Anforowicz7016d05e2021-11-30 03:56:275773
5774
5775def _IsRendererOnlyCppFile(input_api, affected_file):
Sam Maiera6e76d72022-02-11 21:43:505776 path = affected_file.LocalPath()
5777 if not _IsCPlusPlusFile(input_api, path):
5778 return False
5779
5780 # Any code under a "renderer" subdirectory is assumed to be Renderer-only.
5781 if "/renderer/" in path:
5782 return True
5783
5784 # Blink's public/web API is only used/included by Renderer-only code. Note
5785 # that public/platform API may be used in non-Renderer processes (e.g. there
5786 # are some includes in code used by Utility, PDF, or Plugin processes).
5787 if "/blink/public/web/" in path:
5788 return True
5789
5790 # We assume that everything else may be used outside of Renderer processes.
Lukasz Anforowicz7016d05e2021-11-30 03:56:275791 return False
5792
Lukasz Anforowicz7016d05e2021-11-30 03:56:275793# TODO(https://crbug.com/1273182): Remove these checks, once they are replaced
5794# by the Chromium Clang Plugin (which will be preferable because it will
5795# 1) report errors earlier - at compile-time and 2) cover more rules).
5796def CheckRawPtrUsage(input_api, output_api):
Sam Maiera6e76d72022-02-11 21:43:505797 """Rough checks that raw_ptr<T> usage guidelines are followed."""
5798 errors = []
5799 # The regex below matches "raw_ptr<" following a word boundary, but not in a
5800 # C++ comment.
5801 raw_ptr_matcher = input_api.re.compile(r'^((?!//).)*\braw_ptr<')
5802 file_filter = lambda f: _IsRendererOnlyCppFile(input_api, f)
5803 for f, line_num, line in input_api.RightHandSideLines(file_filter):
5804 if raw_ptr_matcher.search(line):
5805 errors.append(
5806 output_api.PresubmitError(
5807 'Problem on {path}:{line} - '\
5808 'raw_ptr<T> should not be used in Renderer-only code '\
5809 '(as documented in the "Pointers to unprotected memory" '\
5810 'section in //base/memory/raw_ptr.md)'.format(
5811 path=f.LocalPath(), line=line_num)))
5812 return errors
Henrique Ferreirof9819f2e32021-11-30 13:31:565813
5814
5815def CheckPythonShebang(input_api, output_api):
5816 """Checks that python scripts use #!/usr/bin/env instead of hardcoding a
5817 system-wide python.
5818 """
5819 errors = []
5820 sources = lambda affected_file: input_api.FilterSourceFile(
5821 affected_file,
5822 files_to_skip=((_THIRD_PARTY_EXCEPT_BLINK,
5823 r'third_party/blink/web_tests/external/') + input_api.
5824 DEFAULT_FILES_TO_SKIP),
5825 files_to_check=[r'.*\.py$'])
5826 for f in input_api.AffectedSourceFiles(sources):
Takuto Ikuta36976512021-11-30 23:15:275827 for line_num, line in f.ChangedContents():
5828 if line_num == 1 and line.startswith('#!/usr/bin/python'):
5829 errors.append(f.LocalPath())
5830 break
Henrique Ferreirof9819f2e32021-11-30 13:31:565831
5832 result = []
5833 for file in errors:
5834 result.append(
5835 output_api.PresubmitError(
5836 "Please use '#!/usr/bin/env python/2/3' as the shebang of %s" %
5837 file))
5838 return result