blob: c5cee8359254f66e48295f4f438ef241407c7a18 [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"""
10
[email protected]eea609a2011-11-18 13:10:1211
[email protected]379e7dd2010-01-28 17:39:2112_EXCLUDED_PATHS = (
[email protected]40d1dbb2012-10-26 07:18:0013 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
14 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
[email protected]8886ffcb2013-02-12 04:56:2815 r"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
[email protected]a18130a2012-01-03 17:52:0816 r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
[email protected]3e4eb112011-01-18 03:29:5417 r"^skia[\\\/].*",
Kent Tamurae9b3a9ec2017-08-31 02:20:1918 r"^third_party[\\\/](WebKit|blink)[\\\/].*",
Mark Mentovaiebb9ddd62017-09-25 17:24:4119 r"^third_party[\\\/]breakpad[\\\/].*",
[email protected]3e4eb112011-01-18 03:29:5420 r"^v8[\\\/].*",
21 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5322 r".+_autogen\.h$",
[email protected]ce145c02012-09-06 09:49:3423 r".+[\\\/]pnacl_shim\.c$",
[email protected]e07b6ac72013-08-20 00:30:4224 r"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
primiano0166ccc82015-10-06 12:12:2825 r"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js",
vapierb2053f542017-03-09 19:46:1026 r"tools[\\\/]md_browser[\\\/].*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1427 # Test pages for Maps telemetry tests.
28 r"tools[\\\/]perf[\\\/]page_sets[\\\/]maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5429 # Test pages for WebRTC telemetry tests.
30 r"tools[\\\/]perf[\\\/]page_sets[\\\/]webrtc_cases.*",
[email protected]4306417642009-06-11 00:33:4031)
[email protected]ca8d1982009-02-19 16:33:1232
wnwenbdc444e2016-05-25 13:44:1533
[email protected]06e6d0ff2012-12-11 01:36:4434# Fragment of a regular expression that matches C++ and Objective-C++
35# implementation files.
36_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
37
wnwenbdc444e2016-05-25 13:44:1538
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:1939# Fragment of a regular expression that matches C++ and Objective-C++
40# header files.
41_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
42
43
[email protected]06e6d0ff2012-12-11 01:36:4444# Regular expression that matches code only used for test binaries
45# (best effort).
46_TEST_CODE_EXCLUDED_PATHS = (
joaodasilva718f87672014-08-30 09:25:4947 r'.*[\\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4448 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
Steven Holte27008b7422018-01-29 20:55:4449 r'.+_(api|browser|eg|int|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
[email protected]e2d7e6f2013-04-23 12:57:1250 _IMPLEMENTATION_EXTENSIONS,
[email protected]06e6d0ff2012-12-11 01:36:4451 r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
joaodasilva718f87672014-08-30 09:25:4952 r'.*[\\\/](test|tool(s)?)[\\\/].*',
[email protected]ef070cc2013-05-03 11:53:0553 # content_shell is used for running layout tests.
joaodasilva718f87672014-08-30 09:25:4954 r'content[\\\/]shell[\\\/].*',
[email protected]7b054982013-11-27 00:44:4755 # Non-production example code.
joaodasilva718f87672014-08-30 09:25:4956 r'mojo[\\\/]examples[\\\/].*',
[email protected]8176de12014-06-20 19:07:0857 # Launcher for running iOS tests on the simulator.
joaodasilva718f87672014-08-30 09:25:4958 r'testing[\\\/]iossim[\\\/]iossim\.mm$',
[email protected]06e6d0ff2012-12-11 01:36:4459)
[email protected]ca8d1982009-02-19 16:33:1260
wnwenbdc444e2016-05-25 13:44:1561
[email protected]eea609a2011-11-18 13:10:1262_TEST_ONLY_WARNING = (
63 'You might be calling functions intended only for testing from\n'
64 'production code. It is OK to ignore this warning if you know what\n'
65 'you are doing, as the heuristics used to detect the situation are\n'
[email protected]b0149772014-03-27 16:47:5866 'not perfect. The commit queue will not block on this warning.')
[email protected]eea609a2011-11-18 13:10:1267
68
[email protected]cf9b78f2012-11-14 11:40:2869_INCLUDE_ORDER_WARNING = (
marjaa017dc482015-03-09 17:13:4070 'Your #include order seems to be broken. Remember to use the right '
avice9a8982015-11-24 20:36:2171 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
72 'cppguide.html#Names_and_Order_of_Includes')
[email protected]cf9b78f2012-11-14 11:40:2873
wnwenbdc444e2016-05-25 13:44:1574
Eric Stevensona9a980972017-09-23 00:04:4175_BANNED_JAVA_FUNCTIONS = (
76 (
77 'StrictMode.allowThreadDiskReads()',
78 (
79 'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
80 'directly.',
81 ),
82 False,
83 ),
84 (
85 'StrictMode.allowThreadDiskWrites()',
86 (
87 'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
88 'directly.',
89 ),
90 False,
91 ),
92)
93
[email protected]127f18ec2012-06-16 05:05:5994_BANNED_OBJC_FUNCTIONS = (
95 (
96 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:2097 (
98 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:5999 'prohibited. Please use CrTrackingArea instead.',
100 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
101 ),
102 False,
103 ),
104 (
[email protected]eaae1972014-04-16 04:17:26105 r'/NSTrackingArea\W',
[email protected]23e6cbc2012-06-16 18:51:20106 (
107 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:59108 'instead.',
109 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
110 ),
111 False,
112 ),
113 (
114 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20115 (
116 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59117 'Please use |convertPoint:(point) fromView:nil| instead.',
118 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
119 ),
120 True,
121 ),
122 (
123 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:20124 (
125 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59126 'Please use |convertPoint:(point) toView:nil| instead.',
127 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
128 ),
129 True,
130 ),
131 (
132 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20133 (
134 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59135 'Please use |convertRect:(point) fromView:nil| instead.',
136 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
137 ),
138 True,
139 ),
140 (
141 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20142 (
143 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59144 'Please use |convertRect:(point) toView:nil| instead.',
145 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
146 ),
147 True,
148 ),
149 (
150 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20151 (
152 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59153 'Please use |convertSize:(point) fromView:nil| instead.',
154 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
155 ),
156 True,
157 ),
158 (
159 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20160 (
161 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59162 'Please use |convertSize:(point) toView:nil| instead.',
163 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
164 ),
165 True,
166 ),
jif65398702016-10-27 10:19:48167 (
168 r"/\s+UTF8String\s*]",
169 (
170 'The use of -[NSString UTF8String] is dangerous as it can return null',
171 'even if |canBeConvertedToEncoding:NSUTF8StringEncoding| returns YES.',
172 'Please use |SysNSStringToUTF8| instead.',
173 ),
174 True,
175 ),
Sylvain Defresne4cf1d182017-09-18 14:16:34176 (
177 r'__unsafe_unretained',
178 (
179 'The use of __unsafe_unretained is almost certainly wrong, unless',
180 'when interacting with NSFastEnumeration or NSInvocation.',
181 'Please use __weak in files build with ARC, nothing otherwise.',
182 ),
183 False,
184 ),
[email protected]127f18ec2012-06-16 05:05:59185)
186
Sylvain Defresnea8b73d252018-02-28 15:45:54187_BANNED_IOS_OBJC_FUNCTIONS = (
188 (
189 r'/\bTEST[(]',
190 (
191 'TEST() macro should not be used in Objective-C++ code as it does not ',
192 'drain the autorelease pool at the end of the test. Use TEST_F() ',
193 'macro instead with a fixture inheriting from PlatformTest (or a ',
194 'typedef).'
195 ),
196 True,
197 ),
198 (
199 r'/\btesting::Test\b',
200 (
201 'testing::Test should not be used in Objective-C++ code as it does ',
202 'not drain the autorelease pool at the end of the test. Use ',
203 'PlatformTest instead.'
204 ),
205 True,
206 ),
207)
208
[email protected]127f18ec2012-06-16 05:05:59209
210_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20211 # Make sure that gtest's FRIEND_TEST() macro is not used; the
212 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
[email protected]e00ccc92012-11-01 17:32:30213 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
[email protected]23e6cbc2012-06-16 18:51:20214 (
thomasandersone7caaa9b2017-03-29 19:22:53215 r'\bNULL\b',
216 (
217 'New code should not use NULL. Use nullptr instead.',
218 ),
219 True,
220 (),
221 ),
222 (
[email protected]23e6cbc2012-06-16 18:51:20223 'FRIEND_TEST(',
224 (
[email protected]e3c945502012-06-26 20:01:49225 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20226 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
227 ),
228 False,
[email protected]7345da02012-11-27 14:31:49229 (),
[email protected]23e6cbc2012-06-16 18:51:20230 ),
231 (
thomasanderson4b569052016-09-14 20:15:53232 r'XSelectInput|CWEventMask|XCB_CW_EVENT_MASK',
233 (
234 'Chrome clients wishing to select events on X windows should use',
235 'ui::XScopedEventSelector. It is safe to ignore this warning only if',
236 'you are selecting events from the GPU process, or if you are using',
237 'an XDisplay other than gfx::GetXDisplay().',
238 ),
239 True,
240 (
241 r"^ui[\\\/]gl[\\\/].*\.cc$",
242 r"^media[\\\/]gpu[\\\/].*\.cc$",
243 r"^gpu[\\\/].*\.cc$",
244 ),
245 ),
246 (
thomasandersone043e3ce2017-06-08 00:43:20247 r'XInternAtom|xcb_intern_atom',
248 (
thomasanderson11aa41d2017-06-08 22:22:38249 'Use gfx::GetAtom() instead of interning atoms directly.',
thomasandersone043e3ce2017-06-08 00:43:20250 ),
251 True,
252 (
thomasanderson11aa41d2017-06-08 22:22:38253 r"^gpu[\\\/]ipc[\\\/]service[\\\/]gpu_watchdog_thread\.cc$",
254 r"^remoting[\\\/]host[\\\/]linux[\\\/]x_server_clipboard\.cc$",
thomasandersone043e3ce2017-06-08 00:43:20255 r"^ui[\\\/]gfx[\\\/]x[\\\/]x11_atom_cache\.cc$",
256 ),
257 ),
258 (
tomhudsone2c14d552016-05-26 17:07:46259 'setMatrixClip',
260 (
261 'Overriding setMatrixClip() is prohibited; ',
262 'the base function is deprecated. ',
263 ),
264 True,
265 (),
266 ),
267 (
[email protected]52657f62013-05-20 05:30:31268 'SkRefPtr',
269 (
270 'The use of SkRefPtr is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22271 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31272 ),
273 True,
274 (),
275 ),
276 (
277 'SkAutoRef',
278 (
279 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
tomhudson7e6e0512016-04-19 19:27:22280 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31281 ),
282 True,
283 (),
284 ),
285 (
286 'SkAutoTUnref',
287 (
288 'The use of SkAutoTUnref is dangerous because it implicitly ',
tomhudson7e6e0512016-04-19 19:27:22289 'converts to a raw pointer. Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31290 ),
291 True,
292 (),
293 ),
294 (
295 'SkAutoUnref',
296 (
297 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
298 'because it implicitly converts to a raw pointer. ',
tomhudson7e6e0512016-04-19 19:27:22299 'Please use sk_sp<> instead.'
[email protected]52657f62013-05-20 05:30:31300 ),
301 True,
302 (),
303 ),
[email protected]d89eec82013-12-03 14:10:59304 (
305 r'/HANDLE_EINTR\(.*close',
306 (
307 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
308 'descriptor will be closed, and it is incorrect to retry the close.',
309 'Either call close directly and ignore its return value, or wrap close',
310 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
311 ),
312 True,
313 (),
314 ),
315 (
316 r'/IGNORE_EINTR\((?!.*close)',
317 (
318 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
319 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
320 ),
321 True,
322 (
323 # Files that #define IGNORE_EINTR.
324 r'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
325 r'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
326 ),
327 ),
[email protected]ec5b3f02014-04-04 18:43:43328 (
329 r'/v8::Extension\(',
330 (
331 'Do not introduce new v8::Extensions into the code base, use',
332 'gin::Wrappable instead. See http://crbug.com/334679',
333 ),
334 True,
[email protected]f55c90ee62014-04-12 00:50:03335 (
joaodasilva718f87672014-08-30 09:25:49336 r'extensions[\\\/]renderer[\\\/]safe_builtins\.*',
[email protected]f55c90ee62014-04-12 00:50:03337 ),
[email protected]ec5b3f02014-04-04 18:43:43338 ),
skyostilf9469f72015-04-20 10:38:52339 (
jame2d1a952016-04-02 00:27:10340 '#pragma comment(lib,',
341 (
342 'Specify libraries to link with in build files and not in the source.',
343 ),
344 True,
Mirko Bonadeif4f0f0e2018-04-12 09:29:41345 (
346 r'^third_party[\\\/]abseil-cpp[\\\/].*',
347 ),
jame2d1a952016-04-02 00:27:10348 ),
fdorayc4ac18d2017-05-01 21:39:59349 (
Gabriel Charette7cc6c432018-04-25 20:52:02350 r'/base::SequenceChecker\b',
gabd52c912a2017-05-11 04:15:59351 (
352 'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
353 ),
354 False,
355 (),
356 ),
357 (
Gabriel Charette7cc6c432018-04-25 20:52:02358 r'/base::ThreadChecker\b',
gabd52c912a2017-05-11 04:15:59359 (
360 'Consider using THREAD_CHECKER macros instead of the class directly.',
361 ),
362 False,
363 (),
364 ),
dbeamb6f4fde2017-06-15 04:03:06365 (
Yuri Wiitala2f8de5c2017-07-21 00:11:06366 r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
367 (
368 'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
369 'deprecated (http://crbug.com/634507). Please avoid converting away',
370 'from the Time types in Chromium code, especially if any math is',
371 'being done on time values. For interfacing with platform/library',
372 'APIs, use FromMicroseconds() or InMicroseconds(), or one of the other',
373 'type converter methods instead. For faking TimeXXX values (for unit',
374 'testing only), use TimeXXX() + TimeDelta::FromMicroseconds(N). For',
375 'other use cases, please contact base/time/OWNERS.',
376 ),
377 False,
378 (),
379 ),
380 (
dbeamb6f4fde2017-06-15 04:03:06381 'CallJavascriptFunctionUnsafe',
382 (
383 "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
384 'AllowJavascript(), OnJavascriptAllowed()/OnJavascriptDisallowed(),',
385 'and CallJavascriptFunction(). See https://goo.gl/qivavq.',
386 ),
387 False,
388 (
389 r'^content[\\\/]browser[\\\/]webui[\\\/]web_ui_impl\.(cc|h)$',
390 r'^content[\\\/]public[\\\/]browser[\\\/]web_ui\.h$',
391 r'^content[\\\/]public[\\\/]test[\\\/]test_web_ui\.(cc|h)$',
392 ),
393 ),
dskiba1474c2bfd62017-07-20 02:19:24394 (
395 'leveldb::DB::Open',
396 (
397 'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
398 'third_party/leveldatabase/env_chromium.h. It exposes databases to',
399 "Chrome's tracing, making their memory usage visible.",
400 ),
401 True,
402 (
403 r'^third_party/leveldatabase/.*\.(cc|h)$',
404 ),
Gabriel Charette0592c3a2017-07-26 12:02:04405 ),
406 (
Chris Mumfordc38afb62017-10-09 17:55:08407 'leveldb::NewMemEnv',
408 (
409 'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
Chris Mumford8d26d10a2018-04-20 17:07:58410 'third_party/leveldatabase/leveldb_chrome.h. It exposes environments',
411 "to Chrome's tracing, making their memory usage visible.",
Chris Mumfordc38afb62017-10-09 17:55:08412 ),
413 True,
414 (
415 r'^third_party/leveldatabase/.*\.(cc|h)$',
416 ),
417 ),
418 (
Gabriel Charetted9839bc2017-07-29 14:17:47419 'RunLoop::QuitCurrent',
420 (
Robert Liao64b7ab22017-08-04 23:03:43421 'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
422 'methods of a specific RunLoop instance instead.',
Gabriel Charetted9839bc2017-07-29 14:17:47423 ),
Gabriel Charettec0a8f3ee2018-04-25 20:49:41424 False,
Gabriel Charetted9839bc2017-07-29 14:17:47425 (),
Gabriel Charettea44975052017-08-21 23:14:04426 ),
427 (
428 'base::ScopedMockTimeMessageLoopTaskRunner',
429 (
Gabriel Charette87cc1af2018-04-25 20:52:51430 'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
431 'ScopedTaskEnvironment::MainThreadType::MOCK_TIME. There are still a',
432 'few cases that may require a ScopedMockTimeMessageLoopTaskRunner',
433 '(i.e. mocking the main MessageLoopForUI in browser_tests), but check',
434 'with gab@ first if you think you need it)',
Gabriel Charettea44975052017-08-21 23:14:04435 ),
Gabriel Charette87cc1af2018-04-25 20:52:51436 False,
Gabriel Charettea44975052017-08-21 23:14:04437 (),
Eric Stevenson6b47b44c2017-08-30 20:41:57438 ),
439 (
440 r'std::regex',
441 (
442 'Using std::regex adds unnecessary binary size to Chrome. Please use',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02443 're2::RE2 instead (crbug.com/755321)',
Eric Stevenson6b47b44c2017-08-30 20:41:57444 ),
445 True,
446 (),
Francois Doray43670e32017-09-27 12:40:38447 ),
448 (
449 (r'/base::ThreadRestrictions::(ScopedAllowIO|AssertIOAllowed|'
450 r'DisallowWaiting|AssertWaitAllowed|SetWaitAllowed|ScopedAllowWait)'),
451 (
452 'Use the new API in base/threading/thread_restrictions.h.',
453 ),
Gabriel Charette04b138f2018-08-06 00:03:22454 False,
Francois Doray43670e32017-09-27 12:40:38455 (),
456 ),
Luis Hector Chavez9bbaed532017-11-30 18:25:38457 (
458 r'/\bbase::Bind\(',
459 (
Gabriel Charette147335ea2018-03-22 15:59:19460 'Please consider using base::Bind{Once,Repeating} instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02461 'of base::Bind. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38462 ),
463 False,
464 (),
465 ),
466 (
467 r'/\bbase::Callback<',
468 (
Gabriel Charette147335ea2018-03-22 15:59:19469 'Please consider using base::{Once,Repeating}Callback instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02470 'of base::Callback. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38471 ),
472 False,
473 (),
474 ),
475 (
476 r'/\bbase::Closure\b',
477 (
Gabriel Charette147335ea2018-03-22 15:59:19478 'Please consider using base::{Once,Repeating}Closure instead',
Mostyn Bramley-Moore6b427322017-12-21 22:11:02479 'of base::Closure. (crbug.com/714018)',
Luis Hector Chavez9bbaed532017-11-30 18:25:38480 ),
481 False,
482 (),
483 ),
Victor Costan3653df62018-02-08 21:38:16484 (
Gabriel Charette147335ea2018-03-22 15:59:19485 r'RunMessageLoop',
486 (
487 'RunMessageLoop is deprecated, use RunLoop instead.',
488 ),
489 False,
490 (),
491 ),
492 (
493 r'RunThisRunLoop',
494 (
495 'RunThisRunLoop is deprecated, use RunLoop directly instead.',
496 ),
497 False,
498 (),
499 ),
500 (
501 r'RunAllPendingInMessageLoop()',
502 (
503 "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
504 "if you're convinced you need this.",
505 ),
506 False,
507 (),
508 ),
509 (
510 r'RunAllPendingInMessageLoop(BrowserThread',
511 (
512 'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
513 'BrowserThread::UI, TestBrowserThreadBundle::RunIOThreadUntilIdle',
514 'for BrowserThread::IO, and prefer RunLoop::QuitClosure to observe',
515 'async events instead of flushing threads.',
516 ),
517 False,
518 (),
519 ),
520 (
521 r'MessageLoopRunner',
522 (
523 'MessageLoopRunner is deprecated, use RunLoop instead.',
524 ),
525 False,
526 (),
527 ),
528 (
529 r'GetDeferredQuitTaskForRunLoop',
530 (
531 "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
532 "gab@ if you found a use case where this is the only solution.",
533 ),
534 False,
535 (),
536 ),
537 (
Victor Costan3653df62018-02-08 21:38:16538 'sqlite3_initialize',
539 (
540 'Instead of sqlite3_initialize, depend on //sql, ',
541 '#include "sql/initialize.h" and use sql::EnsureSqliteInitialized().',
542 ),
543 True,
544 (
545 r'^sql/initialization\.(cc|h)$',
546 r'^third_party/sqlite/.*\.(c|cc|h)$',
547 ),
548 ),
Matt Menke7f520a82018-03-28 21:38:37549 (
550 'net::URLFetcher',
551 (
552 'net::URLFetcher should no longer be used in content embedders. ',
553 'Instead, use network::SimpleURLLoader instead, which supports ',
554 'an out-of-process network stack. ',
555 'net::URLFetcher may still be used in binaries that do not embed',
556 'content.',
557 ),
Matt Menke59716d02018-04-05 12:45:53558 False,
Matt Menke7f520a82018-03-28 21:38:37559 (
560 r'^ios[\\\/].*\.(cc|h)$',
561 r'.*[\\\/]ios[\\\/].*\.(cc|h)$',
562 r'.*_ios\.(cc|h)$',
563 r'^net[\\\/].*\.(cc|h)$',
564 r'.*[\\\/]tools[\\\/].*\.(cc|h)$',
565 ),
566 ),
jdoerried7d10ab2018-04-27 10:46:13567 (
568 r'/\barraysize\b',
569 (
570 "arraysize is deprecated, please use base::size(array) instead ",
571 "(https://crbug.com/837308). ",
572 ),
573 False,
574 (),
575 ),
tzik5de2157f2018-05-08 03:42:47576 (
577 r'std::random_shuffle',
578 (
579 'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
580 'base::RandomShuffle instead.'
581 ),
582 True,
583 (),
584 ),
[email protected]127f18ec2012-06-16 05:05:59585)
586
wnwenbdc444e2016-05-25 13:44:15587
mlamouria82272622014-09-16 18:45:04588_IPC_ENUM_TRAITS_DEPRECATED = (
589 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:50590 'See http://www.chromium.org/Home/chromium-security/education/'
591 'security-tips-for-ipc')
mlamouria82272622014-09-16 18:45:04592
Stephen Martinis97a394142018-06-07 23:06:05593_LONG_PATH_ERROR = (
594 'Some files included in this CL have file names that are too long (> 200'
595 ' characters). If committed, these files will cause issues on Windows. See'
596 ' https://crbug.com/612667 for more details.'
597)
598
Shenghua Zhangbfaa38b82017-11-16 21:58:02599_JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS = [
600 r".*[\\\/]BuildHooksAndroidImpl\.java",
601 r".*[\\\/]LicenseContentProvider\.java",
James Wallace-Leef31ae6c2018-05-01 23:30:20602 r".*[\\\/]PlatformServiceBridgeImpl.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:02603]
[email protected]127f18ec2012-06-16 05:05:59604
Sean Kau46e29bc2017-08-28 16:31:16605# These paths contain test data and other known invalid JSON files.
606_KNOWN_INVALID_JSON_FILE_PATTERNS = [
607 r'test[\\\/]data[\\\/]',
608 r'^components[\\\/]policy[\\\/]resources[\\\/]policy_templates\.json$',
609 r'^third_party[\\\/]protobuf[\\\/]',
Raphael Kubo da Costa211f3b472017-11-16 00:27:16610 r'^third_party[\\\/]WebKit[\\\/]LayoutTests[\\\/]external[\\\/]wpt[\\\/]',
Alexey Kozyatinskiya42a629f2018-04-17 17:49:38611 r'^third_party[\\\/]blink[\\\/]renderer[\\\/]devtools[\\\/]protocol\.json$',
Sean Kau46e29bc2017-08-28 16:31:16612]
613
614
[email protected]b00342e7f2013-03-26 16:21:54615_VALID_OS_MACROS = (
616 # Please keep sorted.
rayb0088ee52017-04-26 22:35:08617 'OS_AIX',
[email protected]b00342e7f2013-03-26 16:21:54618 'OS_ANDROID',
Henrique Nakashimaafff0502018-01-24 17:14:12619 'OS_ASMJS',
[email protected]b00342e7f2013-03-26 16:21:54620 'OS_BSD',
621 'OS_CAT', # For testing.
622 'OS_CHROMEOS',
623 'OS_FREEBSD',
scottmg2f97ee122017-05-12 17:50:37624 'OS_FUCHSIA',
[email protected]b00342e7f2013-03-26 16:21:54625 'OS_IOS',
626 'OS_LINUX',
627 'OS_MACOSX',
628 'OS_NACL',
hidehikof7295f22014-10-28 11:57:21629 'OS_NACL_NONSFI',
630 'OS_NACL_SFI',
krytarowski969759f2016-07-31 23:55:12631 'OS_NETBSD',
[email protected]b00342e7f2013-03-26 16:21:54632 'OS_OPENBSD',
633 'OS_POSIX',
[email protected]eda7afa12014-02-06 12:27:37634 'OS_QNX',
[email protected]b00342e7f2013-03-26 16:21:54635 'OS_SOLARIS',
[email protected]b00342e7f2013-03-26 16:21:54636 'OS_WIN',
637)
638
639
agrievef32bcc72016-04-04 14:57:40640_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Luob2e4b342018-09-20 19:32:39641 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36642 'base/android/jni_generator/jni_generator.pydeps',
643 'base/android/jni_generator/jni_registration_generator.pydeps',
644 'build/android/gyp/aar.pydeps',
645 'build/android/gyp/aidl.pydeps',
646 'build/android/gyp/apkbuilder.pydeps',
647 'build/android/gyp/app_bundle_to_apks.pydeps',
648 'build/android/gyp/bytecode_processor.pydeps',
649 'build/android/gyp/compile_resources.pydeps',
650 'build/android/gyp/create_bundle_wrapper_script.pydeps',
651 'build/android/gyp/copy_ex.pydeps',
652 'build/android/gyp/create_app_bundle.pydeps',
653 'build/android/gyp/create_apk_operations_script.pydeps',
654 'build/android/gyp/create_dist_jar.pydeps',
655 'build/android/gyp/create_java_binary_script.pydeps',
656 'build/android/gyp/create_stack_script.pydeps',
657 'build/android/gyp/create_test_runner_script.pydeps',
658 'build/android/gyp/create_tool_wrapper.pydeps',
659 'build/android/gyp/desugar.pydeps',
660 'build/android/gyp/dex.pydeps',
661 'build/android/gyp/dist_aar.pydeps',
662 'build/android/gyp/emma_instr.pydeps',
663 'build/android/gyp/filter_zip.pydeps',
664 'build/android/gyp/gcc_preprocess.pydeps',
665 'build/android/gyp/generate_proguarded_module_jar.pydeps',
666 'build/android/gyp/ijar.pydeps',
667 'build/android/gyp/java_cpp_enum.pydeps',
668 'build/android/gyp/javac.pydeps',
669 'build/android/gyp/jinja_template.pydeps',
670 'build/android/gyp/lint.pydeps',
671 'build/android/gyp/main_dex_list.pydeps',
672 'build/android/gyp/merge_jar_info_files.pydeps',
673 'build/android/gyp/merge_manifest.pydeps',
674 'build/android/gyp/prepare_resources.pydeps',
675 'build/android/gyp/proguard.pydeps',
676 'build/android/gyp/write_build_config.pydeps',
677 'build/android/gyp/write_ordered_libraries.pydeps',
678 'build/android/incremental_install/generate_android_manifest.pydeps',
679 'build/android/incremental_install/write_installer_json.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22680 'build/android/resource_sizes.pydeps',
agrievef32bcc72016-04-04 14:57:40681 'build/android/test_runner.pydeps',
hzl9b15df52017-03-23 23:43:04682 'build/android/test_wrapper/logdog_wrapper.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36683 'build/protoc_java.pydeps',
jbudorick276cc562017-04-29 01:34:58684 'build/secondary/third_party/android_platform/'
685 'development/scripts/stack.pydeps',
agrieve732db3a2016-04-26 19:18:19686 'net/tools/testserver/testserver.pydeps',
agrievef32bcc72016-04-04 14:57:40687]
688
wnwenbdc444e2016-05-25 13:44:15689
agrievef32bcc72016-04-04 14:57:40690_GENERIC_PYDEPS_FILES = [
John Chencde89192018-01-27 21:18:40691 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Cole Winstanley7045a1b2018-08-27 23:37:29692 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22693 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:40694]
695
wnwenbdc444e2016-05-25 13:44:15696
agrievef32bcc72016-04-04 14:57:40697_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
698
699
Eric Boren6fd2b932018-01-25 15:05:08700# Bypass the AUTHORS check for these accounts.
701_KNOWN_ROBOTS = set(
Chan52654f52018-03-21 21:02:29702 '%s-chromium-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com' % s
703 for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools',
Eric Boren36af476a2018-06-08 16:21:08704 'fuchsia-sdk', 'nacl', 'pdfium', 'perfetto', 'skia',
Eric Boren57cc805b2018-08-20 17:28:32705 'spirv', 'src-internal', 'webrtc')
Sergiy Byelozyorov47158a52018-06-13 22:38:59706 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:45707 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:59708 ) | set('%[email protected]' % s
Eric Boren835d71f2018-09-07 21:09:04709 for s in ('v8-ci-autoroll-builder',)
710 ) | set('%[email protected]' % s
711 for s in ('chromium-autoroll',)
712 ) | set('%[email protected]' % s
Eric Boren2b7e3c3c2018-09-13 18:14:30713 for s in ('chromium-internal-autoroll',))
Eric Boren6fd2b932018-01-25 15:05:08714
715
[email protected]55459852011-08-10 15:17:19716def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
717 """Attempts to prevent use of functions intended only for testing in
718 non-testing code. For now this is just a best-effort implementation
719 that ignores header files and may have some false positives. A
720 better implementation would probably need a proper C++ parser.
721 """
722 # We only scan .cc files and the like, as the declaration of
723 # for-testing functions in header files are hard to distinguish from
724 # calls to such functions without a proper C++ parser.
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49725 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:19726
jochenc0d4808c2015-07-27 09:25:42727 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
[email protected]55459852011-08-10 15:17:19728 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
[email protected]23501822014-05-14 02:06:09729 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
[email protected]55459852011-08-10 15:17:19730 exclusion_pattern = input_api.re.compile(
731 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
732 base_function_pattern, base_function_pattern))
733
734 def FilterFile(affected_file):
[email protected]06e6d0ff2012-12-11 01:36:44735 black_list = (_EXCLUDED_PATHS +
736 _TEST_CODE_EXCLUDED_PATHS +
737 input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19738 return input_api.FilterSourceFile(
739 affected_file,
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49740 white_list=file_inclusion_pattern,
[email protected]55459852011-08-10 15:17:19741 black_list=black_list)
742
743 problems = []
744 for f in input_api.AffectedSourceFiles(FilterFile):
745 local_path = f.LocalPath()
[email protected]825d27182014-01-02 21:24:24746 for line_number, line in f.ChangedContents():
[email protected]2fdd1f362013-01-16 03:56:03747 if (inclusion_pattern.search(line) and
[email protected]de4f7d22013-05-23 14:27:46748 not comment_pattern.search(line) and
[email protected]2fdd1f362013-01-16 03:56:03749 not exclusion_pattern.search(line)):
[email protected]55459852011-08-10 15:17:19750 problems.append(
[email protected]2fdd1f362013-01-16 03:56:03751 '%s:%d\n %s' % (local_path, line_number, line.strip()))
[email protected]55459852011-08-10 15:17:19752
753 if problems:
[email protected]f7051d52013-04-02 18:31:42754 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
[email protected]2fdd1f362013-01-16 03:56:03755 else:
756 return []
[email protected]55459852011-08-10 15:17:19757
758
Vaclav Brozek7dbc28c2018-03-27 08:35:23759def _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
760 """This is a simplified version of
761 _CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
762 """
763 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
764 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
765 name_pattern = r'ForTest(s|ing)?'
766 # Describes an occurrence of "ForTest*" inside a // comment.
767 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
768 # Catch calls.
769 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
770 # Ignore definitions. (Comments are ignored separately.)
771 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
772
773 problems = []
774 sources = lambda x: input_api.FilterSourceFile(
775 x,
776 black_list=(('(?i).*test', r'.*\/junit\/')
777 + input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49778 white_list=[r'.*\.java$']
Vaclav Brozek7dbc28c2018-03-27 08:35:23779 )
780 for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
781 local_path = f.LocalPath()
782 is_inside_javadoc = False
783 for line_number, line in f.ChangedContents():
784 if is_inside_javadoc and javadoc_end_re.search(line):
785 is_inside_javadoc = False
786 if not is_inside_javadoc and javadoc_start_re.search(line):
787 is_inside_javadoc = True
788 if is_inside_javadoc:
789 continue
790 if (inclusion_re.search(line) and
791 not comment_re.search(line) and
792 not exclusion_re.search(line)):
793 problems.append(
794 '%s:%d\n %s' % (local_path, line_number, line.strip()))
795
796 if problems:
797 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
798 else:
799 return []
800
801
[email protected]10689ca2011-09-02 02:31:54802def _CheckNoIOStreamInHeaders(input_api, output_api):
803 """Checks to make sure no .h files include <iostream>."""
804 files = []
805 pattern = input_api.re.compile(r'^#include\s*<iostream>',
806 input_api.re.MULTILINE)
807 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
808 if not f.LocalPath().endswith('.h'):
809 continue
810 contents = input_api.ReadFile(f)
811 if pattern.search(contents):
812 files.append(f)
813
814 if len(files):
yolandyandaabc6d2016-04-18 18:29:39815 return [output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06816 'Do not #include <iostream> in header files, since it inserts static '
817 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54818 '#include <ostream>. See http://crbug.com/94794',
819 files) ]
820 return []
821
Danil Chapovalov3518f362018-08-11 16:13:43822def _CheckNoStrCatRedefines(input_api, output_api):
823 """Checks no windows headers with StrCat redefined are included directly."""
824 files = []
825 pattern_deny = input_api.re.compile(
826 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
827 input_api.re.MULTILINE)
828 pattern_allow = input_api.re.compile(
829 r'^#include\s"base/win/windows_defines.inc"',
830 input_api.re.MULTILINE)
831 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
832 contents = input_api.ReadFile(f)
833 if pattern_deny.search(contents) and not pattern_allow.search(contents):
834 files.append(f.LocalPath())
835
836 if len(files):
837 return [output_api.PresubmitError(
838 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
839 'directly since they pollute code with StrCat macro. Instead, '
840 'include matching header from base/win. See http://crbug.com/856536',
841 files) ]
842 return []
843
[email protected]10689ca2011-09-02 02:31:54844
[email protected]72df4e782012-06-21 16:28:18845def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
danakj61c1aa22015-10-26 19:55:52846 """Checks to make sure no source files use UNIT_TEST."""
[email protected]72df4e782012-06-21 16:28:18847 problems = []
848 for f in input_api.AffectedFiles():
849 if (not f.LocalPath().endswith(('.cc', '.mm'))):
850 continue
851
852 for line_num, line in f.ChangedContents():
[email protected]549f86a2013-11-19 13:00:04853 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
[email protected]72df4e782012-06-21 16:28:18854 problems.append(' %s:%d' % (f.LocalPath(), line_num))
855
856 if not problems:
857 return []
858 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
859 '\n'.join(problems))]
860
Dominic Battre033531052018-09-24 15:45:34861def _CheckNoDISABLETypoInTests(input_api, output_api):
862 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
863
864 This test warns if somebody tries to disable a test with the DISABLE_ prefix
865 instead of DISABLED_. To filter false positives, reports are only generated
866 if a corresponding MAYBE_ line exists.
867 """
868 problems = []
869
870 # The following two patterns are looked for in tandem - is a test labeled
871 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
872 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
873 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
874
875 # This is for the case that a test is disabled on all platforms.
876 full_disable_pattern = input_api.re.compile(
877 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
878 input_api.re.MULTILINE)
879
Katie Df13948e2018-09-25 07:33:44880 for f in input_api.AffectedFiles(False):
Dominic Battre033531052018-09-24 15:45:34881 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
882 continue
883
884 # Search for MABYE_, DISABLE_ pairs.
885 disable_lines = {} # Maps of test name to line number.
886 maybe_lines = {}
887 for line_num, line in f.ChangedContents():
888 disable_match = disable_pattern.search(line)
889 if disable_match:
890 disable_lines[disable_match.group(1)] = line_num
891 maybe_match = maybe_pattern.search(line)
892 if maybe_match:
893 maybe_lines[maybe_match.group(1)] = line_num
894
895 # Search for DISABLE_ occurrences within a TEST() macro.
896 disable_tests = set(disable_lines.keys())
897 maybe_tests = set(maybe_lines.keys())
898 for test in disable_tests.intersection(maybe_tests):
899 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
900
901 contents = input_api.ReadFile(f)
902 full_disable_match = full_disable_pattern.search(contents)
903 if full_disable_match:
904 problems.append(' %s' % f.LocalPath())
905
906 if not problems:
907 return []
908 return [
909 output_api.PresubmitPromptWarning(
910 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
911 '\n'.join(problems))
912 ]
913
[email protected]72df4e782012-06-21 16:28:18914
danakj61c1aa22015-10-26 19:55:52915def _CheckDCHECK_IS_ONHasBraces(input_api, output_api):
kjellanderaee306632017-02-22 19:26:57916 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
danakj61c1aa22015-10-26 19:55:52917 errors = []
918 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))',
919 input_api.re.MULTILINE)
920 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
921 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
922 continue
923 for lnum, line in f.ChangedContents():
924 if input_api.re.search(pattern, line):
dchenge07de812016-06-20 19:27:17925 errors.append(output_api.PresubmitError(
926 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' +
kjellanderaee306632017-02-22 19:26:57927 'DCHECK_IS_ON()", not forgetting the parentheses.')
dchenge07de812016-06-20 19:27:17928 % (f.LocalPath(), lnum)))
danakj61c1aa22015-10-26 19:55:52929 return errors
930
931
mcasasb7440c282015-02-04 14:52:19932def _FindHistogramNameInLine(histogram_name, line):
933 """Tries to find a histogram name or prefix in a line."""
934 if not "affected-histogram" in line:
935 return histogram_name in line
936 # A histogram_suffixes tag type has an affected-histogram name as a prefix of
937 # the histogram_name.
938 if not '"' in line:
939 return False
940 histogram_prefix = line.split('\"')[1]
941 return histogram_prefix in histogram_name
942
943
944def _CheckUmaHistogramChanges(input_api, output_api):
945 """Check that UMA histogram names in touched lines can still be found in other
946 lines of the patch or in histograms.xml. Note that this check would not catch
947 the reverse: changes in histograms.xml not matched in the code itself."""
948 touched_histograms = []
949 histograms_xml_modifications = []
Vaclav Brozekbdac817c2018-03-24 06:30:47950 call_pattern_c = r'\bUMA_HISTOGRAM.*\('
951 call_pattern_java = r'\bRecordHistogram\.record[a-zA-Z]+Histogram\('
952 name_pattern = r'"(.*?)"'
953 single_line_c_re = input_api.re.compile(call_pattern_c + name_pattern)
954 single_line_java_re = input_api.re.compile(call_pattern_java + name_pattern)
955 split_line_c_prefix_re = input_api.re.compile(call_pattern_c)
956 split_line_java_prefix_re = input_api.re.compile(call_pattern_java)
957 split_line_suffix_re = input_api.re.compile(r'^\s*' + name_pattern)
Vaclav Brozek0e730cbd2018-03-24 06:18:17958 last_line_matched_prefix = False
mcasasb7440c282015-02-04 14:52:19959 for f in input_api.AffectedFiles():
960 # If histograms.xml itself is modified, keep the modified lines for later.
961 if f.LocalPath().endswith(('histograms.xml')):
962 histograms_xml_modifications = f.ChangedContents()
963 continue
Vaclav Brozekbdac817c2018-03-24 06:30:47964 if f.LocalPath().endswith(('cc', 'mm', 'cpp')):
965 single_line_re = single_line_c_re
966 split_line_prefix_re = split_line_c_prefix_re
967 elif f.LocalPath().endswith(('java')):
968 single_line_re = single_line_java_re
969 split_line_prefix_re = split_line_java_prefix_re
970 else:
mcasasb7440c282015-02-04 14:52:19971 continue
972 for line_num, line in f.ChangedContents():
Vaclav Brozek0e730cbd2018-03-24 06:18:17973 if last_line_matched_prefix:
974 suffix_found = split_line_suffix_re.search(line)
975 if suffix_found :
976 touched_histograms.append([suffix_found.group(1), f, line_num])
977 last_line_matched_prefix = False
978 continue
Vaclav Brozek8a8e2e202018-03-23 22:01:06979 found = single_line_re.search(line)
mcasasb7440c282015-02-04 14:52:19980 if found:
981 touched_histograms.append([found.group(1), f, line_num])
Vaclav Brozek0e730cbd2018-03-24 06:18:17982 continue
983 last_line_matched_prefix = split_line_prefix_re.search(line)
mcasasb7440c282015-02-04 14:52:19984
985 # Search for the touched histogram names in the local modifications to
986 # histograms.xml, and, if not found, on the base histograms.xml file.
987 unmatched_histograms = []
988 for histogram_info in touched_histograms:
989 histogram_name_found = False
990 for line_num, line in histograms_xml_modifications:
991 histogram_name_found = _FindHistogramNameInLine(histogram_info[0], line)
992 if histogram_name_found:
993 break
994 if not histogram_name_found:
995 unmatched_histograms.append(histogram_info)
996
eromanb90c82e7e32015-04-01 15:13:49997 histograms_xml_path = 'tools/metrics/histograms/histograms.xml'
mcasasb7440c282015-02-04 14:52:19998 problems = []
999 if unmatched_histograms:
eromanb90c82e7e32015-04-01 15:13:491000 with open(histograms_xml_path) as histograms_xml:
mcasasb7440c282015-02-04 14:52:191001 for histogram_name, f, line_num in unmatched_histograms:
mcasas39c1b8b2015-02-25 15:33:451002 histograms_xml.seek(0)
mcasasb7440c282015-02-04 14:52:191003 histogram_name_found = False
1004 for line in histograms_xml:
1005 histogram_name_found = _FindHistogramNameInLine(histogram_name, line)
1006 if histogram_name_found:
1007 break
1008 if not histogram_name_found:
1009 problems.append(' [%s:%d] %s' %
1010 (f.LocalPath(), line_num, histogram_name))
1011
1012 if not problems:
1013 return []
1014 return [output_api.PresubmitPromptWarning('Some UMA_HISTOGRAM lines have '
1015 'been modified and the associated histogram name has no match in either '
eromanb90c82e7e32015-04-01 15:13:491016 '%s or the modifications of it:' % (histograms_xml_path), problems)]
mcasasb7440c282015-02-04 14:52:191017
wnwenbdc444e2016-05-25 13:44:151018
yolandyandaabc6d2016-04-18 18:29:391019def _CheckFlakyTestUsage(input_api, output_api):
1020 """Check that FlakyTest annotation is our own instead of the android one"""
1021 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1022 files = []
1023 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1024 if f.LocalPath().endswith('Test.java'):
1025 if pattern.search(input_api.ReadFile(f)):
1026 files.append(f)
1027 if len(files):
1028 return [output_api.PresubmitError(
1029 'Use org.chromium.base.test.util.FlakyTest instead of '
1030 'android.test.FlakyTest',
1031 files)]
1032 return []
mcasasb7440c282015-02-04 14:52:191033
wnwenbdc444e2016-05-25 13:44:151034
[email protected]8ea5d4b2011-09-13 21:49:221035def _CheckNoNewWStrings(input_api, output_api):
1036 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:271037 problems = []
[email protected]8ea5d4b2011-09-13 21:49:221038 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:201039 if (not f.LocalPath().endswith(('.cc', '.h')) or
scottmge6f04402014-11-05 01:59:571040 f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h')) or
pennymac84fd6692016-07-13 22:35:341041 '/win/' in f.LocalPath() or
1042 'chrome_elf' in f.LocalPath() or
1043 'install_static' in f.LocalPath()):
[email protected]b5c24292011-11-28 14:38:201044 continue
[email protected]8ea5d4b2011-09-13 21:49:221045
[email protected]a11dbe9b2012-08-07 01:32:581046 allowWString = False
[email protected]b5c24292011-11-28 14:38:201047 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:581048 if 'presubmit: allow wstring' in line:
1049 allowWString = True
1050 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:271051 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:581052 allowWString = False
1053 else:
1054 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:221055
[email protected]55463aa62011-10-12 00:48:271056 if not problems:
1057 return []
1058 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:581059 ' If you are calling a cross-platform API that accepts a wstring, '
1060 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:271061 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:221062
1063
[email protected]2a8ac9c2011-10-19 17:20:441064def _CheckNoDEPSGIT(input_api, output_api):
1065 """Make sure .DEPS.git is never modified manually."""
1066 if any(f.LocalPath().endswith('.DEPS.git') for f in
1067 input_api.AffectedFiles()):
1068 return [output_api.PresubmitError(
1069 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1070 'automated system based on what\'s in DEPS and your changes will be\n'
1071 'overwritten.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501072 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1073 'get-the-code#Rolling_DEPS\n'
[email protected]2a8ac9c2011-10-19 17:20:441074 'for more information')]
1075 return []
1076
1077
tandriief664692014-09-23 14:51:471078def _CheckValidHostsInDEPS(input_api, output_api):
1079 """Checks that DEPS file deps are from allowed_hosts."""
1080 # Run only if DEPS file has been modified to annoy fewer bystanders.
1081 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1082 return []
1083 # Outsource work to gclient verify
1084 try:
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201085 input_api.subprocess.check_output(['gclient', 'verify'],
1086 stderr=input_api.subprocess.STDOUT)
tandriief664692014-09-23 14:51:471087 return []
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201088 except input_api.subprocess.CalledProcessError as error:
tandriief664692014-09-23 14:51:471089 return [output_api.PresubmitError(
1090 'DEPS file must have only git dependencies.',
1091 long_text=error.output)]
1092
1093
[email protected]127f18ec2012-06-16 05:05:591094def _CheckNoBannedFunctions(input_api, output_api):
1095 """Make sure that banned functions are not used."""
1096 warnings = []
1097 errors = []
1098
wnwenbdc444e2016-05-25 13:44:151099 def IsBlacklisted(affected_file, blacklist):
1100 local_path = affected_file.LocalPath()
1101 for item in blacklist:
1102 if input_api.re.match(item, local_path):
1103 return True
1104 return False
1105
Sylvain Defresnea8b73d252018-02-28 15:45:541106 def IsIosObcjFile(affected_file):
1107 local_path = affected_file.LocalPath()
1108 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m', '.h'):
1109 return False
1110 basename = input_api.os_path.basename(local_path)
1111 if 'ios' in basename.split('_'):
1112 return True
1113 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1114 if sep and 'ios' in local_path.split(sep):
1115 return True
1116 return False
1117
wnwenbdc444e2016-05-25 13:44:151118 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
1119 matched = False
1120 if func_name[0:1] == '/':
1121 regex = func_name[1:]
1122 if input_api.re.search(regex, line):
1123 matched = True
1124 elif func_name in line:
dchenge07de812016-06-20 19:27:171125 matched = True
wnwenbdc444e2016-05-25 13:44:151126 if matched:
dchenge07de812016-06-20 19:27:171127 problems = warnings
wnwenbdc444e2016-05-25 13:44:151128 if error:
dchenge07de812016-06-20 19:27:171129 problems = errors
wnwenbdc444e2016-05-25 13:44:151130 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num))
1131 for message_line in message:
1132 problems.append(' %s' % message_line)
1133
Eric Stevensona9a980972017-09-23 00:04:411134 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1135 for f in input_api.AffectedFiles(file_filter=file_filter):
1136 for line_num, line in f.ChangedContents():
1137 for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
1138 CheckForMatch(f, line_num, line, func_name, message, error)
1139
[email protected]127f18ec2012-06-16 05:05:591140 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1141 for f in input_api.AffectedFiles(file_filter=file_filter):
1142 for line_num, line in f.ChangedContents():
1143 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
wnwenbdc444e2016-05-25 13:44:151144 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591145
Sylvain Defresnea8b73d252018-02-28 15:45:541146 for f in input_api.AffectedFiles(file_filter=IsIosObcjFile):
1147 for line_num, line in f.ChangedContents():
1148 for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
1149 CheckForMatch(f, line_num, line, func_name, message, error)
1150
[email protected]127f18ec2012-06-16 05:05:591151 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1152 for f in input_api.AffectedFiles(file_filter=file_filter):
1153 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:491154 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
[email protected]7345da02012-11-27 14:31:491155 if IsBlacklisted(f, excluded_paths):
1156 continue
wnwenbdc444e2016-05-25 13:44:151157 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591158
1159 result = []
1160 if (warnings):
1161 result.append(output_api.PresubmitPromptWarning(
1162 'Banned functions were used.\n' + '\n'.join(warnings)))
1163 if (errors):
1164 result.append(output_api.PresubmitError(
1165 'Banned functions were used.\n' + '\n'.join(errors)))
1166 return result
1167
1168
[email protected]6c063c62012-07-11 19:11:061169def _CheckNoPragmaOnce(input_api, output_api):
1170 """Make sure that banned functions are not used."""
1171 files = []
1172 pattern = input_api.re.compile(r'^#pragma\s+once',
1173 input_api.re.MULTILINE)
1174 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1175 if not f.LocalPath().endswith('.h'):
1176 continue
1177 contents = input_api.ReadFile(f)
1178 if pattern.search(contents):
1179 files.append(f)
1180
1181 if files:
1182 return [output_api.PresubmitError(
1183 'Do not use #pragma once in header files.\n'
1184 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1185 files)]
1186 return []
1187
[email protected]127f18ec2012-06-16 05:05:591188
[email protected]e7479052012-09-19 00:26:121189def _CheckNoTrinaryTrueFalse(input_api, output_api):
1190 """Checks to make sure we don't introduce use of foo ? true : false."""
1191 problems = []
1192 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1193 for f in input_api.AffectedFiles():
1194 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1195 continue
1196
1197 for line_num, line in f.ChangedContents():
1198 if pattern.match(line):
1199 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1200
1201 if not problems:
1202 return []
1203 return [output_api.PresubmitPromptWarning(
1204 'Please consider avoiding the "? true : false" pattern if possible.\n' +
1205 '\n'.join(problems))]
1206
1207
[email protected]55f9f382012-07-31 11:02:181208def _CheckUnwantedDependencies(input_api, output_api):
rhalavati08acd232017-04-03 07:23:281209 """Runs checkdeps on #include and import statements added in this
[email protected]55f9f382012-07-31 11:02:181210 change. Breaking - rules is an error, breaking ! rules is a
1211 warning.
1212 """
mohan.reddyf21db962014-10-16 12:26:471213 import sys
[email protected]55f9f382012-07-31 11:02:181214 # We need to wait until we have an input_api object and use this
1215 # roundabout construct to import checkdeps because this file is
1216 # eval-ed and thus doesn't have __file__.
1217 original_sys_path = sys.path
1218 try:
1219 sys.path = sys.path + [input_api.os_path.join(
[email protected]5298cc982014-05-29 20:53:471220 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
[email protected]55f9f382012-07-31 11:02:181221 import checkdeps
1222 from cpp_checker import CppChecker
Jinsuk Kim5a092672017-10-24 22:42:241223 from java_checker import JavaChecker
rhalavati08acd232017-04-03 07:23:281224 from proto_checker import ProtoChecker
[email protected]55f9f382012-07-31 11:02:181225 from rules import Rule
1226 finally:
1227 # Restore sys.path to what it was before.
1228 sys.path = original_sys_path
1229
1230 added_includes = []
rhalavati08acd232017-04-03 07:23:281231 added_imports = []
Jinsuk Kim5a092672017-10-24 22:42:241232 added_java_imports = []
[email protected]55f9f382012-07-31 11:02:181233 for f in input_api.AffectedFiles():
rhalavati08acd232017-04-03 07:23:281234 if CppChecker.IsCppFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501235 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081236 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
rhalavati08acd232017-04-03 07:23:281237 elif ProtoChecker.IsProtoFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501238 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081239 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:241240 elif JavaChecker.IsJavaFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501241 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081242 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
[email protected]55f9f382012-07-31 11:02:181243
[email protected]26385172013-05-09 23:11:351244 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181245
1246 error_descriptions = []
1247 warning_descriptions = []
rhalavati08acd232017-04-03 07:23:281248 error_subjects = set()
1249 warning_subjects = set()
[email protected]55f9f382012-07-31 11:02:181250 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1251 added_includes):
Andrew Grieve085f29f2017-11-02 09:14:081252 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181253 description_with_path = '%s\n %s' % (path, rule_description)
1254 if rule_type == Rule.DISALLOW:
1255 error_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281256 error_subjects.add("#includes")
[email protected]55f9f382012-07-31 11:02:181257 else:
1258 warning_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281259 warning_subjects.add("#includes")
1260
1261 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1262 added_imports):
Andrew Grieve085f29f2017-11-02 09:14:081263 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
rhalavati08acd232017-04-03 07:23:281264 description_with_path = '%s\n %s' % (path, rule_description)
1265 if rule_type == Rule.DISALLOW:
1266 error_descriptions.append(description_with_path)
1267 error_subjects.add("imports")
1268 else:
1269 warning_descriptions.append(description_with_path)
1270 warning_subjects.add("imports")
[email protected]55f9f382012-07-31 11:02:181271
Jinsuk Kim5a092672017-10-24 22:42:241272 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
Shenghua Zhangbfaa38b82017-11-16 21:58:021273 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
Andrew Grieve085f29f2017-11-02 09:14:081274 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
Jinsuk Kim5a092672017-10-24 22:42:241275 description_with_path = '%s\n %s' % (path, rule_description)
1276 if rule_type == Rule.DISALLOW:
1277 error_descriptions.append(description_with_path)
1278 error_subjects.add("imports")
1279 else:
1280 warning_descriptions.append(description_with_path)
1281 warning_subjects.add("imports")
1282
[email protected]55f9f382012-07-31 11:02:181283 results = []
1284 if error_descriptions:
1285 results.append(output_api.PresubmitError(
rhalavati08acd232017-04-03 07:23:281286 'You added one or more %s that violate checkdeps rules.'
1287 % " and ".join(error_subjects),
[email protected]55f9f382012-07-31 11:02:181288 error_descriptions))
1289 if warning_descriptions:
[email protected]f7051d52013-04-02 18:31:421290 results.append(output_api.PresubmitPromptOrNotify(
rhalavati08acd232017-04-03 07:23:281291 'You added one or more %s of files that are temporarily\n'
[email protected]55f9f382012-07-31 11:02:181292 'allowed but being removed. Can you avoid introducing the\n'
rhalavati08acd232017-04-03 07:23:281293 '%s? See relevant DEPS file(s) for details and contacts.' %
1294 (" and ".join(warning_subjects), "/".join(warning_subjects)),
[email protected]55f9f382012-07-31 11:02:181295 warning_descriptions))
1296 return results
1297
1298
[email protected]fbcafe5a2012-08-08 15:31:221299def _CheckFilePermissions(input_api, output_api):
1300 """Check that all files have their permissions properly set."""
[email protected]791507202014-02-03 23:19:151301 if input_api.platform == 'win32':
1302 return []
raphael.kubo.da.costac1d13e60b2016-04-01 11:49:291303 checkperms_tool = input_api.os_path.join(
1304 input_api.PresubmitLocalPath(),
1305 'tools', 'checkperms', 'checkperms.py')
1306 args = [input_api.python_executable, checkperms_tool,
mohan.reddyf21db962014-10-16 12:26:471307 '--root', input_api.change.RepositoryRoot()]
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391308 with input_api.CreateTemporaryFile() as file_list:
1309 for f in input_api.AffectedFiles():
1310 # checkperms.py file/directory arguments must be relative to the
1311 # repository.
1312 file_list.write(f.LocalPath() + '\n')
1313 file_list.close()
1314 args += ['--file-list', file_list.name]
1315 try:
1316 input_api.subprocess.check_output(args)
1317 return []
1318 except input_api.subprocess.CalledProcessError as error:
1319 return [output_api.PresubmitError(
1320 'checkperms.py failed:',
1321 long_text=error.output)]
[email protected]fbcafe5a2012-08-08 15:31:221322
1323
robertocn832f5992017-01-04 19:01:301324def _CheckTeamTags(input_api, output_api):
1325 """Checks that OWNERS files have consistent TEAM and COMPONENT tags."""
1326 checkteamtags_tool = input_api.os_path.join(
1327 input_api.PresubmitLocalPath(),
1328 'tools', 'checkteamtags', 'checkteamtags.py')
1329 args = [input_api.python_executable, checkteamtags_tool,
1330 '--root', input_api.change.RepositoryRoot()]
robertocn5eb82312017-01-09 20:27:221331 files = [f.LocalPath() for f in input_api.AffectedFiles(include_deletes=False)
robertocn832f5992017-01-04 19:01:301332 if input_api.os_path.basename(f.AbsoluteLocalPath()).upper() ==
1333 'OWNERS']
1334 try:
1335 if files:
1336 input_api.subprocess.check_output(args + files)
1337 return []
1338 except input_api.subprocess.CalledProcessError as error:
1339 return [output_api.PresubmitError(
1340 'checkteamtags.py failed:',
1341 long_text=error.output)]
1342
1343
[email protected]c8278b32012-10-30 20:35:491344def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
1345 """Makes sure we don't include ui/aura/window_property.h
1346 in header files.
1347 """
1348 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1349 errors = []
1350 for f in input_api.AffectedFiles():
1351 if not f.LocalPath().endswith('.h'):
1352 continue
1353 for line_num, line in f.ChangedContents():
1354 if pattern.match(line):
1355 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1356
1357 results = []
1358 if errors:
1359 results.append(output_api.PresubmitError(
1360 'Header files should not include ui/aura/window_property.h', errors))
1361 return results
1362
1363
[email protected]70ca77752012-11-20 03:45:031364def _CheckForVersionControlConflictsInFile(input_api, f):
1365 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1366 errors = []
1367 for line_num, line in f.ChangedContents():
dbeam95c35a2f2015-06-02 01:40:231368 if f.LocalPath().endswith('.md'):
1369 # First-level headers in markdown look a lot like version control
1370 # conflict markers. http://daringfireball.net/projects/markdown/basics
1371 continue
[email protected]70ca77752012-11-20 03:45:031372 if pattern.match(line):
1373 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1374 return errors
1375
1376
1377def _CheckForVersionControlConflicts(input_api, output_api):
1378 """Usually this is not intentional and will cause a compile failure."""
1379 errors = []
1380 for f in input_api.AffectedFiles():
1381 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
1382
1383 results = []
1384 if errors:
1385 results.append(output_api.PresubmitError(
1386 'Version control conflict markers found, please resolve.', errors))
1387 return results
1388
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201389
estadee17314a02017-01-12 16:22:161390def _CheckGoogleSupportAnswerUrl(input_api, output_api):
1391 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1392 errors = []
1393 for f in input_api.AffectedFiles():
1394 for line_num, line in f.ChangedContents():
1395 if pattern.search(line):
1396 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1397
1398 results = []
1399 if errors:
1400 results.append(output_api.PresubmitPromptWarning(
Vaclav Brozekd5de76a2018-03-17 07:57:501401 'Found Google support URL addressed by answer number. Please replace '
1402 'with a p= identifier instead. See crbug.com/679462\n', errors))
estadee17314a02017-01-12 16:22:161403 return results
1404
[email protected]70ca77752012-11-20 03:45:031405
[email protected]06e6d0ff2012-12-11 01:36:441406def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
1407 def FilterFile(affected_file):
1408 """Filter function for use with input_api.AffectedSourceFiles,
1409 below. This filters out everything except non-test files from
1410 top-level directories that generally speaking should not hard-code
1411 service URLs (e.g. src/android_webview/, src/content/ and others).
1412 """
1413 return input_api.FilterSourceFile(
1414 affected_file,
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491415 white_list=[r'^(android_webview|base|content|net)[\\\/].*'],
[email protected]06e6d0ff2012-12-11 01:36:441416 black_list=(_EXCLUDED_PATHS +
1417 _TEST_CODE_EXCLUDED_PATHS +
1418 input_api.DEFAULT_BLACK_LIST))
1419
reillyi38965732015-11-16 18:27:331420 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
1421 '\.(com|net)[^"]*"')
[email protected]de4f7d22013-05-23 14:27:461422 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
1423 pattern = input_api.re.compile(base_pattern)
[email protected]06e6d0ff2012-12-11 01:36:441424 problems = [] # items are (filename, line_number, line)
1425 for f in input_api.AffectedSourceFiles(FilterFile):
1426 for line_num, line in f.ChangedContents():
[email protected]de4f7d22013-05-23 14:27:461427 if not comment_pattern.search(line) and pattern.search(line):
[email protected]06e6d0ff2012-12-11 01:36:441428 problems.append((f.LocalPath(), line_num, line))
1429
1430 if problems:
[email protected]f7051d52013-04-02 18:31:421431 return [output_api.PresubmitPromptOrNotify(
[email protected]06e6d0ff2012-12-11 01:36:441432 'Most layers below src/chrome/ should not hardcode service URLs.\n'
[email protected]b0149772014-03-27 16:47:581433 'Are you sure this is correct?',
[email protected]06e6d0ff2012-12-11 01:36:441434 [' %s:%d: %s' % (
1435 problem[0], problem[1], problem[2]) for problem in problems])]
[email protected]2fdd1f362013-01-16 03:56:031436 else:
1437 return []
[email protected]06e6d0ff2012-12-11 01:36:441438
1439
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491440# TODO: add unit tests.
[email protected]d2530012013-01-25 16:39:271441def _CheckNoAbbreviationInPngFileName(input_api, output_api):
1442 """Makes sure there are no abbreviations in the name of PNG files.
binji0dcdf342014-12-12 18:32:311443 The native_client_sdk directory is excluded because it has auto-generated PNG
1444 files for documentation.
[email protected]d2530012013-01-25 16:39:271445 """
[email protected]d2530012013-01-25 16:39:271446 errors = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491447 white_list = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
1448 black_list = [r'^native_client_sdk[\\\/]']
binji0dcdf342014-12-12 18:32:311449 file_filter = lambda f: input_api.FilterSourceFile(
1450 f, white_list=white_list, black_list=black_list)
1451 for f in input_api.AffectedFiles(include_deletes=False,
1452 file_filter=file_filter):
1453 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:271454
1455 results = []
1456 if errors:
1457 results.append(output_api.PresubmitError(
1458 'The name of PNG files should not have abbreviations. \n'
1459 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
1460 'Contact [email protected] if you have questions.', errors))
1461 return results
1462
1463
Daniel Cheng4dcdb6b2017-04-13 08:30:171464def _ExtractAddRulesFromParsedDeps(parsed_deps):
1465 """Extract the rules that add dependencies from a parsed DEPS file.
1466
1467 Args:
1468 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1469 add_rules = set()
1470 add_rules.update([
1471 rule[1:] for rule in parsed_deps.get('include_rules', [])
1472 if rule.startswith('+') or rule.startswith('!')
1473 ])
Vaclav Brozekd5de76a2018-03-17 07:57:501474 for _, rules in parsed_deps.get('specific_include_rules',
Daniel Cheng4dcdb6b2017-04-13 08:30:171475 {}).iteritems():
1476 add_rules.update([
1477 rule[1:] for rule in rules
1478 if rule.startswith('+') or rule.startswith('!')
1479 ])
1480 return add_rules
1481
1482
1483def _ParseDeps(contents):
1484 """Simple helper for parsing DEPS files."""
1485 # Stubs for handling special syntax in the root DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:171486 class _VarImpl:
1487
1488 def __init__(self, local_scope):
1489 self._local_scope = local_scope
1490
1491 def Lookup(self, var_name):
1492 """Implements the Var syntax."""
1493 try:
1494 return self._local_scope['vars'][var_name]
1495 except KeyError:
1496 raise Exception('Var is not defined: %s' % var_name)
1497
1498 local_scope = {}
1499 global_scope = {
Daniel Cheng4dcdb6b2017-04-13 08:30:171500 'Var': _VarImpl(local_scope).Lookup,
1501 }
1502 exec contents in global_scope, local_scope
1503 return local_scope
1504
1505
1506def _CalculateAddedDeps(os_path, old_contents, new_contents):
[email protected]f32e2d1e2013-07-26 21:39:081507 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
[email protected]14a6131c2014-01-08 01:15:411508 a set of DEPS entries that we should look up.
1509
1510 For a directory (rather than a specific filename) we fake a path to
1511 a specific filename by adding /DEPS. This is chosen as a file that
1512 will seldom or never be subject to per-file include_rules.
1513 """
[email protected]2b438d62013-11-14 17:54:141514 # We ignore deps entries on auto-generated directories.
1515 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:081516
Daniel Cheng4dcdb6b2017-04-13 08:30:171517 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1518 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1519
1520 added_deps = new_deps.difference(old_deps)
1521
[email protected]2b438d62013-11-14 17:54:141522 results = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:171523 for added_dep in added_deps:
1524 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
1525 continue
1526 # Assume that a rule that ends in .h is a rule for a specific file.
1527 if added_dep.endswith('.h'):
1528 results.add(added_dep)
1529 else:
1530 results.add(os_path.join(added_dep, 'DEPS'))
[email protected]f32e2d1e2013-07-26 21:39:081531 return results
1532
1533
[email protected]e871964c2013-05-13 14:14:551534def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1535 """When a dependency prefixed with + is added to a DEPS file, we
1536 want to make sure that the change is reviewed by an OWNER of the
1537 target file or directory, to avoid layering violations from being
1538 introduced. This check verifies that this happens.
1539 """
Daniel Cheng4dcdb6b2017-04-13 08:30:171540 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:241541
1542 file_filter = lambda f: not input_api.re.match(
Kent Tamurae9b3a9ec2017-08-31 02:20:191543 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
jochen53efcdd2016-01-29 05:09:241544 for f in input_api.AffectedFiles(include_deletes=False,
1545 file_filter=file_filter):
[email protected]e871964c2013-05-13 14:14:551546 filename = input_api.os_path.basename(f.LocalPath())
1547 if filename == 'DEPS':
Daniel Cheng4dcdb6b2017-04-13 08:30:171548 virtual_depended_on_files.update(_CalculateAddedDeps(
1549 input_api.os_path,
1550 '\n'.join(f.OldContents()),
1551 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:551552
[email protected]e871964c2013-05-13 14:14:551553 if not virtual_depended_on_files:
1554 return []
1555
1556 if input_api.is_committing:
1557 if input_api.tbr:
1558 return [output_api.PresubmitNotifyResult(
1559 '--tbr was specified, skipping OWNERS check for DEPS additions')]
Paweł Hajdan, Jrbe6739ea2016-04-28 15:07:271560 if input_api.dry_run:
1561 return [output_api.PresubmitNotifyResult(
1562 'This is a dry run, skipping OWNERS check for DEPS additions')]
[email protected]e871964c2013-05-13 14:14:551563 if not input_api.change.issue:
1564 return [output_api.PresubmitError(
1565 "DEPS approval by OWNERS check failed: this change has "
Aaron Gable65a99d92017-10-09 19:17:401566 "no change number, so we can't check it for approvals.")]
[email protected]e871964c2013-05-13 14:14:551567 output = output_api.PresubmitError
1568 else:
1569 output = output_api.PresubmitNotifyResult
1570
1571 owners_db = input_api.owners_db
tandriied3b7e12016-05-12 14:38:501572 owner_email, reviewers = (
1573 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1574 input_api,
1575 owners_db.email_regexp,
1576 approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:551577
1578 owner_email = owner_email or input_api.change.author_email
1579
[email protected]de4f7d22013-05-23 14:27:461580 reviewers_plus_owner = set(reviewers)
[email protected]e71c6082013-05-22 02:28:511581 if owner_email:
[email protected]de4f7d22013-05-23 14:27:461582 reviewers_plus_owner.add(owner_email)
[email protected]e871964c2013-05-13 14:14:551583 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1584 reviewers_plus_owner)
[email protected]14a6131c2014-01-08 01:15:411585
1586 # We strip the /DEPS part that was added by
1587 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1588 # directory.
1589 def StripDeps(path):
1590 start_deps = path.rfind('/DEPS')
1591 if start_deps != -1:
1592 return path[:start_deps]
1593 else:
1594 return path
1595 unapproved_dependencies = ["'+%s'," % StripDeps(path)
[email protected]e871964c2013-05-13 14:14:551596 for path in missing_files]
1597
1598 if unapproved_dependencies:
1599 output_list = [
Paweł Hajdan, Jrec17f882016-07-04 14:16:151600 output('You need LGTM from owners of depends-on paths in DEPS that were '
1601 'modified in this CL:\n %s' %
1602 '\n '.join(sorted(unapproved_dependencies)))]
1603 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1604 output_list.append(output(
1605 'Suggested missing target path OWNERS:\n %s' %
1606 '\n '.join(suggested_owners or [])))
[email protected]e871964c2013-05-13 14:14:551607 return output_list
1608
1609 return []
1610
1611
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491612# TODO: add unit tests.
[email protected]85218562013-11-22 07:41:401613def _CheckSpamLogging(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491614 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]85218562013-11-22 07:41:401615 black_list = (_EXCLUDED_PATHS +
1616 _TEST_CODE_EXCLUDED_PATHS +
1617 input_api.DEFAULT_BLACK_LIST +
[email protected]6f742dd02013-11-26 23:19:501618 (r"^base[\\\/]logging\.h$",
[email protected]80f360a2014-01-23 01:36:191619 r"^base[\\\/]logging\.cc$",
[email protected]8dc338c2013-12-09 16:28:481620 r"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
[email protected]6e268db2013-12-04 01:41:461621 r"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
[email protected]4de75262013-12-18 23:16:121622 r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
1623 r"startup_browser_creator\.cc$",
[email protected]fe0e6e12013-12-04 05:52:581624 r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
Olivier Liec4400b22018-07-31 19:50:441625 r"^chrome[\\\/]chrome_cleaner[\\\/].*",
[email protected]8cf6f842014-08-08 21:33:161626 r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
[email protected]f5b9a3f342014-08-08 22:06:031627 r"diagnostics_writer\.cc$",
[email protected]9f13b602014-08-07 02:59:151628 r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
1629 r"^chromecast[\\\/]",
1630 r"^cloud_print[\\\/]",
manzagop85e629e2017-05-09 22:11:481631 r"^components[\\\/]browser_watcher[\\\/]"
1632 r"dump_stability_report_main_win.cc$",
jochen34415e52015-07-10 08:34:311633 r"^components[\\\/]html_viewer[\\\/]"
1634 r"web_test_delegate_impl\.cc$",
Samuel Huang577ef6c2018-03-13 18:19:341635 r"^components[\\\/]zucchini[\\\/].*",
peter80739bb2015-10-20 11:17:461636 # TODO(peter): Remove this exception. https://crbug.com/534537
1637 r"^content[\\\/]browser[\\\/]notifications[\\\/]"
1638 r"notification_event_dispatcher_impl\.cc$",
[email protected]9056e732014-01-08 06:25:251639 r"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
1640 r"gl_helper_benchmark\.cc$",
altimin979ea2e12016-05-18 16:16:241641 r"^courgette[\\\/]courgette_minimal_tool\.cc$",
thestigc9e38a22014-09-13 01:02:111642 r"^courgette[\\\/]courgette_tool\.cc$",
[email protected]9f13b602014-08-07 02:59:151643 r"^extensions[\\\/]renderer[\\\/]logging_native_handler\.cc$",
prashant.nb0252f62014-11-08 05:02:111644 r"^ipc[\\\/]ipc_logging\.cc$",
[email protected]9c36d922014-03-24 16:47:521645 r"^native_client_sdk[\\\/]",
[email protected]cdbdced2013-11-27 21:35:501646 r"^remoting[\\\/]base[\\\/]logging\.h$",
[email protected]67c96ab2013-12-17 02:05:361647 r"^remoting[\\\/]host[\\\/].*",
[email protected]8232f8fd2013-12-14 00:52:311648 r"^sandbox[\\\/]linux[\\\/].*",
[email protected]0b7a21e2014-02-11 18:38:131649 r"^tools[\\\/]",
asvitkine8a40fe5f02017-02-18 15:35:001650 r"^ui[\\\/]base[\\\/]resource[\\\/]data_pack.cc$",
thestig22dfc4012014-09-05 08:29:441651 r"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",
halliwellf7fc61c62016-01-28 17:18:451652 r"^ui[\\\/]ozone[\\\/]platform[\\\/]cast[\\\/]",
vchigrin14251492015-01-12 08:09:021653 r"^storage[\\\/]browser[\\\/]fileapi[\\\/]" +
skyostil87681be82016-12-19 12:46:351654 r"dump_file_system.cc$",
1655 r"^headless[\\\/]app[\\\/]headless_shell\.cc$"))
[email protected]85218562013-11-22 07:41:401656 source_file_filter = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491657 x, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]85218562013-11-22 07:41:401658
thomasanderson625d3932017-03-29 07:16:581659 log_info = set([])
1660 printf = set([])
[email protected]85218562013-11-22 07:41:401661
1662 for f in input_api.AffectedSourceFiles(source_file_filter):
thomasanderson625d3932017-03-29 07:16:581663 for _, line in f.ChangedContents():
1664 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
1665 log_info.add(f.LocalPath())
1666 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
1667 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:371668
thomasanderson625d3932017-03-29 07:16:581669 if input_api.re.search(r"\bprintf\(", line):
1670 printf.add(f.LocalPath())
1671 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
1672 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:401673
1674 if log_info:
1675 return [output_api.PresubmitError(
1676 'These files spam the console log with LOG(INFO):',
1677 items=log_info)]
1678 if printf:
1679 return [output_api.PresubmitError(
1680 'These files spam the console log with printf/fprintf:',
1681 items=printf)]
1682 return []
1683
1684
[email protected]49aa76a2013-12-04 06:59:161685def _CheckForAnonymousVariables(input_api, output_api):
1686 """These types are all expected to hold locks while in scope and
1687 so should never be anonymous (which causes them to be immediately
1688 destroyed)."""
1689 they_who_must_be_named = [
1690 'base::AutoLock',
1691 'base::AutoReset',
1692 'base::AutoUnlock',
1693 'SkAutoAlphaRestore',
1694 'SkAutoBitmapShaderInstall',
1695 'SkAutoBlitterChoose',
1696 'SkAutoBounderCommit',
1697 'SkAutoCallProc',
1698 'SkAutoCanvasRestore',
1699 'SkAutoCommentBlock',
1700 'SkAutoDescriptor',
1701 'SkAutoDisableDirectionCheck',
1702 'SkAutoDisableOvalCheck',
1703 'SkAutoFree',
1704 'SkAutoGlyphCache',
1705 'SkAutoHDC',
1706 'SkAutoLockColors',
1707 'SkAutoLockPixels',
1708 'SkAutoMalloc',
1709 'SkAutoMaskFreeImage',
1710 'SkAutoMutexAcquire',
1711 'SkAutoPathBoundsUpdate',
1712 'SkAutoPDFRelease',
1713 'SkAutoRasterClipValidate',
1714 'SkAutoRef',
1715 'SkAutoTime',
1716 'SkAutoTrace',
1717 'SkAutoUnref',
1718 ]
1719 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1720 # bad: base::AutoLock(lock.get());
1721 # not bad: base::AutoLock lock(lock.get());
1722 bad_pattern = input_api.re.compile(anonymous)
1723 # good: new base::AutoLock(lock.get())
1724 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1725 errors = []
1726
1727 for f in input_api.AffectedFiles():
1728 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1729 continue
1730 for linenum, line in f.ChangedContents():
1731 if bad_pattern.search(line) and not good_pattern.search(line):
1732 errors.append('%s:%d' % (f.LocalPath(), linenum))
1733
1734 if errors:
1735 return [output_api.PresubmitError(
1736 'These lines create anonymous variables that need to be named:',
1737 items=errors)]
1738 return []
1739
1740
Peter Kasting4844e46e2018-02-23 07:27:101741def _CheckUniquePtr(input_api, output_api):
Vaclav Brozekb7fadb692018-08-30 06:39:531742 # Returns whether |template_str| is of the form <T, U...> for some types T
1743 # and U. Assumes that |template_str| is already in the form <...>.
1744 def HasMoreThanOneArg(template_str):
1745 # Level of <...> nesting.
1746 nesting = 0
1747 for c in template_str:
1748 if c == '<':
1749 nesting += 1
1750 elif c == '>':
1751 nesting -= 1
1752 elif c == ',' and nesting == 1:
1753 return True
1754 return False
1755
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491756 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
Peter Kasting4844e46e2018-02-23 07:27:101757 sources = lambda affected_file: input_api.FilterSourceFile(
1758 affected_file,
1759 black_list=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1760 input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491761 white_list=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:551762
1763 # Pattern to capture a single "<...>" block of template arguments. It can
1764 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
1765 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
1766 # latter would likely require counting that < and > match, which is not
1767 # expressible in regular languages. Should the need arise, one can introduce
1768 # limited counting (matching up to a total number of nesting depth), which
1769 # should cover all practical cases for already a low nesting limit.
1770 template_arg_pattern = (
1771 r'<[^>]*' # Opening block of <.
1772 r'>([^<]*>)?') # Closing block of >.
1773 # Prefix expressing that whatever follows is not already inside a <...>
1774 # block.
1775 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
Peter Kasting4844e46e2018-02-23 07:27:101776 null_construct_pattern = input_api.re.compile(
Vaclav Brozeka54c528b2018-04-06 19:23:551777 not_inside_template_arg_pattern
1778 + r'\bstd::unique_ptr'
1779 + template_arg_pattern
1780 + r'\(\)')
1781
1782 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
1783 template_arg_no_array_pattern = (
1784 r'<[^>]*[^]]' # Opening block of <.
1785 r'>([^(<]*[^]]>)?') # Closing block of >.
1786 # Prefix saying that what follows is the start of an expression.
1787 start_of_expr_pattern = r'(=|\breturn|^)\s*'
1788 # Suffix saying that what follows are call parentheses with a non-empty list
1789 # of arguments.
1790 nonempty_arg_list_pattern = r'\(([^)]|$)'
Vaclav Brozekb7fadb692018-08-30 06:39:531791 # Put the template argument into a capture group for deeper examination later.
Vaclav Brozeka54c528b2018-04-06 19:23:551792 return_construct_pattern = input_api.re.compile(
1793 start_of_expr_pattern
1794 + r'std::unique_ptr'
Vaclav Brozekb7fadb692018-08-30 06:39:531795 + '(?P<template_arg>'
Vaclav Brozeka54c528b2018-04-06 19:23:551796 + template_arg_no_array_pattern
Vaclav Brozekb7fadb692018-08-30 06:39:531797 + ')'
Vaclav Brozeka54c528b2018-04-06 19:23:551798 + nonempty_arg_list_pattern)
1799
Vaclav Brozek851d9602018-04-04 16:13:051800 problems_constructor = []
1801 problems_nullptr = []
Peter Kasting4844e46e2018-02-23 07:27:101802 for f in input_api.AffectedSourceFiles(sources):
1803 for line_number, line in f.ChangedContents():
1804 # Disallow:
1805 # return std::unique_ptr<T>(foo);
1806 # bar = std::unique_ptr<T>(foo);
1807 # But allow:
1808 # return std::unique_ptr<T[]>(foo);
1809 # bar = std::unique_ptr<T[]>(foo);
Vaclav Brozekb7fadb692018-08-30 06:39:531810 # And also allow cases when the second template argument is present. Those
1811 # cases cannot be handled by std::make_unique:
1812 # return std::unique_ptr<T, U>(foo);
1813 # bar = std::unique_ptr<T, U>(foo);
Vaclav Brozek851d9602018-04-04 16:13:051814 local_path = f.LocalPath()
Vaclav Brozekb7fadb692018-08-30 06:39:531815 return_construct_result = return_construct_pattern.search(line)
1816 if return_construct_result and not HasMoreThanOneArg(
1817 return_construct_result.group('template_arg')):
Vaclav Brozek851d9602018-04-04 16:13:051818 problems_constructor.append(
1819 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Peter Kasting4844e46e2018-02-23 07:27:101820 # Disallow:
1821 # std::unique_ptr<T>()
1822 if null_construct_pattern.search(line):
Vaclav Brozek851d9602018-04-04 16:13:051823 problems_nullptr.append(
1824 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1825
1826 errors = []
Vaclav Brozekc2fecf42018-04-06 16:40:161827 if problems_nullptr:
Vaclav Brozek851d9602018-04-04 16:13:051828 errors.append(output_api.PresubmitError(
1829 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161830 problems_nullptr))
1831 if problems_constructor:
Vaclav Brozek851d9602018-04-04 16:13:051832 errors.append(output_api.PresubmitError(
1833 'The following files use explicit std::unique_ptr constructor.'
1834 'Use std::make_unique<T>() instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161835 problems_constructor))
Peter Kasting4844e46e2018-02-23 07:27:101836 return errors
1837
1838
[email protected]999261d2014-03-03 20:08:081839def _CheckUserActionUpdate(input_api, output_api):
1840 """Checks if any new user action has been added."""
[email protected]2f92dec2014-03-07 19:21:521841 if any('actions.xml' == input_api.os_path.basename(f) for f in
[email protected]999261d2014-03-03 20:08:081842 input_api.LocalPaths()):
[email protected]2f92dec2014-03-07 19:21:521843 # If actions.xml is already included in the changelist, the PRESUBMIT
1844 # for actions.xml will do a more complete presubmit check.
[email protected]999261d2014-03-03 20:08:081845 return []
1846
[email protected]999261d2014-03-03 20:08:081847 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1848 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
[email protected]2f92dec2014-03-07 19:21:521849 current_actions = None
[email protected]999261d2014-03-03 20:08:081850 for f in input_api.AffectedFiles(file_filter=file_filter):
1851 for line_num, line in f.ChangedContents():
1852 match = input_api.re.search(action_re, line)
1853 if match:
[email protected]2f92dec2014-03-07 19:21:521854 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1855 # loaded only once.
1856 if not current_actions:
1857 with open('tools/metrics/actions/actions.xml') as actions_f:
1858 current_actions = actions_f.read()
1859 # Search for the matched user action name in |current_actions|.
[email protected]999261d2014-03-03 20:08:081860 for action_name in match.groups():
[email protected]2f92dec2014-03-07 19:21:521861 action = 'name="{0}"'.format(action_name)
1862 if action not in current_actions:
[email protected]999261d2014-03-03 20:08:081863 return [output_api.PresubmitPromptWarning(
1864 'File %s line %d: %s is missing in '
[email protected]2f92dec2014-03-07 19:21:521865 'tools/metrics/actions/actions.xml. Please run '
1866 'tools/metrics/actions/extract_actions.py to update.'
[email protected]999261d2014-03-03 20:08:081867 % (f.LocalPath(), line_num, action_name))]
1868 return []
1869
1870
Daniel Cheng13ca61a882017-08-25 15:11:251871def _ImportJSONCommentEater(input_api):
1872 import sys
1873 sys.path = sys.path + [input_api.os_path.join(
1874 input_api.PresubmitLocalPath(),
1875 'tools', 'json_comment_eater')]
1876 import json_comment_eater
1877 return json_comment_eater
1878
1879
[email protected]99171a92014-06-03 08:44:471880def _GetJSONParseError(input_api, filename, eat_comments=True):
1881 try:
1882 contents = input_api.ReadFile(filename)
1883 if eat_comments:
Daniel Cheng13ca61a882017-08-25 15:11:251884 json_comment_eater = _ImportJSONCommentEater(input_api)
plundblad1f5a4509f2015-07-23 11:31:131885 contents = json_comment_eater.Nom(contents)
[email protected]99171a92014-06-03 08:44:471886
1887 input_api.json.loads(contents)
1888 except ValueError as e:
1889 return e
1890 return None
1891
1892
1893def _GetIDLParseError(input_api, filename):
1894 try:
1895 contents = input_api.ReadFile(filename)
1896 idl_schema = input_api.os_path.join(
1897 input_api.PresubmitLocalPath(),
1898 'tools', 'json_schema_compiler', 'idl_schema.py')
1899 process = input_api.subprocess.Popen(
1900 [input_api.python_executable, idl_schema],
1901 stdin=input_api.subprocess.PIPE,
1902 stdout=input_api.subprocess.PIPE,
1903 stderr=input_api.subprocess.PIPE,
1904 universal_newlines=True)
1905 (_, error) = process.communicate(input=contents)
1906 return error or None
1907 except ValueError as e:
1908 return e
1909
1910
1911def _CheckParseErrors(input_api, output_api):
1912 """Check that IDL and JSON files do not contain syntax errors."""
1913 actions = {
1914 '.idl': _GetIDLParseError,
1915 '.json': _GetJSONParseError,
1916 }
[email protected]99171a92014-06-03 08:44:471917 # Most JSON files are preprocessed and support comments, but these do not.
1918 json_no_comments_patterns = [
joaodasilva718f87672014-08-30 09:25:491919 r'^testing[\\\/]',
[email protected]99171a92014-06-03 08:44:471920 ]
1921 # Only run IDL checker on files in these directories.
1922 idl_included_patterns = [
joaodasilva718f87672014-08-30 09:25:491923 r'^chrome[\\\/]common[\\\/]extensions[\\\/]api[\\\/]',
1924 r'^extensions[\\\/]common[\\\/]api[\\\/]',
[email protected]99171a92014-06-03 08:44:471925 ]
1926
1927 def get_action(affected_file):
1928 filename = affected_file.LocalPath()
1929 return actions.get(input_api.os_path.splitext(filename)[1])
1930
[email protected]99171a92014-06-03 08:44:471931 def FilterFile(affected_file):
1932 action = get_action(affected_file)
1933 if not action:
1934 return False
1935 path = affected_file.LocalPath()
1936
Sean Kau46e29bc2017-08-28 16:31:161937 if _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS, path):
[email protected]99171a92014-06-03 08:44:471938 return False
1939
1940 if (action == _GetIDLParseError and
Sean Kau46e29bc2017-08-28 16:31:161941 not _MatchesFile(input_api, idl_included_patterns, path)):
[email protected]99171a92014-06-03 08:44:471942 return False
1943 return True
1944
1945 results = []
1946 for affected_file in input_api.AffectedFiles(
1947 file_filter=FilterFile, include_deletes=False):
1948 action = get_action(affected_file)
1949 kwargs = {}
1950 if (action == _GetJSONParseError and
Sean Kau46e29bc2017-08-28 16:31:161951 _MatchesFile(input_api, json_no_comments_patterns,
1952 affected_file.LocalPath())):
[email protected]99171a92014-06-03 08:44:471953 kwargs['eat_comments'] = False
1954 parse_error = action(input_api,
1955 affected_file.AbsoluteLocalPath(),
1956 **kwargs)
1957 if parse_error:
1958 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1959 (affected_file.LocalPath(), parse_error)))
1960 return results
1961
1962
[email protected]760deea2013-12-10 19:33:491963def _CheckJavaStyle(input_api, output_api):
1964 """Runs checkstyle on changed java files and returns errors if any exist."""
mohan.reddyf21db962014-10-16 12:26:471965 import sys
[email protected]760deea2013-12-10 19:33:491966 original_sys_path = sys.path
1967 try:
1968 sys.path = sys.path + [input_api.os_path.join(
1969 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1970 import checkstyle
1971 finally:
1972 # Restore sys.path to what it was before.
1973 sys.path = original_sys_path
1974
1975 return checkstyle.RunCheckstyle(
davileen72d76532015-01-20 22:30:091976 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
newtd8b7d30e92015-01-23 18:10:511977 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]760deea2013-12-10 19:33:491978
1979
Sean Kau46e29bc2017-08-28 16:31:161980def _MatchesFile(input_api, patterns, path):
1981 for pattern in patterns:
1982 if input_api.re.search(pattern, path):
1983 return True
1984 return False
1985
1986
Daniel Cheng7052cdf2017-11-21 19:23:291987def _GetOwnersFilesToCheckForIpcOwners(input_api):
1988 """Gets a list of OWNERS files to check for correct security owners.
dchenge07de812016-06-20 19:27:171989
Daniel Cheng7052cdf2017-11-21 19:23:291990 Returns:
1991 A dictionary mapping an OWNER file to the list of OWNERS rules it must
1992 contain to cover IPC-related files with noparent reviewer rules.
1993 """
1994 # Whether or not a file affects IPC is (mostly) determined by a simple list
1995 # of filename patterns.
dchenge07de812016-06-20 19:27:171996 file_patterns = [
palmerb19a0932017-01-24 04:00:311997 # Legacy IPC:
dchenge07de812016-06-20 19:27:171998 '*_messages.cc',
1999 '*_messages*.h',
2000 '*_param_traits*.*',
palmerb19a0932017-01-24 04:00:312001 # Mojo IPC:
dchenge07de812016-06-20 19:27:172002 '*.mojom',
Daniel Cheng1f386932018-01-29 19:56:472003 '*_mojom_traits*.*',
dchenge07de812016-06-20 19:27:172004 '*_struct_traits*.*',
2005 '*_type_converter*.*',
palmerb19a0932017-01-24 04:00:312006 '*.typemap',
2007 # Android native IPC:
2008 '*.aidl',
2009 # Blink uses a different file naming convention:
2010 '*EnumTraits*.*',
Daniel Chenge0bf3f62018-01-30 01:56:472011 "*MojomTraits*.*",
dchenge07de812016-06-20 19:27:172012 '*StructTraits*.*',
2013 '*TypeConverter*.*',
2014 ]
2015
scottmg7a6ed5ba2016-11-04 18:22:042016 # These third_party directories do not contain IPCs, but contain files
2017 # matching the above patterns, which trigger false positives.
2018 exclude_paths = [
2019 'third_party/crashpad/*',
Andres Medinae684cf42018-08-27 18:48:232020 'third_party/protobuf/benchmarks/python/*',
Daniel Chengebe635e2018-07-13 12:36:062021 'third_party/third_party/blink/renderer/platform/bindings/*',
Nico Weberee3dc9b2017-08-31 17:09:292022 'third_party/win_build_output/*',
scottmg7a6ed5ba2016-11-04 18:22:042023 ]
2024
dchenge07de812016-06-20 19:27:172025 # Dictionary mapping an OWNERS file path to Patterns.
2026 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2027 # rules ) to a PatternEntry.
2028 # PatternEntry is a dictionary with two keys:
2029 # - 'files': the files that are matched by this pattern
2030 # - 'rules': the per-file rules needed for this pattern
2031 # For example, if we expect OWNERS file to contain rules for *.mojom and
2032 # *_struct_traits*.*, Patterns might look like this:
2033 # {
2034 # '*.mojom': {
2035 # 'files': ...,
2036 # 'rules': [
2037 # 'per-file *.mojom=set noparent',
2038 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2039 # ],
2040 # },
2041 # '*_struct_traits*.*': {
2042 # 'files': ...,
2043 # 'rules': [
2044 # 'per-file *_struct_traits*.*=set noparent',
2045 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2046 # ],
2047 # },
2048 # }
2049 to_check = {}
2050
Daniel Cheng13ca61a882017-08-25 15:11:252051 def AddPatternToCheck(input_file, pattern):
2052 owners_file = input_api.os_path.join(
2053 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2054 if owners_file not in to_check:
2055 to_check[owners_file] = {}
2056 if pattern not in to_check[owners_file]:
2057 to_check[owners_file][pattern] = {
2058 'files': [],
2059 'rules': [
2060 'per-file %s=set noparent' % pattern,
2061 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2062 ]
2063 }
Vaclav Brozekd5de76a2018-03-17 07:57:502064 to_check[owners_file][pattern]['files'].append(input_file)
Daniel Cheng13ca61a882017-08-25 15:11:252065
dchenge07de812016-06-20 19:27:172066 # Iterate through the affected files to see what we actually need to check
2067 # for. We should only nag patch authors about per-file rules if a file in that
2068 # directory would match that pattern. If a directory only contains *.mojom
2069 # files and no *_messages*.h files, we should only nag about rules for
2070 # *.mojom files.
Daniel Cheng13ca61a882017-08-25 15:11:252071 for f in input_api.AffectedFiles(include_deletes=False):
2072 # Manifest files don't have a strong naming convention. Instead, scan
2073 # affected files for .json files and see if they look like a manifest.
Sean Kau46e29bc2017-08-28 16:31:162074 if (f.LocalPath().endswith('.json') and
2075 not _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS,
2076 f.LocalPath())):
Daniel Cheng13ca61a882017-08-25 15:11:252077 json_comment_eater = _ImportJSONCommentEater(input_api)
2078 mostly_json_lines = '\n'.join(f.NewContents())
2079 # Comments aren't allowed in strict JSON, so filter them out.
2080 json_lines = json_comment_eater.Nom(mostly_json_lines)
Daniel Chenge8efd092018-03-23 23:57:432081 try:
2082 json_content = input_api.json.loads(json_lines)
2083 except:
2084 # There's another PRESUBMIT check that already verifies that JSON files
2085 # are not invalid, so no need to emit another warning here.
2086 continue
Daniel Cheng13ca61a882017-08-25 15:11:252087 if 'interface_provider_specs' in json_content:
2088 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
dchenge07de812016-06-20 19:27:172089 for pattern in file_patterns:
2090 if input_api.fnmatch.fnmatch(
2091 input_api.os_path.basename(f.LocalPath()), pattern):
scottmg7a6ed5ba2016-11-04 18:22:042092 skip = False
2093 for exclude in exclude_paths:
2094 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2095 skip = True
2096 break
2097 if skip:
2098 continue
Daniel Cheng13ca61a882017-08-25 15:11:252099 AddPatternToCheck(f, pattern)
dchenge07de812016-06-20 19:27:172100 break
2101
Daniel Cheng7052cdf2017-11-21 19:23:292102 return to_check
2103
2104
2105def _CheckIpcOwners(input_api, output_api):
2106 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2107 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
2108
2109 if to_check:
2110 # If there are any OWNERS files to check, there are IPC-related changes in
2111 # this CL. Auto-CC the review list.
2112 output_api.AppendCC('[email protected]')
2113
2114 # Go through the OWNERS files to check, filtering out rules that are already
2115 # present in that OWNERS file.
dchenge07de812016-06-20 19:27:172116 for owners_file, patterns in to_check.iteritems():
2117 try:
2118 with file(owners_file) as f:
2119 lines = set(f.read().splitlines())
2120 for entry in patterns.itervalues():
2121 entry['rules'] = [rule for rule in entry['rules'] if rule not in lines
2122 ]
2123 except IOError:
2124 # No OWNERS file, so all the rules are definitely missing.
2125 continue
2126
2127 # All the remaining lines weren't found in OWNERS files, so emit an error.
2128 errors = []
2129 for owners_file, patterns in to_check.iteritems():
2130 missing_lines = []
2131 files = []
Vaclav Brozekd5de76a2018-03-17 07:57:502132 for _, entry in patterns.iteritems():
dchenge07de812016-06-20 19:27:172133 missing_lines.extend(entry['rules'])
2134 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2135 if missing_lines:
2136 errors.append(
Vaclav Brozek1893a972018-04-25 05:48:052137 'Because of the presence of files:\n%s\n\n'
2138 '%s needs the following %d lines added:\n\n%s' %
2139 ('\n'.join(files), owners_file, len(missing_lines),
2140 '\n'.join(missing_lines)))
dchenge07de812016-06-20 19:27:172141
2142 results = []
2143 if errors:
vabrf5ce3bf92016-07-11 14:52:412144 if input_api.is_committing:
2145 output = output_api.PresubmitError
2146 else:
2147 output = output_api.PresubmitPromptWarning
2148 results.append(output(
Daniel Cheng52111692017-06-14 08:00:592149 'Found OWNERS files that need to be updated for IPC security ' +
2150 'review coverage.\nPlease update the OWNERS files below:',
dchenge07de812016-06-20 19:27:172151 long_text='\n\n'.join(errors)))
2152
2153 return results
2154
2155
jbriance9e12f162016-11-25 07:57:502156def _CheckUselessForwardDeclarations(input_api, output_api):
jbriance2c51e821a2016-12-12 08:24:312157 """Checks that added or removed lines in non third party affected
2158 header files do not lead to new useless class or struct forward
2159 declaration.
jbriance9e12f162016-11-25 07:57:502160 """
2161 results = []
2162 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
2163 input_api.re.MULTILINE)
2164 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
2165 input_api.re.MULTILINE)
2166 for f in input_api.AffectedFiles(include_deletes=False):
jbriance2c51e821a2016-12-12 08:24:312167 if (f.LocalPath().startswith('third_party') and
Kent Tamurae9b3a9ec2017-08-31 02:20:192168 not f.LocalPath().startswith('third_party/blink') and
2169 not f.LocalPath().startswith('third_party\\blink') and
jbriance2c51e821a2016-12-12 08:24:312170 not f.LocalPath().startswith('third_party/WebKit') and
2171 not f.LocalPath().startswith('third_party\\WebKit')):
2172 continue
2173
jbriance9e12f162016-11-25 07:57:502174 if not f.LocalPath().endswith('.h'):
2175 continue
2176
2177 contents = input_api.ReadFile(f)
2178 fwd_decls = input_api.re.findall(class_pattern, contents)
2179 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
2180
2181 useless_fwd_decls = []
2182 for decl in fwd_decls:
2183 count = sum(1 for _ in input_api.re.finditer(
2184 r'\b%s\b' % input_api.re.escape(decl), contents))
2185 if count == 1:
2186 useless_fwd_decls.append(decl)
2187
2188 if not useless_fwd_decls:
2189 continue
2190
2191 for line in f.GenerateScmDiff().splitlines():
2192 if (line.startswith('-') and not line.startswith('--') or
2193 line.startswith('+') and not line.startswith('++')):
2194 for decl in useless_fwd_decls:
2195 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
2196 results.append(output_api.PresubmitPromptWarning(
ricea6416dea2017-05-19 12:39:242197 '%s: %s forward declaration is no longer needed' %
jbriance9e12f162016-11-25 07:57:502198 (f.LocalPath(), decl)))
2199 useless_fwd_decls.remove(decl)
2200
2201 return results
2202
2203
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492204# TODO: add unit tests
dskiba88634f4e2015-08-14 23:03:292205def _CheckAndroidToastUsage(input_api, output_api):
2206 """Checks that code uses org.chromium.ui.widget.Toast instead of
2207 android.widget.Toast (Chromium Toast doesn't force hardware
2208 acceleration on low-end devices, saving memory).
2209 """
2210 toast_import_pattern = input_api.re.compile(
2211 r'^import android\.widget\.Toast;$')
2212
2213 errors = []
2214
2215 sources = lambda affected_file: input_api.FilterSourceFile(
2216 affected_file,
2217 black_list=(_EXCLUDED_PATHS +
2218 _TEST_CODE_EXCLUDED_PATHS +
2219 input_api.DEFAULT_BLACK_LIST +
2220 (r'^chromecast[\\\/].*',
2221 r'^remoting[\\\/].*')),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492222 white_list=[r'.*\.java$'])
dskiba88634f4e2015-08-14 23:03:292223
2224 for f in input_api.AffectedSourceFiles(sources):
2225 for line_num, line in f.ChangedContents():
2226 if toast_import_pattern.search(line):
2227 errors.append("%s:%d" % (f.LocalPath(), line_num))
2228
2229 results = []
2230
2231 if errors:
2232 results.append(output_api.PresubmitError(
2233 'android.widget.Toast usage is detected. Android toasts use hardware'
2234 ' acceleration, and can be\ncostly on low-end devices. Please use'
2235 ' org.chromium.ui.widget.Toast instead.\n'
2236 'Contact [email protected] if you have any questions.',
2237 errors))
2238
2239 return results
2240
2241
dgnaa68d5e2015-06-10 10:08:222242def _CheckAndroidCrLogUsage(input_api, output_api):
2243 """Checks that new logs using org.chromium.base.Log:
2244 - Are using 'TAG' as variable name for the tags (warn)
dgn38736db2015-09-18 19:20:512245 - Are using a tag that is shorter than 20 characters (error)
dgnaa68d5e2015-06-10 10:08:222246 """
pkotwicza1dd0b002016-05-16 14:41:042247
torne89540622017-03-24 19:41:302248 # Do not check format of logs in the given files
pkotwicza1dd0b002016-05-16 14:41:042249 cr_log_check_excluded_paths = [
torne89540622017-03-24 19:41:302250 # //chrome/android/webapk cannot depend on //base
pkotwicza1dd0b002016-05-16 14:41:042251 r"^chrome[\\\/]android[\\\/]webapk[\\\/].*",
torne89540622017-03-24 19:41:302252 # WebView license viewer code cannot depend on //base; used in stub APK.
2253 r"^android_webview[\\\/]glue[\\\/]java[\\\/]src[\\\/]com[\\\/]android[\\\/]"
2254 r"webview[\\\/]chromium[\\\/]License.*",
Egor Paskoa5c05b02018-09-28 16:04:092255 # The customtabs_benchmark is a small app that does not depend on Chromium
2256 # java pieces.
2257 r"tools[\\\/]android[\\\/]customtabs_benchmark[\\\/].*",
pkotwicza1dd0b002016-05-16 14:41:042258 ]
2259
dgnaa68d5e2015-06-10 10:08:222260 cr_log_import_pattern = input_api.re.compile(
dgn87d9fb62015-06-12 09:15:122261 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
2262 class_in_base_pattern = input_api.re.compile(
2263 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
2264 has_some_log_import_pattern = input_api.re.compile(
2265 r'^import .*\.Log;$', input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222266 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
dgn87d9fb62015-06-12 09:15:122267 log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
dgnaa68d5e2015-06-10 10:08:222268 log_decl_pattern = input_api.re.compile(
dgn38736db2015-09-18 19:20:512269 r'^\s*private static final String TAG = "(?P<name>(.*))";',
dgnaa68d5e2015-06-10 10:08:222270 input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222271
Vincent Scheib16d7b272015-09-15 18:09:072272 REF_MSG = ('See docs/android_logging.md '
dgnaa68d5e2015-06-10 10:08:222273 'or contact [email protected] for more info.')
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492274 sources = lambda x: input_api.FilterSourceFile(x, white_list=[r'.*\.java$'],
pkotwicza1dd0b002016-05-16 14:41:042275 black_list=cr_log_check_excluded_paths)
dgn87d9fb62015-06-12 09:15:122276
dgnaa68d5e2015-06-10 10:08:222277 tag_decl_errors = []
2278 tag_length_errors = []
dgn87d9fb62015-06-12 09:15:122279 tag_errors = []
dgn38736db2015-09-18 19:20:512280 tag_with_dot_errors = []
dgn87d9fb62015-06-12 09:15:122281 util_log_errors = []
dgnaa68d5e2015-06-10 10:08:222282
2283 for f in input_api.AffectedSourceFiles(sources):
2284 file_content = input_api.ReadFile(f)
2285 has_modified_logs = False
2286
2287 # Per line checks
dgn87d9fb62015-06-12 09:15:122288 if (cr_log_import_pattern.search(file_content) or
2289 (class_in_base_pattern.search(file_content) and
2290 not has_some_log_import_pattern.search(file_content))):
2291 # Checks to run for files using cr log
dgnaa68d5e2015-06-10 10:08:222292 for line_num, line in f.ChangedContents():
2293
2294 # Check if the new line is doing some logging
dgn87d9fb62015-06-12 09:15:122295 match = log_call_pattern.search(line)
dgnaa68d5e2015-06-10 10:08:222296 if match:
2297 has_modified_logs = True
2298
2299 # Make sure it uses "TAG"
2300 if not match.group('tag') == 'TAG':
2301 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgn87d9fb62015-06-12 09:15:122302 else:
2303 # Report non cr Log function calls in changed lines
2304 for line_num, line in f.ChangedContents():
2305 if log_call_pattern.search(line):
2306 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgnaa68d5e2015-06-10 10:08:222307
2308 # Per file checks
2309 if has_modified_logs:
2310 # Make sure the tag is using the "cr" prefix and is not too long
2311 match = log_decl_pattern.search(file_content)
dgn38736db2015-09-18 19:20:512312 tag_name = match.group('name') if match else None
2313 if not tag_name:
dgnaa68d5e2015-06-10 10:08:222314 tag_decl_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512315 elif len(tag_name) > 20:
dgnaa68d5e2015-06-10 10:08:222316 tag_length_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512317 elif '.' in tag_name:
2318 tag_with_dot_errors.append(f.LocalPath())
dgnaa68d5e2015-06-10 10:08:222319
2320 results = []
2321 if tag_decl_errors:
2322 results.append(output_api.PresubmitPromptWarning(
2323 'Please define your tags using the suggested format: .\n'
dgn38736db2015-09-18 19:20:512324 '"private static final String TAG = "<package tag>".\n'
2325 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222326 tag_decl_errors))
2327
2328 if tag_length_errors:
2329 results.append(output_api.PresubmitError(
2330 'The tag length is restricted by the system to be at most '
dgn38736db2015-09-18 19:20:512331 '20 characters.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222332 tag_length_errors))
2333
2334 if tag_errors:
2335 results.append(output_api.PresubmitPromptWarning(
2336 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
2337 tag_errors))
2338
dgn87d9fb62015-06-12 09:15:122339 if util_log_errors:
dgn4401aa52015-04-29 16:26:172340 results.append(output_api.PresubmitPromptWarning(
dgn87d9fb62015-06-12 09:15:122341 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
2342 util_log_errors))
2343
dgn38736db2015-09-18 19:20:512344 if tag_with_dot_errors:
2345 results.append(output_api.PresubmitPromptWarning(
2346 'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
2347 tag_with_dot_errors))
2348
dgn4401aa52015-04-29 16:26:172349 return results
2350
2351
Yoland Yanb92fa522017-08-28 17:37:062352def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
2353 """Checks that junit.framework.* is no longer used."""
2354 deprecated_junit_framework_pattern = input_api.re.compile(
2355 r'^import junit\.framework\..*;',
2356 input_api.re.MULTILINE)
2357 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492358 x, white_list=[r'.*\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062359 errors = []
2360 for f in input_api.AffectedFiles(sources):
2361 for line_num, line in f.ChangedContents():
2362 if deprecated_junit_framework_pattern.search(line):
2363 errors.append("%s:%d" % (f.LocalPath(), line_num))
2364
2365 results = []
2366 if errors:
2367 results.append(output_api.PresubmitError(
2368 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
2369 '(org.junit.*) from //third_party/junit. Contact [email protected]'
2370 ' if you have any question.', errors))
2371 return results
2372
2373
2374def _CheckAndroidTestJUnitInheritance(input_api, output_api):
2375 """Checks that if new Java test classes have inheritance.
2376 Either the new test class is JUnit3 test or it is a JUnit4 test class
2377 with a base class, either case is undesirable.
2378 """
2379 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
2380
2381 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492382 x, white_list=[r'.*Test\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062383 errors = []
2384 for f in input_api.AffectedFiles(sources):
2385 if not f.OldContents():
2386 class_declaration_start_flag = False
2387 for line_num, line in f.ChangedContents():
2388 if class_declaration_pattern.search(line):
2389 class_declaration_start_flag = True
2390 if class_declaration_start_flag and ' extends ' in line:
2391 errors.append('%s:%d' % (f.LocalPath(), line_num))
2392 if '{' in line:
2393 class_declaration_start_flag = False
2394
2395 results = []
2396 if errors:
2397 results.append(output_api.PresubmitPromptWarning(
2398 'The newly created files include Test classes that inherits from base'
2399 ' class. Please do not use inheritance in JUnit4 tests or add new'
2400 ' JUnit3 tests. Contact [email protected] if you have any'
2401 ' questions.', errors))
2402 return results
2403
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202404
yolandyan45001472016-12-21 21:12:422405def _CheckAndroidTestAnnotationUsage(input_api, output_api):
2406 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
2407 deprecated_annotation_import_pattern = input_api.re.compile(
2408 r'^import android\.test\.suitebuilder\.annotation\..*;',
2409 input_api.re.MULTILINE)
2410 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492411 x, white_list=[r'.*\.java$'], black_list=None)
yolandyan45001472016-12-21 21:12:422412 errors = []
2413 for f in input_api.AffectedFiles(sources):
2414 for line_num, line in f.ChangedContents():
2415 if deprecated_annotation_import_pattern.search(line):
2416 errors.append("%s:%d" % (f.LocalPath(), line_num))
2417
2418 results = []
2419 if errors:
2420 results.append(output_api.PresubmitError(
2421 'Annotations in android.test.suitebuilder.annotation have been'
2422 ' deprecated since API level 24. Please use android.support.test.filters'
2423 ' from //third_party/android_support_test_runner:runner_java instead.'
2424 ' Contact [email protected] if you have any questions.', errors))
2425 return results
2426
2427
agrieve7b6479d82015-10-07 14:24:222428def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
2429 """Checks if MDPI assets are placed in a correct directory."""
2430 file_filter = lambda f: (f.LocalPath().endswith('.png') and
2431 ('/res/drawable/' in f.LocalPath() or
2432 '/res/drawable-ldrtl/' in f.LocalPath()))
2433 errors = []
2434 for f in input_api.AffectedFiles(include_deletes=False,
2435 file_filter=file_filter):
2436 errors.append(' %s' % f.LocalPath())
2437
2438 results = []
2439 if errors:
2440 results.append(output_api.PresubmitError(
2441 'MDPI assets should be placed in /res/drawable-mdpi/ or '
2442 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
2443 '/res/drawable-ldrtl/.\n'
2444 'Contact [email protected] if you have questions.', errors))
2445 return results
2446
2447
Nate Fischer535972b2017-09-16 01:06:182448def _CheckAndroidWebkitImports(input_api, output_api):
2449 """Checks that code uses org.chromium.base.Callback instead of
2450 android.widget.ValueCallback except in the WebView glue layer.
2451 """
2452 valuecallback_import_pattern = input_api.re.compile(
2453 r'^import android\.webkit\.ValueCallback;$')
2454
2455 errors = []
2456
2457 sources = lambda affected_file: input_api.FilterSourceFile(
2458 affected_file,
2459 black_list=(_EXCLUDED_PATHS +
2460 _TEST_CODE_EXCLUDED_PATHS +
2461 input_api.DEFAULT_BLACK_LIST +
2462 (r'^android_webview[\\\/]glue[\\\/].*',)),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492463 white_list=[r'.*\.java$'])
Nate Fischer535972b2017-09-16 01:06:182464
2465 for f in input_api.AffectedSourceFiles(sources):
2466 for line_num, line in f.ChangedContents():
2467 if valuecallback_import_pattern.search(line):
2468 errors.append("%s:%d" % (f.LocalPath(), line_num))
2469
2470 results = []
2471
2472 if errors:
2473 results.append(output_api.PresubmitError(
2474 'android.webkit.ValueCallback usage is detected outside of the glue'
2475 ' layer. To stay compatible with the support library, android.webkit.*'
2476 ' classes should only be used inside the glue layer and'
2477 ' org.chromium.base.Callback should be used instead.',
2478 errors))
2479
2480 return results
2481
2482
agrievef32bcc72016-04-04 14:57:402483class PydepsChecker(object):
2484 def __init__(self, input_api, pydeps_files):
2485 self._file_cache = {}
2486 self._input_api = input_api
2487 self._pydeps_files = pydeps_files
2488
2489 def _LoadFile(self, path):
2490 """Returns the list of paths within a .pydeps file relative to //."""
2491 if path not in self._file_cache:
2492 with open(path) as f:
2493 self._file_cache[path] = f.read()
2494 return self._file_cache[path]
2495
2496 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
2497 """Returns an interable of paths within the .pydep, relativized to //."""
2498 os_path = self._input_api.os_path
2499 pydeps_dir = os_path.dirname(pydeps_path)
2500 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines()
2501 if not l.startswith('*'))
2502 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries)
2503
2504 def _CreateFilesToPydepsMap(self):
2505 """Returns a map of local_path -> list_of_pydeps."""
2506 ret = {}
2507 for pydep_local_path in self._pydeps_files:
2508 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
2509 ret.setdefault(path, []).append(pydep_local_path)
2510 return ret
2511
2512 def ComputeAffectedPydeps(self):
2513 """Returns an iterable of .pydeps files that might need regenerating."""
2514 affected_pydeps = set()
2515 file_to_pydeps_map = None
2516 for f in self._input_api.AffectedFiles(include_deletes=True):
2517 local_path = f.LocalPath()
2518 if local_path == 'DEPS':
2519 return self._pydeps_files
2520 elif local_path.endswith('.pydeps'):
2521 if local_path in self._pydeps_files:
2522 affected_pydeps.add(local_path)
2523 elif local_path.endswith('.py'):
2524 if file_to_pydeps_map is None:
2525 file_to_pydeps_map = self._CreateFilesToPydepsMap()
2526 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
2527 return affected_pydeps
2528
2529 def DetermineIfStale(self, pydeps_path):
2530 """Runs print_python_deps.py to see if the files is stale."""
phajdan.jr0d9878552016-11-04 10:49:412531 import difflib
John Budorick47ca3fe2018-02-10 00:53:102532 import os
2533
agrievef32bcc72016-04-04 14:57:402534 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
2535 cmd = old_pydeps_data[1][1:].strip()
John Budorick47ca3fe2018-02-10 00:53:102536 env = dict(os.environ)
2537 env['PYTHONDONTWRITEBYTECODE'] = '1'
agrievef32bcc72016-04-04 14:57:402538 new_pydeps_data = self._input_api.subprocess.check_output(
John Budorick47ca3fe2018-02-10 00:53:102539 cmd + ' --output ""', shell=True, env=env)
phajdan.jr0d9878552016-11-04 10:49:412540 old_contents = old_pydeps_data[2:]
2541 new_contents = new_pydeps_data.splitlines()[2:]
agrievef32bcc72016-04-04 14:57:402542 if old_pydeps_data[2:] != new_pydeps_data.splitlines()[2:]:
phajdan.jr0d9878552016-11-04 10:49:412543 return cmd, '\n'.join(difflib.context_diff(old_contents, new_contents))
agrievef32bcc72016-04-04 14:57:402544
2545
2546def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
2547 """Checks if a .pydeps file needs to be regenerated."""
John Chencde89192018-01-27 21:18:402548 # This check is for Python dependency lists (.pydeps files), and involves
2549 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
2550 # doesn't work on Windows and Mac, so skip it on other platforms.
agrieve9bc4200b2016-05-04 16:33:282551 if input_api.platform != 'linux2':
agrievebb9c5b472016-04-22 15:13:002552 return []
Mostyn Bramley-Moore6b427322017-12-21 22:11:022553 # TODO(agrieve): Update when there's a better way to detect
2554 # this: crbug.com/570091
agrievef32bcc72016-04-04 14:57:402555 is_android = input_api.os_path.exists('third_party/android_tools')
2556 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
2557 results = []
2558 # First, check for new / deleted .pydeps.
2559 for f in input_api.AffectedFiles(include_deletes=True):
Zhiling Huang45cabf32018-03-10 00:50:032560 # Check whether we are running the presubmit check for a file in src.
2561 # f.LocalPath is relative to repo (src, or internal repo).
2562 # os_path.exists is relative to src repo.
2563 # Therefore if os_path.exists is true, it means f.LocalPath is relative
2564 # to src and we can conclude that the pydeps is in src.
2565 if input_api.os_path.exists(f.LocalPath()):
2566 if f.LocalPath().endswith('.pydeps'):
2567 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
2568 results.append(output_api.PresubmitError(
2569 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2570 'remove %s' % f.LocalPath()))
2571 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES:
2572 results.append(output_api.PresubmitError(
2573 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2574 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:402575
2576 if results:
2577 return results
2578
2579 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files)
2580
2581 for pydep_path in checker.ComputeAffectedPydeps():
2582 try:
phajdan.jr0d9878552016-11-04 10:49:412583 result = checker.DetermineIfStale(pydep_path)
2584 if result:
2585 cmd, diff = result
agrievef32bcc72016-04-04 14:57:402586 results.append(output_api.PresubmitError(
phajdan.jr0d9878552016-11-04 10:49:412587 'File is stale: %s\nDiff (apply to fix):\n%s\n'
2588 'To regenerate, run:\n\n %s' %
2589 (pydep_path, diff, cmd)))
agrievef32bcc72016-04-04 14:57:402590 except input_api.subprocess.CalledProcessError as error:
2591 return [output_api.PresubmitError('Error running: %s' % error.cmd,
2592 long_text=error.output)]
2593
2594 return results
2595
2596
glidere61efad2015-02-18 17:39:432597def _CheckSingletonInHeaders(input_api, output_api):
2598 """Checks to make sure no header files have |Singleton<|."""
2599 def FileFilter(affected_file):
2600 # It's ok for base/memory/singleton.h to have |Singleton<|.
2601 black_list = (_EXCLUDED_PATHS +
2602 input_api.DEFAULT_BLACK_LIST +
Michael Warrese4451492018-03-07 04:42:472603 (r"^base[\\\/]memory[\\\/]singleton\.h$",
2604 r"^net[\\\/]quic[\\\/]platform[\\\/]impl[\\\/]"
2605 r"quic_singleton_impl\.h$"))
glidere61efad2015-02-18 17:39:432606 return input_api.FilterSourceFile(affected_file, black_list=black_list)
2607
sergeyu34d21222015-09-16 00:11:442608 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
glidere61efad2015-02-18 17:39:432609 files = []
2610 for f in input_api.AffectedSourceFiles(FileFilter):
2611 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
2612 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
2613 contents = input_api.ReadFile(f)
2614 for line in contents.splitlines(False):
oysteinec430ad42015-10-22 20:55:242615 if (not line.lstrip().startswith('//') and # Strip C++ comment.
glidere61efad2015-02-18 17:39:432616 pattern.search(line)):
2617 files.append(f)
2618 break
2619
2620 if files:
yolandyandaabc6d2016-04-18 18:29:392621 return [output_api.PresubmitError(
sergeyu34d21222015-09-16 00:11:442622 'Found base::Singleton<T> in the following header files.\n' +
glidere61efad2015-02-18 17:39:432623 'Please move them to an appropriate source file so that the ' +
2624 'template gets instantiated in a single compilation unit.',
2625 files) ]
2626 return []
2627
2628
[email protected]fd20b902014-05-09 02:14:532629_DEPRECATED_CSS = [
2630 # Values
2631 ( "-webkit-box", "flex" ),
2632 ( "-webkit-inline-box", "inline-flex" ),
2633 ( "-webkit-flex", "flex" ),
2634 ( "-webkit-inline-flex", "inline-flex" ),
2635 ( "-webkit-min-content", "min-content" ),
2636 ( "-webkit-max-content", "max-content" ),
2637
2638 # Properties
2639 ( "-webkit-background-clip", "background-clip" ),
2640 ( "-webkit-background-origin", "background-origin" ),
2641 ( "-webkit-background-size", "background-size" ),
2642 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:442643 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:532644
2645 # Functions
2646 ( "-webkit-gradient", "gradient" ),
2647 ( "-webkit-repeating-gradient", "repeating-gradient" ),
2648 ( "-webkit-linear-gradient", "linear-gradient" ),
2649 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
2650 ( "-webkit-radial-gradient", "radial-gradient" ),
2651 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
2652]
2653
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202654
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492655# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242656def _CheckNoDeprecatedCss(input_api, output_api):
[email protected]fd20b902014-05-09 02:14:532657 """ Make sure that we don't use deprecated CSS
[email protected]9a48e3f82014-05-22 00:06:252658 properties, functions or values. Our external
mdjonesae0286c32015-06-10 18:10:342659 documentation and iOS CSS for dom distiller
2660 (reader mode) are ignored by the hooks as it
[email protected]9a48e3f82014-05-22 00:06:252661 needs to be consumed by WebKit. """
[email protected]fd20b902014-05-09 02:14:532662 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492663 file_inclusion_pattern = [r".+\.css$"]
[email protected]9a48e3f82014-05-22 00:06:252664 black_list = (_EXCLUDED_PATHS +
2665 _TEST_CODE_EXCLUDED_PATHS +
2666 input_api.DEFAULT_BLACK_LIST +
2667 (r"^chrome/common/extensions/docs",
2668 r"^chrome/docs",
mdjonesae0286c32015-06-10 18:10:342669 r"^components/dom_distiller/core/css/distilledpage_ios.css",
sdefresne6308d7f2016-02-15 09:38:442670 r"^components/neterror/resources/neterror.css",
[email protected]9a48e3f82014-05-22 00:06:252671 r"^native_client_sdk"))
2672 file_filter = lambda f: input_api.FilterSourceFile(
2673 f, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]fd20b902014-05-09 02:14:532674 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2675 for line_num, line in fpath.ChangedContents():
2676 for (deprecated_value, value) in _DEPRECATED_CSS:
dbeam070cfe62014-10-22 06:44:022677 if deprecated_value in line:
[email protected]fd20b902014-05-09 02:14:532678 results.append(output_api.PresubmitError(
2679 "%s:%d: Use of deprecated CSS %s, use %s instead" %
2680 (fpath.LocalPath(), line_num, deprecated_value, value)))
2681 return results
2682
mohan.reddyf21db962014-10-16 12:26:472683
dbeam070cfe62014-10-22 06:44:022684_DEPRECATED_JS = [
2685 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
2686 ( "__defineGetter__", "Object.defineProperty" ),
2687 ( "__defineSetter__", "Object.defineProperty" ),
2688]
2689
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202690
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492691# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242692def _CheckNoDeprecatedJs(input_api, output_api):
dbeam070cfe62014-10-22 06:44:022693 """Make sure that we don't use deprecated JS in Chrome code."""
2694 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492695 file_inclusion_pattern = [r".+\.js$"] # TODO(dbeam): .html?
dbeam070cfe62014-10-22 06:44:022696 black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2697 input_api.DEFAULT_BLACK_LIST)
2698 file_filter = lambda f: input_api.FilterSourceFile(
2699 f, white_list=file_inclusion_pattern, black_list=black_list)
2700 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2701 for lnum, line in fpath.ChangedContents():
2702 for (deprecated, replacement) in _DEPRECATED_JS:
2703 if deprecated in line:
2704 results.append(output_api.PresubmitError(
2705 "%s:%d: Use of deprecated JS %s, use %s instead" %
2706 (fpath.LocalPath(), lnum, deprecated, replacement)))
2707 return results
2708
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202709
dpapadd651231d82017-07-21 02:44:472710def _CheckForRiskyJsArrowFunction(line_number, line):
2711 if ' => ' in line:
2712 return "line %d, is using an => (arrow) function\n %s\n" % (
2713 line_number, line)
2714 return ''
2715
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202716
dpapadd651231d82017-07-21 02:44:472717def _CheckForRiskyJsConstLet(input_api, line_number, line):
2718 if input_api.re.match('^\s*(const|let)\s', line):
2719 return "line %d, is using const/let keyword\n %s\n" % (
2720 line_number, line)
2721 return ''
dbeam070cfe62014-10-22 06:44:022722
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202723
dbeam1ec68ac2016-12-15 05:22:242724def _CheckForRiskyJsFeatures(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492725 maybe_ios_js = [r"^(ios|components|ui\/webui\/resources)\/.+\.js$"]
Steven Bennetts90545f3cb2017-08-14 18:11:002726 # 'ui/webui/resources/cr_components are not allowed on ios'
2727 not_ios_filter = (r".*ui\/webui\/resources\/cr_components.*", )
Steven Bennetts9c7e3c22017-08-02 19:10:572728 file_filter = lambda f: input_api.FilterSourceFile(f, white_list=maybe_ios_js,
Steven Bennetts90545f3cb2017-08-14 18:11:002729 black_list=not_ios_filter)
dpapadd651231d82017-07-21 02:44:472730 results = []
dbeam1ec68ac2016-12-15 05:22:242731 for f in input_api.AffectedFiles(file_filter=file_filter):
dpapadd651231d82017-07-21 02:44:472732 arrow_error_lines = []
2733 const_let_error_lines = []
dbeam1ec68ac2016-12-15 05:22:242734 for lnum, line in f.ChangedContents():
dpapadd651231d82017-07-21 02:44:472735 arrow_error_lines += filter(None, [
2736 _CheckForRiskyJsArrowFunction(lnum, line),
2737 ])
dbeam1ec68ac2016-12-15 05:22:242738
dpapadd651231d82017-07-21 02:44:472739 const_let_error_lines += filter(None, [
2740 _CheckForRiskyJsConstLet(input_api, lnum, line),
2741 ])
dbeam1ec68ac2016-12-15 05:22:242742
dpapadd651231d82017-07-21 02:44:472743 if arrow_error_lines:
2744 arrow_error_lines = map(
2745 lambda e: "%s:%s" % (f.LocalPath(), e), arrow_error_lines)
2746 results.append(
2747 output_api.PresubmitPromptWarning('\n'.join(arrow_error_lines + [
2748"""
2749Use of => (arrow) operator detected in:
dbeam1ec68ac2016-12-15 05:22:242750%s
2751Please ensure your code does not run on iOS9 (=> (arrow) does not work there).
2752https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#Arrow-Functions
dpapadd651231d82017-07-21 02:44:472753""" % f.LocalPath()
2754 ])))
dbeam1ec68ac2016-12-15 05:22:242755
dpapadd651231d82017-07-21 02:44:472756 if const_let_error_lines:
2757 const_let_error_lines = map(
2758 lambda e: "%s:%s" % (f.LocalPath(), e), const_let_error_lines)
2759 results.append(
2760 output_api.PresubmitPromptWarning('\n'.join(const_let_error_lines + [
2761"""
2762Use of const/let keywords detected in:
2763%s
2764Please ensure your code does not run on iOS9 because const/let is not fully
2765supported.
2766https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#let-Block_Scoped-Variables
2767https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#const-Block_Scoped-Constants
2768""" % f.LocalPath()
2769 ])))
2770
2771 return results
dbeam1ec68ac2016-12-15 05:22:242772
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202773
rlanday6802cf632017-05-30 17:48:362774def _CheckForRelativeIncludes(input_api, output_api):
2775 # Need to set the sys.path so PRESUBMIT_test.py runs properly
2776 import sys
2777 original_sys_path = sys.path
2778 try:
2779 sys.path = sys.path + [input_api.os_path.join(
2780 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
2781 from cpp_checker import CppChecker
2782 finally:
2783 # Restore sys.path to what it was before.
2784 sys.path = original_sys_path
2785
2786 bad_files = {}
2787 for f in input_api.AffectedFiles(include_deletes=False):
2788 if (f.LocalPath().startswith('third_party') and
2789 not f.LocalPath().startswith('third_party/WebKit') and
2790 not f.LocalPath().startswith('third_party\\WebKit')):
2791 continue
2792
2793 if not CppChecker.IsCppFile(f.LocalPath()):
2794 continue
2795
Vaclav Brozekd5de76a2018-03-17 07:57:502796 relative_includes = [line for _, line in f.ChangedContents()
rlanday6802cf632017-05-30 17:48:362797 if "#include" in line and "../" in line]
2798 if not relative_includes:
2799 continue
2800 bad_files[f.LocalPath()] = relative_includes
2801
2802 if not bad_files:
2803 return []
2804
2805 error_descriptions = []
2806 for file_path, bad_lines in bad_files.iteritems():
2807 error_description = file_path
2808 for line in bad_lines:
2809 error_description += '\n ' + line
2810 error_descriptions.append(error_description)
2811
2812 results = []
2813 results.append(output_api.PresubmitError(
2814 'You added one or more relative #include paths (including "../").\n'
2815 'These shouldn\'t be used because they can be used to include headers\n'
2816 'from code that\'s not correctly specified as a dependency in the\n'
2817 'relevant BUILD.gn file(s).',
2818 error_descriptions))
2819
2820 return results
2821
Takeshi Yoshinoe387aa32017-08-02 13:16:132822
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202823def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
2824 if not isinstance(key, ast.Str):
2825 return 'Key at line %d must be a string literal' % key.lineno
2826 if not isinstance(value, ast.Dict):
2827 return 'Value at line %d must be a dict' % value.lineno
2828 if len(value.keys) != 1:
2829 return 'Dict at line %d must have single entry' % value.lineno
2830 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
2831 return (
2832 'Entry at line %d must have a string literal \'filepath\' as key' %
2833 value.lineno)
2834 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132835
Takeshi Yoshinoe387aa32017-08-02 13:16:132836
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202837def _CheckWatchlistsEntrySyntax(key, value, ast):
2838 if not isinstance(key, ast.Str):
2839 return 'Key at line %d must be a string literal' % key.lineno
2840 if not isinstance(value, ast.List):
2841 return 'Value at line %d must be a list' % value.lineno
2842 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132843
Takeshi Yoshinoe387aa32017-08-02 13:16:132844
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202845def _CheckWATCHLISTSEntries(wd_dict, w_dict, ast):
2846 mismatch_template = (
2847 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
2848 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:132849
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202850 i = 0
2851 last_key = ''
2852 while True:
2853 if i >= len(wd_dict.keys):
2854 if i >= len(w_dict.keys):
2855 return None
2856 return mismatch_template % ('missing', 'line %d' % w_dict.keys[i].lineno)
2857 elif i >= len(w_dict.keys):
2858 return (
2859 mismatch_template % ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:132860
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202861 wd_key = wd_dict.keys[i]
2862 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:132863
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202864 result = _CheckWatchlistDefinitionsEntrySyntax(
2865 wd_key, wd_dict.values[i], ast)
2866 if result is not None:
2867 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:132868
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202869 result = _CheckWatchlistsEntrySyntax(w_key, w_dict.values[i], ast)
2870 if result is not None:
2871 return 'Bad entry in WATCHLISTS dict: %s' % result
2872
2873 if wd_key.s != w_key.s:
2874 return mismatch_template % (
2875 '%s at line %d' % (wd_key.s, wd_key.lineno),
2876 '%s at line %d' % (w_key.s, w_key.lineno))
2877
2878 if wd_key.s < last_key:
2879 return (
2880 'WATCHLISTS dict is not sorted lexicographically at line %d and %d' %
2881 (wd_key.lineno, w_key.lineno))
2882 last_key = wd_key.s
2883
2884 i = i + 1
2885
2886
2887def _CheckWATCHLISTSSyntax(expression, ast):
2888 if not isinstance(expression, ast.Expression):
2889 return 'WATCHLISTS file must contain a valid expression'
2890 dictionary = expression.body
2891 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
2892 return 'WATCHLISTS file must have single dict with exactly two entries'
2893
2894 first_key = dictionary.keys[0]
2895 first_value = dictionary.values[0]
2896 second_key = dictionary.keys[1]
2897 second_value = dictionary.values[1]
2898
2899 if (not isinstance(first_key, ast.Str) or
2900 first_key.s != 'WATCHLIST_DEFINITIONS' or
2901 not isinstance(first_value, ast.Dict)):
2902 return (
2903 'The first entry of the dict in WATCHLISTS file must be '
2904 'WATCHLIST_DEFINITIONS dict')
2905
2906 if (not isinstance(second_key, ast.Str) or
2907 second_key.s != 'WATCHLISTS' or
2908 not isinstance(second_value, ast.Dict)):
2909 return (
2910 'The second entry of the dict in WATCHLISTS file must be '
2911 'WATCHLISTS dict')
2912
2913 return _CheckWATCHLISTSEntries(first_value, second_value, ast)
Takeshi Yoshinoe387aa32017-08-02 13:16:132914
2915
2916def _CheckWATCHLISTS(input_api, output_api):
2917 for f in input_api.AffectedFiles(include_deletes=False):
2918 if f.LocalPath() == 'WATCHLISTS':
2919 contents = input_api.ReadFile(f, 'r')
2920
2921 try:
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202922 # First, make sure that it can be evaluated.
Takeshi Yoshinoe387aa32017-08-02 13:16:132923 input_api.ast.literal_eval(contents)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202924 # Get an AST tree for it and scan the tree for detailed style checking.
2925 expression = input_api.ast.parse(
2926 contents, filename='WATCHLISTS', mode='eval')
2927 except ValueError as e:
2928 return [output_api.PresubmitError(
2929 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2930 except SyntaxError as e:
2931 return [output_api.PresubmitError(
2932 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2933 except TypeError as e:
2934 return [output_api.PresubmitError(
2935 'Cannot parse WATCHLISTS file', long_text=repr(e))]
Takeshi Yoshinoe387aa32017-08-02 13:16:132936
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202937 result = _CheckWATCHLISTSSyntax(expression, input_api.ast)
2938 if result is not None:
2939 return [output_api.PresubmitError(result)]
2940 break
Takeshi Yoshinoe387aa32017-08-02 13:16:132941
2942 return []
2943
2944
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:192945def _CheckNewHeaderWithoutGnChange(input_api, output_api):
2946 """Checks that newly added header files have corresponding GN changes.
2947 Note that this is only a heuristic. To be precise, run script:
2948 build/check_gn_headers.py.
2949 """
2950
2951 def headers(f):
2952 return input_api.FilterSourceFile(
2953 f, white_list=(r'.+%s' % _HEADER_EXTENSIONS, ))
2954
2955 new_headers = []
2956 for f in input_api.AffectedSourceFiles(headers):
2957 if f.Action() != 'A':
2958 continue
2959 new_headers.append(f.LocalPath())
2960
2961 def gn_files(f):
2962 return input_api.FilterSourceFile(f, white_list=(r'.+\.gn', ))
2963
2964 all_gn_changed_contents = ''
2965 for f in input_api.AffectedSourceFiles(gn_files):
2966 for _, line in f.ChangedContents():
2967 all_gn_changed_contents += line
2968
2969 problems = []
2970 for header in new_headers:
2971 basename = input_api.os_path.basename(header)
2972 if basename not in all_gn_changed_contents:
2973 problems.append(header)
2974
2975 if problems:
2976 return [output_api.PresubmitPromptWarning(
2977 'Missing GN changes for new header files', items=sorted(problems),
2978 long_text='Please double check whether newly added header files need '
2979 'corresponding changes in gn or gni files.\nThis checking is only a '
2980 'heuristic. Run build/check_gn_headers.py to be precise.\n'
2981 'Read https://crbug.com/661774 for more info.')]
2982 return []
2983
2984
dgnaa68d5e2015-06-10 10:08:222985def _AndroidSpecificOnUploadChecks(input_api, output_api):
2986 """Groups checks that target android code."""
2987 results = []
dgnaa68d5e2015-06-10 10:08:222988 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
agrieve7b6479d82015-10-07 14:24:222989 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
dskiba88634f4e2015-08-14 23:03:292990 results.extend(_CheckAndroidToastUsage(input_api, output_api))
Yoland Yanb92fa522017-08-28 17:37:062991 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
2992 results.extend(_CheckAndroidTestJUnitFrameworkImport(input_api, output_api))
yolandyan45001472016-12-21 21:12:422993 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
Nate Fischer535972b2017-09-16 01:06:182994 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:222995 return results
2996
2997
[email protected]22c9bd72011-03-27 16:47:392998def _CommonChecks(input_api, output_api):
2999 """Checks common to both upload and commit."""
3000 results = []
3001 results.extend(input_api.canned_checks.PanProjectChecks(
[email protected]3de922f2013-12-20 13:27:383002 input_api, output_api,
qyearsleyfa2cfcf82016-12-15 18:03:543003 excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:083004
3005 author = input_api.change.author_email
3006 if author and author not in _KNOWN_ROBOTS:
3007 results.extend(
3008 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
3009
[email protected]55459852011-08-10 15:17:193010 results.extend(
[email protected]760deea2013-12-10 19:33:493011 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
Vaclav Brozek7dbc28c2018-03-27 08:35:233012 results.extend(
3013 _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:543014 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:183015 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
Dominic Battre033531052018-09-24 15:45:343016 results.extend(_CheckNoDISABLETypoInTests(input_api, output_api))
danakj61c1aa22015-10-26 19:55:523017 results.extend(_CheckDCHECK_IS_ONHasBraces(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:223018 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:443019 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:593020 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:063021 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:123022 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:183023 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:223024 results.extend(_CheckFilePermissions(input_api, output_api))
robertocn832f5992017-01-04 19:01:303025 results.extend(_CheckTeamTags(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:493026 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:033027 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:493028 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]06e6d0ff2012-12-11 01:36:443029 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
[email protected]d2530012013-01-25 16:39:273030 results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
Kent Tamura5a8755d2017-06-29 23:37:073031 results.extend(_CheckBuildConfigMacrosWithoutInclude(input_api, output_api))
[email protected]b00342e7f2013-03-26 16:21:543032 results.extend(_CheckForInvalidOSMacros(input_api, output_api))
lliabraa35bab3932014-10-01 12:16:443033 results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
yolandyandaabc6d2016-04-18 18:29:393034 results.extend(_CheckFlakyTestUsage(input_api, output_api))
[email protected]e871964c2013-05-13 14:14:553035 results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
[email protected]9f919cc2013-07-31 03:04:043036 results.extend(
3037 input_api.canned_checks.CheckChangeHasNoTabs(
3038 input_api,
3039 output_api,
3040 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
[email protected]85218562013-11-22 07:41:403041 results.extend(_CheckSpamLogging(input_api, output_api))
[email protected]49aa76a2013-12-04 06:59:163042 results.extend(_CheckForAnonymousVariables(input_api, output_api))
[email protected]999261d2014-03-03 20:08:083043 results.extend(_CheckUserActionUpdate(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243044 results.extend(_CheckNoDeprecatedCss(input_api, output_api))
3045 results.extend(_CheckNoDeprecatedJs(input_api, output_api))
[email protected]99171a92014-06-03 08:44:473046 results.extend(_CheckParseErrors(input_api, output_api))
mlamouria82272622014-09-16 18:45:043047 results.extend(_CheckForIPCRules(input_api, output_api))
Stephen Martinis97a394142018-06-07 23:06:053048 results.extend(_CheckForLongPathnames(input_api, output_api))
Daniel Bratell8ba52722018-03-02 16:06:143049 results.extend(_CheckForIncludeGuards(input_api, output_api))
mostynbb639aca52015-01-07 20:31:233050 results.extend(_CheckForWindowsLineEndings(input_api, output_api))
glidere61efad2015-02-18 17:39:433051 results.extend(_CheckSingletonInHeaders(input_api, output_api))
agrievef32bcc72016-04-04 14:57:403052 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api))
wnwenbdc444e2016-05-25 13:44:153053 results.extend(_CheckJavaStyle(input_api, output_api))
dchenge07de812016-06-20 19:27:173054 results.extend(_CheckIpcOwners(input_api, output_api))
jbriance9e12f162016-11-25 07:57:503055 results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243056 results.extend(_CheckForRiskyJsFeatures(input_api, output_api))
rlanday6802cf632017-05-30 17:48:363057 results.extend(_CheckForRelativeIncludes(input_api, output_api))
Takeshi Yoshinoe387aa32017-08-02 13:16:133058 results.extend(_CheckWATCHLISTS(input_api, output_api))
Sergiy Byelozyorov366b6482017-11-06 18:20:433059 results.extend(input_api.RunTests(
3060 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143061 results.extend(_CheckTranslationScreenshots(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:243062
Vaclav Brozekcdc7defb2018-03-20 09:54:353063 for f in input_api.AffectedFiles():
3064 path, name = input_api.os_path.split(f.LocalPath())
3065 if name == 'PRESUBMIT.py':
3066 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
Caleb Rouleaua6117be2018-05-11 20:10:003067 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
3068 if f.Action() != 'D' and input_api.os_path.exists(test_file):
Dirk Pranke38557312018-04-18 00:53:073069 # The PRESUBMIT.py file (and the directory containing it) might
3070 # have been affected by being moved or removed, so only try to
3071 # run the tests if they still exist.
3072 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
3073 input_api, output_api, full_path,
3074 whitelist=[r'^PRESUBMIT_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:393075 return results
[email protected]1f7b4172010-01-28 01:17:343076
[email protected]b337cb5b2011-01-23 21:24:053077
[email protected]b8079ae4a2012-12-05 19:56:493078def _CheckPatchFiles(input_api, output_api):
3079 problems = [f.LocalPath() for f in input_api.AffectedFiles()
3080 if f.LocalPath().endswith(('.orig', '.rej'))]
3081 if problems:
3082 return [output_api.PresubmitError(
3083 "Don't commit .rej and .orig files.", problems)]
[email protected]2fdd1f362013-01-16 03:56:033084 else:
3085 return []
[email protected]b8079ae4a2012-12-05 19:56:493086
3087
Kent Tamura5a8755d2017-06-29 23:37:073088def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Kent Tamura79ef8f82017-07-18 00:00:213089 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
3090 macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
3091 'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
Kent Tamura5a8755d2017-06-29 23:37:073092 include_re = input_api.re.compile(
3093 r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
3094 extension_re = input_api.re.compile(r'\.[a-z]+$')
3095 errors = []
3096 for f in input_api.AffectedFiles():
3097 if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
3098 continue
3099 found_line_number = None
3100 found_macro = None
3101 for line_num, line in f.ChangedContents():
3102 match = macro_re.search(line)
3103 if match:
3104 found_line_number = line_num
3105 found_macro = match.group(2)
3106 break
3107 if not found_line_number:
3108 continue
3109
3110 found_include = False
3111 for line in f.NewContents():
3112 if include_re.search(line):
3113 found_include = True
3114 break
3115 if found_include:
3116 continue
3117
3118 if not f.LocalPath().endswith('.h'):
3119 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
3120 try:
3121 content = input_api.ReadFile(primary_header_path, 'r')
3122 if include_re.search(content):
3123 continue
3124 except IOError:
3125 pass
3126 errors.append('%s:%d %s macro is used without including build/'
3127 'build_config.h.'
3128 % (f.LocalPath(), found_line_number, found_macro))
3129 if errors:
3130 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
3131 return []
3132
3133
[email protected]b00342e7f2013-03-26 16:21:543134def _DidYouMeanOSMacro(bad_macro):
3135 try:
3136 return {'A': 'OS_ANDROID',
3137 'B': 'OS_BSD',
3138 'C': 'OS_CHROMEOS',
3139 'F': 'OS_FREEBSD',
3140 'L': 'OS_LINUX',
3141 'M': 'OS_MACOSX',
3142 'N': 'OS_NACL',
3143 'O': 'OS_OPENBSD',
3144 'P': 'OS_POSIX',
3145 'S': 'OS_SOLARIS',
3146 'W': 'OS_WIN'}[bad_macro[3].upper()]
3147 except KeyError:
3148 return ''
3149
3150
3151def _CheckForInvalidOSMacrosInFile(input_api, f):
3152 """Check for sensible looking, totally invalid OS macros."""
3153 preprocessor_statement = input_api.re.compile(r'^\s*#')
3154 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
3155 results = []
3156 for lnum, line in f.ChangedContents():
3157 if preprocessor_statement.search(line):
3158 for match in os_macro.finditer(line):
3159 if not match.group(1) in _VALID_OS_MACROS:
3160 good = _DidYouMeanOSMacro(match.group(1))
3161 did_you_mean = ' (did you mean %s?)' % good if good else ''
3162 results.append(' %s:%d %s%s' % (f.LocalPath(),
3163 lnum,
3164 match.group(1),
3165 did_you_mean))
3166 return results
3167
3168
3169def _CheckForInvalidOSMacros(input_api, output_api):
3170 """Check all affected files for invalid OS macros."""
3171 bad_macros = []
3172 for f in input_api.AffectedFiles():
ellyjones47654342016-05-06 15:50:473173 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
[email protected]b00342e7f2013-03-26 16:21:543174 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
3175
3176 if not bad_macros:
3177 return []
3178
3179 return [output_api.PresubmitError(
3180 'Possibly invalid OS macro[s] found. Please fix your code\n'
3181 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
3182
lliabraa35bab3932014-10-01 12:16:443183
3184def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
3185 """Check all affected files for invalid "if defined" macros."""
3186 ALWAYS_DEFINED_MACROS = (
3187 "TARGET_CPU_PPC",
3188 "TARGET_CPU_PPC64",
3189 "TARGET_CPU_68K",
3190 "TARGET_CPU_X86",
3191 "TARGET_CPU_ARM",
3192 "TARGET_CPU_MIPS",
3193 "TARGET_CPU_SPARC",
3194 "TARGET_CPU_ALPHA",
3195 "TARGET_IPHONE_SIMULATOR",
3196 "TARGET_OS_EMBEDDED",
3197 "TARGET_OS_IPHONE",
3198 "TARGET_OS_MAC",
3199 "TARGET_OS_UNIX",
3200 "TARGET_OS_WIN32",
3201 )
3202 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
3203 results = []
3204 for lnum, line in f.ChangedContents():
3205 for match in ifdef_macro.finditer(line):
3206 if match.group(1) in ALWAYS_DEFINED_MACROS:
3207 always_defined = ' %s is always defined. ' % match.group(1)
3208 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
3209 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
3210 lnum,
3211 always_defined,
3212 did_you_mean))
3213 return results
3214
3215
3216def _CheckForInvalidIfDefinedMacros(input_api, output_api):
3217 """Check all affected files for invalid "if defined" macros."""
3218 bad_macros = []
3219 for f in input_api.AffectedFiles():
sdefresne4e1eccb32017-05-24 08:45:213220 if f.LocalPath().startswith('third_party/sqlite/'):
3221 continue
lliabraa35bab3932014-10-01 12:16:443222 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
3223 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
3224
3225 if not bad_macros:
3226 return []
3227
3228 return [output_api.PresubmitError(
3229 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
3230 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
3231 bad_macros)]
3232
3233
mlamouria82272622014-09-16 18:45:043234def _CheckForIPCRules(input_api, output_api):
3235 """Check for same IPC rules described in
3236 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
3237 """
3238 base_pattern = r'IPC_ENUM_TRAITS\('
3239 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
3240 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
3241
3242 problems = []
3243 for f in input_api.AffectedSourceFiles(None):
3244 local_path = f.LocalPath()
3245 if not local_path.endswith('.h'):
3246 continue
3247 for line_number, line in f.ChangedContents():
3248 if inclusion_pattern.search(line) and not comment_pattern.search(line):
3249 problems.append(
3250 '%s:%d\n %s' % (local_path, line_number, line.strip()))
3251
3252 if problems:
3253 return [output_api.PresubmitPromptWarning(
3254 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
3255 else:
3256 return []
3257
[email protected]b00342e7f2013-03-26 16:21:543258
Stephen Martinis97a394142018-06-07 23:06:053259def _CheckForLongPathnames(input_api, output_api):
3260 """Check to make sure no files being submitted have long paths.
3261 This causes issues on Windows.
3262 """
3263 problems = []
3264 for f in input_api.AffectedSourceFiles(None):
3265 local_path = f.LocalPath()
3266 # Windows has a path limit of 260 characters. Limit path length to 200 so
3267 # that we have some extra for the prefix on dev machines and the bots.
3268 if len(local_path) > 200:
3269 problems.append(local_path)
3270
3271 if problems:
3272 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
3273 else:
3274 return []
3275
3276
Daniel Bratell8ba52722018-03-02 16:06:143277def _CheckForIncludeGuards(input_api, output_api):
3278 """Check that header files have proper guards against multiple inclusion.
3279 If a file should not have such guards (and it probably should) then it
3280 should include the string "no-include-guard-because-multiply-included".
3281 """
Daniel Bratell6a75baef62018-06-04 10:04:453282 def is_chromium_header_file(f):
3283 # We only check header files under the control of the Chromium
3284 # project. That is, those outside third_party apart from
3285 # third_party/blink.
3286 file_with_path = input_api.os_path.normpath(f.LocalPath())
3287 return (file_with_path.endswith('.h') and
3288 (not file_with_path.startswith('third_party') or
3289 file_with_path.startswith(
3290 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:143291
3292 def replace_special_with_underscore(string):
Olivier Robinbba137492018-07-30 11:31:343293 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:143294
3295 errors = []
3296
Daniel Bratell6a75baef62018-06-04 10:04:453297 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
Daniel Bratell8ba52722018-03-02 16:06:143298 guard_name = None
3299 guard_line_number = None
3300 seen_guard_end = False
3301
3302 file_with_path = input_api.os_path.normpath(f.LocalPath())
3303 base_file_name = input_api.os_path.splitext(
3304 input_api.os_path.basename(file_with_path))[0]
3305 upper_base_file_name = base_file_name.upper()
3306
3307 expected_guard = replace_special_with_underscore(
3308 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:143309
3310 # For "path/elem/file_name.h" we should really only accept
Daniel Bratell39b5b062018-05-16 18:09:573311 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
3312 # are too many (1000+) files with slight deviations from the
3313 # coding style. The most important part is that the include guard
3314 # is there, and that it's unique, not the name so this check is
3315 # forgiving for existing files.
Daniel Bratell8ba52722018-03-02 16:06:143316 #
3317 # As code becomes more uniform, this could be made stricter.
3318
3319 guard_name_pattern_list = [
3320 # Anything with the right suffix (maybe with an extra _).
3321 r'\w+_H__?',
3322
Daniel Bratell39b5b062018-05-16 18:09:573323 # To cover include guards with old Blink style.
Daniel Bratell8ba52722018-03-02 16:06:143324 r'\w+_h',
3325
3326 # Anything including the uppercase name of the file.
3327 r'\w*' + input_api.re.escape(replace_special_with_underscore(
3328 upper_base_file_name)) + r'\w*',
3329 ]
3330 guard_name_pattern = '|'.join(guard_name_pattern_list)
3331 guard_pattern = input_api.re.compile(
3332 r'#ifndef\s+(' + guard_name_pattern + ')')
3333
3334 for line_number, line in enumerate(f.NewContents()):
3335 if 'no-include-guard-because-multiply-included' in line:
3336 guard_name = 'DUMMY' # To not trigger check outside the loop.
3337 break
3338
3339 if guard_name is None:
3340 match = guard_pattern.match(line)
3341 if match:
3342 guard_name = match.group(1)
3343 guard_line_number = line_number
3344
Daniel Bratell39b5b062018-05-16 18:09:573345 # We allow existing files to use include guards whose names
Daniel Bratell6a75baef62018-06-04 10:04:453346 # don't match the chromium style guide, but new files should
3347 # get it right.
3348 if not f.OldContents():
Daniel Bratell39b5b062018-05-16 18:09:573349 if guard_name != expected_guard:
Daniel Bratell8ba52722018-03-02 16:06:143350 errors.append(output_api.PresubmitPromptWarning(
3351 'Header using the wrong include guard name %s' % guard_name,
3352 ['%s:%d' % (f.LocalPath(), line_number + 1)],
Daniel Bratell39b5b062018-05-16 18:09:573353 'Expected: %r\nFound: %r' % (expected_guard, guard_name)))
Daniel Bratell8ba52722018-03-02 16:06:143354 else:
3355 # The line after #ifndef should have a #define of the same name.
3356 if line_number == guard_line_number + 1:
3357 expected_line = '#define %s' % guard_name
3358 if line != expected_line:
3359 errors.append(output_api.PresubmitPromptWarning(
3360 'Missing "%s" for include guard' % expected_line,
3361 ['%s:%d' % (f.LocalPath(), line_number + 1)],
3362 'Expected: %r\nGot: %r' % (expected_line, line)))
3363
3364 if not seen_guard_end and line == '#endif // %s' % guard_name:
3365 seen_guard_end = True
3366 elif seen_guard_end:
3367 if line.strip() != '':
3368 errors.append(output_api.PresubmitPromptWarning(
3369 'Include guard %s not covering the whole file' % (
3370 guard_name), [f.LocalPath()]))
3371 break # Nothing else to check and enough to warn once.
3372
3373 if guard_name is None:
3374 errors.append(output_api.PresubmitPromptWarning(
3375 'Missing include guard %s' % expected_guard,
3376 [f.LocalPath()],
3377 'Missing include guard in %s\n'
3378 'Recommended name: %s\n'
3379 'This check can be disabled by having the string\n'
3380 'no-include-guard-because-multiply-included in the header.' %
3381 (f.LocalPath(), expected_guard)))
3382
3383 return errors
3384
3385
mostynbb639aca52015-01-07 20:31:233386def _CheckForWindowsLineEndings(input_api, output_api):
3387 """Check source code and known ascii text files for Windows style line
3388 endings.
3389 """
earthdok1b5e0ee2015-03-10 15:19:103390 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate)$'
mostynbb639aca52015-01-07 20:31:233391
3392 file_inclusion_pattern = (
3393 known_text_files,
3394 r'.+%s' % _IMPLEMENTATION_EXTENSIONS
3395 )
3396
mostynbb639aca52015-01-07 20:31:233397 problems = []
Andrew Grieve933d12e2017-10-30 20:22:533398 source_file_filter = lambda f: input_api.FilterSourceFile(
3399 f, white_list=file_inclusion_pattern, black_list=None)
3400 for f in input_api.AffectedSourceFiles(source_file_filter):
Vaclav Brozekd5de76a2018-03-17 07:57:503401 include_file = False
3402 for _, line in f.ChangedContents():
mostynbb639aca52015-01-07 20:31:233403 if line.endswith('\r\n'):
Vaclav Brozekd5de76a2018-03-17 07:57:503404 include_file = True
3405 if include_file:
3406 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:233407
3408 if problems:
3409 return [output_api.PresubmitPromptWarning('Are you sure that you want '
3410 'these files to contain Windows style line endings?\n' +
3411 '\n'.join(problems))]
3412
3413 return []
3414
3415
Vaclav Brozekd5de76a2018-03-17 07:57:503416def _CheckSyslogUseWarning(input_api, output_api, source_file_filter=None):
pastarmovj89f7ee12016-09-20 14:58:133417 """Checks that all source files use SYSLOG properly."""
3418 syslog_files = []
3419 for f in input_api.AffectedSourceFiles(source_file_filter):
pastarmovj032ba5bc2017-01-12 10:41:563420 for line_number, line in f.ChangedContents():
3421 if 'SYSLOG' in line:
3422 syslog_files.append(f.LocalPath() + ':' + str(line_number))
3423
pastarmovj89f7ee12016-09-20 14:58:133424 if syslog_files:
3425 return [output_api.PresubmitPromptWarning(
3426 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
3427 ' calls.\nFiles to check:\n', items=syslog_files)]
3428 return []
3429
3430
[email protected]1f7b4172010-01-28 01:17:343431def CheckChangeOnUpload(input_api, output_api):
3432 results = []
3433 results.extend(_CommonChecks(input_api, output_api))
tandriief664692014-09-23 14:51:473434 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
scottmg39b29952014-12-08 18:31:283435 results.extend(
jam93a6ee792017-02-08 23:59:223436 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
mcasasb7440c282015-02-04 14:52:193437 results.extend(_CheckUmaHistogramChanges(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:223438 results.extend(_AndroidSpecificOnUploadChecks(input_api, output_api))
pastarmovj89f7ee12016-09-20 14:58:133439 results.extend(_CheckSyslogUseWarning(input_api, output_api))
estadee17314a02017-01-12 16:22:163440 results.extend(_CheckGoogleSupportAnswerUrl(input_api, output_api))
Vaclav Brozekea41ab22018-04-06 13:21:533441 results.extend(_CheckUniquePtr(input_api, output_api))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193442 results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543443 return results
[email protected]ca8d1982009-02-19 16:33:123444
3445
[email protected]1bfb8322014-04-23 01:02:413446def GetTryServerMasterForBot(bot):
3447 """Returns the Try Server master for the given bot.
3448
[email protected]0bb112362014-07-26 04:38:323449 It tries to guess the master from the bot name, but may still fail
3450 and return None. There is no longer a default master.
3451 """
3452 # Potentially ambiguous bot names are listed explicitly.
3453 master_map = {
tandriie5587792016-07-14 00:34:503454 'chromium_presubmit': 'master.tryserver.chromium.linux',
3455 'tools_build_presubmit': 'master.tryserver.chromium.linux',
[email protected]1bfb8322014-04-23 01:02:413456 }
[email protected]0bb112362014-07-26 04:38:323457 master = master_map.get(bot)
3458 if not master:
wnwen4fbaab82016-05-25 12:54:363459 if 'android' in bot:
tandriie5587792016-07-14 00:34:503460 master = 'master.tryserver.chromium.android'
wnwen4fbaab82016-05-25 12:54:363461 elif 'linux' in bot or 'presubmit' in bot:
tandriie5587792016-07-14 00:34:503462 master = 'master.tryserver.chromium.linux'
[email protected]0bb112362014-07-26 04:38:323463 elif 'win' in bot:
tandriie5587792016-07-14 00:34:503464 master = 'master.tryserver.chromium.win'
[email protected]0bb112362014-07-26 04:38:323465 elif 'mac' in bot or 'ios' in bot:
tandriie5587792016-07-14 00:34:503466 master = 'master.tryserver.chromium.mac'
[email protected]0bb112362014-07-26 04:38:323467 return master
[email protected]1bfb8322014-04-23 01:02:413468
3469
[email protected]ca8d1982009-02-19 16:33:123470def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:543471 results = []
[email protected]1f7b4172010-01-28 01:17:343472 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543473 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:273474 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:343475 input_api,
3476 output_api,
[email protected]2fdd1f362013-01-16 03:56:033477 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:273478
jam93a6ee792017-02-08 23:59:223479 results.extend(
3480 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]3e4eb112011-01-18 03:29:543481 results.extend(input_api.canned_checks.CheckChangeHasBugField(
3482 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:413483 results.extend(input_api.canned_checks.CheckChangeHasDescription(
3484 input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543485 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143486
3487
3488def _CheckTranslationScreenshots(input_api, output_api):
3489 PART_FILE_TAG = "part"
3490 import os
3491 import sys
3492 from io import StringIO
3493
3494 try:
3495 old_sys_path = sys.path
3496 sys.path = sys.path + [input_api.os_path.join(
3497 input_api.PresubmitLocalPath(), 'tools', 'grit')]
3498 import grit.grd_reader
3499 import grit.node.message
3500 import grit.util
3501 finally:
3502 sys.path = old_sys_path
3503
3504 def _GetGrdMessages(grd_path_or_string, dir_path='.'):
3505 """Load the grd file and return a dict of message ids to messages.
3506
3507 Ignores any nested grdp files pointed by <part> tag.
3508 """
3509 doc = grit.grd_reader.Parse(grd_path_or_string, dir_path,
3510 stop_after=None, first_ids_file=None,
3511 debug=False, defines=None,
3512 tags_to_ignore=set([PART_FILE_TAG]))
3513 return {
3514 msg.attrs['name']:msg for msg in doc.GetChildrenOfType(
3515 grit.node.message.MessageNode)
3516 }
3517
3518 def _GetGrdpMessagesFromString(grdp_string):
3519 """Parses the contents of a grdp file given in grdp_string.
3520
3521 grd_reader can't parse grdp files directly. Instead, this creates a
3522 temporary directory with a grd file pointing to the grdp file, and loads the
3523 grd from there. Any nested grdp files (pointed by <part> tag) are ignored.
3524 """
3525 WRAPPER = """<?xml version="1.0" encoding="utf-8"?>
3526 <grit latest_public_release="1" current_release="1">
3527 <release seq="1">
3528 <messages>
3529 <part file="sub.grdp" />
3530 </messages>
3531 </release>
3532 </grit>
3533 """
3534 with grit.util.TempDir({'main.grd': WRAPPER,
3535 'sub.grdp': grdp_string}) as temp_dir:
3536 return _GetGrdMessages(temp_dir.GetPath('main.grd'), temp_dir.GetPath())
3537
3538 new_or_added_paths = set(f.LocalPath()
3539 for f in input_api.AffectedFiles()
3540 if (f.Action() == 'A' or f.Action() == 'M'))
3541 removed_paths = set(f.LocalPath()
3542 for f in input_api.AffectedFiles(include_deletes=True)
3543 if f.Action() == 'D')
3544
3545 affected_grds = [f for f in input_api.AffectedFiles()
3546 if (f.LocalPath().endswith('.grd') or
3547 f.LocalPath().endswith('.grdp'))]
3548 affected_png_paths = [f.AbsoluteLocalPath()
3549 for f in input_api.AffectedFiles()
3550 if (f.LocalPath().endswith('.png'))]
3551
3552 # Check for screenshots. Developers can upload screenshots using
3553 # tools/translation/upload_screenshots.py which finds and uploads
3554 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
3555 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
3556 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
3557 #
3558 # The logic here is as follows:
3559 #
3560 # - If the CL has a .png file under the screenshots directory for a grd
3561 # file, warn the developer. Actual images should never be checked into the
3562 # Chrome repo.
3563 #
3564 # - If the CL contains modified or new messages in grd files and doesn't
3565 # contain the corresponding .sha1 files, warn the developer to add images
3566 # and upload them via tools/translation/upload_screenshots.py.
3567 #
3568 # - If the CL contains modified or new messages in grd files and the
3569 # corresponding .sha1 files, everything looks good.
3570 #
3571 # - If the CL contains removed messages in grd files but the corresponding
3572 # .sha1 files aren't removed, warn the developer to remove them.
3573 unnecessary_screenshots = []
3574 missing_sha1 = []
3575 unnecessary_sha1_files = []
3576
3577
3578 def _CheckScreenshotAdded(screenshots_dir, message_id):
3579 sha1_path = input_api.os_path.join(
3580 screenshots_dir, message_id + '.png.sha1')
3581 if sha1_path not in new_or_added_paths:
3582 missing_sha1.append(sha1_path)
3583
3584
3585 def _CheckScreenshotRemoved(screenshots_dir, message_id):
3586 sha1_path = input_api.os_path.join(
3587 screenshots_dir, message_id + '.png.sha1')
3588 if sha1_path not in removed_paths:
3589 unnecessary_sha1_files.append(sha1_path)
3590
3591
3592 for f in affected_grds:
3593 file_path = f.LocalPath()
3594 old_id_to_msg_map = {}
3595 new_id_to_msg_map = {}
3596 if file_path.endswith('.grdp'):
3597 if f.OldContents():
3598 old_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393599 unicode('\n'.join(f.OldContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143600 if f.NewContents():
3601 new_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393602 unicode('\n'.join(f.NewContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143603 else:
3604 if f.OldContents():
3605 old_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393606 StringIO(unicode('\n'.join(f.OldContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143607 if f.NewContents():
3608 new_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393609 StringIO(unicode('\n'.join(f.NewContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143610
3611 # Compute added, removed and modified message IDs.
3612 old_ids = set(old_id_to_msg_map)
3613 new_ids = set(new_id_to_msg_map)
3614 added_ids = new_ids - old_ids
3615 removed_ids = old_ids - new_ids
3616 modified_ids = set([])
3617 for key in old_ids.intersection(new_ids):
3618 if (old_id_to_msg_map[key].FormatXml()
3619 != new_id_to_msg_map[key].FormatXml()):
3620 modified_ids.add(key)
3621
3622 grd_name, ext = input_api.os_path.splitext(
3623 input_api.os_path.basename(file_path))
3624 screenshots_dir = input_api.os_path.join(
3625 input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
3626
3627 # Check the screenshot directory for .png files. Warn if there is any.
3628 for png_path in affected_png_paths:
3629 if png_path.startswith(screenshots_dir):
3630 unnecessary_screenshots.append(png_path)
3631
3632 for added_id in added_ids:
3633 _CheckScreenshotAdded(screenshots_dir, added_id)
3634
3635 for modified_id in modified_ids:
3636 _CheckScreenshotAdded(screenshots_dir, modified_id)
3637
3638 for removed_id in removed_ids:
3639 _CheckScreenshotRemoved(screenshots_dir, removed_id)
3640
3641 results = []
3642 if unnecessary_screenshots:
3643 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393644 'Do not include actual screenshots in the changelist. Run '
3645 'tools/translate/upload_screenshots.py to upload them instead:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143646 sorted(unnecessary_screenshots)))
3647
3648 if missing_sha1:
3649 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393650 'You are adding or modifying UI strings.\n'
3651 'To ensure the best translations, take screenshots of the relevant UI '
3652 '(https://g.co/chrome/translation) and add these files to your '
3653 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143654
3655 if unnecessary_sha1_files:
3656 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393657 'You removed strings associated with these files. Remove:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143658 sorted(unnecessary_sha1_files)))
3659
3660 return results