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.
5 """Top-level presubmit script for cc.
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into depot_tools.
14 CC_SOURCE_FILES=(r'^cc[\\/].*\.(cc|h)$',)
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)
20 return input_api.canned_checks.CheckChangeLintsClean(
21 input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
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)
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())
36 return [output_api.PresubmitError(
37 'These files use ASSERT instead of using DCHECK:',
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,
48 using_std_abs_files = []
50 missing_std_prefix_files = []
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());
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)"
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())
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))
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))
87 def CheckPassByValue(input_api,
89 white_list=CC_SOURCE_FILES,
91 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
92 source_file_filter = lambda x: input_api.FilterSourceFile(x,
98 # Well-defined simple classes the same size as a primitive type.
99 pass_by_value_types = ['base::Time',
103 for f in input_api.AffectedSourceFiles(source_file_filter):
104 contents = input_api.ReadFile(f, 'rb')
106 r'\bconst +' + '(?P<type>(%s))&' %
107 string.join(pass_by_value_types, '|'),
110 local_errors.append(output_api.PresubmitError(
111 '%s passes %s by const ref instead of by value.' %
112 (f.LocalPath(), match.group('type'))))
115 def CheckTodos(input_api, output_api):
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())
125 return [output_api.PresubmitError(
126 'All TODO comments should be of the form TODO(name/bug). ' +
127 'Use TODO instead of FIX' + 'ME',
131 def CheckDoubleAngles(input_api, output_api, white_list=CC_SOURCE_FILES,
135 source_file_filter = lambda x: input_api.FilterSourceFile(x,
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())
144 return [output_api.PresubmitError('Use >> instead of > >:', items=errors)]
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
151 def FindUselessIfdefs(input_api, output_api):
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())
159 return [output_api.PresubmitError(
160 'Don\'t use #if '+'0; just delete the code.',
164 def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]):
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)
178 return False # The namespace is not used at all.
180 open_brace = len(contents)
182 close_brace = len(contents)
184 quote = len(contents)
186 next = min(open_brace, min(close_brace, min(quote, name)))
188 if next == open_brace:
190 elif next == close_brace:
193 quote_count = 0 if quote_count else 1
194 elif next == name and not quote_count:
197 if re.match(w, contents[next:]):
205 # Checks for the use of cc:: within the cc namespace, which is usually
207 def CheckNamespace(input_api, output_api):
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)
216 if FindNamespaceInBlock(match.end(), 'cc', contents, whitelist=whitelist):
217 errors.append(f.LocalPath())
220 return [output_api.PresubmitError(
221 'Do not use cc:: inside of the cc namespace.',
225 def CheckForUseOfWrongClock(input_api,
227 white_list=CC_SOURCE_FILES,
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,
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|$)'
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)::'
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:
249 # int64 foo_us = foo_s * Time::kMicrosecondsPerSecond;
250 using_base_time_decl_pattern = r'^\s*using\s+(::)?base::Time\s*;'
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+'
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')')
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):
267 ' %s:%d\n %s' % (f.LocalPath(), line_number, line.strip()))
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))]
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
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)
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'),]
297 def CheckChangeOnUpload(input_api, output_api):
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)
311 def PostUploadHook(cl, change, output_api):
312 """git cl upload will call this hook after the issue is created/modified.
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.
317 return output_api.EnsureCQIncludeTrybotsAreAdded(
320 'master.tryserver.blink:linux_trusty_blink_rel',
321 'luci.chromium.try:android_optional_gpu_tests_rel',
323 'Automatically added Blink and Android GPU trybots for CQ.')