Skip to content

[bug] self_recursive / unguarded_recursion match calls by bare name → super().save() and same-named wrappers are false positives #599

Description

@ecosuper2025

Version: codebase-memory-mcp v0.8.1
Project: real-world repo, 93,315 nodes

Summary

The recursion detector appears to flag a function as self-recursive whenever its body contains a call whose bare name equals the function's own name — ignoring the receiver. This makes super().method() overrides and same-named delegating wrappers false positives. In our repo, every unguarded_recursion=true node also has self_recursive=true, and a large fraction are not recursive at all.

Repro

MATCH (f:Function) WHERE f.unguarded_recursion = true RETURN f.name, f.qualified_name, f.file_path, f.self_recursive
(and the same for :Method). Representative false positives:

node actual body why it's not recursion
ConsumerAddress.save, ProductPriceHistory.save, PurchaseOrderV2.save super().save(*args, **kwargs) calls the parent class method
MPApiError.__init__, CloudAIExhausted.__init__ super().__init__(...) parent ctor
StoreAdmin.get_queryset, MerchantCompanyAdmin.get_queryset super().get_queryset(...) parent method
*.setUp, DecimalEncoder.default super().setUp() / super().default(o) parent method
ApiClient.get/post/put (TS) return this.request('GET', …) / axios.get(…) different receiver
console-style error/warn wrappers console.error(…) different receiver
minified bundle ids e,Vo,Ii (lines=1) minified not analyzable

Minimal example

class Order(models.Model):
    def save(self, *a, **k):
        self.full_clean()
        super().save(*a, **k)   # parent call — flagged as self-recursion today
class ApiClient {
  get(url: string) { return axios.get(url); }   // flagged as self-recursion today
}

Expected

  • A call is self-recursion only when it resolves to the same function/method (same receiver / same class binding), not by bare name.
  • Specifically: super().X() is a parent-class call and must never count as recursion of X.
  • Calls on a different object (axios.get, this.request, console.error) must not count.

Bonus (likely same subsystem)

transitive_loop_depth saturates to an impossible 174 on many unrelated functions (e.g. whatsapp_webhook, several mains) — looks like the interprocedural propagation doesn't break cycles and saturates. Worth checking alongside the recursion resolver.

Impact

unguarded_recursion and transitive_loop_depth are currently unusable as-is for hot-path/risk triage on Python/TS codebases (super() overrides are everywhere).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions