Upload upstream chromium 114.0.5735.31
[platform/framework/web/chromium-efl.git] / cc / PRESUBMIT.py
1 # Copyright 2012 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 """Top-level presubmit script for cc.
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 re
12
13 PRESUBMIT_VERSION = '2.0.0'
14
15 USE_PYTHON3 = True
16 CC_SOURCE_FILES=(r'^cc[\\/].*\.(cc|h)$',)
17
18 def _CheckChangeLintsClean(input_api, output_api):
19   allowlist = CC_SOURCE_FILES
20   denylist = None
21   source_filter = lambda x: input_api.FilterSourceFile(x, allowlist, denylist)
22
23   return input_api.canned_checks.CheckChangeLintsClean(
24       input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
25
26 def _CheckAsserts(input_api, output_api, allowlist=CC_SOURCE_FILES,
27                  denylist=None):
28   denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
29   source_file_filter = lambda x: input_api.FilterSourceFile(x, allowlist,
30       denylist)
31
32   assert_files = []
33
34   for f in input_api.AffectedSourceFiles(source_file_filter):
35     contents = input_api.ReadFile(f, 'rb')
36     # WebKit ASSERT() is not allowed.
37     if re.search(r"\bASSERT\(", contents):
38       assert_files.append(f.LocalPath())
39
40   if assert_files:
41     return [output_api.PresubmitError(
42       'These files use ASSERT instead of using DCHECK:',
43       items=assert_files)]
44   return []
45
46 def _CheckStdAbs(input_api, output_api,
47                 allowlist=CC_SOURCE_FILES, denylist=None):
48   denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
49   source_file_filter = lambda x: input_api.FilterSourceFile(x,
50                                                             allowlist,
51                                                             denylist)
52
53   using_std_abs_files = []
54   found_fabs_files = []
55   missing_std_prefix_files = []
56
57   for f in input_api.AffectedSourceFiles(source_file_filter):
58     contents = input_api.ReadFile(f, 'rb')
59     if re.search(r"using std::f?abs;", contents):
60       using_std_abs_files.append(f.LocalPath())
61     if re.search(r"\bfabsf?\(", contents):
62       found_fabs_files.append(f.LocalPath());
63
64     no_std_prefix = r"(?<!std::)"
65     # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix.
66     abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix
67     fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix
68     # Skips matching any lines that have "// NOLINT".
69     no_nolint = r"(?![^\n]*//\s+NOLINT)"
70
71     expression = re.compile("(%s|%s)%s" %
72         (abs_without_prefix, fabs_without_prefix, no_nolint))
73     if expression.search(contents):
74       missing_std_prefix_files.append(f.LocalPath())
75
76   result = []
77   if using_std_abs_files:
78     result.append(output_api.PresubmitError(
79         'These files have "using std::abs" which is not permitted.',
80         items=using_std_abs_files))
81   if found_fabs_files:
82     result.append(output_api.PresubmitError(
83         'std::abs() should be used instead of std::fabs() for consistency.',
84         items=found_fabs_files))
85   if missing_std_prefix_files:
86     result.append(output_api.PresubmitError(
87         'These files use abs(), absf(), fabs(), or fabsf() without qualifying '
88         'the std namespace. Please use std::abs() in all places.',
89         items=missing_std_prefix_files))
90   return result
91
92 def _CheckPassByValue(input_api,
93                      output_api,
94                      allowlist=CC_SOURCE_FILES,
95                      denylist=None):
96   denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
97   source_file_filter = lambda x: input_api.FilterSourceFile(x,
98                                                             allowlist,
99                                                             denylist)
100
101   local_errors = []
102
103   # Well-defined simple classes the same size as a primitive type.
104   pass_by_value_types = ['base::Time',
105                          'base::TimeTicks',
106                          ]
107
108   for f in input_api.AffectedSourceFiles(source_file_filter):
109     contents = input_api.ReadFile(f, 'rb')
110     sep = '|'
111     match = re.search(
112       r'\bconst +' + '(?P<type>(%s))&' % sep.join(pass_by_value_types),
113       contents)
114     if match:
115       local_errors.append(output_api.PresubmitError(
116         '%s passes %s by const ref instead of by value.' %
117         (f.LocalPath(), match.group('type'))))
118   return local_errors
119
120 def _CheckTodos(input_api, output_api):
121   errors = []
122
123   source_file_filter = lambda x: x
124   for f in input_api.AffectedSourceFiles(source_file_filter):
125     contents = input_api.ReadFile(f, 'rb')
126     if ('FIX'+'ME') in contents:
127       errors.append(f.LocalPath())
128
129   if errors:
130     return [output_api.PresubmitError(
131       'All TODO comments should be of the form TODO(name/bug). ' +
132       'Use TODO instead of FIX' + 'ME',
133       items=errors)]
134   return []
135
136 def _CheckDoubleAngles(input_api, output_api, allowlist=CC_SOURCE_FILES,
137                       denylist=None):
138   errors = []
139
140   source_file_filter = lambda x: input_api.FilterSourceFile(x,
141                                                             allowlist,
142                                                             denylist)
143   for f in input_api.AffectedSourceFiles(source_file_filter):
144     contents = input_api.ReadFile(f, 'rb')
145     if ('> >') in contents:
146       errors.append(f.LocalPath())
147
148   if errors:
149     return [output_api.PresubmitError('Use >> instead of > >:', items=errors)]
150   return []
151
152 def _FindUnquotedQuote(contents, pos):
153   match = re.search(r"(?<!\\)(?P<quote>\")", contents[pos:])
154   return -1 if not match else match.start("quote") + pos
155
156 def _FindUselessIfdefs(input_api, output_api):
157   errors = []
158   source_file_filter = lambda x: x
159   for f in input_api.AffectedSourceFiles(source_file_filter):
160     contents = input_api.ReadFile(f, 'rb')
161     if re.search(r'#if\s*0\s', contents):
162       errors.append(f.LocalPath())
163   if errors:
164     return [output_api.PresubmitError(
165       'Don\'t use #if '+'0; just delete the code.',
166       items=errors)]
167   return []
168
169 def _FindNamespaceInBlock(pos, namespace, contents, allowlist=[]):
170   open_brace = -1
171   close_brace = -1
172   quote = -1
173   name = -1
174   brace_count = 1
175   quote_count = 0
176   while pos < len(contents) and brace_count > 0:
177     if open_brace < pos: open_brace = contents.find("{", pos)
178     if close_brace < pos: close_brace = contents.find("}", pos)
179     if quote < pos: quote = _FindUnquotedQuote(contents, pos)
180     if name < pos: name = contents.find(("%s::" % namespace), pos)
181
182     if name < 0:
183       return False # The namespace is not used at all.
184     if open_brace < 0:
185       open_brace = len(contents)
186     if close_brace < 0:
187       close_brace = len(contents)
188     if quote < 0:
189       quote = len(contents)
190
191     next = min(open_brace, min(close_brace, min(quote, name)))
192
193     if next == open_brace:
194       brace_count += 1
195     elif next == close_brace:
196       brace_count -= 1
197     elif next == quote:
198       quote_count = 0 if quote_count else 1
199     elif next == name and not quote_count:
200       in_allowlist = False
201       for w in allowlist:
202         if re.match(w, contents[next:]):
203           in_allowlist = True
204           break
205       if not in_allowlist:
206         return True
207     pos = next + 1
208   return False
209
210 # Checks for the use of cc:: within the cc namespace, which is usually
211 # redundant.
212 def _CheckNamespace(input_api, output_api):
213   errors = []
214
215   source_file_filter = lambda x: x
216   for f in input_api.AffectedSourceFiles(source_file_filter):
217     contents = input_api.ReadFile(f, 'rb')
218     match = re.search(r'namespace\s*cc\s*{', contents)
219     if match:
220       allowlist = []
221       if _FindNamespaceInBlock(match.end(),
222                                'cc',
223                                contents,
224                                allowlist=allowlist):
225         errors.append(f.LocalPath())
226
227   if errors:
228     return [output_api.PresubmitError(
229       'Do not use cc:: inside of the cc namespace.',
230       items=errors)]
231   return []
232
233 def _CheckForUseOfWrongClock(input_api,
234                             output_api,
235                             allowlist=CC_SOURCE_FILES,
236                             denylist=None):
237   """Make sure new lines of code don't use a clock susceptible to skew."""
238   denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
239   source_file_filter = lambda x: input_api.FilterSourceFile(x,
240                                                             allowlist,
241                                                             denylist)
242   # Regular expression that should detect any explicit references to the
243   # base::Time type (or base::Clock/DefaultClock), whether in using decls,
244   # typedefs, or to call static methods.
245   base_time_type_pattern = r'(^|\W)base::(Time|Clock|DefaultClock)(\W|$)'
246
247   # Regular expression that should detect references to the base::Time class
248   # members, such as a call to base::Time::Now.
249   base_time_member_pattern = r'(^|\W)(Time|Clock|DefaultClock)::'
250
251   # Regular expression to detect "using base::Time" declarations.  We want to
252   # prevent these from triggerring a warning.  For example, it's perfectly
253   # reasonable for code to be written like this:
254   #
255   #   using base::Time;
256   #   ...
257   #   int64 foo_us = foo_s * Time::kMicrosecondsPerSecond;
258   using_base_time_decl_pattern = r'^\s*using\s+(::)?base::Time\s*;'
259
260   # Regular expression to detect references to the kXXX constants in the
261   # base::Time class.  We want to prevent these from triggerring a warning.
262   base_time_konstant_pattern = r'(^|\W)Time::k\w+'
263
264   problem_re = input_api.re.compile(
265       r'(' + base_time_type_pattern + r')|(' + base_time_member_pattern + r')')
266   exception_re = input_api.re.compile(
267       r'(' + using_base_time_decl_pattern + r')|(' +
268       base_time_konstant_pattern + r')')
269   problems = []
270   for f in input_api.AffectedSourceFiles(source_file_filter):
271     for line_number, line in f.ChangedContents():
272       if problem_re.search(line):
273         if not exception_re.search(line):
274           problems.append(
275               '  %s:%d\n    %s' % (f.LocalPath(), line_number, line.strip()))
276
277   if problems:
278     return [output_api.PresubmitPromptOrNotify(
279         'You added one or more references to the base::Time class and/or one\n'
280         'of its member functions (or base::Clock/DefaultClock). In cc code,\n'
281         'it is most certainly incorrect! Instead use base::TimeTicks.\n\n'
282         '\n'.join(problems))]
283   else:
284     return []
285
286 def CheckChangeOnUpload(input_api, output_api):
287   results = []
288   results += _CheckAsserts(input_api, output_api)
289   results += _CheckStdAbs(input_api, output_api)
290   results += _CheckPassByValue(input_api, output_api)
291   results += _CheckChangeLintsClean(input_api, output_api)
292   results += _CheckTodos(input_api, output_api)
293   results += _CheckDoubleAngles(input_api, output_api)
294   results += _CheckNamespace(input_api, output_api)
295   results += _CheckForUseOfWrongClock(input_api, output_api)
296   results += _FindUselessIfdefs(input_api, output_api)
297   return results