Avi Drissman | ea1be23 | 2022-09-14 23:29:06 | [diff] [blame] | 1 | # Copyright 2017 The Chromium Authors |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """Presubmit script for ios. |
| 6 | |
| 7 | See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
| 8 | for more details about the presubmit API built into depot_tools. |
| 9 | """ |
| 10 | |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 11 | import os |
| 12 | |
Josip Sokcevic | 2e6b82bd | 2021-06-14 09:37:31 | [diff] [blame] | 13 | USE_PYTHON3 = True |
| 14 | |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 15 | NULLABILITY_PATTERN = r'(nonnull|nullable|_Nullable|_Nonnull)' |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 16 | TODO_PATTERN = r'TO[D]O\(([^\)]*)\)' |
| 17 | CRBUG_PATTERN = r'crbug\.com/\d+$' |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 18 | INCLUDE_PATTERN = r'^#include' |
Gauthier Ambard | f85c5f1 | 2022-09-14 11:26:54 | [diff] [blame] | 19 | PIPE_IN_COMMENT_PATTERN = r'//.*[^|]\|(?!\|)' |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 20 | IOS_PACKAGE_PATTERN = r'^ios' |
Gauthier Ambard | 7fc07bf | 2018-01-02 09:48:09 | [diff] [blame] | 21 | ARC_COMPILE_GUARD = [ |
| 22 | '#if !defined(__has_feature) || !__has_feature(objc_arc)', |
| 23 | '#error "This file requires ARC support."', |
| 24 | '#endif', |
| 25 | ] |
Louis Romero | e33c2da | 2023-01-19 14:18:53 | [diff] [blame^] | 26 | BOXED_BOOL_PATTERN = r'@\((YES|NO)\)' |
Gauthier Ambard | 7fc07bf | 2018-01-02 09:48:09 | [diff] [blame] | 27 | |
| 28 | def IsSubListOf(needle, hay): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 29 | """Returns whether there is a slice of |hay| equal to |needle|.""" |
| 30 | for i, line in enumerate(hay): |
| 31 | if line == needle[0]: |
| 32 | if needle == hay[i:i + len(needle)]: |
| 33 | return True |
| 34 | return False |
| 35 | |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 36 | |
| 37 | def _CheckARCCompilationGuard(input_api, output_api): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 38 | """ Checks whether new objc files have proper ARC compile guards.""" |
| 39 | files_without_headers = [] |
| 40 | for f in input_api.AffectedFiles(): |
| 41 | if f.Action() != 'A': |
| 42 | continue |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 43 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 44 | _, ext = os.path.splitext(f.LocalPath()) |
| 45 | if ext not in ('.m', '.mm'): |
| 46 | continue |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 47 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 48 | if not IsSubListOf(ARC_COMPILE_GUARD, f.NewContents()): |
| 49 | files_without_headers.append(f.LocalPath()) |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 50 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 51 | if not files_without_headers: |
| 52 | return [] |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 53 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 54 | plural_suffix = '' if len(files_without_headers) == 1 else 's' |
| 55 | error_message = '\n'.join([ |
| 56 | 'Found new Objective-C implementation file%(plural)s without compile' |
| 57 | ' guard%(plural)s. Please use the following compile guard' |
| 58 | ':' % { |
| 59 | 'plural': plural_suffix |
| 60 | } |
| 61 | ] + ARC_COMPILE_GUARD + files_without_headers) + '\n' |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 62 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 63 | return [output_api.PresubmitError(error_message)] |
[email protected] | 0066f73 | 2017-12-28 10:33:08 | [diff] [blame] | 64 | |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 65 | |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 66 | def _CheckNullabilityAnnotations(input_api, output_api): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 67 | """ Checks whether there are nullability annotations in ios code.""" |
| 68 | nullability_regex = input_api.re.compile(NULLABILITY_PATTERN) |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 69 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 70 | errors = [] |
| 71 | for f in input_api.AffectedFiles(): |
| 72 | for line_num, line in f.ChangedContents(): |
| 73 | if nullability_regex.search(line): |
| 74 | errors.append('%s:%s' % (f.LocalPath(), line_num)) |
| 75 | if not errors: |
| 76 | return [] |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 77 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 78 | plural_suffix = '' if len(errors) == 1 else 's' |
| 79 | error_message = ('Found Nullability annotation%(plural)s. ' |
| 80 | 'Prefer DCHECKs in ios code to check for nullness:' % { |
| 81 | 'plural': plural_suffix |
| 82 | }) |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 83 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 84 | return [output_api.PresubmitPromptWarning(error_message, items=errors)] |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 85 | |
| 86 | |
| 87 | def _CheckBugInToDo(input_api, output_api): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 88 | """ Checks whether TODOs in ios code are identified by a bug number.""" |
| 89 | errors = [] |
| 90 | for f in input_api.AffectedFiles(): |
| 91 | for line_num, line in f.ChangedContents(): |
| 92 | if _HasToDoWithNoBug(input_api, line): |
| 93 | errors.append('%s:%s' % (f.LocalPath(), line_num)) |
| 94 | if not errors: |
| 95 | return [] |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 96 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 97 | plural_suffix = '' if len(errors) == 1 else 's' |
| 98 | error_message = '\n'.join([ |
| 99 | 'Found TO' |
| 100 | 'DO%(plural)s without bug number%(plural)s (expected format ' |
| 101 | 'is \"TO' |
| 102 | 'DO(crbug.com/######)\":' % { |
| 103 | 'plural': plural_suffix |
| 104 | } |
| 105 | ] + errors) + '\n' |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 106 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 107 | return [output_api.PresubmitError(error_message)] |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 108 | |
| 109 | |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 110 | def _CheckHasNoIncludeDirectives(input_api, output_api): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 111 | """ Checks that #include preprocessor directives are not present.""" |
| 112 | errors = [] |
| 113 | for f in input_api.AffectedFiles(): |
| 114 | if not _IsInIosPackage(input_api, f.LocalPath()): |
| 115 | continue |
| 116 | _, ext = os.path.splitext(f.LocalPath()) |
| 117 | if ext != '.mm': |
| 118 | continue |
| 119 | for line_num, line in f.ChangedContents(): |
| 120 | if _HasIncludeDirective(input_api, line): |
| 121 | errors.append('%s:%s' % (f.LocalPath(), line_num)) |
| 122 | if not errors: |
| 123 | return [] |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 124 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 125 | singular_plural = 'it' if len(errors) == 1 else 'them' |
| 126 | plural_suffix = '' if len(errors) == 1 else 's' |
| 127 | error_message = '\n'.join([ |
| 128 | 'Found usage of `#include` preprocessor directive%(plural)s! Please, ' |
| 129 | 'replace %(singular_plural)s with `#import` preprocessor ' |
| 130 | 'directive%(plural)s instead. ' |
| 131 | 'Consider replacing all existing `#include` with `#import` (if any) in ' |
| 132 | 'this file for the code clean up. See ' |
| 133 | 'https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main' |
| 134 | '/styleguide/objective-c/objective-c.md' |
| 135 | '#import-and-include-in-the-directory for more details. ' |
| 136 | '\n\nAffected file%(plural)s:' % { |
| 137 | 'plural': plural_suffix, |
| 138 | 'singular_plural': singular_plural |
| 139 | } |
| 140 | ] + errors) + '\n' |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 141 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 142 | return [output_api.PresubmitError(error_message)] |
| 143 | |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 144 | |
Gauthier Ambard | f85c5f1 | 2022-09-14 11:26:54 | [diff] [blame] | 145 | def _CheckHasNoPipeInComment(input_api, output_api): |
| 146 | """ Checks that comments don't contain pipes.""" |
| 147 | pipe_regex = input_api.re.compile(PIPE_IN_COMMENT_PATTERN) |
| 148 | |
| 149 | errors = [] |
| 150 | for f in input_api.AffectedFiles(): |
| 151 | if not _IsInIosPackage(input_api, f.LocalPath()): |
| 152 | continue |
| 153 | for line_num, line in f.ChangedContents(): |
| 154 | if pipe_regex.search(line): |
| 155 | errors.append('%s:%s' % (f.LocalPath(), line_num)) |
| 156 | if not errors: |
| 157 | return [] |
| 158 | error_message = '\n'.join([ |
| 159 | 'Please use backticks "`" instead of pipes "|" if you need to quote' |
| 160 | ' variable names and symbols in comments.\n' |
| 161 | 'Found potential uses of pipes in:' |
| 162 | ] + errors) + '\n' |
| 163 | |
| 164 | return [output_api.PresubmitPromptWarning(error_message)] |
| 165 | |
| 166 | |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 167 | def _IsInIosPackage(input_api, path): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 168 | """ Returns True if path is within ios package""" |
| 169 | ios_package_regex = input_api.re.compile(IOS_PACKAGE_PATTERN) |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 170 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 171 | return ios_package_regex.search(path) |
| 172 | |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 173 | |
| 174 | def _HasIncludeDirective(input_api, line): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 175 | """ Returns True if #include is found in the line""" |
| 176 | include_regex = input_api.re.compile(INCLUDE_PATTERN) |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 177 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 178 | return include_regex.search(line) |
| 179 | |
Petro Akzhygitov | db9b35162 | 2022-07-01 08:21:50 | [diff] [blame] | 180 | |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 181 | def _HasToDoWithNoBug(input_api, line): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 182 | """ Returns True if TODO is not identified by a bug number.""" |
| 183 | todo_regex = input_api.re.compile(TODO_PATTERN) |
| 184 | crbug_regex = input_api.re.compile(CRBUG_PATTERN) |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 185 | |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 186 | todo_match = todo_regex.search(line) |
| 187 | if not todo_match: |
| 188 | return False |
| 189 | crbug_match = crbug_regex.match(todo_match.group(1)) |
| 190 | return not crbug_match |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 191 | |
Louis Romero | e33c2da | 2023-01-19 14:18:53 | [diff] [blame^] | 192 | def _CheckHasNoBoxedBOOL(input_api, output_api): |
| 193 | """ Checks that there are no @(YES) or @(NO).""" |
| 194 | boxed_BOOL_regex = input_api.re.compile(BOXED_BOOL_PATTERN) |
| 195 | |
| 196 | errors = [] |
| 197 | for f in input_api.AffectedFiles(): |
| 198 | for line_num, line in f.ChangedContents(): |
| 199 | if boxed_BOOL_regex.search(line): |
| 200 | errors.append('%s:%s' % (f.LocalPath(), line_num)) |
| 201 | if not errors: |
| 202 | return [] |
| 203 | |
| 204 | plural_suffix = '' if len(errors) == 1 else 's' |
| 205 | error_message = ('Found boxed BOOL%(plural)s. ' |
| 206 | 'Prefer @YES or @NO in ios code:' % { |
| 207 | 'plural': plural_suffix |
| 208 | }) |
| 209 | |
| 210 | return [output_api.PresubmitPromptWarning(error_message, items=errors)] |
Nohemi Fernandez | a383337 | 2020-03-23 17:22:19 | [diff] [blame] | 211 | |
Sylvain Defresne | fcda19f | 2017-06-27 10:14:01 | [diff] [blame] | 212 | def CheckChangeOnUpload(input_api, output_api): |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 213 | results = [] |
| 214 | results.extend(_CheckBugInToDo(input_api, output_api)) |
| 215 | results.extend(_CheckNullabilityAnnotations(input_api, output_api)) |
| 216 | results.extend(_CheckARCCompilationGuard(input_api, output_api)) |
| 217 | results.extend(_CheckHasNoIncludeDirectives(input_api, output_api)) |
Gauthier Ambard | f85c5f1 | 2022-09-14 11:26:54 | [diff] [blame] | 218 | results.extend(_CheckHasNoPipeInComment(input_api, output_api)) |
Louis Romero | e33c2da | 2023-01-19 14:18:53 | [diff] [blame^] | 219 | results.extend(_CheckHasNoBoxedBOOL(input_api, output_api)) |
Gauthier Ambard | 1981d62 | 2022-09-13 09:34:49 | [diff] [blame] | 220 | return results |