Return parameter names that this function treats as sinks. A sink parameter is one where pass-by-value + move is the right call instead of pass-by-const-ref: - The body (or a constructor's field initializer list) calls `std::move( )`. - The body ends with `return <
(func_node, src: bytes)
| 388 | |
| 389 | |
| 390 | def _sink_param_names(func_node, src: bytes) -> set: |
| 391 | """Return parameter names that this function treats as sinks. |
| 392 | |
| 393 | A sink parameter is one where pass-by-value + move is the right call |
| 394 | instead of pass-by-const-ref: |
| 395 | - The body (or a constructor's field initializer list) calls |
| 396 | `std::move(<param>)`. |
| 397 | - The body ends with `return <param>;` AFTER mutating it -- the |
| 398 | param is the function's return value, so the implicit move on |
| 399 | `return` makes the by-value form at least as cheap as |
| 400 | `const T&` + explicit copy.""" |
| 401 | names: set = set() |
| 402 | body = func_node.child_by_field_name("body") |
| 403 | if body is not None: |
| 404 | body_text = _node_text(body, src) |
| 405 | for m in re.finditer(r"\bstd::move\s*\(\s*([A-Za-z_]\w*)\s*\)", body_text): |
| 406 | names.add(m.group(1)) |
| 407 | # `return <name>;` at any point in the body: param flows out as |
| 408 | # the return value, which the compiler implicitly moves from. |
| 409 | for m in re.finditer(r"\breturn\s+([A-Za-z_]\w*)\s*;", body_text): |
| 410 | names.add(m.group(1)) |
| 411 | for c in func_node.children: |
| 412 | if c.type != "field_initializer_list": |
| 413 | continue |
| 414 | for m in re.finditer( |
| 415 | r"\bstd::move\s*\(\s*([A-Za-z_]\w*)\s*\)", _node_text(c, src) |
| 416 | ): |
| 417 | names.add(m.group(1)) |
| 418 | return names |
| 419 | |
| 420 | |
| 421 | def _parameter_perf_findings(func_node, src: bytes, fenced) -> list: |
no test coverage detected