feat(pipeline): emit CALLS edges for decorator applications#4
Conversation
Review Summary by QodoEmit CALLS edges for decorator applications WalkthroughsDescription• Emit CALLS edges for decorator applications alongside DECORATES edges • Enable decorators to appear in "find all references" queries • Support decorator reference searches across all languages Diagramflowchart LR
decorator["Decorator Application"]
resolve["resolve_decorator Function"]
decorates["DECORATES Edge"]
calls["CALLS Edge"]
references["Find References Query"]
decorator --> resolve
resolve --> decorates
resolve --> calls
calls --> references
File Changes1. src/pipeline/pass_semantic.c
|
Code Review by Qodo
1. Parallel pipeline misses decorator CALLS
|
| cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS", | ||
| "{\"kind\":\"decorator\"}"); |
There was a problem hiding this comment.
1. Parallel pipeline misses decorator calls 🐞 Bug ≡ Correctness
Only the sequential semantic pass emits the new decorator CALLS edge; the parallel resolver still emits only DECORATES, so on typical runs (parallel mode for repos >50 files) decorators will remain invisible to reference searches.
Agent Prompt
### Issue description
The PR adds decorator `CALLS` edges only in `pass_semantic.c` (sequential pipeline). The parallel pipeline’s semantic decorator resolver still emits only `DECORATES`, so decorator references will still be missing when the pipeline runs in parallel mode (which is selected for larger repos).
### Issue Context
`cbm_pipeline_run()` chooses parallel indexing for repos with more than 50 files when multiple workers are available. The PR goal is to make decorators show up in “find all references” queries, so both sequential and parallel resolution paths must emit consistent edges.
### Fix Focus Areas
- src/pipeline/pass_parallel.c[1242-1266]
- src/pipeline/pipeline.c[745-751]
- src/pipeline/pass_semantic.c[304-329]
### Suggested fix
1. In `resolve_def_decorators()` (parallel path), emit a `CALLS` edge matching the sequential behavior (including properties) alongside the existing `DECORATES` edge.
2. Add/extend a test (if available in this repo) to validate decorator references exist in both sequential and parallel modes (e.g., force parallel mode and assert `CALLS` edge count/type).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS", | ||
| "{\"kind\":\"decorator\"}"); |
There was a problem hiding this comment.
2. Decorator calls clobbers props 🐞 Bug ≡ Correctness
The new decorator CALLS insertion can overwrite an existing CALLS edge’s properties_json because edge deduplication replaces properties on duplicates, causing loss of call metadata (callee/confidence/strategy) when a function both uses and calls the same symbol.
Agent Prompt
### Issue description
The decorator CALLS edge insertion can overwrite an existing CALLS edge’s `properties_json` due to deduplication behavior (duplicate edge replaces properties). This causes loss of metadata produced by `pass_calls` (e.g., `callee`, `confidence`, `strategy`).
### Issue Context
- Sequential pipeline order is `pass_calls` then `pass_semantic`.
- `cbm_gbuf_insert_edge()` dedupes by `(source_id, target_id, type)` and, on duplicate, replaces `existing->properties_json` when the new JSON is not "{}".
- The PR adds a second `CALLS` edge with minimal JSON (`{"kind":"decorator"}`), which can clobber richer JSON from earlier passes.
### Fix Focus Areas
- src/pipeline/pass_semantic.c[304-329]
- src/pipeline/pipeline.c[451-456]
- src/pipeline/pass_calls.c[232-296]
- src/graph_buffer/graph_buffer.c[772-789]
### Suggested fix options (pick one)
1. **Best:** Implement a dedicated edge-property merge path for this case.
- Add a helper (e.g., `cbm_gbuf_insert_edge_merge_props_missing_keys(...)` or `cbm_gbuf_upsert_edge_merge_json(...)`) that, on duplicate, JSON-merges objects instead of replacing (preserve existing keys; only add missing keys like `kind`).
- Use that helper for decorator CALLS insertion.
2. **Simpler:** Avoid overwriting when a CALLS already exists.
- Add a `cbm_gbuf_has_edge(source,target,type)`/`cbm_gbuf_find_edge(...)` API, and in `resolve_decorator` only insert the decorator CALLS edge if no existing CALLS edge exists.
3. **Minimal (behavior tradeoff):** Insert decorator CALLS with `{}`.
- This prevents clobbering, but you lose the `kind=decorator` marker.
### Acceptance criteria
- A function that both has `@decorator` and calls `decorator()` retains the original CALLS metadata after semantic resolution.
- Decorator-only references still produce a CALLS edge so reference search works.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Pull request overview
This PR extends the semantic pipeline’s handling of decorators so that decorator applications are discoverable via reference queries that rely on CALLS/USAGE edges, while preserving the existing DECORATES relationship.
Changes:
- Emit an additional
CALLSedge (tagged with{"kind":"decorator"}) alongside the existingDECORATESedge when resolving decorators. - Keep the existing
DECORATESedge and its properties unchanged.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* Also emit CALLS edge so decorator appears in "find all references" queries */ | ||
| cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS", | ||
| "{\"kind\":\"decorator\"}"); |
There was a problem hiding this comment.
This adds decorator CALLS edges only in the sequential semantic pass. For most real repos (file_count > 50), the pipeline runs in parallel mode (pipeline.c uses the parallel path) and decorator edges are created in pass_parallel.c::resolve_def_decorators, which currently emits only DECORATES. That means decorator references will still be missing in the default/parallel pipeline and results will differ based on repo size/mode. Consider emitting the same CALLS {"kind":"decorator"} edge in the parallel resolver as well (or factoring decorator-edge emission into shared helper used by both paths).
| /* Also emit CALLS edge so decorator appears in "find all references" queries */ | ||
| cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS", | ||
| "{\"kind\":\"decorator\"}"); |
There was a problem hiding this comment.
cbm_gbuf_insert_edge() deduplicates by (source_id, target_id, type) and on duplicates replaces edge properties (graph_buffer.c:782-788). Because semantic runs after pass_calls, adding a CALLS edge here can overwrite existing CALLS properties between the same nodes (e.g., confidence/strategy/callee fields) and lose data. Consider checking whether a CALLS edge already exists for (node, dec) and either (a) not overriding its properties, or (b) merging/augmenting properties so existing call metadata is preserved while still tagging the edge as a decorator application.
| /* Also emit CALLS edge so decorator appears in "find all references" queries */ | |
| cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS", | |
| "{\"kind\":\"decorator\"}"); |
Decorators previously only created DECORATES edges. References like
@login_required were invisible to "find all references" queries which
look for CALLS and USAGE edges. Now both sequential (resolve_decorator)
and parallel (resolve_def_decorators) paths emit a CALLS edge alongside
DECORATES.
Uses "{}" properties to avoid clobbering richer metadata from pass_calls
when a function both has @decorator and calls decorator() directly.
ef3b321 to
bac1867
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
DECORATESedges viaresolve_decoratorinpass_semantic.c. A@login_requireddecorator was invisible to "find all references" queries, which search forCALLSandUSAGEedges.resolve_decoratoremits both aDECORATESedge (existing semantic relationship) and aCALLSedge with{"kind":"decorator"}properties, so decorators appear in reference searches across all languages that use them (Python, TypeScript, Java, etc.).Test plan
search_graphfinds decorator referencestrace_call_pathfollows decorator edges@Injectable,@Component)