sourcecode 2.5.1__tar.gz → 2.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. {sourcecode-2.5.1 → sourcecode-2.5.2}/CHANGELOG.md +31 -0
  2. {sourcecode-2.5.1 → sourcecode-2.5.2}/PKG-INFO +1 -1
  3. {sourcecode-2.5.1 → sourcecode-2.5.2}/pyproject.toml +1 -1
  4. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/repository_ir.py +22 -5
  6. {sourcecode-2.5.1 → sourcecode-2.5.2}/.github/workflows/build-windows.yml +0 -0
  7. {sourcecode-2.5.1 → sourcecode-2.5.2}/.gitignore +0 -0
  8. {sourcecode-2.5.1 → sourcecode-2.5.2}/.ruff.toml +0 -0
  9. {sourcecode-2.5.1 → sourcecode-2.5.2}/CONTRIBUTING.md +0 -0
  10. {sourcecode-2.5.1 → sourcecode-2.5.2}/LICENSE +0 -0
  11. {sourcecode-2.5.1 → sourcecode-2.5.2}/README.md +0 -0
  12. {sourcecode-2.5.1 → sourcecode-2.5.2}/SECURITY.md +0 -0
  13. {sourcecode-2.5.1 → sourcecode-2.5.2}/raw +0 -0
  14. {sourcecode-2.5.1 → sourcecode-2.5.2}/scripts/compare_integration_engines.py +0 -0
  15. {sourcecode-2.5.1 → sourcecode-2.5.2}/scripts/customer_smoke_test.sh +0 -0
  16. {sourcecode-2.5.1 → sourcecode-2.5.2}/scripts/generate_jdk_exports.py +0 -0
  17. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/adaptive_scanner.py +0 -0
  18. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/archetype.py +0 -0
  19. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/architecture_analyzer.py +0 -0
  20. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/architecture_summary.py +0 -0
  21. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/ast_extractor.py +0 -0
  22. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/cache.py +0 -0
  23. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/call_surface.py +0 -0
  24. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/caller_metrics.py +0 -0
  25. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/canonical_ir.py +0 -0
  26. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/cir_graphs.py +0 -0
  27. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/classifier.py +0 -0
  28. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/cli.py +0 -0
  29. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/code_notes_analyzer.py +0 -0
  30. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/confidence_analyzer.py +0 -0
  31. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/context_cache.py +0 -0
  32. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/context_graph.py +0 -0
  33. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/context_scorer.py +0 -0
  34. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/context_summarizer.py +0 -0
  35. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/contract_model.py +0 -0
  36. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/contract_pipeline.py +0 -0
  37. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/coverage_parser.py +0 -0
  38. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/dependency_analyzer.py +0 -0
  39. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/__init__.py +0 -0
  40. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/base.py +0 -0
  41. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/csproj_parser.py +0 -0
  42. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/dart.py +0 -0
  43. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/dotnet.py +0 -0
  44. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/elixir.py +0 -0
  45. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/go.py +0 -0
  46. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/heuristic.py +0 -0
  47. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/hybrid.py +0 -0
  48. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/java.py +0 -0
  49. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/jvm_ext.py +0 -0
  50. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/nodejs.py +0 -0
  51. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/parsers.py +0 -0
  52. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/php.py +0 -0
  53. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/project.py +0 -0
  54. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/python.py +0 -0
  55. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/ruby.py +0 -0
  56. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/rust.py +0 -0
  57. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/systems.py +0 -0
  58. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/terraform.py +0 -0
  59. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/detectors/tooling.py +0 -0
  60. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/doc_analyzer.py +0 -0
  61. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/dynamic_argument_surface.py +0 -0
  62. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/endpoint_literals.py +0 -0
  63. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/endpoint_metrics.py +0 -0
  64. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/entrypoint_classifier.py +0 -0
  65. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/env_analyzer.py +0 -0
  66. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/error_schema.py +0 -0
  67. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/evidence_provider.py +0 -0
  68. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/explain.py +0 -0
  69. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/file_chunker.py +0 -0
  70. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/file_classifier.py +0 -0
  71. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/format_contract.py +0 -0
  72. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/fqn_utils.py +0 -0
  73. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/git_analyzer.py +0 -0
  74. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/graph_analyzer.py +0 -0
  75. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/graph_evidence.py +0 -0
  76. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/hibernate_strat.py +0 -0
  77. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/jdk_exports.py +0 -0
  78. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/license.py +0 -0
  79. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/__init__.py +0 -0
  80. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  81. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  82. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  83. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  84. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  85. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/orchestrator.py +0 -0
  86. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/registry.py +0 -0
  87. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/runner.py +0 -0
  88. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp/server.py +0 -0
  89. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/mcp_nudge.py +0 -0
  90. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/metrics_analyzer.py +0 -0
  91. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/migrate_check.py +0 -0
  92. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/openapi_surface.py +0 -0
  93. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/output_budget.py +0 -0
  94. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/path_filters.py +0 -0
  95. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/pr_comment_renderer.py +0 -0
  96. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/pr_impact.py +0 -0
  97. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/prepare_context.py +0 -0
  98. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/progress.py +0 -0
  99. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/ranking_engine.py +0 -0
  100. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/redactor.py +0 -0
  101. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/relevance_scorer.py +0 -0
  102. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/rename_refactor.py +0 -0
  103. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/repo_classifier.py +0 -0
  104. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/__init__.py +0 -0
  105. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/context.py +0 -0
  106. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/errors.py +0 -0
  107. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/executor.py +0 -0
  108. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/planner.py +0 -0
  109. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/query.py +0 -0
  110. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/request.py +0 -0
  111. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/resolution.py +0 -0
  112. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/result.py +0 -0
  113. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/retriever.py +0 -0
  114. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/runtime.py +0 -0
  115. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps.py +0 -0
  116. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  117. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_graph.py +0 -0
  118. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_impact.py +0 -0
  119. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_intf.py +0 -0
  120. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_struct.py +0 -0
  121. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  122. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/ris.py +0 -0
  123. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/runtime_classifier.py +0 -0
  124. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/scanner.py +0 -0
  125. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/schema.py +0 -0
  126. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/security_config.py +0 -0
  127. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/security_posture.py +0 -0
  128. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/semantic_analyzer.py +0 -0
  129. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/semantic_impact_engine.py +0 -0
  130. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/semantic_integration_engine.py +0 -0
  131. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/semantic_services.py +0 -0
  132. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/serializer.py +0 -0
  133. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_event_topology.py +0 -0
  134. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_findings.py +0 -0
  135. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_impact.py +0 -0
  136. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_model.py +0 -0
  137. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_security_audit.py +0 -0
  138. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_semantic.py +0 -0
  139. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/spring_tx_analyzer.py +0 -0
  140. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/summarizer.py +0 -0
  141. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/__init__.py +0 -0
  142. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/config.py +0 -0
  143. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/consent.py +0 -0
  144. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/events.py +0 -0
  145. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/filters.py +0 -0
  146. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/telemetry/transport.py +0 -0
  147. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/tree_utils.py +0 -0
  148. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/type_usage_surface.py +0 -0
  149. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/validation_inference.py +0 -0
  150. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/validation_surface.py +0 -0
  151. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/version_check.py +0 -0
  152. {sourcecode-2.5.1 → sourcecode-2.5.2}/src/sourcecode/workspace.py +0 -0
  153. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/functions/README.md +0 -0
  154. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/functions/get-license/index.ts +0 -0
  155. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  156. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/functions/telemetry/index.ts +0 -0
  157. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/sql/license_event_ordering.sql +0 -0
  158. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/sql/licensing_schema.sql +0 -0
  159. {sourcecode-2.5.1 → sourcecode-2.5.2}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.5.2] — 2026-07-14
