123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- # 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+$'
- INCLUDE_PATTERN = r'^#include'
- IOS_PACKAGE_PATTERN = r'^ios'
- 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 _CheckHasNoIncludeDirectives(input_api, output_api):
- """ Checks that #include preprocessor directives are not present."""
- errors = []
- for f in input_api.AffectedFiles():
- if not _IsInIosPackage(input_api, f.LocalPath()):
- continue
- _, ext = os.path.splitext(f.LocalPath())
- if ext != '.mm':
- continue
- for line_num, line in f.ChangedContents():
- if _HasIncludeDirective(input_api, line):
- errors.append('%s:%s' % (f.LocalPath(), line_num))
- if not errors:
- return []
- singular_plural = 'it' if len(errors) == 1 else 'them'
- plural_suffix = '' if len(errors) == 1 else 's'
- error_message = '\n'.join([
- 'Found usage of `#include` preprocessor directive%(plural)s! Please, '
- 'replace %(singular_plural)s with `#import` preprocessor '
- 'directive%(plural)s instead. '
- 'Consider replacing all existing `#include` with `#import` (if any) in '
- 'this file for the code clean up. See '
- 'https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main'
- '/styleguide/objective-c/objective-c.md#import-and-include-in-the-directory'
- ' for more details. '
- '\n\nAffected file%(plural)s:' % {'plural': plural_suffix,
- 'singular_plural': singular_plural }
- ] + errors) + '\n'
- return [output_api.PresubmitError(error_message)]
- def _IsInIosPackage(input_api, path):
- """ Returns True if path is within ios package"""
- ios_package_regex = input_api.re.compile(IOS_PACKAGE_PATTERN)
- return ios_package_regex.search(path)
- def _HasIncludeDirective(input_api, line):
- """ Returns True if #include is found in the line"""
- include_regex = input_api.re.compile(INCLUDE_PATTERN)
- return include_regex.search(line)
- 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))
- results.extend(_CheckHasNoIncludeDirectives(input_api, output_api))
- return results
|