[M108 Migration][HBBTV] Implement ewk_context_register_jsplugin_mime_types API
[platform/framework/web/chromium-efl.git] / ios / PRESUBMIT.py
1 # Copyright 2017 The Chromium Authors
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
11 import os
12
13 USE_PYTHON3 = True
14
15 NULLABILITY_PATTERN = r'(nonnull|nullable|_Nullable|_Nonnull)'
16 TODO_PATTERN = r'TO[D]O\(([^\)]*)\)'
17 CRBUG_PATTERN = r'crbug\.com/\d+$'
18 INCLUDE_PATTERN = r'^#include'
19 PIPE_IN_COMMENT_PATTERN = r'//.*[^|]\|(?!\|)'
20 IOS_PACKAGE_PATTERN = r'^ios'
21 ARC_COMPILE_GUARD = [
22     '#if !defined(__has_feature) || !__has_feature(objc_arc)',
23     '#error "This file requires ARC support."',
24     '#endif',
25 ]
26
27 def IsSubListOf(needle, hay):
28     """Returns whether there is a slice of |hay| equal to |needle|."""
29     for i, line in enumerate(hay):
30         if line == needle[0]:
31             if needle == hay[i:i + len(needle)]:
32                 return True
33     return False
34
35
36 def _CheckARCCompilationGuard(input_api, output_api):
37     """ Checks whether new objc files have proper ARC compile guards."""
38     files_without_headers = []
39     for f in input_api.AffectedFiles():
40         if f.Action() != 'A':
41             continue
42
43         _, ext = os.path.splitext(f.LocalPath())
44         if ext not in ('.m', '.mm'):
45             continue
46
47         if not IsSubListOf(ARC_COMPILE_GUARD, f.NewContents()):
48             files_without_headers.append(f.LocalPath())
49
50     if not files_without_headers:
51         return []
52
53     plural_suffix = '' if len(files_without_headers) == 1 else 's'
54     error_message = '\n'.join([
55         'Found new Objective-C implementation file%(plural)s without compile'
56         ' guard%(plural)s. Please use the following compile guard'
57         ':' % {
58             'plural': plural_suffix
59         }
60     ] + ARC_COMPILE_GUARD + files_without_headers) + '\n'
61
62     return [output_api.PresubmitError(error_message)]
63
64
65 def _CheckNullabilityAnnotations(input_api, output_api):
66     """ Checks whether there are nullability annotations in ios code."""
67     nullability_regex = input_api.re.compile(NULLABILITY_PATTERN)
68
69     errors = []
70     for f in input_api.AffectedFiles():
71         for line_num, line in f.ChangedContents():
72             if nullability_regex.search(line):
73                 errors.append('%s:%s' % (f.LocalPath(), line_num))
74     if not errors:
75         return []
76
77     plural_suffix = '' if len(errors) == 1 else 's'
78     error_message = ('Found Nullability annotation%(plural)s. '
79                      'Prefer DCHECKs in ios code to check for nullness:' % {
80                          'plural': plural_suffix
81                      })
82
83     return [output_api.PresubmitPromptWarning(error_message, items=errors)]
84
85
86 def _CheckBugInToDo(input_api, output_api):
87     """ Checks whether TODOs in ios code are identified by a bug number."""
88     errors = []
89     for f in input_api.AffectedFiles():
90         for line_num, line in f.ChangedContents():
91             if _HasToDoWithNoBug(input_api, line):
92                 errors.append('%s:%s' % (f.LocalPath(), line_num))
93     if not errors:
94         return []
95
96     plural_suffix = '' if len(errors) == 1 else 's'
97     error_message = '\n'.join([
98         'Found TO'
99         'DO%(plural)s without bug number%(plural)s (expected format '
100         'is \"TO'
101         'DO(crbug.com/######)\":' % {
102             'plural': plural_suffix
103         }
104     ] + errors) + '\n'
105
106     return [output_api.PresubmitError(error_message)]
107
108
109 def _CheckHasNoIncludeDirectives(input_api, output_api):
110     """ Checks that #include preprocessor directives are not present."""
111     errors = []
112     for f in input_api.AffectedFiles():
113         if not _IsInIosPackage(input_api, f.LocalPath()):
114             continue
115         _, ext = os.path.splitext(f.LocalPath())
116         if ext != '.mm':
117             continue
118         for line_num, line in f.ChangedContents():
119             if _HasIncludeDirective(input_api, line):
120                 errors.append('%s:%s' % (f.LocalPath(), line_num))
121     if not errors:
122         return []
123
124     singular_plural = 'it' if len(errors) == 1 else 'them'
125     plural_suffix = '' if len(errors) == 1 else 's'
126     error_message = '\n'.join([
127         'Found usage of `#include` preprocessor directive%(plural)s! Please, '
128         'replace %(singular_plural)s with `#import` preprocessor '
129         'directive%(plural)s instead. '
130         'Consider replacing all existing `#include` with `#import` (if any) in '
131         'this file for the code clean up. See '
132         'https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main'
133         '/styleguide/objective-c/objective-c.md'
134         '#import-and-include-in-the-directory for more details. '
135         '\n\nAffected file%(plural)s:' % {
136             'plural': plural_suffix,
137             'singular_plural': singular_plural
138         }
139     ] + errors) + '\n'
140
141     return [output_api.PresubmitError(error_message)]
142
143
144 def _CheckHasNoPipeInComment(input_api, output_api):
145     """ Checks that comments don't contain pipes."""
146     pipe_regex = input_api.re.compile(PIPE_IN_COMMENT_PATTERN)
147
148     errors = []
149     for f in input_api.AffectedFiles():
150         if not _IsInIosPackage(input_api, f.LocalPath()):
151             continue
152         for line_num, line in f.ChangedContents():
153             if pipe_regex.search(line):
154                 errors.append('%s:%s' % (f.LocalPath(), line_num))
155     if not errors:
156         return []
157     error_message = '\n'.join([
158         'Please use backticks "`" instead of pipes "|" if you need to quote'
159         ' variable names and symbols in comments.\n'
160         'Found potential uses of pipes in:'
161     ] + errors) + '\n'
162
163     return [output_api.PresubmitPromptWarning(error_message)]
164
165
166 def _IsInIosPackage(input_api, path):
167     """ Returns True if path is within ios package"""
168     ios_package_regex = input_api.re.compile(IOS_PACKAGE_PATTERN)
169
170     return ios_package_regex.search(path)
171
172
173 def _HasIncludeDirective(input_api, line):
174     """ Returns True if #include is found in the line"""
175     include_regex = input_api.re.compile(INCLUDE_PATTERN)
176
177     return include_regex.search(line)
178
179
180 def _HasToDoWithNoBug(input_api, line):
181     """ Returns True if TODO is not identified by a bug number."""
182     todo_regex = input_api.re.compile(TODO_PATTERN)
183     crbug_regex = input_api.re.compile(CRBUG_PATTERN)
184
185     todo_match = todo_regex.search(line)
186     if not todo_match:
187         return False
188     crbug_match = crbug_regex.match(todo_match.group(1))
189     return not crbug_match
190
191
192 def CheckChangeOnUpload(input_api, output_api):
193     results = []
194     results.extend(_CheckBugInToDo(input_api, output_api))
195     results.extend(_CheckNullabilityAnnotations(input_api, output_api))
196     results.extend(_CheckARCCompilationGuard(input_api, output_api))
197     results.extend(_CheckHasNoIncludeDirectives(input_api, output_api))
198     results.extend(_CheckHasNoPipeInComment(input_api, output_api))
199     return results