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