MCPcopy Create free account
hub / github.com/Serial-Studio/Serial-Studio / _recursion_findings

Function _recursion_findings

scripts/code_verify_rules.py:719–786  ·  view source on GitHub ↗

Flag direct self-recursion in a hotpath method. Recursion at kHz rates blows the i-cache (200+ cyc for an L2 miss), trashes the RAS predictor (mispredict on every return), and prevents inlining. Only flags **stack** recursion. The following are NOT recursion and are skipped: -

(func_node, fname: str, body, src: bytes, fenced)

Source from the content-addressed store, hash-verified

717
718
719def _recursion_findings(func_node, fname: str, body, src: bytes, fenced) -> list:
720 """Flag direct self-recursion in a hotpath method. Recursion at kHz
721 rates blows the i-cache (200+ cyc for an L2 miss), trashes the RAS
722 predictor (mispredict on every return), and prevents inlining.
723
724 Only flags **stack** recursion. The following are NOT recursion and
725 are skipped:
726 - Calls inside a `lambda_expression` body (deferred to whichever
727 executor consumes the lambda; doesn't grow the stack here).
728 - Qualified calls like `Base::fname(...)` or `Foo::fname(...)`
729 (statically dispatched to a different function).
730 - Method calls on a different object (`other.fname(...)`,
731 `other->fname(...)`); only bare `fname(...)` and `this->fname(...)`
732 are real self-calls."""
733 if body is None or not fname or fname not in _HOTPATH_METHODS:
734 return []
735 findings: list = []
736 seen_lines: set = set()
737 for node in _walk(body):
738 if node.type != "call_expression":
739 continue
740 # Skip calls that live inside a lambda body -- those execute when
741 # the lambda runs, not on this call's stack frame.
742 cur = node.parent
743 in_lambda = False
744 while cur is not None and cur is not body:
745 if cur.type == "lambda_expression":
746 in_lambda = True
747 break
748 cur = cur.parent
749 if in_lambda:
750 continue
751 callee = node.child_by_field_name("function")
752 if callee is None:
753 continue
754 # Recognise: `fname(...)` (identifier) or `this->fname(...)` (field_expression
755 # whose object is `this`). Reject `Foo::fname`, `obj.fname`, `obj->fname`.
756 is_self = False
757 if callee.type == "identifier":
758 is_self = _node_text(callee, src) == fname
759 elif callee.type == "field_expression":
760 obj = callee.child_by_field_name("argument")
761 field = callee.child_by_field_name("field")
762 if (
763 obj is not None
764 and field is not None
765 and obj.type == "this"
766 and _node_text(field, src) == fname
767 ):
768 is_self = True
769 if not is_self:
770 continue
771 line = node.start_point[0] + 1
772 if fenced(line) or line in seen_lines:
773 continue
774 seen_lines.add(line)
775 findings.append(
776 Finding(

Callers 1

_cpp_rulesFunction · 0.85

Calls 7

_walkFunction · 0.85
_node_textFunction · 0.85
fencedFunction · 0.85
FindingClass · 0.70
setFunction · 0.50
addMethod · 0.45
appendMethod · 0.45

Tested by

no test coverage detected