| @@ 4269-4384 (lines=116) @@ | ||
| 4266 | return (None, -1) |
|
| 4267 | ||
| 4268 | ||
| 4269 | def CheckCheck(filename, clean_lines, linenum, error): |
|
| 4270 | """Checks the use of CHECK and EXPECT macros. |
|
| 4271 | ||
| 4272 | Args: |
|
| 4273 | filename: The name of the current file. |
|
| 4274 | clean_lines: A CleansedLines instance containing the file. |
|
| 4275 | linenum: The number of the line to check. |
|
| 4276 | error: The function to call with any errors found. |
|
| 4277 | """ |
|
| 4278 | ||
| 4279 | # Decide the set of replacement macros that should be suggested |
|
| 4280 | lines = clean_lines.elided |
|
| 4281 | (check_macro, start_pos) = FindCheckMacro(lines[linenum]) |
|
| 4282 | if not check_macro: |
|
| 4283 | return |
|
| 4284 | ||
| 4285 | # Find end of the boolean expression by matching parentheses |
|
| 4286 | (last_line, end_line, end_pos) = CloseExpression( |
|
| 4287 | clean_lines, linenum, start_pos) |
|
| 4288 | if end_pos < 0: |
|
| 4289 | return |
|
| 4290 | ||
| 4291 | # If the check macro is followed by something other than a |
|
| 4292 | # semicolon, assume users will log their own custom error messages |
|
| 4293 | # and don't suggest any replacements. |
|
| 4294 | if not Match(r'\s*;', last_line[end_pos:]): |
|
| 4295 | return |
|
| 4296 | ||
| 4297 | if linenum == end_line: |
|
| 4298 | expression = lines[linenum][start_pos + 1:end_pos - 1] |
|
| 4299 | else: |
|
| 4300 | expression = lines[linenum][start_pos + 1:] |
|
| 4301 | for i in xrange(linenum + 1, end_line): |
|
| 4302 | expression += lines[i] |
|
| 4303 | expression += last_line[0:end_pos - 1] |
|
| 4304 | ||
| 4305 | # Parse expression so that we can take parentheses into account. |
|
| 4306 | # This avoids false positives for inputs like "CHECK((a < 4) == b)", |
|
| 4307 | # which is not replaceable by CHECK_LE. |
|
| 4308 | lhs = '' |
|
| 4309 | rhs = '' |
|
| 4310 | operator = None |
|
| 4311 | while expression: |
|
| 4312 | matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' |
|
| 4313 | r'==|!=|>=|>|<=|<|\()(.*)$', expression) |
|
| 4314 | if matched: |
|
| 4315 | token = matched.group(1) |
|
| 4316 | if token == '(': |
|
| 4317 | # Parenthesized operand |
|
| 4318 | expression = matched.group(2) |
|
| 4319 | (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) |
|
| 4320 | if end < 0: |
|
| 4321 | return # Unmatched parenthesis |
|
| 4322 | lhs += '(' + expression[0:end] |
|
| 4323 | expression = expression[end:] |
|
| 4324 | elif token in ('&&', '||'): |
|
| 4325 | # Logical and/or operators. This means the expression |
|
| 4326 | # contains more than one term, for example: |
|
| 4327 | # CHECK(42 < a && a < b); |
|
| 4328 | # |
|
| 4329 | # These are not replaceable with CHECK_LE, so bail out early. |
|
| 4330 | return |
|
| 4331 | elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): |
|
| 4332 | # Non-relational operator |
|
| 4333 | lhs += token |
|
| 4334 | expression = matched.group(2) |
|
| 4335 | else: |
|
| 4336 | # Relational operator |
|
| 4337 | operator = token |
|
| 4338 | rhs = matched.group(2) |
|
| 4339 | break |
|
| 4340 | else: |
|
| 4341 | # Unparenthesized operand. Instead of appending to lhs one character |
|
| 4342 | # at a time, we do another regular expression match to consume several |
|
| 4343 | # characters at once if possible. Trivial benchmark shows that this |
|
| 4344 | # is more efficient when the operands are longer than a single |
|
| 4345 | # character, which is generally the case. |
|
| 4346 | matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) |
|
| 4347 | if not matched: |
|
| 4348 | matched = Match(r'^(\s*\S)(.*)$', expression) |
|
| 4349 | if not matched: |
|
| 4350 | break |
|
| 4351 | lhs += matched.group(1) |
|
| 4352 | expression = matched.group(2) |
|
| 4353 | ||
| 4354 | # Only apply checks if we got all parts of the boolean expression |
|
| 4355 | if not (lhs and operator and rhs): |
|
| 4356 | return |
|
| 4357 | ||
| 4358 | # Check that rhs do not contain logical operators. We already know |
|
| 4359 | # that lhs is fine since the loop above parses out && and ||. |
|
| 4360 | if rhs.find('&&') > -1 or rhs.find('||') > -1: |
|
| 4361 | return |
|
| 4362 | ||
| 4363 | # At least one of the operands must be a constant literal. This is |
|
| 4364 | # to avoid suggesting replacements for unprintable things like |
|
| 4365 | # CHECK(variable != iterator) |
|
| 4366 | # |
|
| 4367 | # The following pattern matches decimal, hex integers, strings, and |
|
| 4368 | # characters (in that order). |
|
| 4369 | lhs = lhs.strip() |
|
| 4370 | rhs = rhs.strip() |
|
| 4371 | match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' |
|
| 4372 | if Match(match_constant, lhs) or Match(match_constant, rhs): |
|
| 4373 | # Note: since we know both lhs and rhs, we can provide a more |
|
| 4374 | # descriptive error message like: |
|
| 4375 | # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) |
|
| 4376 | # Instead of: |
|
| 4377 | # Consider using CHECK_EQ instead of CHECK(a == b) |
|
| 4378 | # |
|
| 4379 | # We are still keeping the less descriptive message because if lhs |
|
| 4380 | # or rhs gets long, the error message might become unreadable. |
|
| 4381 | error(filename, linenum, 'readability/check', 2, |
|
| 4382 | 'Consider using %s instead of %s(a %s b)' % ( |
|
| 4383 | _CHECK_REPLACEMENT[check_macro][operator], |
|
| 4384 | check_macro, operator)) |
|
| 4385 | ||
| 4386 | ||
| 4387 | def CheckAltTokens(filename, clean_lines, linenum, error): |
|
| @@ 4269-4384 (lines=116) @@ | ||
| 4266 | return (None, -1) |
|
| 4267 | ||
| 4268 | ||
| 4269 | def CheckCheck(filename, clean_lines, linenum, error): |
|
| 4270 | """Checks the use of CHECK and EXPECT macros. |
|
| 4271 | ||
| 4272 | Args: |
|
| 4273 | filename: The name of the current file. |
|
| 4274 | clean_lines: A CleansedLines instance containing the file. |
|
| 4275 | linenum: The number of the line to check. |
|
| 4276 | error: The function to call with any errors found. |
|
| 4277 | """ |
|
| 4278 | ||
| 4279 | # Decide the set of replacement macros that should be suggested |
|
| 4280 | lines = clean_lines.elided |
|
| 4281 | (check_macro, start_pos) = FindCheckMacro(lines[linenum]) |
|
| 4282 | if not check_macro: |
|
| 4283 | return |
|
| 4284 | ||
| 4285 | # Find end of the boolean expression by matching parentheses |
|
| 4286 | (last_line, end_line, end_pos) = CloseExpression( |
|
| 4287 | clean_lines, linenum, start_pos) |
|
| 4288 | if end_pos < 0: |
|
| 4289 | return |
|
| 4290 | ||
| 4291 | # If the check macro is followed by something other than a |
|
| 4292 | # semicolon, assume users will log their own custom error messages |
|
| 4293 | # and don't suggest any replacements. |
|
| 4294 | if not Match(r'\s*;', last_line[end_pos:]): |
|
| 4295 | return |
|
| 4296 | ||
| 4297 | if linenum == end_line: |
|
| 4298 | expression = lines[linenum][start_pos + 1:end_pos - 1] |
|
| 4299 | else: |
|
| 4300 | expression = lines[linenum][start_pos + 1:] |
|
| 4301 | for i in xrange(linenum + 1, end_line): |
|
| 4302 | expression += lines[i] |
|
| 4303 | expression += last_line[0:end_pos - 1] |
|
| 4304 | ||
| 4305 | # Parse expression so that we can take parentheses into account. |
|
| 4306 | # This avoids false positives for inputs like "CHECK((a < 4) == b)", |
|
| 4307 | # which is not replaceable by CHECK_LE. |
|
| 4308 | lhs = '' |
|
| 4309 | rhs = '' |
|
| 4310 | operator = None |
|
| 4311 | while expression: |
|
| 4312 | matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' |
|
| 4313 | r'==|!=|>=|>|<=|<|\()(.*)$', expression) |
|
| 4314 | if matched: |
|
| 4315 | token = matched.group(1) |
|
| 4316 | if token == '(': |
|
| 4317 | # Parenthesized operand |
|
| 4318 | expression = matched.group(2) |
|
| 4319 | (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) |
|
| 4320 | if end < 0: |
|
| 4321 | return # Unmatched parenthesis |
|
| 4322 | lhs += '(' + expression[0:end] |
|
| 4323 | expression = expression[end:] |
|
| 4324 | elif token in ('&&', '||'): |
|
| 4325 | # Logical and/or operators. This means the expression |
|
| 4326 | # contains more than one term, for example: |
|
| 4327 | # CHECK(42 < a && a < b); |
|
| 4328 | # |
|
| 4329 | # These are not replaceable with CHECK_LE, so bail out early. |
|
| 4330 | return |
|
| 4331 | elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): |
|
| 4332 | # Non-relational operator |
|
| 4333 | lhs += token |
|
| 4334 | expression = matched.group(2) |
|
| 4335 | else: |
|
| 4336 | # Relational operator |
|
| 4337 | operator = token |
|
| 4338 | rhs = matched.group(2) |
|
| 4339 | break |
|
| 4340 | else: |
|
| 4341 | # Unparenthesized operand. Instead of appending to lhs one character |
|
| 4342 | # at a time, we do another regular expression match to consume several |
|
| 4343 | # characters at once if possible. Trivial benchmark shows that this |
|
| 4344 | # is more efficient when the operands are longer than a single |
|
| 4345 | # character, which is generally the case. |
|
| 4346 | matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) |
|
| 4347 | if not matched: |
|
| 4348 | matched = Match(r'^(\s*\S)(.*)$', expression) |
|
| 4349 | if not matched: |
|
| 4350 | break |
|
| 4351 | lhs += matched.group(1) |
|
| 4352 | expression = matched.group(2) |
|
| 4353 | ||
| 4354 | # Only apply checks if we got all parts of the boolean expression |
|
| 4355 | if not (lhs and operator and rhs): |
|
| 4356 | return |
|
| 4357 | ||
| 4358 | # Check that rhs do not contain logical operators. We already know |
|
| 4359 | # that lhs is fine since the loop above parses out && and ||. |
|
| 4360 | if rhs.find('&&') > -1 or rhs.find('||') > -1: |
|
| 4361 | return |
|
| 4362 | ||
| 4363 | # At least one of the operands must be a constant literal. This is |
|
| 4364 | # to avoid suggesting replacements for unprintable things like |
|
| 4365 | # CHECK(variable != iterator) |
|
| 4366 | # |
|
| 4367 | # The following pattern matches decimal, hex integers, strings, and |
|
| 4368 | # characters (in that order). |
|
| 4369 | lhs = lhs.strip() |
|
| 4370 | rhs = rhs.strip() |
|
| 4371 | match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' |
|
| 4372 | if Match(match_constant, lhs) or Match(match_constant, rhs): |
|
| 4373 | # Note: since we know both lhs and rhs, we can provide a more |
|
| 4374 | # descriptive error message like: |
|
| 4375 | # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) |
|
| 4376 | # Instead of: |
|
| 4377 | # Consider using CHECK_EQ instead of CHECK(a == b) |
|
| 4378 | # |
|
| 4379 | # We are still keeping the less descriptive message because if lhs |
|
| 4380 | # or rhs gets long, the error message might become unreadable. |
|
| 4381 | error(filename, linenum, 'readability/check', 2, |
|
| 4382 | 'Consider using %s instead of %s(a %s b)' % ( |
|
| 4383 | _CHECK_REPLACEMENT[check_macro][operator], |
|
| 4384 | check_macro, operator)) |
|
| 4385 | ||
| 4386 | ||
| 4387 | def CheckAltTokens(filename, clean_lines, linenum, error): |
|