Skip to content

feat(pipeline): emit CALLS edges for decorator applications#4

Merged
map588 merged 3 commits intomainfrom
feat/decorator-references
Apr 6, 2026
Merged

feat(pipeline): emit CALLS edges for decorator applications#4
map588 merged 3 commits intomainfrom
feat/decorator-references

Conversation

@map588
Copy link
Copy Markdown
Owner

@map588 map588 commented Apr 5, 2026

Summary

  • Decorators previously only created DECORATES edges via resolve_decorator in pass_semantic.c. A @login_required decorator was invisible to "find all references" queries, which search for CALLS and USAGE edges.
  • Now resolve_decorator emits both a DECORATES edge (existing semantic relationship) and a CALLS edge with {"kind":"decorator"} properties, so decorators appear in reference searches across all languages that use them (Python, TypeScript, Java, etc.).

Test plan

  • Full test suite passes (2718 tests) with ASan + UBSan
  • Index a Python project with decorators and verify search_graph finds decorator references
  • Verify trace_call_path follows decorator edges
  • Test with TypeScript decorators (@Injectable, @Component)

Copilot AI review requested due to automatic review settings April 5, 2026 00:58
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Emit CALLS edges for decorator applications
✨ Enhancement

Grey Divider

Walkthroughs

Description
• 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
Diagram
flowchart 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
Loading

Grey Divider

File Changes

1. src/pipeline/pass_semantic.c ✨ Enhancement +3/-0

Emit CALLS edges for decorator applications

• Added CALLS edge emission in resolve_decorator function
• CALLS edge includes {"kind":"decorator"} properties
• Decorators now visible to reference search queries
• Maintains existing DECORATES edge functionality

src/pipeline/pass_semantic.c


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 5, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX Issues (0)

Grey Divider


Action required

1. Parallel pipeline misses decorator CALLS 🐞 Bug ≡ Correctness
Description
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.
Code

src/pipeline/pass_semantic.c[R325-326]

+        cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS",
+                             "{\"kind\":\"decorator\"}");
Evidence
The PR adds CALLS emission inside resolve_decorator in the sequential semantic pass. However,
cbm_pipeline_run selects the parallel pipeline when worker_count > 1 and file_count > 50, and the
parallel semantic resolver’s decorator path only inserts DECORATES edges, so the new behavior won’t
apply in that common execution path.

src/pipeline/pass_semantic.c[304-329]
src/pipeline/pipeline.c[745-751]
src/pipeline/pass_parallel.c[1242-1266]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### 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


2. Decorator CALLS clobbers props 🐞 Bug ≡ Correctness
Description
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.
Code

src/pipeline/pass_semantic.c[R325-326]

+        cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS",
+                             "{\"kind\":\"decorator\"}");
Evidence
In the sequential pipeline, pass_calls runs before pass_semantic, so a normal CALLS edge (with rich
properties) may already exist when resolve_decorator inserts a second CALLS edge for the same
(source,target,type). cbm_gbuf_insert_edge deduplicates by (source_id,target_id,type) and replaces
existing->properties_json when the new properties_json is not "{}", so the decorator insertion can
silently replace the original CALLS metadata with just {"kind":"decorator"}.

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]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### 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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread src/pipeline/pass_semantic.c Outdated
Comment on lines +325 to +326
cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS",
"{\"kind\":\"decorator\"}");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Comment thread src/pipeline/pass_semantic.c Outdated
Comment on lines +325 to +326
cbm_gbuf_insert_edge(ctx->gbuf, node->id, dec->id, "CALLS",
"{\"kind\":\"decorator\"}");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CALLS edge (tagged with {"kind":"decorator"}) alongside the existing DECORATES edge when resolving decorators.
  • Keep the existing DECORATES edge and its properties unchanged.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/pipeline/pass_semantic.c Outdated
Comment on lines +324 to +326
/* 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\"}");
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment thread src/pipeline/pass_semantic.c Outdated
Comment on lines +324 to +326
/* 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\"}");
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
/* 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\"}");

Copilot uses AI. Check for mistakes.
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.
@map588 map588 force-pushed the feat/decorator-references branch from ef3b321 to bac1867 Compare April 5, 2026 01:09
map588 and others added 2 commits April 5, 2026 15:07
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@map588 map588 merged commit b7779c7 into main Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants