Fix Crashes in the New Autofill UI
[chromium-blink-merge.git] / PRESUBMIT.py
blobf534343b131baddb822e93f2de34416a39491e84
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Top-level presubmit script for Chromium.
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
9 """
12 import re
13 import subprocess
14 import sys
17 _EXCLUDED_PATHS = (
18 r"^breakpad[\\\/].*",
19 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
20 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
21 r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
22 r"^skia[\\\/].*",
23 r"^v8[\\\/].*",
24 r".*MakeFile$",
25 r".+_autogen\.h$",
26 r"^cc[\\\/].*",
27 r"^webkit[\\\/]compositor_bindings[\\\/].*",
28 r".+[\\\/]pnacl_shim\.c$",
32 _TEST_ONLY_WARNING = (
33 'You might be calling functions intended only for testing from\n'
34 'production code. It is OK to ignore this warning if you know what\n'
35 'you are doing, as the heuristics used to detect the situation are\n'
36 'not perfect. The commit queue will not block on this warning.\n'
37 'Email joi@chromium.org if you have questions.')
40 _BANNED_OBJC_FUNCTIONS = (
42 'addTrackingRect:',
44 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
45 'prohibited. Please use CrTrackingArea instead.',
46 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
48 False,
51 'NSTrackingArea',
53 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
54 'instead.',
55 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
57 False,
60 'convertPointFromBase:',
62 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
63 'Please use |convertPoint:(point) fromView:nil| instead.',
64 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
66 True,
69 'convertPointToBase:',
71 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
72 'Please use |convertPoint:(point) toView:nil| instead.',
73 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
75 True,
78 'convertRectFromBase:',
80 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
81 'Please use |convertRect:(point) fromView:nil| instead.',
82 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
84 True,
87 'convertRectToBase:',
89 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
90 'Please use |convertRect:(point) toView:nil| instead.',
91 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
93 True,
96 'convertSizeFromBase:',
98 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
99 'Please use |convertSize:(point) fromView:nil| instead.',
100 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
102 True,
105 'convertSizeToBase:',
107 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
108 'Please use |convertSize:(point) toView:nil| instead.',
109 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
111 True,
116 _BANNED_CPP_FUNCTIONS = (
117 # Make sure that gtest's FRIEND_TEST() macro is not used; the
118 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
119 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
121 'FRIEND_TEST(',
123 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
124 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
126 False,
129 'ScopedAllowIO',
131 'New code should not use ScopedAllowIO. Post a task to the blocking',
132 'pool or the FILE thread instead.',
134 True,
137 'FilePathWatcher::Delegate',
139 'New code should not use FilePathWatcher::Delegate. Use the callback',
140 'interface instead.',
142 False,
145 'browser::FindLastActiveWithProfile',
147 'This function is deprecated and we\'re working on removing it. Pass',
148 'more context to get a Browser*, like a WebContents, window, or session',
149 'id. Talk to robertshield@ for more information.',
151 True,
154 'browser::FindAnyBrowser',
156 'This function is deprecated and we\'re working on removing it. Pass',
157 'more context to get a Browser*, like a WebContents, window, or session',
158 'id. Talk to robertshield@ for more information.',
160 True,
163 'browser::FindOrCreateTabbedBrowser',
165 'This function is deprecated and we\'re working on removing it. Pass',
166 'more context to get a Browser*, like a WebContents, window, or session',
167 'id. Talk to robertshield@ for more information.',
169 True,
172 'browser::FindTabbedBrowserDeprecated',
174 'This function is deprecated and we\'re working on removing it. Pass',
175 'more context to get a Browser*, like a WebContents, window, or session',
176 'id. Talk to robertshield@ for more information.',
178 True,
181 'RunAllPending()',
183 'This function is deprecated and we\'re working on removing it. Rename',
184 'to RunUntilIdle',
186 True,
192 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
193 """Attempts to prevent use of functions intended only for testing in
194 non-testing code. For now this is just a best-effort implementation
195 that ignores header files and may have some false positives. A
196 better implementation would probably need a proper C++ parser.
198 # We only scan .cc files and the like, as the declaration of
199 # for-testing functions in header files are hard to distinguish from
200 # calls to such functions without a proper C++ parser.
201 platform_specifiers = r'(_(android|chromeos|gtk|mac|posix|win))?'
202 source_extensions = r'\.(cc|cpp|cxx|mm)$'
203 file_inclusion_pattern = r'.+%s' % source_extensions
204 file_exclusion_patterns = (
205 r'.*[/\\](fake_|test_|mock_).+%s' % source_extensions,
206 r'.+_test_(base|support|util)%s' % source_extensions,
207 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers,
208 source_extensions),
209 r'.+profile_sync_service_harness%s' % source_extensions,
211 path_exclusion_patterns = (
212 r'.*[/\\](test|tool(s)?)[/\\].*',
213 # At request of folks maintaining this folder.
214 r'chrome[/\\]browser[/\\]automation[/\\].*',
217 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
218 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
219 exclusion_pattern = input_api.re.compile(
220 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
221 base_function_pattern, base_function_pattern))
223 def FilterFile(affected_file):
224 black_list = (file_exclusion_patterns + path_exclusion_patterns +
225 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
226 return input_api.FilterSourceFile(
227 affected_file,
228 white_list=(file_inclusion_pattern, ),
229 black_list=black_list)
231 problems = []
232 for f in input_api.AffectedSourceFiles(FilterFile):
233 local_path = f.LocalPath()
234 lines = input_api.ReadFile(f).splitlines()
235 line_number = 0
236 for line in lines:
237 if (inclusion_pattern.search(line) and
238 not exclusion_pattern.search(line)):
239 problems.append(
240 '%s:%d\n %s' % (local_path, line_number, line.strip()))
241 line_number += 1
243 if problems:
244 if not input_api.is_committing:
245 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
246 else:
247 # We don't warn on commit, to avoid stopping commits going through CQ.
248 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
249 else:
250 return []
253 def _CheckNoIOStreamInHeaders(input_api, output_api):
254 """Checks to make sure no .h files include <iostream>."""
255 files = []
256 pattern = input_api.re.compile(r'^#include\s*<iostream>',
257 input_api.re.MULTILINE)
258 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
259 if not f.LocalPath().endswith('.h'):
260 continue
261 contents = input_api.ReadFile(f)
262 if pattern.search(contents):
263 files.append(f)
265 if len(files):
266 return [ output_api.PresubmitError(
267 'Do not #include <iostream> in header files, since it inserts static '
268 'initialization into every file including the header. Instead, '
269 '#include <ostream>. See http://crbug.com/94794',
270 files) ]
271 return []
274 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
275 """Checks to make sure no source files use UNIT_TEST"""
276 problems = []
277 for f in input_api.AffectedFiles():
278 if (not f.LocalPath().endswith(('.cc', '.mm'))):
279 continue
281 for line_num, line in f.ChangedContents():
282 if 'UNIT_TEST' in line:
283 problems.append(' %s:%d' % (f.LocalPath(), line_num))
285 if not problems:
286 return []
287 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
288 '\n'.join(problems))]
291 def _CheckNoNewWStrings(input_api, output_api):
292 """Checks to make sure we don't introduce use of wstrings."""
293 problems = []
294 for f in input_api.AffectedFiles():
295 if (not f.LocalPath().endswith(('.cc', '.h')) or
296 f.LocalPath().endswith('test.cc')):
297 continue
299 allowWString = False
300 for line_num, line in f.ChangedContents():
301 if 'presubmit: allow wstring' in line:
302 allowWString = True
303 elif not allowWString and 'wstring' in line:
304 problems.append(' %s:%d' % (f.LocalPath(), line_num))
305 allowWString = False
306 else:
307 allowWString = False
309 if not problems:
310 return []
311 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
312 ' If you are calling a cross-platform API that accepts a wstring, '
313 'fix the API.\n' +
314 '\n'.join(problems))]
317 def _CheckNoDEPSGIT(input_api, output_api):
318 """Make sure .DEPS.git is never modified manually."""
319 if any(f.LocalPath().endswith('.DEPS.git') for f in
320 input_api.AffectedFiles()):
321 return [output_api.PresubmitError(
322 'Never commit changes to .DEPS.git. This file is maintained by an\n'
323 'automated system based on what\'s in DEPS and your changes will be\n'
324 'overwritten.\n'
325 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
326 'for more information')]
327 return []
330 def _CheckNoBannedFunctions(input_api, output_api):
331 """Make sure that banned functions are not used."""
332 warnings = []
333 errors = []
335 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
336 for f in input_api.AffectedFiles(file_filter=file_filter):
337 for line_num, line in f.ChangedContents():
338 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
339 if func_name in line:
340 problems = warnings;
341 if error:
342 problems = errors;
343 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
344 for message_line in message:
345 problems.append(' %s' % message_line)
347 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
348 for f in input_api.AffectedFiles(file_filter=file_filter):
349 for line_num, line in f.ChangedContents():
350 for func_name, message, error in _BANNED_CPP_FUNCTIONS:
351 if func_name in line:
352 problems = warnings;
353 if error:
354 problems = errors;
355 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
356 for message_line in message:
357 problems.append(' %s' % message_line)
359 result = []
360 if (warnings):
361 result.append(output_api.PresubmitPromptWarning(
362 'Banned functions were used.\n' + '\n'.join(warnings)))
363 if (errors):
364 result.append(output_api.PresubmitError(
365 'Banned functions were used.\n' + '\n'.join(errors)))
366 return result
369 def _CheckNoPragmaOnce(input_api, output_api):
370 """Make sure that banned functions are not used."""
371 files = []
372 pattern = input_api.re.compile(r'^#pragma\s+once',
373 input_api.re.MULTILINE)
374 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
375 if not f.LocalPath().endswith('.h'):
376 continue
377 contents = input_api.ReadFile(f)
378 if pattern.search(contents):
379 files.append(f)
381 if files:
382 return [output_api.PresubmitError(
383 'Do not use #pragma once in header files.\n'
384 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
385 files)]
386 return []
389 def _CheckNoTrinaryTrueFalse(input_api, output_api):
390 """Checks to make sure we don't introduce use of foo ? true : false."""
391 problems = []
392 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
393 for f in input_api.AffectedFiles():
394 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
395 continue
397 for line_num, line in f.ChangedContents():
398 if pattern.match(line):
399 problems.append(' %s:%d' % (f.LocalPath(), line_num))
401 if not problems:
402 return []
403 return [output_api.PresubmitPromptWarning(
404 'Please consider avoiding the "? true : false" pattern if possible.\n' +
405 '\n'.join(problems))]
408 def _CheckUnwantedDependencies(input_api, output_api):
409 """Runs checkdeps on #include statements added in this
410 change. Breaking - rules is an error, breaking ! rules is a
411 warning.
413 # We need to wait until we have an input_api object and use this
414 # roundabout construct to import checkdeps because this file is
415 # eval-ed and thus doesn't have __file__.
416 original_sys_path = sys.path
417 try:
418 sys.path = sys.path + [input_api.os_path.join(
419 input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
420 import checkdeps
421 from cpp_checker import CppChecker
422 from rules import Rule
423 finally:
424 # Restore sys.path to what it was before.
425 sys.path = original_sys_path
427 added_includes = []
428 for f in input_api.AffectedFiles():
429 if not CppChecker.IsCppFile(f.LocalPath()):
430 continue
432 changed_lines = [line for line_num, line in f.ChangedContents()]
433 added_includes.append([f.LocalPath(), changed_lines])
435 deps_checker = checkdeps.DepsChecker()
437 error_descriptions = []
438 warning_descriptions = []
439 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
440 added_includes):
441 description_with_path = '%s\n %s' % (path, rule_description)
442 if rule_type == Rule.DISALLOW:
443 error_descriptions.append(description_with_path)
444 else:
445 warning_descriptions.append(description_with_path)
447 results = []
448 if error_descriptions:
449 results.append(output_api.PresubmitError(
450 'You added one or more #includes that violate checkdeps rules.',
451 error_descriptions))
452 if warning_descriptions:
453 if not input_api.is_committing:
454 warning_factory = output_api.PresubmitPromptWarning
455 else:
456 # We don't want to block use of the CQ when there is a warning
457 # of this kind, so we only show a message when committing.
458 warning_factory = output_api.PresubmitNotifyResult
459 results.append(warning_factory(
460 'You added one or more #includes of files that are temporarily\n'
461 'allowed but being removed. Can you avoid introducing the\n'
462 '#include? See relevant DEPS file(s) for details and contacts.',
463 warning_descriptions))
464 return results
467 def _CheckFilePermissions(input_api, output_api):
468 """Check that all files have their permissions properly set."""
469 args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
470 input_api.change.RepositoryRoot()]
471 for f in input_api.AffectedFiles():
472 args += ['--file', f.LocalPath()]
473 errors = []
474 (errors, stderrdata) = subprocess.Popen(args).communicate()
476 results = []
477 if errors:
478 results.append(output_api.PresubmitError('checkperms.py failed.',
479 errors))
480 return results
483 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
484 """Makes sure we don't include ui/aura/window_property.h
485 in header files.
487 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
488 errors = []
489 for f in input_api.AffectedFiles():
490 if not f.LocalPath().endswith('.h'):
491 continue
492 for line_num, line in f.ChangedContents():
493 if pattern.match(line):
494 errors.append(' %s:%d' % (f.LocalPath(), line_num))
496 results = []
497 if errors:
498 results.append(output_api.PresubmitError(
499 'Header files should not include ui/aura/window_property.h', errors))
500 return results
503 def _CommonChecks(input_api, output_api):
504 """Checks common to both upload and commit."""
505 results = []
506 results.extend(input_api.canned_checks.PanProjectChecks(
507 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
508 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
509 results.extend(
510 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
511 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
512 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
513 results.extend(_CheckNoNewWStrings(input_api, output_api))
514 results.extend(_CheckNoDEPSGIT(input_api, output_api))
515 results.extend(_CheckNoBannedFunctions(input_api, output_api))
516 results.extend(_CheckNoPragmaOnce(input_api, output_api))
517 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
518 results.extend(_CheckUnwantedDependencies(input_api, output_api))
519 results.extend(_CheckFilePermissions(input_api, output_api))
520 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
521 return results
524 def _CheckSubversionConfig(input_api, output_api):
525 """Verifies the subversion config file is correctly setup.
527 Checks that autoprops are enabled, returns an error otherwise.
529 join = input_api.os_path.join
530 if input_api.platform == 'win32':
531 appdata = input_api.environ.get('APPDATA', '')
532 if not appdata:
533 return [output_api.PresubmitError('%APPDATA% is not configured.')]
534 path = join(appdata, 'Subversion', 'config')
535 else:
536 home = input_api.environ.get('HOME', '')
537 if not home:
538 return [output_api.PresubmitError('$HOME is not configured.')]
539 path = join(home, '.subversion', 'config')
541 error_msg = (
542 'Please look at http://dev.chromium.org/developers/coding-style to\n'
543 'configure your subversion configuration file. This enables automatic\n'
544 'properties to simplify the project maintenance.\n'
545 'Pro-tip: just download and install\n'
546 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
548 try:
549 lines = open(path, 'r').read().splitlines()
550 # Make sure auto-props is enabled and check for 2 Chromium standard
551 # auto-prop.
552 if (not '*.cc = svn:eol-style=LF' in lines or
553 not '*.pdf = svn:mime-type=application/pdf' in lines or
554 not 'enable-auto-props = yes' in lines):
555 return [
556 output_api.PresubmitNotifyResult(
557 'It looks like you have not configured your subversion config '
558 'file or it is not up-to-date.\n' + error_msg)
560 except (OSError, IOError):
561 return [
562 output_api.PresubmitNotifyResult(
563 'Can\'t find your subversion config file.\n' + error_msg)
565 return []
568 def _CheckAuthorizedAuthor(input_api, output_api):
569 """For non-googler/chromites committers, verify the author's email address is
570 in AUTHORS.
572 # TODO(maruel): Add it to input_api?
573 import fnmatch
575 author = input_api.change.author_email
576 if not author:
577 input_api.logging.info('No author, skipping AUTHOR check')
578 return []
579 authors_path = input_api.os_path.join(
580 input_api.PresubmitLocalPath(), 'AUTHORS')
581 valid_authors = (
582 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
583 for line in open(authors_path))
584 valid_authors = [item.group(1).lower() for item in valid_authors if item]
585 if input_api.verbose:
586 print 'Valid authors are %s' % ', '.join(valid_authors)
587 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
588 return [output_api.PresubmitPromptWarning(
589 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
590 '\n'
591 'http://www.chromium.org/developers/contributing-code and read the '
592 '"Legal" section\n'
593 'If you are a chromite, verify the contributor signed the CLA.') %
594 author)]
595 return []
598 def CheckChangeOnUpload(input_api, output_api):
599 results = []
600 results.extend(_CommonChecks(input_api, output_api))
601 return results
604 def CheckChangeOnCommit(input_api, output_api):
605 results = []
606 results.extend(_CommonChecks(input_api, output_api))
607 # TODO(thestig) temporarily disabled, doesn't work in third_party/
608 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
609 # input_api, output_api, sources))
610 # Make sure the tree is 'open'.
611 results.extend(input_api.canned_checks.CheckTreeIsOpen(
612 input_api,
613 output_api,
614 json_url='http://chromium-status.appspot.com/current?format=json'))
615 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
616 output_api, 'http://codereview.chromium.org',
617 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
618 'tryserver@chromium.org'))
620 results.extend(input_api.canned_checks.CheckChangeHasBugField(
621 input_api, output_api))
622 results.extend(input_api.canned_checks.CheckChangeHasDescription(
623 input_api, output_api))
624 results.extend(_CheckSubversionConfig(input_api, output_api))
625 return results
628 def GetPreferredTrySlaves(project, change):
629 files = change.LocalPaths()
631 if not files:
632 return []
634 if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
635 return ['mac_rel', 'mac_asan']
636 if all(re.search('(^|[/_])win[/_.]', f) for f in files):
637 return ['win_rel']
638 if all(re.search('(^|[/_])android[/_.]', f) for f in files):
639 return ['android_dbg', 'android_clang_dbg']
640 if all(re.search('^native_client_sdk', f) for f in files):
641 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
642 if all(re.search('[/_]ios[/_.]', f) for f in files):
643 return ['ios_rel_device', 'ios_dbg_simulator']
645 trybots = [
646 'android_clang_dbg',
647 'android_dbg',
648 'ios_dbg_simulator',
649 'ios_rel_device',
650 'linux_asan',
651 'linux_chromeos',
652 'linux_clang:compile',
653 'linux_rel',
654 'mac_asan',
655 'mac_rel',
656 'win_rel',
659 # Match things like path/aura/file.cc and path/file_aura.cc.
660 # Same for ash and chromeos.
661 if any(re.search('[/_](ash|aura)', f) for f in files):
662 trybots += ['linux_chromeos_clang:compile', 'win_aura',
663 'linux_chromeos_asan']
664 elif any(re.search('[/_]chromeos', f) for f in files):
665 trybots += ['linux_chromeos_clang:compile', 'linux_chromeos_asan']
667 return trybots