blob: 6c76d51ea27f86c2097e048d24d2bb4771e18345 [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Presubmit script for ios.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
import os
USE_PYTHON3 = True
NULLABILITY_PATTERN = r'(nonnull|nullable|_Nullable|_Nonnull)'
TODO_PATTERN = r'TO[D]O\(([^\)]*)\)'
CRBUG_PATTERN = r'crbug\.com/\d+$'
ARC_COMPILE_GUARD = [
'#if !defined(__has_feature) || !__has_feature(objc_arc)',
'#error "This file requires ARC support."',
'#endif',
]
def IsSubListOf(needle, hay):
"""Returns whether there is a slice of |hay| equal to |needle|."""
for i, line in enumerate(hay):
if line == needle[0]:
if needle == hay[i:i+len(needle)]:
return True
return False
def _CheckARCCompilationGuard(input_api, output_api):
""" Checks whether new objc files have proper ARC compile guards."""
files_without_headers = []
for f in input_api.AffectedFiles():
if f.Action() != 'A':
continue
_, ext = os.path.splitext(f.LocalPath())
if ext not in ('.m', '.mm'):
continue
if not IsSubListOf(ARC_COMPILE_GUARD, f.NewContents()):
files_without_headers.append(f.LocalPath())
if not files_without_headers:
return []
plural_suffix = '' if len(files_without_headers) == 1 else 's'
error_message = '\n'.join([
'Found new Objective-C implementation file%(plural)s without compile'
' guard%(plural)s. Please use the following compile guard'
':' % {'plural': plural_suffix}
] + ARC_COMPILE_GUARD + files_without_headers) + '\n'
return [output_api.PresubmitError(error_message)]
def _CheckNullabilityAnnotations(input_api, output_api):
""" Checks whether there are nullability annotations in ios code."""
nullability_regex = input_api.re.compile(NULLABILITY_PATTERN)
errors = []
for f in input_api.AffectedFiles():
for line_num, line in f.ChangedContents():
if nullability_regex.search(line):
errors.append('%s:%s' % (f.LocalPath(), line_num))
if not errors:
return []
plural_suffix = '' if len(errors) == 1 else 's'
error_message = ('Found Nullability annotation%(plural)s. '
'Prefer DCHECKs in ios code to check for nullness:'
% {'plural': plural_suffix})
return [output_api.PresubmitPromptWarning(error_message, items=errors)]
def _CheckBugInToDo(input_api, output_api):
""" Checks whether TODOs in ios code are identified by a bug number."""
errors = []
for f in input_api.AffectedFiles():
for line_num, line in f.ChangedContents():
if _HasToDoWithNoBug(input_api, line):
errors.append('%s:%s' % (f.LocalPath(), line_num))
if not errors:
return []
plural_suffix = '' if len(errors) == 1 else 's'
error_message = '\n'.join([
'Found TO''DO%(plural)s without bug number%(plural)s (expected format is '
'\"TO''DO(crbug.com/######)\":' % {'plural': plural_suffix}
] + errors) + '\n'
return [output_api.PresubmitError(error_message)]
def _HasToDoWithNoBug(input_api, line):
""" Returns True if TODO is not identified by a bug number."""
todo_regex = input_api.re.compile(TODO_PATTERN)
crbug_regex = input_api.re.compile(CRBUG_PATTERN)
todo_match = todo_regex.search(line)
if not todo_match:
return False
crbug_match = crbug_regex.match(todo_match.group(1))
return not crbug_match
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CheckBugInToDo(input_api, output_api))
results.extend(_CheckNullabilityAnnotations(input_api, output_api))
results.extend(_CheckARCCompilationGuard(input_api, output_api))
return results