4
+
5
+ **impact-chain correctness + cold-scan performance.** Two fixes surfaced by an
6
+ adversarial audit across 12 real repositories (alfresco, keycloak, neo4j, jenkins,
7
+ openmrs, ofbiz, jobrunr, eureka, BroadleafCommerce, spring-petclinic, and torture
8
+ fixtures).
9
+
10
+ ### Fixed
11
+
12
+ - **impact-chain lost same-class method callers (P0)** (`spring_impact.py`): a method
13
+ whose only callers were sibling methods in the same class (e.g. a private helper
14
+ `doWork()` invoked by `callWork()`) came back with **0 direct callers** — a false
15
+ "isolated / safe" verdict that defeated the 2.5.1 intra-class caller feature. The
16
+ `calls` edge existed in the reverse graph but the caller BFS discarded it: the
17
+ whole-class self-exclusion (v1.70.0 BUG #2) was applied to *method*-scoped queries,
18
+ where a *different* method on the same class is a legitimate caller, not
19
+ self-reference. Fix threads the existing `_method_scoped` flag into `_bfs_callers`;
20
+ method-scoped queries now exclude only genuine self-recursion, class-scoped queries
21
+ keep the cohesion exclusion. Verified: `TxService#doPrivateWork` → `[callPrivate]`
22
+ (was `[]`); cross-class blast radius unchanged (petclinic 13 endpoints).
23
+
24
+ - **Quadratic cold scan on method-dense classes (P1)** (`repository_ir.py`):
25
+ `_intra_class_call_edges` ran a fresh regex search *per sibling method name* over each
26
+ method body — O(methods × body_len) per class, i.e. quadratic in method count. Large,
27
+ method-dense classes (generated parsers, multi-thousand-line test classes) made cold
28
+ scans pathological. Rewritten to a single pass per body (`_INTRA_CALL_RE`) that
29
+ collects call-site names and intersects them with the sibling index — O(body_len),
30
+ byte-identical edge set. Cold-scan wins: openmrs 43s→19s (2.3×), alfresco 220s→126s
31
+ (1.75×), jenkins 43s→35s. `_build_relations` cumulative time dropped 36.6s→3.9s
32
+ (openmrs profile).
33
+
3
34
  ## [2.5.1] — 2026-07-14
4
35
 
5
36
  **impact-chain correctness fix — field-injected callers are no longer lost.** A service
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 2.5.1
3
+ Version: 2.5.2
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "2.5.1"
7
+ version = "2.5.2"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.5.1"
7
+ __version__ = "2.5.2"
@@ -1489,6 +1489,16 @@ def _method_body(raw_lines: list[str], start_idx: int) -> str:
1489
1489
  return "\n".join(out)
1490
1490
 
1491
1491
 
1492
+ # Intra-class call-site matcher: a bare `name(` (not preceded by a word char or '.')
1493
+ # OR an explicit `this.name(`. Group 1 = bare callee, group 2 = this-qualified callee.
1494
+ # Encodes the SAME two match forms the old per-sibling regex used, so the derived edge
1495
+ # set is byte-identical — but as ONE alternation scanned once per body instead of once
1496
+ # per sibling name (see _intra_class_call_edges perf note).
1497
+ _INTRA_CALL_RE = re.compile(
1498
+ r'(?<![\w.])([A-Za-z_]\w*)\s*\(' r'|\bthis\s*\.\s*([A-Za-z_]\w*)\s*\('
1499
+ )
1500
+
1501
+
1492
1502
  def _intra_class_call_edges(symbols: list[SymbolRecord], source: str) -> list[RelationEdge]:
1493
1503
  """Method-level `calls` edges for intra-class invocations.
1494
1504
 
@@ -1528,13 +1538,20 @@ def _intra_class_call_edges(symbols: list[SymbolRecord], source: str) -> list[Re
1528
1538
  if not body:
1529
1539
  continue
1530
1540
  body = _STRING_LITERAL_RE.sub('', _strip_java_comments(body))
1531
- for name, fqns in sib.items():
1541
+ # PERF: one pass over the body collects every bare / this-qualified callee name,
1542
+ # then intersect with the sibling index. The old code ran a fresh regex search
1543
+ # per sibling name — O(siblings × body_len) per method, i.e. quadratic in method
1544
+ # count, which made large method-dense classes (generated parsers, 4k-line test
1545
+ # classes) pathological (alfresco cold scan ~220s). This is O(body_len). The
1546
+ # edge set is identical: _INTRA_CALL_RE matches the same two call forms.
1547
+ called: set[str] = set()
1548
+ for m in _INTRA_CALL_RE.finditer(body):
1549
+ called.add(m.group(1) or m.group(2))
1550
+ for name in sorted(called): # sorted → deterministic edge order
1532
1551
  if name in _CALL_KEYWORDS:
1533
1552
  continue
1534
- # bare `name(` (not preceded by word char or '.') OR explicit `this.name(`
1535
- pat = (r'(?<![\w.])' + re.escape(name) + r'\s*\('
1536
- + r'|\bthis\s*\.\s*' + re.escape(name) + r'\s*\(')
1537
- if not re.search(pat, body):
1553
+ fqns = sib.get(name)
1554
+ if not fqns:
1538
1555
  continue
1539
1556
  for tgt in fqns:
1540
1557
  if tgt == caller.symbol:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes