blob: c85026e301104d96eec742ea0eec7595fc438418 [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"""
Saagar Sanghavifceeaae2020-08-12 16:40:3610PRESUBMIT_VERSION = '2.0.0'
[email protected]eea609a2011-11-18 13:10:1211
Dirk Prankee3c9c62d2021-05-18 18:35:5912# This line is 'magic' in that git-cl looks for it to decide whether to
13# use Python3 instead of Python2 when running the code in this file.
14USE_PYTHON3 = True
15
[email protected]379e7dd2010-01-28 17:39:2116_EXCLUDED_PATHS = (
Mila Greene3aa7222021-09-07 16:34:0817 # File needs to write to stdout to emulate a tool it's replacing.
Mila Greend3fc6a42021-09-10 17:38:2318 r"chrome[\\/]updater[\\/]mac[\\/]keystone[\\/]ksadmin.mm",
Ilya Shermane8a7d2d2020-07-25 04:33:4719 # Generated file.
20 (r"^components[\\/]variations[\\/]proto[\\/]devtools[\\/]"
Ilya Shermanc167a962020-08-18 18:40:2621 r"client_variations.js"),
Mila Greene3aa7222021-09-07 16:34:0822 r"^native_client_sdksrc[\\/]build_tools[\\/]make_rules.py",
Egor Paskoce145c42018-09-28 19:31:0423 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_simple.py",
24 r"^native_client_sdk[\\/]src[\\/]tools[\\/].*.mk",
25 r"^net[\\/]tools[\\/]spdyshark[\\/].*",
26 r"^skia[\\/].*",
Kent Tamura32dbbcb2018-11-30 12:28:4927 r"^third_party[\\/]blink[\\/].*",
Egor Paskoce145c42018-09-28 19:31:0428 r"^third_party[\\/]breakpad[\\/].*",
Darwin Huangd74a9d32019-07-17 17:58:4629 # sqlite is an imported third party dependency.
30 r"^third_party[\\/]sqlite[\\/].*",
Egor Paskoce145c42018-09-28 19:31:0431 r"^v8[\\/].*",
[email protected]3e4eb112011-01-18 03:29:5432 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5333 r".+_autogen\.h$",
John Budorick1e701d322019-09-11 23:35:1234 r".+_pb2\.py$",
Egor Paskoce145c42018-09-28 19:31:0435 r".+[\\/]pnacl_shim\.c$",
36 r"^gpu[\\/]config[\\/].*_list_json\.cc$",
Egor Paskoce145c42018-09-28 19:31:0437 r"tools[\\/]md_browser[\\/].*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1438 # Test pages for Maps telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0439 r"tools[\\/]perf[\\/]page_sets[\\/]maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5440 # Test pages for WebRTC telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0441 r"tools[\\/]perf[\\/]page_sets[\\/]webrtc_cases.*",
[email protected]4306417642009-06-11 00:33:4042)
[email protected]ca8d1982009-02-19 16:33:1243
John Abd-El-Malek759fea62021-03-13 03:41:1444_EXCLUDED_SET_NO_PARENT_PATHS = (
45 # It's for historical reasons that blink isn't a top level directory, where
46 # it would be allowed to have "set noparent" to avoid top level owners
47 # accidentally +1ing changes.
48 'third_party/blink/OWNERS',
49)
50
wnwenbdc444e2016-05-25 13:44:1551
[email protected]06e6d0ff2012-12-11 01:36:4452# Fragment of a regular expression that matches C++ and Objective-C++
53# implementation files.
54_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
55
wnwenbdc444e2016-05-25 13:44:1556
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:1957# Fragment of a regular expression that matches C++ and Objective-C++
58# header files.
59_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
60
61
[email protected]06e6d0ff2012-12-11 01:36:4462# Regular expression that matches code only used for test binaries
63# (best effort).
64_TEST_CODE_EXCLUDED_PATHS = (
Egor Paskoce145c42018-09-28 19:31:0465 r'.*[\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4466 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
James Cook1b4dc132021-03-09 22:45:1367 # Test suite files, like:
68 # foo_browsertest.cc
69 # bar_unittest_mac.cc (suffix)
70 # baz_unittests.cc (plural)
71 r'.+_(api|browser|eg|int|perf|pixel|unit|ui)?test(s)?(_[a-z]+)?%s' %
[email protected]e2d7e6f2013-04-23 12:57:1272 _IMPLEMENTATION_EXTENSIONS,
Matthew Denton63ea1e62019-03-25 20:39:1873 r'.+_(fuzz|fuzzer)(_[a-z]+)?%s' % _IMPLEMENTATION_EXTENSIONS,
Victor Hugo Vianna Silvac22e0202021-06-09 19:46:2174 r'.+sync_service_impl_harness%s' % _IMPLEMENTATION_EXTENSIONS,
Egor Paskoce145c42018-09-28 19:31:0475 r'.*[\\/](test|tool(s)?)[\\/].*',
danakj89f47082020-09-02 17:53:4376 # content_shell is used for running content_browsertests.
Egor Paskoce145c42018-09-28 19:31:0477 r'content[\\/]shell[\\/].*',
danakj89f47082020-09-02 17:53:4378 # Web test harness.
79 r'content[\\/]web_test[\\/].*',
[email protected]7b054982013-11-27 00:44:4780 # Non-production example code.
Egor Paskoce145c42018-09-28 19:31:0481 r'mojo[\\/]examples[\\/].*',
[email protected]8176de12014-06-20 19:07:0882 # Launcher for running iOS tests on the simulator.
Egor Paskoce145c42018-09-28 19:31:0483 r'testing[\\/]iossim[\\/]iossim\.mm$',
Olivier Robinbcea0fa2019-11-12 08:56:4184 # EarlGrey app side code for tests.
85 r'ios[\\/].*_app_interface\.mm$',
Allen Bauer0678d772020-05-11 22:25:1786 # Views Examples code
87 r'ui[\\/]views[\\/]examples[\\/].*',
Austin Sullivan33da70a2020-10-07 15:39:4188 # Chromium Codelab
89 r'codelabs[\\/]*'
[email protected]06e6d0ff2012-12-11 01:36:4490)
[email protected]ca8d1982009-02-19 16:33:1291
Daniel Bratell609102be2019-03-27 20:53:2192_THIRD_PARTY_EXCEPT_BLINK = 'third_party/(?!blink/)'
wnwenbdc444e2016-05-25 13:44:1593
[email protected]eea609a2011-11-18 13:10:1294_TEST_ONLY_WARNING = (
95 'You might be calling functions intended only for testing from\n'
danakj5f6e3b82020-09-10 13:52:5596 'production code. If you are doing this from inside another method\n'
97 'named as *ForTesting(), then consider exposing things to have tests\n'
98 'make that same call directly.\n'
99 'If that is not possible, you may put a comment on the same line with\n'
100 ' // IN-TEST \n'
101 'to tell the PRESUBMIT script that the code is inside a *ForTesting()\n'
102 'method and can be ignored. Do not do this inside production code.\n'
103 'The android-binary-size trybot will block if the method exists in the\n'
104 'release apk.')
[email protected]eea609a2011-11-18 13:10:12105
106
[email protected]cf9b78f2012-11-14 11:40:28107_INCLUDE_ORDER_WARNING = (
marjaa017dc482015-03-09 17:13:40108 'Your #include order seems to be broken. Remember to use the right '
avice9a8982015-11-24 20:36:21109 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
110 'cppguide.html#Names_and_Order_of_Includes')
[email protected]cf9b78f2012-11-14 11:40:28111
Michael Thiessen44457642020-02-06 00:24:15112# Format: Sequence of tuples containing:
113# * Full import path.
114# * Sequence of strings to show when the pattern matches.
115# * Sequence of path or filename exceptions to this rule
116_BANNED_JAVA_IMPORTS = (
117 (
Colin Blundell170d78c82020-03-12 13:56:04118 'java.net.URI;',
Michael Thiessen44457642020-02-06 00:24:15119 (
120 'Use org.chromium.url.GURL instead of java.net.URI, where possible.',
121 ),
122 (
123 'net/android/javatests/src/org/chromium/net/'
124 'AndroidProxySelectorTest.java',
125 'components/cronet/',
Ben Joyce615ba2b2020-05-20 18:22:04126 'third_party/robolectric/local/',
Michael Thiessen44457642020-02-06 00:24:15127 ),
128 ),
Michael Thiessened631912020-08-07 19:01:31129 (
130 'android.support.test.rule.UiThreadTestRule;',
131 (
132 'Do not use UiThreadTestRule, just use '
danakj89f47082020-09-02 17:53:43133 '@org.chromium.base.test.UiThreadTest on test methods that should run '
134 'on the UI thread. See https://crbug.com/1111893.',
Michael Thiessened631912020-08-07 19:01:31135 ),
136 (),
137 ),
138 (
139 'android.support.test.annotation.UiThreadTest;',
140 (
141 'Do not use android.support.test.annotation.UiThreadTest, use '
142 'org.chromium.base.test.UiThreadTest instead. See '
143 'https://crbug.com/1111893.',
144 ),
145 ()
Michael Thiessenfd6919b2020-12-08 20:44:01146 ),
147 (
148 'android.support.test.rule.ActivityTestRule;',
149 (
150 'Do not use ActivityTestRule, use '
151 'org.chromium.base.test.BaseActivityTestRule instead.',
152 ),
153 (
154 'components/cronet/',
155 )
Michael Thiessened631912020-08-07 19:01:31156 )
Michael Thiessen44457642020-02-06 00:24:15157)
wnwenbdc444e2016-05-25 13:44:15158
Daniel Bratell609102be2019-03-27 20:53:21159# Format: Sequence of tuples containing:
160# * String pattern or, if starting with a slash, a regular expression.
161# * Sequence of strings to show when the pattern matches.
162# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
Eric Stevensona9a980972017-09-23 00:04:41163_BANNED_JAVA_FUNCTIONS = (
164 (
165 'StrictMode.allowThreadDiskReads()',
166 (
167 'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
168 'directly.',
169 ),
170 False,
171 ),
172 (
173 'StrictMode.allowThreadDiskWrites()',
174 (
175 'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
176 'directly.',
177 ),
178 False,
179 ),
Michael Thiessen0f2547e2020-07-27 21:55:36180 (
181 '.waitForIdleSync()',
182 (
183 'Do not use waitForIdleSync as it masks underlying issues. There is '
184 'almost always something else you should wait on instead.',
185 ),
186 False,
187 ),
Eric Stevensona9a980972017-09-23 00:04:41188)
189
Daniel Bratell609102be2019-03-27 20:53:21190# Format: Sequence of tuples containing:
191# * String pattern or, if starting with a slash, a regular expression.
192# * Sequence of strings to show when the pattern matches.
193# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
[email protected]127f18ec2012-06-16 05:05:59194_BANNED_OBJC_FUNCTIONS = (
195 (
196 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:20197 (
198 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:59199 'prohibited. Please use CrTrackingArea instead.',
200 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
201 ),
202 False,
203 ),
204 (
[email protected]eaae1972014-04-16 04:17:26205 r'/NSTrackingArea\W',
[email protected]23e6cbc2012-06-16 18:51:20206 (
207 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:59208 'instead.',
209 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
210 ),
211 False,
212 ),
213 (
214 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20215 (
216 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59217 'Please use |convertPoint:(point) fromView:nil| instead.',
218 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
219 ),
220 True,
221 ),
222 (
223 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:20224 (
225 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59226 'Please use |convertPoint:(point) toView:nil| instead.',
227 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
228 ),
229 True,
230 ),
231 (
232 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20233 (
234 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59235 'Please use |convertRect:(point) fromView:nil| instead.',
236 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
237 ),
238 True,
239 ),
240 (
241 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20242 (
243 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59244 'Please use |convertRect:(point) toView:nil| instead.',
245 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
246 ),
247 True,
248 ),
249 (
250 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20251 (
252 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59253 'Please use |convertSize:(point) fromView:nil| instead.',
254 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
255 ),
256 True,
257 ),
258 (
259 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20260 (
261 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59262 'Please use |convertSize:(point) toView:nil| instead.',
263 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
264 ),
265 True,
266 ),
jif65398702016-10-27 10:19:48267 (
268 r"/\s+UTF8String\s*]",
269 (
270 'The use of -[NSString UTF8String] is dangerous as it can return null',
271 'even if |canBeConvertedToEncoding:NSUTF8StringEncoding| returns YES.',
272 'Please use |SysNSStringToUTF8| instead.',
273 ),
274 True,
275 ),
Sylvain Defresne4cf1d182017-09-18 14:16:34276 (
277 r'__unsafe_unretained',
278 (
279 'The use of __unsafe_unretained is almost certainly wrong, unless',
280 'when interacting with NSFastEnumeration or NSInvocation.',
281 'Please use __weak in files build with ARC, nothing otherwise.',
282 ),
283 False,
284 ),
Avi Drissman7382afa02019-04-29 23:27:13285 (
286 'freeWhenDone:NO',
287 (
288 'The use of "freeWhenDone:NO" with the NoCopy creation of ',
289 'Foundation types is prohibited.',
290 ),
291 True,
292 ),
[email protected]127f18ec2012-06-16 05:05:59293)
294
Daniel Bratell609102be2019-03-27 20:53:21295# Format: Sequence of tuples containing:
296# * String pattern or, if starting with a slash, a regular expression.
297# * Sequence of strings to show when the pattern matches.
298# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
Sylvain Defresnea8b73d252018-02-28 15:45:54299_BANNED_IOS_OBJC_FUNCTIONS = (
300 (
301 r'/\bTEST[(]',
302 (
303 'TEST() macro should not be used in Objective-C++ code as it does not ',
304 'drain the autorelease pool at the end of the test. Use TEST_F() ',
305 'macro instead with a fixture inheriting from PlatformTest (or a ',
306 'typedef).'
307 ),
308 True,
309 ),
310 (
311 r'/\btesting::Test\b',
312 (
313 'testing::Test should not be used in Objective-C++ code as it does ',
314 'not drain the autorelease pool at the end of the test. Use ',
315 'PlatformTest instead.'
316 ),
317 True,
318 ),
319)
320
Peter K. Lee6c03ccff2019-07-15 14:40:05321# Format: Sequence of tuples containing:
322# * String pattern or, if starting with a slash, a regular expression.
323# * Sequence of strings to show when the pattern matches.
324# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
325_BANNED_IOS_EGTEST_FUNCTIONS = (
326 (
327 r'/\bEXPECT_OCMOCK_VERIFY\b',
328 (
329 'EXPECT_OCMOCK_VERIFY should not be used in EarlGrey tests because ',
330 'it is meant for GTests. Use [mock verify] instead.'
331 ),
332 True,
333 ),
334)
335
Daniel Bratell609102be2019-03-27 20:53:21336# Format: Sequence of tuples containing:
337# * String pattern or, if starting with a slash, a regular expression.
338# * Sequence of strings to show when the pattern matches.
339# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
340# * Sequence of paths to *not* check (regexps).
[email protected]127f18ec2012-06-16 05:05:59341_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20342 (
Peter Kasting94a56c42019-10-25 21:54:04343 r'/\busing namespace ',
344 (
345 'Using directives ("using namespace x") are banned by the Google Style',
346 'Guide ( http://google.github.io/styleguide/cppguide.html#Namespaces ).',
347 'Explicitly qualify symbols or use using declarations ("using x::foo").',
348 ),
349 True,
350 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
351 ),
Antonio Gomes07300d02019-03-13 20:59:57352 # Make sure that gtest's FRIEND_TEST() macro is not used; the
353 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
354 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
thomasandersone7caaa9b2017-03-29 19:22:53355 (
[email protected]23e6cbc2012-06-16 18:51:20356 'FRIEND_TEST(',
357 (
[email protected]e3c945502012-06-26 20:01:49358 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20359 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
360 ),
361 False,
[email protected]7345da02012-11-27 14:31:49362 (),
[email protected]23e6cbc2012-06-16 18:51:20363 ),
364 (
tomhudsone2c14d552016-05-26 17:07:46365 'setMatrixClip',
366 (
367 'Overriding setMatrixClip() is prohibited; ',
368 'the base function is deprecated. ',
369 ),
370 True,
371 (),
372 ),
373 (
[email protected]52657f62013-05-20 05:30:31374 'SkRefPtr',
375 (
376 'The use of SkRefPtr is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22377 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31378 ),
379 True,
380 (),
381 ),
382 (
383 'SkAutoRef',
384 (
385 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22386 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31387 ),
388 True,
389 (),
390 ),
391 (
392 'SkAutoTUnref',
393 (
394 'The use of SkAutoTUnref is dangerous because it implicitly ',
tomhudson7e6e0512016-04-19 19:27:22395 'converts to a raw pointer. Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31396 ),
397 True,
398 (),
399 ),
400 (
401 'SkAutoUnref',
402 (
403 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
404 'because it implicitly converts to a raw pointer. ',
tomhudson7e6e0512016-04-19 19:27:22405 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31406 ),
407 True,
408 (),
409 ),
[email protected]d89eec82013-12-03 14:10:59410 (
411 r'/HANDLE_EINTR\(.*close',
412 (
413 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
414 'descriptor will be closed, and it is incorrect to retry the close.',
415 'Either call close directly and ignore its return value, or wrap close',
416 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
417 ),
418 True,
419 (),
420 ),
421 (
422 r'/IGNORE_EINTR\((?!.*close)',
423 (
424 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
425 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
426 ),
427 True,
428 (
429 # Files that #define IGNORE_EINTR.
Egor Paskoce145c42018-09-28 19:31:04430 r'^base[\\/]posix[\\/]eintr_wrapper\.h$',
431 r'^ppapi[\\/]tests[\\/]test_broker\.cc$',
[email protected]d89eec82013-12-03 14:10:59432 ),
433 ),
[email protected]ec5b3f02014-04-04 18:43:43434 (
435 r'/v8::Extension\(',
436 (
437 'Do not introduce new v8::Extensions into the code base, use',
438 'gin::Wrappable instead. See http://crbug.com/334679',
439 ),
440 True,
[email protected]f55c90ee62014-04-12 00:50:03441 (
Egor Paskoce145c42018-09-28 19:31:04442 r'extensions[\\/]renderer[\\/]safe_builtins\.*',
[email protected]f55c90ee62014-04-12 00:50:03443 ),
[email protected]ec5b3f02014-04-04 18:43:43444 ),
skyostilf9469f72015-04-20 10:38:52445 (
jame2d1a952016-04-02 00:27:10446 '#pragma comment(lib,',
447 (
448 'Specify libraries to link with in build files and not in the source.',
449 ),
450 True,
Mirko Bonadeif4f0f0e2018-04-12 09:29:41451 (
tzik3f295992018-12-04 20:32:23452 r'^base[\\/]third_party[\\/]symbolize[\\/].*',
Egor Paskoce145c42018-09-28 19:31:04453 r'^third_party[\\/]abseil-cpp[\\/].*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41454 ),
jame2d1a952016-04-02 00:27:10455 ),
fdorayc4ac18d2017-05-01 21:39:59456 (
Gabriel Charette7cc6c432018-04-25 20:52:02457 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59458 (
459 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
460 ),
461 False,
462 (),
463 ),
464 (
Gabriel Charette7cc6c432018-04-25 20:52:02465 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59466 (
467 'Consider using THREAD_CHECKER macros instead of the class directly.',
468 ),
469 False,
470 (),
471 ),
dbeamb6f4fde2017-06-15 04:03:06472 (
Yuri Wiitala2f8de5c2017-07-21 00:11:06473 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
474 (
475 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
476 'deprecated (http://crbug.com/634507). Please avoid converting away',
477 'from the Time types in Chromium code, especially if any math is',
478 'being done on time values. For interfacing with platform/library',
479 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
480 'type converter methods instead. For faking TimeXXX values (for unit',
481 'testing only), use TimeXXX() + TimeDelta::FromMicroseconds(N). For',
482 'other use cases, please contact base/time/OWNERS.',
483 ),
484 False,
485 (),
486 ),
487 (
dbeamb6f4fde2017-06-15 04:03:06488 'CallJavascriptFunctionUnsafe',
489 (
490 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
491 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
492 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
493 ),
494 False,
495 (
Egor Paskoce145c42018-09-28 19:31:04496 r'^content[\\/]browser[\\/]webui[\\/]web_ui_impl\.(cc|h)$',
497 r'^content[\\/]public[\\/]browser[\\/]web_ui\.h$',
498 r'^content[\\/]public[\\/]test[\\/]test_web_ui\.(cc|h)$',
dbeamb6f4fde2017-06-15 04:03:06499 ),
500 ),
dskiba1474c2bfd62017-07-20 02:19:24501 (
502 'leveldb::DB::Open',
503 (
504 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
505 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
506 "Chrome's tracing, making their memory usage visible.",
507 ),
508 True,
509 (
510 r'^third_party/leveldatabase/.*\.(cc|h)$',
511 ),
Gabriel Charette0592c3a2017-07-26 12:02:04512 ),
513 (
Chris Mumfordc38afb62017-10-09 17:55:08514 'leveldb::NewMemEnv',
515 (
516 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58517 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
518 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08519 ),
520 True,
521 (
522 r'^third_party/leveldatabase/.*\.(cc|h)$',
523 ),
524 ),
525 (
Gabriel Charetted9839bc2017-07-29 14:17:47526 'RunLoop::QuitCurrent',
527 (
Robert Liao64b7ab22017-08-04 23:03:43528 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
529 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47530 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41531 False,
Gabriel Charetted9839bc2017-07-29 14:17:47532 (),
Gabriel Charettea44975052017-08-21 23:14:04533 ),
534 (
535 'base::ScopedMockTimeMessageLoopTaskRunner',
536 (
Gabriel Charette87cc1af2018-04-25 20:52:51537 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
Gabriel Charettedfa36042019-08-19 17:30:11538 'TaskEnvironment::TimeSource::MOCK_TIME. There are still a',
Gabriel Charette87cc1af2018-04-25 20:52:51539 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
540 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
541 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04542 ),
Gabriel Charette87cc1af2018-04-25 20:52:51543 False,
Gabriel Charettea44975052017-08-21 23:14:04544 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57545 ),
546 (
Dave Tapuska98199b612019-07-10 13:30:44547 'std::regex',
Eric Stevenson6b47b44c2017-08-30 20:41:57548 (
549 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02550 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57551 ),
552 True,
Danil Chapovalov7bc42a72020-12-09 18:20:16553 # Abseil's benchmarks never linked into chrome.
554 ['third_party/abseil-cpp/.*_benchmark.cc'],
Francois Doray43670e32017-09-27 12:40:38555 ),
556 (
Peter Kasting991618a62019-06-17 22:00:09557 r'/\bstd::stoi\b',
558 (
559 'std::stoi uses exceptions to communicate results. ',
560 'Use base::StringToInt() instead.',
561 ),
562 True,
563 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
564 ),
565 (
566 r'/\bstd::stol\b',
567 (
568 'std::stol uses exceptions to communicate results. ',
569 'Use base::StringToInt() instead.',
570 ),
571 True,
572 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
573 ),
574 (
575 r'/\bstd::stoul\b',
576 (
577 'std::stoul uses exceptions to communicate results. ',
578 'Use base::StringToUint() instead.',
579 ),
580 True,
581 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
582 ),
583 (
584 r'/\bstd::stoll\b',
585 (
586 'std::stoll uses exceptions to communicate results. ',
587 'Use base::StringToInt64() instead.',
588 ),
589 True,
590 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
591 ),
592 (
593 r'/\bstd::stoull\b',
594 (
595 'std::stoull uses exceptions to communicate results. ',
596 'Use base::StringToUint64() instead.',
597 ),
598 True,
599 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
600 ),
601 (
602 r'/\bstd::stof\b',
603 (
604 'std::stof uses exceptions to communicate results. ',
605 'For locale-independent values, e.g. reading numbers from disk',
606 'profiles, use base::StringToDouble().',
607 'For user-visible values, parse using ICU.',
608 ),
609 True,
610 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
611 ),
612 (
613 r'/\bstd::stod\b',
614 (
615 'std::stod uses exceptions to communicate results. ',
616 'For locale-independent values, e.g. reading numbers from disk',
617 'profiles, use base::StringToDouble().',
618 'For user-visible values, parse using ICU.',
619 ),
620 True,
621 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
622 ),
623 (
624 r'/\bstd::stold\b',
625 (
626 'std::stold uses exceptions to communicate results. ',
627 'For locale-independent values, e.g. reading numbers from disk',
628 'profiles, use base::StringToDouble().',
629 'For user-visible values, parse using ICU.',
630 ),
631 True,
632 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
633 ),
634 (
Daniel Bratell69334cc2019-03-26 11:07:45635 r'/\bstd::to_string\b',
636 (
637 'std::to_string is locale dependent and slower than alternatives.',
Peter Kasting991618a62019-06-17 22:00:09638 'For locale-independent strings, e.g. writing numbers to disk',
639 'profiles, use base::NumberToString().',
Daniel Bratell69334cc2019-03-26 11:07:45640 'For user-visible strings, use base::FormatNumber() and',
641 'the related functions in base/i18n/number_formatting.h.',
642 ),
Peter Kasting991618a62019-06-17 22:00:09643 False, # Only a warning since it is already used.
Daniel Bratell609102be2019-03-27 20:53:21644 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45645 ),
646 (
647 r'/\bstd::shared_ptr\b',
648 (
649 'std::shared_ptr should not be used. Use scoped_refptr instead.',
650 ),
651 True,
Ulan Degenbaev947043882021-02-10 14:02:31652 [
653 # Needed for interop with third-party library.
654 '^third_party/blink/renderer/core/typed_arrays/array_buffer/' +
Alex Chau9eb03cdd52020-07-13 21:04:57655 'array_buffer_contents\.(cc|h)',
Wez5f56be52021-05-04 09:30:58656 '^gin/array_buffer\.(cc|h)',
657 '^chrome/services/sharing/nearby/',
Meilin Wang00efc7c2021-05-13 01:12:42658 # gRPC provides some C++ libraries that use std::shared_ptr<>.
659 '^chromeos/services/libassistant/grpc/',
Wez5f56be52021-05-04 09:30:58660 # Fuchsia provides C++ libraries that use std::shared_ptr<>.
661 '.*fuchsia.*test\.(cc|h)',
Alex Chau9eb03cdd52020-07-13 21:04:57662 _THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell609102be2019-03-27 20:53:21663 ),
664 (
Peter Kasting991618a62019-06-17 22:00:09665 r'/\bstd::weak_ptr\b',
666 (
667 'std::weak_ptr should not be used. Use base::WeakPtr instead.',
668 ),
669 True,
670 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
671 ),
672 (
Daniel Bratell609102be2019-03-27 20:53:21673 r'/\blong long\b',
674 (
675 'long long is banned. Use stdint.h if you need a 64 bit number.',
676 ),
677 False, # Only a warning since it is already used.
678 [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
679 ),
680 (
681 r'/\bstd::bind\b',
682 (
683 'std::bind is banned because of lifetime risks.',
684 'Use base::BindOnce or base::BindRepeating instead.',
685 ),
686 True,
687 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
688 ),
689 (
690 r'/\b#include <chrono>\b',
691 (
692 '<chrono> overlaps with Time APIs in base. Keep using',
693 'base classes.',
694 ),
695 True,
696 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
697 ),
698 (
699 r'/\b#include <exception>\b',
700 (
701 'Exceptions are banned and disabled in Chromium.',
702 ),
703 True,
704 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
705 ),
706 (
707 r'/\bstd::function\b',
708 (
Colin Blundellea615d422021-05-12 09:35:41709 'std::function is banned. Instead use base::OnceCallback or ',
710 'base::RepeatingCallback, which directly support Chromium\'s weak ',
711 'pointers, ref counting and more.',
Daniel Bratell609102be2019-03-27 20:53:21712 ),
Peter Kasting991618a62019-06-17 22:00:09713 False, # Only a warning since it is already used.
Daniel Bratell609102be2019-03-27 20:53:21714 [_THIRD_PARTY_EXCEPT_BLINK], # Do not warn in third_party folders.
715 ),
716 (
717 r'/\b#include <random>\b',
718 (
719 'Do not use any random number engines from <random>. Instead',
720 'use base::RandomBitGenerator.',
721 ),
722 True,
723 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
724 ),
725 (
Tom Andersona95e12042020-09-09 23:08:00726 r'/\b#include <X11/',
727 (
728 'Do not use Xlib. Use xproto (from //ui/gfx/x:xproto) instead.',
729 ),
730 True,
731 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
732 ),
733 (
Daniel Bratell609102be2019-03-27 20:53:21734 r'/\bstd::ratio\b',
735 (
736 'std::ratio is banned by the Google Style Guide.',
737 ),
738 True,
739 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
Daniel Bratell69334cc2019-03-26 11:07:45740 ),
741 (
Francois Doray43670e32017-09-27 12:40:38742 (r'/base::ThreadRestrictions::(ScopedAllowIO|AssertIOAllowed|'
743 r'DisallowWaiting|AssertWaitAllowed|SetWaitAllowed|ScopedAllowWait)'),
744 (
745 'Use the new API in base/threading/thread_restrictions.h.',
746 ),
Gabriel Charette04b138f2018-08-06 00:03:22747 False,
Francois Doray43670e32017-09-27 12:40:38748 (),
749 ),
Luis Hector Chavez9bbaed532017-11-30 18:25:38750 (
Michael Giuffrida7f93d6922019-04-19 14:39:58751 r'/\bRunMessageLoop\b',
Gabriel Charette147335ea2018-03-22 15:59:19752 (
753 'RunMessageLoop is deprecated, use RunLoop instead.',
754 ),
755 False,
756 (),
757 ),
758 (
Dave Tapuska98199b612019-07-10 13:30:44759 'RunThisRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19760 (
761 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
762 ),
763 False,
764 (),
765 ),
766 (
Dave Tapuska98199b612019-07-10 13:30:44767 'RunAllPendingInMessageLoop()',
Gabriel Charette147335ea2018-03-22 15:59:19768 (
769 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
770 "if you're convinced you need this.",
771 ),
772 False,
773 (),
774 ),
775 (
Dave Tapuska98199b612019-07-10 13:30:44776 'RunAllPendingInMessageLoop(BrowserThread',
Gabriel Charette147335ea2018-03-22 15:59:19777 (
778 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
Gabriel Charette798fde72019-08-20 22:24:04779 'BrowserThread::UI, BrowserTaskEnvironment::RunIOThreadUntilIdle',
Gabriel Charette147335ea2018-03-22 15:59:19780 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
781 'async events instead of flushing threads.',
782 ),
783 False,
784 (),
785 ),
786 (
787 r'MessageLoopRunner',
788 (
789 'MessageLoopRunner is deprecated, use RunLoop instead.',
790 ),
791 False,
792 (),
793 ),
794 (
Dave Tapuska98199b612019-07-10 13:30:44795 'GetDeferredQuitTaskForRunLoop',
Gabriel Charette147335ea2018-03-22 15:59:19796 (
797 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
798 "gab@ if you found a use case where this is the only solution.",
799 ),
800 False,
801 (),
802 ),
803 (
Victor Costane48a2e82019-03-15 22:02:34804 'sqlite3_initialize(',
Victor Costan3653df62018-02-08 21:38:16805 (
Victor Costane48a2e82019-03-15 22:02:34806 'Instead of calling sqlite3_initialize(), depend on //sql, ',
Victor Costan3653df62018-02-08 21:38:16807 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
808 ),
809 True,
810 (
811 r'^sql/initialization\.(cc|h)$',
812 r'^third_party/sqlite/.*\.(c|cc|h)$',
813 ),
814 ),
Matt Menke7f520a82018-03-28 21:38:37815 (
Dave Tapuska98199b612019-07-10 13:30:44816 'std::random_shuffle',
tzik5de2157f2018-05-08 03:42:47817 (
818 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
819 'base::RandomShuffle instead.'
820 ),
821 True,
822 (),
823 ),
Javier Ernesto Flores Robles749e6c22018-10-08 09:36:24824 (
825 'ios/web/public/test/http_server',
826 (
827 'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
828 ),
829 False,
830 (),
831 ),
Robert Liao764c9492019-01-24 18:46:28832 (
833 'GetAddressOf',
834 (
835 'Improper use of Microsoft::WRL::ComPtr<T>::GetAddressOf() has been ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53836 'implicated in a few leaks. ReleaseAndGetAddressOf() is safe but ',
Joshua Berenhaus8b972ec2020-09-11 20:00:11837 'operator& is generally recommended. So always use operator& instead. ',
Xiaohan Wangfb31b4cd2020-07-08 01:18:53838 'See http://crbug.com/914910 for more conversion guidance.'
Robert Liao764c9492019-01-24 18:46:28839 ),
840 True,
841 (),
842 ),
Antonio Gomes07300d02019-03-13 20:59:57843 (
Ben Lewisa9514602019-04-29 17:53:05844 'SHFileOperation',
845 (
846 'SHFileOperation was deprecated in Windows Vista, and there are less ',
847 'complex functions to achieve the same goals. Use IFileOperation for ',
848 'any esoteric actions instead.'
849 ),
850 True,
851 (),
852 ),
Cliff Smolinskyb11abed2019-04-29 19:43:18853 (
Cliff Smolinsky81951642019-04-30 21:39:51854 'StringFromGUID2',
855 (
856 'StringFromGUID2 introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:24857 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:51858 ),
859 True,
860 (
861 r'/base/win/win_util_unittest.cc'
862 ),
863 ),
864 (
865 'StringFromCLSID',
866 (
867 'StringFromCLSID introduces an unnecessary dependency on ole32.dll.',
Jan Wilken Dörrieec815922020-07-22 07:46:24868 'Use base::win::WStringFromGUID instead.'
Cliff Smolinsky81951642019-04-30 21:39:51869 ),
870 True,
871 (
872 r'/base/win/win_util_unittest.cc'
873 ),
874 ),
875 (
Avi Drissman7382afa02019-04-29 23:27:13876 'kCFAllocatorNull',
877 (
878 'The use of kCFAllocatorNull with the NoCopy creation of ',
879 'CoreFoundation types is prohibited.',
880 ),
881 True,
882 (),
883 ),
Oksana Zhuravlovafd247772019-05-16 16:57:29884 (
885 'mojo::ConvertTo',
886 (
887 'mojo::ConvertTo and TypeConverter are deprecated. Please consider',
888 'StructTraits / UnionTraits / EnumTraits / ArrayTraits / MapTraits /',
889 'StringTraits if you would like to convert between custom types and',
890 'the wire format of mojom types.'
891 ),
Oksana Zhuravlova1d3b59de2019-05-17 00:08:22892 False,
Oksana Zhuravlovafd247772019-05-16 16:57:29893 (
Wezf89dec092019-09-11 19:38:33894 r'^fuchsia/engine/browser/url_request_rewrite_rules_manager\.cc$',
895 r'^fuchsia/engine/url_request_rewrite_type_converters\.cc$',
Oksana Zhuravlovafd247772019-05-16 16:57:29896 r'^third_party/blink/.*\.(cc|h)$',
897 r'^content/renderer/.*\.(cc|h)$',
898 ),
899 ),
Robert Liao1d78df52019-11-11 20:02:01900 (
Oksana Zhuravlovac8222d22019-12-19 19:21:16901 'GetInterfaceProvider',
902 (
903 'InterfaceProvider is deprecated.',
904 'Please use ExecutionContext::GetBrowserInterfaceBroker and overrides',
905 'or Platform::GetBrowserInterfaceBroker.'
906 ),
907 False,
908 (),
909 ),
910 (
Robert Liao1d78df52019-11-11 20:02:01911 'CComPtr',
912 (
913 'New code should use Microsoft::WRL::ComPtr from wrl/client.h as a ',
914 'replacement for CComPtr from ATL. See http://crbug.com/5027 for more ',
915 'details.'
916 ),
917 False,
918 (),
919 ),
Xiaohan Wang72bd2ba2020-02-18 21:38:20920 (
921 r'/\b(IFACE|STD)METHOD_?\(',
922 (
923 'IFACEMETHOD() and STDMETHOD() make code harder to format and read.',
924 'Instead, always use IFACEMETHODIMP in the declaration.'
925 ),
926 False,
927 [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders.
928 ),
Allen Bauer53b43fb12020-03-12 17:21:47929 (
930 'set_owned_by_client',
931 (
932 'set_owned_by_client is deprecated.',
933 'views::View already owns the child views by default. This introduces ',
934 'a competing ownership model which makes the code difficult to reason ',
935 'about. See http://crbug.com/1044687 for more details.'
936 ),
937 False,
938 (),
939 ),
Eric Secklerbe6f48d2020-05-06 18:09:12940 (
Peter Boström7ff41522021-07-29 03:43:27941 'RemoveAllChildViewsWithoutDeleting',
942 (
943 'RemoveAllChildViewsWithoutDeleting is deprecated.',
944 'This method is deemed dangerous as, unless raw pointers are re-added,',
945 'calls to this method introduce memory leaks.'
946 ),
947 False,
948 (),
949 ),
950 (
Eric Secklerbe6f48d2020-05-06 18:09:12951 r'/\bTRACE_EVENT_ASYNC_',
952 (
953 'Please use TRACE_EVENT_NESTABLE_ASYNC_.. macros instead',
954 'of TRACE_EVENT_ASYNC_.. (crbug.com/1038710).',
955 ),
956 False,
957 (
958 r'^base/trace_event/.*',
959 r'^base/tracing/.*',
960 ),
961 ),
Sigurdur Asgeirsson9c1f87c2020-11-10 01:03:26962 (
Robert Liao22f66a52021-04-10 00:57:52963 'RoInitialize',
964 (
Robert Liao48018922021-04-16 23:03:02965 'Improper use of [base::win]::RoInitialize() has been implicated in a ',
Robert Liao22f66a52021-04-10 00:57:52966 'few COM initialization leaks. Use base::win::ScopedWinrtInitializer ',
967 'instead. See http://crbug.com/1197722 for more information.'
968 ),
969 True,
Robert Liao48018922021-04-16 23:03:02970 (
971 r'^base[\\/]win[\\/]scoped_winrt_initializer\.cc$'
972 ),
Robert Liao22f66a52021-04-10 00:57:52973 ),
Lei Zhang1ddeadb2021-05-20 22:14:34974 (
975 r'/DISALLOW_(COPY|ASSIGN|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS)\(',
976 (
977 'DISALLOW_xxx macros are deprecated. See base/macros.h for details.',
978 ),
979 False,
980 (),
981 ),
[email protected]127f18ec2012-06-16 05:05:59982)
983
Mario Sanchez Prada2472cab2019-09-18 10:58:31984# Format: Sequence of tuples containing:
985# * String pattern or, if starting with a slash, a regular expression.
986# * Sequence of strings to show when the pattern matches.
987_DEPRECATED_MOJO_TYPES = (
988 (
Mario Sanchez Prada2472cab2019-09-18 10:58:31989 r'/\bmojo::AssociatedInterfacePtrInfo\b',
990 (
991 'mojo::AssociatedInterfacePtrInfo<Interface> is deprecated.',
992 'Use mojo::PendingAssociatedRemote<Interface> instead.',
993 ),
994 ),
995 (
996 r'/\bmojo::AssociatedInterfaceRequest\b',
997 (
998 'mojo::AssociatedInterfaceRequest<Interface> is deprecated.',
999 'Use mojo::PendingAssociatedReceiver<Interface> instead.',
1000 ),
1001 ),
1002 (
Mario Sanchez Prada2472cab2019-09-18 10:58:311003 r'/\bmojo::InterfacePtr\b',
1004 (
1005 'mojo::InterfacePtr<Interface> is deprecated.',
1006 'Use mojo::Remote<Interface> instead.',
1007 ),
1008 ),
1009 (
1010 r'/\bmojo::InterfacePtrInfo\b',
1011 (
1012 'mojo::InterfacePtrInfo<Interface> is deprecated.',
1013 'Use mojo::PendingRemote<Interface> instead.',
1014 ),
1015 ),
1016 (
1017 r'/\bmojo::InterfaceRequest\b',
1018 (
1019 'mojo::InterfaceRequest<Interface> is deprecated.',
1020 'Use mojo::PendingReceiver<Interface> instead.',
1021 ),
1022 ),
1023 (
1024 r'/\bmojo::MakeRequest\b',
1025 (
1026 'mojo::MakeRequest is deprecated.',
1027 'Use mojo::Remote::BindNewPipeAndPassReceiver() instead.',
1028 ),
1029 ),
Mario Sanchez Prada2472cab2019-09-18 10:58:311030)
wnwenbdc444e2016-05-25 13:44:151031
mlamouria82272622014-09-16 18:45:041032_IPC_ENUM_TRAITS_DEPRECATED = (
1033 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501034 'See http://www.chromium.org/Home/chromium-security/education/'
1035 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:041036
Stephen Martinis97a394142018-06-07 23:06:051037_LONG_PATH_ERROR = (
1038 'Some files included in this CL have file names that are too long (> 200'
1039 ' characters). If committed, these files will cause issues on Windows. See'
1040 ' https://crbug.com/612667 for more details.'
1041)
1042
Shenghua Zhangbfaa38b82017-11-16 21:58:021043_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
Scott Violet1dbd37e12021-05-14 16:35:041044 r".*[\\/]AppHooksImpl\.java",
Egor Paskoce145c42018-09-28 19:31:041045 r".*[\\/]BuildHooksAndroidImpl\.java",
1046 r".*[\\/]LicenseContentProvider\.java",
1047 r".*[\\/]PlatformServiceBridgeImpl.java",
Patrick Noland5475bc0d2018-10-01 20:04:281048 r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:021049]
[email protected]127f18ec2012-06-16 05:05:591050
Mohamed Heikald048240a2019-11-12 16:57:371051# List of image extensions that are used as resources in chromium.
1052_IMAGE_EXTENSIONS = ['.svg', '.png', '.webp']
1053
Sean Kau46e29bc2017-08-28 16:31:161054# These paths contain test data and other known invalid JSON files.
Erik Staab2dd72b12020-04-16 15:03:401055_KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS = [
Egor Paskoce145c42018-09-28 19:31:041056 r'test[\\/]data[\\/]',
Erik Staab2dd72b12020-04-16 15:03:401057 r'testing[\\/]buildbot[\\/]',
Egor Paskoce145c42018-09-28 19:31:041058 r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
1059 r'^third_party[\\/]protobuf[\\/]',
Egor Paskoce145c42018-09-28 19:31:041060 r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
Kent Tamura77578cc2018-11-25 22:33:431061 r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]',
Sean Kau46e29bc2017-08-28 16:31:161062]
1063
1064
[email protected]b00342e7f2013-03-26 16:21:541065_VALID_OS_MACROS = (
1066 # Please keep sorted.
rayb0088ee52017-04-26 22:35:081067 'OS_AIX',
[email protected]b00342e7f2013-03-26 16:21:541068 'OS_ANDROID',
Avi Drissman34594e902020-07-25 05:35:441069 'OS_APPLE',
Henrique Nakashimaafff0502018-01-24 17:14:121070 'OS_ASMJS',
[email protected]b00342e7f2013-03-26 16:21:541071 'OS_BSD',
1072 'OS_CAT', # For testing.
1073 'OS_CHROMEOS',
Eugene Kliuchnikovb99125c2018-11-26 17:33:041074 'OS_CYGWIN', # third_party code.
[email protected]b00342e7f2013-03-26 16:21:541075 'OS_FREEBSD',
scottmg2f97ee122017-05-12 17:50:371076 'OS_FUCHSIA',
[email protected]b00342e7f2013-03-26 16:21:541077 'OS_IOS',
1078 'OS_LINUX',
Avi Drissman34594e902020-07-25 05:35:441079 'OS_MAC',
[email protected]b00342e7f2013-03-26 16:21:541080 'OS_NACL',
hidehikof7295f22014-10-28 11:57:211081 'OS_NACL_NONSFI',
1082 'OS_NACL_SFI',
krytarowski969759f2016-07-31 23:55:121083 'OS_NETBSD',
[email protected]b00342e7f2013-03-26 16:21:541084 'OS_OPENBSD',
1085 'OS_POSIX',
[email protected]eda7afa12014-02-06 12:27:371086 'OS_QNX',
[email protected]b00342e7f2013-03-26 16:21:541087 'OS_SOLARIS',
[email protected]b00342e7f2013-03-26 16:21:541088 'OS_WIN',
1089)
1090
1091
Andrew Grieveb773bad2020-06-05 18:00:381092# These are not checked on the public chromium-presubmit trybot.
1093# Add files here that rely on .py files that exists only for target_os="android"
Samuel Huangc2f5d6bb2020-08-17 23:46:041094# checkouts.
agrievef32bcc72016-04-04 14:57:401095_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Grieveb773bad2020-06-05 18:00:381096 'chrome/android/features/create_stripped_java_factory.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381097]
1098
1099
1100_GENERIC_PYDEPS_FILES = [
Samuel Huangc2f5d6bb2020-08-17 23:46:041101 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361102 'base/android/jni_generator/jni_generator.pydeps',
1103 'base/android/jni_generator/jni_registration_generator.pydeps',
Andrew Grieve4c4cede2020-11-20 22:09:361104 'build/android/apk_operations.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041105 'build/android/devil_chromium.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361106 'build/android/gyp/aar.pydeps',
1107 'build/android/gyp/aidl.pydeps',
Tibor Goldschwendt0bef2d7a2019-10-24 21:19:271108 'build/android/gyp/allot_native_libraries.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361109 'build/android/gyp/apkbuilder.pydeps',
Andrew Grievea417ad302019-02-06 19:54:381110 'build/android/gyp/assert_static_initializers.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361111 'build/android/gyp/bytecode_processor.pydeps',
Robbie McElrath360e54d2020-11-12 20:38:021112 'build/android/gyp/bytecode_rewriter.pydeps',
Mohamed Heikal6305bcc2021-03-15 15:34:221113 'build/android/gyp/check_flag_expectations.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111114 'build/android/gyp/compile_java.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361115 'build/android/gyp/compile_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361116 'build/android/gyp/copy_ex.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361117 'build/android/gyp/create_apk_operations_script.pydeps',
Andrew Grieve8d083ea2019-12-13 06:49:111118 'build/android/gyp/create_app_bundle.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041119 'build/android/gyp/create_app_bundle_apks.pydeps',
1120 'build/android/gyp/create_bundle_wrapper_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361121 'build/android/gyp/create_java_binary_script.pydeps',
Mohamed Heikaladbe4e482020-07-09 19:25:121122 'build/android/gyp/create_r_java.pydeps',
Mohamed Heikal8cd763a52021-02-01 23:32:091123 'build/android/gyp/create_r_txt.pydeps',
Andrew Grieveb838d832019-02-11 16:55:221124 'build/android/gyp/create_size_info_files.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001125 'build/android/gyp/create_ui_locale_resources.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361126 'build/android/gyp/desugar.pydeps',
1127 'build/android/gyp/dex.pydeps',
Andrew Grieve723c1502020-04-23 16:27:421128 'build/android/gyp/dex_jdk_libs.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041129 'build/android/gyp/dexsplitter.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361130 'build/android/gyp/dist_aar.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361131 'build/android/gyp/filter_zip.pydeps',
1132 'build/android/gyp/gcc_preprocess.pydeps',
Christopher Grant99e0e20062018-11-21 21:22:361133 'build/android/gyp/generate_linker_version_script.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361134 'build/android/gyp/ijar.pydeps',
Yun Liueb4075ddf2019-05-13 19:47:581135 'build/android/gyp/jacoco_instr.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361136 'build/android/gyp/java_cpp_enum.pydeps',
Nate Fischerac07b2622020-10-01 20:20:141137 'build/android/gyp/java_cpp_features.pydeps',
Ian Vollickb99472e2019-03-07 21:35:261138 'build/android/gyp/java_cpp_strings.pydeps',
Andrew Grieve09457912021-04-27 15:22:471139 'build/android/gyp/java_google_api_keys.pydeps',
Andrew Grieve5853fbd2020-02-20 17:26:011140 'build/android/gyp/jetify_jar.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041141 'build/android/gyp/jinja_template.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361142 'build/android/gyp/lint.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361143 'build/android/gyp/merge_manifest.pydeps',
1144 'build/android/gyp/prepare_resources.pydeps',
Mohamed Heikalf85138b2020-10-06 15:43:221145 'build/android/gyp/process_native_prebuilt.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361146 'build/android/gyp/proguard.pydeps',
Peter Wen578730b2020-03-19 19:55:461147 'build/android/gyp/turbine.pydeps',
Mohamed Heikal246710c2021-06-14 15:34:301148 'build/android/gyp/unused_resources.pydeps',
Eric Stevensona82cf6082019-07-24 14:35:241149 'build/android/gyp/validate_static_library_dex_references.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361150 'build/android/gyp/write_build_config.pydeps',
Tibor Goldschwendtc4caae92019-07-12 00:33:461151 'build/android/gyp/write_native_libraries_java.pydeps',
Andrew Grieve9ff17792018-11-30 04:55:561152 'build/android/gyp/zip.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361153 'build/android/incremental_install/generate_android_manifest.pydeps',
1154 'build/android/incremental_install/write_installer_json.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041155 'build/android/resource_sizes.pydeps',
1156 'build/android/test_runner.pydeps',
1157 'build/android/test_wrapper/logdog_wrapper.pydeps',
Samuel Huange65eb3f12020-08-14 19:04:361158 'build/lacros/lacros_resource_sizes.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:361159 'build/protoc_java.pydeps',
Peter Kotwicz64667b02020-10-18 06:43:321160 'chrome/android/monochrome/scripts/monochrome_python_tests.pydeps',
Peter Wenefb56c72020-06-04 15:12:271161 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
1162 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Junbo Kedcd3a452021-03-19 17:55:041163 'chromecast/resource_sizes/chromecast_resource_sizes.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001164 'components/cronet/tools/generate_javadoc.pydeps',
1165 'components/cronet/tools/jar_src.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381166 'components/module_installer/android/module_desc_java.pydeps',
Andrew Grieve5a01ad32020-06-25 18:06:001167 'content/public/android/generate_child_service.pydeps',
Andrew Grieveb773bad2020-06-05 18:00:381168 'net/tools/testserver/testserver.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041169 'testing/scripts/run_android_wpt.pydeps',
Peter Kotwicz3c339f32020-10-19 19:59:181170 'testing/scripts/run_isolated_script_test.pydeps',
Samuel Huangc2f5d6bb2020-08-17 23:46:041171 'third_party/android_platform/development/scripts/stack.pydeps',
Hitoshi Yoshida0f228c42019-08-07 09:37:421172 'third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps',
1173 'third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps',
Yuki Shiinoe7827aa2019-09-13 12:26:131174 'third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps',
Canon Mukaif32f8f592021-04-23 18:56:501175 'third_party/blink/renderer/bindings/scripts/validate_web_idl.pydeps',
John Budorickbc3571aa2019-04-25 02:20:061176 'tools/binary_size/sizes.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:221177 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:401178]
1179
wnwenbdc444e2016-05-25 13:44:151180
agrievef32bcc72016-04-04 14:57:401181_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
1182
1183
Eric Boren6fd2b932018-01-25 15:05:081184# Bypass the AUTHORS check for these accounts.
1185_KNOWN_ROBOTS = set(
Sergiy Byelozyorov47158a52018-06-13 22:38:591186 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:451187 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:591188 ) | set('%[email protected]' % s
smutde797052019-12-04 02:03:521189 for s in ('bling-autoroll-builder', 'v8-ci-autoroll-builder',
Sven Zhengf7abd31d2021-08-09 19:06:231190 'wpt-autoroller', 'chrome-weblayer-builder',
1191 'lacros-version-skew-roller', 'skylab-test-cros-roller')
Eric Boren835d71f2018-09-07 21:09:041192 ) | set('%[email protected]' % s
Eric Boren66150e52020-01-08 11:20:271193 for s in ('chromium-autoroll', 'chromium-release-autoroll')
Eric Boren835d71f2018-09-07 21:09:041194 ) | set('%[email protected]' % s
Yulan Lineb0cfba2021-04-09 18:43:161195 for s in ('chromium-internal-autoroll',)
1196 ) | set('%[email protected]' % s
1197 for s in ('swarming-tasks',))
Eric Boren6fd2b932018-01-25 15:05:081198
Matt Stark6ef08872021-07-29 01:21:461199_INVALID_GRD_FILE_LINE = [
1200 (r'<file lang=.* path=.*', 'Path should come before lang in GRD files.')
1201]
Eric Boren6fd2b932018-01-25 15:05:081202
Daniel Bratell65b033262019-04-23 08:17:061203def _IsCPlusPlusFile(input_api, file_path):
1204 """Returns True if this file contains C++-like code (and not Python,
1205 Go, Java, MarkDown, ...)"""
1206
1207 ext = input_api.os_path.splitext(file_path)[1]
1208 # This list is compatible with CppChecker.IsCppFile but we should
1209 # consider adding ".c" to it. If we do that we can use this function
1210 # at more places in the code.
1211 return ext in (
1212 '.h',
1213 '.cc',
1214 '.cpp',
1215 '.m',
1216 '.mm',
1217 )
1218
1219def _IsCPlusPlusHeaderFile(input_api, file_path):
1220 return input_api.os_path.splitext(file_path)[1] == ".h"
1221
1222
1223def _IsJavaFile(input_api, file_path):
1224 return input_api.os_path.splitext(file_path)[1] == ".java"
1225
1226
1227def _IsProtoFile(input_api, file_path):
1228 return input_api.os_path.splitext(file_path)[1] == ".proto"
1229
Mohamed Heikal5e5b7922020-10-29 18:57:591230
1231def CheckNoUpstreamDepsOnClank(input_api, output_api):
1232 """Prevent additions of dependencies from the upstream repo on //clank."""
1233 # clank can depend on clank
1234 if input_api.change.RepositoryRoot().endswith('clank'):
1235 return []
1236 build_file_patterns = [
1237 r'(.+/)?BUILD\.gn',
1238 r'.+\.gni',
1239 ]
1240 excluded_files = [
1241 r'build[/\\]config[/\\]android[/\\]config\.gni'
1242 ]
1243 bad_pattern = input_api.re.compile(r'^[^#]*//clank')
1244
1245 error_message = 'Disallowed import on //clank in an upstream build file:'
1246
1247 def FilterFile(affected_file):
1248 return input_api.FilterSourceFile(
1249 affected_file,
1250 files_to_check=build_file_patterns,
1251 files_to_skip=excluded_files)
1252
1253 problems = []
1254 for f in input_api.AffectedSourceFiles(FilterFile):
1255 local_path = f.LocalPath()
1256 for line_number, line in f.ChangedContents():
1257 if (bad_pattern.search(line)):
1258 problems.append(
1259 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1260 if problems:
1261 return [output_api.PresubmitPromptOrNotify(error_message, problems)]
1262 else:
1263 return []
1264
1265
Saagar Sanghavifceeaae2020-08-12 16:40:361266def CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
[email protected]55459852011-08-10 15:17:191267 """Attempts to prevent use of functions intended only for testing in
1268 non-testing code. For now this is just a best-effort implementation
1269 that ignores header files and may have some false positives. A
1270 better implementation would probably need a proper C++ parser.
1271 """
1272 # We only scan .cc files and the like, as the declaration of
1273 # for-testing functions in header files are hard to distinguish from
1274 # calls to such functions without a proper C++ parser.
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491275 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:191276
jochenc0d4808c2015-07-27 09:25:421277 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
[email protected]55459852011-08-10 15:17:191278 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
[email protected]23501822014-05-14 02:06:091279 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
danakjf26536bf2020-09-10 00:46:131280 allowlist_pattern = input_api.re.compile(r'// IN-TEST$')
[email protected]55459852011-08-10 15:17:191281 exclusion_pattern = input_api.re.compile(
1282 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
1283 base_function_pattern, base_function_pattern))
danakjf26536bf2020-09-10 00:46:131284 # Avoid a false positive in this case, where the method name, the ::, and
1285 # the closing { are all on different lines due to line wrapping.
1286 # HelperClassForTesting::
1287 # HelperClassForTesting(
1288 # args)
1289 # : member(0) {}
1290 method_defn_pattern = input_api.re.compile(r'[A-Za-z0-9_]+::$')
[email protected]55459852011-08-10 15:17:191291
1292 def FilterFile(affected_file):
James Cook24a504192020-07-23 00:08:441293 files_to_skip = (_EXCLUDED_PATHS +
1294 _TEST_CODE_EXCLUDED_PATHS +
1295 input_api.DEFAULT_FILES_TO_SKIP)
[email protected]55459852011-08-10 15:17:191296 return input_api.FilterSourceFile(
1297 affected_file,
James Cook24a504192020-07-23 00:08:441298 files_to_check=file_inclusion_pattern,
1299 files_to_skip=files_to_skip)
[email protected]55459852011-08-10 15:17:191300
1301 problems = []
1302 for f in input_api.AffectedSourceFiles(FilterFile):
1303 local_path = f.LocalPath()
danakjf26536bf2020-09-10 00:46:131304 in_method_defn = False
[email protected]825d27182014-01-02 21:24:241305 for line_number, line in f.ChangedContents():
[email protected]2fdd1f362013-01-16 03:56:031306 if (inclusion_pattern.search(line) and
[email protected]de4f7d22013-05-23 14:27:461307 not comment_pattern.search(line) and
danakjf26536bf2020-09-10 00:46:131308 not exclusion_pattern.search(line) and
1309 not allowlist_pattern.search(line) and
1310 not in_method_defn):
[email protected]55459852011-08-10 15:17:191311 problems.append(
[email protected]2fdd1f362013-01-16 03:56:031312 '%s:%d\n %s' % (local_path, line_number, line.strip()))
danakjf26536bf2020-09-10 00:46:131313 in_method_defn = method_defn_pattern.search(line)
[email protected]55459852011-08-10 15:17:191314
1315 if problems:
[email protected]f7051d52013-04-02 18:31:421316 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
[email protected]2fdd1f362013-01-16 03:56:031317 else:
1318 return []
[email protected]55459852011-08-10 15:17:191319
1320
Saagar Sanghavifceeaae2020-08-12 16:40:361321def CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
Vaclav Brozek7dbc28c2018-03-27 08:35:231322 """This is a simplified version of
Saagar Sanghavi0bc3e692020-08-13 19:46:591323 CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
Vaclav Brozek7dbc28c2018-03-27 08:35:231324 """
1325 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
1326 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
1327 name_pattern = r'ForTest(s|ing)?'
1328 # Describes an occurrence of "ForTest*" inside a // comment.
1329 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
Peter Wen6367b882020-08-05 16:55:501330 # Describes @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
Sky Malice9e6d6032020-10-15 22:49:551331 annotation_re = input_api.re.compile(r'@VisibleForTesting\(')
Vaclav Brozek7dbc28c2018-03-27 08:35:231332 # Catch calls.
1333 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
1334 # Ignore definitions. (Comments are ignored separately.)
1335 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
1336
1337 problems = []
1338 sources = lambda x: input_api.FilterSourceFile(
1339 x,
James Cook24a504192020-07-23 00:08:441340 files_to_skip=(('(?i).*test', r'.*\/junit\/')
1341 + input_api.DEFAULT_FILES_TO_SKIP),
1342 files_to_check=[r'.*\.java$']
Vaclav Brozek7dbc28c2018-03-27 08:35:231343 )
1344 for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
1345 local_path = f.LocalPath()
1346 is_inside_javadoc = False
1347 for line_number, line in f.ChangedContents():
1348 if is_inside_javadoc and javadoc_end_re.search(line):
1349 is_inside_javadoc = False
1350 if not is_inside_javadoc and javadoc_start_re.search(line):
1351 is_inside_javadoc = True
1352 if is_inside_javadoc:
1353 continue
1354 if (inclusion_re.search(line) and
1355 not comment_re.search(line) and
Peter Wen6367b882020-08-05 16:55:501356 not annotation_re.search(line) and
Vaclav Brozek7dbc28c2018-03-27 08:35:231357 not exclusion_re.search(line)):
1358 problems.append(
1359 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1360
1361 if problems:
1362 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
1363 else:
1364 return []
1365
1366
Saagar Sanghavifceeaae2020-08-12 16:40:361367def CheckNoIOStreamInHeaders(input_api, output_api):
[email protected]10689ca2011-09-02 02:31:541368 """Checks to make sure no .h files include <iostream>."""
1369 files = []
1370 pattern = input_api.re.compile(r'^#include\s*<iostream>',
1371 input_api.re.MULTILINE)
1372 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1373 if not f.LocalPath().endswith('.h'):
1374 continue
1375 contents = input_api.ReadFile(f)
1376 if pattern.search(contents):
1377 files.append(f)
1378
1379 if len(files):
yolandyandaabc6d2016-04-18 18:29:391380 return [output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:061381 'Do not #include <iostream> in header files, since it inserts static '
1382 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:541383 '#include <ostream>. See http://crbug.com/94794',
1384 files) ]
1385 return []
1386
Danil Chapovalov3518f362018-08-11 16:13:431387def _CheckNoStrCatRedefines(input_api, output_api):
1388 """Checks no windows headers with StrCat redefined are included directly."""
1389 files = []
1390 pattern_deny = input_api.re.compile(
1391 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
1392 input_api.re.MULTILINE)
1393 pattern_allow = input_api.re.compile(
1394 r'^#include\s"base/win/windows_defines.inc"',
1395 input_api.re.MULTILINE)
1396 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1397 contents = input_api.ReadFile(f)
1398 if pattern_deny.search(contents) and not pattern_allow.search(contents):
1399 files.append(f.LocalPath())
1400
1401 if len(files):
1402 return [output_api.PresubmitError(
1403 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
1404 'directly since they pollute code with StrCat macro. Instead, '
1405 'include matching header from base/win. See http://crbug.com/856536',
1406 files) ]
1407 return []
1408
[email protected]10689ca2011-09-02 02:31:541409
Saagar Sanghavifceeaae2020-08-12 16:40:361410def CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
danakj61c1aa22015-10-26 19:55:521411 """Checks to make sure no source files use UNIT_TEST."""
[email protected]72df4e782012-06-21 16:28:181412 problems = []
1413 for f in input_api.AffectedFiles():
1414 if (not f.LocalPath().endswith(('.cc', '.mm'))):
1415 continue
1416
1417 for line_num, line in f.ChangedContents():
[email protected]549f86a2013-11-19 13:00:041418 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
[email protected]72df4e782012-06-21 16:28:181419 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1420
1421 if not problems:
1422 return []
1423 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
1424 '\n'.join(problems))]
1425
Saagar Sanghavifceeaae2020-08-12 16:40:361426def CheckNoDISABLETypoInTests(input_api, output_api):
Dominic Battre033531052018-09-24 15:45:341427 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
1428
1429 This test warns if somebody tries to disable a test with the DISABLE_ prefix
1430 instead of DISABLED_. To filter false positives, reports are only generated
1431 if a corresponding MAYBE_ line exists.
1432 """
1433 problems = []
1434
1435 # The following two patterns are looked for in tandem - is a test labeled
1436 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
1437 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
1438 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
1439
1440 # This is for the case that a test is disabled on all platforms.
1441 full_disable_pattern = input_api.re.compile(
1442 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
1443 input_api.re.MULTILINE)
1444
Katie Df13948e2018-09-25 07:33:441445 for f in input_api.AffectedFiles(False):
Dominic Battre033531052018-09-24 15:45:341446 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1447 continue
1448
1449 # Search for MABYE_, DISABLE_ pairs.
1450 disable_lines = {} # Maps of test name to line number.
1451 maybe_lines = {}
1452 for line_num, line in f.ChangedContents():
1453 disable_match = disable_pattern.search(line)
1454 if disable_match:
1455 disable_lines[disable_match.group(1)] = line_num
1456 maybe_match = maybe_pattern.search(line)
1457 if maybe_match:
1458 maybe_lines[maybe_match.group(1)] = line_num
1459
1460 # Search for DISABLE_ occurrences within a TEST() macro.
1461 disable_tests = set(disable_lines.keys())
1462 maybe_tests = set(maybe_lines.keys())
1463 for test in disable_tests.intersection(maybe_tests):
1464 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
1465
1466 contents = input_api.ReadFile(f)
1467 full_disable_match = full_disable_pattern.search(contents)
1468 if full_disable_match:
1469 problems.append(' %s' % f.LocalPath())
1470
1471 if not problems:
1472 return []
1473 return [
1474 output_api.PresubmitPromptWarning(
1475 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
1476 '\n'.join(problems))
1477 ]
1478
Nina Satragnof7660532021-09-20 18:03:351479def CheckForgettingMAYBEInTests(input_api, output_api):
1480 """Checks to make sure tests disabled conditionally are not missing a
1481 corresponding MAYBE_ prefix.
1482 """
1483 # Expect at least a lowercase character in the test name. This helps rule out
1484 # false positives with macros wrapping the actual tests name.
1485 define_maybe_pattern = input_api.re.compile(
1486 r'^\#define MAYBE_(?P<test_name>\w*[a-z]\w*)')
1487 test_maybe_pattern = r'^\s*\w*TEST[^(]*\(\s*\w+,\s*MAYBE_{test_name}\)'
1488 suite_maybe_pattern = r'^\s*\w*TEST[^(]*\(\s*MAYBE_{test_name}[\),]'
1489 warnings = []
1490
1491 # Read the entire files. We can't just read the affected lines, forgetting to
1492 # add MAYBE_ on a change would not show up otherwise.
1493 for f in input_api.AffectedFiles(False):
1494 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
1495 continue
1496 contents = input_api.ReadFile(f)
1497 lines = contents.splitlines(True)
1498 current_position = 0
1499 warning_test_names = set()
1500 for line_num, line in enumerate(lines, start=1):
1501 current_position += len(line)
1502 maybe_match = define_maybe_pattern.search(line)
1503 if maybe_match:
1504 test_name = maybe_match.group('test_name')
1505 # Do not warn twice for the same test.
1506 if (test_name in warning_test_names):
1507 continue
1508 warning_test_names.add(test_name)
1509
1510 # Attempt to find the corresponding MAYBE_ test or suite, starting from
1511 # the current position.
1512 test_match = input_api.re.compile(
1513 test_maybe_pattern.format(test_name=test_name),
1514 input_api.re.MULTILINE).search(contents, current_position)
1515 suite_match = input_api.re.compile(
1516 suite_maybe_pattern.format(test_name=test_name),
1517 input_api.re.MULTILINE).search(contents, current_position)
1518 if not test_match and not suite_match:
1519 warnings.append(
1520 output_api.PresubmitPromptWarning(
1521 '%s:%d found MAYBE_ defined without corresponding test %s' %
1522 (f.LocalPath(), line_num, test_name)))
1523 return warnings
[email protected]72df4e782012-06-21 16:28:181524
Saagar Sanghavifceeaae2020-08-12 16:40:361525def CheckDCHECK_IS_ONHasBraces(input_api, output_api):
kjellanderaee306632017-02-22 19:26:571526 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
danakj61c1aa22015-10-26 19:55:521527 errors = []
Hans Wennborg944479f2020-06-25 21:39:251528 pattern = input_api.re.compile(r'DCHECK_IS_ON\b(?!\(\))',
danakj61c1aa22015-10-26 19:55:521529 input_api.re.MULTILINE)
1530 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1531 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
1532 continue
1533 for lnum, line in f.ChangedContents():
1534 if input_api.re.search(pattern, line):
dchenge07de812016-06-20 19:27:171535 errors.append(output_api.PresubmitError(
1536 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' +
kjellanderaee306632017-02-22 19:26:571537 'DCHECK_IS_ON()", not forgetting the parentheses.')
dchenge07de812016-06-20 19:27:171538 % (f.LocalPath(), lnum)))
danakj61c1aa22015-10-26 19:55:521539 return errors
1540
1541
Weilun Shia487fad2020-10-28 00:10:341542# TODO(crbug/1138055): Reimplement CheckUmaHistogramChangesOnUpload check in a
1543# more reliable way. See
1544# https://chromium-review.googlesource.com/c/chromium/src/+/2500269
mcasasb7440c282015-02-04 14:52:191545
wnwenbdc444e2016-05-25 13:44:151546
Saagar Sanghavifceeaae2020-08-12 16:40:361547def CheckFlakyTestUsage(input_api, output_api):
yolandyandaabc6d2016-04-18 18:29:391548 """Check that FlakyTest annotation is our own instead of the android one"""
1549 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1550 files = []
1551 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1552 if f.LocalPath().endswith('Test.java'):
1553 if pattern.search(input_api.ReadFile(f)):
1554 files.append(f)
1555 if len(files):
1556 return [output_api.PresubmitError(
1557 'Use org.chromium.base.test.util.FlakyTest instead of '
1558 'android.test.FlakyTest',
1559 files)]
1560 return []
mcasasb7440c282015-02-04 14:52:191561
wnwenbdc444e2016-05-25 13:44:151562
Saagar Sanghavifceeaae2020-08-12 16:40:361563def CheckNoDEPSGIT(input_api, output_api):
[email protected]2a8ac9c2011-10-19 17:20:441564 """Make sure .DEPS.git is never modified manually."""
1565 if any(f.LocalPath().endswith('.DEPS.git') for f in
1566 input_api.AffectedFiles()):
1567 return [output_api.PresubmitError(
1568 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1569 'automated system based on what\'s in DEPS and your changes will be\n'
1570 'overwritten.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501571 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1572 'get-the-code#Rolling_DEPS\n'
[email protected]2a8ac9c2011-10-19 17:20:441573 'for more information')]
1574 return []
1575
1576
Saagar Sanghavifceeaae2020-08-12 16:40:361577def CheckValidHostsInDEPSOnUpload(input_api, output_api):
tandriief664692014-09-23 14:51:471578 """Checks that DEPS file deps are from allowed_hosts."""
1579 # Run only if DEPS file has been modified to annoy fewer bystanders.
1580 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1581 return []
1582 # Outsource work to gclient verify
1583 try:
John Budorickf20c0042019-04-25 23:23:401584 gclient_path = input_api.os_path.join(
1585 input_api.PresubmitLocalPath(),
1586 'third_party', 'depot_tools', 'gclient.py')
1587 input_api.subprocess.check_output(
1588 [input_api.python_executable, gclient_path, 'verify'],
1589 stderr=input_api.subprocess.STDOUT)
tandriief664692014-09-23 14:51:471590 return []
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201591 except input_api.subprocess.CalledProcessError as error:
tandriief664692014-09-23 14:51:471592 return [output_api.PresubmitError(
1593 'DEPS file must have only git dependencies.',
1594 long_text=error.output)]
1595
1596
Mario Sanchez Prada2472cab2019-09-18 10:58:311597def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
1598 type_name, message):
Saagar Sanghavi0bc3e692020-08-13 19:46:591599 """Helper method for CheckNoBannedFunctions and CheckNoDeprecatedMojoTypes.
Mario Sanchez Prada2472cab2019-09-18 10:58:311600
1601 Returns an string composed of the name of the file, the line number where the
1602 match has been found and the additional text passed as |message| in case the
1603 target type name matches the text inside the line passed as parameter.
1604 """
Peng Huang9c5949a02020-06-11 19:20:541605 result = []
1606
danakjd18e8892020-12-17 17:42:011607 if input_api.re.search(r"^ *//", line): # Ignore comments about banned types.
1608 return result
1609 if line.endswith(" nocheck"): # A // nocheck comment will bypass this error.
Peng Huang9c5949a02020-06-11 19:20:541610 return result
1611
Mario Sanchez Prada2472cab2019-09-18 10:58:311612 matched = False
1613 if type_name[0:1] == '/':
1614 regex = type_name[1:]
1615 if input_api.re.search(regex, line):
1616 matched = True
1617 elif type_name in line:
1618 matched = True
1619
Mario Sanchez Prada2472cab2019-09-18 10:58:311620 if matched:
1621 result.append(' %s:%d:' % (affected_file.LocalPath(), line_number))
1622 for message_line in message:
1623 result.append(' %s' % message_line)
1624
1625 return result
1626
1627
Saagar Sanghavifceeaae2020-08-12 16:40:361628def CheckNoBannedFunctions(input_api, output_api):
[email protected]127f18ec2012-06-16 05:05:591629 """Make sure that banned functions are not used."""
1630 warnings = []
1631 errors = []
1632
James Cook24a504192020-07-23 00:08:441633 def IsExcludedFile(affected_file, excluded_paths):
wnwenbdc444e2016-05-25 13:44:151634 local_path = affected_file.LocalPath()
James Cook24a504192020-07-23 00:08:441635 for item in excluded_paths:
wnwenbdc444e2016-05-25 13:44:151636 if input_api.re.match(item, local_path):
1637 return True
1638 return False
1639
Peter K. Lee6c03ccff2019-07-15 14:40:051640 def IsIosObjcFile(affected_file):
Sylvain Defresnea8b73d252018-02-28 15:45:541641 local_path = affected_file.LocalPath()
1642 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m', '.h'):
1643 return False
1644 basename = input_api.os_path.basename(local_path)
1645 if 'ios' in basename.split('_'):
1646 return True
1647 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1648 if sep and 'ios' in local_path.split(sep):
1649 return True
1650 return False
1651
wnwenbdc444e2016-05-25 13:44:151652 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
Mario Sanchez Prada2472cab2019-09-18 10:58:311653 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
1654 func_name, message)
1655 if problems:
wnwenbdc444e2016-05-25 13:44:151656 if error:
Mario Sanchez Prada2472cab2019-09-18 10:58:311657 errors.extend(problems)
1658 else:
1659 warnings.extend(problems)
wnwenbdc444e2016-05-25 13:44:151660
Eric Stevensona9a980972017-09-23 00:04:411661 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1662 for f in input_api.AffectedFiles(file_filter=file_filter):
1663 for line_num, line in f.ChangedContents():
1664 for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
1665 CheckForMatch(f, line_num, line, func_name, message, error)
1666
[email protected]127f18ec2012-06-16 05:05:591667 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1668 for f in input_api.AffectedFiles(file_filter=file_filter):
1669 for line_num, line in f.ChangedContents():
1670 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
wnwenbdc444e2016-05-25 13:44:151671 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591672
Peter K. Lee6c03ccff2019-07-15 14:40:051673 for f in input_api.AffectedFiles(file_filter=IsIosObjcFile):
Sylvain Defresnea8b73d252018-02-28 15:45:541674 for line_num, line in f.ChangedContents():
1675 for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
1676 CheckForMatch(f, line_num, line, func_name, message, error)
1677
Peter K. Lee6c03ccff2019-07-15 14:40:051678 egtest_filter = lambda f: f.LocalPath().endswith(('_egtest.mm'))
1679 for f in input_api.AffectedFiles(file_filter=egtest_filter):
1680 for line_num, line in f.ChangedContents():
1681 for func_name, message, error in _BANNED_IOS_EGTEST_FUNCTIONS:
1682 CheckForMatch(f, line_num, line, func_name, message, error)
1683
[email protected]127f18ec2012-06-16 05:05:591684 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1685 for f in input_api.AffectedFiles(file_filter=file_filter):
1686 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:491687 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
James Cook24a504192020-07-23 00:08:441688 if IsExcludedFile(f, excluded_paths):
[email protected]7345da02012-11-27 14:31:491689 continue
wnwenbdc444e2016-05-25 13:44:151690 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591691
1692 result = []
1693 if (warnings):
1694 result.append(output_api.PresubmitPromptWarning(
1695 'Banned functions were used.\n' + '\n'.join(warnings)))
1696 if (errors):
1697 result.append(output_api.PresubmitError(
1698 'Banned functions were used.\n' + '\n'.join(errors)))
1699 return result
1700
1701
Michael Thiessen44457642020-02-06 00:24:151702def _CheckAndroidNoBannedImports(input_api, output_api):
1703 """Make sure that banned java imports are not used."""
1704 errors = []
1705
1706 def IsException(path, exceptions):
1707 for exception in exceptions:
1708 if (path.startswith(exception)):
1709 return True
1710 return False
1711
1712 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1713 for f in input_api.AffectedFiles(file_filter=file_filter):
1714 for line_num, line in f.ChangedContents():
1715 for import_name, message, exceptions in _BANNED_JAVA_IMPORTS:
1716 if IsException(f.LocalPath(), exceptions):
1717 continue;
1718 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
1719 'import ' + import_name, message)
1720 if problems:
1721 errors.extend(problems)
1722 result = []
1723 if (errors):
1724 result.append(output_api.PresubmitError(
1725 'Banned imports were used.\n' + '\n'.join(errors)))
1726 return result
1727
1728
Saagar Sanghavifceeaae2020-08-12 16:40:361729def CheckNoDeprecatedMojoTypes(input_api, output_api):
Mario Sanchez Prada2472cab2019-09-18 10:58:311730 """Make sure that old Mojo types are not used."""
1731 warnings = []
Mario Sanchez Pradacec9cef2019-12-15 11:54:571732 errors = []
Mario Sanchez Prada2472cab2019-09-18 10:58:311733
Mario Sanchez Pradaaab91382019-12-19 08:57:091734 # For any path that is not an "ok" or an "error" path, a warning will be
1735 # raised if deprecated mojo types are found.
1736 ok_paths = ['components/arc']
1737 error_paths = ['third_party/blink', 'content']
1738
Mario Sanchez Prada2472cab2019-09-18 10:58:311739 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1740 for f in input_api.AffectedFiles(file_filter=file_filter):
Mario Sanchez Pradacec9cef2019-12-15 11:54:571741 # Don't check //components/arc, not yet migrated (see crrev.com/c/1868870).
Mario Sanchez Pradaaab91382019-12-19 08:57:091742 if any(map(lambda path: f.LocalPath().startswith(path), ok_paths)):
Mario Sanchez Prada2472cab2019-09-18 10:58:311743 continue
1744
1745 for line_num, line in f.ChangedContents():
1746 for func_name, message in _DEPRECATED_MOJO_TYPES:
1747 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
1748 func_name, message)
Mario Sanchez Pradacec9cef2019-12-15 11:54:571749
Mario Sanchez Prada2472cab2019-09-18 10:58:311750 if problems:
Mario Sanchez Pradaaab91382019-12-19 08:57:091751 # Raise errors inside |error_paths| and warnings everywhere else.
1752 if any(map(lambda path: f.LocalPath().startswith(path), error_paths)):
Mario Sanchez Pradacec9cef2019-12-15 11:54:571753 errors.extend(problems)
1754 else:
Mario Sanchez Prada2472cab2019-09-18 10:58:311755 warnings.extend(problems)
1756
1757 result = []
1758 if (warnings):
1759 result.append(output_api.PresubmitPromptWarning(
1760 'Banned Mojo types were used.\n' + '\n'.join(warnings)))
Mario Sanchez Pradacec9cef2019-12-15 11:54:571761 if (errors):
1762 result.append(output_api.PresubmitError(
1763 'Banned Mojo types were used.\n' + '\n'.join(errors)))
Mario Sanchez Prada2472cab2019-09-18 10:58:311764 return result
1765
1766
Saagar Sanghavifceeaae2020-08-12 16:40:361767def CheckNoPragmaOnce(input_api, output_api):
[email protected]6c063c62012-07-11 19:11:061768 """Make sure that banned functions are not used."""
1769 files = []
1770 pattern = input_api.re.compile(r'^#pragma\s+once',
1771 input_api.re.MULTILINE)
1772 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1773 if not f.LocalPath().endswith('.h'):
1774 continue
1775 contents = input_api.ReadFile(f)
1776 if pattern.search(contents):
1777 files.append(f)
1778
1779 if files:
1780 return [output_api.PresubmitError(
1781 'Do not use #pragma once in header files.\n'
1782 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1783 files)]
1784 return []
1785
[email protected]127f18ec2012-06-16 05:05:591786
Saagar Sanghavifceeaae2020-08-12 16:40:361787def CheckNoTrinaryTrueFalse(input_api, output_api):
[email protected]e7479052012-09-19 00:26:121788 """Checks to make sure we don't introduce use of foo ? true : false."""
1789 problems = []
1790 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1791 for f in input_api.AffectedFiles():
1792 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1793 continue
1794
1795 for line_num, line in f.ChangedContents():
1796 if pattern.match(line):
1797 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1798
1799 if not problems:
1800 return []
1801 return [output_api.PresubmitPromptWarning(
1802 'Please consider avoiding the "? true : false" pattern if possible.\n' +
1803 '\n'.join(problems))]
1804
1805
Saagar Sanghavifceeaae2020-08-12 16:40:361806def CheckUnwantedDependencies(input_api, output_api):
rhalavati08acd232017-04-03 07:23:281807 """Runs checkdeps on #include and import statements added in this
[email protected]55f9f382012-07-31 11:02:181808 change. Breaking - rules is an error, breaking ! rules is a
1809 warning.
1810 """
mohan.reddyf21db962014-10-16 12:26:471811 import sys
[email protected]55f9f382012-07-31 11:02:181812 # We need to wait until we have an input_api object and use this
1813 # roundabout construct to import checkdeps because this file is
1814 # eval-ed and thus doesn't have __file__.
1815 original_sys_path = sys.path
1816 try:
1817 sys.path = sys.path + [input_api.os_path.join(
[email protected]5298cc982014-05-29 20:53:471818 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
[email protected]55f9f382012-07-31 11:02:181819 import checkdeps
[email protected]55f9f382012-07-31 11:02:181820 from rules import Rule
1821 finally:
1822 # Restore sys.path to what it was before.
1823 sys.path = original_sys_path
1824
1825 added_includes = []
rhalavati08acd232017-04-03 07:23:281826 added_imports = []
Jinsuk Kim5a092672017-10-24 22:42:241827 added_java_imports = []
[email protected]55f9f382012-07-31 11:02:181828 for f in input_api.AffectedFiles():
Daniel Bratell65b033262019-04-23 08:17:061829 if _IsCPlusPlusFile(input_api, f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501830 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081831 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
Daniel Bratell65b033262019-04-23 08:17:061832 elif _IsProtoFile(input_api, f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501833 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081834 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
Daniel Bratell65b033262019-04-23 08:17:061835 elif _IsJavaFile(input_api, f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501836 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081837 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
[email protected]55f9f382012-07-31 11:02:181838
[email protected]26385172013-05-09 23:11:351839 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181840
1841 error_descriptions = []
1842 warning_descriptions = []
rhalavati08acd232017-04-03 07:23:281843 error_subjects = set()
1844 warning_subjects = set()
Saagar Sanghavifceeaae2020-08-12 16:40:361845
[email protected]55f9f382012-07-31 11:02:181846 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1847 added_includes):
Andrew Grieve085f29f2017-11-02 09:14:081848 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181849 description_with_path = '%s\n %s' % (path, rule_description)
1850 if rule_type == Rule.DISALLOW:
1851 error_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281852 error_subjects.add("#includes")
[email protected]55f9f382012-07-31 11:02:181853 else:
1854 warning_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281855 warning_subjects.add("#includes")
1856
1857 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1858 added_imports):
Andrew Grieve085f29f2017-11-02 09:14:081859 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
rhalavati08acd232017-04-03 07:23:281860 description_with_path = '%s\n %s' % (path, rule_description)
1861 if rule_type == Rule.DISALLOW:
1862 error_descriptions.append(description_with_path)
1863 error_subjects.add("imports")
1864 else:
1865 warning_descriptions.append(description_with_path)
1866 warning_subjects.add("imports")
[email protected]55f9f382012-07-31 11:02:181867
Jinsuk Kim5a092672017-10-24 22:42:241868 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
Shenghua Zhangbfaa38b82017-11-16 21:58:021869 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
Andrew Grieve085f29f2017-11-02 09:14:081870 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
Jinsuk Kim5a092672017-10-24 22:42:241871 description_with_path = '%s\n %s' % (path, rule_description)
1872 if rule_type == Rule.DISALLOW:
1873 error_descriptions.append(description_with_path)
1874 error_subjects.add("imports")
1875 else:
1876 warning_descriptions.append(description_with_path)
1877 warning_subjects.add("imports")
1878
[email protected]55f9f382012-07-31 11:02:181879 results = []
1880 if error_descriptions:
1881 results.append(output_api.PresubmitError(
rhalavati08acd232017-04-03 07:23:281882 'You added one or more %s that violate checkdeps rules.'
1883 % " and ".join(error_subjects),
[email protected]55f9f382012-07-31 11:02:181884 error_descriptions))
1885 if warning_descriptions:
[email protected]f7051d52013-04-02 18:31:421886 results.append(output_api.PresubmitPromptOrNotify(
rhalavati08acd232017-04-03 07:23:281887 'You added one or more %s of files that are temporarily\n'
[email protected]55f9f382012-07-31 11:02:181888 'allowed but being removed. Can you avoid introducing the\n'
rhalavati08acd232017-04-03 07:23:281889 '%s? See relevant DEPS file(s) for details and contacts.' %
1890 (" and ".join(warning_subjects), "/".join(warning_subjects)),
[email protected]55f9f382012-07-31 11:02:181891 warning_descriptions))
1892 return results
1893
1894
Saagar Sanghavifceeaae2020-08-12 16:40:361895def CheckFilePermissions(input_api, output_api):
[email protected]fbcafe5a2012-08-08 15:31:221896 """Check that all files have their permissions properly set."""
[email protected]791507202014-02-03 23:19:151897 if input_api.platform == 'win32':
1898 return []
raphael.kubo.da.costac1d13e60b2016-04-01 11:49:291899 checkperms_tool = input_api.os_path.join(
1900 input_api.PresubmitLocalPath(),
1901 'tools', 'checkperms', 'checkperms.py')
1902 args = [input_api.python_executable, checkperms_tool,
mohan.reddyf21db962014-10-16 12:26:471903 '--root', input_api.change.RepositoryRoot()]
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391904 with input_api.CreateTemporaryFile() as file_list:
1905 for f in input_api.AffectedFiles():
1906 # checkperms.py file/directory arguments must be relative to the
1907 # repository.
Dirk Prankee3c9c62d2021-05-18 18:35:591908 file_list.write((f.LocalPath() + '\n').encode('utf8'))
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391909 file_list.close()
1910 args += ['--file-list', file_list.name]
1911 try:
1912 input_api.subprocess.check_output(args)
1913 return []
1914 except input_api.subprocess.CalledProcessError as error:
1915 return [output_api.PresubmitError(
1916 'checkperms.py failed:',
Ari Chivukula45f58dd52021-06-18 04:23:041917 long_text=error.output.decode('utf-8', 'ignore'))]
[email protected]fbcafe5a2012-08-08 15:31:221918
1919
Saagar Sanghavifceeaae2020-08-12 16:40:361920def CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
[email protected]c8278b32012-10-30 20:35:491921 """Makes sure we don't include ui/aura/window_property.h
1922 in header files.
1923 """
1924 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1925 errors = []
1926 for f in input_api.AffectedFiles():
1927 if not f.LocalPath().endswith('.h'):
1928 continue
1929 for line_num, line in f.ChangedContents():
1930 if pattern.match(line):
1931 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1932
1933 results = []
1934 if errors:
1935 results.append(output_api.PresubmitError(
1936 'Header files should not include ui/aura/window_property.h', errors))
1937 return results
1938
1939
Omer Katzcc77ea92021-04-26 10:23:281940def CheckNoInternalHeapIncludes(input_api, output_api):
1941 """Makes sure we don't include any headers from
1942 third_party/blink/renderer/platform/heap/impl or
1943 third_party/blink/renderer/platform/heap/v8_wrapper from files outside of
1944 third_party/blink/renderer/platform/heap
1945 """
1946 impl_pattern = input_api.re.compile(
1947 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/impl/.*"')
1948 v8_wrapper_pattern = input_api.re.compile(
1949 r'^\s*#include\s*"third_party/blink/renderer/platform/heap/v8_wrapper/.*"')
1950 file_filter = lambda f: not input_api.re.match(
1951 r"^third_party[\\/]blink[\\/]renderer[\\/]platform[\\/]heap[\\/].*",
1952 f.LocalPath())
1953 errors = []
1954
1955 for f in input_api.AffectedFiles(file_filter=file_filter):
1956 for line_num, line in f.ChangedContents():
1957 if impl_pattern.match(line) or v8_wrapper_pattern.match(line):
1958 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1959
1960 results = []
1961 if errors:
1962 results.append(output_api.PresubmitError(
1963 'Do not include files from third_party/blink/renderer/platform/heap/impl'
1964 ' or third_party/blink/renderer/platform/heap/v8_wrapper. Use the '
1965 'relevant counterparts from third_party/blink/renderer/platform/heap',
1966 errors))
1967 return results
1968
1969
[email protected]70ca77752012-11-20 03:45:031970def _CheckForVersionControlConflictsInFile(input_api, f):
1971 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1972 errors = []
1973 for line_num, line in f.ChangedContents():
Luke Zielinski9bc14ac72019-03-04 19:02:161974 if f.LocalPath().endswith(('.md', '.rst', '.txt')):
dbeam95c35a2f2015-06-02 01:40:231975 # First-level headers in markdown look a lot like version control
1976 # conflict markers. http://daringfireball.net/projects/markdown/basics
1977 continue
[email protected]70ca77752012-11-20 03:45:031978 if pattern.match(line):
1979 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1980 return errors
1981
1982
Saagar Sanghavifceeaae2020-08-12 16:40:361983def CheckForVersionControlConflicts(input_api, output_api):
[email protected]70ca77752012-11-20 03:45:031984 """Usually this is not intentional and will cause a compile failure."""
1985 errors = []
1986 for f in input_api.AffectedFiles():
1987 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
1988
1989 results = []
1990 if errors:
1991 results.append(output_api.PresubmitError(
1992 'Version control conflict markers found, please resolve.', errors))
1993 return results
1994
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201995
Saagar Sanghavifceeaae2020-08-12 16:40:361996def CheckGoogleSupportAnswerUrlOnUpload(input_api, output_api):
estadee17314a02017-01-12 16:22:161997 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1998 errors = []
1999 for f in input_api.AffectedFiles():
2000 for line_num, line in f.ChangedContents():
2001 if pattern.search(line):
2002 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
2003
2004 results = []
2005 if errors:
2006 results.append(output_api.PresubmitPromptWarning(
Vaclav Brozekd5de76a2018-03-17 07:57:502007 'Found Google support URL addressed by answer number. Please replace '
2008 'with a p= identifier instead. See crbug.com/679462\n', errors))
estadee17314a02017-01-12 16:22:162009 return results
2010
[email protected]70ca77752012-11-20 03:45:032011
Saagar Sanghavifceeaae2020-08-12 16:40:362012def CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
[email protected]06e6d0ff2012-12-11 01:36:442013 def FilterFile(affected_file):
2014 """Filter function for use with input_api.AffectedSourceFiles,
2015 below. This filters out everything except non-test files from
2016 top-level directories that generally speaking should not hard-code
2017 service URLs (e.g. src/android_webview/, src/content/ and others).
2018 """
2019 return input_api.FilterSourceFile(
2020 affected_file,
James Cook24a504192020-07-23 00:08:442021 files_to_check=[r'^(android_webview|base|content|net)[\\/].*'],
2022 files_to_skip=(_EXCLUDED_PATHS +
2023 _TEST_CODE_EXCLUDED_PATHS +
2024 input_api.DEFAULT_FILES_TO_SKIP))
[email protected]06e6d0ff2012-12-11 01:36:442025
reillyi38965732015-11-16 18:27:332026 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
2027 '\.(com|net)[^"]*"')
[email protected]de4f7d22013-05-23 14:27:462028 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
2029 pattern = input_api.re.compile(base_pattern)
[email protected]06e6d0ff2012-12-11 01:36:442030 problems = [] # items are (filename, line_number, line)
2031 for f in input_api.AffectedSourceFiles(FilterFile):
2032 for line_num, line in f.ChangedContents():
[email protected]de4f7d22013-05-23 14:27:462033 if not comment_pattern.search(line) and pattern.search(line):
[email protected]06e6d0ff2012-12-11 01:36:442034 problems.append((f.LocalPath(), line_num, line))
2035
2036 if problems:
[email protected]f7051d52013-04-02 18:31:422037 return [output_api.PresubmitPromptOrNotify(
[email protected]06e6d0ff2012-12-11 01:36:442038 'Most layers below src/chrome/ should not hardcode service URLs.\n'
[email protected]b0149772014-03-27 16:47:582039 'Are you sure this is correct?',
[email protected]06e6d0ff2012-12-11 01:36:442040 [' %s:%d: %s' % (
2041 problem[0], problem[1], problem[2]) for problem in problems])]
[email protected]2fdd1f362013-01-16 03:56:032042 else:
2043 return []
[email protected]06e6d0ff2012-12-11 01:36:442044
2045
Saagar Sanghavifceeaae2020-08-12 16:40:362046def CheckChromeOsSyncedPrefRegistration(input_api, output_api):
James Cook6b6597c2019-11-06 22:05:292047 """Warns if Chrome OS C++ files register syncable prefs as browser prefs."""
2048 def FileFilter(affected_file):
2049 """Includes directories known to be Chrome OS only."""
2050 return input_api.FilterSourceFile(
2051 affected_file,
James Cook24a504192020-07-23 00:08:442052 files_to_check=('^ash/',
2053 '^chromeos/', # Top-level src/chromeos.
2054 '/chromeos/', # Any path component.
2055 '^components/arc',
2056 '^components/exo'),
2057 files_to_skip=(input_api.DEFAULT_FILES_TO_SKIP))
James Cook6b6597c2019-11-06 22:05:292058
2059 prefs = []
2060 priority_prefs = []
2061 for f in input_api.AffectedFiles(file_filter=FileFilter):
2062 for line_num, line in f.ChangedContents():
2063 if input_api.re.search('PrefRegistrySyncable::SYNCABLE_PREF', line):
2064 prefs.append(' %s:%d:' % (f.LocalPath(), line_num))
2065 prefs.append(' %s' % line)
2066 if input_api.re.search(
2067 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF', line):
2068 priority_prefs.append(' %s:%d' % (f.LocalPath(), line_num))
2069 priority_prefs.append(' %s' % line)
2070
2071 results = []
2072 if (prefs):
2073 results.append(output_api.PresubmitPromptWarning(
2074 'Preferences were registered as SYNCABLE_PREF and will be controlled '
2075 'by browser sync settings. If these prefs should be controlled by OS '
2076 'sync settings use SYNCABLE_OS_PREF instead.\n' + '\n'.join(prefs)))
2077 if (priority_prefs):
2078 results.append(output_api.PresubmitPromptWarning(
2079 'Preferences were registered as SYNCABLE_PRIORITY_PREF and will be '
2080 'controlled by browser sync settings. If these prefs should be '
2081 'controlled by OS sync settings use SYNCABLE_OS_PRIORITY_PREF '
2082 'instead.\n' + '\n'.join(prefs)))
2083 return results
2084
2085
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492086# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362087def CheckNoAbbreviationInPngFileName(input_api, output_api):
[email protected]d2530012013-01-25 16:39:272088 """Makes sure there are no abbreviations in the name of PNG files.
binji0dcdf342014-12-12 18:32:312089 The native_client_sdk directory is excluded because it has auto-generated PNG
2090 files for documentation.
[email protected]d2530012013-01-25 16:39:272091 """
[email protected]d2530012013-01-25 16:39:272092 errors = []
James Cook24a504192020-07-23 00:08:442093 files_to_check = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
2094 files_to_skip = [r'^native_client_sdk[\\/]']
binji0dcdf342014-12-12 18:32:312095 file_filter = lambda f: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:442096 f, files_to_check=files_to_check, files_to_skip=files_to_skip)
binji0dcdf342014-12-12 18:32:312097 for f in input_api.AffectedFiles(include_deletes=False,
2098 file_filter=file_filter):
2099 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:272100
2101 results = []
2102 if errors:
2103 results.append(output_api.PresubmitError(
2104 'The name of PNG files should not have abbreviations. \n'
2105 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
2106 'Contact [email protected] if you have questions.', errors))
2107 return results
2108
2109
Daniel Cheng4dcdb6b2017-04-13 08:30:172110def _ExtractAddRulesFromParsedDeps(parsed_deps):
2111 """Extract the rules that add dependencies from a parsed DEPS file.
2112
2113 Args:
2114 parsed_deps: the locals dictionary from evaluating the DEPS file."""
2115 add_rules = set()
2116 add_rules.update([
2117 rule[1:] for rule in parsed_deps.get('include_rules', [])
2118 if rule.startswith('+') or rule.startswith('!')
2119 ])
Vaclav Brozekd5de76a2018-03-17 07:57:502120 for _, rules in parsed_deps.get('specific_include_rules',
Dirk Prankee3c9c62d2021-05-18 18:35:592121 {}).items():
Daniel Cheng4dcdb6b2017-04-13 08:30:172122 add_rules.update([
2123 rule[1:] for rule in rules
2124 if rule.startswith('+') or rule.startswith('!')
2125 ])
2126 return add_rules
2127
2128
2129def _ParseDeps(contents):
2130 """Simple helper for parsing DEPS files."""
2131 # Stubs for handling special syntax in the root DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:172132 class _VarImpl:
2133
2134 def __init__(self, local_scope):
2135 self._local_scope = local_scope
2136
2137 def Lookup(self, var_name):
2138 """Implements the Var syntax."""
2139 try:
2140 return self._local_scope['vars'][var_name]
2141 except KeyError:
2142 raise Exception('Var is not defined: %s' % var_name)
2143
2144 local_scope = {}
2145 global_scope = {
Daniel Cheng4dcdb6b2017-04-13 08:30:172146 'Var': _VarImpl(local_scope).Lookup,
Ben Pastene3e49749c2020-07-06 20:22:592147 'Str': str,
Daniel Cheng4dcdb6b2017-04-13 08:30:172148 }
Dirk Pranke1b9e06382021-05-14 01:16:222149
Dirk Prankee3c9c62d2021-05-18 18:35:592150 exec(contents, global_scope, local_scope)
Daniel Cheng4dcdb6b2017-04-13 08:30:172151 return local_scope
2152
2153
2154def _CalculateAddedDeps(os_path, old_contents, new_contents):
Saagar Sanghavi0bc3e692020-08-13 19:46:592155 """Helper method for CheckAddedDepsHaveTargetApprovals. Returns
[email protected]14a6131c2014-01-08 01:15:412156 a set of DEPS entries that we should look up.
2157
2158 For a directory (rather than a specific filename) we fake a path to
2159 a specific filename by adding /DEPS. This is chosen as a file that
2160 will seldom or never be subject to per-file include_rules.
2161 """
[email protected]2b438d62013-11-14 17:54:142162 # We ignore deps entries on auto-generated directories.
2163 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:082164
Daniel Cheng4dcdb6b2017-04-13 08:30:172165 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
2166 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
2167
2168 added_deps = new_deps.difference(old_deps)
2169
[email protected]2b438d62013-11-14 17:54:142170 results = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:172171 for added_dep in added_deps:
2172 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
2173 continue
2174 # Assume that a rule that ends in .h is a rule for a specific file.
2175 if added_dep.endswith('.h'):
2176 results.add(added_dep)
2177 else:
2178 results.add(os_path.join(added_dep, 'DEPS'))
[email protected]f32e2d1e2013-07-26 21:39:082179 return results
2180
2181
Saagar Sanghavifceeaae2020-08-12 16:40:362182def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
[email protected]e871964c2013-05-13 14:14:552183 """When a dependency prefixed with + is added to a DEPS file, we
2184 want to make sure that the change is reviewed by an OWNER of the
2185 target file or directory, to avoid layering violations from being
2186 introduced. This check verifies that this happens.
2187 """
Joey Mou57048132021-02-26 22:17:552188 # We rely on Gerrit's code-owners to check approvals.
2189 # input_api.gerrit is always set for Chromium, but other projects
2190 # might not use Gerrit.
2191 if not input_api.gerrit:
2192 return []
Edward Lesmes44feb2332021-03-19 01:27:522193 if (input_api.change.issue and
2194 input_api.gerrit.IsOwnersOverrideApproved(input_api.change.issue)):
Edward Lesmes6fba51082021-01-20 04:20:232195 # Skip OWNERS check when Owners-Override label is approved. This is intended
2196 # for global owners, trusted bots, and on-call sheriffs. Review is still
2197 # required for these changes.
Edward Lesmes44feb2332021-03-19 01:27:522198 return []
Edward Lesmes6fba51082021-01-20 04:20:232199
Daniel Cheng4dcdb6b2017-04-13 08:30:172200 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:242201
2202 file_filter = lambda f: not input_api.re.match(
Kent Tamura32dbbcb2018-11-30 12:28:492203 r"^third_party[\\/]blink[\\/].*", f.LocalPath())
jochen53efcdd2016-01-29 05:09:242204 for f in input_api.AffectedFiles(include_deletes=False,
2205 file_filter=file_filter):
[email protected]e871964c2013-05-13 14:14:552206 filename = input_api.os_path.basename(f.LocalPath())
2207 if filename == 'DEPS':
Daniel Cheng4dcdb6b2017-04-13 08:30:172208 virtual_depended_on_files.update(_CalculateAddedDeps(
2209 input_api.os_path,
2210 '\n'.join(f.OldContents()),
2211 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:552212
[email protected]e871964c2013-05-13 14:14:552213 if not virtual_depended_on_files:
2214 return []
2215
2216 if input_api.is_committing:
2217 if input_api.tbr:
2218 return [output_api.PresubmitNotifyResult(
2219 '--tbr was specified, skipping OWNERS check for DEPS additions')]
Paweł Hajdan, Jrbe6739ea2016-04-28 15:07:272220 if input_api.dry_run:
2221 return [output_api.PresubmitNotifyResult(
2222 'This is a dry run, skipping OWNERS check for DEPS additions')]
[email protected]e871964c2013-05-13 14:14:552223 if not input_api.change.issue:
2224 return [output_api.PresubmitError(
2225 "DEPS approval by OWNERS check failed: this change has "
Aaron Gable65a99d92017-10-09 19:17:402226 "no change number, so we can't check it for approvals.")]
[email protected]e871964c2013-05-13 14:14:552227 output = output_api.PresubmitError
2228 else:
2229 output = output_api.PresubmitNotifyResult
2230
tandriied3b7e12016-05-12 14:38:502231 owner_email, reviewers = (
2232 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
2233 input_api,
Edward Lesmesa3846442021-02-08 20:20:032234 None,
tandriied3b7e12016-05-12 14:38:502235 approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:552236
2237 owner_email = owner_email or input_api.change.author_email
2238
Edward Lesmesa3846442021-02-08 20:20:032239 approval_status = input_api.owners_client.GetFilesApprovalStatus(
2240 virtual_depended_on_files, reviewers.union([owner_email]), [])
2241 missing_files = [
2242 f for f in virtual_depended_on_files
2243 if approval_status[f] != input_api.owners_client.APPROVED]
[email protected]14a6131c2014-01-08 01:15:412244
2245 # We strip the /DEPS part that was added by
2246 # _FilesToCheckForIncomingDeps to fake a path to a file in a
2247 # directory.
2248 def StripDeps(path):
2249 start_deps = path.rfind('/DEPS')
2250 if start_deps != -1:
2251 return path[:start_deps]
2252 else:
2253 return path
2254 unapproved_dependencies = ["'+%s'," % StripDeps(path)
[email protected]e871964c2013-05-13 14:14:552255 for path in missing_files]
2256
2257 if unapproved_dependencies:
2258 output_list = [
Paweł Hajdan, Jrec17f882016-07-04 14:16:152259 output('You need LGTM from owners of depends-on paths in DEPS that were '
2260 'modified in this CL:\n %s' %
2261 '\n '.join(sorted(unapproved_dependencies)))]
Edward Lesmesa3846442021-02-08 20:20:032262 suggested_owners = input_api.owners_client.SuggestOwners(
2263 missing_files, exclude=[owner_email])
Paweł Hajdan, Jrec17f882016-07-04 14:16:152264 output_list.append(output(
2265 'Suggested missing target path OWNERS:\n %s' %
2266 '\n '.join(suggested_owners or [])))
[email protected]e871964c2013-05-13 14:14:552267 return output_list
2268
2269 return []
2270
2271
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492272# TODO: add unit tests.
Saagar Sanghavifceeaae2020-08-12 16:40:362273def CheckSpamLogging(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492274 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
James Cook24a504192020-07-23 00:08:442275 files_to_skip = (_EXCLUDED_PATHS +
2276 _TEST_CODE_EXCLUDED_PATHS +
2277 input_api.DEFAULT_FILES_TO_SKIP +
2278 (r"^base[\\/]logging\.h$",
2279 r"^base[\\/]logging\.cc$",
2280 r"^base[\\/]task[\\/]thread_pool[\\/]task_tracker\.cc$",
2281 r"^chrome[\\/]app[\\/]chrome_main_delegate\.cc$",
2282 r"^chrome[\\/]browser[\\/]chrome_browser_main\.cc$",
2283 r"^chrome[\\/]browser[\\/]ui[\\/]startup[\\/]"
2284 r"startup_browser_creator\.cc$",
2285 r"^chrome[\\/]browser[\\/]browser_switcher[\\/]bho[\\/].*",
2286 r"^chrome[\\/]browser[\\/]diagnostics[\\/]" +
2287 r"diagnostics_writer\.cc$",
2288 r"^chrome[\\/]chrome_cleaner[\\/].*",
2289 r"^chrome[\\/]chrome_elf[\\/]dll_hash[\\/]" +
2290 r"dll_hash_main\.cc$",
2291 r"^chrome[\\/]installer[\\/]setup[\\/].*",
2292 r"^chromecast[\\/]",
2293 r"^cloud_print[\\/]",
2294 r"^components[\\/]browser_watcher[\\/]"
2295 r"dump_stability_report_main_win.cc$",
2296 r"^components[\\/]media_control[\\/]renderer[\\/]"
2297 r"media_playback_options\.cc$",
ziyangch5f89c4a62021-02-26 19:57:352298 r"^components[\\/]viz[\\/]service[\\/]display[\\/]"
2299 r"overlay_strategy_underlay_cast\.cc$",
James Cook24a504192020-07-23 00:08:442300 r"^components[\\/]zucchini[\\/].*",
2301 # TODO(peter): Remove exception. https://crbug.com/534537
2302 r"^content[\\/]browser[\\/]notifications[\\/]"
2303 r"notification_event_dispatcher_impl\.cc$",
2304 r"^content[\\/]common[\\/]gpu[\\/]client[\\/]"
2305 r"gl_helper_benchmark\.cc$",
2306 r"^courgette[\\/]courgette_minimal_tool\.cc$",
2307 r"^courgette[\\/]courgette_tool\.cc$",
2308 r"^extensions[\\/]renderer[\\/]logging_native_handler\.cc$",
David Dorwinfa9aef42021-08-17 06:46:202309 r"^fuchsia[\\/]base[\\/]init_logging.cc$",
James Cook24a504192020-07-23 00:08:442310 r"^fuchsia[\\/]engine[\\/]browser[\\/]frame_impl.cc$",
Sergey Ulanov6db14b4d62021-05-10 07:59:482311 r"^fuchsia[\\/]runners[\\/]common[\\/]web_component.cc$",
James Cook24a504192020-07-23 00:08:442312 r"^headless[\\/]app[\\/]headless_shell\.cc$",
2313 r"^ipc[\\/]ipc_logging\.cc$",
2314 r"^native_client_sdk[\\/]",
2315 r"^remoting[\\/]base[\\/]logging\.h$",
2316 r"^remoting[\\/]host[\\/].*",
2317 r"^sandbox[\\/]linux[\\/].*",
2318 r"^storage[\\/]browser[\\/]file_system[\\/]" +
2319 r"dump_file_system.cc$",
2320 r"^tools[\\/]",
2321 r"^ui[\\/]base[\\/]resource[\\/]data_pack.cc$",
2322 r"^ui[\\/]aura[\\/]bench[\\/]bench_main\.cc$",
2323 r"^ui[\\/]ozone[\\/]platform[\\/]cast[\\/]",
2324 r"^ui[\\/]base[\\/]x[\\/]xwmstartupcheck[\\/]"
2325 r"xwmstartupcheck\.cc$"))
[email protected]85218562013-11-22 07:41:402326 source_file_filter = lambda x: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:442327 x, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
[email protected]85218562013-11-22 07:41:402328
thomasanderson625d3932017-03-29 07:16:582329 log_info = set([])
2330 printf = set([])
[email protected]85218562013-11-22 07:41:402331
2332 for f in input_api.AffectedSourceFiles(source_file_filter):
thomasanderson625d3932017-03-29 07:16:582333 for _, line in f.ChangedContents():
2334 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
2335 log_info.add(f.LocalPath())
2336 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
2337 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:372338
thomasanderson625d3932017-03-29 07:16:582339 if input_api.re.search(r"\bprintf\(", line):
2340 printf.add(f.LocalPath())
2341 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
2342 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:402343
2344 if log_info:
2345 return [output_api.PresubmitError(
2346 'These files spam the console log with LOG(INFO):',
2347 items=log_info)]
2348 if printf:
2349 return [output_api.PresubmitError(
2350 'These files spam the console log with printf/fprintf:',
2351 items=printf)]
2352 return []
2353
2354
Saagar Sanghavifceeaae2020-08-12 16:40:362355def CheckForAnonymousVariables(input_api, output_api):
[email protected]49aa76a2013-12-04 06:59:162356 """These types are all expected to hold locks while in scope and
2357 so should never be anonymous (which causes them to be immediately
2358 destroyed)."""
2359 they_who_must_be_named = [
2360 'base::AutoLock',
2361 'base::AutoReset',
2362 'base::AutoUnlock',
2363 'SkAutoAlphaRestore',
2364 'SkAutoBitmapShaderInstall',
2365 'SkAutoBlitterChoose',
2366 'SkAutoBounderCommit',
2367 'SkAutoCallProc',
2368 'SkAutoCanvasRestore',
2369 'SkAutoCommentBlock',
2370 'SkAutoDescriptor',
2371 'SkAutoDisableDirectionCheck',
2372 'SkAutoDisableOvalCheck',
2373 'SkAutoFree',
2374 'SkAutoGlyphCache',
2375 'SkAutoHDC',
2376 'SkAutoLockColors',
2377 'SkAutoLockPixels',
2378 'SkAutoMalloc',
2379 'SkAutoMaskFreeImage',
2380 'SkAutoMutexAcquire',
2381 'SkAutoPathBoundsUpdate',
2382 'SkAutoPDFRelease',
2383 'SkAutoRasterClipValidate',
2384 'SkAutoRef',
2385 'SkAutoTime',
2386 'SkAutoTrace',
2387 'SkAutoUnref',
2388 ]
2389 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
2390 # bad: base::AutoLock(lock.get());
2391 # not bad: base::AutoLock lock(lock.get());
2392 bad_pattern = input_api.re.compile(anonymous)
2393 # good: new base::AutoLock(lock.get())
2394 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
2395 errors = []
2396
2397 for f in input_api.AffectedFiles():
2398 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
2399 continue
2400 for linenum, line in f.ChangedContents():
2401 if bad_pattern.search(line) and not good_pattern.search(line):
2402 errors.append('%s:%d' % (f.LocalPath(), linenum))
2403
2404 if errors:
2405 return [output_api.PresubmitError(
2406 'These lines create anonymous variables that need to be named:',
2407 items=errors)]
2408 return []
2409
2410
Saagar Sanghavifceeaae2020-08-12 16:40:362411def CheckUniquePtrOnUpload(input_api, output_api):
Vaclav Brozekb7fadb692018-08-30 06:39:532412 # Returns whether |template_str| is of the form <T, U...> for some types T
2413 # and U. Assumes that |template_str| is already in the form <...>.
2414 def HasMoreThanOneArg(template_str):
2415 # Level of <...> nesting.
2416 nesting = 0
2417 for c in template_str:
2418 if c == '<':
2419 nesting += 1
2420 elif c == '>':
2421 nesting -= 1
2422 elif c == ',' and nesting == 1:
2423 return True
2424 return False
2425
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492426 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
Peter Kasting4844e46e2018-02-23 07:27:102427 sources = lambda affected_file: input_api.FilterSourceFile(
2428 affected_file,
James Cook24a504192020-07-23 00:08:442429 files_to_skip=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2430 input_api.DEFAULT_FILES_TO_SKIP),
2431 files_to_check=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:552432
2433 # Pattern to capture a single "<...>" block of template arguments. It can
2434 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
2435 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
2436 # latter would likely require counting that < and > match, which is not
2437 # expressible in regular languages. Should the need arise, one can introduce
2438 # limited counting (matching up to a total number of nesting depth), which
2439 # should cover all practical cases for already a low nesting limit.
2440 template_arg_pattern = (
2441 r'<[^>]*' # Opening block of <.
2442 r'>([^<]*>)?') # Closing block of >.
2443 # Prefix expressing that whatever follows is not already inside a <...>
2444 # block.
2445 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
Peter Kasting4844e46e2018-02-23 07:27:102446 null_construct_pattern = input_api.re.compile(
Vaclav Brozeka54c528b2018-04-06 19:23:552447 not_inside_template_arg_pattern
2448 + r'\bstd::unique_ptr'
2449 + template_arg_pattern
2450 + r'\(\)')
2451
2452 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
2453 template_arg_no_array_pattern = (
2454 r'<[^>]*[^]]' # Opening block of <.
2455 r'>([^(<]*[^]]>)?') # Closing block of >.
2456 # Prefix saying that what follows is the start of an expression.
2457 start_of_expr_pattern = r'(=|\breturn|^)\s*'
2458 # Suffix saying that what follows are call parentheses with a non-empty list
2459 # of arguments.
2460 nonempty_arg_list_pattern = r'\(([^)]|$)'
Vaclav Brozekb7fadb692018-08-30 06:39:532461 # Put the template argument into a capture group for deeper examination later.
Vaclav Brozeka54c528b2018-04-06 19:23:552462 return_construct_pattern = input_api.re.compile(
2463 start_of_expr_pattern
2464 + r'std::unique_ptr'
Vaclav Brozekb7fadb692018-08-30 06:39:532465 + '(?P<template_arg>'
Vaclav Brozeka54c528b2018-04-06 19:23:552466 + template_arg_no_array_pattern
Vaclav Brozekb7fadb692018-08-30 06:39:532467 + ')'
Vaclav Brozeka54c528b2018-04-06 19:23:552468 + nonempty_arg_list_pattern)
2469
Vaclav Brozek851d9602018-04-04 16:13:052470 problems_constructor = []
2471 problems_nullptr = []
Peter Kasting4844e46e2018-02-23 07:27:102472 for f in input_api.AffectedSourceFiles(sources):
2473 for line_number, line in f.ChangedContents():
2474 # Disallow:
2475 # return std::unique_ptr<T>(foo);
2476 # bar = std::unique_ptr<T>(foo);
2477 # But allow:
2478 # return std::unique_ptr<T[]>(foo);
2479 # bar = std::unique_ptr<T[]>(foo);
Vaclav Brozekb7fadb692018-08-30 06:39:532480 # And also allow cases when the second template argument is present. Those
2481 # cases cannot be handled by std::make_unique:
2482 # return std::unique_ptr<T, U>(foo);
2483 # bar = std::unique_ptr<T, U>(foo);
Vaclav Brozek851d9602018-04-04 16:13:052484 local_path = f.LocalPath()
Vaclav Brozekb7fadb692018-08-30 06:39:532485 return_construct_result = return_construct_pattern.search(line)
2486 if return_construct_result and not HasMoreThanOneArg(
2487 return_construct_result.group('template_arg')):
Vaclav Brozek851d9602018-04-04 16:13:052488 problems_constructor.append(
2489 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Peter Kasting4844e46e2018-02-23 07:27:102490 # Disallow:
2491 # std::unique_ptr<T>()
2492 if null_construct_pattern.search(line):
Vaclav Brozek851d9602018-04-04 16:13:052493 problems_nullptr.append(
2494 '%s:%d\n %s' % (local_path, line_number, line.strip()))
2495
2496 errors = []
Vaclav Brozekc2fecf42018-04-06 16:40:162497 if problems_nullptr:
Vaclav Brozek851d9602018-04-04 16:13:052498 errors.append(output_api.PresubmitError(
2499 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:162500 problems_nullptr))
2501 if problems_constructor:
Vaclav Brozek851d9602018-04-04 16:13:052502 errors.append(output_api.PresubmitError(
2503 'The following files use explicit std::unique_ptr constructor.'
2504 'Use std::make_unique<T>() instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:162505 problems_constructor))
Peter Kasting4844e46e2018-02-23 07:27:102506 return errors
2507
2508
Saagar Sanghavifceeaae2020-08-12 16:40:362509def CheckUserActionUpdate(input_api, output_api):
[email protected]999261d2014-03-03 20:08:082510 """Checks if any new user action has been added."""
[email protected]2f92dec2014-03-07 19:21:522511 if any('actions.xml' == input_api.os_path.basename(f) for f in
[email protected]999261d2014-03-03 20:08:082512 input_api.LocalPaths()):
[email protected]2f92dec2014-03-07 19:21:522513 # If actions.xml is already included in the changelist, the PRESUBMIT
2514 # for actions.xml will do a more complete presubmit check.
[email protected]999261d2014-03-03 20:08:082515 return []
2516
Alexei Svitkine64505a92021-03-11 22:00:542517 file_inclusion_pattern = [r'.*\.(cc|mm)$']
2518 files_to_skip = (_EXCLUDED_PATHS +
2519 _TEST_CODE_EXCLUDED_PATHS +
2520 input_api.DEFAULT_FILES_TO_SKIP )
2521 file_filter = lambda f: input_api.FilterSourceFile(
2522 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
2523
[email protected]999261d2014-03-03 20:08:082524 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
[email protected]2f92dec2014-03-07 19:21:522525 current_actions = None
[email protected]999261d2014-03-03 20:08:082526 for f in input_api.AffectedFiles(file_filter=file_filter):
2527 for line_num, line in f.ChangedContents():
2528 match = input_api.re.search(action_re, line)
2529 if match:
[email protected]2f92dec2014-03-07 19:21:522530 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
2531 # loaded only once.
2532 if not current_actions:
2533 with open('tools/metrics/actions/actions.xml') as actions_f:
2534 current_actions = actions_f.read()
2535 # Search for the matched user action name in |current_actions|.
[email protected]999261d2014-03-03 20:08:082536 for action_name in match.groups():
[email protected]2f92dec2014-03-07 19:21:522537 action = 'name="{0}"'.format(action_name)
2538 if action not in current_actions:
[email protected]999261d2014-03-03 20:08:082539 return [output_api.PresubmitPromptWarning(
2540 'File %s line %d: %s is missing in '
[email protected]2f92dec2014-03-07 19:21:522541 'tools/metrics/actions/actions.xml. Please run '
2542 'tools/metrics/actions/extract_actions.py to update.'
[email protected]999261d2014-03-03 20:08:082543 % (f.LocalPath(), line_num, action_name))]
2544 return []
2545
2546
Daniel Cheng13ca61a882017-08-25 15:11:252547def _ImportJSONCommentEater(input_api):
2548 import sys
2549 sys.path = sys.path + [input_api.os_path.join(
2550 input_api.PresubmitLocalPath(),
2551 'tools', 'json_comment_eater')]
2552 import json_comment_eater
2553 return json_comment_eater
2554
2555
[email protected]99171a92014-06-03 08:44:472556def _GetJSONParseError(input_api, filename, eat_comments=True):
2557 try:
2558 contents = input_api.ReadFile(filename)
2559 if eat_comments:
Daniel Cheng13ca61a882017-08-25 15:11:252560 json_comment_eater = _ImportJSONCommentEater(input_api)
plundblad1f5a4509f2015-07-23 11:31:132561 contents = json_comment_eater.Nom(contents)
[email protected]99171a92014-06-03 08:44:472562
2563 input_api.json.loads(contents)
2564 except ValueError as e:
2565 return e
2566 return None
2567
2568
2569def _GetIDLParseError(input_api, filename):
2570 try:
2571 contents = input_api.ReadFile(filename)
2572 idl_schema = input_api.os_path.join(
2573 input_api.PresubmitLocalPath(),
2574 'tools', 'json_schema_compiler', 'idl_schema.py')
2575 process = input_api.subprocess.Popen(
2576 [input_api.python_executable, idl_schema],
2577 stdin=input_api.subprocess.PIPE,
2578 stdout=input_api.subprocess.PIPE,
2579 stderr=input_api.subprocess.PIPE,
2580 universal_newlines=True)
2581 (_, error) = process.communicate(input=contents)
2582 return error or None
2583 except ValueError as e:
2584 return e
2585
2586
Saagar Sanghavifceeaae2020-08-12 16:40:362587def CheckParseErrors(input_api, output_api):
[email protected]99171a92014-06-03 08:44:472588 """Check that IDL and JSON files do not contain syntax errors."""
2589 actions = {
2590 '.idl': _GetIDLParseError,
2591 '.json': _GetJSONParseError,
2592 }
[email protected]99171a92014-06-03 08:44:472593 # Most JSON files are preprocessed and support comments, but these do not.
2594 json_no_comments_patterns = [
Egor Paskoce145c42018-09-28 19:31:042595 r'^testing[\\/]',
[email protected]99171a92014-06-03 08:44:472596 ]
2597 # Only run IDL checker on files in these directories.
2598 idl_included_patterns = [
Egor Paskoce145c42018-09-28 19:31:042599 r'^chrome[\\/]common[\\/]extensions[\\/]api[\\/]',
2600 r'^extensions[\\/]common[\\/]api[\\/]',
[email protected]99171a92014-06-03 08:44:472601 ]
2602
2603 def get_action(affected_file):
2604 filename = affected_file.LocalPath()
2605 return actions.get(input_api.os_path.splitext(filename)[1])
2606
[email protected]99171a92014-06-03 08:44:472607 def FilterFile(affected_file):
2608 action = get_action(affected_file)
2609 if not action:
2610 return False
2611 path = affected_file.LocalPath()
2612
Erik Staab2dd72b12020-04-16 15:03:402613 if _MatchesFile(input_api,
2614 _KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS,
2615 path):
[email protected]99171a92014-06-03 08:44:472616 return False
2617
2618 if (action == _GetIDLParseError and
Sean Kau46e29bc2017-08-28 16:31:162619 not _MatchesFile(input_api, idl_included_patterns, path)):
[email protected]99171a92014-06-03 08:44:472620 return False
2621 return True
2622
2623 results = []
2624 for affected_file in input_api.AffectedFiles(
2625 file_filter=FilterFile, include_deletes=False):
2626 action = get_action(affected_file)
2627 kwargs = {}
2628 if (action == _GetJSONParseError and
Sean Kau46e29bc2017-08-28 16:31:162629 _MatchesFile(input_api, json_no_comments_patterns,
2630 affected_file.LocalPath())):
[email protected]99171a92014-06-03 08:44:472631 kwargs['eat_comments'] = False
2632 parse_error = action(input_api,
2633 affected_file.AbsoluteLocalPath(),
2634 **kwargs)
2635 if parse_error:
2636 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
2637 (affected_file.LocalPath(), parse_error)))
2638 return results
2639
2640
Saagar Sanghavifceeaae2020-08-12 16:40:362641def CheckJavaStyle(input_api, output_api):
[email protected]760deea2013-12-10 19:33:492642 """Runs checkstyle on changed java files and returns errors if any exist."""
mohan.reddyf21db962014-10-16 12:26:472643 import sys
[email protected]760deea2013-12-10 19:33:492644 original_sys_path = sys.path
2645 try:
2646 sys.path = sys.path + [input_api.os_path.join(
2647 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
2648 import checkstyle
2649 finally:
2650 # Restore sys.path to what it was before.
2651 sys.path = original_sys_path
2652
2653 return checkstyle.RunCheckstyle(
davileen72d76532015-01-20 22:30:092654 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
James Cook24a504192020-07-23 00:08:442655 files_to_skip=_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP)
[email protected]760deea2013-12-10 19:33:492656
2657
Saagar Sanghavifceeaae2020-08-12 16:40:362658def CheckPythonDevilInit(input_api, output_api):
Nate Fischerdfd9812e2019-07-18 22:03:002659 """Checks to make sure devil is initialized correctly in python scripts."""
2660 script_common_initialize_pattern = input_api.re.compile(
2661 r'script_common\.InitializeEnvironment\(')
2662 devil_env_config_initialize = input_api.re.compile(
2663 r'devil_env\.config\.Initialize\(')
2664
2665 errors = []
2666
2667 sources = lambda affected_file: input_api.FilterSourceFile(
2668 affected_file,
James Cook24a504192020-07-23 00:08:442669 files_to_skip=(_EXCLUDED_PATHS + input_api.DEFAULT_FILES_TO_SKIP +
2670 (r'^build[\\/]android[\\/]devil_chromium\.py',
2671 r'^third_party[\\/].*',)),
2672 files_to_check=[r'.*\.py$'])
Nate Fischerdfd9812e2019-07-18 22:03:002673
2674 for f in input_api.AffectedSourceFiles(sources):
2675 for line_num, line in f.ChangedContents():
2676 if (script_common_initialize_pattern.search(line) or
2677 devil_env_config_initialize.search(line)):
2678 errors.append("%s:%d" % (f.LocalPath(), line_num))
2679
2680 results = []
2681
2682 if errors:
2683 results.append(output_api.PresubmitError(
2684 'Devil initialization should always be done using '
2685 'devil_chromium.Initialize() in the chromium project, to use better '
2686 'defaults for dependencies (ex. up-to-date version of adb).',
2687 errors))
2688
2689 return results
2690
2691
Sean Kau46e29bc2017-08-28 16:31:162692def _MatchesFile(input_api, patterns, path):
2693 for pattern in patterns:
2694 if input_api.re.search(pattern, path):
2695 return True
2696 return False
2697
2698
Daniel Cheng7052cdf2017-11-21 19:23:292699def _GetOwnersFilesToCheckForIpcOwners(input_api):
2700 """Gets a list of OWNERS files to check for correct security owners.
dchenge07de812016-06-20 19:27:172701
Daniel Cheng7052cdf2017-11-21 19:23:292702 Returns:
2703 A dictionary mapping an OWNER file to the list of OWNERS rules it must
2704 contain to cover IPC-related files with noparent reviewer rules.
2705 """
2706 # Whether or not a file affects IPC is (mostly) determined by a simple list
2707 # of filename patterns.
dchenge07de812016-06-20 19:27:172708 file_patterns = [
palmerb19a0932017-01-24 04:00:312709 # Legacy IPC:
dchenge07de812016-06-20 19:27:172710 '*_messages.cc',
2711 '*_messages*.h',
2712 '*_param_traits*.*',
palmerb19a0932017-01-24 04:00:312713 # Mojo IPC:
dchenge07de812016-06-20 19:27:172714 '*.mojom',
Daniel Cheng1f386932018-01-29 19:56:472715 '*_mojom_traits*.*',
dchenge07de812016-06-20 19:27:172716 '*_struct_traits*.*',
2717 '*_type_converter*.*',
palmerb19a0932017-01-24 04:00:312718 '*.typemap',
2719 # Android native IPC:
2720 '*.aidl',
2721 # Blink uses a different file naming convention:
2722 '*EnumTraits*.*',
Daniel Chenge0bf3f62018-01-30 01:56:472723 "*MojomTraits*.*",
dchenge07de812016-06-20 19:27:172724 '*StructTraits*.*',
2725 '*TypeConverter*.*',
2726 ]
2727
scottmg7a6ed5ba2016-11-04 18:22:042728 # These third_party directories do not contain IPCs, but contain files
2729 # matching the above patterns, which trigger false positives.
2730 exclude_paths = [
2731 'third_party/crashpad/*',
Raphael Kubo da Costa4a224cf42019-11-19 18:44:162732 'third_party/blink/renderer/platform/bindings/*',
Andres Medinae684cf42018-08-27 18:48:232733 'third_party/protobuf/benchmarks/python/*',
Nico Weberee3dc9b2017-08-31 17:09:292734 'third_party/win_build_output/*',
Scott Violet9f82d362019-11-06 21:42:162735 # These files are just used to communicate between class loaders running
2736 # in the same process.
2737 'weblayer/browser/java/org/chromium/weblayer_private/interfaces/*',
Mugdha Lakhani6230b962020-01-13 13:00:572738 'weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/*',
2739
scottmg7a6ed5ba2016-11-04 18:22:042740 ]
2741
dchenge07de812016-06-20 19:27:172742 # Dictionary mapping an OWNERS file path to Patterns.
2743 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2744 # rules ) to a PatternEntry.
2745 # PatternEntry is a dictionary with two keys:
2746 # - 'files': the files that are matched by this pattern
2747 # - 'rules': the per-file rules needed for this pattern
2748 # For example, if we expect OWNERS file to contain rules for *.mojom and
2749 # *_struct_traits*.*, Patterns might look like this:
2750 # {
2751 # '*.mojom': {
2752 # 'files': ...,
2753 # 'rules': [
2754 # 'per-file *.mojom=set noparent',
2755 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2756 # ],
2757 # },
2758 # '*_struct_traits*.*': {
2759 # 'files': ...,
2760 # 'rules': [
2761 # 'per-file *_struct_traits*.*=set noparent',
2762 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2763 # ],
2764 # },
2765 # }
2766 to_check = {}
2767
Daniel Cheng13ca61a882017-08-25 15:11:252768 def AddPatternToCheck(input_file, pattern):
2769 owners_file = input_api.os_path.join(
2770 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2771 if owners_file not in to_check:
2772 to_check[owners_file] = {}
2773 if pattern not in to_check[owners_file]:
2774 to_check[owners_file][pattern] = {
2775 'files': [],
2776 'rules': [
2777 'per-file %s=set noparent' % pattern,
2778 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2779 ]
2780 }
Vaclav Brozekd5de76a2018-03-17 07:57:502781 to_check[owners_file][pattern]['files'].append(input_file)
Daniel Cheng13ca61a882017-08-25 15:11:252782
dchenge07de812016-06-20 19:27:172783 # Iterate through the affected files to see what we actually need to check
2784 # for. We should only nag patch authors about per-file rules if a file in that
2785 # directory would match that pattern. If a directory only contains *.mojom
2786 # files and no *_messages*.h files, we should only nag about rules for
2787 # *.mojom files.
Daniel Cheng13ca61a882017-08-25 15:11:252788 for f in input_api.AffectedFiles(include_deletes=False):
Daniel Cheng76f49cc2020-04-21 01:48:262789 # Manifest files don't have a strong naming convention. Instead, try to find
2790 # affected .cc and .h files which look like they contain a manifest
2791 # definition.
2792 manifest_pattern = input_api.re.compile('manifests?\.(cc|h)$')
2793 test_manifest_pattern = input_api.re.compile('test_manifests?\.(cc|h)')
2794 if (manifest_pattern.search(f.LocalPath()) and not
2795 test_manifest_pattern.search(f.LocalPath())):
2796 # We expect all actual service manifest files to contain at least one
2797 # qualified reference to service_manager::Manifest.
2798 if 'service_manager::Manifest' in '\n'.join(f.NewContents()):
Daniel Cheng13ca61a882017-08-25 15:11:252799 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
dchenge07de812016-06-20 19:27:172800 for pattern in file_patterns:
2801 if input_api.fnmatch.fnmatch(
2802 input_api.os_path.basename(f.LocalPath()), pattern):
scottmg7a6ed5ba2016-11-04 18:22:042803 skip = False
2804 for exclude in exclude_paths:
2805 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2806 skip = True
2807 break
2808 if skip:
2809 continue
Daniel Cheng13ca61a882017-08-25 15:11:252810 AddPatternToCheck(f, pattern)
dchenge07de812016-06-20 19:27:172811 break
2812
Daniel Cheng7052cdf2017-11-21 19:23:292813 return to_check
2814
2815
Wez17c66962020-04-29 15:26:032816def _AddOwnersFilesToCheckForFuchsiaSecurityOwners(input_api, to_check):
2817 """Adds OWNERS files to check for correct Fuchsia security owners."""
2818
2819 file_patterns = [
2820 # Component specifications.
2821 '*.cml', # Component Framework v2.
2822 '*.cmx', # Component Framework v1.
2823
2824 # Fuchsia IDL protocol specifications.
2825 '*.fidl',
2826 ]
2827
Joshua Peraza1ca6d392020-12-08 00:14:092828 # Don't check for owners files for changes in these directories.
2829 exclude_paths = [
2830 'third_party/crashpad/*',
2831 ]
2832
Wez17c66962020-04-29 15:26:032833 def AddPatternToCheck(input_file, pattern):
2834 owners_file = input_api.os_path.join(
2835 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2836 if owners_file not in to_check:
2837 to_check[owners_file] = {}
2838 if pattern not in to_check[owners_file]:
2839 to_check[owners_file][pattern] = {
2840 'files': [],
2841 'rules': [
2842 'per-file %s=set noparent' % pattern,
2843 'per-file %s=file://fuchsia/SECURITY_OWNERS' % pattern,
2844 ]
2845 }
2846 to_check[owners_file][pattern]['files'].append(input_file)
2847
2848 # Iterate through the affected files to see what we actually need to check
2849 # for. We should only nag patch authors about per-file rules if a file in that
2850 # directory would match that pattern.
2851 for f in input_api.AffectedFiles(include_deletes=False):
Joshua Peraza1ca6d392020-12-08 00:14:092852 skip = False
2853 for exclude in exclude_paths:
2854 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2855 skip = True
2856 if skip:
2857 continue
2858
Wez17c66962020-04-29 15:26:032859 for pattern in file_patterns:
2860 if input_api.fnmatch.fnmatch(
2861 input_api.os_path.basename(f.LocalPath()), pattern):
2862 AddPatternToCheck(f, pattern)
2863 break
2864
2865 return to_check
2866
2867
Saagar Sanghavifceeaae2020-08-12 16:40:362868def CheckSecurityOwners(input_api, output_api):
Daniel Cheng7052cdf2017-11-21 19:23:292869 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2870 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
Wez17c66962020-04-29 15:26:032871 _AddOwnersFilesToCheckForFuchsiaSecurityOwners(input_api, to_check)
Daniel Cheng7052cdf2017-11-21 19:23:292872
2873 if to_check:
2874 # If there are any OWNERS files to check, there are IPC-related changes in
2875 # this CL. Auto-CC the review list.
2876 output_api.AppendCC('[email protected]')
2877
2878 # Go through the OWNERS files to check, filtering out rules that are already
2879 # present in that OWNERS file.
Dirk Prankee3c9c62d2021-05-18 18:35:592880 for owners_file, patterns in to_check.items():
dchenge07de812016-06-20 19:27:172881 try:
Dirk Prankee3c9c62d2021-05-18 18:35:592882 with open(owners_file) as f:
dchenge07de812016-06-20 19:27:172883 lines = set(f.read().splitlines())
Jeffrey Youngf3a5c8c42021-05-14 21:56:102884 for entry in patterns.values():
dchenge07de812016-06-20 19:27:172885 entry['rules'] = [rule for rule in entry['rules'] if rule not in lines
2886 ]
2887 except IOError:
2888 # No OWNERS file, so all the rules are definitely missing.
2889 continue
2890
2891 # All the remaining lines weren't found in OWNERS files, so emit an error.
2892 errors = []
Dirk Prankee3c9c62d2021-05-18 18:35:592893 for owners_file, patterns in to_check.items():
dchenge07de812016-06-20 19:27:172894 missing_lines = []
2895 files = []
Dirk Prankee3c9c62d2021-05-18 18:35:592896 for _, entry in patterns.items():
dchenge07de812016-06-20 19:27:172897 missing_lines.extend(entry['rules'])
2898 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2899 if missing_lines:
2900 errors.append(
Vaclav Brozek1893a972018-04-25 05:48:052901 'Because of the presence of files:\n%s\n\n'
2902 '%s needs the following %d lines added:\n\n%s' %
2903 ('\n'.join(files), owners_file, len(missing_lines),
2904 '\n'.join(missing_lines)))
dchenge07de812016-06-20 19:27:172905
2906 results = []
2907 if errors:
vabrf5ce3bf92016-07-11 14:52:412908 if input_api.is_committing:
2909 output = output_api.PresubmitError
2910 else:
2911 output = output_api.PresubmitPromptWarning
2912 results.append(output(
Daniel Cheng52111692017-06-14 08:00:592913 'Found OWNERS files that need to be updated for IPC security ' +
2914 'review coverage.\nPlease update the OWNERS files below:',
dchenge07de812016-06-20 19:27:172915 long_text='\n\n'.join(errors)))
2916
2917 return results
2918
2919
Robert Sesek2c905332020-05-06 23:17:132920def _GetFilesUsingSecurityCriticalFunctions(input_api):
2921 """Checks affected files for changes to security-critical calls. This
2922 function checks the full change diff, to catch both additions/changes
2923 and removals.
2924
2925 Returns a dict keyed by file name, and the value is a set of detected
2926 functions.
2927 """
2928 # Map of function pretty name (displayed in an error) to the pattern to
2929 # match it with.
2930 _PATTERNS_TO_CHECK = {
Alex Goughbc964dd2020-06-15 17:52:372931 'content::GetServiceSandboxType<>()':
2932 'GetServiceSandboxType\\<'
Robert Sesek2c905332020-05-06 23:17:132933 }
2934 _PATTERNS_TO_CHECK = {
2935 k: input_api.re.compile(v)
2936 for k, v in _PATTERNS_TO_CHECK.items()
2937 }
2938
2939 # Scan all affected files for changes touching _FUNCTIONS_TO_CHECK.
2940 files_to_functions = {}
2941 for f in input_api.AffectedFiles():
2942 diff = f.GenerateScmDiff()
2943 for line in diff.split('\n'):
2944 # Not using just RightHandSideLines() because removing a
2945 # call to a security-critical function can be just as important
2946 # as adding or changing the arguments.
2947 if line.startswith('-') or (line.startswith('+') and
2948 not line.startswith('++')):
2949 for name, pattern in _PATTERNS_TO_CHECK.items():
2950 if pattern.search(line):
2951 path = f.LocalPath()
2952 if not path in files_to_functions:
2953 files_to_functions[path] = set()
2954 files_to_functions[path].add(name)
2955 return files_to_functions
2956
2957
Saagar Sanghavifceeaae2020-08-12 16:40:362958def CheckSecurityChanges(input_api, output_api):
Robert Sesek2c905332020-05-06 23:17:132959 """Checks that changes involving security-critical functions are reviewed
2960 by the security team.
2961 """
2962 files_to_functions = _GetFilesUsingSecurityCriticalFunctions(input_api)
Edward Lesmes1e9fade2021-02-08 20:31:122963 if not len(files_to_functions):
2964 return []
Robert Sesek2c905332020-05-06 23:17:132965
Edward Lesmes1e9fade2021-02-08 20:31:122966 owner_email, reviewers = (
2967 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
2968 input_api,
2969 None,
2970 approval_needed=input_api.is_committing))
Robert Sesek2c905332020-05-06 23:17:132971
Edward Lesmes1e9fade2021-02-08 20:31:122972 # Load the OWNERS file for security changes.
2973 owners_file = 'ipc/SECURITY_OWNERS'
2974 security_owners = input_api.owners_client.ListOwners(owners_file)
2975 has_security_owner = any([owner in reviewers for owner in security_owners])
2976 if has_security_owner:
2977 return []
Robert Sesek2c905332020-05-06 23:17:132978
Edward Lesmes1e9fade2021-02-08 20:31:122979 msg = 'The following files change calls to security-sensive functions\n' \
2980 'that need to be reviewed by {}.\n'.format(owners_file)
2981 for path, names in files_to_functions.items():
2982 msg += ' {}\n'.format(path)
2983 for name in names:
2984 msg += ' {}\n'.format(name)
2985 msg += '\n'
Robert Sesek2c905332020-05-06 23:17:132986
Edward Lesmes1e9fade2021-02-08 20:31:122987 if input_api.is_committing:
2988 output = output_api.PresubmitError
2989 else:
2990 output = output_api.PresubmitNotifyResult
2991 return [output(msg)]
Robert Sesek2c905332020-05-06 23:17:132992
2993
Saagar Sanghavifceeaae2020-08-12 16:40:362994def CheckSetNoParent(input_api, output_api):
Jochen Eisingerf9fbe7b6c32019-11-18 09:37:262995 """Checks that set noparent is only used together with an OWNERS file in
2996 //build/OWNERS.setnoparent (see also
2997 //docs/code_reviews.md#owners-files-details)
2998 """
2999 errors = []
3000
3001 allowed_owners_files_file = 'build/OWNERS.setnoparent'
3002 allowed_owners_files = set()
3003 with open(allowed_owners_files_file, 'r') as f:
3004 for line in f:
3005 line = line.strip()
3006 if not line or line.startswith('#'):
3007 continue
3008 allowed_owners_files.add(line)
3009
3010 per_file_pattern = input_api.re.compile('per-file (.+)=(.+)')
3011
3012 for f in input_api.AffectedFiles(include_deletes=False):
3013 if not f.LocalPath().endswith('OWNERS'):
3014 continue
3015
3016 found_owners_files = set()
3017 found_set_noparent_lines = dict()
3018
3019 # Parse the OWNERS file.
3020 for lineno, line in enumerate(f.NewContents(), 1):
3021 line = line.strip()
3022 if line.startswith('set noparent'):
3023 found_set_noparent_lines[''] = lineno
3024 if line.startswith('file://'):
3025 if line in allowed_owners_files:
3026 found_owners_files.add('')
3027 if line.startswith('per-file'):
3028 match = per_file_pattern.match(line)
3029 if match:
3030 glob = match.group(1).strip()
3031 directive = match.group(2).strip()
3032 if directive == 'set noparent':
3033 found_set_noparent_lines[glob] = lineno
3034 if directive.startswith('file://'):
3035 if directive in allowed_owners_files:
3036 found_owners_files.add(glob)
Sean McCulloughf5cdfea2021-03-05 00:41:153037
Jochen Eisingerf9fbe7b6c32019-11-18 09:37:263038 # Check that every set noparent line has a corresponding file:// line
John Abd-El-Malekdfd1edc2021-02-24 22:22:403039 # listed in build/OWNERS.setnoparent. An exception is made for top level
3040 # directories since src/OWNERS shouldn't review them.
John Abd-El-Malek759fea62021-03-13 03:41:143041 if (f.LocalPath().count('/') != 1 and
3042 (not f.LocalPath() in _EXCLUDED_SET_NO_PARENT_PATHS)):
John Abd-El-Malekdfd1edc2021-02-24 22:22:403043 for set_noparent_line in found_set_noparent_lines:
3044 if set_noparent_line in found_owners_files:
3045 continue
3046 errors.append(' %s:%d' % (f.LocalPath(),
3047 found_set_noparent_lines[set_noparent_line]))
Jochen Eisingerf9fbe7b6c32019-11-18 09:37:263048
3049 results = []
3050 if errors:
3051 if input_api.is_committing:
3052 output = output_api.PresubmitError
3053 else:
3054 output = output_api.PresubmitPromptWarning
3055 results.append(output(
3056 'Found the following "set noparent" restrictions in OWNERS files that '
3057 'do not include owners from build/OWNERS.setnoparent:',
3058 long_text='\n\n'.join(errors)))
3059 return results
3060
3061
Saagar Sanghavifceeaae2020-08-12 16:40:363062def CheckUselessForwardDeclarations(input_api, output_api):
jbriance2c51e821a2016-12-12 08:24:313063 """Checks that added or removed lines in non third party affected
3064 header files do not lead to new useless class or struct forward
3065 declaration.
jbriance9e12f162016-11-25 07:57:503066 """
3067 results = []
3068 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
3069 input_api.re.MULTILINE)
3070 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
3071 input_api.re.MULTILINE)
3072 for f in input_api.AffectedFiles(include_deletes=False):
jbriance2c51e821a2016-12-12 08:24:313073 if (f.LocalPath().startswith('third_party') and
Kent Tamurae9b3a9ec2017-08-31 02:20:193074 not f.LocalPath().startswith('third_party/blink') and
Kent Tamura32dbbcb2018-11-30 12:28:493075 not f.LocalPath().startswith('third_party\\blink')):
jbriance2c51e821a2016-12-12 08:24:313076 continue
3077
jbriance9e12f162016-11-25 07:57:503078 if not f.LocalPath().endswith('.h'):
3079 continue
3080
3081 contents = input_api.ReadFile(f)
3082 fwd_decls = input_api.re.findall(class_pattern, contents)
3083 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
3084
3085 useless_fwd_decls = []
3086 for decl in fwd_decls:
3087 count = sum(1 for _ in input_api.re.finditer(
3088 r'\b%s\b' % input_api.re.escape(decl), contents))
3089 if count == 1:
3090 useless_fwd_decls.append(decl)
3091
3092 if not useless_fwd_decls:
3093 continue
3094
3095 for line in f.GenerateScmDiff().splitlines():
3096 if (line.startswith('-') and not line.startswith('--') or
3097 line.startswith('+') and not line.startswith('++')):
3098 for decl in useless_fwd_decls:
3099 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
3100 results.append(output_api.PresubmitPromptWarning(
ricea6416dea2017-05-19 12:39:243101 '%s: %s forward declaration is no longer needed' %
jbriance9e12f162016-11-25 07:57:503102 (f.LocalPath(), decl)))
3103 useless_fwd_decls.remove(decl)
3104
3105 return results
3106
Jinsong Fan91ebbbd2019-04-16 14:57:173107def _CheckAndroidDebuggableBuild(input_api, output_api):
3108 """Checks that code uses BuildInfo.isDebugAndroid() instead of
3109 Build.TYPE.equals('') or ''.equals(Build.TYPE) to check if
3110 this is a debuggable build of Android.
3111 """
3112 build_type_check_pattern = input_api.re.compile(
3113 r'\bBuild\.TYPE\.equals\(|\.equals\(\s*\bBuild\.TYPE\)')
3114
3115 errors = []
3116
3117 sources = lambda affected_file: input_api.FilterSourceFile(
3118 affected_file,
James Cook24a504192020-07-23 00:08:443119 files_to_skip=(_EXCLUDED_PATHS +
3120 _TEST_CODE_EXCLUDED_PATHS +
3121 input_api.DEFAULT_FILES_TO_SKIP +
3122 (r"^android_webview[\\/]support_library[\\/]"
3123 "boundary_interfaces[\\/]",
3124 r"^chrome[\\/]android[\\/]webapk[\\/].*",
3125 r'^third_party[\\/].*',
3126 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
3127 r"webview[\\/]chromium[\\/]License.*",)),
3128 files_to_check=[r'.*\.java$'])
Jinsong Fan91ebbbd2019-04-16 14:57:173129
3130 for f in input_api.AffectedSourceFiles(sources):
3131 for line_num, line in f.ChangedContents():
3132 if build_type_check_pattern.search(line):
3133 errors.append("%s:%d" % (f.LocalPath(), line_num))
3134
3135 results = []
3136
3137 if errors:
3138 results.append(output_api.PresubmitPromptWarning(
3139 'Build.TYPE.equals or .equals(Build.TYPE) usage is detected.'
3140 ' Please use BuildInfo.isDebugAndroid() instead.',
3141 errors))
3142
3143 return results
jbriance9e12f162016-11-25 07:57:503144
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:493145# TODO: add unit tests
dskiba88634f4e2015-08-14 23:03:293146def _CheckAndroidToastUsage(input_api, output_api):
3147 """Checks that code uses org.chromium.ui.widget.Toast instead of
3148 android.widget.Toast (Chromium Toast doesn't force hardware
3149 acceleration on low-end devices, saving memory).
3150 """
3151 toast_import_pattern = input_api.re.compile(
3152 r'^import android\.widget\.Toast;$')
3153
3154 errors = []
3155
3156 sources = lambda affected_file: input_api.FilterSourceFile(
3157 affected_file,
James Cook24a504192020-07-23 00:08:443158 files_to_skip=(_EXCLUDED_PATHS +
3159 _TEST_CODE_EXCLUDED_PATHS +
3160 input_api.DEFAULT_FILES_TO_SKIP +
3161 (r'^chromecast[\\/].*',
3162 r'^remoting[\\/].*')),
3163 files_to_check=[r'.*\.java$'])
dskiba88634f4e2015-08-14 23:03:293164
3165 for f in input_api.AffectedSourceFiles(sources):
3166 for line_num, line in f.ChangedContents():
3167 if toast_import_pattern.search(line):
3168 errors.append("%s:%d" % (f.LocalPath(), line_num))
3169
3170 results = []
3171
3172 if errors:
3173 results.append(output_api.PresubmitError(
3174 'android.widget.Toast usage is detected. Android toasts use hardware'
3175 ' acceleration, and can be\ncostly on low-end devices. Please use'
3176 ' org.chromium.ui.widget.Toast instead.\n'
3177 'Contact [email protected] if you have any questions.',
3178 errors))
3179
3180 return results
3181
3182
dgnaa68d5e2015-06-10 10:08:223183def _CheckAndroidCrLogUsage(input_api, output_api):
3184 """Checks that new logs using org.chromium.base.Log:
3185 - Are using 'TAG' as variable name for the tags (warn)
dgn38736db2015-09-18 19:20:513186 - Are using a tag that is shorter than 20 characters (error)
dgnaa68d5e2015-06-10 10:08:223187 """
pkotwicza1dd0b002016-05-16 14:41:043188
torne89540622017-03-24 19:41:303189 # Do not check format of logs in the given files
pkotwicza1dd0b002016-05-16 14:41:043190 cr_log_check_excluded_paths = [
torne89540622017-03-24 19:41:303191 # //chrome/android/webapk cannot depend on //base
Egor Paskoce145c42018-09-28 19:31:043192 r"^chrome[\\/]android[\\/]webapk[\\/].*",
torne89540622017-03-24 19:41:303193 # WebView license viewer code cannot depend on //base; used in stub APK.
Egor Paskoce145c42018-09-28 19:31:043194 r"^android_webview[\\/]glue[\\/]java[\\/]src[\\/]com[\\/]android[\\/]"
3195 r"webview[\\/]chromium[\\/]License.*",
Egor Paskoa5c05b02018-09-28 16:04:093196 # The customtabs_benchmark is a small app that does not depend on Chromium
3197 # java pieces.
Egor Paskoce145c42018-09-28 19:31:043198 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
pkotwicza1dd0b002016-05-16 14:41:043199 ]
3200
dgnaa68d5e2015-06-10 10:08:223201 cr_log_import_pattern = input_api.re.compile(
dgn87d9fb62015-06-12 09:15:123202 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
3203 class_in_base_pattern = input_api.re.compile(
3204 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
3205 has_some_log_import_pattern = input_api.re.compile(
3206 r'^import .*\.Log;$', input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:223207 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
Tomasz Śniatowski3ae2f102020-03-23 15:35:553208 log_call_pattern = input_api.re.compile(r'\bLog\.\w\((?P<tag>\"?\w+)')
dgnaa68d5e2015-06-10 10:08:223209 log_decl_pattern = input_api.re.compile(
Torne (Richard Coles)3bd7ad02019-10-22 21:20:463210 r'static final String TAG = "(?P<name>(.*))"')
Tomasz Śniatowski3ae2f102020-03-23 15:35:553211 rough_log_decl_pattern = input_api.re.compile(r'\bString TAG\s*=')
dgnaa68d5e2015-06-10 10:08:223212
Torne (Richard Coles)3bd7ad02019-10-22 21:20:463213 REF_MSG = ('See docs/android_logging.md for more info.')
James Cook24a504192020-07-23 00:08:443214 sources = lambda x: input_api.FilterSourceFile(x,
3215 files_to_check=[r'.*\.java$'],
3216 files_to_skip=cr_log_check_excluded_paths)
dgn87d9fb62015-06-12 09:15:123217
dgnaa68d5e2015-06-10 10:08:223218 tag_decl_errors = []
3219 tag_length_errors = []
dgn87d9fb62015-06-12 09:15:123220 tag_errors = []
dgn38736db2015-09-18 19:20:513221 tag_with_dot_errors = []
dgn87d9fb62015-06-12 09:15:123222 util_log_errors = []
dgnaa68d5e2015-06-10 10:08:223223
3224 for f in input_api.AffectedSourceFiles(sources):
3225 file_content = input_api.ReadFile(f)
3226 has_modified_logs = False
dgnaa68d5e2015-06-10 10:08:223227 # Per line checks
dgn87d9fb62015-06-12 09:15:123228 if (cr_log_import_pattern.search(file_content) or
3229 (class_in_base_pattern.search(file_content) and
3230 not has_some_log_import_pattern.search(file_content))):
3231 # Checks to run for files using cr log
dgnaa68d5e2015-06-10 10:08:223232 for line_num, line in f.ChangedContents():
Tomasz Śniatowski3ae2f102020-03-23 15:35:553233 if rough_log_decl_pattern.search(line):
3234 has_modified_logs = True
dgnaa68d5e2015-06-10 10:08:223235
3236 # Check if the new line is doing some logging
dgn87d9fb62015-06-12 09:15:123237 match = log_call_pattern.search(line)
dgnaa68d5e2015-06-10 10:08:223238 if match:
3239 has_modified_logs = True
3240
3241 # Make sure it uses "TAG"
3242 if not match.group('tag') == 'TAG':
3243 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgn87d9fb62015-06-12 09:15:123244 else:
3245 # Report non cr Log function calls in changed lines
3246 for line_num, line in f.ChangedContents():
3247 if log_call_pattern.search(line):
3248 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgnaa68d5e2015-06-10 10:08:223249
3250 # Per file checks
3251 if has_modified_logs:
3252 # Make sure the tag is using the "cr" prefix and is not too long
3253 match = log_decl_pattern.search(file_content)
dgn38736db2015-09-18 19:20:513254 tag_name = match.group('name') if match else None
3255 if not tag_name:
dgnaa68d5e2015-06-10 10:08:223256 tag_decl_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:513257 elif len(tag_name) > 20:
dgnaa68d5e2015-06-10 10:08:223258 tag_length_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:513259 elif '.' in tag_name:
3260 tag_with_dot_errors.append(f.LocalPath())
dgnaa68d5e2015-06-10 10:08:223261
3262 results = []
3263 if tag_decl_errors:
3264 results.append(output_api.PresubmitPromptWarning(
3265 'Please define your tags using the suggested format: .\n'
dgn38736db2015-09-18 19:20:513266 '"private static final String TAG = "<package tag>".\n'
3267 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:223268 tag_decl_errors))
3269
3270 if tag_length_errors:
3271 results.append(output_api.PresubmitError(
3272 'The tag length is restricted by the system to be at most '
dgn38736db2015-09-18 19:20:513273 '20 characters.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:223274 tag_length_errors))
3275
3276 if tag_errors:
3277 results.append(output_api.PresubmitPromptWarning(
3278 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
3279 tag_errors))
3280
dgn87d9fb62015-06-12 09:15:123281 if util_log_errors:
dgn4401aa52015-04-29 16:26:173282 results.append(output_api.PresubmitPromptWarning(
dgn87d9fb62015-06-12 09:15:123283 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
3284 util_log_errors))
3285
dgn38736db2015-09-18 19:20:513286 if tag_with_dot_errors:
3287 results.append(output_api.PresubmitPromptWarning(
3288 'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
3289 tag_with_dot_errors))
3290
dgn4401aa52015-04-29 16:26:173291 return results
3292
3293
Yoland Yanb92fa522017-08-28 17:37:063294def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
3295 """Checks that junit.framework.* is no longer used."""
3296 deprecated_junit_framework_pattern = input_api.re.compile(
3297 r'^import junit\.framework\..*;',
3298 input_api.re.MULTILINE)
3299 sources = lambda x: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:443300 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
Yoland Yanb92fa522017-08-28 17:37:063301 errors = []
Edward Lemur7bbfdf12020-01-15 02:06:133302 for f in input_api.AffectedFiles(file_filter=sources):
Yoland Yanb92fa522017-08-28 17:37:063303 for line_num, line in f.ChangedContents():
3304 if deprecated_junit_framework_pattern.search(line):
3305 errors.append("%s:%d" % (f.LocalPath(), line_num))
3306
3307 results = []
3308 if errors:
3309 results.append(output_api.PresubmitError(
3310 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
3311 '(org.junit.*) from //third_party/junit. Contact [email protected]'
3312 ' if you have any question.', errors))
3313 return results
3314
3315
3316def _CheckAndroidTestJUnitInheritance(input_api, output_api):
3317 """Checks that if new Java test classes have inheritance.
3318 Either the new test class is JUnit3 test or it is a JUnit4 test class
3319 with a base class, either case is undesirable.
3320 """
3321 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
3322
3323 sources = lambda x: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:443324 x, files_to_check=[r'.*Test\.java$'], files_to_skip=None)
Yoland Yanb92fa522017-08-28 17:37:063325 errors = []
Edward Lemur7bbfdf12020-01-15 02:06:133326 for f in input_api.AffectedFiles(file_filter=sources):
Yoland Yanb92fa522017-08-28 17:37:063327 if not f.OldContents():
3328 class_declaration_start_flag = False
3329 for line_num, line in f.ChangedContents():
3330 if class_declaration_pattern.search(line):
3331 class_declaration_start_flag = True
3332 if class_declaration_start_flag and ' extends ' in line:
3333 errors.append('%s:%d' % (f.LocalPath(), line_num))
3334 if '{' in line:
3335 class_declaration_start_flag = False
3336
3337 results = []
3338 if errors:
3339 results.append(output_api.PresubmitPromptWarning(
3340 'The newly created files include Test classes that inherits from base'
3341 ' class. Please do not use inheritance in JUnit4 tests or add new'
3342 ' JUnit3 tests. Contact [email protected] if you have any'
3343 ' questions.', errors))
3344 return results
3345
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:203346
yolandyan45001472016-12-21 21:12:423347def _CheckAndroidTestAnnotationUsage(input_api, output_api):
3348 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
3349 deprecated_annotation_import_pattern = input_api.re.compile(
3350 r'^import android\.test\.suitebuilder\.annotation\..*;',
3351 input_api.re.MULTILINE)
3352 sources = lambda x: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:443353 x, files_to_check=[r'.*\.java$'], files_to_skip=None)
yolandyan45001472016-12-21 21:12:423354 errors = []
Edward Lemur7bbfdf12020-01-15 02:06:133355 for f in input_api.AffectedFiles(file_filter=sources):
yolandyan45001472016-12-21 21:12:423356 for line_num, line in f.ChangedContents():
3357 if deprecated_annotation_import_pattern.search(line):
3358 errors.append("%s:%d" % (f.LocalPath(), line_num))
3359
3360 results = []
3361 if errors:
3362 results.append(output_api.PresubmitError(
3363 'Annotations in android.test.suitebuilder.annotation have been'
3364 ' deprecated since API level 24. Please use android.support.test.filters'
3365 ' from //third_party/android_support_test_runner:runner_java instead.'
3366 ' Contact [email protected] if you have any questions.', errors))
3367 return results
3368
3369
agrieve7b6479d82015-10-07 14:24:223370def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
3371 """Checks if MDPI assets are placed in a correct directory."""
3372 file_filter = lambda f: (f.LocalPath().endswith('.png') and
3373 ('/res/drawable/' in f.LocalPath() or
3374 '/res/drawable-ldrtl/' in f.LocalPath()))
3375 errors = []
3376 for f in input_api.AffectedFiles(include_deletes=False,
3377 file_filter=file_filter):
3378 errors.append(' %s' % f.LocalPath())
3379
3380 results = []
3381 if errors:
3382 results.append(output_api.PresubmitError(
3383 'MDPI assets should be placed in /res/drawable-mdpi/ or '
3384 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
3385 '/res/drawable-ldrtl/.\n'
3386 'Contact [email protected] if you have questions.', errors))
3387 return results
3388
3389
Nate Fischer535972b2017-09-16 01:06:183390def _CheckAndroidWebkitImports(input_api, output_api):
3391 """Checks that code uses org.chromium.base.Callback instead of
Bo Liubfde1c02019-09-24 23:08:353392 android.webview.ValueCallback except in the WebView glue layer
3393 and WebLayer.
Nate Fischer535972b2017-09-16 01:06:183394 """
3395 valuecallback_import_pattern = input_api.re.compile(
3396 r'^import android\.webkit\.ValueCallback;$')
3397
3398 errors = []
3399
3400 sources = lambda affected_file: input_api.FilterSourceFile(
3401 affected_file,
James Cook24a504192020-07-23 00:08:443402 files_to_skip=(_EXCLUDED_PATHS +
3403 _TEST_CODE_EXCLUDED_PATHS +
3404 input_api.DEFAULT_FILES_TO_SKIP +
3405 (r'^android_webview[\\/]glue[\\/].*',
3406 r'^weblayer[\\/].*',)),
3407 files_to_check=[r'.*\.java$'])
Nate Fischer535972b2017-09-16 01:06:183408
3409 for f in input_api.AffectedSourceFiles(sources):
3410 for line_num, line in f.ChangedContents():
3411 if valuecallback_import_pattern.search(line):
3412 errors.append("%s:%d" % (f.LocalPath(), line_num))
3413
3414 results = []
3415
3416 if errors:
3417 results.append(output_api.PresubmitError(
3418 'android.webkit.ValueCallback usage is detected outside of the glue'
3419 ' layer. To stay compatible with the support library, android.webkit.*'
3420 ' classes should only be used inside the glue layer and'
3421 ' org.chromium.base.Callback should be used instead.',
3422 errors))
3423
3424 return results
3425
3426
Becky Zhou7c69b50992018-12-10 19:37:573427def _CheckAndroidXmlStyle(input_api, output_api, is_check_on_upload):
3428 """Checks Android XML styles """
3429 import sys
3430 original_sys_path = sys.path
3431 try:
3432 sys.path = sys.path + [input_api.os_path.join(
3433 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkxmlstyle')]
3434 import checkxmlstyle
3435 finally:
3436 # Restore sys.path to what it was before.
3437 sys.path = original_sys_path
3438
3439 if is_check_on_upload:
3440 return checkxmlstyle.CheckStyleOnUpload(input_api, output_api)
3441 else:
3442 return checkxmlstyle.CheckStyleOnCommit(input_api, output_api)
3443
3444
agrievef32bcc72016-04-04 14:57:403445class PydepsChecker(object):
3446 def __init__(self, input_api, pydeps_files):
3447 self._file_cache = {}
3448 self._input_api = input_api
3449 self._pydeps_files = pydeps_files
3450
3451 def _LoadFile(self, path):
3452 """Returns the list of paths within a .pydeps file relative to //."""
3453 if path not in self._file_cache:
3454 with open(path) as f:
3455 self._file_cache[path] = f.read()
3456 return self._file_cache[path]
3457
3458 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
3459 """Returns an interable of paths within the .pydep, relativized to //."""
Andrew Grieve5bb4cf702020-10-22 20:21:393460 pydeps_data = self._LoadFile(pydeps_path)
3461 uses_gn_paths = '--gn-paths' in pydeps_data
3462 entries = (l for l in pydeps_data.splitlines() if not l.startswith('#'))
3463 if uses_gn_paths:
3464 # Paths look like: //foo/bar/baz
3465 return (e[2:] for e in entries)
3466 else:
3467 # Paths look like: path/relative/to/file.pydeps
3468 os_path = self._input_api.os_path
3469 pydeps_dir = os_path.dirname(pydeps_path)
3470 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries)
agrievef32bcc72016-04-04 14:57:403471
3472 def _CreateFilesToPydepsMap(self):
3473 """Returns a map of local_path -> list_of_pydeps."""
3474 ret = {}
3475 for pydep_local_path in self._pydeps_files:
3476 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
3477 ret.setdefault(path, []).append(pydep_local_path)
3478 return ret
3479
3480 def ComputeAffectedPydeps(self):
3481 """Returns an iterable of .pydeps files that might need regenerating."""
3482 affected_pydeps = set()
3483 file_to_pydeps_map = None
3484 for f in self._input_api.AffectedFiles(include_deletes=True):
3485 local_path = f.LocalPath()
Andrew Grieve892bb3f2019-03-20 17:33:463486 # Changes to DEPS can lead to .pydeps changes if any .py files are in
3487 # subrepositories. We can't figure out which files change, so re-check
3488 # all files.
3489 # Changes to print_python_deps.py affect all .pydeps.
Andrew Grieveb773bad2020-06-05 18:00:383490 if local_path in ('DEPS', 'PRESUBMIT.py') or local_path.endswith(
3491 'print_python_deps.py'):
agrievef32bcc72016-04-04 14:57:403492 return self._pydeps_files
3493 elif local_path.endswith('.pydeps'):
3494 if local_path in self._pydeps_files:
3495 affected_pydeps.add(local_path)
3496 elif local_path.endswith('.py'):
3497 if file_to_pydeps_map is None:
3498 file_to_pydeps_map = self._CreateFilesToPydepsMap()
3499 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
3500 return affected_pydeps
3501
3502 def DetermineIfStale(self, pydeps_path):
3503 """Runs print_python_deps.py to see if the files is stale."""
phajdan.jr0d9878552016-11-04 10:49:413504 import difflib
John Budorick47ca3fe2018-02-10 00:53:103505 import os
3506
agrievef32bcc72016-04-04 14:57:403507 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
Mohamed Heikale217fc852020-07-06 19:44:033508 if old_pydeps_data:
3509 cmd = old_pydeps_data[1][1:].strip()
Andrew Grieve5bb4cf702020-10-22 20:21:393510 if '--output' not in cmd:
3511 cmd += ' --output ' + pydeps_path
Mohamed Heikale217fc852020-07-06 19:44:033512 old_contents = old_pydeps_data[2:]
3513 else:
3514 # A default cmd that should work in most cases (as long as pydeps filename
3515 # matches the script name) so that PRESUBMIT.py does not crash if pydeps
3516 # file is empty/new.
3517 cmd = 'build/print_python_deps.py {} --root={} --output={}'.format(
3518 pydeps_path[:-4], os.path.dirname(pydeps_path), pydeps_path)
3519 old_contents = []
John Budorick47ca3fe2018-02-10 00:53:103520 env = dict(os.environ)
3521 env['PYTHONDONTWRITEBYTECODE'] = '1'
agrievef32bcc72016-04-04 14:57:403522 new_pydeps_data = self._input_api.subprocess.check_output(
John Budorick47ca3fe2018-02-10 00:53:103523 cmd + ' --output ""', shell=True, env=env)
phajdan.jr0d9878552016-11-04 10:49:413524 new_contents = new_pydeps_data.splitlines()[2:]
Mohamed Heikale217fc852020-07-06 19:44:033525 if old_contents != new_contents:
phajdan.jr0d9878552016-11-04 10:49:413526 return cmd, '\n'.join(difflib.context_diff(old_contents, new_contents))
agrievef32bcc72016-04-04 14:57:403527
3528
Tibor Goldschwendt360793f72019-06-25 18:23:493529def _ParseGclientArgs():
3530 args = {}
3531 with open('build/config/gclient_args.gni', 'r') as f:
3532 for line in f:
3533 line = line.strip()
3534 if not line or line.startswith('#'):
3535 continue
3536 attribute, value = line.split('=')
3537 args[attribute.strip()] = value.strip()
3538 return args
3539
3540
Saagar Sanghavifceeaae2020-08-12 16:40:363541def CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
agrievef32bcc72016-04-04 14:57:403542 """Checks if a .pydeps file needs to be regenerated."""
John Chencde89192018-01-27 21:18:403543 # This check is for Python dependency lists (.pydeps files), and involves
3544 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
3545 # doesn't work on Windows and Mac, so skip it on other platforms.
agrieve9bc4200b2016-05-04 16:33:283546 if input_api.platform != 'linux2':
agrievebb9c5b472016-04-22 15:13:003547 return []
Tibor Goldschwendt360793f72019-06-25 18:23:493548 is_android = _ParseGclientArgs().get('checkout_android', 'false') == 'true'
Mohamed Heikal7cd4d8312020-06-16 16:49:403549 pydeps_to_check = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
agrievef32bcc72016-04-04 14:57:403550 results = []
3551 # First, check for new / deleted .pydeps.
3552 for f in input_api.AffectedFiles(include_deletes=True):
Zhiling Huang45cabf32018-03-10 00:50:033553 # Check whether we are running the presubmit check for a file in src.
3554 # f.LocalPath is relative to repo (src, or internal repo).
3555 # os_path.exists is relative to src repo.
3556 # Therefore if os_path.exists is true, it means f.LocalPath is relative
3557 # to src and we can conclude that the pydeps is in src.
3558 if input_api.os_path.exists(f.LocalPath()):
3559 if f.LocalPath().endswith('.pydeps'):
3560 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
3561 results.append(output_api.PresubmitError(
3562 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
3563 'remove %s' % f.LocalPath()))
3564 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES:
3565 results.append(output_api.PresubmitError(
3566 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
3567 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:403568
3569 if results:
3570 return results
3571
Mohamed Heikal7cd4d8312020-06-16 16:49:403572 checker = checker_for_tests or PydepsChecker(input_api, _ALL_PYDEPS_FILES)
3573 affected_pydeps = set(checker.ComputeAffectedPydeps())
3574 affected_android_pydeps = affected_pydeps.intersection(
3575 set(_ANDROID_SPECIFIC_PYDEPS_FILES))
3576 if affected_android_pydeps and not is_android:
3577 results.append(output_api.PresubmitPromptOrNotify(
3578 'You have changed python files that may affect pydeps for android\n'
3579 'specific scripts. However, the relevant presumbit check cannot be\n'
3580 'run because you are not using an Android checkout. To validate that\n'
3581 'the .pydeps are correct, re-run presubmit in an Android checkout, or\n'
3582 'use the android-internal-presubmit optional trybot.\n'
3583 'Possibly stale pydeps files:\n{}'.format(
3584 '\n'.join(affected_android_pydeps))))
agrievef32bcc72016-04-04 14:57:403585
Mohamed Heikal7cd4d8312020-06-16 16:49:403586 affected_pydeps_to_check = affected_pydeps.intersection(set(pydeps_to_check))
3587 for pydep_path in affected_pydeps_to_check:
agrievef32bcc72016-04-04 14:57:403588 try:
phajdan.jr0d9878552016-11-04 10:49:413589 result = checker.DetermineIfStale(pydep_path)
3590 if result:
3591 cmd, diff = result
agrievef32bcc72016-04-04 14:57:403592 results.append(output_api.PresubmitError(
phajdan.jr0d9878552016-11-04 10:49:413593 'File is stale: %s\nDiff (apply to fix):\n%s\n'
3594 'To regenerate, run:\n\n %s' %
3595 (pydep_path, diff, cmd)))
agrievef32bcc72016-04-04 14:57:403596 except input_api.subprocess.CalledProcessError as error:
3597 return [output_api.PresubmitError('Error running: %s' % error.cmd,
3598 long_text=error.output)]
3599
3600 return results
3601
3602
Saagar Sanghavifceeaae2020-08-12 16:40:363603def CheckSingletonInHeaders(input_api, output_api):
glidere61efad2015-02-18 17:39:433604 """Checks to make sure no header files have |Singleton<|."""
3605 def FileFilter(affected_file):
3606 # It's ok for base/memory/singleton.h to have |Singleton<|.
James Cook24a504192020-07-23 00:08:443607 files_to_skip = (_EXCLUDED_PATHS +
3608 input_api.DEFAULT_FILES_TO_SKIP +
3609 (r"^base[\\/]memory[\\/]singleton\.h$",
3610 r"^net[\\/]quic[\\/]platform[\\/]impl[\\/]"
3611 r"quic_singleton_impl\.h$"))
3612 return input_api.FilterSourceFile(affected_file,
3613 files_to_skip=files_to_skip)
glidere61efad2015-02-18 17:39:433614
sergeyu34d21222015-09-16 00:11:443615 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
glidere61efad2015-02-18 17:39:433616 files = []
3617 for f in input_api.AffectedSourceFiles(FileFilter):
3618 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
3619 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
3620 contents = input_api.ReadFile(f)
3621 for line in contents.splitlines(False):
oysteinec430ad42015-10-22 20:55:243622 if (not line.lstrip().startswith('//') and # Strip C++ comment.
glidere61efad2015-02-18 17:39:433623 pattern.search(line)):
3624 files.append(f)
3625 break
3626
3627 if files:
yolandyandaabc6d2016-04-18 18:29:393628 return [output_api.PresubmitError(
sergeyu34d21222015-09-16 00:11:443629 'Found base::Singleton<T> in the following header files.\n' +
glidere61efad2015-02-18 17:39:433630 'Please move them to an appropriate source file so that the ' +
3631 'template gets instantiated in a single compilation unit.',
3632 files) ]
3633 return []
3634
3635
[email protected]fd20b902014-05-09 02:14:533636_DEPRECATED_CSS = [
3637 # Values
3638 ( "-webkit-box", "flex" ),
3639 ( "-webkit-inline-box", "inline-flex" ),
3640 ( "-webkit-flex", "flex" ),
3641 ( "-webkit-inline-flex", "inline-flex" ),
3642 ( "-webkit-min-content", "min-content" ),
3643 ( "-webkit-max-content", "max-content" ),
3644
3645 # Properties
3646 ( "-webkit-background-clip", "background-clip" ),
3647 ( "-webkit-background-origin", "background-origin" ),
3648 ( "-webkit-background-size", "background-size" ),
3649 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:443650 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:533651
3652 # Functions
3653 ( "-webkit-gradient", "gradient" ),
3654 ( "-webkit-repeating-gradient", "repeating-gradient" ),
3655 ( "-webkit-linear-gradient", "linear-gradient" ),
3656 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
3657 ( "-webkit-radial-gradient", "radial-gradient" ),
3658 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
3659]
3660
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:203661
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:493662# TODO: add unit tests
Saagar Sanghavifceeaae2020-08-12 16:40:363663def CheckNoDeprecatedCss(input_api, output_api):
[email protected]fd20b902014-05-09 02:14:533664 """ Make sure that we don't use deprecated CSS
[email protected]9a48e3f82014-05-22 00:06:253665 properties, functions or values. Our external
mdjonesae0286c32015-06-10 18:10:343666 documentation and iOS CSS for dom distiller
3667 (reader mode) are ignored by the hooks as it
[email protected]9a48e3f82014-05-22 00:06:253668 needs to be consumed by WebKit. """
[email protected]fd20b902014-05-09 02:14:533669 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:493670 file_inclusion_pattern = [r".+\.css$"]
James Cook24a504192020-07-23 00:08:443671 files_to_skip = (_EXCLUDED_PATHS +
3672 _TEST_CODE_EXCLUDED_PATHS +
3673 input_api.DEFAULT_FILES_TO_SKIP +
3674 (r"^chrome/common/extensions/docs",
3675 r"^chrome/docs",
3676 r"^components/dom_distiller/core/css/distilledpage_ios.css",
3677 r"^components/neterror/resources/neterror.css",
3678 r"^native_client_sdk"))
[email protected]9a48e3f82014-05-22 00:06:253679 file_filter = lambda f: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:443680 f, files_to_check=file_inclusion_pattern, files_to_skip=files_to_skip)
[email protected]fd20b902014-05-09 02:14:533681 for fpath in input_api.AffectedFiles(file_filter=file_filter):
3682 for line_num, line in fpath.ChangedContents():
3683 for (deprecated_value, value) in _DEPRECATED_CSS:
dbeam070cfe62014-10-22 06:44:023684 if deprecated_value in line:
[email protected]fd20b902014-05-09 02:14:533685 results.append(output_api.PresubmitError(
3686 "%s:%d: Use of deprecated CSS %s, use %s instead" %
3687 (fpath.LocalPath(), line_num, deprecated_value, value)))
3688 return results
3689
mohan.reddyf21db962014-10-16 12:26:473690
Saagar Sanghavifceeaae2020-08-12 16:40:363691def CheckForRelativeIncludes(input_api, output_api):
rlanday6802cf632017-05-30 17:48:363692 bad_files = {}
3693 for f in input_api.AffectedFiles(include_deletes=False):
3694 if (f.LocalPath().startswith('third_party') and
Kent Tamura32dbbcb2018-11-30 12:28:493695 not f.LocalPath().startswith('third_party/blink') and
3696 not f.LocalPath().startswith('third_party\\blink')):
rlanday6802cf632017-05-30 17:48:363697 continue
3698
Daniel Bratell65b033262019-04-23 08:17:063699 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
rlanday6802cf632017-05-30 17:48:363700 continue
3701
Vaclav Brozekd5de76a2018-03-17 07:57:503702 relative_includes = [line for _, line in f.ChangedContents()
rlanday6802cf632017-05-30 17:48:363703 if "#include" in line and "../" in line]
3704 if not relative_includes:
3705 continue
3706 bad_files[f.LocalPath()] = relative_includes
3707
3708 if not bad_files:
3709 return []
3710
3711 error_descriptions = []
Dirk Prankee3c9c62d2021-05-18 18:35:593712 for file_path, bad_lines in bad_files.items():
rlanday6802cf632017-05-30 17:48:363713 error_description = file_path
3714 for line in bad_lines:
3715 error_description += '\n ' + line
3716 error_descriptions.append(error_description)
3717
3718 results = []
3719 results.append(output_api.PresubmitError(
3720 'You added one or more relative #include paths (including "../").\n'
3721 'These shouldn\'t be used because they can be used to include headers\n'
3722 'from code that\'s not correctly specified as a dependency in the\n'
3723 'relevant BUILD.gn file(s).',
3724 error_descriptions))
3725
3726 return results
3727
Takeshi Yoshinoe387aa32017-08-02 13:16:133728
Saagar Sanghavifceeaae2020-08-12 16:40:363729def CheckForCcIncludes(input_api, output_api):
Daniel Bratell65b033262019-04-23 08:17:063730 """Check that nobody tries to include a cc file. It's a relatively
3731 common error which results in duplicate symbols in object
3732 files. This may not always break the build until someone later gets
3733 very confusing linking errors."""
3734 results = []
3735 for f in input_api.AffectedFiles(include_deletes=False):
3736 # We let third_party code do whatever it wants
3737 if (f.LocalPath().startswith('third_party') and
3738 not f.LocalPath().startswith('third_party/blink') and
3739 not f.LocalPath().startswith('third_party\\blink')):
3740 continue
3741
3742 if not _IsCPlusPlusFile(input_api, f.LocalPath()):
3743 continue
3744
3745 for _, line in f.ChangedContents():
3746 if line.startswith('#include "'):
3747 included_file = line.split('"')[1]
3748 if _IsCPlusPlusFile(input_api, included_file):
3749 # The most common naming for external files with C++ code,
3750 # apart from standard headers, is to call them foo.inc, but
3751 # Chromium sometimes uses foo-inc.cc so allow that as well.
3752 if not included_file.endswith(('.h', '-inc.cc')):
3753 results.append(output_api.PresubmitError(
3754 'Only header files or .inc files should be included in other\n'
3755 'C++ files. Compiling the contents of a cc file more than once\n'
3756 'will cause duplicate information in the build which may later\n'
3757 'result in strange link_errors.\n' +
3758 f.LocalPath() + ':\n ' +
3759 line))
3760
3761 return results
3762
3763
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203764def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
3765 if not isinstance(key, ast.Str):
3766 return 'Key at line %d must be a string literal' % key.lineno
3767 if not isinstance(value, ast.Dict):
3768 return 'Value at line %d must be a dict' % value.lineno
3769 if len(value.keys) != 1:
3770 return 'Dict at line %d must have single entry' % value.lineno
3771 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
3772 return (
3773 'Entry at line %d must have a string literal \'filepath\' as key' %
3774 value.lineno)
3775 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:133776
Takeshi Yoshinoe387aa32017-08-02 13:16:133777
Sergey Ulanov4af16052018-11-08 02:41:463778def _CheckWatchlistsEntrySyntax(key, value, ast, email_regex):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203779 if not isinstance(key, ast.Str):
3780 return 'Key at line %d must be a string literal' % key.lineno
3781 if not isinstance(value, ast.List):
3782 return 'Value at line %d must be a list' % value.lineno
Sergey Ulanov4af16052018-11-08 02:41:463783 for element in value.elts:
3784 if not isinstance(element, ast.Str):
3785 return 'Watchlist elements on line %d is not a string' % key.lineno
3786 if not email_regex.match(element.s):
3787 return ('Watchlist element on line %d doesn\'t look like a valid ' +
3788 'email: %s') % (key.lineno, element.s)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203789 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:133790
Takeshi Yoshinoe387aa32017-08-02 13:16:133791
Sergey Ulanov4af16052018-11-08 02:41:463792def _CheckWATCHLISTSEntries(wd_dict, w_dict, input_api):
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203793 mismatch_template = (
3794 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
3795 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:133796
Sergey Ulanov4af16052018-11-08 02:41:463797 email_regex = input_api.re.compile(
3798 r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$")
3799
3800 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203801 i = 0
3802 last_key = ''
3803 while True:
3804 if i >= len(wd_dict.keys):
3805 if i >= len(w_dict.keys):
3806 return None
3807 return mismatch_template % ('missing', 'line %d' % w_dict.keys[i].lineno)
3808 elif i >= len(w_dict.keys):
3809 return (
3810 mismatch_template % ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:133811
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203812 wd_key = wd_dict.keys[i]
3813 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:133814
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203815 result = _CheckWatchlistDefinitionsEntrySyntax(
3816 wd_key, wd_dict.values[i], ast)
3817 if result is not None:
3818 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:133819
Sergey Ulanov4af16052018-11-08 02:41:463820 result = _CheckWatchlistsEntrySyntax(
3821 w_key, w_dict.values[i], ast, email_regex)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203822 if result is not None:
3823 return 'Bad entry in WATCHLISTS dict: %s' % result
3824
3825 if wd_key.s != w_key.s:
3826 return mismatch_template % (
3827 '%s at line %d' % (wd_key.s, wd_key.lineno),
3828 '%s at line %d' % (w_key.s, w_key.lineno))
3829
3830 if wd_key.s < last_key:
3831 return (
3832 'WATCHLISTS dict is not sorted lexicographically at line %d and %d' %
3833 (wd_key.lineno, w_key.lineno))
3834 last_key = wd_key.s
3835
3836 i = i + 1
3837
3838
Sergey Ulanov4af16052018-11-08 02:41:463839def _CheckWATCHLISTSSyntax(expression, input_api):
3840 ast = input_api.ast
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203841 if not isinstance(expression, ast.Expression):
3842 return 'WATCHLISTS file must contain a valid expression'
3843 dictionary = expression.body
3844 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
3845 return 'WATCHLISTS file must have single dict with exactly two entries'
3846
3847 first_key = dictionary.keys[0]
3848 first_value = dictionary.values[0]
3849 second_key = dictionary.keys[1]
3850 second_value = dictionary.values[1]
3851
3852 if (not isinstance(first_key, ast.Str) or
3853 first_key.s != 'WATCHLIST_DEFINITIONS' or
3854 not isinstance(first_value, ast.Dict)):
3855 return (
3856 'The first entry of the dict in WATCHLISTS file must be '
3857 'WATCHLIST_DEFINITIONS dict')
3858
3859 if (not isinstance(second_key, ast.Str) or
3860 second_key.s != 'WATCHLISTS' or
3861 not isinstance(second_value, ast.Dict)):
3862 return (
3863 'The second entry of the dict in WATCHLISTS file must be '
3864 'WATCHLISTS dict')
3865
Sergey Ulanov4af16052018-11-08 02:41:463866 return _CheckWATCHLISTSEntries(first_value, second_value, input_api)
Takeshi Yoshinoe387aa32017-08-02 13:16:133867
3868
Saagar Sanghavifceeaae2020-08-12 16:40:363869def CheckWATCHLISTS(input_api, output_api):
Takeshi Yoshinoe387aa32017-08-02 13:16:133870 for f in input_api.AffectedFiles(include_deletes=False):
3871 if f.LocalPath() == 'WATCHLISTS':
3872 contents = input_api.ReadFile(f, 'r')
3873
3874 try:
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203875 # First, make sure that it can be evaluated.
Takeshi Yoshinoe387aa32017-08-02 13:16:133876 input_api.ast.literal_eval(contents)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203877 # Get an AST tree for it and scan the tree for detailed style checking.
3878 expression = input_api.ast.parse(
3879 contents, filename='WATCHLISTS', mode='eval')
3880 except ValueError as e:
3881 return [output_api.PresubmitError(
3882 'Cannot parse WATCHLISTS file', long_text=repr(e))]
3883 except SyntaxError as e:
3884 return [output_api.PresubmitError(
3885 'Cannot parse WATCHLISTS file', long_text=repr(e))]
3886 except TypeError as e:
3887 return [output_api.PresubmitError(
3888 'Cannot parse WATCHLISTS file', long_text=repr(e))]
Takeshi Yoshinoe387aa32017-08-02 13:16:133889
Sergey Ulanov4af16052018-11-08 02:41:463890 result = _CheckWATCHLISTSSyntax(expression, input_api)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:203891 if result is not None:
3892 return [output_api.PresubmitError(result)]
3893 break
Takeshi Yoshinoe387aa32017-08-02 13:16:133894
3895 return []
3896
3897
Andrew Grieve1b290e4a22020-11-24 20:07:013898def CheckGnGlobForward(input_api, output_api):
3899 """Checks that forward_variables_from(invoker, "*") follows best practices.
3900
3901 As documented at //build/docs/writing_gn_templates.md
3902 """
3903 def gn_files(f):
3904 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gni', ))
3905
3906 problems = []
3907 for f in input_api.AffectedSourceFiles(gn_files):
3908 for line_num, line in f.ChangedContents():
3909 if 'forward_variables_from(invoker, "*")' in line:
3910 problems.append(
3911 'Bare forward_variables_from(invoker, "*") in %s:%d' % (
3912 f.LocalPath(), line_num))
3913
3914 if problems:
3915 return [output_api.PresubmitPromptWarning(
3916 'forward_variables_from("*") without exclusions',
3917 items=sorted(problems),
3918 long_text=('The variables "visibilty" and "test_only" should be '
3919 'explicitly listed in forward_variables_from(). For more '
3920 'details, see:\n'
3921 'https://chromium.googlesource.com/chromium/src/+/HEAD/'
3922 'build/docs/writing_gn_templates.md'
3923 '#Using-forward_variables_from'))]
3924 return []
3925
3926
Saagar Sanghavifceeaae2020-08-12 16:40:363927def CheckNewHeaderWithoutGnChangeOnUpload(input_api, output_api):
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193928 """Checks that newly added header files have corresponding GN changes.
3929 Note that this is only a heuristic. To be precise, run script:
3930 build/check_gn_headers.py.
3931 """
3932
3933 def headers(f):
3934 return input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:443935 f, files_to_check=(r'.+%s' % _HEADER_EXTENSIONS, ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193936
3937 new_headers = []
3938 for f in input_api.AffectedSourceFiles(headers):
3939 if f.Action() != 'A':
3940 continue
3941 new_headers.append(f.LocalPath())
3942
3943 def gn_files(f):
James Cook24a504192020-07-23 00:08:443944 return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gn', ))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193945
3946 all_gn_changed_contents = ''
3947 for f in input_api.AffectedSourceFiles(gn_files):
3948 for _, line in f.ChangedContents():
3949 all_gn_changed_contents += line
3950
3951 problems = []
3952 for header in new_headers:
3953 basename = input_api.os_path.basename(header)
3954 if basename not in all_gn_changed_contents:
3955 problems.append(header)
3956
3957 if problems:
3958 return [output_api.PresubmitPromptWarning(
3959 'Missing GN changes for new header files', items=sorted(problems),
3960 long_text='Please double check whether newly added header files need '
3961 'corresponding changes in gn or gni files.\nThis checking is only a '
3962 'heuristic. Run build/check_gn_headers.py to be precise.\n'
3963 'Read https://crbug.com/661774 for more info.')]
3964 return []
3965
3966
Saagar Sanghavifceeaae2020-08-12 16:40:363967def CheckCorrectProductNameInMessages(input_api, output_api):
Michael Giuffridad3bc8672018-10-25 22:48:023968 """Check that Chromium-branded strings don't include "Chrome" or vice versa.
3969
3970 This assumes we won't intentionally reference one product from the other
3971 product.
3972 """
3973 all_problems = []
3974 test_cases = [{
3975 "filename_postfix": "google_chrome_strings.grd",
3976 "correct_name": "Chrome",
3977 "incorrect_name": "Chromium",
3978 }, {
3979 "filename_postfix": "chromium_strings.grd",
3980 "correct_name": "Chromium",
3981 "incorrect_name": "Chrome",
3982 }]
3983
3984 for test_case in test_cases:
3985 problems = []
3986 filename_filter = lambda x: x.LocalPath().endswith(
3987 test_case["filename_postfix"])
3988
3989 # Check each new line. Can yield false positives in multiline comments, but
3990 # easier than trying to parse the XML because messages can have nested
3991 # children, and associating message elements with affected lines is hard.
3992 for f in input_api.AffectedSourceFiles(filename_filter):
3993 for line_num, line in f.ChangedContents():
3994 if "<message" in line or "<!--" in line or "-->" in line:
3995 continue
3996 if test_case["incorrect_name"] in line:
3997 problems.append(
3998 "Incorrect product name in %s:%d" % (f.LocalPath(), line_num))
3999
4000 if problems:
4001 message = (
4002 "Strings in %s-branded string files should reference \"%s\", not \"%s\""
4003 % (test_case["correct_name"], test_case["correct_name"],
4004 test_case["incorrect_name"]))
4005 all_problems.append(
4006 output_api.PresubmitPromptWarning(message, items=problems))
4007
4008 return all_problems
4009
4010
Saagar Sanghavifceeaae2020-08-12 16:40:364011def CheckForTooLargeFiles(input_api, output_api):
Daniel Bratell93eb6c62019-04-29 20:13:364012 """Avoid large files, especially binary files, in the repository since
4013 git doesn't scale well for those. They will be in everyone's repo
4014 clones forever, forever making Chromium slower to clone and work
4015 with."""
4016
4017 # Uploading files to cloud storage is not trivial so we don't want
4018 # to set the limit too low, but the upper limit for "normal" large
4019 # files seems to be 1-2 MB, with a handful around 5-8 MB, so
4020 # anything over 20 MB is exceptional.
4021 TOO_LARGE_FILE_SIZE_LIMIT = 20 * 1024 * 1024 # 10 MB
4022
4023 too_large_files = []
4024 for f in input_api.AffectedFiles():
4025 # Check both added and modified files (but not deleted files).
4026 if f.Action() in ('A', 'M'):
Dirk Pranked6d45c32019-04-30 22:37:384027 size = input_api.os_path.getsize(f.AbsoluteLocalPath())
Daniel Bratell93eb6c62019-04-29 20:13:364028 if size > TOO_LARGE_FILE_SIZE_LIMIT:
4029 too_large_files.append("%s: %d bytes" % (f.LocalPath(), size))
4030
4031 if too_large_files:
4032 message = (
4033 'Do not commit large files to git since git scales badly for those.\n' +
4034 'Instead put the large files in cloud storage and use DEPS to\n' +
4035 'fetch them.\n' + '\n'.join(too_large_files)
4036 )
4037 return [output_api.PresubmitError(
4038 'Too large files found in commit', long_text=message + '\n')]
4039 else:
4040 return []
4041
Max Morozb47503b2019-08-08 21:03:274042
Saagar Sanghavifceeaae2020-08-12 16:40:364043def CheckFuzzTargetsOnUpload(input_api, output_api):
Max Morozb47503b2019-08-08 21:03:274044 """Checks specific for fuzz target sources."""
4045 EXPORTED_SYMBOLS = [
4046 'LLVMFuzzerInitialize',
4047 'LLVMFuzzerCustomMutator',
4048 'LLVMFuzzerCustomCrossOver',
4049 'LLVMFuzzerMutate',
4050 ]
4051
4052 REQUIRED_HEADER = '#include "testing/libfuzzer/libfuzzer_exports.h"'
4053
4054 def FilterFile(affected_file):
4055 """Ignore libFuzzer source code."""
James Cook24a504192020-07-23 00:08:444056 files_to_check = r'.*fuzz.*\.(h|hpp|hcc|cc|cpp|cxx)$'
4057 files_to_skip = r"^third_party[\\/]libFuzzer"
Max Morozb47503b2019-08-08 21:03:274058
4059 return input_api.FilterSourceFile(
4060 affected_file,
James Cook24a504192020-07-23 00:08:444061 files_to_check=[files_to_check],
4062 files_to_skip=[files_to_skip])
Max Morozb47503b2019-08-08 21:03:274063
4064 files_with_missing_header = []
4065 for f in input_api.AffectedSourceFiles(FilterFile):
4066 contents = input_api.ReadFile(f, 'r')
4067 if REQUIRED_HEADER in contents:
4068 continue
4069
4070 if any(symbol in contents for symbol in EXPORTED_SYMBOLS):
4071 files_with_missing_header.append(f.LocalPath())
4072
4073 if not files_with_missing_header:
4074 return []
4075
4076 long_text = (
4077 'If you define any of the libFuzzer optional functions (%s), it is '
4078 'recommended to add \'%s\' directive. Otherwise, the fuzz target may '
4079 'work incorrectly on Mac (crbug.com/687076).\nNote that '
4080 'LLVMFuzzerInitialize should not be used, unless your fuzz target needs '
4081 'to access command line arguments passed to the fuzzer. Instead, prefer '
4082 'static initialization and shared resources as documented in '
John Palmer0e0f72bf2021-06-07 09:10:204083 'https://chromium.googlesource.com/chromium/src/+/main/testing/'
Max Morozb47503b2019-08-08 21:03:274084 'libfuzzer/efficient_fuzzing.md#simplifying-initialization_cleanup.\n' % (
4085 ', '.join(EXPORTED_SYMBOLS), REQUIRED_HEADER)
4086 )
4087
4088 return [output_api.PresubmitPromptWarning(
4089 message="Missing '%s' in:" % REQUIRED_HEADER,
4090 items=files_with_missing_header,
4091 long_text=long_text)]
4092
4093
Mohamed Heikald048240a2019-11-12 16:57:374094def _CheckNewImagesWarning(input_api, output_api):
4095 """
4096 Warns authors who add images into the repo to make sure their images are
4097 optimized before committing.
4098 """
4099 images_added = False
4100 image_paths = []
4101 errors = []
4102 filter_lambda = lambda x: input_api.FilterSourceFile(
4103 x,
James Cook24a504192020-07-23 00:08:444104 files_to_skip=(('(?i).*test', r'.*\/junit\/')
4105 + input_api.DEFAULT_FILES_TO_SKIP),
4106 files_to_check=[r'.*\/(drawable|mipmap)' ]
Mohamed Heikald048240a2019-11-12 16:57:374107 )
4108 for f in input_api.AffectedFiles(
4109 include_deletes=False, file_filter=filter_lambda):
4110 local_path = f.LocalPath().lower()
4111 if any(local_path.endswith(extension) for extension in _IMAGE_EXTENSIONS):
4112 images_added = True
4113 image_paths.append(f)
4114 if images_added:
4115 errors.append(output_api.PresubmitPromptWarning(
4116 'It looks like you are trying to commit some images. If these are '
4117 'non-test-only images, please make sure to read and apply the tips in '
4118 'https://chromium.googlesource.com/chromium/src/+/HEAD/docs/speed/'
4119 'binary_size/optimization_advice.md#optimizing-images\nThis check is '
4120 'FYI only and will not block your CL on the CQ.', image_paths))
4121 return errors
4122
4123
Saagar Sanghavifceeaae2020-08-12 16:40:364124def ChecksAndroidSpecificOnUpload(input_api, output_api):
Becky Zhou7c69b50992018-12-10 19:37:574125 """Groups upload checks that target android code."""
dgnaa68d5e2015-06-10 10:08:224126 results = []
dgnaa68d5e2015-06-10 10:08:224127 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
Jinsong Fan91ebbbd2019-04-16 14:57:174128 results.extend(_CheckAndroidDebuggableBuild(input_api, output_api))
agrieve7b6479d82015-10-07 14:24:224129 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
dskiba88634f4e2015-08-14 23:03:294130 results.extend(_CheckAndroidToastUsage(input_api, output_api))
Yoland Yanb92fa522017-08-28 17:37:064131 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
4132 results.extend(_CheckAndroidTestJUnitFrameworkImport(input_api, output_api))
yolandyan45001472016-12-21 21:12:424133 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
Nate Fischer535972b2017-09-16 01:06:184134 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
Becky Zhou7c69b50992018-12-10 19:37:574135 results.extend(_CheckAndroidXmlStyle(input_api, output_api, True))
Mohamed Heikald048240a2019-11-12 16:57:374136 results.extend(_CheckNewImagesWarning(input_api, output_api))
Michael Thiessen44457642020-02-06 00:24:154137 results.extend(_CheckAndroidNoBannedImports(input_api, output_api))
Becky Zhou7c69b50992018-12-10 19:37:574138 return results
4139
Saagar Sanghavifceeaae2020-08-12 16:40:364140def ChecksAndroidSpecificOnCommit(input_api, output_api):
Becky Zhou7c69b50992018-12-10 19:37:574141 """Groups commit checks that target android code."""
4142 results = []
4143 results.extend(_CheckAndroidXmlStyle(input_api, output_api, False))
dgnaa68d5e2015-06-10 10:08:224144 return results
4145
Chris Hall59f8d0c72020-05-01 07:31:194146# TODO(chrishall): could we additionally match on any path owned by
4147# ui/accessibility/OWNERS ?
4148_ACCESSIBILITY_PATHS = (
4149 r"^chrome[\\/]browser.*[\\/]accessibility[\\/]",
4150 r"^chrome[\\/]browser[\\/]extensions[\\/]api[\\/]automation.*[\\/]",
4151 r"^chrome[\\/]renderer[\\/]extensions[\\/]accessibility_.*",
4152 r"^chrome[\\/]tests[\\/]data[\\/]accessibility[\\/]",
4153 r"^content[\\/]browser[\\/]accessibility[\\/]",
4154 r"^content[\\/]renderer[\\/]accessibility[\\/]",
4155 r"^content[\\/]tests[\\/]data[\\/]accessibility[\\/]",
4156 r"^extensions[\\/]renderer[\\/]api[\\/]automation[\\/]",
4157 r"^ui[\\/]accessibility[\\/]",
4158 r"^ui[\\/]views[\\/]accessibility[\\/]",
4159)
4160
Saagar Sanghavifceeaae2020-08-12 16:40:364161def CheckAccessibilityRelnotesField(input_api, output_api):
Chris Hall59f8d0c72020-05-01 07:31:194162 """Checks that commits to accessibility code contain an AX-Relnotes field in
4163 their commit message."""
4164 def FileFilter(affected_file):
4165 paths = _ACCESSIBILITY_PATHS
James Cook24a504192020-07-23 00:08:444166 return input_api.FilterSourceFile(affected_file, files_to_check=paths)
Chris Hall59f8d0c72020-05-01 07:31:194167
4168 # Only consider changes affecting accessibility paths.
4169 if not any(input_api.AffectedFiles(file_filter=FileFilter)):
4170 return []
4171
Akihiro Ota08108e542020-05-20 15:30:534172 # AX-Relnotes can appear in either the description or the footer.
4173 # When searching the description, require 'AX-Relnotes:' to appear at the
4174 # beginning of a line.
4175 ax_regex = input_api.re.compile('ax-relnotes[:=]')
4176 description_has_relnotes = any(ax_regex.match(line)
4177 for line in input_api.change.DescriptionText().lower().splitlines())
4178
4179 footer_relnotes = input_api.change.GitFootersFromDescription().get(
4180 'AX-Relnotes', [])
4181 if description_has_relnotes or footer_relnotes:
Chris Hall59f8d0c72020-05-01 07:31:194182 return []
4183
4184 # TODO(chrishall): link to Relnotes documentation in message.
4185 message = ("Missing 'AX-Relnotes:' field required for accessibility changes"
4186 "\n please add 'AX-Relnotes: [release notes].' to describe any "
4187 "user-facing changes"
4188 "\n otherwise add 'AX-Relnotes: n/a.' if this change has no "
4189 "user-facing effects"
4190 "\n if this is confusing or annoying then please contact members "
4191 "of ui/accessibility/OWNERS.")
4192
4193 return [output_api.PresubmitNotifyResult(message)]
dgnaa68d5e2015-06-10 10:08:224194
seanmccullough4a9356252021-04-08 19:54:094195# string pattern, sequence of strings to show when pattern matches,
4196# error flag. True if match is a presubmit error, otherwise it's a warning.
4197_NON_INCLUSIVE_TERMS = (
4198 (
4199 # Note that \b pattern in python re is pretty particular. In this
4200 # regexp, 'class WhiteList ...' will match, but 'class FooWhiteList
4201 # ...' will not. This may require some tweaking to catch these cases
4202 # without triggering a lot of false positives. Leaving it naive and
4203 # less matchy for now.
4204 r'/\b(?i)((black|white)list|slave)\b', # nocheck
4205 (
4206 'Please don\'t use blacklist, whitelist, ' # nocheck
4207 'or slave in your', # nocheck
4208 'code and make every effort to use other terms. Using "// nocheck"',
4209 '"# nocheck" or "<!-- nocheck -->"',
4210 'at the end of the offending line will bypass this PRESUBMIT error',
4211 'but avoid using this whenever possible. Reach out to',
4212 '[email protected] if you have questions'),
4213 True),)
4214
Saagar Sanghavifceeaae2020-08-12 16:40:364215def ChecksCommon(input_api, output_api):
[email protected]22c9bd72011-03-27 16:47:394216 """Checks common to both upload and commit."""
4217 results = []
4218 results.extend(input_api.canned_checks.PanProjectChecks(
[email protected]3de922f2013-12-20 13:27:384219 input_api, output_api,
qyearsleyfa2cfcf82016-12-15 18:03:544220 excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:084221
4222 author = input_api.change.author_email
4223 if author and author not in _KNOWN_ROBOTS:
4224 results.extend(
4225 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
4226
[email protected]9f919cc2013-07-31 03:04:044227 results.extend(
4228 input_api.canned_checks.CheckChangeHasNoTabs(
4229 input_api,
4230 output_api,
4231 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
Sergiy Byelozyorov366b6482017-11-06 18:20:434232 results.extend(input_api.RunTests(
4233 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
[email protected]2299dcf2012-11-15 19:56:244234
Edward Lesmesce51df52020-08-04 22:10:174235 dirmd_bin = input_api.os_path.join(
4236 input_api.PresubmitLocalPath(), 'third_party', 'depot_tools', 'dirmd')
4237 results.extend(input_api.RunTests(
4238 input_api.canned_checks.CheckDirMetadataFormat(
4239 input_api, output_api, dirmd_bin)))
4240 results.extend(
4241 input_api.canned_checks.CheckOwnersDirMetadataExclusive(
4242 input_api, output_api))
Edward Lesmes8c62329f2020-12-14 22:46:554243 results.extend(
4244 input_api.canned_checks.CheckNoNewMetadataInOwners(
4245 input_api, output_api))
seanmccullough4a9356252021-04-08 19:54:094246 results.extend(input_api.canned_checks.CheckInclusiveLanguage(
4247 input_api, output_api,
4248 excluded_directories_relative_path = [
4249 'infra',
4250 'inclusive_language_presubmit_exempt_dirs.txt'
4251 ],
4252 non_inclusive_terms=_NON_INCLUSIVE_TERMS))
Edward Lesmesce51df52020-08-04 22:10:174253
Vaclav Brozekcdc7defb2018-03-20 09:54:354254 for f in input_api.AffectedFiles():
4255 path, name = input_api.os_path.split(f.LocalPath())
4256 if name == 'PRESUBMIT.py':
4257 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
Caleb Rouleaua6117be2018-05-11 20:10:004258 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
4259 if f.Action() != 'D' and input_api.os_path.exists(test_file):
Dirk Pranke38557312018-04-18 00:53:074260 # The PRESUBMIT.py file (and the directory containing it) might
4261 # have been affected by being moved or removed, so only try to
4262 # run the tests if they still exist.
Dirk Prankee3c9c62d2021-05-18 18:35:594263 use_python3 = False
4264 with open(f.LocalPath()) as fp:
4265 use_python3 = any(line.startswith('USE_PYTHON3 = True')
4266 for line in fp.readlines())
4267
Dirk Pranke38557312018-04-18 00:53:074268 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
4269 input_api, output_api, full_path,
Dirk Prankee3c9c62d2021-05-18 18:35:594270 files_to_check=[r'^PRESUBMIT_test\.py$'],
4271 run_on_python2=not use_python3,
4272 run_on_python3=use_python3))
[email protected]22c9bd72011-03-27 16:47:394273 return results
[email protected]1f7b4172010-01-28 01:17:344274
[email protected]b337cb5b2011-01-23 21:24:054275
Saagar Sanghavifceeaae2020-08-12 16:40:364276def CheckPatchFiles(input_api, output_api):
[email protected]b8079ae4a2012-12-05 19:56:494277 problems = [f.LocalPath() for f in input_api.AffectedFiles()
4278 if f.LocalPath().endswith(('.orig', '.rej'))]
4279 if problems:
4280 return [output_api.PresubmitError(
4281 "Don't commit .rej and .orig files.", problems)]
[email protected]2fdd1f362013-01-16 03:56:034282 else:
4283 return []
[email protected]b8079ae4a2012-12-05 19:56:494284
4285
Saagar Sanghavifceeaae2020-08-12 16:40:364286def CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Kent Tamura79ef8f82017-07-18 00:00:214287 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
4288 macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
4289 'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
Kent Tamura5a8755d2017-06-29 23:37:074290 include_re = input_api.re.compile(
4291 r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
4292 extension_re = input_api.re.compile(r'\.[a-z]+$')
4293 errors = []
Bruce Dawsonaae5e652021-06-24 15:05:394294 for f in input_api.AffectedFiles(include_deletes=False):
Kent Tamura5a8755d2017-06-29 23:37:074295 if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
4296 continue
4297 found_line_number = None
4298 found_macro = None
Bruce Dawsonaae5e652021-06-24 15:05:394299 all_lines = input_api.ReadFile(f, 'r').splitlines()
4300 for line_num, line in enumerate(all_lines):
Kent Tamura5a8755d2017-06-29 23:37:074301 match = macro_re.search(line)
4302 if match:
4303 found_line_number = line_num
4304 found_macro = match.group(2)
4305 break
4306 if not found_line_number:
4307 continue
4308
Bruce Dawsonaae5e652021-06-24 15:05:394309 found_include_line = -1
4310 for line_num, line in enumerate(all_lines):
Kent Tamura5a8755d2017-06-29 23:37:074311 if include_re.search(line):
Bruce Dawsonaae5e652021-06-24 15:05:394312 found_include_line = line_num
Kent Tamura5a8755d2017-06-29 23:37:074313 break
Bruce Dawsonaae5e652021-06-24 15:05:394314 if found_include_line >= 0 and found_include_line < found_line_number:
Kent Tamura5a8755d2017-06-29 23:37:074315 continue
4316
4317 if not f.LocalPath().endswith('.h'):
4318 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
4319 try:
4320 content = input_api.ReadFile(primary_header_path, 'r')
4321 if include_re.search(content):
4322 continue
4323 except IOError:
4324 pass
Bruce Dawsonaae5e652021-06-24 15:05:394325 errors.append('%s:%d %s macro is used without first including build/'
Kent Tamura5a8755d2017-06-29 23:37:074326 'build_config.h.'
4327 % (f.LocalPath(), found_line_number, found_macro))
4328 if errors:
4329 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4330 return []
4331
4332
Lei Zhang1c12a22f2021-05-12 11:28:454333def CheckForSuperfluousStlIncludesInHeaders(input_api, output_api):
4334 stl_include_re = input_api.re.compile(
Lei Zhang0643e342021-05-12 18:02:124335 r'^#include\s+<('
Lei Zhang1c12a22f2021-05-12 11:28:454336 r'algorithm|'
4337 r'array|'
4338 r'limits|'
4339 r'list|'
4340 r'map|'
4341 r'memory|'
4342 r'queue|'
4343 r'set|'
4344 r'string|'
4345 r'unordered_map|'
4346 r'unordered_set|'
4347 r'utility|'
Lei Zhang0643e342021-05-12 18:02:124348 r'vector)>')
Lei Zhang1c12a22f2021-05-12 11:28:454349 std_namespace_re = input_api.re.compile(r'std::')
4350 errors = []
4351 for f in input_api.AffectedFiles():
4352 if not _IsCPlusPlusHeaderFile(input_api, f.LocalPath()):
4353 continue
4354
4355 uses_std_namespace = False
4356 has_stl_include = False
4357 for line in f.NewContents():
4358 if has_stl_include and uses_std_namespace:
4359 break
4360
4361 if not has_stl_include and stl_include_re.search(line):
4362 has_stl_include = True
4363 continue
4364
4365 if not uses_std_namespace and std_namespace_re.search(line):
4366 uses_std_namespace = True
4367 continue
4368
4369 if has_stl_include and not uses_std_namespace:
4370 errors.append('%s: Includes STL header(s) but does not reference std::'
4371 % f.LocalPath())
4372 if errors:
4373 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
4374 return []
4375
4376
[email protected]b00342e7f2013-03-26 16:21:544377def _DidYouMeanOSMacro(bad_macro):
4378 try:
4379 return {'A': 'OS_ANDROID',
4380 'B': 'OS_BSD',
4381 'C': 'OS_CHROMEOS',
4382 'F': 'OS_FREEBSD',
Avi Drissman34594e902020-07-25 05:35:444383 'I': 'OS_IOS',
[email protected]b00342e7f2013-03-26 16:21:544384 'L': 'OS_LINUX',
Avi Drissman34594e902020-07-25 05:35:444385 'M': 'OS_MAC',
[email protected]b00342e7f2013-03-26 16:21:544386 'N': 'OS_NACL',
4387 'O': 'OS_OPENBSD',
4388 'P': 'OS_POSIX',
4389 'S': 'OS_SOLARIS',
4390 'W': 'OS_WIN'}[bad_macro[3].upper()]
4391 except KeyError:
4392 return ''
4393
4394
4395def _CheckForInvalidOSMacrosInFile(input_api, f):
4396 """Check for sensible looking, totally invalid OS macros."""
4397 preprocessor_statement = input_api.re.compile(r'^\s*#')
4398 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
4399 results = []
4400 for lnum, line in f.ChangedContents():
4401 if preprocessor_statement.search(line):
4402 for match in os_macro.finditer(line):
4403 if not match.group(1) in _VALID_OS_MACROS:
4404 good = _DidYouMeanOSMacro(match.group(1))
4405 did_you_mean = ' (did you mean %s?)' % good if good else ''
4406 results.append(' %s:%d %s%s' % (f.LocalPath(),
4407 lnum,
4408 match.group(1),
4409 did_you_mean))
4410 return results
4411
4412
Saagar Sanghavifceeaae2020-08-12 16:40:364413def CheckForInvalidOSMacros(input_api, output_api):
[email protected]b00342e7f2013-03-26 16:21:544414 """Check all affected files for invalid OS macros."""
4415 bad_macros = []
tzik3f295992018-12-04 20:32:234416 for f in input_api.AffectedSourceFiles(None):
ellyjones47654342016-05-06 15:50:474417 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
[email protected]b00342e7f2013-03-26 16:21:544418 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
4419
4420 if not bad_macros:
4421 return []
4422
4423 return [output_api.PresubmitError(
4424 'Possibly invalid OS macro[s] found. Please fix your code\n'
4425 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
4426
lliabraa35bab3932014-10-01 12:16:444427
4428def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
4429 """Check all affected files for invalid "if defined" macros."""
4430 ALWAYS_DEFINED_MACROS = (
4431 "TARGET_CPU_PPC",
4432 "TARGET_CPU_PPC64",
4433 "TARGET_CPU_68K",
4434 "TARGET_CPU_X86",
4435 "TARGET_CPU_ARM",
4436 "TARGET_CPU_MIPS",
4437 "TARGET_CPU_SPARC",
4438 "TARGET_CPU_ALPHA",
4439 "TARGET_IPHONE_SIMULATOR",
4440 "TARGET_OS_EMBEDDED",
4441 "TARGET_OS_IPHONE",
4442 "TARGET_OS_MAC",
4443 "TARGET_OS_UNIX",
4444 "TARGET_OS_WIN32",
4445 )
4446 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
4447 results = []
4448 for lnum, line in f.ChangedContents():
4449 for match in ifdef_macro.finditer(line):
4450 if match.group(1) in ALWAYS_DEFINED_MACROS:
4451 always_defined = ' %s is always defined. ' % match.group(1)
4452 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
4453 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
4454 lnum,
4455 always_defined,
4456 did_you_mean))
4457 return results
4458
4459
Saagar Sanghavifceeaae2020-08-12 16:40:364460def CheckForInvalidIfDefinedMacros(input_api, output_api):
lliabraa35bab3932014-10-01 12:16:444461 """Check all affected files for invalid "if defined" macros."""
4462 bad_macros = []
Mirko Bonadei28112c02019-05-17 20:25:054463 skipped_paths = ['third_party/sqlite/', 'third_party/abseil-cpp/']
lliabraa35bab3932014-10-01 12:16:444464 for f in input_api.AffectedFiles():
Mirko Bonadei28112c02019-05-17 20:25:054465 if any([f.LocalPath().startswith(path) for path in skipped_paths]):
sdefresne4e1eccb32017-05-24 08:45:214466 continue
lliabraa35bab3932014-10-01 12:16:444467 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
4468 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
4469
4470 if not bad_macros:
4471 return []
4472
4473 return [output_api.PresubmitError(
4474 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
4475 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
4476 bad_macros)]
4477
4478
Saagar Sanghavifceeaae2020-08-12 16:40:364479def CheckForIPCRules(input_api, output_api):
mlamouria82272622014-09-16 18:45:044480 """Check for same IPC rules described in
4481 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
4482 """
4483 base_pattern = r'IPC_ENUM_TRAITS\('
4484 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
4485 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
4486
4487 problems = []
4488 for f in input_api.AffectedSourceFiles(None):
4489 local_path = f.LocalPath()
4490 if not local_path.endswith('.h'):
4491 continue
4492 for line_number, line in f.ChangedContents():
4493 if inclusion_pattern.search(line) and not comment_pattern.search(line):
4494 problems.append(
4495 '%s:%d\n %s' % (local_path, line_number, line.strip()))
4496
4497 if problems:
4498 return [output_api.PresubmitPromptWarning(
4499 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
4500 else:
4501 return []
4502
[email protected]b00342e7f2013-03-26 16:21:544503
Saagar Sanghavifceeaae2020-08-12 16:40:364504def CheckForLongPathnames(input_api, output_api):
Stephen Martinis97a394142018-06-07 23:06:054505 """Check to make sure no files being submitted have long paths.
4506 This causes issues on Windows.
4507 """
4508 problems = []
Stephen Martinisc4b246b2019-10-31 23:04:194509 for f in input_api.AffectedTestableFiles():
Stephen Martinis97a394142018-06-07 23:06:054510 local_path = f.LocalPath()
4511 # Windows has a path limit of 260 characters. Limit path length to 200 so
4512 # that we have some extra for the prefix on dev machines and the bots.
4513 if len(local_path) > 200:
4514 problems.append(local_path)
4515
4516 if problems:
4517 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
4518 else:
4519 return []
4520
4521
Saagar Sanghavifceeaae2020-08-12 16:40:364522def CheckForIncludeGuards(input_api, output_api):
Daniel Bratell8ba52722018-03-02 16:06:144523 """Check that header files have proper guards against multiple inclusion.
4524 If a file should not have such guards (and it probably should) then it
4525 should include the string "no-include-guard-because-multiply-included".
4526 """
Daniel Bratell6a75baef62018-06-04 10:04:454527 def is_chromium_header_file(f):
4528 # We only check header files under the control of the Chromium
4529 # project. That is, those outside third_party apart from
4530 # third_party/blink.
Kinuko Yasuda0cdb3da2019-07-31 21:50:324531 # We also exclude *_message_generator.h headers as they use
4532 # include guards in a special, non-typical way.
Daniel Bratell6a75baef62018-06-04 10:04:454533 file_with_path = input_api.os_path.normpath(f.LocalPath())
4534 return (file_with_path.endswith('.h') and
Kinuko Yasuda0cdb3da2019-07-31 21:50:324535 not file_with_path.endswith('_message_generator.h') and
Daniel Bratell6a75baef62018-06-04 10:04:454536 (not file_with_path.startswith('third_party') or
4537 file_with_path.startswith(
4538 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:144539
4540 def replace_special_with_underscore(string):
Olivier Robinbba137492018-07-30 11:31:344541 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:144542
4543 errors = []
4544
Daniel Bratell6a75baef62018-06-04 10:04:454545 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
Daniel Bratell8ba52722018-03-02 16:06:144546 guard_name = None
4547 guard_line_number = None
4548 seen_guard_end = False
4549
4550 file_with_path = input_api.os_path.normpath(f.LocalPath())
4551 base_file_name = input_api.os_path.splitext(
4552 input_api.os_path.basename(file_with_path))[0]
4553 upper_base_file_name = base_file_name.upper()
4554
4555 expected_guard = replace_special_with_underscore(
4556 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:144557
4558 # For "path/elem/file_name.h" we should really only accept
Daniel Bratell39b5b062018-05-16 18:09:574559 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
4560 # are too many (1000+) files with slight deviations from the
4561 # coding style. The most important part is that the include guard
4562 # is there, and that it's unique, not the name so this check is
4563 # forgiving for existing files.
Daniel Bratell8ba52722018-03-02 16:06:144564 #
4565 # As code becomes more uniform, this could be made stricter.
4566
4567 guard_name_pattern_list = [
4568 # Anything with the right suffix (maybe with an extra _).
4569 r'\w+_H__?',
4570
Daniel Bratell39b5b062018-05-16 18:09:574571 # To cover include guards with old Blink style.
Daniel Bratell8ba52722018-03-02 16:06:144572 r'\w+_h',
4573
4574 # Anything including the uppercase name of the file.
4575 r'\w*' + input_api.re.escape(replace_special_with_underscore(
4576 upper_base_file_name)) + r'\w*',
4577 ]
4578 guard_name_pattern = '|'.join(guard_name_pattern_list)
4579 guard_pattern = input_api.re.compile(
4580 r'#ifndef\s+(' + guard_name_pattern + ')')
4581
4582 for line_number, line in enumerate(f.NewContents()):
4583 if 'no-include-guard-because-multiply-included' in line:
4584 guard_name = 'DUMMY' # To not trigger check outside the loop.
4585 break
4586
4587 if guard_name is None:
4588 match = guard_pattern.match(line)
4589 if match:
4590 guard_name = match.group(1)
4591 guard_line_number = line_number
4592
Daniel Bratell39b5b062018-05-16 18:09:574593 # We allow existing files to use include guards whose names
Daniel Bratell6a75baef62018-06-04 10:04:454594 # don't match the chromium style guide, but new files should
4595 # get it right.
4596 if not f.OldContents():
Daniel Bratell39b5b062018-05-16 18:09:574597 if guard_name != expected_guard:
Daniel Bratell8ba52722018-03-02 16:06:144598 errors.append(output_api.PresubmitPromptWarning(
4599 'Header using the wrong include guard name %s' % guard_name,
4600 ['%s:%d' % (f.LocalPath(), line_number + 1)],
Istiaque Ahmed9ad6cd22019-10-04 00:26:574601 'Expected: %r\nFound: %r' % (expected_guard, guard_name)))
Daniel Bratell8ba52722018-03-02 16:06:144602 else:
4603 # The line after #ifndef should have a #define of the same name.
4604 if line_number == guard_line_number + 1:
4605 expected_line = '#define %s' % guard_name
4606 if line != expected_line:
4607 errors.append(output_api.PresubmitPromptWarning(
4608 'Missing "%s" for include guard' % expected_line,
4609 ['%s:%d' % (f.LocalPath(), line_number + 1)],
4610 'Expected: %r\nGot: %r' % (expected_line, line)))
4611
4612 if not seen_guard_end and line == '#endif // %s' % guard_name:
4613 seen_guard_end = True
4614 elif seen_guard_end:
4615 if line.strip() != '':
4616 errors.append(output_api.PresubmitPromptWarning(
4617 'Include guard %s not covering the whole file' % (
4618 guard_name), [f.LocalPath()]))
4619 break # Nothing else to check and enough to warn once.
4620
4621 if guard_name is None:
4622 errors.append(output_api.PresubmitPromptWarning(
4623 'Missing include guard %s' % expected_guard,
4624 [f.LocalPath()],
4625 'Missing include guard in %s\n'
4626 'Recommended name: %s\n'
4627 'This check can be disabled by having the string\n'
4628 'no-include-guard-because-multiply-included in the header.' %
4629 (f.LocalPath(), expected_guard)))
4630
4631 return errors
4632
4633
Saagar Sanghavifceeaae2020-08-12 16:40:364634def CheckForWindowsLineEndings(input_api, output_api):
mostynbb639aca52015-01-07 20:31:234635 """Check source code and known ascii text files for Windows style line
4636 endings.
4637 """
Evan Stade6cfc964c12021-05-18 20:21:164638 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate|icon)$'
mostynbb639aca52015-01-07 20:31:234639
4640 file_inclusion_pattern = (
4641 known_text_files,
Bruce Dawson6141d4a2021-06-08 15:56:114642 r'.+%s' % _IMPLEMENTATION_EXTENSIONS,
4643 r'.+%s' % _HEADER_EXTENSIONS
mostynbb639aca52015-01-07 20:31:234644 )
4645
mostynbb639aca52015-01-07 20:31:234646 problems = []
Andrew Grieve933d12e2017-10-30 20:22:534647 source_file_filter = lambda f: input_api.FilterSourceFile(
James Cook24a504192020-07-23 00:08:444648 f, files_to_check=file_inclusion_pattern, files_to_skip=None)
Andrew Grieve933d12e2017-10-30 20:22:534649 for f in input_api.AffectedSourceFiles(source_file_filter):
Vaclav Brozekd5de76a2018-03-17 07:57:504650 include_file = False
Bruce Dawsonb2cfdfe2021-06-10 19:01:204651 for line in input_api.ReadFile(f, 'r').splitlines(True):
mostynbb639aca52015-01-07 20:31:234652 if line.endswith('\r\n'):
Vaclav Brozekd5de76a2018-03-17 07:57:504653 include_file = True
4654 if include_file:
4655 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:234656
4657 if problems:
4658 return [output_api.PresubmitPromptWarning('Are you sure that you want '
4659 'these files to contain Windows style line endings?\n' +
4660 '\n'.join(problems))]
4661
4662 return []
4663
Evan Stade6cfc964c12021-05-18 20:21:164664def CheckIconFilesForLicenseHeaders(input_api, output_api):
4665 """Check that .icon files (which are fragments of C++) have license headers.
4666 """
4667
4668 icon_files = (r'.*\.icon$',)
4669
4670 icons = lambda x: input_api.FilterSourceFile(x, files_to_check=icon_files)
4671 return input_api.canned_checks.CheckLicense(
4672 input_api, output_api, source_file_filter=icons)
4673
Jose Magana2b456f22021-03-09 23:26:404674def CheckForUseOfChromeAppsDeprecations(input_api, output_api):
4675 """Check source code for use of Chrome App technologies being
4676 deprecated.
4677 """
4678
4679 def _CheckForDeprecatedTech(input_api, output_api,
4680 detection_list, files_to_check = None, files_to_skip = None):
4681
4682 if (files_to_check or files_to_skip):
4683 source_file_filter = lambda f: input_api.FilterSourceFile(
4684 f, files_to_check=files_to_check,
4685 files_to_skip=files_to_skip)
4686 else:
4687 source_file_filter = None
4688
4689 problems = []
4690
4691 for f in input_api.AffectedSourceFiles(source_file_filter):
4692 if f.Action() == 'D':
4693 continue
4694 for _, line in f.ChangedContents():
4695 if any( detect in line for detect in detection_list ):
4696 problems.append(f.LocalPath())
4697
4698 return problems
4699
4700 # to avoid this presubmit script triggering warnings
4701 files_to_skip = ['PRESUBMIT.py','PRESUBMIT_test.py']
4702
4703 problems =[]
4704
4705 # NMF: any files with extensions .nmf or NMF
4706 _NMF_FILES = r'\.(nmf|NMF)$'
4707 problems += _CheckForDeprecatedTech(input_api, output_api,
4708 detection_list = [''], # any change to the file will trigger warning
4709 files_to_check = [ r'.+%s' % _NMF_FILES ])
4710
4711 # MANIFEST: any manifest.json that in its diff includes "app":
4712 _MANIFEST_FILES = r'(manifest\.json)$'
4713 problems += _CheckForDeprecatedTech(input_api, output_api,
4714 detection_list = ['"app":'],
4715 files_to_check = [ r'.*%s' % _MANIFEST_FILES ])
4716
4717 # NaCl / PNaCl: any file that in its diff contains the strings in the list
4718 problems += _CheckForDeprecatedTech(input_api, output_api,
4719 detection_list = ['config=nacl','enable-nacl','cpu=pnacl', 'nacl_io'],
4720 files_to_skip = files_to_skip + [ r"^native_client_sdk[\\/]"])
4721
4722 # PPAPI: any C/C++ file that in its diff includes a ppappi library
4723 problems += _CheckForDeprecatedTech(input_api, output_api,
4724 detection_list = ['#include "ppapi','#include <ppapi'],
4725 files_to_check = (
4726 r'.+%s' % _HEADER_EXTENSIONS,
4727 r'.+%s' % _IMPLEMENTATION_EXTENSIONS ),
4728 files_to_skip = [r"^ppapi[\\/]"] )
4729
Jose Magana2b456f22021-03-09 23:26:404730 if problems:
4731 return [output_api.PresubmitPromptWarning('You are adding/modifying code'
4732 'related to technologies which will soon be deprecated (Chrome Apps, NaCl,'
4733 ' PNaCl, PPAPI). See this blog post for more details:\n'
4734 'https://blog.chromium.org/2020/08/changes-to-chrome-app-support-timeline.html\n'
4735 'and this documentation for options to replace these technologies:\n'
4736 'https://developer.chrome.com/docs/apps/migration/\n'+
4737 '\n'.join(problems))]
4738
4739 return []
4740
mostynbb639aca52015-01-07 20:31:234741
Saagar Sanghavifceeaae2020-08-12 16:40:364742def CheckSyslogUseWarningOnUpload(input_api, output_api, src_file_filter=None):
pastarmovj89f7ee12016-09-20 14:58:134743 """Checks that all source files use SYSLOG properly."""
4744 syslog_files = []
Saagar Sanghavifceeaae2020-08-12 16:40:364745 for f in input_api.AffectedSourceFiles(src_file_filter):
pastarmovj032ba5bc2017-01-12 10:41:564746 for line_number, line in f.ChangedContents():
4747 if 'SYSLOG' in line:
4748 syslog_files.append(f.LocalPath() + ':' + str(line_number))
4749
pastarmovj89f7ee12016-09-20 14:58:134750 if syslog_files:
4751 return [output_api.PresubmitPromptWarning(
4752 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
4753 ' calls.\nFiles to check:\n', items=syslog_files)]
4754 return []
4755
4756
[email protected]1f7b4172010-01-28 01:17:344757def CheckChangeOnUpload(input_api, output_api):
Saagar Sanghavifceeaae2020-08-12 16:40:364758 if input_api.version < [2, 0, 0]:
4759 return [output_api.PresubmitError("Your depot_tools is out of date. "
4760 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
4761 "but your version is %d.%d.%d" % tuple(input_api.version))]
[email protected]1f7b4172010-01-28 01:17:344762 results = []
scottmg39b29952014-12-08 18:31:284763 results.extend(
jam93a6ee792017-02-08 23:59:224764 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:544765 return results
[email protected]ca8d1982009-02-19 16:33:124766
4767
4768def CheckChangeOnCommit(input_api, output_api):
Saagar Sanghavifceeaae2020-08-12 16:40:364769 if input_api.version < [2, 0, 0]:
4770 return [output_api.PresubmitError("Your depot_tools is out of date. "
4771 "This PRESUBMIT.py requires at least presubmit_support version 2.0.0, "
4772 "but your version is %d.%d.%d" % tuple(input_api.version))]
4773
[email protected]fe5f57c52009-06-05 14:25:544774 results = []
[email protected]fe5f57c52009-06-05 14:25:544775 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:274776 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:344777 input_api,
4778 output_api,
[email protected]2fdd1f362013-01-16 03:56:034779 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:274780
jam93a6ee792017-02-08 23:59:224781 results.extend(
4782 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]3e4eb112011-01-18 03:29:544783 results.extend(input_api.canned_checks.CheckChangeHasBugField(
4784 input_api, output_api))
Dan Beam39f28cb2019-10-04 01:01:384785 results.extend(input_api.canned_checks.CheckChangeHasNoUnwantedTags(
4786 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:414787 results.extend(input_api.canned_checks.CheckChangeHasDescription(
4788 input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:544789 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144790
4791
Saagar Sanghavifceeaae2020-08-12 16:40:364792def CheckStrings(input_api, output_api):
Rainhard Findlingfc31844c52020-05-15 09:58:264793 """Check string ICU syntax validity and if translation screenshots exist."""
Edward Lesmesf7c5c6d2020-05-14 23:30:024794 # Skip translation screenshots check if a SkipTranslationScreenshotsCheck
4795 # footer is set to true.
4796 git_footers = input_api.change.GitFootersFromDescription()
Rainhard Findlingfc31844c52020-05-15 09:58:264797 skip_screenshot_check_footer = [
Edward Lesmesf7c5c6d2020-05-14 23:30:024798 footer.lower()
4799 for footer in git_footers.get(u'Skip-Translation-Screenshots-Check', [])]
Rainhard Findlingfc31844c52020-05-15 09:58:264800 run_screenshot_check = u'true' not in skip_screenshot_check_footer
Edward Lesmesf7c5c6d2020-05-14 23:30:024801
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144802 import os
Rainhard Findlingfc31844c52020-05-15 09:58:264803 import re
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144804 import sys
4805 from io import StringIO
4806
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144807 new_or_added_paths = set(f.LocalPath()
4808 for f in input_api.AffectedFiles()
4809 if (f.Action() == 'A' or f.Action() == 'M'))
4810 removed_paths = set(f.LocalPath()
4811 for f in input_api.AffectedFiles(include_deletes=True)
4812 if f.Action() == 'D')
4813
Andrew Grieve0e8790c2020-09-03 17:27:324814 affected_grds = [
4815 f for f in input_api.AffectedFiles()
4816 if f.LocalPath().endswith(('.grd', '.grdp'))
4817 ]
4818 affected_grds = [f for f in affected_grds if not 'testdata' in f.LocalPath()]
meacer8c0d3832019-12-26 21:46:164819 if not affected_grds:
4820 return []
4821
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144822 affected_png_paths = [f.AbsoluteLocalPath()
4823 for f in input_api.AffectedFiles()
4824 if (f.LocalPath().endswith('.png'))]
4825
4826 # Check for screenshots. Developers can upload screenshots using
4827 # tools/translation/upload_screenshots.py which finds and uploads
4828 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
4829 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
4830 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
4831 #
4832 # The logic here is as follows:
4833 #
4834 # - If the CL has a .png file under the screenshots directory for a grd
4835 # file, warn the developer. Actual images should never be checked into the
4836 # Chrome repo.
4837 #
4838 # - If the CL contains modified or new messages in grd files and doesn't
4839 # contain the corresponding .sha1 files, warn the developer to add images
4840 # and upload them via tools/translation/upload_screenshots.py.
4841 #
4842 # - If the CL contains modified or new messages in grd files and the
4843 # corresponding .sha1 files, everything looks good.
4844 #
4845 # - If the CL contains removed messages in grd files but the corresponding
4846 # .sha1 files aren't removed, warn the developer to remove them.
4847 unnecessary_screenshots = []
4848 missing_sha1 = []
4849 unnecessary_sha1_files = []
4850
Rainhard Findlingfc31844c52020-05-15 09:58:264851 # This checks verifies that the ICU syntax of messages this CL touched is
4852 # valid, and reports any found syntax errors.
4853 # Without this presubmit check, ICU syntax errors in Chromium strings can land
4854 # without developers being aware of them. Later on, such ICU syntax errors
4855 # break message extraction for translation, hence would block Chromium
4856 # translations until they are fixed.
4857 icu_syntax_errors = []
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144858
4859 def _CheckScreenshotAdded(screenshots_dir, message_id):
4860 sha1_path = input_api.os_path.join(
4861 screenshots_dir, message_id + '.png.sha1')
4862 if sha1_path not in new_or_added_paths:
4863 missing_sha1.append(sha1_path)
4864
4865
4866 def _CheckScreenshotRemoved(screenshots_dir, message_id):
4867 sha1_path = input_api.os_path.join(
4868 screenshots_dir, message_id + '.png.sha1')
meacere7be7532019-10-02 17:41:034869 if input_api.os_path.exists(sha1_path) and sha1_path not in removed_paths:
Mustafa Emre Acer29bf6ac92018-07-30 21:42:144870 unnecessary_sha1_files.append(sha1_path)
4871
Rainhard Findlingfc31844c52020-05-15 09:58:264872
4873 def _ValidateIcuSyntax(text, level, signatures):
4874 """Validates ICU syntax of a text string.
4875
4876 Check if text looks similar to ICU and checks for ICU syntax correctness
4877 in this case. Reports various issues with ICU syntax and values of
4878 variants. Supports checking of nested messages. Accumulate information of
4879 each ICU messages found in the text for further checking.
4880
4881 Args:
4882 text: a string to check.
4883 level: a number of current nesting level.
4884 signatures: an accumulator, a list of tuple of (level, variable,
4885 kind, variants).
4886
4887 Returns:
4888 None if a string is not ICU or no issue detected.
4889 A tuple of (message, start index, end index) if an issue detected.
4890 """
4891 valid_types = {
4892 'plural': (frozenset(
4893 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many', 'other']),
4894 frozenset(['=1', 'other'])),
4895 'selectordinal': (frozenset(
4896 ['=0', '=1', 'zero', 'one', 'two', 'few', 'many', 'other']),
4897 frozenset(['one', 'other'])),
4898 'select': (frozenset(), frozenset(['other'])),
4899 }
4900
4901 # Check if the message looks like an attempt to use ICU
4902 # plural. If yes - check if its syntax strictly matches ICU format.
4903 like = re.match(r'^[^{]*\{[^{]*\b(plural|selectordinal|select)\b', text)
4904 if not like:
4905 signatures.append((level, None, None, None))
4906 return
4907
4908 # Check for valid prefix and suffix
4909 m = re.match(
4910 r'^([^{]*\{)([a-zA-Z0-9_]+),\s*'
4911 r'(plural|selectordinal|select),\s*'
4912 r'(?:offset:\d+)?\s*(.*)', text, re.DOTALL)
4913 if not m:
4914 return (('This message looks like an ICU plural, '
4915 'but does not follow ICU syntax.'), like.start(), like.end())
4916 starting, variable, kind, variant_pairs = m.groups()
4917 variants, depth, last_pos = _ParseIcuVariants(variant_pairs, m.start(4))
4918 if depth:
4919 return ('Invalid ICU format. Unbalanced opening bracket', last_pos,
4920 len(text))
4921 first = text[0]
4922 ending = text[last_pos:]
4923 if not starting:
4924 return ('Invalid ICU format. No initial opening bracket', last_pos - 1,
4925 last_pos)
4926 if not ending or '}' not in ending:
4927 return ('Invalid ICU format. No final closing bracket', last_pos - 1,
4928 last_pos)
4929 elif first != '{':
4930 return (
4931 ('Invalid ICU format. Extra characters at the start of a complex '
4932 'message (go/icu-message-migration): "%s"') %
4933 starting, 0, len(starting))
4934 elif ending != '}':
4935 return (('Invalid ICU format. Extra characters at the end of a complex '
4936 'message (go/icu-message-migration): "%s"')
4937 % ending, last_pos - 1, len(text) - 1)
4938 if kind not in valid_types:
4939 return (('Unknown ICU message type %s. '
4940 'Valid types are: plural, select, selectordinal') % kind, 0, 0)
4941 known, required = valid_types[kind]
4942 defined_variants = set()
4943 for variant, variant_range, value, value_range in variants:
4944 start, end = variant_range
4945 if variant in defined_variants:
4946 return ('Variant "%s" is defined more than once' % variant,
4947 start, end)
4948 elif known and variant not in known:
4949 return ('Variant "%s" is not valid for %s message' % (variant, kind),
4950 start, end)
4951 defined_variants.add(variant)
4952 # Check for nested structure
4953 res = _ValidateIcuSyntax(value[1:-1], level + 1, signatures)
4954 if res:
4955 return (res[0], res[1] + value_range[0] + 1,
4956 res[2] + value_range[0] + 1)
4957 missing = required - defined_variants
4958 if missing:
4959 return ('Required variants missing: %s' % ', '.join(missing), 0,
4960 len(text))
4961 signatures.append((level, variable, kind, defined_variants))
4962
4963
4964 def _ParseIcuVariants(text, offset=0):
4965 """Parse variants part of ICU complex message.
4966
4967 Builds a tuple of variant names and values, as well as
4968 their offsets in the input string.
4969
4970 Args:
4971 text: a string to parse
4972 offset: additional offset to add to positions in the text to get correct
4973 position in the complete ICU string.
4974
4975 Returns:
4976 List of tuples, each tuple consist of four fields: variant name,
4977 variant name span (tuple of two integers), variant value, value
4978 span (tuple of two integers).
4979 """
4980 depth, start, end = 0, -1, -1
4981 variants = []
4982 key = None
4983 for idx, char in enumerate(text):
4984 if char == '{':
4985 if not depth:
4986 start = idx
4987 chunk = text[end + 1:start]
4988 key = chunk.strip()
4989 pos = offset + end + 1 + chunk.find(key)
4990 span = (pos, pos + len(key))
4991 depth += 1
4992 elif char == '}':
4993 if not depth:
4994 return variants, depth, offset + idx
4995 depth -= 1
4996 if not depth:
4997 end = idx
4998 variants.append((key, span, text[start:end + 1], (offset + start,
4999 offset + end + 1)))
5000 return variants, depth, offset + end + 1
5001
meacer8c0d3832019-12-26 21:46:165002 try:
5003 old_sys_path = sys.path
5004 sys.path = sys.path + [input_api.os_path.join(
5005 input_api.PresubmitLocalPath(), 'tools', 'translation')]
5006 from helper import grd_helper
5007 finally:
5008 sys.path = old_sys_path
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145009
5010 for f in affected_grds:
5011 file_path = f.LocalPath()
5012 old_id_to_msg_map = {}
5013 new_id_to_msg_map = {}
Mustafa Emre Acerd697ac92020-02-06 19:03:385014 # Note that this code doesn't check if the file has been deleted. This is
5015 # OK because it only uses the old and new file contents and doesn't load
5016 # the file via its path.
5017 # It's also possible that a file's content refers to a renamed or deleted
5018 # file via a <part> tag, such as <part file="now-deleted-file.grdp">. This
5019 # is OK as well, because grd_helper ignores <part> tags when loading .grd or
5020 # .grdp files.
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145021 if file_path.endswith('.grdp'):
5022 if f.OldContents():
meacerff8a9b62019-12-10 19:43:585023 old_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
Dirk Prankee3c9c62d2021-05-18 18:35:595024 '\n'.join(f.OldContents()))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145025 if f.NewContents():
meacerff8a9b62019-12-10 19:43:585026 new_id_to_msg_map = grd_helper.GetGrdpMessagesFromString(
Dirk Prankee3c9c62d2021-05-18 18:35:595027 '\n'.join(f.NewContents()))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145028 else:
meacerff8a9b62019-12-10 19:43:585029 file_dir = input_api.os_path.dirname(file_path) or '.'
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145030 if f.OldContents():
meacerff8a9b62019-12-10 19:43:585031 old_id_to_msg_map = grd_helper.GetGrdMessages(
Dirk Prankee3c9c62d2021-05-18 18:35:595032 StringIO('\n'.join(f.OldContents())), file_dir)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145033 if f.NewContents():
meacerff8a9b62019-12-10 19:43:585034 new_id_to_msg_map = grd_helper.GetGrdMessages(
Dirk Prankee3c9c62d2021-05-18 18:35:595035 StringIO('\n'.join(f.NewContents())), file_dir)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145036
Rainhard Findlingd8d04372020-08-13 13:30:095037 grd_name, ext = input_api.os_path.splitext(
5038 input_api.os_path.basename(file_path))
5039 screenshots_dir = input_api.os_path.join(
5040 input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
5041
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145042 # Compute added, removed and modified message IDs.
5043 old_ids = set(old_id_to_msg_map)
5044 new_ids = set(new_id_to_msg_map)
5045 added_ids = new_ids - old_ids
5046 removed_ids = old_ids - new_ids
5047 modified_ids = set([])
5048 for key in old_ids.intersection(new_ids):
Rainhard Findling1a3e71e2020-09-21 07:33:355049 if (old_id_to_msg_map[key].ContentsAsXml('', True)
Rainhard Findlingd8d04372020-08-13 13:30:095050 != new_id_to_msg_map[key].ContentsAsXml('', True)):
5051 # The message content itself changed. Require an updated screenshot.
5052 modified_ids.add(key)
Rainhard Findling1a3e71e2020-09-21 07:33:355053 elif old_id_to_msg_map[key].attrs['meaning'] != \
5054 new_id_to_msg_map[key].attrs['meaning']:
5055 # The message meaning changed. Ensure there is a screenshot for it.
5056 sha1_path = input_api.os_path.join(screenshots_dir, key + '.png.sha1')
5057 if sha1_path not in new_or_added_paths and not \
5058 input_api.os_path.exists(sha1_path):
5059 # There is neither a previous screenshot nor is a new one added now.
5060 # Require a screenshot.
5061 modified_ids.add(key)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145062
Rainhard Findlingfc31844c52020-05-15 09:58:265063 if run_screenshot_check:
5064 # Check the screenshot directory for .png files. Warn if there is any.
5065 for png_path in affected_png_paths:
5066 if png_path.startswith(screenshots_dir):
5067 unnecessary_screenshots.append(png_path)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145068
Rainhard Findlingfc31844c52020-05-15 09:58:265069 for added_id in added_ids:
5070 _CheckScreenshotAdded(screenshots_dir, added_id)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145071
Rainhard Findlingfc31844c52020-05-15 09:58:265072 for modified_id in modified_ids:
5073 _CheckScreenshotAdded(screenshots_dir, modified_id)
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145074
Rainhard Findlingfc31844c52020-05-15 09:58:265075 for removed_id in removed_ids:
5076 _CheckScreenshotRemoved(screenshots_dir, removed_id)
5077
5078 # Check new and changed strings for ICU syntax errors.
5079 for key in added_ids.union(modified_ids):
5080 msg = new_id_to_msg_map[key].ContentsAsXml('', True)
5081 err = _ValidateIcuSyntax(msg, 0, [])
5082 if err is not None:
5083 icu_syntax_errors.append(str(key) + ': ' + str(err[0]))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145084
5085 results = []
Rainhard Findlingfc31844c52020-05-15 09:58:265086 if run_screenshot_check:
5087 if unnecessary_screenshots:
Mustafa Emre Acerc6ed2682020-07-07 07:24:005088 results.append(output_api.PresubmitError(
Rainhard Findlingfc31844c52020-05-15 09:58:265089 'Do not include actual screenshots in the changelist. Run '
5090 'tools/translate/upload_screenshots.py to upload them instead:',
5091 sorted(unnecessary_screenshots)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145092
Rainhard Findlingfc31844c52020-05-15 09:58:265093 if missing_sha1:
Mustafa Emre Acerc6ed2682020-07-07 07:24:005094 results.append(output_api.PresubmitError(
Rainhard Findlingfc31844c52020-05-15 09:58:265095 'You are adding or modifying UI strings.\n'
5096 'To ensure the best translations, take screenshots of the relevant UI '
5097 '(https://g.co/chrome/translation) and add these files to your '
5098 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145099
Rainhard Findlingfc31844c52020-05-15 09:58:265100 if unnecessary_sha1_files:
Mustafa Emre Acerc6ed2682020-07-07 07:24:005101 results.append(output_api.PresubmitError(
Rainhard Findlingfc31844c52020-05-15 09:58:265102 'You removed strings associated with these files. Remove:',
5103 sorted(unnecessary_sha1_files)))
5104 else:
5105 results.append(output_api.PresubmitPromptOrNotify('Skipping translation '
5106 'screenshots check.'))
5107
5108 if icu_syntax_errors:
Rainhard Findling0e8d74c12020-06-26 13:48:075109 results.append(output_api.PresubmitPromptWarning(
Rainhard Findlingfc31844c52020-05-15 09:58:265110 'ICU syntax errors were found in the following strings (problems or '
5111 'feedback? Contact [email protected]):', items=icu_syntax_errors))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:145112
5113 return results
Mustafa Emre Acer51f2f742020-03-09 19:41:125114
5115
Saagar Sanghavifceeaae2020-08-12 16:40:365116def CheckTranslationExpectations(input_api, output_api,
Mustafa Emre Acer51f2f742020-03-09 19:41:125117 repo_root=None,
5118 translation_expectations_path=None,
5119 grd_files=None):
5120 import sys
5121 affected_grds = [f for f in input_api.AffectedFiles()
5122 if (f.LocalPath().endswith('.grd') or
5123 f.LocalPath().endswith('.grdp'))]
5124 if not affected_grds:
5125 return []
5126
5127 try:
5128 old_sys_path = sys.path
5129 sys.path = sys.path + [
5130 input_api.os_path.join(
5131 input_api.PresubmitLocalPath(), 'tools', 'translation')]
5132 from helper import git_helper
5133 from helper import translation_helper
5134 finally:
5135 sys.path = old_sys_path
5136
5137 # Check that translation expectations can be parsed and we can get a list of
5138 # translatable grd files. |repo_root| and |translation_expectations_path| are
5139 # only passed by tests.
5140 if not repo_root:
5141 repo_root = input_api.PresubmitLocalPath()
5142 if not translation_expectations_path:
5143 translation_expectations_path = input_api.os_path.join(
5144 repo_root, 'tools', 'gritsettings',
5145 'translation_expectations.pyl')
5146 if not grd_files:
5147 grd_files = git_helper.list_grds_in_repository(repo_root)
5148
dpapad8e21b472020-10-23 17:15:035149 # Ignore bogus grd files used only for testing
5150 # ui/webui/resoucres/tools/generate_grd.py.
5151 ignore_path = input_api.os_path.join(
5152 'ui', 'webui', 'resources', 'tools', 'tests')
Dirk Prankee3c9c62d2021-05-18 18:35:595153 grd_files = [p for p in grd_files if ignore_path not in p]
dpapad8e21b472020-10-23 17:15:035154
Mustafa Emre Acer51f2f742020-03-09 19:41:125155 try:
5156 translation_helper.get_translatable_grds(repo_root, grd_files,
5157 translation_expectations_path)
5158 except Exception as e:
5159 return [output_api.PresubmitNotifyResult(
5160 'Failed to get a list of translatable grd files. This happens when:\n'
5161 ' - One of the modified grd or grdp files cannot be parsed or\n'
5162 ' - %s is not updated.\n'
5163 'Stack:\n%s' % (translation_expectations_path, str(e)))]
5164 return []
Ken Rockotc31f4832020-05-29 18:58:515165
5166
Saagar Sanghavifceeaae2020-08-12 16:40:365167def CheckStableMojomChanges(input_api, output_api):
Ken Rockotc31f4832020-05-29 18:58:515168 """Changes to [Stable] mojom types must preserve backward-compatibility."""
Ken Rockotad7901f942020-06-04 20:17:095169 changed_mojoms = input_api.AffectedFiles(
5170 include_deletes=True,
5171 file_filter=lambda f: f.LocalPath().endswith(('.mojom')))
Ken Rockotc31f4832020-05-29 18:58:515172 delta = []
5173 for mojom in changed_mojoms:
5174 old_contents = ''.join(mojom.OldContents()) or None
5175 new_contents = ''.join(mojom.NewContents()) or None
5176 delta.append({
5177 'filename': mojom.LocalPath(),
5178 'old': '\n'.join(mojom.OldContents()) or None,
5179 'new': '\n'.join(mojom.NewContents()) or None,
5180 })
5181
5182 process = input_api.subprocess.Popen(
5183 [input_api.python_executable,
5184 input_api.os_path.join(input_api.PresubmitLocalPath(), 'mojo',
5185 'public', 'tools', 'mojom',
5186 'check_stable_mojom_compatibility.py'),
5187 '--src-root', input_api.PresubmitLocalPath()],
5188 stdin=input_api.subprocess.PIPE,
5189 stdout=input_api.subprocess.PIPE,
5190 stderr=input_api.subprocess.PIPE,
5191 universal_newlines=True)
5192 (x, error) = process.communicate(input=input_api.json.dumps(delta))
5193 if process.returncode:
5194 return [output_api.PresubmitError(
5195 'One or more [Stable] mojom definitions appears to have been changed '
5196 'in a way that is not backward-compatible.',
5197 long_text=error)]
5198 return []
Dominic Battre645d42342020-12-04 16:14:105199
5200def CheckDeprecationOfPreferences(input_api, output_api):
5201 """Removing a preference should come with a deprecation."""
5202
5203 def FilterFile(affected_file):
5204 """Accept only .cc files and the like."""
5205 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
5206 files_to_skip = (_EXCLUDED_PATHS +
5207 _TEST_CODE_EXCLUDED_PATHS +
5208 input_api.DEFAULT_FILES_TO_SKIP)
5209 return input_api.FilterSourceFile(
5210 affected_file,
5211 files_to_check=file_inclusion_pattern,
5212 files_to_skip=files_to_skip)
5213
5214 def ModifiedLines(affected_file):
5215 """Returns a list of tuples (line number, line text) of added and removed
5216 lines.
5217
5218 Deleted lines share the same line number as the previous line.
5219
5220 This relies on the scm diff output describing each changed code section
5221 with a line of the form
5222
5223 ^@@ <old line num>,<old size> <new line num>,<new size> @@$
5224 """
5225 line_num = 0
5226 modified_lines = []
5227 for line in affected_file.GenerateScmDiff().splitlines():
5228 # Extract <new line num> of the patch fragment (see format above).
5229 m = input_api.re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@', line)
5230 if m:
5231 line_num = int(m.groups(1)[0])
5232 continue
5233 if ((line.startswith('+') and not line.startswith('++')) or
5234 (line.startswith('-') and not line.startswith('--'))):
5235 modified_lines.append((line_num, line))
5236
5237 if not line.startswith('-'):
5238 line_num += 1
5239 return modified_lines
5240
5241 def FindLineWith(lines, needle):
5242 """Returns the line number (i.e. index + 1) in `lines` containing `needle`.
5243
5244 If 0 or >1 lines contain `needle`, -1 is returned.
5245 """
5246 matching_line_numbers = [
5247 # + 1 for 1-based counting of line numbers.
5248 i + 1 for i, line
5249 in enumerate(lines)
5250 if needle in line]
5251 return matching_line_numbers[0] if len(matching_line_numbers) == 1 else -1
5252
5253 def ModifiedPrefMigration(affected_file):
5254 """Returns whether the MigrateObsolete.*Pref functions were modified."""
5255 # Determine first and last lines of MigrateObsolete.*Pref functions.
5256 new_contents = affected_file.NewContents();
5257 range_1 = (
5258 FindLineWith(new_contents, 'BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'),
5259 FindLineWith(new_contents, 'END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'))
5260 range_2 = (
5261 FindLineWith(new_contents, 'BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS'),
5262 FindLineWith(new_contents, 'END_MIGRATE_OBSOLETE_PROFILE_PREFS'))
5263 if (-1 in range_1 + range_2):
5264 raise Exception(
5265 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.')
5266
5267 # Check whether any of the modified lines are part of the
5268 # MigrateObsolete.*Pref functions.
5269 for line_nr, line in ModifiedLines(affected_file):
5270 if (range_1[0] <= line_nr <= range_1[1] or
5271 range_2[0] <= line_nr <= range_2[1]):
5272 return True
5273 return False
5274
5275 register_pref_pattern = input_api.re.compile(r'Register.+Pref')
5276 browser_prefs_file_pattern = input_api.re.compile(
5277 r'chrome/browser/prefs/browser_prefs.cc')
5278
5279 changes = input_api.AffectedFiles(include_deletes=True,
5280 file_filter=FilterFile)
5281 potential_problems = []
5282 for f in changes:
5283 for line in f.GenerateScmDiff().splitlines():
5284 # Check deleted lines for pref registrations.
5285 if (line.startswith('-') and not line.startswith('--') and
5286 register_pref_pattern.search(line)):
5287 potential_problems.append('%s: %s' % (f.LocalPath(), line))
5288
5289 if browser_prefs_file_pattern.search(f.LocalPath()):
5290 # If the developer modified the MigrateObsolete.*Prefs() functions, we
5291 # assume that they knew that they have to deprecate preferences and don't
5292 # warn.
5293 try:
5294 if ModifiedPrefMigration(f):
5295 return []
5296 except Exception as e:
5297 return [output_api.PresubmitError(str(e))]
5298
5299 if potential_problems:
5300 return [output_api.PresubmitPromptWarning(
5301 'Discovered possible removal of preference registrations.\n\n'
5302 'Please make sure to properly deprecate preferences by clearing their\n'
5303 'value for a couple of milestones before finally removing the code.\n'
5304 'Otherwise data may stay in the preferences files forever. See\n'
Gabriel Charetteecb784302021-04-13 14:17:195305 'Migrate*Prefs() in chrome/browser/prefs/browser_prefs.cc and\n'
5306 'chrome/browser/prefs/README.md for examples.\n'
Dominic Battre645d42342020-12-04 16:14:105307 'This may be a false positive warning (e.g. if you move preference\n'
5308 'registrations to a different place).\n',
5309 potential_problems
5310 )]
5311 return []
Matt Stark6ef08872021-07-29 01:21:465312
5313def CheckConsistentGrdChanges(input_api, output_api):
5314 """Changes to GRD files must be consistent for tools to read them."""
5315 changed_grds = input_api.AffectedFiles(
5316 include_deletes=False,
5317 file_filter=lambda f: f.LocalPath().endswith(('.grd')))
5318 errors = []
5319 invalid_file_regexes = [(input_api.re.compile(matcher), msg) for matcher, msg in _INVALID_GRD_FILE_LINE]
5320 for grd in changed_grds:
5321 for i, line in enumerate(grd.NewContents()):
5322 for matcher, msg in invalid_file_regexes:
5323 if matcher.search(line):
5324 errors.append(output_api.PresubmitError('Problem on {grd}:{i} - {msg}'.format(grd=grd.LocalPath(), i=i + 1, msg=msg)))
5325 return errors
5326
5327