| @@ 5168-5304 (lines=137) @@ | ||
| 5165 | return False |
|
| 5166 | ||
| 5167 | ||
| 5168 | def CheckForNonConstReference(filename, clean_lines, linenum, |
|
| 5169 | nesting_state, error): |
|
| 5170 | """Check for non-const references. |
|
| 5171 | ||
| 5172 | Separate from CheckLanguage since it scans backwards from current |
|
| 5173 | line, instead of scanning forward. |
|
| 5174 | ||
| 5175 | Args: |
|
| 5176 | filename: The name of the current file. |
|
| 5177 | clean_lines: A CleansedLines instance containing the file. |
|
| 5178 | linenum: The number of the line to check. |
|
| 5179 | nesting_state: A NestingState instance which maintains information about |
|
| 5180 | the current stack of nested blocks being parsed. |
|
| 5181 | error: The function to call with any errors found. |
|
| 5182 | """ |
|
| 5183 | # Do nothing if there is no '&' on current line. |
|
| 5184 | line = clean_lines.elided[linenum] |
|
| 5185 | if '&' not in line: |
|
| 5186 | return |
|
| 5187 | ||
| 5188 | # If a function is inherited, current function doesn't have much of |
|
| 5189 | # a choice, so any non-const references should not be blamed on |
|
| 5190 | # derived function. |
|
| 5191 | if IsDerivedFunction(clean_lines, linenum): |
|
| 5192 | return |
|
| 5193 | ||
| 5194 | # Don't warn on out-of-line method definitions, as we would warn on the |
|
| 5195 | # in-line declaration, if it isn't marked with 'override'. |
|
| 5196 | if IsOutOfLineMethodDefinition(clean_lines, linenum): |
|
| 5197 | return |
|
| 5198 | ||
| 5199 | # Long type names may be broken across multiple lines, usually in one |
|
| 5200 | # of these forms: |
|
| 5201 | # LongType |
|
| 5202 | # ::LongTypeContinued &identifier |
|
| 5203 | # LongType:: |
|
| 5204 | # LongTypeContinued &identifier |
|
| 5205 | # LongType< |
|
| 5206 | # ...>::LongTypeContinued &identifier |
|
| 5207 | # |
|
| 5208 | # If we detected a type split across two lines, join the previous |
|
| 5209 | # line to current line so that we can match const references |
|
| 5210 | # accordingly. |
|
| 5211 | # |
|
| 5212 | # Note that this only scans back one line, since scanning back |
|
| 5213 | # arbitrary number of lines would be expensive. If you have a type |
|
| 5214 | # that spans more than 2 lines, please use a typedef. |
|
| 5215 | if linenum > 1: |
|
| 5216 | previous = None |
|
| 5217 | if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): |
|
| 5218 | # previous_line\n + ::current_line |
|
| 5219 | previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', |
|
| 5220 | clean_lines.elided[linenum - 1]) |
|
| 5221 | elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): |
|
| 5222 | # previous_line::\n + current_line |
|
| 5223 | previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', |
|
| 5224 | clean_lines.elided[linenum - 1]) |
|
| 5225 | if previous: |
|
| 5226 | line = previous.group(1) + line.lstrip() |
|
| 5227 | else: |
|
| 5228 | # Check for templated parameter that is split across multiple lines |
|
| 5229 | endpos = line.rfind('>') |
|
| 5230 | if endpos > -1: |
|
| 5231 | (_, startline, startpos) = ReverseCloseExpression( |
|
| 5232 | clean_lines, linenum, endpos) |
|
| 5233 | if startpos > -1 and startline < linenum: |
|
| 5234 | # Found the matching < on an earlier line, collect all |
|
| 5235 | # pieces up to current line. |
|
| 5236 | line = '' |
|
| 5237 | for i in xrange(startline, linenum + 1): |
|
| 5238 | line += clean_lines.elided[i].strip() |
|
| 5239 | ||
| 5240 | # Check for non-const references in function parameters. A single '&' may |
|
| 5241 | # found in the following places: |
|
| 5242 | # inside expression: binary & for bitwise AND |
|
| 5243 | # inside expression: unary & for taking the address of something |
|
| 5244 | # inside declarators: reference parameter |
|
| 5245 | # We will exclude the first two cases by checking that we are not inside a |
|
| 5246 | # function body, including one that was just introduced by a trailing '{'. |
|
| 5247 | # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. |
|
| 5248 | if (nesting_state.previous_stack_top and |
|
| 5249 | not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or |
|
| 5250 | isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): |
|
| 5251 | # Not at toplevel, not within a class, and not within a namespace |
|
| 5252 | return |
|
| 5253 | ||
| 5254 | # Avoid initializer lists. We only need to scan back from the |
|
| 5255 | # current line for something that starts with ':'. |
|
| 5256 | # |
|
| 5257 | # We don't need to check the current line, since the '&' would |
|
| 5258 | # appear inside the second set of parentheses on the current line as |
|
| 5259 | # opposed to the first set. |
|
| 5260 | if linenum > 0: |
|
| 5261 | for i in xrange(linenum - 1, max(0, linenum - 10), -1): |
|
| 5262 | previous_line = clean_lines.elided[i] |
|
| 5263 | if not Search(r'[),]\s*$', previous_line): |
|
| 5264 | break |
|
| 5265 | if Match(r'^\s*:\s+\S', previous_line): |
|
| 5266 | return |
|
| 5267 | ||
| 5268 | # Avoid preprocessors |
|
| 5269 | if Search(r'\\\s*$', line): |
|
| 5270 | return |
|
| 5271 | ||
| 5272 | # Avoid constructor initializer lists |
|
| 5273 | if IsInitializerList(clean_lines, linenum): |
|
| 5274 | return |
|
| 5275 | ||
| 5276 | # We allow non-const references in a few standard places, like functions |
|
| 5277 | # called "swap()" or iostream operators like "<<" or ">>". Do not check |
|
| 5278 | # those function parameters. |
|
| 5279 | # |
|
| 5280 | # We also accept & in static_assert, which looks like a function but |
|
| 5281 | # it's actually a declaration expression. |
|
| 5282 | whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' |
|
| 5283 | r'operator\s*[<>][<>]|' |
|
| 5284 | r'static_assert|COMPILE_ASSERT' |
|
| 5285 | r')\s*\(') |
|
| 5286 | if Search(whitelisted_functions, line): |
|
| 5287 | return |
|
| 5288 | elif not Search(r'\S+\([^)]*$', line): |
|
| 5289 | # Don't see a whitelisted function on this line. Actually we |
|
| 5290 | # didn't see any function name on this line, so this is likely a |
|
| 5291 | # multi-line parameter list. Try a bit harder to catch this case. |
|
| 5292 | for i in xrange(2): |
|
| 5293 | if (linenum > i and |
|
| 5294 | Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): |
|
| 5295 | return |
|
| 5296 | ||
| 5297 | decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body |
|
| 5298 | for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): |
|
| 5299 | if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and |
|
| 5300 | not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): |
|
| 5301 | error(filename, linenum, 'runtime/references', 2, |
|
| 5302 | 'Is this a non-const reference? ' |
|
| 5303 | 'If so, make const or use a pointer: ' + |
|
| 5304 | ReplaceAll(' *<', '<', parameter)) |
|
| 5305 | ||
| 5306 | ||
| 5307 | def CheckCasts(filename, clean_lines, linenum, error): |
|
| @@ 5168-5304 (lines=137) @@ | ||
| 5165 | return False |
|
| 5166 | ||
| 5167 | ||
| 5168 | def CheckForNonConstReference(filename, clean_lines, linenum, |
|
| 5169 | nesting_state, error): |
|
| 5170 | """Check for non-const references. |
|
| 5171 | ||
| 5172 | Separate from CheckLanguage since it scans backwards from current |
|
| 5173 | line, instead of scanning forward. |
|
| 5174 | ||
| 5175 | Args: |
|
| 5176 | filename: The name of the current file. |
|
| 5177 | clean_lines: A CleansedLines instance containing the file. |
|
| 5178 | linenum: The number of the line to check. |
|
| 5179 | nesting_state: A NestingState instance which maintains information about |
|
| 5180 | the current stack of nested blocks being parsed. |
|
| 5181 | error: The function to call with any errors found. |
|
| 5182 | """ |
|
| 5183 | # Do nothing if there is no '&' on current line. |
|
| 5184 | line = clean_lines.elided[linenum] |
|
| 5185 | if '&' not in line: |
|
| 5186 | return |
|
| 5187 | ||
| 5188 | # If a function is inherited, current function doesn't have much of |
|
| 5189 | # a choice, so any non-const references should not be blamed on |
|
| 5190 | # derived function. |
|
| 5191 | if IsDerivedFunction(clean_lines, linenum): |
|
| 5192 | return |
|
| 5193 | ||
| 5194 | # Don't warn on out-of-line method definitions, as we would warn on the |
|
| 5195 | # in-line declaration, if it isn't marked with 'override'. |
|
| 5196 | if IsOutOfLineMethodDefinition(clean_lines, linenum): |
|
| 5197 | return |
|
| 5198 | ||
| 5199 | # Long type names may be broken across multiple lines, usually in one |
|
| 5200 | # of these forms: |
|
| 5201 | # LongType |
|
| 5202 | # ::LongTypeContinued &identifier |
|
| 5203 | # LongType:: |
|
| 5204 | # LongTypeContinued &identifier |
|
| 5205 | # LongType< |
|
| 5206 | # ...>::LongTypeContinued &identifier |
|
| 5207 | # |
|
| 5208 | # If we detected a type split across two lines, join the previous |
|
| 5209 | # line to current line so that we can match const references |
|
| 5210 | # accordingly. |
|
| 5211 | # |
|
| 5212 | # Note that this only scans back one line, since scanning back |
|
| 5213 | # arbitrary number of lines would be expensive. If you have a type |
|
| 5214 | # that spans more than 2 lines, please use a typedef. |
|
| 5215 | if linenum > 1: |
|
| 5216 | previous = None |
|
| 5217 | if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): |
|
| 5218 | # previous_line\n + ::current_line |
|
| 5219 | previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', |
|
| 5220 | clean_lines.elided[linenum - 1]) |
|
| 5221 | elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): |
|
| 5222 | # previous_line::\n + current_line |
|
| 5223 | previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', |
|
| 5224 | clean_lines.elided[linenum - 1]) |
|
| 5225 | if previous: |
|
| 5226 | line = previous.group(1) + line.lstrip() |
|
| 5227 | else: |
|
| 5228 | # Check for templated parameter that is split across multiple lines |
|
| 5229 | endpos = line.rfind('>') |
|
| 5230 | if endpos > -1: |
|
| 5231 | (_, startline, startpos) = ReverseCloseExpression( |
|
| 5232 | clean_lines, linenum, endpos) |
|
| 5233 | if startpos > -1 and startline < linenum: |
|
| 5234 | # Found the matching < on an earlier line, collect all |
|
| 5235 | # pieces up to current line. |
|
| 5236 | line = '' |
|
| 5237 | for i in xrange(startline, linenum + 1): |
|
| 5238 | line += clean_lines.elided[i].strip() |
|
| 5239 | ||
| 5240 | # Check for non-const references in function parameters. A single '&' may |
|
| 5241 | # found in the following places: |
|
| 5242 | # inside expression: binary & for bitwise AND |
|
| 5243 | # inside expression: unary & for taking the address of something |
|
| 5244 | # inside declarators: reference parameter |
|
| 5245 | # We will exclude the first two cases by checking that we are not inside a |
|
| 5246 | # function body, including one that was just introduced by a trailing '{'. |
|
| 5247 | # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. |
|
| 5248 | if (nesting_state.previous_stack_top and |
|
| 5249 | not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or |
|
| 5250 | isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): |
|
| 5251 | # Not at toplevel, not within a class, and not within a namespace |
|
| 5252 | return |
|
| 5253 | ||
| 5254 | # Avoid initializer lists. We only need to scan back from the |
|
| 5255 | # current line for something that starts with ':'. |
|
| 5256 | # |
|
| 5257 | # We don't need to check the current line, since the '&' would |
|
| 5258 | # appear inside the second set of parentheses on the current line as |
|
| 5259 | # opposed to the first set. |
|
| 5260 | if linenum > 0: |
|
| 5261 | for i in xrange(linenum - 1, max(0, linenum - 10), -1): |
|
| 5262 | previous_line = clean_lines.elided[i] |
|
| 5263 | if not Search(r'[),]\s*$', previous_line): |
|
| 5264 | break |
|
| 5265 | if Match(r'^\s*:\s+\S', previous_line): |
|
| 5266 | return |
|
| 5267 | ||
| 5268 | # Avoid preprocessors |
|
| 5269 | if Search(r'\\\s*$', line): |
|
| 5270 | return |
|
| 5271 | ||
| 5272 | # Avoid constructor initializer lists |
|
| 5273 | if IsInitializerList(clean_lines, linenum): |
|
| 5274 | return |
|
| 5275 | ||
| 5276 | # We allow non-const references in a few standard places, like functions |
|
| 5277 | # called "swap()" or iostream operators like "<<" or ">>". Do not check |
|
| 5278 | # those function parameters. |
|
| 5279 | # |
|
| 5280 | # We also accept & in static_assert, which looks like a function but |
|
| 5281 | # it's actually a declaration expression. |
|
| 5282 | whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' |
|
| 5283 | r'operator\s*[<>][<>]|' |
|
| 5284 | r'static_assert|COMPILE_ASSERT' |
|
| 5285 | r')\s*\(') |
|
| 5286 | if Search(whitelisted_functions, line): |
|
| 5287 | return |
|
| 5288 | elif not Search(r'\S+\([^)]*$', line): |
|
| 5289 | # Don't see a whitelisted function on this line. Actually we |
|
| 5290 | # didn't see any function name on this line, so this is likely a |
|
| 5291 | # multi-line parameter list. Try a bit harder to catch this case. |
|
| 5292 | for i in xrange(2): |
|
| 5293 | if (linenum > i and |
|
| 5294 | Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): |
|
| 5295 | return |
|
| 5296 | ||
| 5297 | decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body |
|
| 5298 | for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): |
|
| 5299 | if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and |
|
| 5300 | not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): |
|
| 5301 | error(filename, linenum, 'runtime/references', 2, |
|
| 5302 | 'Is this a non-const reference? ' |
|
| 5303 | 'If so, make const or use a pointer: ' + |
|
| 5304 | ReplaceAll(' *<', '<', parameter)) |
|
| 5305 | ||
| 5306 | ||
| 5307 | def CheckCasts(filename, clean_lines, linenum, error): |
|