blob: 963588242384db2ca1aa7e55fe67f97d105100c1 [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 = (
Egor Paskoce145c42018-09-28 19:31:0413 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_rules.py",
14 r"^native_client_sdk[\\/]src[\\/]build_tools[\\/]make_simple.py",
15 r"^native_client_sdk[\\/]src[\\/]tools[\\/].*.mk",
16 r"^net[\\/]tools[\\/]spdyshark[\\/].*",
17 r"^skia[\\/].*",
18 r"^third_party[\\/](WebKit|blink)[\\/].*",
19 r"^third_party[\\/]breakpad[\\/].*",
20 r"^v8[\\/].*",
[email protected]3e4eb112011-01-18 03:29:5421 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5322 r".+_autogen\.h$",
Egor Paskoce145c42018-09-28 19:31:0423 r".+[\\/]pnacl_shim\.c$",
24 r"^gpu[\\/]config[\\/].*_list_json\.cc$",
25 r"^chrome[\\/]browser[\\/]resources[\\/]pdf[\\/]index.js",
26 r"tools[\\/]md_browser[\\/].*\.css$",
Kenneth Russell077c8d92017-12-16 02:52:1427 # Test pages for Maps telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0428 r"tools[\\/]perf[\\/]page_sets[\\/]maps_perf_test.*",
ehmaldonado78eee2ed2017-03-28 13:16:5429 # Test pages for WebRTC telemetry tests.
Egor Paskoce145c42018-09-28 19:31:0430 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 = (
Egor Paskoce145c42018-09-28 19:31:0447 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,
Egor Paskoce145c42018-09-28 19:31:0452 r'.*[\\/](test|tool(s)?)[\\/].*',
[email protected]ef070cc2013-05-03 11:53:0553 # content_shell is used for running layout tests.
Egor Paskoce145c42018-09-28 19:31:0454 r'content[\\/]shell[\\/].*',
[email protected]7b054982013-11-27 00:44:4755 # Non-production example code.
Egor Paskoce145c42018-09-28 19:31:0456 r'mojo[\\/]examples[\\/].*',
[email protected]8176de12014-06-20 19:07:0857 # Launcher for running iOS tests on the simulator.
Egor Paskoce145c42018-09-28 19:31:0458 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 (
Egor Paskoce145c42018-09-28 19:31:04241 r"^ui[\\/]gl[\\/].*\.cc$",
242 r"^media[\\/]gpu[\\/].*\.cc$",
243 r"^gpu[\\/].*\.cc$",
thomasanderson4b569052016-09-14 20:15:53244 ),
245 ),
246 (
thomasandersone043e3ce2017-06-08 00:43:20247 r'XInternAtom|xcb_intern_atom',
248 (
thomasanderson11aa41dc2017-06-08 22:22:38249 'Use gfx::GetAtom() instead of interning atoms directly.',
thomasandersone043e3ce2017-06-08 00:43:20250 ),
251 True,
252 (
Egor Paskoce145c42018-09-28 19:31:04253 r"^gpu[\\/]ipc[\\/]service[\\/]gpu_watchdog_thread\.cc$",
254 r"^remoting[\\/]host[\\/]linux[\\/]x_server_clipboard\.cc$",
255 r"^ui[\\/]gfx[\\/]x[\\/]x11_atom_cache\.cc$",
thomasandersone043e3ce2017-06-08 00:43:20256 ),
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.
Egor Paskoce145c42018-09-28 19:31:04324 r'^base[\\/]posix[\\/]eintr_wrapper\.h$',
325 r'^ppapi[\\/]tests[\\/]test_broker\.cc$',
[email protected]d89eec82013-12-03 14:10:59326 ),
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 (
Egor Paskoce145c42018-09-28 19:31:04336 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 (
Egor Paskoce145c42018-09-28 19:31:04346 r'^third_party[\\/]abseil-cpp[\\/].*',
Mirko Bonadeif4f0f0e2018-04-12 09:29:41347 ),
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 (
Egor Paskoce145c42018-09-28 19:31:04389 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)$',
dbeamb6f4fde2017-06-15 04:03:06392 ),
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 (
Egor Paskoce145c42018-09-28 19:31:04560 r'^ios[\\/].*\.(cc|h)$',
561 r'.*[\\/]ios[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37562 r'.*_ios\.(cc|h)$',
Egor Paskoce145c42018-09-28 19:31:04563 r'^net[\\/].*\.(cc|h)$',
564 r'.*[\\/]tools[\\/].*\.(cc|h)$',
Matt Menke7f520a82018-03-28 21:38:37565 ),
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 = [
Egor Paskoce145c42018-09-28 19:31:04600 r".*[\\/]BuildHooksAndroidImpl\.java",
601 r".*[\\/]LicenseContentProvider\.java",
602 r".*[\\/]PlatformServiceBridgeImpl.java",
Patrick Noland5475bc0d2018-10-01 20:04:28603 r".*chrome[\\\/]android[\\\/]feed[\\\/]dummy[\\\/].*\.java",
Shenghua Zhangbfaa38b82017-11-16 21:58:02604]
[email protected]127f18ec2012-06-16 05:05:59605
Sean Kau46e29bc2017-08-28 16:31:16606# These paths contain test data and other known invalid JSON files.
607_KNOWN_INVALID_JSON_FILE_PATTERNS = [
Egor Paskoce145c42018-09-28 19:31:04608 r'test[\\/]data[\\/]',
609 r'^components[\\/]policy[\\/]resources[\\/]policy_templates\.json$',
610 r'^third_party[\\/]protobuf[\\/]',
611 r'^third_party[\\/]WebKit[\\/]LayoutTests[\\/]external[\\/]wpt[\\/]',
612 r'^third_party[\\/]blink[\\/]renderer[\\/]devtools[\\/]protocol\.json$',
Sean Kau46e29bc2017-08-28 16:31:16613]
614
615
[email protected]b00342e7f2013-03-26 16:21:54616_VALID_OS_MACROS = (
617 # Please keep sorted.
rayb0088ee52017-04-26 22:35:08618 'OS_AIX',
[email protected]b00342e7f2013-03-26 16:21:54619 'OS_ANDROID',
Henrique Nakashimaafff0502018-01-24 17:14:12620 'OS_ASMJS',
[email protected]b00342e7f2013-03-26 16:21:54621 'OS_BSD',
622 'OS_CAT', # For testing.
623 'OS_CHROMEOS',
624 'OS_FREEBSD',
scottmg2f97ee122017-05-12 17:50:37625 'OS_FUCHSIA',
[email protected]b00342e7f2013-03-26 16:21:54626 'OS_IOS',
627 'OS_LINUX',
628 'OS_MACOSX',
629 'OS_NACL',
hidehikof7295f22014-10-28 11:57:21630 'OS_NACL_NONSFI',
631 'OS_NACL_SFI',
krytarowski969759f2016-07-31 23:55:12632 'OS_NETBSD',
[email protected]b00342e7f2013-03-26 16:21:54633 'OS_OPENBSD',
634 'OS_POSIX',
[email protected]eda7afa12014-02-06 12:27:37635 'OS_QNX',
[email protected]b00342e7f2013-03-26 16:21:54636 'OS_SOLARIS',
[email protected]b00342e7f2013-03-26 16:21:54637 'OS_WIN',
638)
639
640
agrievef32bcc72016-04-04 14:57:40641_ANDROID_SPECIFIC_PYDEPS_FILES = [
Andrew Luob2e4b342018-09-20 19:32:39642 'android_webview/tools/run_cts.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36643 'base/android/jni_generator/jni_generator.pydeps',
644 'base/android/jni_generator/jni_registration_generator.pydeps',
645 'build/android/gyp/aar.pydeps',
646 'build/android/gyp/aidl.pydeps',
647 'build/android/gyp/apkbuilder.pydeps',
648 'build/android/gyp/app_bundle_to_apks.pydeps',
649 'build/android/gyp/bytecode_processor.pydeps',
650 'build/android/gyp/compile_resources.pydeps',
651 'build/android/gyp/create_bundle_wrapper_script.pydeps',
652 'build/android/gyp/copy_ex.pydeps',
653 'build/android/gyp/create_app_bundle.pydeps',
654 'build/android/gyp/create_apk_operations_script.pydeps',
655 'build/android/gyp/create_dist_jar.pydeps',
656 'build/android/gyp/create_java_binary_script.pydeps',
657 'build/android/gyp/create_stack_script.pydeps',
658 'build/android/gyp/create_test_runner_script.pydeps',
659 'build/android/gyp/create_tool_wrapper.pydeps',
660 'build/android/gyp/desugar.pydeps',
661 'build/android/gyp/dex.pydeps',
662 'build/android/gyp/dist_aar.pydeps',
663 'build/android/gyp/emma_instr.pydeps',
664 'build/android/gyp/filter_zip.pydeps',
665 'build/android/gyp/gcc_preprocess.pydeps',
666 'build/android/gyp/generate_proguarded_module_jar.pydeps',
667 'build/android/gyp/ijar.pydeps',
668 'build/android/gyp/java_cpp_enum.pydeps',
669 'build/android/gyp/javac.pydeps',
670 'build/android/gyp/jinja_template.pydeps',
671 'build/android/gyp/lint.pydeps',
672 'build/android/gyp/main_dex_list.pydeps',
673 'build/android/gyp/merge_jar_info_files.pydeps',
674 'build/android/gyp/merge_manifest.pydeps',
675 'build/android/gyp/prepare_resources.pydeps',
676 'build/android/gyp/proguard.pydeps',
677 'build/android/gyp/write_build_config.pydeps',
678 'build/android/gyp/write_ordered_libraries.pydeps',
679 'build/android/incremental_install/generate_android_manifest.pydeps',
680 'build/android/incremental_install/write_installer_json.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22681 'build/android/resource_sizes.pydeps',
agrievef32bcc72016-04-04 14:57:40682 'build/android/test_runner.pydeps',
hzl9b15df52017-03-23 23:43:04683 'build/android/test_wrapper/logdog_wrapper.pydeps',
David 'Digit' Turner0006f4732018-08-07 07:12:36684 'build/protoc_java.pydeps',
jbudorick276cc562017-04-29 01:34:58685 'build/secondary/third_party/android_platform/'
686 'development/scripts/stack.pydeps',
agrieve732db3a2016-04-26 19:18:19687 'net/tools/testserver/testserver.pydeps',
agrievef32bcc72016-04-04 14:57:40688]
689
wnwenbdc444e2016-05-25 13:44:15690
agrievef32bcc72016-04-04 14:57:40691_GENERIC_PYDEPS_FILES = [
John Chencde89192018-01-27 21:18:40692 'chrome/test/chromedriver/test/run_py_tests.pydeps',
Cole Winstanley7045a1b2018-08-27 23:37:29693 'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
Andrew Grievea7f1ee902018-05-18 16:17:22694 'tools/binary_size/supersize.pydeps',
agrievef32bcc72016-04-04 14:57:40695]
696
wnwenbdc444e2016-05-25 13:44:15697
agrievef32bcc72016-04-04 14:57:40698_ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES
699
700
Eric Boren6fd2b932018-01-25 15:05:08701# Bypass the AUTHORS check for these accounts.
702_KNOWN_ROBOTS = set(
Chan52654f52018-03-21 21:02:29703 '%s-chromium-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com' % s
704 for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools',
Eric Boren36af476a2018-06-08 16:21:08705 'fuchsia-sdk', 'nacl', 'pdfium', 'perfetto', 'skia',
Eric Boren57cc805b2018-08-20 17:28:32706 'spirv', 'src-internal', 'webrtc')
Sergiy Byelozyorov47158a52018-06-13 22:38:59707 ) | set('%[email protected]' % s for s in ('findit-for-me',)
Achuith Bhandarkar35905562018-07-25 19:28:45708 ) | set('%[email protected]' % s for s in ('3su6n15k.default',)
Sergiy Byelozyorov47158a52018-06-13 22:38:59709 ) | set('%[email protected]' % s
Eric Boren835d71f2018-09-07 21:09:04710 for s in ('v8-ci-autoroll-builder',)
711 ) | set('%[email protected]' % s
712 for s in ('chromium-autoroll',)
713 ) | set('%[email protected]' % s
Eric Boren2b7e3c3c2018-09-13 18:14:30714 for s in ('chromium-internal-autoroll',))
Eric Boren6fd2b932018-01-25 15:05:08715
716
[email protected]55459852011-08-10 15:17:19717def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
718 """Attempts to prevent use of functions intended only for testing in
719 non-testing code. For now this is just a best-effort implementation
720 that ignores header files and may have some false positives. A
721 better implementation would probably need a proper C++ parser.
722 """
723 # We only scan .cc files and the like, as the declaration of
724 # for-testing functions in header files are hard to distinguish from
725 # calls to such functions without a proper C++ parser.
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49726 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]55459852011-08-10 15:17:19727
jochenc0d4808c2015-07-27 09:25:42728 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
[email protected]55459852011-08-10 15:17:19729 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
[email protected]23501822014-05-14 02:06:09730 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
[email protected]55459852011-08-10 15:17:19731 exclusion_pattern = input_api.re.compile(
732 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
733 base_function_pattern, base_function_pattern))
734
735 def FilterFile(affected_file):
[email protected]06e6d0ff2012-12-11 01:36:44736 black_list = (_EXCLUDED_PATHS +
737 _TEST_CODE_EXCLUDED_PATHS +
738 input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19739 return input_api.FilterSourceFile(
740 affected_file,
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49741 white_list=file_inclusion_pattern,
[email protected]55459852011-08-10 15:17:19742 black_list=black_list)
743
744 problems = []
745 for f in input_api.AffectedSourceFiles(FilterFile):
746 local_path = f.LocalPath()
[email protected]825d27182014-01-02 21:24:24747 for line_number, line in f.ChangedContents():
[email protected]2fdd1f362013-01-16 03:56:03748 if (inclusion_pattern.search(line) and
[email protected]de4f7d22013-05-23 14:27:46749 not comment_pattern.search(line) and
[email protected]2fdd1f362013-01-16 03:56:03750 not exclusion_pattern.search(line)):
[email protected]55459852011-08-10 15:17:19751 problems.append(
[email protected]2fdd1f362013-01-16 03:56:03752 '%s:%d\n %s' % (local_path, line_number, line.strip()))
[email protected]55459852011-08-10 15:17:19753
754 if problems:
[email protected]f7051d52013-04-02 18:31:42755 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
[email protected]2fdd1f362013-01-16 03:56:03756 else:
757 return []
[email protected]55459852011-08-10 15:17:19758
759
Vaclav Brozek7dbc28c2018-03-27 08:35:23760def _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api):
761 """This is a simplified version of
762 _CheckNoProductionCodeUsingTestOnlyFunctions for Java files.
763 """
764 javadoc_start_re = input_api.re.compile(r'^\s*/\*\*')
765 javadoc_end_re = input_api.re.compile(r'^\s*\*/')
766 name_pattern = r'ForTest(s|ing)?'
767 # Describes an occurrence of "ForTest*" inside a // comment.
768 comment_re = input_api.re.compile(r'//.*%s' % name_pattern)
769 # Catch calls.
770 inclusion_re = input_api.re.compile(r'(%s)\s*\(' % name_pattern)
771 # Ignore definitions. (Comments are ignored separately.)
772 exclusion_re = input_api.re.compile(r'(%s)[^;]+\{' % name_pattern)
773
774 problems = []
775 sources = lambda x: input_api.FilterSourceFile(
776 x,
777 black_list=(('(?i).*test', r'.*\/junit\/')
778 + input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:49779 white_list=[r'.*\.java$']
Vaclav Brozek7dbc28c2018-03-27 08:35:23780 )
781 for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
782 local_path = f.LocalPath()
783 is_inside_javadoc = False
784 for line_number, line in f.ChangedContents():
785 if is_inside_javadoc and javadoc_end_re.search(line):
786 is_inside_javadoc = False
787 if not is_inside_javadoc and javadoc_start_re.search(line):
788 is_inside_javadoc = True
789 if is_inside_javadoc:
790 continue
791 if (inclusion_re.search(line) and
792 not comment_re.search(line) and
793 not exclusion_re.search(line)):
794 problems.append(
795 '%s:%d\n %s' % (local_path, line_number, line.strip()))
796
797 if problems:
798 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
799 else:
800 return []
801
802
[email protected]10689ca2011-09-02 02:31:54803def _CheckNoIOStreamInHeaders(input_api, output_api):
804 """Checks to make sure no .h files include <iostream>."""
805 files = []
806 pattern = input_api.re.compile(r'^#include\s*<iostream>',
807 input_api.re.MULTILINE)
808 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
809 if not f.LocalPath().endswith('.h'):
810 continue
811 contents = input_api.ReadFile(f)
812 if pattern.search(contents):
813 files.append(f)
814
815 if len(files):
yolandyandaabc6d2016-04-18 18:29:39816 return [output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06817 'Do not #include <iostream> in header files, since it inserts static '
818 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54819 '#include <ostream>. See http://crbug.com/94794',
820 files) ]
821 return []
822
Danil Chapovalov3518f362018-08-11 16:13:43823def _CheckNoStrCatRedefines(input_api, output_api):
824 """Checks no windows headers with StrCat redefined are included directly."""
825 files = []
826 pattern_deny = input_api.re.compile(
827 r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
828 input_api.re.MULTILINE)
829 pattern_allow = input_api.re.compile(
830 r'^#include\s"base/win/windows_defines.inc"',
831 input_api.re.MULTILINE)
832 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
833 contents = input_api.ReadFile(f)
834 if pattern_deny.search(contents) and not pattern_allow.search(contents):
835 files.append(f.LocalPath())
836
837 if len(files):
838 return [output_api.PresubmitError(
839 'Do not #include shlwapi.h, atlbase.h, propvarutil.h or sphelper.h '
840 'directly since they pollute code with StrCat macro. Instead, '
841 'include matching header from base/win. See http://crbug.com/856536',
842 files) ]
843 return []
844
[email protected]10689ca2011-09-02 02:31:54845
[email protected]72df4e782012-06-21 16:28:18846def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
danakj61c1aa22015-10-26 19:55:52847 """Checks to make sure no source files use UNIT_TEST."""
[email protected]72df4e782012-06-21 16:28:18848 problems = []
849 for f in input_api.AffectedFiles():
850 if (not f.LocalPath().endswith(('.cc', '.mm'))):
851 continue
852
853 for line_num, line in f.ChangedContents():
[email protected]549f86a2013-11-19 13:00:04854 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
[email protected]72df4e782012-06-21 16:28:18855 problems.append(' %s:%d' % (f.LocalPath(), line_num))
856
857 if not problems:
858 return []
859 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
860 '\n'.join(problems))]
861
Dominic Battre033531052018-09-24 15:45:34862def _CheckNoDISABLETypoInTests(input_api, output_api):
863 """Checks to prevent attempts to disable tests with DISABLE_ prefix.
864
865 This test warns if somebody tries to disable a test with the DISABLE_ prefix
866 instead of DISABLED_. To filter false positives, reports are only generated
867 if a corresponding MAYBE_ line exists.
868 """
869 problems = []
870
871 # The following two patterns are looked for in tandem - is a test labeled
872 # as MAYBE_ followed by a DISABLE_ (instead of the correct DISABLED)
873 maybe_pattern = input_api.re.compile(r'MAYBE_([a-zA-Z0-9_]+)')
874 disable_pattern = input_api.re.compile(r'DISABLE_([a-zA-Z0-9_]+)')
875
876 # This is for the case that a test is disabled on all platforms.
877 full_disable_pattern = input_api.re.compile(
878 r'^\s*TEST[^(]*\([a-zA-Z0-9_]+,\s*DISABLE_[a-zA-Z0-9_]+\)',
879 input_api.re.MULTILINE)
880
Katie Df13948e2018-09-25 07:33:44881 for f in input_api.AffectedFiles(False):
Dominic Battre033531052018-09-24 15:45:34882 if not 'test' in f.LocalPath() or not f.LocalPath().endswith('.cc'):
883 continue
884
885 # Search for MABYE_, DISABLE_ pairs.
886 disable_lines = {} # Maps of test name to line number.
887 maybe_lines = {}
888 for line_num, line in f.ChangedContents():
889 disable_match = disable_pattern.search(line)
890 if disable_match:
891 disable_lines[disable_match.group(1)] = line_num
892 maybe_match = maybe_pattern.search(line)
893 if maybe_match:
894 maybe_lines[maybe_match.group(1)] = line_num
895
896 # Search for DISABLE_ occurrences within a TEST() macro.
897 disable_tests = set(disable_lines.keys())
898 maybe_tests = set(maybe_lines.keys())
899 for test in disable_tests.intersection(maybe_tests):
900 problems.append(' %s:%d' % (f.LocalPath(), disable_lines[test]))
901
902 contents = input_api.ReadFile(f)
903 full_disable_match = full_disable_pattern.search(contents)
904 if full_disable_match:
905 problems.append(' %s' % f.LocalPath())
906
907 if not problems:
908 return []
909 return [
910 output_api.PresubmitPromptWarning(
911 'Attempt to disable a test with DISABLE_ instead of DISABLED_?\n' +
912 '\n'.join(problems))
913 ]
914
[email protected]72df4e782012-06-21 16:28:18915
danakj61c1aa22015-10-26 19:55:52916def _CheckDCHECK_IS_ONHasBraces(input_api, output_api):
kjellanderaee306632017-02-22 19:26:57917 """Checks to make sure DCHECK_IS_ON() does not skip the parentheses."""
danakj61c1aa22015-10-26 19:55:52918 errors = []
919 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))',
920 input_api.re.MULTILINE)
921 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
922 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))):
923 continue
924 for lnum, line in f.ChangedContents():
925 if input_api.re.search(pattern, line):
dchenge07de812016-06-20 19:27:17926 errors.append(output_api.PresubmitError(
927 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' +
kjellanderaee306632017-02-22 19:26:57928 'DCHECK_IS_ON()", not forgetting the parentheses.')
dchenge07de812016-06-20 19:27:17929 % (f.LocalPath(), lnum)))
danakj61c1aa22015-10-26 19:55:52930 return errors
931
932
mcasasb7440c282015-02-04 14:52:19933def _FindHistogramNameInLine(histogram_name, line):
934 """Tries to find a histogram name or prefix in a line."""
935 if not "affected-histogram" in line:
936 return histogram_name in line
937 # A histogram_suffixes tag type has an affected-histogram name as a prefix of
938 # the histogram_name.
939 if not '"' in line:
940 return False
941 histogram_prefix = line.split('\"')[1]
942 return histogram_prefix in histogram_name
943
944
945def _CheckUmaHistogramChanges(input_api, output_api):
946 """Check that UMA histogram names in touched lines can still be found in other
947 lines of the patch or in histograms.xml. Note that this check would not catch
948 the reverse: changes in histograms.xml not matched in the code itself."""
949 touched_histograms = []
950 histograms_xml_modifications = []
Vaclav Brozekbdac817c2018-03-24 06:30:47951 call_pattern_c = r'\bUMA_HISTOGRAM.*\('
952 call_pattern_java = r'\bRecordHistogram\.record[a-zA-Z]+Histogram\('
953 name_pattern = r'"(.*?)"'
954 single_line_c_re = input_api.re.compile(call_pattern_c + name_pattern)
955 single_line_java_re = input_api.re.compile(call_pattern_java + name_pattern)
956 split_line_c_prefix_re = input_api.re.compile(call_pattern_c)
957 split_line_java_prefix_re = input_api.re.compile(call_pattern_java)
958 split_line_suffix_re = input_api.re.compile(r'^\s*' + name_pattern)
Vaclav Brozek0e730cbd2018-03-24 06:18:17959 last_line_matched_prefix = False
mcasasb7440c282015-02-04 14:52:19960 for f in input_api.AffectedFiles():
961 # If histograms.xml itself is modified, keep the modified lines for later.
962 if f.LocalPath().endswith(('histograms.xml')):
963 histograms_xml_modifications = f.ChangedContents()
964 continue
Vaclav Brozekbdac817c2018-03-24 06:30:47965 if f.LocalPath().endswith(('cc', 'mm', 'cpp')):
966 single_line_re = single_line_c_re
967 split_line_prefix_re = split_line_c_prefix_re
968 elif f.LocalPath().endswith(('java')):
969 single_line_re = single_line_java_re
970 split_line_prefix_re = split_line_java_prefix_re
971 else:
mcasasb7440c282015-02-04 14:52:19972 continue
973 for line_num, line in f.ChangedContents():
Vaclav Brozek0e730cbd2018-03-24 06:18:17974 if last_line_matched_prefix:
975 suffix_found = split_line_suffix_re.search(line)
976 if suffix_found :
977 touched_histograms.append([suffix_found.group(1), f, line_num])
978 last_line_matched_prefix = False
979 continue
Vaclav Brozek8a8e2e202018-03-23 22:01:06980 found = single_line_re.search(line)
mcasasb7440c282015-02-04 14:52:19981 if found:
982 touched_histograms.append([found.group(1), f, line_num])
Vaclav Brozek0e730cbd2018-03-24 06:18:17983 continue
984 last_line_matched_prefix = split_line_prefix_re.search(line)
mcasasb7440c282015-02-04 14:52:19985
986 # Search for the touched histogram names in the local modifications to
987 # histograms.xml, and, if not found, on the base histograms.xml file.
988 unmatched_histograms = []
989 for histogram_info in touched_histograms:
990 histogram_name_found = False
991 for line_num, line in histograms_xml_modifications:
992 histogram_name_found = _FindHistogramNameInLine(histogram_info[0], line)
993 if histogram_name_found:
994 break
995 if not histogram_name_found:
996 unmatched_histograms.append(histogram_info)
997
eromanb90c82e7e32015-04-01 15:13:49998 histograms_xml_path = 'tools/metrics/histograms/histograms.xml'
mcasasb7440c282015-02-04 14:52:19999 problems = []
1000 if unmatched_histograms:
eromanb90c82e7e32015-04-01 15:13:491001 with open(histograms_xml_path) as histograms_xml:
mcasasb7440c282015-02-04 14:52:191002 for histogram_name, f, line_num in unmatched_histograms:
mcasas39c1b8b2015-02-25 15:33:451003 histograms_xml.seek(0)
mcasasb7440c282015-02-04 14:52:191004 histogram_name_found = False
1005 for line in histograms_xml:
1006 histogram_name_found = _FindHistogramNameInLine(histogram_name, line)
1007 if histogram_name_found:
1008 break
1009 if not histogram_name_found:
1010 problems.append(' [%s:%d] %s' %
1011 (f.LocalPath(), line_num, histogram_name))
1012
1013 if not problems:
1014 return []
1015 return [output_api.PresubmitPromptWarning('Some UMA_HISTOGRAM lines have '
1016 'been modified and the associated histogram name has no match in either '
eromanb90c82e7e32015-04-01 15:13:491017 '%s or the modifications of it:' % (histograms_xml_path), problems)]
mcasasb7440c282015-02-04 14:52:191018
wnwenbdc444e2016-05-25 13:44:151019
yolandyandaabc6d2016-04-18 18:29:391020def _CheckFlakyTestUsage(input_api, output_api):
1021 """Check that FlakyTest annotation is our own instead of the android one"""
1022 pattern = input_api.re.compile(r'import android.test.FlakyTest;')
1023 files = []
1024 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1025 if f.LocalPath().endswith('Test.java'):
1026 if pattern.search(input_api.ReadFile(f)):
1027 files.append(f)
1028 if len(files):
1029 return [output_api.PresubmitError(
1030 'Use org.chromium.base.test.util.FlakyTest instead of '
1031 'android.test.FlakyTest',
1032 files)]
1033 return []
mcasasb7440c282015-02-04 14:52:191034
wnwenbdc444e2016-05-25 13:44:151035
[email protected]8ea5d4b2011-09-13 21:49:221036def _CheckNoNewWStrings(input_api, output_api):
1037 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:271038 problems = []
[email protected]8ea5d4b2011-09-13 21:49:221039 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:201040 if (not f.LocalPath().endswith(('.cc', '.h')) or
scottmge6f04402014-11-05 01:59:571041 f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h')) or
pennymac84fd6692016-07-13 22:35:341042 '/win/' in f.LocalPath() or
1043 'chrome_elf' in f.LocalPath() or
1044 'install_static' in f.LocalPath()):
[email protected]b5c24292011-11-28 14:38:201045 continue
[email protected]8ea5d4b2011-09-13 21:49:221046
[email protected]a11dbe9b2012-08-07 01:32:581047 allowWString = False
[email protected]b5c24292011-11-28 14:38:201048 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:581049 if 'presubmit: allow wstring' in line:
1050 allowWString = True
1051 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:271052 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:581053 allowWString = False
1054 else:
1055 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:221056
[email protected]55463aa62011-10-12 00:48:271057 if not problems:
1058 return []
1059 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:581060 ' If you are calling a cross-platform API that accepts a wstring, '
1061 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:271062 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:221063
1064
[email protected]2a8ac9c2011-10-19 17:20:441065def _CheckNoDEPSGIT(input_api, output_api):
1066 """Make sure .DEPS.git is never modified manually."""
1067 if any(f.LocalPath().endswith('.DEPS.git') for f in
1068 input_api.AffectedFiles()):
1069 return [output_api.PresubmitError(
1070 'Never commit changes to .DEPS.git. This file is maintained by an\n'
1071 'automated system based on what\'s in DEPS and your changes will be\n'
1072 'overwritten.\n'
Vaclav Brozekd5de76a2018-03-17 07:57:501073 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/'
1074 'get-the-code#Rolling_DEPS\n'
[email protected]2a8ac9c2011-10-19 17:20:441075 'for more information')]
1076 return []
1077
1078
tandriief664692014-09-23 14:51:471079def _CheckValidHostsInDEPS(input_api, output_api):
1080 """Checks that DEPS file deps are from allowed_hosts."""
1081 # Run only if DEPS file has been modified to annoy fewer bystanders.
1082 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
1083 return []
1084 # Outsource work to gclient verify
1085 try:
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201086 input_api.subprocess.check_output(['gclient', 'verify'],
1087 stderr=input_api.subprocess.STDOUT)
tandriief664692014-09-23 14:51:471088 return []
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201089 except input_api.subprocess.CalledProcessError as error:
tandriief664692014-09-23 14:51:471090 return [output_api.PresubmitError(
1091 'DEPS file must have only git dependencies.',
1092 long_text=error.output)]
1093
1094
[email protected]127f18ec2012-06-16 05:05:591095def _CheckNoBannedFunctions(input_api, output_api):
1096 """Make sure that banned functions are not used."""
1097 warnings = []
1098 errors = []
1099
wnwenbdc444e2016-05-25 13:44:151100 def IsBlacklisted(affected_file, blacklist):
1101 local_path = affected_file.LocalPath()
1102 for item in blacklist:
1103 if input_api.re.match(item, local_path):
1104 return True
1105 return False
1106
Sylvain Defresnea8b73d252018-02-28 15:45:541107 def IsIosObcjFile(affected_file):
1108 local_path = affected_file.LocalPath()
1109 if input_api.os_path.splitext(local_path)[-1] not in ('.mm', '.m', '.h'):
1110 return False
1111 basename = input_api.os_path.basename(local_path)
1112 if 'ios' in basename.split('_'):
1113 return True
1114 for sep in (input_api.os_path.sep, input_api.os_path.altsep):
1115 if sep and 'ios' in local_path.split(sep):
1116 return True
1117 return False
1118
wnwenbdc444e2016-05-25 13:44:151119 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
1120 matched = False
1121 if func_name[0:1] == '/':
1122 regex = func_name[1:]
1123 if input_api.re.search(regex, line):
1124 matched = True
1125 elif func_name in line:
dchenge07de812016-06-20 19:27:171126 matched = True
wnwenbdc444e2016-05-25 13:44:151127 if matched:
dchenge07de812016-06-20 19:27:171128 problems = warnings
wnwenbdc444e2016-05-25 13:44:151129 if error:
dchenge07de812016-06-20 19:27:171130 problems = errors
wnwenbdc444e2016-05-25 13:44:151131 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num))
1132 for message_line in message:
1133 problems.append(' %s' % message_line)
1134
Eric Stevensona9a980972017-09-23 00:04:411135 file_filter = lambda f: f.LocalPath().endswith(('.java'))
1136 for f in input_api.AffectedFiles(file_filter=file_filter):
1137 for line_num, line in f.ChangedContents():
1138 for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
1139 CheckForMatch(f, line_num, line, func_name, message, error)
1140
[email protected]127f18ec2012-06-16 05:05:591141 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
1142 for f in input_api.AffectedFiles(file_filter=file_filter):
1143 for line_num, line in f.ChangedContents():
1144 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
wnwenbdc444e2016-05-25 13:44:151145 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591146
Sylvain Defresnea8b73d252018-02-28 15:45:541147 for f in input_api.AffectedFiles(file_filter=IsIosObcjFile):
1148 for line_num, line in f.ChangedContents():
1149 for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
1150 CheckForMatch(f, line_num, line, func_name, message, error)
1151
[email protected]127f18ec2012-06-16 05:05:591152 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
1153 for f in input_api.AffectedFiles(file_filter=file_filter):
1154 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:491155 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
[email protected]7345da02012-11-27 14:31:491156 if IsBlacklisted(f, excluded_paths):
1157 continue
wnwenbdc444e2016-05-25 13:44:151158 CheckForMatch(f, line_num, line, func_name, message, error)
[email protected]127f18ec2012-06-16 05:05:591159
1160 result = []
1161 if (warnings):
1162 result.append(output_api.PresubmitPromptWarning(
1163 'Banned functions were used.\n' + '\n'.join(warnings)))
1164 if (errors):
1165 result.append(output_api.PresubmitError(
1166 'Banned functions were used.\n' + '\n'.join(errors)))
1167 return result
1168
1169
[email protected]6c063c62012-07-11 19:11:061170def _CheckNoPragmaOnce(input_api, output_api):
1171 """Make sure that banned functions are not used."""
1172 files = []
1173 pattern = input_api.re.compile(r'^#pragma\s+once',
1174 input_api.re.MULTILINE)
1175 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
1176 if not f.LocalPath().endswith('.h'):
1177 continue
1178 contents = input_api.ReadFile(f)
1179 if pattern.search(contents):
1180 files.append(f)
1181
1182 if files:
1183 return [output_api.PresubmitError(
1184 'Do not use #pragma once in header files.\n'
1185 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
1186 files)]
1187 return []
1188
[email protected]127f18ec2012-06-16 05:05:591189
[email protected]e7479052012-09-19 00:26:121190def _CheckNoTrinaryTrueFalse(input_api, output_api):
1191 """Checks to make sure we don't introduce use of foo ? true : false."""
1192 problems = []
1193 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
1194 for f in input_api.AffectedFiles():
1195 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1196 continue
1197
1198 for line_num, line in f.ChangedContents():
1199 if pattern.match(line):
1200 problems.append(' %s:%d' % (f.LocalPath(), line_num))
1201
1202 if not problems:
1203 return []
1204 return [output_api.PresubmitPromptWarning(
1205 'Please consider avoiding the "? true : false" pattern if possible.\n' +
1206 '\n'.join(problems))]
1207
1208
[email protected]55f9f382012-07-31 11:02:181209def _CheckUnwantedDependencies(input_api, output_api):
rhalavati08acd232017-04-03 07:23:281210 """Runs checkdeps on #include and import statements added in this
[email protected]55f9f382012-07-31 11:02:181211 change. Breaking - rules is an error, breaking ! rules is a
1212 warning.
1213 """
mohan.reddyf21db962014-10-16 12:26:471214 import sys
[email protected]55f9f382012-07-31 11:02:181215 # We need to wait until we have an input_api object and use this
1216 # roundabout construct to import checkdeps because this file is
1217 # eval-ed and thus doesn't have __file__.
1218 original_sys_path = sys.path
1219 try:
1220 sys.path = sys.path + [input_api.os_path.join(
[email protected]5298cc982014-05-29 20:53:471221 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
[email protected]55f9f382012-07-31 11:02:181222 import checkdeps
1223 from cpp_checker import CppChecker
Jinsuk Kim5a092672017-10-24 22:42:241224 from java_checker import JavaChecker
rhalavati08acd232017-04-03 07:23:281225 from proto_checker import ProtoChecker
[email protected]55f9f382012-07-31 11:02:181226 from rules import Rule
1227 finally:
1228 # Restore sys.path to what it was before.
1229 sys.path = original_sys_path
1230
1231 added_includes = []
rhalavati08acd232017-04-03 07:23:281232 added_imports = []
Jinsuk Kim5a092672017-10-24 22:42:241233 added_java_imports = []
[email protected]55f9f382012-07-31 11:02:181234 for f in input_api.AffectedFiles():
rhalavati08acd232017-04-03 07:23:281235 if CppChecker.IsCppFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501236 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081237 added_includes.append([f.AbsoluteLocalPath(), changed_lines])
rhalavati08acd232017-04-03 07:23:281238 elif ProtoChecker.IsProtoFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501239 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081240 added_imports.append([f.AbsoluteLocalPath(), changed_lines])
Jinsuk Kim5a092672017-10-24 22:42:241241 elif JavaChecker.IsJavaFile(f.LocalPath()):
Vaclav Brozekd5de76a2018-03-17 07:57:501242 changed_lines = [line for _, line in f.ChangedContents()]
Andrew Grieve085f29f2017-11-02 09:14:081243 added_java_imports.append([f.AbsoluteLocalPath(), changed_lines])
[email protected]55f9f382012-07-31 11:02:181244
[email protected]26385172013-05-09 23:11:351245 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181246
1247 error_descriptions = []
1248 warning_descriptions = []
rhalavati08acd232017-04-03 07:23:281249 error_subjects = set()
1250 warning_subjects = set()
[email protected]55f9f382012-07-31 11:02:181251 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
1252 added_includes):
Andrew Grieve085f29f2017-11-02 09:14:081253 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
[email protected]55f9f382012-07-31 11:02:181254 description_with_path = '%s\n %s' % (path, rule_description)
1255 if rule_type == Rule.DISALLOW:
1256 error_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281257 error_subjects.add("#includes")
[email protected]55f9f382012-07-31 11:02:181258 else:
1259 warning_descriptions.append(description_with_path)
rhalavati08acd232017-04-03 07:23:281260 warning_subjects.add("#includes")
1261
1262 for path, rule_type, rule_description in deps_checker.CheckAddedProtoImports(
1263 added_imports):
Andrew Grieve085f29f2017-11-02 09:14:081264 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
rhalavati08acd232017-04-03 07:23:281265 description_with_path = '%s\n %s' % (path, rule_description)
1266 if rule_type == Rule.DISALLOW:
1267 error_descriptions.append(description_with_path)
1268 error_subjects.add("imports")
1269 else:
1270 warning_descriptions.append(description_with_path)
1271 warning_subjects.add("imports")
[email protected]55f9f382012-07-31 11:02:181272
Jinsuk Kim5a092672017-10-24 22:42:241273 for path, rule_type, rule_description in deps_checker.CheckAddedJavaImports(
Shenghua Zhangbfaa38b82017-11-16 21:58:021274 added_java_imports, _JAVA_MULTIPLE_DEFINITION_EXCLUDED_PATHS):
Andrew Grieve085f29f2017-11-02 09:14:081275 path = input_api.os_path.relpath(path, input_api.PresubmitLocalPath())
Jinsuk Kim5a092672017-10-24 22:42:241276 description_with_path = '%s\n %s' % (path, rule_description)
1277 if rule_type == Rule.DISALLOW:
1278 error_descriptions.append(description_with_path)
1279 error_subjects.add("imports")
1280 else:
1281 warning_descriptions.append(description_with_path)
1282 warning_subjects.add("imports")
1283
[email protected]55f9f382012-07-31 11:02:181284 results = []
1285 if error_descriptions:
1286 results.append(output_api.PresubmitError(
rhalavati08acd232017-04-03 07:23:281287 'You added one or more %s that violate checkdeps rules.'
1288 % " and ".join(error_subjects),
[email protected]55f9f382012-07-31 11:02:181289 error_descriptions))
1290 if warning_descriptions:
[email protected]f7051d52013-04-02 18:31:421291 results.append(output_api.PresubmitPromptOrNotify(
rhalavati08acd232017-04-03 07:23:281292 'You added one or more %s of files that are temporarily\n'
[email protected]55f9f382012-07-31 11:02:181293 'allowed but being removed. Can you avoid introducing the\n'
rhalavati08acd232017-04-03 07:23:281294 '%s? See relevant DEPS file(s) for details and contacts.' %
1295 (" and ".join(warning_subjects), "/".join(warning_subjects)),
[email protected]55f9f382012-07-31 11:02:181296 warning_descriptions))
1297 return results
1298
1299
[email protected]fbcafe5a2012-08-08 15:31:221300def _CheckFilePermissions(input_api, output_api):
1301 """Check that all files have their permissions properly set."""
[email protected]791507202014-02-03 23:19:151302 if input_api.platform == 'win32':
1303 return []
raphael.kubo.da.costac1d13e60b2016-04-01 11:49:291304 checkperms_tool = input_api.os_path.join(
1305 input_api.PresubmitLocalPath(),
1306 'tools', 'checkperms', 'checkperms.py')
1307 args = [input_api.python_executable, checkperms_tool,
mohan.reddyf21db962014-10-16 12:26:471308 '--root', input_api.change.RepositoryRoot()]
Raphael Kubo da Costa6ff391d2017-11-13 16:43:391309 with input_api.CreateTemporaryFile() as file_list:
1310 for f in input_api.AffectedFiles():
1311 # checkperms.py file/directory arguments must be relative to the
1312 # repository.
1313 file_list.write(f.LocalPath() + '\n')
1314 file_list.close()
1315 args += ['--file-list', file_list.name]
1316 try:
1317 input_api.subprocess.check_output(args)
1318 return []
1319 except input_api.subprocess.CalledProcessError as error:
1320 return [output_api.PresubmitError(
1321 'checkperms.py failed:',
1322 long_text=error.output)]
[email protected]fbcafe5a2012-08-08 15:31:221323
1324
robertocn832f5992017-01-04 19:01:301325def _CheckTeamTags(input_api, output_api):
1326 """Checks that OWNERS files have consistent TEAM and COMPONENT tags."""
1327 checkteamtags_tool = input_api.os_path.join(
1328 input_api.PresubmitLocalPath(),
1329 'tools', 'checkteamtags', 'checkteamtags.py')
1330 args = [input_api.python_executable, checkteamtags_tool,
1331 '--root', input_api.change.RepositoryRoot()]
robertocn5eb82312017-01-09 20:27:221332 files = [f.LocalPath() for f in input_api.AffectedFiles(include_deletes=False)
robertocn832f5992017-01-04 19:01:301333 if input_api.os_path.basename(f.AbsoluteLocalPath()).upper() ==
1334 'OWNERS']
1335 try:
1336 if files:
1337 input_api.subprocess.check_output(args + files)
1338 return []
1339 except input_api.subprocess.CalledProcessError as error:
1340 return [output_api.PresubmitError(
1341 'checkteamtags.py failed:',
1342 long_text=error.output)]
1343
1344
[email protected]c8278b32012-10-30 20:35:491345def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
1346 """Makes sure we don't include ui/aura/window_property.h
1347 in header files.
1348 """
1349 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
1350 errors = []
1351 for f in input_api.AffectedFiles():
1352 if not f.LocalPath().endswith('.h'):
1353 continue
1354 for line_num, line in f.ChangedContents():
1355 if pattern.match(line):
1356 errors.append(' %s:%d' % (f.LocalPath(), line_num))
1357
1358 results = []
1359 if errors:
1360 results.append(output_api.PresubmitError(
1361 'Header files should not include ui/aura/window_property.h', errors))
1362 return results
1363
1364
[email protected]70ca77752012-11-20 03:45:031365def _CheckForVersionControlConflictsInFile(input_api, f):
1366 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
1367 errors = []
1368 for line_num, line in f.ChangedContents():
dbeam95c35a2f2015-06-02 01:40:231369 if f.LocalPath().endswith('.md'):
1370 # First-level headers in markdown look a lot like version control
1371 # conflict markers. http://daringfireball.net/projects/markdown/basics
1372 continue
[email protected]70ca77752012-11-20 03:45:031373 if pattern.match(line):
1374 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1375 return errors
1376
1377
1378def _CheckForVersionControlConflicts(input_api, output_api):
1379 """Usually this is not intentional and will cause a compile failure."""
1380 errors = []
1381 for f in input_api.AffectedFiles():
1382 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
1383
1384 results = []
1385 if errors:
1386 results.append(output_api.PresubmitError(
1387 'Version control conflict markers found, please resolve.', errors))
1388 return results
1389
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:201390
estadee17314a02017-01-12 16:22:161391def _CheckGoogleSupportAnswerUrl(input_api, output_api):
1392 pattern = input_api.re.compile('support\.google\.com\/chrome.*/answer')
1393 errors = []
1394 for f in input_api.AffectedFiles():
1395 for line_num, line in f.ChangedContents():
1396 if pattern.search(line):
1397 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
1398
1399 results = []
1400 if errors:
1401 results.append(output_api.PresubmitPromptWarning(
Vaclav Brozekd5de76a2018-03-17 07:57:501402 'Found Google support URL addressed by answer number. Please replace '
1403 'with a p= identifier instead. See crbug.com/679462\n', errors))
estadee17314a02017-01-12 16:22:161404 return results
1405
[email protected]70ca77752012-11-20 03:45:031406
[email protected]06e6d0ff2012-12-11 01:36:441407def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
1408 def FilterFile(affected_file):
1409 """Filter function for use with input_api.AffectedSourceFiles,
1410 below. This filters out everything except non-test files from
1411 top-level directories that generally speaking should not hard-code
1412 service URLs (e.g. src/android_webview/, src/content/ and others).
1413 """
1414 return input_api.FilterSourceFile(
1415 affected_file,
Egor Paskoce145c42018-09-28 19:31:041416 white_list=[r'^(android_webview|base|content|net)[\\/].*'],
[email protected]06e6d0ff2012-12-11 01:36:441417 black_list=(_EXCLUDED_PATHS +
1418 _TEST_CODE_EXCLUDED_PATHS +
1419 input_api.DEFAULT_BLACK_LIST))
1420
reillyi38965732015-11-16 18:27:331421 base_pattern = ('"[^"]*(google|googleapis|googlezip|googledrive|appspot)'
1422 '\.(com|net)[^"]*"')
[email protected]de4f7d22013-05-23 14:27:461423 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
1424 pattern = input_api.re.compile(base_pattern)
[email protected]06e6d0ff2012-12-11 01:36:441425 problems = [] # items are (filename, line_number, line)
1426 for f in input_api.AffectedSourceFiles(FilterFile):
1427 for line_num, line in f.ChangedContents():
[email protected]de4f7d22013-05-23 14:27:461428 if not comment_pattern.search(line) and pattern.search(line):
[email protected]06e6d0ff2012-12-11 01:36:441429 problems.append((f.LocalPath(), line_num, line))
1430
1431 if problems:
[email protected]f7051d52013-04-02 18:31:421432 return [output_api.PresubmitPromptOrNotify(
[email protected]06e6d0ff2012-12-11 01:36:441433 'Most layers below src/chrome/ should not hardcode service URLs.\n'
[email protected]b0149772014-03-27 16:47:581434 'Are you sure this is correct?',
[email protected]06e6d0ff2012-12-11 01:36:441435 [' %s:%d: %s' % (
1436 problem[0], problem[1], problem[2]) for problem in problems])]
[email protected]2fdd1f362013-01-16 03:56:031437 else:
1438 return []
[email protected]06e6d0ff2012-12-11 01:36:441439
1440
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491441# TODO: add unit tests.
[email protected]d2530012013-01-25 16:39:271442def _CheckNoAbbreviationInPngFileName(input_api, output_api):
1443 """Makes sure there are no abbreviations in the name of PNG files.
binji0dcdf342014-12-12 18:32:311444 The native_client_sdk directory is excluded because it has auto-generated PNG
1445 files for documentation.
[email protected]d2530012013-01-25 16:39:271446 """
[email protected]d2530012013-01-25 16:39:271447 errors = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491448 white_list = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
Egor Paskoce145c42018-09-28 19:31:041449 black_list = [r'^native_client_sdk[\\/]']
binji0dcdf342014-12-12 18:32:311450 file_filter = lambda f: input_api.FilterSourceFile(
1451 f, white_list=white_list, black_list=black_list)
1452 for f in input_api.AffectedFiles(include_deletes=False,
1453 file_filter=file_filter):
1454 errors.append(' %s' % f.LocalPath())
[email protected]d2530012013-01-25 16:39:271455
1456 results = []
1457 if errors:
1458 results.append(output_api.PresubmitError(
1459 'The name of PNG files should not have abbreviations. \n'
1460 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
1461 'Contact [email protected] if you have questions.', errors))
1462 return results
1463
1464
Daniel Cheng4dcdb6b2017-04-13 08:30:171465def _ExtractAddRulesFromParsedDeps(parsed_deps):
1466 """Extract the rules that add dependencies from a parsed DEPS file.
1467
1468 Args:
1469 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1470 add_rules = set()
1471 add_rules.update([
1472 rule[1:] for rule in parsed_deps.get('include_rules', [])
1473 if rule.startswith('+') or rule.startswith('!')
1474 ])
Vaclav Brozekd5de76a2018-03-17 07:57:501475 for _, rules in parsed_deps.get('specific_include_rules',
Daniel Cheng4dcdb6b2017-04-13 08:30:171476 {}).iteritems():
1477 add_rules.update([
1478 rule[1:] for rule in rules
1479 if rule.startswith('+') or rule.startswith('!')
1480 ])
1481 return add_rules
1482
1483
1484def _ParseDeps(contents):
1485 """Simple helper for parsing DEPS files."""
1486 # Stubs for handling special syntax in the root DEPS file.
Daniel Cheng4dcdb6b2017-04-13 08:30:171487 class _VarImpl:
1488
1489 def __init__(self, local_scope):
1490 self._local_scope = local_scope
1491
1492 def Lookup(self, var_name):
1493 """Implements the Var syntax."""
1494 try:
1495 return self._local_scope['vars'][var_name]
1496 except KeyError:
1497 raise Exception('Var is not defined: %s' % var_name)
1498
1499 local_scope = {}
1500 global_scope = {
Daniel Cheng4dcdb6b2017-04-13 08:30:171501 'Var': _VarImpl(local_scope).Lookup,
1502 }
1503 exec contents in global_scope, local_scope
1504 return local_scope
1505
1506
1507def _CalculateAddedDeps(os_path, old_contents, new_contents):
[email protected]f32e2d1e2013-07-26 21:39:081508 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
[email protected]14a6131c2014-01-08 01:15:411509 a set of DEPS entries that we should look up.
1510
1511 For a directory (rather than a specific filename) we fake a path to
1512 a specific filename by adding /DEPS. This is chosen as a file that
1513 will seldom or never be subject to per-file include_rules.
1514 """
[email protected]2b438d62013-11-14 17:54:141515 # We ignore deps entries on auto-generated directories.
1516 AUTO_GENERATED_DIRS = ['grit', 'jni']
[email protected]f32e2d1e2013-07-26 21:39:081517
Daniel Cheng4dcdb6b2017-04-13 08:30:171518 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1519 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1520
1521 added_deps = new_deps.difference(old_deps)
1522
[email protected]2b438d62013-11-14 17:54:141523 results = set()
Daniel Cheng4dcdb6b2017-04-13 08:30:171524 for added_dep in added_deps:
1525 if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
1526 continue
1527 # Assume that a rule that ends in .h is a rule for a specific file.
1528 if added_dep.endswith('.h'):
1529 results.add(added_dep)
1530 else:
1531 results.add(os_path.join(added_dep, 'DEPS'))
[email protected]f32e2d1e2013-07-26 21:39:081532 return results
1533
1534
[email protected]e871964c2013-05-13 14:14:551535def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1536 """When a dependency prefixed with + is added to a DEPS file, we
1537 want to make sure that the change is reviewed by an OWNER of the
1538 target file or directory, to avoid layering violations from being
1539 introduced. This check verifies that this happens.
1540 """
Daniel Cheng4dcdb6b2017-04-13 08:30:171541 virtual_depended_on_files = set()
jochen53efcdd2016-01-29 05:09:241542
1543 file_filter = lambda f: not input_api.re.match(
Egor Paskoce145c42018-09-28 19:31:041544 r"^third_party[\\/](WebKit|blink)[\\/].*", f.LocalPath())
jochen53efcdd2016-01-29 05:09:241545 for f in input_api.AffectedFiles(include_deletes=False,
1546 file_filter=file_filter):
[email protected]e871964c2013-05-13 14:14:551547 filename = input_api.os_path.basename(f.LocalPath())
1548 if filename == 'DEPS':
Daniel Cheng4dcdb6b2017-04-13 08:30:171549 virtual_depended_on_files.update(_CalculateAddedDeps(
1550 input_api.os_path,
1551 '\n'.join(f.OldContents()),
1552 '\n'.join(f.NewContents())))
[email protected]e871964c2013-05-13 14:14:551553
[email protected]e871964c2013-05-13 14:14:551554 if not virtual_depended_on_files:
1555 return []
1556
1557 if input_api.is_committing:
1558 if input_api.tbr:
1559 return [output_api.PresubmitNotifyResult(
1560 '--tbr was specified, skipping OWNERS check for DEPS additions')]
Paweł Hajdan, Jrbe6739ea2016-04-28 15:07:271561 if input_api.dry_run:
1562 return [output_api.PresubmitNotifyResult(
1563 'This is a dry run, skipping OWNERS check for DEPS additions')]
[email protected]e871964c2013-05-13 14:14:551564 if not input_api.change.issue:
1565 return [output_api.PresubmitError(
1566 "DEPS approval by OWNERS check failed: this change has "
Aaron Gable65a99d92017-10-09 19:17:401567 "no change number, so we can't check it for approvals.")]
[email protected]e871964c2013-05-13 14:14:551568 output = output_api.PresubmitError
1569 else:
1570 output = output_api.PresubmitNotifyResult
1571
1572 owners_db = input_api.owners_db
tandriied3b7e12016-05-12 14:38:501573 owner_email, reviewers = (
1574 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1575 input_api,
1576 owners_db.email_regexp,
1577 approval_needed=input_api.is_committing))
[email protected]e871964c2013-05-13 14:14:551578
1579 owner_email = owner_email or input_api.change.author_email
1580
[email protected]de4f7d22013-05-23 14:27:461581 reviewers_plus_owner = set(reviewers)
[email protected]e71c6082013-05-22 02:28:511582 if owner_email:
[email protected]de4f7d22013-05-23 14:27:461583 reviewers_plus_owner.add(owner_email)
[email protected]e871964c2013-05-13 14:14:551584 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1585 reviewers_plus_owner)
[email protected]14a6131c2014-01-08 01:15:411586
1587 # We strip the /DEPS part that was added by
1588 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1589 # directory.
1590 def StripDeps(path):
1591 start_deps = path.rfind('/DEPS')
1592 if start_deps != -1:
1593 return path[:start_deps]
1594 else:
1595 return path
1596 unapproved_dependencies = ["'+%s'," % StripDeps(path)
[email protected]e871964c2013-05-13 14:14:551597 for path in missing_files]
1598
1599 if unapproved_dependencies:
1600 output_list = [
Paweł Hajdan, Jrec17f882016-07-04 14:16:151601 output('You need LGTM from owners of depends-on paths in DEPS that were '
1602 'modified in this CL:\n %s' %
1603 '\n '.join(sorted(unapproved_dependencies)))]
1604 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1605 output_list.append(output(
1606 'Suggested missing target path OWNERS:\n %s' %
1607 '\n '.join(suggested_owners or [])))
[email protected]e871964c2013-05-13 14:14:551608 return output_list
1609
1610 return []
1611
1612
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491613# TODO: add unit tests.
[email protected]85218562013-11-22 07:41:401614def _CheckSpamLogging(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491615 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
[email protected]85218562013-11-22 07:41:401616 black_list = (_EXCLUDED_PATHS +
1617 _TEST_CODE_EXCLUDED_PATHS +
1618 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:041619 (r"^base[\\/]logging\.h$",
1620 r"^base[\\/]logging\.cc$",
1621 r"^chrome[\\/]app[\\/]chrome_main_delegate\.cc$",
1622 r"^chrome[\\/]browser[\\/]chrome_browser_main\.cc$",
1623 r"^chrome[\\/]browser[\\/]ui[\\/]startup[\\/]"
[email protected]4de75262013-12-18 23:16:121624 r"startup_browser_creator\.cc$",
Egor Paskoce145c42018-09-28 19:31:041625 r"^chrome[\\/]installer[\\/]setup[\\/].*",
1626 r"^chrome[\\/]chrome_cleaner[\\/].*",
1627 r"chrome[\\/]browser[\\/]diagnostics[\\/]" +
[email protected]f5b9a3f342014-08-08 22:06:031628 r"diagnostics_writer\.cc$",
Egor Paskoce145c42018-09-28 19:31:041629 r"^chrome_elf[\\/]dll_hash[\\/]dll_hash_main\.cc$",
1630 r"^chromecast[\\/]",
1631 r"^cloud_print[\\/]",
1632 r"^components[\\/]browser_watcher[\\/]"
manzagop85e629e2017-05-09 22:11:481633 r"dump_stability_report_main_win.cc$",
Egor Paskoce145c42018-09-28 19:31:041634 r"^components[\\/]html_viewer[\\/]"
jochen34415e52015-07-10 08:34:311635 r"web_test_delegate_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041636 r"^components[\\/]zucchini[\\/].*",
peter80739bb2015-10-20 11:17:461637 # TODO(peter): Remove this exception. https://crbug.com/534537
Egor Paskoce145c42018-09-28 19:31:041638 r"^content[\\/]browser[\\/]notifications[\\/]"
peter80739bb2015-10-20 11:17:461639 r"notification_event_dispatcher_impl\.cc$",
Egor Paskoce145c42018-09-28 19:31:041640 r"^content[\\/]common[\\/]gpu[\\/]client[\\/]"
[email protected]9056e732014-01-08 06:25:251641 r"gl_helper_benchmark\.cc$",
Egor Paskoce145c42018-09-28 19:31:041642 r"^courgette[\\/]courgette_minimal_tool\.cc$",
1643 r"^courgette[\\/]courgette_tool\.cc$",
1644 r"^extensions[\\/]renderer[\\/]logging_native_handler\.cc$",
1645 r"^ipc[\\/]ipc_logging\.cc$",
1646 r"^native_client_sdk[\\/]",
1647 r"^remoting[\\/]base[\\/]logging\.h$",
1648 r"^remoting[\\/]host[\\/].*",
1649 r"^sandbox[\\/]linux[\\/].*",
1650 r"^tools[\\/]",
1651 r"^ui[\\/]base[\\/]resource[\\/]data_pack.cc$",
1652 r"^ui[\\/]aura[\\/]bench[\\/]bench_main\.cc$",
1653 r"^ui[\\/]ozone[\\/]platform[\\/]cast[\\/]",
1654 r"^storage[\\/]browser[\\/]fileapi[\\/]" +
skyostil87681be82016-12-19 12:46:351655 r"dump_file_system.cc$",
Egor Paskoce145c42018-09-28 19:31:041656 r"^headless[\\/]app[\\/]headless_shell\.cc$"))
[email protected]85218562013-11-22 07:41:401657 source_file_filter = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491658 x, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]85218562013-11-22 07:41:401659
thomasanderson625d3932017-03-29 07:16:581660 log_info = set([])
1661 printf = set([])
[email protected]85218562013-11-22 07:41:401662
1663 for f in input_api.AffectedSourceFiles(source_file_filter):
thomasanderson625d3932017-03-29 07:16:581664 for _, line in f.ChangedContents():
1665 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", line):
1666 log_info.add(f.LocalPath())
1667 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", line):
1668 log_info.add(f.LocalPath())
[email protected]18b466b2013-12-02 22:01:371669
thomasanderson625d3932017-03-29 07:16:581670 if input_api.re.search(r"\bprintf\(", line):
1671 printf.add(f.LocalPath())
1672 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", line):
1673 printf.add(f.LocalPath())
[email protected]85218562013-11-22 07:41:401674
1675 if log_info:
1676 return [output_api.PresubmitError(
1677 'These files spam the console log with LOG(INFO):',
1678 items=log_info)]
1679 if printf:
1680 return [output_api.PresubmitError(
1681 'These files spam the console log with printf/fprintf:',
1682 items=printf)]
1683 return []
1684
1685
[email protected]49aa76a2013-12-04 06:59:161686def _CheckForAnonymousVariables(input_api, output_api):
1687 """These types are all expected to hold locks while in scope and
1688 so should never be anonymous (which causes them to be immediately
1689 destroyed)."""
1690 they_who_must_be_named = [
1691 'base::AutoLock',
1692 'base::AutoReset',
1693 'base::AutoUnlock',
1694 'SkAutoAlphaRestore',
1695 'SkAutoBitmapShaderInstall',
1696 'SkAutoBlitterChoose',
1697 'SkAutoBounderCommit',
1698 'SkAutoCallProc',
1699 'SkAutoCanvasRestore',
1700 'SkAutoCommentBlock',
1701 'SkAutoDescriptor',
1702 'SkAutoDisableDirectionCheck',
1703 'SkAutoDisableOvalCheck',
1704 'SkAutoFree',
1705 'SkAutoGlyphCache',
1706 'SkAutoHDC',
1707 'SkAutoLockColors',
1708 'SkAutoLockPixels',
1709 'SkAutoMalloc',
1710 'SkAutoMaskFreeImage',
1711 'SkAutoMutexAcquire',
1712 'SkAutoPathBoundsUpdate',
1713 'SkAutoPDFRelease',
1714 'SkAutoRasterClipValidate',
1715 'SkAutoRef',
1716 'SkAutoTime',
1717 'SkAutoTrace',
1718 'SkAutoUnref',
1719 ]
1720 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1721 # bad: base::AutoLock(lock.get());
1722 # not bad: base::AutoLock lock(lock.get());
1723 bad_pattern = input_api.re.compile(anonymous)
1724 # good: new base::AutoLock(lock.get())
1725 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1726 errors = []
1727
1728 for f in input_api.AffectedFiles():
1729 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1730 continue
1731 for linenum, line in f.ChangedContents():
1732 if bad_pattern.search(line) and not good_pattern.search(line):
1733 errors.append('%s:%d' % (f.LocalPath(), linenum))
1734
1735 if errors:
1736 return [output_api.PresubmitError(
1737 'These lines create anonymous variables that need to be named:',
1738 items=errors)]
1739 return []
1740
1741
Peter Kasting4844e46e2018-02-23 07:27:101742def _CheckUniquePtr(input_api, output_api):
Vaclav Brozekb7fadb692018-08-30 06:39:531743 # Returns whether |template_str| is of the form <T, U...> for some types T
1744 # and U. Assumes that |template_str| is already in the form <...>.
1745 def HasMoreThanOneArg(template_str):
1746 # Level of <...> nesting.
1747 nesting = 0
1748 for c in template_str:
1749 if c == '<':
1750 nesting += 1
1751 elif c == '>':
1752 nesting -= 1
1753 elif c == ',' and nesting == 1:
1754 return True
1755 return False
1756
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491757 file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
Peter Kasting4844e46e2018-02-23 07:27:101758 sources = lambda affected_file: input_api.FilterSourceFile(
1759 affected_file,
1760 black_list=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1761 input_api.DEFAULT_BLACK_LIST),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:491762 white_list=file_inclusion_pattern)
Vaclav Brozeka54c528b2018-04-06 19:23:551763
1764 # Pattern to capture a single "<...>" block of template arguments. It can
1765 # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
1766 # cannot handle branching structures, such as "<pair<set<T>,set<U>>". The
1767 # latter would likely require counting that < and > match, which is not
1768 # expressible in regular languages. Should the need arise, one can introduce
1769 # limited counting (matching up to a total number of nesting depth), which
1770 # should cover all practical cases for already a low nesting limit.
1771 template_arg_pattern = (
1772 r'<[^>]*' # Opening block of <.
1773 r'>([^<]*>)?') # Closing block of >.
1774 # Prefix expressing that whatever follows is not already inside a <...>
1775 # block.
1776 not_inside_template_arg_pattern = r'(^|[^<,\s]\s*)'
Peter Kasting4844e46e2018-02-23 07:27:101777 null_construct_pattern = input_api.re.compile(
Vaclav Brozeka54c528b2018-04-06 19:23:551778 not_inside_template_arg_pattern
1779 + r'\bstd::unique_ptr'
1780 + template_arg_pattern
1781 + r'\(\)')
1782
1783 # Same as template_arg_pattern, but excluding type arrays, e.g., <T[]>.
1784 template_arg_no_array_pattern = (
1785 r'<[^>]*[^]]' # Opening block of <.
1786 r'>([^(<]*[^]]>)?') # Closing block of >.
1787 # Prefix saying that what follows is the start of an expression.
1788 start_of_expr_pattern = r'(=|\breturn|^)\s*'
1789 # Suffix saying that what follows are call parentheses with a non-empty list
1790 # of arguments.
1791 nonempty_arg_list_pattern = r'\(([^)]|$)'
Vaclav Brozekb7fadb692018-08-30 06:39:531792 # Put the template argument into a capture group for deeper examination later.
Vaclav Brozeka54c528b2018-04-06 19:23:551793 return_construct_pattern = input_api.re.compile(
1794 start_of_expr_pattern
1795 + r'std::unique_ptr'
Vaclav Brozekb7fadb692018-08-30 06:39:531796 + '(?P<template_arg>'
Vaclav Brozeka54c528b2018-04-06 19:23:551797 + template_arg_no_array_pattern
Vaclav Brozekb7fadb692018-08-30 06:39:531798 + ')'
Vaclav Brozeka54c528b2018-04-06 19:23:551799 + nonempty_arg_list_pattern)
1800
Vaclav Brozek851d9602018-04-04 16:13:051801 problems_constructor = []
1802 problems_nullptr = []
Peter Kasting4844e46e2018-02-23 07:27:101803 for f in input_api.AffectedSourceFiles(sources):
1804 for line_number, line in f.ChangedContents():
1805 # Disallow:
1806 # return std::unique_ptr<T>(foo);
1807 # bar = std::unique_ptr<T>(foo);
1808 # But allow:
1809 # return std::unique_ptr<T[]>(foo);
1810 # bar = std::unique_ptr<T[]>(foo);
Vaclav Brozekb7fadb692018-08-30 06:39:531811 # And also allow cases when the second template argument is present. Those
1812 # cases cannot be handled by std::make_unique:
1813 # return std::unique_ptr<T, U>(foo);
1814 # bar = std::unique_ptr<T, U>(foo);
Vaclav Brozek851d9602018-04-04 16:13:051815 local_path = f.LocalPath()
Vaclav Brozekb7fadb692018-08-30 06:39:531816 return_construct_result = return_construct_pattern.search(line)
1817 if return_construct_result and not HasMoreThanOneArg(
1818 return_construct_result.group('template_arg')):
Vaclav Brozek851d9602018-04-04 16:13:051819 problems_constructor.append(
1820 '%s:%d\n %s' % (local_path, line_number, line.strip()))
Peter Kasting4844e46e2018-02-23 07:27:101821 # Disallow:
1822 # std::unique_ptr<T>()
1823 if null_construct_pattern.search(line):
Vaclav Brozek851d9602018-04-04 16:13:051824 problems_nullptr.append(
1825 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1826
1827 errors = []
Vaclav Brozekc2fecf42018-04-06 16:40:161828 if problems_nullptr:
Vaclav Brozek851d9602018-04-04 16:13:051829 errors.append(output_api.PresubmitError(
1830 'The following files use std::unique_ptr<T>(). Use nullptr instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161831 problems_nullptr))
1832 if problems_constructor:
Vaclav Brozek851d9602018-04-04 16:13:051833 errors.append(output_api.PresubmitError(
1834 'The following files use explicit std::unique_ptr constructor.'
1835 'Use std::make_unique<T>() instead.',
Vaclav Brozekc2fecf42018-04-06 16:40:161836 problems_constructor))
Peter Kasting4844e46e2018-02-23 07:27:101837 return errors
1838
1839
[email protected]999261d2014-03-03 20:08:081840def _CheckUserActionUpdate(input_api, output_api):
1841 """Checks if any new user action has been added."""
[email protected]2f92dec2014-03-07 19:21:521842 if any('actions.xml' == input_api.os_path.basename(f) for f in
[email protected]999261d2014-03-03 20:08:081843 input_api.LocalPaths()):
[email protected]2f92dec2014-03-07 19:21:521844 # If actions.xml is already included in the changelist, the PRESUBMIT
1845 # for actions.xml will do a more complete presubmit check.
[email protected]999261d2014-03-03 20:08:081846 return []
1847
[email protected]999261d2014-03-03 20:08:081848 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1849 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
[email protected]2f92dec2014-03-07 19:21:521850 current_actions = None
[email protected]999261d2014-03-03 20:08:081851 for f in input_api.AffectedFiles(file_filter=file_filter):
1852 for line_num, line in f.ChangedContents():
1853 match = input_api.re.search(action_re, line)
1854 if match:
[email protected]2f92dec2014-03-07 19:21:521855 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1856 # loaded only once.
1857 if not current_actions:
1858 with open('tools/metrics/actions/actions.xml') as actions_f:
1859 current_actions = actions_f.read()
1860 # Search for the matched user action name in |current_actions|.
[email protected]999261d2014-03-03 20:08:081861 for action_name in match.groups():
[email protected]2f92dec2014-03-07 19:21:521862 action = 'name="{0}"'.format(action_name)
1863 if action not in current_actions:
[email protected]999261d2014-03-03 20:08:081864 return [output_api.PresubmitPromptWarning(
1865 'File %s line %d: %s is missing in '
[email protected]2f92dec2014-03-07 19:21:521866 'tools/metrics/actions/actions.xml. Please run '
1867 'tools/metrics/actions/extract_actions.py to update.'
[email protected]999261d2014-03-03 20:08:081868 % (f.LocalPath(), line_num, action_name))]
1869 return []
1870
1871
Daniel Cheng13ca61a882017-08-25 15:11:251872def _ImportJSONCommentEater(input_api):
1873 import sys
1874 sys.path = sys.path + [input_api.os_path.join(
1875 input_api.PresubmitLocalPath(),
1876 'tools', 'json_comment_eater')]
1877 import json_comment_eater
1878 return json_comment_eater
1879
1880
[email protected]99171a92014-06-03 08:44:471881def _GetJSONParseError(input_api, filename, eat_comments=True):
1882 try:
1883 contents = input_api.ReadFile(filename)
1884 if eat_comments:
Daniel Cheng13ca61a882017-08-25 15:11:251885 json_comment_eater = _ImportJSONCommentEater(input_api)
plundblad1f5a4509f2015-07-23 11:31:131886 contents = json_comment_eater.Nom(contents)
[email protected]99171a92014-06-03 08:44:471887
1888 input_api.json.loads(contents)
1889 except ValueError as e:
1890 return e
1891 return None
1892
1893
1894def _GetIDLParseError(input_api, filename):
1895 try:
1896 contents = input_api.ReadFile(filename)
1897 idl_schema = input_api.os_path.join(
1898 input_api.PresubmitLocalPath(),
1899 'tools', 'json_schema_compiler', 'idl_schema.py')
1900 process = input_api.subprocess.Popen(
1901 [input_api.python_executable, idl_schema],
1902 stdin=input_api.subprocess.PIPE,
1903 stdout=input_api.subprocess.PIPE,
1904 stderr=input_api.subprocess.PIPE,
1905 universal_newlines=True)
1906 (_, error) = process.communicate(input=contents)
1907 return error or None
1908 except ValueError as e:
1909 return e
1910
1911
1912def _CheckParseErrors(input_api, output_api):
1913 """Check that IDL and JSON files do not contain syntax errors."""
1914 actions = {
1915 '.idl': _GetIDLParseError,
1916 '.json': _GetJSONParseError,
1917 }
[email protected]99171a92014-06-03 08:44:471918 # Most JSON files are preprocessed and support comments, but these do not.
1919 json_no_comments_patterns = [
Egor Paskoce145c42018-09-28 19:31:041920 r'^testing[\\/]',
[email protected]99171a92014-06-03 08:44:471921 ]
1922 # Only run IDL checker on files in these directories.
1923 idl_included_patterns = [
Egor Paskoce145c42018-09-28 19:31:041924 r'^chrome[\\/]common[\\/]extensions[\\/]api[\\/]',
1925 r'^extensions[\\/]common[\\/]api[\\/]',
[email protected]99171a92014-06-03 08:44:471926 ]
1927
1928 def get_action(affected_file):
1929 filename = affected_file.LocalPath()
1930 return actions.get(input_api.os_path.splitext(filename)[1])
1931
[email protected]99171a92014-06-03 08:44:471932 def FilterFile(affected_file):
1933 action = get_action(affected_file)
1934 if not action:
1935 return False
1936 path = affected_file.LocalPath()
1937
Sean Kau46e29bc2017-08-28 16:31:161938 if _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS, path):
[email protected]99171a92014-06-03 08:44:471939 return False
1940
1941 if (action == _GetIDLParseError and
Sean Kau46e29bc2017-08-28 16:31:161942 not _MatchesFile(input_api, idl_included_patterns, path)):
[email protected]99171a92014-06-03 08:44:471943 return False
1944 return True
1945
1946 results = []
1947 for affected_file in input_api.AffectedFiles(
1948 file_filter=FilterFile, include_deletes=False):
1949 action = get_action(affected_file)
1950 kwargs = {}
1951 if (action == _GetJSONParseError and
Sean Kau46e29bc2017-08-28 16:31:161952 _MatchesFile(input_api, json_no_comments_patterns,
1953 affected_file.LocalPath())):
[email protected]99171a92014-06-03 08:44:471954 kwargs['eat_comments'] = False
1955 parse_error = action(input_api,
1956 affected_file.AbsoluteLocalPath(),
1957 **kwargs)
1958 if parse_error:
1959 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1960 (affected_file.LocalPath(), parse_error)))
1961 return results
1962
1963
[email protected]760deea2013-12-10 19:33:491964def _CheckJavaStyle(input_api, output_api):
1965 """Runs checkstyle on changed java files and returns errors if any exist."""
mohan.reddyf21db962014-10-16 12:26:471966 import sys
[email protected]760deea2013-12-10 19:33:491967 original_sys_path = sys.path
1968 try:
1969 sys.path = sys.path + [input_api.os_path.join(
1970 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1971 import checkstyle
1972 finally:
1973 # Restore sys.path to what it was before.
1974 sys.path = original_sys_path
1975
1976 return checkstyle.RunCheckstyle(
davileen72d76532015-01-20 22:30:091977 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
newtd8b7d30e92015-01-23 18:10:511978 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]760deea2013-12-10 19:33:491979
1980
Sean Kau46e29bc2017-08-28 16:31:161981def _MatchesFile(input_api, patterns, path):
1982 for pattern in patterns:
1983 if input_api.re.search(pattern, path):
1984 return True
1985 return False
1986
1987
Daniel Cheng7052cdf2017-11-21 19:23:291988def _GetOwnersFilesToCheckForIpcOwners(input_api):
1989 """Gets a list of OWNERS files to check for correct security owners.
dchenge07de812016-06-20 19:27:171990
Daniel Cheng7052cdf2017-11-21 19:23:291991 Returns:
1992 A dictionary mapping an OWNER file to the list of OWNERS rules it must
1993 contain to cover IPC-related files with noparent reviewer rules.
1994 """
1995 # Whether or not a file affects IPC is (mostly) determined by a simple list
1996 # of filename patterns.
dchenge07de812016-06-20 19:27:171997 file_patterns = [
palmerb19a0932017-01-24 04:00:311998 # Legacy IPC:
dchenge07de812016-06-20 19:27:171999 '*_messages.cc',
2000 '*_messages*.h',
2001 '*_param_traits*.*',
palmerb19a0932017-01-24 04:00:312002 # Mojo IPC:
dchenge07de812016-06-20 19:27:172003 '*.mojom',
Daniel Cheng1f386932018-01-29 19:56:472004 '*_mojom_traits*.*',
dchenge07de812016-06-20 19:27:172005 '*_struct_traits*.*',
2006 '*_type_converter*.*',
palmerb19a0932017-01-24 04:00:312007 '*.typemap',
2008 # Android native IPC:
2009 '*.aidl',
2010 # Blink uses a different file naming convention:
2011 '*EnumTraits*.*',
Daniel Chenge0bf3f62018-01-30 01:56:472012 "*MojomTraits*.*",
dchenge07de812016-06-20 19:27:172013 '*StructTraits*.*',
2014 '*TypeConverter*.*',
2015 ]
2016
scottmg7a6ed5ba2016-11-04 18:22:042017 # These third_party directories do not contain IPCs, but contain files
2018 # matching the above patterns, which trigger false positives.
2019 exclude_paths = [
2020 'third_party/crashpad/*',
Andres Medinae684cf42018-08-27 18:48:232021 'third_party/protobuf/benchmarks/python/*',
Daniel Chengebe635e2018-07-13 12:36:062022 'third_party/third_party/blink/renderer/platform/bindings/*',
Nico Weberee3dc9b2017-08-31 17:09:292023 'third_party/win_build_output/*',
scottmg7a6ed5ba2016-11-04 18:22:042024 ]
2025
dchenge07de812016-06-20 19:27:172026 # Dictionary mapping an OWNERS file path to Patterns.
2027 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file
2028 # rules ) to a PatternEntry.
2029 # PatternEntry is a dictionary with two keys:
2030 # - 'files': the files that are matched by this pattern
2031 # - 'rules': the per-file rules needed for this pattern
2032 # For example, if we expect OWNERS file to contain rules for *.mojom and
2033 # *_struct_traits*.*, Patterns might look like this:
2034 # {
2035 # '*.mojom': {
2036 # 'files': ...,
2037 # 'rules': [
2038 # 'per-file *.mojom=set noparent',
2039 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS',
2040 # ],
2041 # },
2042 # '*_struct_traits*.*': {
2043 # 'files': ...,
2044 # 'rules': [
2045 # 'per-file *_struct_traits*.*=set noparent',
2046 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS',
2047 # ],
2048 # },
2049 # }
2050 to_check = {}
2051
Daniel Cheng13ca61a882017-08-25 15:11:252052 def AddPatternToCheck(input_file, pattern):
2053 owners_file = input_api.os_path.join(
2054 input_api.os_path.dirname(input_file.LocalPath()), 'OWNERS')
2055 if owners_file not in to_check:
2056 to_check[owners_file] = {}
2057 if pattern not in to_check[owners_file]:
2058 to_check[owners_file][pattern] = {
2059 'files': [],
2060 'rules': [
2061 'per-file %s=set noparent' % pattern,
2062 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern,
2063 ]
2064 }
Vaclav Brozekd5de76a2018-03-17 07:57:502065 to_check[owners_file][pattern]['files'].append(input_file)
Daniel Cheng13ca61a882017-08-25 15:11:252066
dchenge07de812016-06-20 19:27:172067 # Iterate through the affected files to see what we actually need to check
2068 # for. We should only nag patch authors about per-file rules if a file in that
2069 # directory would match that pattern. If a directory only contains *.mojom
2070 # files and no *_messages*.h files, we should only nag about rules for
2071 # *.mojom files.
Daniel Cheng13ca61a882017-08-25 15:11:252072 for f in input_api.AffectedFiles(include_deletes=False):
2073 # Manifest files don't have a strong naming convention. Instead, scan
2074 # affected files for .json files and see if they look like a manifest.
Sean Kau46e29bc2017-08-28 16:31:162075 if (f.LocalPath().endswith('.json') and
2076 not _MatchesFile(input_api, _KNOWN_INVALID_JSON_FILE_PATTERNS,
2077 f.LocalPath())):
Daniel Cheng13ca61a882017-08-25 15:11:252078 json_comment_eater = _ImportJSONCommentEater(input_api)
2079 mostly_json_lines = '\n'.join(f.NewContents())
2080 # Comments aren't allowed in strict JSON, so filter them out.
2081 json_lines = json_comment_eater.Nom(mostly_json_lines)
Daniel Chenge8efd092018-03-23 23:57:432082 try:
2083 json_content = input_api.json.loads(json_lines)
2084 except:
2085 # There's another PRESUBMIT check that already verifies that JSON files
2086 # are not invalid, so no need to emit another warning here.
2087 continue
Daniel Cheng13ca61a882017-08-25 15:11:252088 if 'interface_provider_specs' in json_content:
2089 AddPatternToCheck(f, input_api.os_path.basename(f.LocalPath()))
dchenge07de812016-06-20 19:27:172090 for pattern in file_patterns:
2091 if input_api.fnmatch.fnmatch(
2092 input_api.os_path.basename(f.LocalPath()), pattern):
scottmg7a6ed5ba2016-11-04 18:22:042093 skip = False
2094 for exclude in exclude_paths:
2095 if input_api.fnmatch.fnmatch(f.LocalPath(), exclude):
2096 skip = True
2097 break
2098 if skip:
2099 continue
Daniel Cheng13ca61a882017-08-25 15:11:252100 AddPatternToCheck(f, pattern)
dchenge07de812016-06-20 19:27:172101 break
2102
Daniel Cheng7052cdf2017-11-21 19:23:292103 return to_check
2104
2105
2106def _CheckIpcOwners(input_api, output_api):
2107 """Checks that affected files involving IPC have an IPC OWNERS rule."""
2108 to_check = _GetOwnersFilesToCheckForIpcOwners(input_api)
2109
2110 if to_check:
2111 # If there are any OWNERS files to check, there are IPC-related changes in
2112 # this CL. Auto-CC the review list.
2113 output_api.AppendCC('[email protected]')
2114
2115 # Go through the OWNERS files to check, filtering out rules that are already
2116 # present in that OWNERS file.
dchenge07de812016-06-20 19:27:172117 for owners_file, patterns in to_check.iteritems():
2118 try:
2119 with file(owners_file) as f:
2120 lines = set(f.read().splitlines())
2121 for entry in patterns.itervalues():
2122 entry['rules'] = [rule for rule in entry['rules'] if rule not in lines
2123 ]
2124 except IOError:
2125 # No OWNERS file, so all the rules are definitely missing.
2126 continue
2127
2128 # All the remaining lines weren't found in OWNERS files, so emit an error.
2129 errors = []
2130 for owners_file, patterns in to_check.iteritems():
2131 missing_lines = []
2132 files = []
Vaclav Brozekd5de76a2018-03-17 07:57:502133 for _, entry in patterns.iteritems():
dchenge07de812016-06-20 19:27:172134 missing_lines.extend(entry['rules'])
2135 files.extend([' %s' % f.LocalPath() for f in entry['files']])
2136 if missing_lines:
2137 errors.append(
Vaclav Brozek1893a972018-04-25 05:48:052138 'Because of the presence of files:\n%s\n\n'
2139 '%s needs the following %d lines added:\n\n%s' %
2140 ('\n'.join(files), owners_file, len(missing_lines),
2141 '\n'.join(missing_lines)))
dchenge07de812016-06-20 19:27:172142
2143 results = []
2144 if errors:
vabrf5ce3bf92016-07-11 14:52:412145 if input_api.is_committing:
2146 output = output_api.PresubmitError
2147 else:
2148 output = output_api.PresubmitPromptWarning
2149 results.append(output(
Daniel Cheng52111692017-06-14 08:00:592150 'Found OWNERS files that need to be updated for IPC security ' +
2151 'review coverage.\nPlease update the OWNERS files below:',
dchenge07de812016-06-20 19:27:172152 long_text='\n\n'.join(errors)))
2153
2154 return results
2155
2156
jbriance9e12f162016-11-25 07:57:502157def _CheckUselessForwardDeclarations(input_api, output_api):
jbriance2c51e821a2016-12-12 08:24:312158 """Checks that added or removed lines in non third party affected
2159 header files do not lead to new useless class or struct forward
2160 declaration.
jbriance9e12f162016-11-25 07:57:502161 """
2162 results = []
2163 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
2164 input_api.re.MULTILINE)
2165 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
2166 input_api.re.MULTILINE)
2167 for f in input_api.AffectedFiles(include_deletes=False):
jbriance2c51e821a2016-12-12 08:24:312168 if (f.LocalPath().startswith('third_party') and
Kent Tamurae9b3a9ec2017-08-31 02:20:192169 not f.LocalPath().startswith('third_party/blink') and
2170 not f.LocalPath().startswith('third_party\\blink') and
jbriance2c51e821a2016-12-12 08:24:312171 not f.LocalPath().startswith('third_party/WebKit') and
2172 not f.LocalPath().startswith('third_party\\WebKit')):
2173 continue
2174
jbriance9e12f162016-11-25 07:57:502175 if not f.LocalPath().endswith('.h'):
2176 continue
2177
2178 contents = input_api.ReadFile(f)
2179 fwd_decls = input_api.re.findall(class_pattern, contents)
2180 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
2181
2182 useless_fwd_decls = []
2183 for decl in fwd_decls:
2184 count = sum(1 for _ in input_api.re.finditer(
2185 r'\b%s\b' % input_api.re.escape(decl), contents))
2186 if count == 1:
2187 useless_fwd_decls.append(decl)
2188
2189 if not useless_fwd_decls:
2190 continue
2191
2192 for line in f.GenerateScmDiff().splitlines():
2193 if (line.startswith('-') and not line.startswith('--') or
2194 line.startswith('+') and not line.startswith('++')):
2195 for decl in useless_fwd_decls:
2196 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
2197 results.append(output_api.PresubmitPromptWarning(
ricea6416dea2017-05-19 12:39:242198 '%s: %s forward declaration is no longer needed' %
jbriance9e12f162016-11-25 07:57:502199 (f.LocalPath(), decl)))
2200 useless_fwd_decls.remove(decl)
2201
2202 return results
2203
2204
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492205# TODO: add unit tests
dskiba88634f4e2015-08-14 23:03:292206def _CheckAndroidToastUsage(input_api, output_api):
2207 """Checks that code uses org.chromium.ui.widget.Toast instead of
2208 android.widget.Toast (Chromium Toast doesn't force hardware
2209 acceleration on low-end devices, saving memory).
2210 """
2211 toast_import_pattern = input_api.re.compile(
2212 r'^import android\.widget\.Toast;$')
2213
2214 errors = []
2215
2216 sources = lambda affected_file: input_api.FilterSourceFile(
2217 affected_file,
2218 black_list=(_EXCLUDED_PATHS +
2219 _TEST_CODE_EXCLUDED_PATHS +
2220 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042221 (r'^chromecast[\\/].*',
2222 r'^remoting[\\/].*')),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492223 white_list=[r'.*\.java$'])
dskiba88634f4e2015-08-14 23:03:292224
2225 for f in input_api.AffectedSourceFiles(sources):
2226 for line_num, line in f.ChangedContents():
2227 if toast_import_pattern.search(line):
2228 errors.append("%s:%d" % (f.LocalPath(), line_num))
2229
2230 results = []
2231
2232 if errors:
2233 results.append(output_api.PresubmitError(
2234 'android.widget.Toast usage is detected. Android toasts use hardware'
2235 ' acceleration, and can be\ncostly on low-end devices. Please use'
2236 ' org.chromium.ui.widget.Toast instead.\n'
2237 'Contact [email protected] if you have any questions.',
2238 errors))
2239
2240 return results
2241
2242
dgnaa68d5e2015-06-10 10:08:222243def _CheckAndroidCrLogUsage(input_api, output_api):
2244 """Checks that new logs using org.chromium.base.Log:
2245 - Are using 'TAG' as variable name for the tags (warn)
dgn38736db2015-09-18 19:20:512246 - Are using a tag that is shorter than 20 characters (error)
dgnaa68d5e2015-06-10 10:08:222247 """
pkotwicza1dd0b002016-05-16 14:41:042248
torne89540622017-03-24 19:41:302249 # Do not check format of logs in the given files
pkotwicza1dd0b002016-05-16 14:41:042250 cr_log_check_excluded_paths = [
torne89540622017-03-24 19:41:302251 # //chrome/android/webapk cannot depend on //base
Egor Paskoce145c42018-09-28 19:31:042252 r"^chrome[\\/]android[\\/]webapk[\\/].*",
torne89540622017-03-24 19:41:302253 # WebView license viewer code cannot depend on //base; used in stub APK.
Egor Paskoce145c42018-09-28 19:31:042254 r"^android_webview[\\/]glue[\\/]java[\\/]src[\\/]com[\\/]android[\\/]"
2255 r"webview[\\/]chromium[\\/]License.*",
Egor Paskoa5c05b02018-09-28 16:04:092256 # The customtabs_benchmark is a small app that does not depend on Chromium
2257 # java pieces.
Egor Paskoce145c42018-09-28 19:31:042258 r"tools[\\/]android[\\/]customtabs_benchmark[\\/].*",
pkotwicza1dd0b002016-05-16 14:41:042259 ]
2260
dgnaa68d5e2015-06-10 10:08:222261 cr_log_import_pattern = input_api.re.compile(
dgn87d9fb62015-06-12 09:15:122262 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
2263 class_in_base_pattern = input_api.re.compile(
2264 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
2265 has_some_log_import_pattern = input_api.re.compile(
2266 r'^import .*\.Log;$', input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222267 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
dgn87d9fb62015-06-12 09:15:122268 log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
dgnaa68d5e2015-06-10 10:08:222269 log_decl_pattern = input_api.re.compile(
dgn38736db2015-09-18 19:20:512270 r'^\s*private static final String TAG = "(?P<name>(.*))";',
dgnaa68d5e2015-06-10 10:08:222271 input_api.re.MULTILINE)
dgnaa68d5e2015-06-10 10:08:222272
Vincent Scheib16d7b272015-09-15 18:09:072273 REF_MSG = ('See docs/android_logging.md '
dgnaa68d5e2015-06-10 10:08:222274 'or contact [email protected] for more info.')
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492275 sources = lambda x: input_api.FilterSourceFile(x, white_list=[r'.*\.java$'],
pkotwicza1dd0b002016-05-16 14:41:042276 black_list=cr_log_check_excluded_paths)
dgn87d9fb62015-06-12 09:15:122277
dgnaa68d5e2015-06-10 10:08:222278 tag_decl_errors = []
2279 tag_length_errors = []
dgn87d9fb62015-06-12 09:15:122280 tag_errors = []
dgn38736db2015-09-18 19:20:512281 tag_with_dot_errors = []
dgn87d9fb62015-06-12 09:15:122282 util_log_errors = []
dgnaa68d5e2015-06-10 10:08:222283
2284 for f in input_api.AffectedSourceFiles(sources):
2285 file_content = input_api.ReadFile(f)
2286 has_modified_logs = False
2287
2288 # Per line checks
dgn87d9fb62015-06-12 09:15:122289 if (cr_log_import_pattern.search(file_content) or
2290 (class_in_base_pattern.search(file_content) and
2291 not has_some_log_import_pattern.search(file_content))):
2292 # Checks to run for files using cr log
dgnaa68d5e2015-06-10 10:08:222293 for line_num, line in f.ChangedContents():
2294
2295 # Check if the new line is doing some logging
dgn87d9fb62015-06-12 09:15:122296 match = log_call_pattern.search(line)
dgnaa68d5e2015-06-10 10:08:222297 if match:
2298 has_modified_logs = True
2299
2300 # Make sure it uses "TAG"
2301 if not match.group('tag') == 'TAG':
2302 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgn87d9fb62015-06-12 09:15:122303 else:
2304 # Report non cr Log function calls in changed lines
2305 for line_num, line in f.ChangedContents():
2306 if log_call_pattern.search(line):
2307 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
dgnaa68d5e2015-06-10 10:08:222308
2309 # Per file checks
2310 if has_modified_logs:
2311 # Make sure the tag is using the "cr" prefix and is not too long
2312 match = log_decl_pattern.search(file_content)
dgn38736db2015-09-18 19:20:512313 tag_name = match.group('name') if match else None
2314 if not tag_name:
dgnaa68d5e2015-06-10 10:08:222315 tag_decl_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512316 elif len(tag_name) > 20:
dgnaa68d5e2015-06-10 10:08:222317 tag_length_errors.append(f.LocalPath())
dgn38736db2015-09-18 19:20:512318 elif '.' in tag_name:
2319 tag_with_dot_errors.append(f.LocalPath())
dgnaa68d5e2015-06-10 10:08:222320
2321 results = []
2322 if tag_decl_errors:
2323 results.append(output_api.PresubmitPromptWarning(
2324 'Please define your tags using the suggested format: .\n'
dgn38736db2015-09-18 19:20:512325 '"private static final String TAG = "<package tag>".\n'
2326 'They will be prepended with "cr_" automatically.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222327 tag_decl_errors))
2328
2329 if tag_length_errors:
2330 results.append(output_api.PresubmitError(
2331 'The tag length is restricted by the system to be at most '
dgn38736db2015-09-18 19:20:512332 '20 characters.\n' + REF_MSG,
dgnaa68d5e2015-06-10 10:08:222333 tag_length_errors))
2334
2335 if tag_errors:
2336 results.append(output_api.PresubmitPromptWarning(
2337 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
2338 tag_errors))
2339
dgn87d9fb62015-06-12 09:15:122340 if util_log_errors:
dgn4401aa52015-04-29 16:26:172341 results.append(output_api.PresubmitPromptWarning(
dgn87d9fb62015-06-12 09:15:122342 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
2343 util_log_errors))
2344
dgn38736db2015-09-18 19:20:512345 if tag_with_dot_errors:
2346 results.append(output_api.PresubmitPromptWarning(
2347 'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
2348 tag_with_dot_errors))
2349
dgn4401aa52015-04-29 16:26:172350 return results
2351
2352
Yoland Yanb92fa522017-08-28 17:37:062353def _CheckAndroidTestJUnitFrameworkImport(input_api, output_api):
2354 """Checks that junit.framework.* is no longer used."""
2355 deprecated_junit_framework_pattern = input_api.re.compile(
2356 r'^import junit\.framework\..*;',
2357 input_api.re.MULTILINE)
2358 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492359 x, white_list=[r'.*\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062360 errors = []
2361 for f in input_api.AffectedFiles(sources):
2362 for line_num, line in f.ChangedContents():
2363 if deprecated_junit_framework_pattern.search(line):
2364 errors.append("%s:%d" % (f.LocalPath(), line_num))
2365
2366 results = []
2367 if errors:
2368 results.append(output_api.PresubmitError(
2369 'APIs from junit.framework.* are deprecated, please use JUnit4 framework'
2370 '(org.junit.*) from //third_party/junit. Contact [email protected]'
2371 ' if you have any question.', errors))
2372 return results
2373
2374
2375def _CheckAndroidTestJUnitInheritance(input_api, output_api):
2376 """Checks that if new Java test classes have inheritance.
2377 Either the new test class is JUnit3 test or it is a JUnit4 test class
2378 with a base class, either case is undesirable.
2379 """
2380 class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
2381
2382 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492383 x, white_list=[r'.*Test\.java$'], black_list=None)
Yoland Yanb92fa522017-08-28 17:37:062384 errors = []
2385 for f in input_api.AffectedFiles(sources):
2386 if not f.OldContents():
2387 class_declaration_start_flag = False
2388 for line_num, line in f.ChangedContents():
2389 if class_declaration_pattern.search(line):
2390 class_declaration_start_flag = True
2391 if class_declaration_start_flag and ' extends ' in line:
2392 errors.append('%s:%d' % (f.LocalPath(), line_num))
2393 if '{' in line:
2394 class_declaration_start_flag = False
2395
2396 results = []
2397 if errors:
2398 results.append(output_api.PresubmitPromptWarning(
2399 'The newly created files include Test classes that inherits from base'
2400 ' class. Please do not use inheritance in JUnit4 tests or add new'
2401 ' JUnit3 tests. Contact [email protected] if you have any'
2402 ' questions.', errors))
2403 return results
2404
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202405
yolandyan45001472016-12-21 21:12:422406def _CheckAndroidTestAnnotationUsage(input_api, output_api):
2407 """Checks that android.test.suitebuilder.annotation.* is no longer used."""
2408 deprecated_annotation_import_pattern = input_api.re.compile(
2409 r'^import android\.test\.suitebuilder\.annotation\..*;',
2410 input_api.re.MULTILINE)
2411 sources = lambda x: input_api.FilterSourceFile(
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492412 x, white_list=[r'.*\.java$'], black_list=None)
yolandyan45001472016-12-21 21:12:422413 errors = []
2414 for f in input_api.AffectedFiles(sources):
2415 for line_num, line in f.ChangedContents():
2416 if deprecated_annotation_import_pattern.search(line):
2417 errors.append("%s:%d" % (f.LocalPath(), line_num))
2418
2419 results = []
2420 if errors:
2421 results.append(output_api.PresubmitError(
2422 'Annotations in android.test.suitebuilder.annotation have been'
2423 ' deprecated since API level 24. Please use android.support.test.filters'
2424 ' from //third_party/android_support_test_runner:runner_java instead.'
2425 ' Contact [email protected] if you have any questions.', errors))
2426 return results
2427
2428
agrieve7b6479d82015-10-07 14:24:222429def _CheckAndroidNewMdpiAssetLocation(input_api, output_api):
2430 """Checks if MDPI assets are placed in a correct directory."""
2431 file_filter = lambda f: (f.LocalPath().endswith('.png') and
2432 ('/res/drawable/' in f.LocalPath() or
2433 '/res/drawable-ldrtl/' in f.LocalPath()))
2434 errors = []
2435 for f in input_api.AffectedFiles(include_deletes=False,
2436 file_filter=file_filter):
2437 errors.append(' %s' % f.LocalPath())
2438
2439 results = []
2440 if errors:
2441 results.append(output_api.PresubmitError(
2442 'MDPI assets should be placed in /res/drawable-mdpi/ or '
2443 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and'
2444 '/res/drawable-ldrtl/.\n'
2445 'Contact [email protected] if you have questions.', errors))
2446 return results
2447
2448
Nate Fischer535972b2017-09-16 01:06:182449def _CheckAndroidWebkitImports(input_api, output_api):
2450 """Checks that code uses org.chromium.base.Callback instead of
2451 android.widget.ValueCallback except in the WebView glue layer.
2452 """
2453 valuecallback_import_pattern = input_api.re.compile(
2454 r'^import android\.webkit\.ValueCallback;$')
2455
2456 errors = []
2457
2458 sources = lambda affected_file: input_api.FilterSourceFile(
2459 affected_file,
2460 black_list=(_EXCLUDED_PATHS +
2461 _TEST_CODE_EXCLUDED_PATHS +
2462 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042463 (r'^android_webview[\\/]glue[\\/].*',)),
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492464 white_list=[r'.*\.java$'])
Nate Fischer535972b2017-09-16 01:06:182465
2466 for f in input_api.AffectedSourceFiles(sources):
2467 for line_num, line in f.ChangedContents():
2468 if valuecallback_import_pattern.search(line):
2469 errors.append("%s:%d" % (f.LocalPath(), line_num))
2470
2471 results = []
2472
2473 if errors:
2474 results.append(output_api.PresubmitError(
2475 'android.webkit.ValueCallback usage is detected outside of the glue'
2476 ' layer. To stay compatible with the support library, android.webkit.*'
2477 ' classes should only be used inside the glue layer and'
2478 ' org.chromium.base.Callback should be used instead.',
2479 errors))
2480
2481 return results
2482
2483
agrievef32bcc72016-04-04 14:57:402484class PydepsChecker(object):
2485 def __init__(self, input_api, pydeps_files):
2486 self._file_cache = {}
2487 self._input_api = input_api
2488 self._pydeps_files = pydeps_files
2489
2490 def _LoadFile(self, path):
2491 """Returns the list of paths within a .pydeps file relative to //."""
2492 if path not in self._file_cache:
2493 with open(path) as f:
2494 self._file_cache[path] = f.read()
2495 return self._file_cache[path]
2496
2497 def _ComputeNormalizedPydepsEntries(self, pydeps_path):
2498 """Returns an interable of paths within the .pydep, relativized to //."""
2499 os_path = self._input_api.os_path
2500 pydeps_dir = os_path.dirname(pydeps_path)
2501 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines()
2502 if not l.startswith('*'))
2503 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries)
2504
2505 def _CreateFilesToPydepsMap(self):
2506 """Returns a map of local_path -> list_of_pydeps."""
2507 ret = {}
2508 for pydep_local_path in self._pydeps_files:
2509 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path):
2510 ret.setdefault(path, []).append(pydep_local_path)
2511 return ret
2512
2513 def ComputeAffectedPydeps(self):
2514 """Returns an iterable of .pydeps files that might need regenerating."""
2515 affected_pydeps = set()
2516 file_to_pydeps_map = None
2517 for f in self._input_api.AffectedFiles(include_deletes=True):
2518 local_path = f.LocalPath()
2519 if local_path == 'DEPS':
2520 return self._pydeps_files
2521 elif local_path.endswith('.pydeps'):
2522 if local_path in self._pydeps_files:
2523 affected_pydeps.add(local_path)
2524 elif local_path.endswith('.py'):
2525 if file_to_pydeps_map is None:
2526 file_to_pydeps_map = self._CreateFilesToPydepsMap()
2527 affected_pydeps.update(file_to_pydeps_map.get(local_path, ()))
2528 return affected_pydeps
2529
2530 def DetermineIfStale(self, pydeps_path):
2531 """Runs print_python_deps.py to see if the files is stale."""
phajdan.jr0d9878552016-11-04 10:49:412532 import difflib
John Budorick47ca3fe2018-02-10 00:53:102533 import os
2534
agrievef32bcc72016-04-04 14:57:402535 old_pydeps_data = self._LoadFile(pydeps_path).splitlines()
2536 cmd = old_pydeps_data[1][1:].strip()
John Budorick47ca3fe2018-02-10 00:53:102537 env = dict(os.environ)
2538 env['PYTHONDONTWRITEBYTECODE'] = '1'
agrievef32bcc72016-04-04 14:57:402539 new_pydeps_data = self._input_api.subprocess.check_output(
John Budorick47ca3fe2018-02-10 00:53:102540 cmd + ' --output ""', shell=True, env=env)
phajdan.jr0d9878552016-11-04 10:49:412541 old_contents = old_pydeps_data[2:]
2542 new_contents = new_pydeps_data.splitlines()[2:]
agrievef32bcc72016-04-04 14:57:402543 if old_pydeps_data[2:] != new_pydeps_data.splitlines()[2:]:
phajdan.jr0d9878552016-11-04 10:49:412544 return cmd, '\n'.join(difflib.context_diff(old_contents, new_contents))
agrievef32bcc72016-04-04 14:57:402545
2546
2547def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None):
2548 """Checks if a .pydeps file needs to be regenerated."""
John Chencde89192018-01-27 21:18:402549 # This check is for Python dependency lists (.pydeps files), and involves
2550 # paths not only in the PRESUBMIT.py, but also in the .pydeps files. It
2551 # doesn't work on Windows and Mac, so skip it on other platforms.
agrieve9bc4200b2016-05-04 16:33:282552 if input_api.platform != 'linux2':
agrievebb9c5b472016-04-22 15:13:002553 return []
Mostyn Bramley-Moore6b427322017-12-21 22:11:022554 # TODO(agrieve): Update when there's a better way to detect
2555 # this: crbug.com/570091
agrievef32bcc72016-04-04 14:57:402556 is_android = input_api.os_path.exists('third_party/android_tools')
2557 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES
2558 results = []
2559 # First, check for new / deleted .pydeps.
2560 for f in input_api.AffectedFiles(include_deletes=True):
Zhiling Huang45cabf32018-03-10 00:50:032561 # Check whether we are running the presubmit check for a file in src.
2562 # f.LocalPath is relative to repo (src, or internal repo).
2563 # os_path.exists is relative to src repo.
2564 # Therefore if os_path.exists is true, it means f.LocalPath is relative
2565 # to src and we can conclude that the pydeps is in src.
2566 if input_api.os_path.exists(f.LocalPath()):
2567 if f.LocalPath().endswith('.pydeps'):
2568 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES:
2569 results.append(output_api.PresubmitError(
2570 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2571 'remove %s' % f.LocalPath()))
2572 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES:
2573 results.append(output_api.PresubmitError(
2574 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to '
2575 'include %s' % f.LocalPath()))
agrievef32bcc72016-04-04 14:57:402576
2577 if results:
2578 return results
2579
2580 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files)
2581
2582 for pydep_path in checker.ComputeAffectedPydeps():
2583 try:
phajdan.jr0d9878552016-11-04 10:49:412584 result = checker.DetermineIfStale(pydep_path)
2585 if result:
2586 cmd, diff = result
agrievef32bcc72016-04-04 14:57:402587 results.append(output_api.PresubmitError(
phajdan.jr0d9878552016-11-04 10:49:412588 'File is stale: %s\nDiff (apply to fix):\n%s\n'
2589 'To regenerate, run:\n\n %s' %
2590 (pydep_path, diff, cmd)))
agrievef32bcc72016-04-04 14:57:402591 except input_api.subprocess.CalledProcessError as error:
2592 return [output_api.PresubmitError('Error running: %s' % error.cmd,
2593 long_text=error.output)]
2594
2595 return results
2596
2597
glidere61efad2015-02-18 17:39:432598def _CheckSingletonInHeaders(input_api, output_api):
2599 """Checks to make sure no header files have |Singleton<|."""
2600 def FileFilter(affected_file):
2601 # It's ok for base/memory/singleton.h to have |Singleton<|.
2602 black_list = (_EXCLUDED_PATHS +
2603 input_api.DEFAULT_BLACK_LIST +
Egor Paskoce145c42018-09-28 19:31:042604 (r"^base[\\/]memory[\\/]singleton\.h$",
2605 r"^net[\\/]quic[\\/]platform[\\/]impl[\\/]"
Michael Warrese4451492018-03-07 04:42:472606 r"quic_singleton_impl\.h$"))
glidere61efad2015-02-18 17:39:432607 return input_api.FilterSourceFile(affected_file, black_list=black_list)
2608
sergeyu34d21222015-09-16 00:11:442609 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
glidere61efad2015-02-18 17:39:432610 files = []
2611 for f in input_api.AffectedSourceFiles(FileFilter):
2612 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
2613 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
2614 contents = input_api.ReadFile(f)
2615 for line in contents.splitlines(False):
oysteinec430ad42015-10-22 20:55:242616 if (not line.lstrip().startswith('//') and # Strip C++ comment.
glidere61efad2015-02-18 17:39:432617 pattern.search(line)):
2618 files.append(f)
2619 break
2620
2621 if files:
yolandyandaabc6d2016-04-18 18:29:392622 return [output_api.PresubmitError(
sergeyu34d21222015-09-16 00:11:442623 'Found base::Singleton<T> in the following header files.\n' +
glidere61efad2015-02-18 17:39:432624 'Please move them to an appropriate source file so that the ' +
2625 'template gets instantiated in a single compilation unit.',
2626 files) ]
2627 return []
2628
2629
[email protected]fd20b902014-05-09 02:14:532630_DEPRECATED_CSS = [
2631 # Values
2632 ( "-webkit-box", "flex" ),
2633 ( "-webkit-inline-box", "inline-flex" ),
2634 ( "-webkit-flex", "flex" ),
2635 ( "-webkit-inline-flex", "inline-flex" ),
2636 ( "-webkit-min-content", "min-content" ),
2637 ( "-webkit-max-content", "max-content" ),
2638
2639 # Properties
2640 ( "-webkit-background-clip", "background-clip" ),
2641 ( "-webkit-background-origin", "background-origin" ),
2642 ( "-webkit-background-size", "background-size" ),
2643 ( "-webkit-box-shadow", "box-shadow" ),
dbeam6936c67f2017-01-19 01:51:442644 ( "-webkit-user-select", "user-select" ),
[email protected]fd20b902014-05-09 02:14:532645
2646 # Functions
2647 ( "-webkit-gradient", "gradient" ),
2648 ( "-webkit-repeating-gradient", "repeating-gradient" ),
2649 ( "-webkit-linear-gradient", "linear-gradient" ),
2650 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
2651 ( "-webkit-radial-gradient", "radial-gradient" ),
2652 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
2653]
2654
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202655
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492656# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242657def _CheckNoDeprecatedCss(input_api, output_api):
[email protected]fd20b902014-05-09 02:14:532658 """ Make sure that we don't use deprecated CSS
[email protected]9a48e3f82014-05-22 00:06:252659 properties, functions or values. Our external
mdjonesae0286c32015-06-10 18:10:342660 documentation and iOS CSS for dom distiller
2661 (reader mode) are ignored by the hooks as it
[email protected]9a48e3f82014-05-22 00:06:252662 needs to be consumed by WebKit. """
[email protected]fd20b902014-05-09 02:14:532663 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492664 file_inclusion_pattern = [r".+\.css$"]
[email protected]9a48e3f82014-05-22 00:06:252665 black_list = (_EXCLUDED_PATHS +
2666 _TEST_CODE_EXCLUDED_PATHS +
2667 input_api.DEFAULT_BLACK_LIST +
2668 (r"^chrome/common/extensions/docs",
2669 r"^chrome/docs",
mdjonesae0286c32015-06-10 18:10:342670 r"^components/dom_distiller/core/css/distilledpage_ios.css",
sdefresne6308d7f2016-02-15 09:38:442671 r"^components/neterror/resources/neterror.css",
[email protected]9a48e3f82014-05-22 00:06:252672 r"^native_client_sdk"))
2673 file_filter = lambda f: input_api.FilterSourceFile(
2674 f, white_list=file_inclusion_pattern, black_list=black_list)
[email protected]fd20b902014-05-09 02:14:532675 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2676 for line_num, line in fpath.ChangedContents():
2677 for (deprecated_value, value) in _DEPRECATED_CSS:
dbeam070cfe62014-10-22 06:44:022678 if deprecated_value in line:
[email protected]fd20b902014-05-09 02:14:532679 results.append(output_api.PresubmitError(
2680 "%s:%d: Use of deprecated CSS %s, use %s instead" %
2681 (fpath.LocalPath(), line_num, deprecated_value, value)))
2682 return results
2683
mohan.reddyf21db962014-10-16 12:26:472684
dbeam070cfe62014-10-22 06:44:022685_DEPRECATED_JS = [
2686 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
2687 ( "__defineGetter__", "Object.defineProperty" ),
2688 ( "__defineSetter__", "Object.defineProperty" ),
2689]
2690
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202691
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492692# TODO: add unit tests
dbeam1ec68ac2016-12-15 05:22:242693def _CheckNoDeprecatedJs(input_api, output_api):
dbeam070cfe62014-10-22 06:44:022694 """Make sure that we don't use deprecated JS in Chrome code."""
2695 results = []
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492696 file_inclusion_pattern = [r".+\.js$"] # TODO(dbeam): .html?
dbeam070cfe62014-10-22 06:44:022697 black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
2698 input_api.DEFAULT_BLACK_LIST)
2699 file_filter = lambda f: input_api.FilterSourceFile(
2700 f, white_list=file_inclusion_pattern, black_list=black_list)
2701 for fpath in input_api.AffectedFiles(file_filter=file_filter):
2702 for lnum, line in fpath.ChangedContents():
2703 for (deprecated, replacement) in _DEPRECATED_JS:
2704 if deprecated in line:
2705 results.append(output_api.PresubmitError(
2706 "%s:%d: Use of deprecated JS %s, use %s instead" %
2707 (fpath.LocalPath(), lnum, deprecated, replacement)))
2708 return results
2709
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202710
dpapadd651231d82017-07-21 02:44:472711def _CheckForRiskyJsArrowFunction(line_number, line):
2712 if ' => ' in line:
2713 return "line %d, is using an => (arrow) function\n %s\n" % (
2714 line_number, line)
2715 return ''
2716
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202717
dpapadd651231d82017-07-21 02:44:472718def _CheckForRiskyJsConstLet(input_api, line_number, line):
2719 if input_api.re.match('^\s*(const|let)\s', line):
2720 return "line %d, is using const/let keyword\n %s\n" % (
2721 line_number, line)
2722 return ''
dbeam070cfe62014-10-22 06:44:022723
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202724
dbeam1ec68ac2016-12-15 05:22:242725def _CheckForRiskyJsFeatures(input_api, output_api):
Wei-Yin Chen (陳威尹)dca729a2018-07-31 21:35:492726 maybe_ios_js = [r"^(ios|components|ui\/webui\/resources)\/.+\.js$"]
Steven Bennetts90545f3cb2017-08-14 18:11:002727 # 'ui/webui/resources/cr_components are not allowed on ios'
2728 not_ios_filter = (r".*ui\/webui\/resources\/cr_components.*", )
Steven Bennetts9c7e3c22017-08-02 19:10:572729 file_filter = lambda f: input_api.FilterSourceFile(f, white_list=maybe_ios_js,
Steven Bennetts90545f3cb2017-08-14 18:11:002730 black_list=not_ios_filter)
dpapadd651231d82017-07-21 02:44:472731 results = []
dbeam1ec68ac2016-12-15 05:22:242732 for f in input_api.AffectedFiles(file_filter=file_filter):
dpapadd651231d82017-07-21 02:44:472733 arrow_error_lines = []
2734 const_let_error_lines = []
dbeam1ec68ac2016-12-15 05:22:242735 for lnum, line in f.ChangedContents():
dpapadd651231d82017-07-21 02:44:472736 arrow_error_lines += filter(None, [
2737 _CheckForRiskyJsArrowFunction(lnum, line),
2738 ])
dbeam1ec68ac2016-12-15 05:22:242739
dpapadd651231d82017-07-21 02:44:472740 const_let_error_lines += filter(None, [
2741 _CheckForRiskyJsConstLet(input_api, lnum, line),
2742 ])
dbeam1ec68ac2016-12-15 05:22:242743
dpapadd651231d82017-07-21 02:44:472744 if arrow_error_lines:
2745 arrow_error_lines = map(
2746 lambda e: "%s:%s" % (f.LocalPath(), e), arrow_error_lines)
2747 results.append(
2748 output_api.PresubmitPromptWarning('\n'.join(arrow_error_lines + [
2749"""
2750Use of => (arrow) operator detected in:
dbeam1ec68ac2016-12-15 05:22:242751%s
2752Please ensure your code does not run on iOS9 (=> (arrow) does not work there).
Dan Beamdd2470c2018-10-03 00:07:222753https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#Arrow-Functions
dpapadd651231d82017-07-21 02:44:472754""" % f.LocalPath()
2755 ])))
dbeam1ec68ac2016-12-15 05:22:242756
dpapadd651231d82017-07-21 02:44:472757 if const_let_error_lines:
2758 const_let_error_lines = map(
2759 lambda e: "%s:%s" % (f.LocalPath(), e), const_let_error_lines)
2760 results.append(
2761 output_api.PresubmitPromptWarning('\n'.join(const_let_error_lines + [
2762"""
2763Use of const/let keywords detected in:
2764%s
2765Please ensure your code does not run on iOS9 because const/let is not fully
2766supported.
Dan Beamdd2470c2018-10-03 00:07:222767https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#let-Block_Scoped-Variables
2768https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#const-Block_Scoped-Constants
dpapadd651231d82017-07-21 02:44:472769""" % f.LocalPath()
2770 ])))
2771
2772 return results
dbeam1ec68ac2016-12-15 05:22:242773
Wei-Yin Chen (陳威尹)f799d442018-07-31 02:20:202774
rlanday6802cf632017-05-30 17:48:362775def _CheckForRelativeIncludes(input_api, output_api):
2776 # Need to set the sys.path so PRESUBMIT_test.py runs properly
2777 import sys
2778 original_sys_path = sys.path
2779 try:
2780 sys.path = sys.path + [input_api.os_path.join(
2781 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
2782 from cpp_checker import CppChecker
2783 finally:
2784 # Restore sys.path to what it was before.
2785 sys.path = original_sys_path
2786
2787 bad_files = {}
2788 for f in input_api.AffectedFiles(include_deletes=False):
2789 if (f.LocalPath().startswith('third_party') and
2790 not f.LocalPath().startswith('third_party/WebKit') and
2791 not f.LocalPath().startswith('third_party\\WebKit')):
2792 continue
2793
2794 if not CppChecker.IsCppFile(f.LocalPath()):
2795 continue
2796
Vaclav Brozekd5de76a2018-03-17 07:57:502797 relative_includes = [line for _, line in f.ChangedContents()
rlanday6802cf632017-05-30 17:48:362798 if "#include" in line and "../" in line]
2799 if not relative_includes:
2800 continue
2801 bad_files[f.LocalPath()] = relative_includes
2802
2803 if not bad_files:
2804 return []
2805
2806 error_descriptions = []
2807 for file_path, bad_lines in bad_files.iteritems():
2808 error_description = file_path
2809 for line in bad_lines:
2810 error_description += '\n ' + line
2811 error_descriptions.append(error_description)
2812
2813 results = []
2814 results.append(output_api.PresubmitError(
2815 'You added one or more relative #include paths (including "../").\n'
2816 'These shouldn\'t be used because they can be used to include headers\n'
2817 'from code that\'s not correctly specified as a dependency in the\n'
2818 'relevant BUILD.gn file(s).',
2819 error_descriptions))
2820
2821 return results
2822
Takeshi Yoshinoe387aa32017-08-02 13:16:132823
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202824def _CheckWatchlistDefinitionsEntrySyntax(key, value, ast):
2825 if not isinstance(key, ast.Str):
2826 return 'Key at line %d must be a string literal' % key.lineno
2827 if not isinstance(value, ast.Dict):
2828 return 'Value at line %d must be a dict' % value.lineno
2829 if len(value.keys) != 1:
2830 return 'Dict at line %d must have single entry' % value.lineno
2831 if not isinstance(value.keys[0], ast.Str) or value.keys[0].s != 'filepath':
2832 return (
2833 'Entry at line %d must have a string literal \'filepath\' as key' %
2834 value.lineno)
2835 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132836
Takeshi Yoshinoe387aa32017-08-02 13:16:132837
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202838def _CheckWatchlistsEntrySyntax(key, value, ast):
2839 if not isinstance(key, ast.Str):
2840 return 'Key at line %d must be a string literal' % key.lineno
2841 if not isinstance(value, ast.List):
2842 return 'Value at line %d must be a list' % value.lineno
2843 return None
Takeshi Yoshinoe387aa32017-08-02 13:16:132844
Takeshi Yoshinoe387aa32017-08-02 13:16:132845
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202846def _CheckWATCHLISTSEntries(wd_dict, w_dict, ast):
2847 mismatch_template = (
2848 'Mismatch between WATCHLIST_DEFINITIONS entry (%s) and WATCHLISTS '
2849 'entry (%s)')
Takeshi Yoshinoe387aa32017-08-02 13:16:132850
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202851 i = 0
2852 last_key = ''
2853 while True:
2854 if i >= len(wd_dict.keys):
2855 if i >= len(w_dict.keys):
2856 return None
2857 return mismatch_template % ('missing', 'line %d' % w_dict.keys[i].lineno)
2858 elif i >= len(w_dict.keys):
2859 return (
2860 mismatch_template % ('line %d' % wd_dict.keys[i].lineno, 'missing'))
Takeshi Yoshinoe387aa32017-08-02 13:16:132861
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202862 wd_key = wd_dict.keys[i]
2863 w_key = w_dict.keys[i]
Takeshi Yoshinoe387aa32017-08-02 13:16:132864
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202865 result = _CheckWatchlistDefinitionsEntrySyntax(
2866 wd_key, wd_dict.values[i], ast)
2867 if result is not None:
2868 return 'Bad entry in WATCHLIST_DEFINITIONS dict: %s' % result
Takeshi Yoshinoe387aa32017-08-02 13:16:132869
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202870 result = _CheckWatchlistsEntrySyntax(w_key, w_dict.values[i], ast)
2871 if result is not None:
2872 return 'Bad entry in WATCHLISTS dict: %s' % result
2873
2874 if wd_key.s != w_key.s:
2875 return mismatch_template % (
2876 '%s at line %d' % (wd_key.s, wd_key.lineno),
2877 '%s at line %d' % (w_key.s, w_key.lineno))
2878
2879 if wd_key.s < last_key:
2880 return (
2881 'WATCHLISTS dict is not sorted lexicographically at line %d and %d' %
2882 (wd_key.lineno, w_key.lineno))
2883 last_key = wd_key.s
2884
2885 i = i + 1
2886
2887
2888def _CheckWATCHLISTSSyntax(expression, ast):
2889 if not isinstance(expression, ast.Expression):
2890 return 'WATCHLISTS file must contain a valid expression'
2891 dictionary = expression.body
2892 if not isinstance(dictionary, ast.Dict) or len(dictionary.keys) != 2:
2893 return 'WATCHLISTS file must have single dict with exactly two entries'
2894
2895 first_key = dictionary.keys[0]
2896 first_value = dictionary.values[0]
2897 second_key = dictionary.keys[1]
2898 second_value = dictionary.values[1]
2899
2900 if (not isinstance(first_key, ast.Str) or
2901 first_key.s != 'WATCHLIST_DEFINITIONS' or
2902 not isinstance(first_value, ast.Dict)):
2903 return (
2904 'The first entry of the dict in WATCHLISTS file must be '
2905 'WATCHLIST_DEFINITIONS dict')
2906
2907 if (not isinstance(second_key, ast.Str) or
2908 second_key.s != 'WATCHLISTS' or
2909 not isinstance(second_value, ast.Dict)):
2910 return (
2911 'The second entry of the dict in WATCHLISTS file must be '
2912 'WATCHLISTS dict')
2913
2914 return _CheckWATCHLISTSEntries(first_value, second_value, ast)
Takeshi Yoshinoe387aa32017-08-02 13:16:132915
2916
2917def _CheckWATCHLISTS(input_api, output_api):
2918 for f in input_api.AffectedFiles(include_deletes=False):
2919 if f.LocalPath() == 'WATCHLISTS':
2920 contents = input_api.ReadFile(f, 'r')
2921
2922 try:
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202923 # First, make sure that it can be evaluated.
Takeshi Yoshinoe387aa32017-08-02 13:16:132924 input_api.ast.literal_eval(contents)
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202925 # Get an AST tree for it and scan the tree for detailed style checking.
2926 expression = input_api.ast.parse(
2927 contents, filename='WATCHLISTS', mode='eval')
2928 except ValueError as e:
2929 return [output_api.PresubmitError(
2930 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2931 except SyntaxError as e:
2932 return [output_api.PresubmitError(
2933 'Cannot parse WATCHLISTS file', long_text=repr(e))]
2934 except TypeError as e:
2935 return [output_api.PresubmitError(
2936 'Cannot parse WATCHLISTS file', long_text=repr(e))]
Takeshi Yoshinoe387aa32017-08-02 13:16:132937
Takeshi Yoshino3a8f9cb52017-08-10 11:32:202938 result = _CheckWATCHLISTSSyntax(expression, input_api.ast)
2939 if result is not None:
2940 return [output_api.PresubmitError(result)]
2941 break
Takeshi Yoshinoe387aa32017-08-02 13:16:132942
2943 return []
2944
2945
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:192946def _CheckNewHeaderWithoutGnChange(input_api, output_api):
2947 """Checks that newly added header files have corresponding GN changes.
2948 Note that this is only a heuristic. To be precise, run script:
2949 build/check_gn_headers.py.
2950 """
2951
2952 def headers(f):
2953 return input_api.FilterSourceFile(
2954 f, white_list=(r'.+%s' % _HEADER_EXTENSIONS, ))
2955
2956 new_headers = []
2957 for f in input_api.AffectedSourceFiles(headers):
2958 if f.Action() != 'A':
2959 continue
2960 new_headers.append(f.LocalPath())
2961
2962 def gn_files(f):
2963 return input_api.FilterSourceFile(f, white_list=(r'.+\.gn', ))
2964
2965 all_gn_changed_contents = ''
2966 for f in input_api.AffectedSourceFiles(gn_files):
2967 for _, line in f.ChangedContents():
2968 all_gn_changed_contents += line
2969
2970 problems = []
2971 for header in new_headers:
2972 basename = input_api.os_path.basename(header)
2973 if basename not in all_gn_changed_contents:
2974 problems.append(header)
2975
2976 if problems:
2977 return [output_api.PresubmitPromptWarning(
2978 'Missing GN changes for new header files', items=sorted(problems),
2979 long_text='Please double check whether newly added header files need '
2980 'corresponding changes in gn or gni files.\nThis checking is only a '
2981 'heuristic. Run build/check_gn_headers.py to be precise.\n'
2982 'Read https://crbug.com/661774 for more info.')]
2983 return []
2984
2985
dgnaa68d5e2015-06-10 10:08:222986def _AndroidSpecificOnUploadChecks(input_api, output_api):
2987 """Groups checks that target android code."""
2988 results = []
dgnaa68d5e2015-06-10 10:08:222989 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
agrieve7b6479d82015-10-07 14:24:222990 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api))
dskiba88634f4e2015-08-14 23:03:292991 results.extend(_CheckAndroidToastUsage(input_api, output_api))
Yoland Yanb92fa522017-08-28 17:37:062992 results.extend(_CheckAndroidTestJUnitInheritance(input_api, output_api))
2993 results.extend(_CheckAndroidTestJUnitFrameworkImport(input_api, output_api))
yolandyan45001472016-12-21 21:12:422994 results.extend(_CheckAndroidTestAnnotationUsage(input_api, output_api))
Nate Fischer535972b2017-09-16 01:06:182995 results.extend(_CheckAndroidWebkitImports(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:222996 return results
2997
2998
[email protected]22c9bd72011-03-27 16:47:392999def _CommonChecks(input_api, output_api):
3000 """Checks common to both upload and commit."""
3001 results = []
3002 results.extend(input_api.canned_checks.PanProjectChecks(
[email protected]3de922f2013-12-20 13:27:383003 input_api, output_api,
qyearsleyfa2cfcf82016-12-15 18:03:543004 excluded_paths=_EXCLUDED_PATHS))
Eric Boren6fd2b932018-01-25 15:05:083005
3006 author = input_api.change.author_email
3007 if author and author not in _KNOWN_ROBOTS:
3008 results.extend(
3009 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
3010
[email protected]55459852011-08-10 15:17:193011 results.extend(
[email protected]760deea2013-12-10 19:33:493012 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
Vaclav Brozek7dbc28c2018-03-27 08:35:233013 results.extend(
3014 _CheckNoProductionCodeUsingTestOnlyFunctionsJava(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:543015 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:183016 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
Dominic Battre033531052018-09-24 15:45:343017 results.extend(_CheckNoDISABLETypoInTests(input_api, output_api))
danakj61c1aa22015-10-26 19:55:523018 results.extend(_CheckDCHECK_IS_ONHasBraces(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:223019 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:443020 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:593021 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:063022 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:123023 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:183024 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:223025 results.extend(_CheckFilePermissions(input_api, output_api))
robertocn832f5992017-01-04 19:01:303026 results.extend(_CheckTeamTags(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:493027 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:033028 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:493029 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]06e6d0ff2012-12-11 01:36:443030 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
[email protected]d2530012013-01-25 16:39:273031 results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
Kent Tamura5a8755d2017-06-29 23:37:073032 results.extend(_CheckBuildConfigMacrosWithoutInclude(input_api, output_api))
[email protected]b00342e7f2013-03-26 16:21:543033 results.extend(_CheckForInvalidOSMacros(input_api, output_api))
lliabraa35bab3932014-10-01 12:16:443034 results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
yolandyandaabc6d2016-04-18 18:29:393035 results.extend(_CheckFlakyTestUsage(input_api, output_api))
[email protected]e871964c2013-05-13 14:14:553036 results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
[email protected]9f919cc2013-07-31 03:04:043037 results.extend(
3038 input_api.canned_checks.CheckChangeHasNoTabs(
3039 input_api,
3040 output_api,
3041 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
[email protected]85218562013-11-22 07:41:403042 results.extend(_CheckSpamLogging(input_api, output_api))
[email protected]49aa76a2013-12-04 06:59:163043 results.extend(_CheckForAnonymousVariables(input_api, output_api))
[email protected]999261d2014-03-03 20:08:083044 results.extend(_CheckUserActionUpdate(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243045 results.extend(_CheckNoDeprecatedCss(input_api, output_api))
3046 results.extend(_CheckNoDeprecatedJs(input_api, output_api))
[email protected]99171a92014-06-03 08:44:473047 results.extend(_CheckParseErrors(input_api, output_api))
mlamouria82272622014-09-16 18:45:043048 results.extend(_CheckForIPCRules(input_api, output_api))
Stephen Martinis97a394142018-06-07 23:06:053049 results.extend(_CheckForLongPathnames(input_api, output_api))
Daniel Bratell8ba52722018-03-02 16:06:143050 results.extend(_CheckForIncludeGuards(input_api, output_api))
mostynbb639aca52015-01-07 20:31:233051 results.extend(_CheckForWindowsLineEndings(input_api, output_api))
glidere61efad2015-02-18 17:39:433052 results.extend(_CheckSingletonInHeaders(input_api, output_api))
agrievef32bcc72016-04-04 14:57:403053 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api))
wnwenbdc444e2016-05-25 13:44:153054 results.extend(_CheckJavaStyle(input_api, output_api))
dchenge07de812016-06-20 19:27:173055 results.extend(_CheckIpcOwners(input_api, output_api))
jbriance9e12f162016-11-25 07:57:503056 results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
dbeam1ec68ac2016-12-15 05:22:243057 results.extend(_CheckForRiskyJsFeatures(input_api, output_api))
rlanday6802cf632017-05-30 17:48:363058 results.extend(_CheckForRelativeIncludes(input_api, output_api))
Takeshi Yoshinoe387aa32017-08-02 13:16:133059 results.extend(_CheckWATCHLISTS(input_api, output_api))
Sergiy Byelozyorov366b6482017-11-06 18:20:433060 results.extend(input_api.RunTests(
3061 input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143062 results.extend(_CheckTranslationScreenshots(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:243063
Vaclav Brozekcdc7defb2018-03-20 09:54:353064 for f in input_api.AffectedFiles():
3065 path, name = input_api.os_path.split(f.LocalPath())
3066 if name == 'PRESUBMIT.py':
3067 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
Caleb Rouleaua6117be2018-05-11 20:10:003068 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
3069 if f.Action() != 'D' and input_api.os_path.exists(test_file):
Dirk Pranke38557312018-04-18 00:53:073070 # The PRESUBMIT.py file (and the directory containing it) might
3071 # have been affected by being moved or removed, so only try to
3072 # run the tests if they still exist.
3073 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
3074 input_api, output_api, full_path,
3075 whitelist=[r'^PRESUBMIT_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:393076 return results
[email protected]1f7b4172010-01-28 01:17:343077
[email protected]b337cb5b2011-01-23 21:24:053078
[email protected]b8079ae4a2012-12-05 19:56:493079def _CheckPatchFiles(input_api, output_api):
3080 problems = [f.LocalPath() for f in input_api.AffectedFiles()
3081 if f.LocalPath().endswith(('.orig', '.rej'))]
3082 if problems:
3083 return [output_api.PresubmitError(
3084 "Don't commit .rej and .orig files.", problems)]
[email protected]2fdd1f362013-01-16 03:56:033085 else:
3086 return []
[email protected]b8079ae4a2012-12-05 19:56:493087
3088
Kent Tamura5a8755d2017-06-29 23:37:073089def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
Kent Tamura79ef8f82017-07-18 00:00:213090 # Excludes OS_CHROMEOS, which is not defined in build_config.h.
3091 macro_re = input_api.re.compile(r'^\s*#(el)?if.*\bdefined\(((OS_(?!CHROMEOS)|'
3092 'COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
Kent Tamura5a8755d2017-06-29 23:37:073093 include_re = input_api.re.compile(
3094 r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
3095 extension_re = input_api.re.compile(r'\.[a-z]+$')
3096 errors = []
3097 for f in input_api.AffectedFiles():
3098 if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
3099 continue
3100 found_line_number = None
3101 found_macro = None
3102 for line_num, line in f.ChangedContents():
3103 match = macro_re.search(line)
3104 if match:
3105 found_line_number = line_num
3106 found_macro = match.group(2)
3107 break
3108 if not found_line_number:
3109 continue
3110
3111 found_include = False
3112 for line in f.NewContents():
3113 if include_re.search(line):
3114 found_include = True
3115 break
3116 if found_include:
3117 continue
3118
3119 if not f.LocalPath().endswith('.h'):
3120 primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
3121 try:
3122 content = input_api.ReadFile(primary_header_path, 'r')
3123 if include_re.search(content):
3124 continue
3125 except IOError:
3126 pass
3127 errors.append('%s:%d %s macro is used without including build/'
3128 'build_config.h.'
3129 % (f.LocalPath(), found_line_number, found_macro))
3130 if errors:
3131 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
3132 return []
3133
3134
[email protected]b00342e7f2013-03-26 16:21:543135def _DidYouMeanOSMacro(bad_macro):
3136 try:
3137 return {'A': 'OS_ANDROID',
3138 'B': 'OS_BSD',
3139 'C': 'OS_CHROMEOS',
3140 'F': 'OS_FREEBSD',
3141 'L': 'OS_LINUX',
3142 'M': 'OS_MACOSX',
3143 'N': 'OS_NACL',
3144 'O': 'OS_OPENBSD',
3145 'P': 'OS_POSIX',
3146 'S': 'OS_SOLARIS',
3147 'W': 'OS_WIN'}[bad_macro[3].upper()]
3148 except KeyError:
3149 return ''
3150
3151
3152def _CheckForInvalidOSMacrosInFile(input_api, f):
3153 """Check for sensible looking, totally invalid OS macros."""
3154 preprocessor_statement = input_api.re.compile(r'^\s*#')
3155 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
3156 results = []
3157 for lnum, line in f.ChangedContents():
3158 if preprocessor_statement.search(line):
3159 for match in os_macro.finditer(line):
3160 if not match.group(1) in _VALID_OS_MACROS:
3161 good = _DidYouMeanOSMacro(match.group(1))
3162 did_you_mean = ' (did you mean %s?)' % good if good else ''
3163 results.append(' %s:%d %s%s' % (f.LocalPath(),
3164 lnum,
3165 match.group(1),
3166 did_you_mean))
3167 return results
3168
3169
3170def _CheckForInvalidOSMacros(input_api, output_api):
3171 """Check all affected files for invalid OS macros."""
3172 bad_macros = []
3173 for f in input_api.AffectedFiles():
ellyjones47654342016-05-06 15:50:473174 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css', '.md')):
[email protected]b00342e7f2013-03-26 16:21:543175 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
3176
3177 if not bad_macros:
3178 return []
3179
3180 return [output_api.PresubmitError(
3181 'Possibly invalid OS macro[s] found. Please fix your code\n'
3182 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
3183
lliabraa35bab3932014-10-01 12:16:443184
3185def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
3186 """Check all affected files for invalid "if defined" macros."""
3187 ALWAYS_DEFINED_MACROS = (
3188 "TARGET_CPU_PPC",
3189 "TARGET_CPU_PPC64",
3190 "TARGET_CPU_68K",
3191 "TARGET_CPU_X86",
3192 "TARGET_CPU_ARM",
3193 "TARGET_CPU_MIPS",
3194 "TARGET_CPU_SPARC",
3195 "TARGET_CPU_ALPHA",
3196 "TARGET_IPHONE_SIMULATOR",
3197 "TARGET_OS_EMBEDDED",
3198 "TARGET_OS_IPHONE",
3199 "TARGET_OS_MAC",
3200 "TARGET_OS_UNIX",
3201 "TARGET_OS_WIN32",
3202 )
3203 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
3204 results = []
3205 for lnum, line in f.ChangedContents():
3206 for match in ifdef_macro.finditer(line):
3207 if match.group(1) in ALWAYS_DEFINED_MACROS:
3208 always_defined = ' %s is always defined. ' % match.group(1)
3209 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
3210 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
3211 lnum,
3212 always_defined,
3213 did_you_mean))
3214 return results
3215
3216
3217def _CheckForInvalidIfDefinedMacros(input_api, output_api):
3218 """Check all affected files for invalid "if defined" macros."""
3219 bad_macros = []
3220 for f in input_api.AffectedFiles():
sdefresne4e1eccb32017-05-24 08:45:213221 if f.LocalPath().startswith('third_party/sqlite/'):
3222 continue
lliabraa35bab3932014-10-01 12:16:443223 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
3224 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
3225
3226 if not bad_macros:
3227 return []
3228
3229 return [output_api.PresubmitError(
3230 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
3231 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
3232 bad_macros)]
3233
3234
mlamouria82272622014-09-16 18:45:043235def _CheckForIPCRules(input_api, output_api):
3236 """Check for same IPC rules described in
3237 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
3238 """
3239 base_pattern = r'IPC_ENUM_TRAITS\('
3240 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
3241 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
3242
3243 problems = []
3244 for f in input_api.AffectedSourceFiles(None):
3245 local_path = f.LocalPath()
3246 if not local_path.endswith('.h'):
3247 continue
3248 for line_number, line in f.ChangedContents():
3249 if inclusion_pattern.search(line) and not comment_pattern.search(line):
3250 problems.append(
3251 '%s:%d\n %s' % (local_path, line_number, line.strip()))
3252
3253 if problems:
3254 return [output_api.PresubmitPromptWarning(
3255 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
3256 else:
3257 return []
3258
[email protected]b00342e7f2013-03-26 16:21:543259
Stephen Martinis97a394142018-06-07 23:06:053260def _CheckForLongPathnames(input_api, output_api):
3261 """Check to make sure no files being submitted have long paths.
3262 This causes issues on Windows.
3263 """
3264 problems = []
3265 for f in input_api.AffectedSourceFiles(None):
3266 local_path = f.LocalPath()
3267 # Windows has a path limit of 260 characters. Limit path length to 200 so
3268 # that we have some extra for the prefix on dev machines and the bots.
3269 if len(local_path) > 200:
3270 problems.append(local_path)
3271
3272 if problems:
3273 return [output_api.PresubmitError(_LONG_PATH_ERROR, problems)]
3274 else:
3275 return []
3276
3277
Daniel Bratell8ba52722018-03-02 16:06:143278def _CheckForIncludeGuards(input_api, output_api):
3279 """Check that header files have proper guards against multiple inclusion.
3280 If a file should not have such guards (and it probably should) then it
3281 should include the string "no-include-guard-because-multiply-included".
3282 """
Daniel Bratell6a75baef62018-06-04 10:04:453283 def is_chromium_header_file(f):
3284 # We only check header files under the control of the Chromium
3285 # project. That is, those outside third_party apart from
3286 # third_party/blink.
3287 file_with_path = input_api.os_path.normpath(f.LocalPath())
3288 return (file_with_path.endswith('.h') and
3289 (not file_with_path.startswith('third_party') or
3290 file_with_path.startswith(
3291 input_api.os_path.join('third_party', 'blink'))))
Daniel Bratell8ba52722018-03-02 16:06:143292
3293 def replace_special_with_underscore(string):
Olivier Robinbba137492018-07-30 11:31:343294 return input_api.re.sub(r'[+\\/.-]', '_', string)
Daniel Bratell8ba52722018-03-02 16:06:143295
3296 errors = []
3297
Daniel Bratell6a75baef62018-06-04 10:04:453298 for f in input_api.AffectedSourceFiles(is_chromium_header_file):
Daniel Bratell8ba52722018-03-02 16:06:143299 guard_name = None
3300 guard_line_number = None
3301 seen_guard_end = False
3302
3303 file_with_path = input_api.os_path.normpath(f.LocalPath())
3304 base_file_name = input_api.os_path.splitext(
3305 input_api.os_path.basename(file_with_path))[0]
3306 upper_base_file_name = base_file_name.upper()
3307
3308 expected_guard = replace_special_with_underscore(
3309 file_with_path.upper() + '_')
Daniel Bratell8ba52722018-03-02 16:06:143310
3311 # For "path/elem/file_name.h" we should really only accept
Daniel Bratell39b5b062018-05-16 18:09:573312 # PATH_ELEM_FILE_NAME_H_ per coding style. Unfortunately there
3313 # are too many (1000+) files with slight deviations from the
3314 # coding style. The most important part is that the include guard
3315 # is there, and that it's unique, not the name so this check is
3316 # forgiving for existing files.
Daniel Bratell8ba52722018-03-02 16:06:143317 #
3318 # As code becomes more uniform, this could be made stricter.
3319
3320 guard_name_pattern_list = [
3321 # Anything with the right suffix (maybe with an extra _).
3322 r'\w+_H__?',
3323
Daniel Bratell39b5b062018-05-16 18:09:573324 # To cover include guards with old Blink style.
Daniel Bratell8ba52722018-03-02 16:06:143325 r'\w+_h',
3326
3327 # Anything including the uppercase name of the file.
3328 r'\w*' + input_api.re.escape(replace_special_with_underscore(
3329 upper_base_file_name)) + r'\w*',
3330 ]
3331 guard_name_pattern = '|'.join(guard_name_pattern_list)
3332 guard_pattern = input_api.re.compile(
3333 r'#ifndef\s+(' + guard_name_pattern + ')')
3334
3335 for line_number, line in enumerate(f.NewContents()):
3336 if 'no-include-guard-because-multiply-included' in line:
3337 guard_name = 'DUMMY' # To not trigger check outside the loop.
3338 break
3339
3340 if guard_name is None:
3341 match = guard_pattern.match(line)
3342 if match:
3343 guard_name = match.group(1)
3344 guard_line_number = line_number
3345
Daniel Bratell39b5b062018-05-16 18:09:573346 # We allow existing files to use include guards whose names
Daniel Bratell6a75baef62018-06-04 10:04:453347 # don't match the chromium style guide, but new files should
3348 # get it right.
3349 if not f.OldContents():
Daniel Bratell39b5b062018-05-16 18:09:573350 if guard_name != expected_guard:
Daniel Bratell8ba52722018-03-02 16:06:143351 errors.append(output_api.PresubmitPromptWarning(
3352 'Header using the wrong include guard name %s' % guard_name,
3353 ['%s:%d' % (f.LocalPath(), line_number + 1)],
Daniel Bratell39b5b062018-05-16 18:09:573354 'Expected: %r\nFound: %r' % (expected_guard, guard_name)))
Daniel Bratell8ba52722018-03-02 16:06:143355 else:
3356 # The line after #ifndef should have a #define of the same name.
3357 if line_number == guard_line_number + 1:
3358 expected_line = '#define %s' % guard_name
3359 if line != expected_line:
3360 errors.append(output_api.PresubmitPromptWarning(
3361 'Missing "%s" for include guard' % expected_line,
3362 ['%s:%d' % (f.LocalPath(), line_number + 1)],
3363 'Expected: %r\nGot: %r' % (expected_line, line)))
3364
3365 if not seen_guard_end and line == '#endif // %s' % guard_name:
3366 seen_guard_end = True
3367 elif seen_guard_end:
3368 if line.strip() != '':
3369 errors.append(output_api.PresubmitPromptWarning(
3370 'Include guard %s not covering the whole file' % (
3371 guard_name), [f.LocalPath()]))
3372 break # Nothing else to check and enough to warn once.
3373
3374 if guard_name is None:
3375 errors.append(output_api.PresubmitPromptWarning(
3376 'Missing include guard %s' % expected_guard,
3377 [f.LocalPath()],
3378 'Missing include guard in %s\n'
3379 'Recommended name: %s\n'
3380 'This check can be disabled by having the string\n'
3381 'no-include-guard-because-multiply-included in the header.' %
3382 (f.LocalPath(), expected_guard)))
3383
3384 return errors
3385
3386
mostynbb639aca52015-01-07 20:31:233387def _CheckForWindowsLineEndings(input_api, output_api):
3388 """Check source code and known ascii text files for Windows style line
3389 endings.
3390 """
earthdok1b5e0ee2015-03-10 15:19:103391 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate)$'
mostynbb639aca52015-01-07 20:31:233392
3393 file_inclusion_pattern = (
3394 known_text_files,
3395 r'.+%s' % _IMPLEMENTATION_EXTENSIONS
3396 )
3397
mostynbb639aca52015-01-07 20:31:233398 problems = []
Andrew Grieve933d12e2017-10-30 20:22:533399 source_file_filter = lambda f: input_api.FilterSourceFile(
3400 f, white_list=file_inclusion_pattern, black_list=None)
3401 for f in input_api.AffectedSourceFiles(source_file_filter):
Vaclav Brozekd5de76a2018-03-17 07:57:503402 include_file = False
3403 for _, line in f.ChangedContents():
mostynbb639aca52015-01-07 20:31:233404 if line.endswith('\r\n'):
Vaclav Brozekd5de76a2018-03-17 07:57:503405 include_file = True
3406 if include_file:
3407 problems.append(f.LocalPath())
mostynbb639aca52015-01-07 20:31:233408
3409 if problems:
3410 return [output_api.PresubmitPromptWarning('Are you sure that you want '
3411 'these files to contain Windows style line endings?\n' +
3412 '\n'.join(problems))]
3413
3414 return []
3415
3416
Vaclav Brozekd5de76a2018-03-17 07:57:503417def _CheckSyslogUseWarning(input_api, output_api, source_file_filter=None):
pastarmovj89f7ee12016-09-20 14:58:133418 """Checks that all source files use SYSLOG properly."""
3419 syslog_files = []
3420 for f in input_api.AffectedSourceFiles(source_file_filter):
pastarmovj032ba5bc2017-01-12 10:41:563421 for line_number, line in f.ChangedContents():
3422 if 'SYSLOG' in line:
3423 syslog_files.append(f.LocalPath() + ':' + str(line_number))
3424
pastarmovj89f7ee12016-09-20 14:58:133425 if syslog_files:
3426 return [output_api.PresubmitPromptWarning(
3427 'Please make sure there are no privacy sensitive bits of data in SYSLOG'
3428 ' calls.\nFiles to check:\n', items=syslog_files)]
3429 return []
3430
3431
[email protected]1f7b4172010-01-28 01:17:343432def CheckChangeOnUpload(input_api, output_api):
3433 results = []
3434 results.extend(_CommonChecks(input_api, output_api))
tandriief664692014-09-23 14:51:473435 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
scottmg39b29952014-12-08 18:31:283436 results.extend(
jam93a6ee792017-02-08 23:59:223437 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
mcasasb7440c282015-02-04 14:52:193438 results.extend(_CheckUmaHistogramChanges(input_api, output_api))
dgnaa68d5e2015-06-10 10:08:223439 results.extend(_AndroidSpecificOnUploadChecks(input_api, output_api))
pastarmovj89f7ee12016-09-20 14:58:133440 results.extend(_CheckSyslogUseWarning(input_api, output_api))
estadee17314a02017-01-12 16:22:163441 results.extend(_CheckGoogleSupportAnswerUrl(input_api, output_api))
Vaclav Brozekea41ab22018-04-06 13:21:533442 results.extend(_CheckUniquePtr(input_api, output_api))
Wei-Yin Chen (陳威尹)c0624d002018-07-30 18:22:193443 results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543444 return results
[email protected]ca8d1982009-02-19 16:33:123445
3446
[email protected]1bfb8322014-04-23 01:02:413447def GetTryServerMasterForBot(bot):
3448 """Returns the Try Server master for the given bot.
3449
[email protected]0bb112362014-07-26 04:38:323450 It tries to guess the master from the bot name, but may still fail
3451 and return None. There is no longer a default master.
3452 """
3453 # Potentially ambiguous bot names are listed explicitly.
3454 master_map = {
tandriie5587792016-07-14 00:34:503455 'chromium_presubmit': 'master.tryserver.chromium.linux',
3456 'tools_build_presubmit': 'master.tryserver.chromium.linux',
[email protected]1bfb8322014-04-23 01:02:413457 }
[email protected]0bb112362014-07-26 04:38:323458 master = master_map.get(bot)
3459 if not master:
wnwen4fbaab82016-05-25 12:54:363460 if 'android' in bot:
tandriie5587792016-07-14 00:34:503461 master = 'master.tryserver.chromium.android'
wnwen4fbaab82016-05-25 12:54:363462 elif 'linux' in bot or 'presubmit' in bot:
tandriie5587792016-07-14 00:34:503463 master = 'master.tryserver.chromium.linux'
[email protected]0bb112362014-07-26 04:38:323464 elif 'win' in bot:
tandriie5587792016-07-14 00:34:503465 master = 'master.tryserver.chromium.win'
[email protected]0bb112362014-07-26 04:38:323466 elif 'mac' in bot or 'ios' in bot:
tandriie5587792016-07-14 00:34:503467 master = 'master.tryserver.chromium.mac'
[email protected]0bb112362014-07-26 04:38:323468 return master
[email protected]1bfb8322014-04-23 01:02:413469
3470
[email protected]ca8d1982009-02-19 16:33:123471def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:543472 results = []
[email protected]1f7b4172010-01-28 01:17:343473 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543474 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:273475 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:343476 input_api,
3477 output_api,
[email protected]2fdd1f362013-01-16 03:56:033478 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:273479
jam93a6ee792017-02-08 23:59:223480 results.extend(
3481 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
[email protected]3e4eb112011-01-18 03:29:543482 results.extend(input_api.canned_checks.CheckChangeHasBugField(
3483 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:413484 results.extend(input_api.canned_checks.CheckChangeHasDescription(
3485 input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:543486 return results
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143487
3488
3489def _CheckTranslationScreenshots(input_api, output_api):
3490 PART_FILE_TAG = "part"
3491 import os
3492 import sys
3493 from io import StringIO
3494
3495 try:
3496 old_sys_path = sys.path
3497 sys.path = sys.path + [input_api.os_path.join(
3498 input_api.PresubmitLocalPath(), 'tools', 'grit')]
3499 import grit.grd_reader
3500 import grit.node.message
3501 import grit.util
3502 finally:
3503 sys.path = old_sys_path
3504
3505 def _GetGrdMessages(grd_path_or_string, dir_path='.'):
3506 """Load the grd file and return a dict of message ids to messages.
3507
3508 Ignores any nested grdp files pointed by <part> tag.
3509 """
3510 doc = grit.grd_reader.Parse(grd_path_or_string, dir_path,
3511 stop_after=None, first_ids_file=None,
3512 debug=False, defines=None,
3513 tags_to_ignore=set([PART_FILE_TAG]))
3514 return {
3515 msg.attrs['name']:msg for msg in doc.GetChildrenOfType(
3516 grit.node.message.MessageNode)
3517 }
3518
3519 def _GetGrdpMessagesFromString(grdp_string):
3520 """Parses the contents of a grdp file given in grdp_string.
3521
3522 grd_reader can't parse grdp files directly. Instead, this creates a
3523 temporary directory with a grd file pointing to the grdp file, and loads the
3524 grd from there. Any nested grdp files (pointed by <part> tag) are ignored.
3525 """
3526 WRAPPER = """<?xml version="1.0" encoding="utf-8"?>
3527 <grit latest_public_release="1" current_release="1">
3528 <release seq="1">
3529 <messages>
3530 <part file="sub.grdp" />
3531 </messages>
3532 </release>
3533 </grit>
3534 """
3535 with grit.util.TempDir({'main.grd': WRAPPER,
3536 'sub.grdp': grdp_string}) as temp_dir:
3537 return _GetGrdMessages(temp_dir.GetPath('main.grd'), temp_dir.GetPath())
3538
3539 new_or_added_paths = set(f.LocalPath()
3540 for f in input_api.AffectedFiles()
3541 if (f.Action() == 'A' or f.Action() == 'M'))
3542 removed_paths = set(f.LocalPath()
3543 for f in input_api.AffectedFiles(include_deletes=True)
3544 if f.Action() == 'D')
3545
3546 affected_grds = [f for f in input_api.AffectedFiles()
3547 if (f.LocalPath().endswith('.grd') or
3548 f.LocalPath().endswith('.grdp'))]
3549 affected_png_paths = [f.AbsoluteLocalPath()
3550 for f in input_api.AffectedFiles()
3551 if (f.LocalPath().endswith('.png'))]
3552
3553 # Check for screenshots. Developers can upload screenshots using
3554 # tools/translation/upload_screenshots.py which finds and uploads
3555 # images associated with .grd files (e.g. test_grd/IDS_STRING.png for the
3556 # message named IDS_STRING in test.grd) and produces a .sha1 file (e.g.
3557 # test_grd/IDS_STRING.png.sha1) for each png when the upload is successful.
3558 #
3559 # The logic here is as follows:
3560 #
3561 # - If the CL has a .png file under the screenshots directory for a grd
3562 # file, warn the developer. Actual images should never be checked into the
3563 # Chrome repo.
3564 #
3565 # - If the CL contains modified or new messages in grd files and doesn't
3566 # contain the corresponding .sha1 files, warn the developer to add images
3567 # and upload them via tools/translation/upload_screenshots.py.
3568 #
3569 # - If the CL contains modified or new messages in grd files and the
3570 # corresponding .sha1 files, everything looks good.
3571 #
3572 # - If the CL contains removed messages in grd files but the corresponding
3573 # .sha1 files aren't removed, warn the developer to remove them.
3574 unnecessary_screenshots = []
3575 missing_sha1 = []
3576 unnecessary_sha1_files = []
3577
3578
3579 def _CheckScreenshotAdded(screenshots_dir, message_id):
3580 sha1_path = input_api.os_path.join(
3581 screenshots_dir, message_id + '.png.sha1')
3582 if sha1_path not in new_or_added_paths:
3583 missing_sha1.append(sha1_path)
3584
3585
3586 def _CheckScreenshotRemoved(screenshots_dir, message_id):
3587 sha1_path = input_api.os_path.join(
3588 screenshots_dir, message_id + '.png.sha1')
3589 if sha1_path not in removed_paths:
3590 unnecessary_sha1_files.append(sha1_path)
3591
3592
3593 for f in affected_grds:
3594 file_path = f.LocalPath()
3595 old_id_to_msg_map = {}
3596 new_id_to_msg_map = {}
3597 if file_path.endswith('.grdp'):
3598 if f.OldContents():
3599 old_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393600 unicode('\n'.join(f.OldContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143601 if f.NewContents():
3602 new_id_to_msg_map = _GetGrdpMessagesFromString(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393603 unicode('\n'.join(f.NewContents())))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143604 else:
3605 if f.OldContents():
3606 old_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393607 StringIO(unicode('\n'.join(f.OldContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143608 if f.NewContents():
3609 new_id_to_msg_map = _GetGrdMessages(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393610 StringIO(unicode('\n'.join(f.NewContents()))))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143611
3612 # Compute added, removed and modified message IDs.
3613 old_ids = set(old_id_to_msg_map)
3614 new_ids = set(new_id_to_msg_map)
3615 added_ids = new_ids - old_ids
3616 removed_ids = old_ids - new_ids
3617 modified_ids = set([])
3618 for key in old_ids.intersection(new_ids):
3619 if (old_id_to_msg_map[key].FormatXml()
3620 != new_id_to_msg_map[key].FormatXml()):
3621 modified_ids.add(key)
3622
3623 grd_name, ext = input_api.os_path.splitext(
3624 input_api.os_path.basename(file_path))
3625 screenshots_dir = input_api.os_path.join(
3626 input_api.os_path.dirname(file_path), grd_name + ext.replace('.', '_'))
3627
3628 # Check the screenshot directory for .png files. Warn if there is any.
3629 for png_path in affected_png_paths:
3630 if png_path.startswith(screenshots_dir):
3631 unnecessary_screenshots.append(png_path)
3632
3633 for added_id in added_ids:
3634 _CheckScreenshotAdded(screenshots_dir, added_id)
3635
3636 for modified_id in modified_ids:
3637 _CheckScreenshotAdded(screenshots_dir, modified_id)
3638
3639 for removed_id in removed_ids:
3640 _CheckScreenshotRemoved(screenshots_dir, removed_id)
3641
3642 results = []
3643 if unnecessary_screenshots:
3644 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393645 'Do not include actual screenshots in the changelist. Run '
3646 'tools/translate/upload_screenshots.py to upload them instead:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143647 sorted(unnecessary_screenshots)))
3648
3649 if missing_sha1:
3650 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393651 'You are adding or modifying UI strings.\n'
3652 'To ensure the best translations, take screenshots of the relevant UI '
3653 '(https://g.co/chrome/translation) and add these files to your '
3654 'changelist:', sorted(missing_sha1)))
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143655
3656 if unnecessary_sha1_files:
3657 results.append(output_api.PresubmitNotifyResult(
Mustafa Emre Acerc8a012d2018-07-31 00:00:393658 'You removed strings associated with these files. Remove:',
Mustafa Emre Acer29bf6ac92018-07-30 21:42:143659 sorted(unnecessary_sha1_files)))
3660
3661 return results