@@ 5307-5421 (lines=115) @@ | ||
5304 | ReplaceAll(' *<', '<', parameter)) |
|
5305 | ||
5306 | ||
5307 | def CheckCasts(filename, clean_lines, linenum, error): |
|
5308 | """Various cast related checks. |
|
5309 | ||
5310 | Args: |
|
5311 | filename: The name of the current file. |
|
5312 | clean_lines: A CleansedLines instance containing the file. |
|
5313 | linenum: The number of the line to check. |
|
5314 | error: The function to call with any errors found. |
|
5315 | """ |
|
5316 | line = clean_lines.elided[linenum] |
|
5317 | ||
5318 | # Check to see if they're using an conversion function cast. |
|
5319 | # I just try to capture the most common basic types, though there are more. |
|
5320 | # Parameterless conversion functions, such as bool(), are allowed as they are |
|
5321 | # probably a member operator declaration or default constructor. |
|
5322 | match = Search( |
|
5323 | r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' |
|
5324 | r'(int|float|double|bool|char|int32|uint32|int64|uint64)' |
|
5325 | r'(\([^)].*)', line) |
|
5326 | expecting_function = ExpectingFunctionArgs(clean_lines, linenum) |
|
5327 | if match and not expecting_function: |
|
5328 | matched_type = match.group(2) |
|
5329 | ||
5330 | # matched_new_or_template is used to silence two false positives: |
|
5331 | # - New operators |
|
5332 | # - Template arguments with function types |
|
5333 | # |
|
5334 | # For template arguments, we match on types immediately following |
|
5335 | # an opening bracket without any spaces. This is a fast way to |
|
5336 | # silence the common case where the function type is the first |
|
5337 | # template argument. False negative with less-than comparison is |
|
5338 | # avoided because those operators are usually followed by a space. |
|
5339 | # |
|
5340 | # function<double(double)> // bracket + no space = false positive |
|
5341 | # value < double(42) // bracket + space = true positive |
|
5342 | matched_new_or_template = match.group(1) |
|
5343 | ||
5344 | # Avoid arrays by looking for brackets that come after the closing |
|
5345 | # parenthesis. |
|
5346 | if Match(r'\([^()]+\)\s*\[', match.group(3)): |
|
5347 | return |
|
5348 | ||
5349 | # Other things to ignore: |
|
5350 | # - Function pointers |
|
5351 | # - Casts to pointer types |
|
5352 | # - Placement new |
|
5353 | # - Alias declarations |
|
5354 | matched_funcptr = match.group(3) |
|
5355 | if (matched_new_or_template is None and |
|
5356 | not (matched_funcptr and |
|
5357 | (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', |
|
5358 | matched_funcptr) or |
|
5359 | matched_funcptr.startswith('(*)'))) and |
|
5360 | not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and |
|
5361 | not Search(r'new\(\S+\)\s*' + matched_type, line)): |
|
5362 | error(filename, linenum, 'readability/casting', 4, |
|
5363 | 'Using deprecated casting style. ' |
|
5364 | 'Use static_cast<%s>(...) instead' % |
|
5365 | matched_type) |
|
5366 | ||
5367 | if not expecting_function: |
|
5368 | CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', |
|
5369 | r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) |
|
5370 | ||
5371 | # This doesn't catch all cases. Consider (const char * const)"hello". |
|
5372 | # |
|
5373 | # (char *) "foo" should always be a const_cast (reinterpret_cast won't |
|
5374 | # compile). |
|
5375 | if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', |
|
5376 | r'\((char\s?\*+\s?)\)\s*"', error): |
|
5377 | pass |
|
5378 | else: |
|
5379 | # Check pointer casts for other than string constants |
|
5380 | CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', |
|
5381 | r'\((\w+\s?\*+\s?)\)', error) |
|
5382 | ||
5383 | # In addition, we look for people taking the address of a cast. This |
|
5384 | # is dangerous -- casts can assign to temporaries, so the pointer doesn't |
|
5385 | # point where you think. |
|
5386 | # |
|
5387 | # Some non-identifier character is required before the '&' for the |
|
5388 | # expression to be recognized as a cast. These are casts: |
|
5389 | # expression = &static_cast<int*>(temporary()); |
|
5390 | # function(&(int*)(temporary())); |
|
5391 | # |
|
5392 | # This is not a cast: |
|
5393 | # reference_type&(int* function_param); |
|
5394 | match = Search( |
|
5395 | r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' |
|
5396 | r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) |
|
5397 | if match: |
|
5398 | # Try a better error message when the & is bound to something |
|
5399 | # dereferenced by the casted pointer, as opposed to the casted |
|
5400 | # pointer itself. |
|
5401 | parenthesis_error = False |
|
5402 | match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) |
|
5403 | if match: |
|
5404 | _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) |
|
5405 | if x1 >= 0 and clean_lines.elided[y1][x1] == '(': |
|
5406 | _, y2, x2 = CloseExpression(clean_lines, y1, x1) |
|
5407 | if x2 >= 0: |
|
5408 | extended_line = clean_lines.elided[y2][x2:] |
|
5409 | if y2 < clean_lines.NumLines() - 1: |
|
5410 | extended_line += clean_lines.elided[y2 + 1] |
|
5411 | if Match(r'\s*(?:->|\[)', extended_line): |
|
5412 | parenthesis_error = True |
|
5413 | ||
5414 | if parenthesis_error: |
|
5415 | error(filename, linenum, 'readability/casting', 4, |
|
5416 | ('Are you taking an address of something dereferenced ' |
|
5417 | 'from a cast? Wrapping the dereferenced expression in ' |
|
5418 | 'parentheses will make the binding more obvious')) |
|
5419 | else: |
|
5420 | error(filename, linenum, 'runtime/casting', 4, |
|
5421 | ('Are you taking an address of a cast? ' |
|
5422 | 'This is dangerous: could be a temp var. ' |
|
5423 | 'Take the address before doing the cast, rather than after')) |
|
5424 |
@@ 5307-5421 (lines=115) @@ | ||
5304 | ReplaceAll(' *<', '<', parameter)) |
|
5305 | ||
5306 | ||
5307 | def CheckCasts(filename, clean_lines, linenum, error): |
|
5308 | """Various cast related checks. |
|
5309 | ||
5310 | Args: |
|
5311 | filename: The name of the current file. |
|
5312 | clean_lines: A CleansedLines instance containing the file. |
|
5313 | linenum: The number of the line to check. |
|
5314 | error: The function to call with any errors found. |
|
5315 | """ |
|
5316 | line = clean_lines.elided[linenum] |
|
5317 | ||
5318 | # Check to see if they're using an conversion function cast. |
|
5319 | # I just try to capture the most common basic types, though there are more. |
|
5320 | # Parameterless conversion functions, such as bool(), are allowed as they are |
|
5321 | # probably a member operator declaration or default constructor. |
|
5322 | match = Search( |
|
5323 | r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' |
|
5324 | r'(int|float|double|bool|char|int32|uint32|int64|uint64)' |
|
5325 | r'(\([^)].*)', line) |
|
5326 | expecting_function = ExpectingFunctionArgs(clean_lines, linenum) |
|
5327 | if match and not expecting_function: |
|
5328 | matched_type = match.group(2) |
|
5329 | ||
5330 | # matched_new_or_template is used to silence two false positives: |
|
5331 | # - New operators |
|
5332 | # - Template arguments with function types |
|
5333 | # |
|
5334 | # For template arguments, we match on types immediately following |
|
5335 | # an opening bracket without any spaces. This is a fast way to |
|
5336 | # silence the common case where the function type is the first |
|
5337 | # template argument. False negative with less-than comparison is |
|
5338 | # avoided because those operators are usually followed by a space. |
|
5339 | # |
|
5340 | # function<double(double)> // bracket + no space = false positive |
|
5341 | # value < double(42) // bracket + space = true positive |
|
5342 | matched_new_or_template = match.group(1) |
|
5343 | ||
5344 | # Avoid arrays by looking for brackets that come after the closing |
|
5345 | # parenthesis. |
|
5346 | if Match(r'\([^()]+\)\s*\[', match.group(3)): |
|
5347 | return |
|
5348 | ||
5349 | # Other things to ignore: |
|
5350 | # - Function pointers |
|
5351 | # - Casts to pointer types |
|
5352 | # - Placement new |
|
5353 | # - Alias declarations |
|
5354 | matched_funcptr = match.group(3) |
|
5355 | if (matched_new_or_template is None and |
|
5356 | not (matched_funcptr and |
|
5357 | (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', |
|
5358 | matched_funcptr) or |
|
5359 | matched_funcptr.startswith('(*)'))) and |
|
5360 | not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and |
|
5361 | not Search(r'new\(\S+\)\s*' + matched_type, line)): |
|
5362 | error(filename, linenum, 'readability/casting', 4, |
|
5363 | 'Using deprecated casting style. ' |
|
5364 | 'Use static_cast<%s>(...) instead' % |
|
5365 | matched_type) |
|
5366 | ||
5367 | if not expecting_function: |
|
5368 | CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', |
|
5369 | r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) |
|
5370 | ||
5371 | # This doesn't catch all cases. Consider (const char * const)"hello". |
|
5372 | # |
|
5373 | # (char *) "foo" should always be a const_cast (reinterpret_cast won't |
|
5374 | # compile). |
|
5375 | if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', |
|
5376 | r'\((char\s?\*+\s?)\)\s*"', error): |
|
5377 | pass |
|
5378 | else: |
|
5379 | # Check pointer casts for other than string constants |
|
5380 | CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', |
|
5381 | r'\((\w+\s?\*+\s?)\)', error) |
|
5382 | ||
5383 | # In addition, we look for people taking the address of a cast. This |
|
5384 | # is dangerous -- casts can assign to temporaries, so the pointer doesn't |
|
5385 | # point where you think. |
|
5386 | # |
|
5387 | # Some non-identifier character is required before the '&' for the |
|
5388 | # expression to be recognized as a cast. These are casts: |
|
5389 | # expression = &static_cast<int*>(temporary()); |
|
5390 | # function(&(int*)(temporary())); |
|
5391 | # |
|
5392 | # This is not a cast: |
|
5393 | # reference_type&(int* function_param); |
|
5394 | match = Search( |
|
5395 | r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' |
|
5396 | r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) |
|
5397 | if match: |
|
5398 | # Try a better error message when the & is bound to something |
|
5399 | # dereferenced by the casted pointer, as opposed to the casted |
|
5400 | # pointer itself. |
|
5401 | parenthesis_error = False |
|
5402 | match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) |
|
5403 | if match: |
|
5404 | _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) |
|
5405 | if x1 >= 0 and clean_lines.elided[y1][x1] == '(': |
|
5406 | _, y2, x2 = CloseExpression(clean_lines, y1, x1) |
|
5407 | if x2 >= 0: |
|
5408 | extended_line = clean_lines.elided[y2][x2:] |
|
5409 | if y2 < clean_lines.NumLines() - 1: |
|
5410 | extended_line += clean_lines.elided[y2 + 1] |
|
5411 | if Match(r'\s*(?:->|\[)', extended_line): |
|
5412 | parenthesis_error = True |
|
5413 | ||
5414 | if parenthesis_error: |
|
5415 | error(filename, linenum, 'readability/casting', 4, |
|
5416 | ('Are you taking an address of something dereferenced ' |
|
5417 | 'from a cast? Wrapping the dereferenced expression in ' |
|
5418 | 'parentheses will make the binding more obvious')) |
|
5419 | else: |
|
5420 | error(filename, linenum, 'runtime/casting', 4, |
|
5421 | ('Are you taking an address of a cast? ' |
|
5422 | 'This is dangerous: could be a temp var. ' |
|
5423 | 'Take the address before doing the cast, rather than after')) |
|
5424 |