|
66 | 66 | symbol_id, |
67 | 67 | ) |
68 | 68 | from path_filtering import LayeredIgnore, iter_java_source_files |
69 | | -from java_ontology import VALID_CLIENT_KINDS, VALID_HTTP_CALL_MATCHES, VALID_PRODUCER_KINDS |
| 69 | +from java_ontology import ( |
| 70 | + CLIENT_KIND_FEIGN_METHOD, |
| 71 | + CLIENT_KIND_REST_TEMPLATE, |
| 72 | + VALID_CLIENT_KINDS, |
| 73 | + VALID_HTTP_CALL_MATCHES, |
| 74 | + VALID_PRODUCER_KINDS, |
| 75 | +) |
70 | 76 |
|
71 | 77 | log = logging.getLogger(__name__) |
72 | 78 |
|
@@ -2403,7 +2409,7 @@ def _phantom_async_route_id(call: OutgoingCallDecl) -> str: |
2403 | 2409 | ) |
2404 | 2410 | rid = "" |
2405 | 2411 | strategy = call.resolution_strategy |
2406 | | - if call.client_kind == "feign_method": |
| 2412 | + if call.client_kind == CLIENT_KIND_FEIGN_METHOD: |
2407 | 2413 | exposing = next((e for e in tables.exposes_rows if e.symbol_id == member.node_id), None) |
2408 | 2414 | if exposing is not None: |
2409 | 2415 | rid = exposing.route_id |
@@ -2606,7 +2612,7 @@ def _match_call_edge( |
2606 | 2612 | return "unresolved", [] |
2607 | 2613 |
|
2608 | 2614 | candidates: list[RouteRow] = [] |
2609 | | - if call.client_kind == "feign_method": |
| 2615 | + if call.client_kind == CLIENT_KIND_FEIGN_METHOD: |
2610 | 2616 | # Prefer endpoint matching by target service + path/method for Feign declarations. |
2611 | 2617 | path_value = call.path_template_call |
2612 | 2618 | method_value = call.method_call |
@@ -2735,7 +2741,7 @@ def _micro_factor(member: MemberEntry | None) -> float: |
2735 | 2741 | if src_route is None and member is not None: |
2736 | 2742 | # Recover feign caller hints from persisted caller-side Client declarations. |
2737 | 2743 | for client in client_hints_by_member.get(member.node_id, ()): |
2738 | | - if client.client_kind != "feign_method": |
| 2744 | + if client.client_kind != CLIENT_KIND_FEIGN_METHOD: |
2739 | 2745 | continue |
2740 | 2746 | path_template, path_regex = _normalize_path(client.path) |
2741 | 2747 | src_route = RouteRow( |
@@ -2771,7 +2777,7 @@ def _micro_factor(member: MemberEntry | None) -> float: |
2771 | 2777 | call = OutgoingCallDecl( |
2772 | 2778 | method_fqn=f"{member.parent_fqn}#{member.decl.signature}" if member else "", |
2773 | 2779 | method_sig=member.decl.signature if member else "", |
2774 | | - client_kind="feign_method" if _feign_like else "rest_template", |
| 2780 | + client_kind=CLIENT_KIND_FEIGN_METHOD if _feign_like else CLIENT_KIND_REST_TEMPLATE, |
2775 | 2781 | channel="http", |
2776 | 2782 | feign_target_name=src_route.feign_name if src_route else "", |
2777 | 2783 | feign_target_url=src_route.feign_url if src_route else "", |
@@ -3454,13 +3460,15 @@ def _write_edges(conn: ladybug.Connection, tables: GraphTables, _file_by_node_id |
3454 | 3460 | _bulk_copy(conn, "OVERRIDES", _REL_OVERRIDES_COLUMNS, overrides_rows) |
3455 | 3461 |
|
3456 | 3462 | # Stage CALLS rows with dedup and callee_declaring_role materialization |
3457 | | - seen_calls: set[tuple[str, str, int, int]] = set() |
| 3463 | + seen_calls: set[tuple[str, str, int, int, int]] = set() |
3458 | 3464 | calls_rows: list[dict] = [] |
3459 | 3465 | member_by_id = {m.node_id: m for m in tables.members} |
3460 | 3466 | for row in tables.calls_rows: |
3461 | 3467 | if row.src_id not in valid_ids or row.dst_id not in valid_ids: |
3462 | 3468 | continue |
3463 | | - key = (row.src_id, row.dst_id, row.arg_count, row.call_site_line) |
| 3469 | + # Include call_site_byte so two call sites of the same method on the same |
| 3470 | + # source line (same arg_count) are kept as distinct edges (issue #359). |
| 3471 | + key = (row.src_id, row.dst_id, row.arg_count, row.call_site_line, row.call_site_byte) |
3464 | 3472 | if key in seen_calls: |
3465 | 3473 | continue |
3466 | 3474 | seen_calls.add(key) |
@@ -3636,10 +3644,15 @@ def _write_routes_and_exposes(conn: ladybug.Connection, tables: GraphTables, _fi |
3636 | 3644 |
|
3637 | 3645 |
|
3638 | 3646 | def _write_meta(conn: ladybug.Connection, tables: GraphTables, source_root: Path) -> None: |
3639 | | - seen_calls: set[tuple[str, str, int, int]] = set() |
| 3647 | + # Dedup key MUST match _write_edges (build_ast_graph.py, _REL_CALLS writer): the |
| 3648 | + # 5-tuple includes call_site_byte so two call sites of the same method on the |
| 3649 | + # same source line are counted separately. A previous version used the 4-tuple |
| 3650 | + # here, which made counts['calls'] (678) diverge from the real CALLS edge count |
| 3651 | + # (684) that _write_edges actually persisted — describe/stats then undercounted. |
| 3652 | + seen_calls: set[tuple[str, str, int, int, int]] = set() |
3640 | 3653 | calls_unique = 0 |
3641 | 3654 | for row in tables.calls_rows: |
3642 | | - key = (row.src_id, row.dst_id, row.arg_count, row.call_site_line) |
| 3655 | + key = (row.src_id, row.dst_id, row.arg_count, row.call_site_line, row.call_site_byte) |
3643 | 3656 | if key not in seen_calls: |
3644 | 3657 | seen_calls.add(key) |
3645 | 3658 | calls_unique += 1 |
|
0 commit comments