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