Skip to content

fix(extract): scan every attribute_list sibling, not just the first (#1692) - #1759

Open
Jumaga2015 wants to merge 3 commits into
DeusData:mainfrom
Jumaga2015:fix/1692-csharp-multi-attribute-decorates
Open

fix(extract): scan every attribute_list sibling, not just the first (#1692)#1759
Jumaga2015 wants to merge 3 commits into
DeusData:mainfrom
Jumaga2015:fix/1692-csharp-multi-attribute-decorates

Conversation

@Jumaga2015

Copy link
Copy Markdown

Summary

  • Addresses C#: log calls inside Kafka-consumer classes misclassified as Route nodes; ASP.NET Core [Route]/[HttpGet] attribute routes not extracted (cross-repo-intelligence returns 0 edges) #1692 (Repro 2 — the ASP.NET route-extraction half, not the Kafka
    misclassification half, which is a separate call-resolution defect left
    for its own atomic fix as requested in the issue thread): a C# method or
    class with 2+ stacked attributes only got a DECORATES edge for the
    first bracket group. find_jvm_modifiers() used
    ts_node_child_by_field_name(), which returns only the first matching
    child, so a route attribute stacked behind another one (e.g.
    [ServiceFilter(...)] then [Route("api/[controller]")]) was silently
    invisible to references/symbols and to route extraction.
  • find_jvm_modifiers() now returns every matching wrapper via a new
    cbm_find_children_by_kind() helper; both callers (extract_decorators,
    scan_route_annotations) iterate all of them.
  • C#-only: Java/Kotlin/Swift use the modifiers wrapper (never repeats);
    PHP already groups a stack under one attribute_list.

Test plan

  • repro_issue1692 (Makefile.cbm TEST_REPRO_SRCS) — indexes
    [Foo][Bar("x")][Baz] through the real production pipeline and
    asserts DECORATES == 3. Proven RED with the pre-fix lookup
    (decorates=1), GREEN with the fix.
  • scripts/test.sh CC=clang CXX=clang++ — full suite, no regressions
  • scripts/lint.sh CC=clang CXX=clang++ — clang-tidy (diff-scoped),
    clang-format, cppcheck all clean

Made with Claude Code

A C# attribute stack ([Foo][Bar][Baz]) compiles to separate sibling
attribute_list nodes, one per bracket group — not one attribute_list holding
several entries. find_jvm_modifiers() used ts_node_child_by_field_name(),
which only ever returns the first child registered under a given field, so
every attribute after the first bracket group was silently dropped and never
produced a DECORATES edge (DeusData#1692).

find_jvm_modifiers() now returns every matching wrapper (count + out array,
capped at MAX_ATTR_WRAPPERS) instead of a single TSNode, and its two callers
(extract_decorators, scan_route_annotations) iterate all of them. The
multi-match child scan lives in a new helper, cbm_find_children_by_kind()
(helpers.c/helpers.h), the multi-match sibling of the existing
cbm_find_child_by_kind() — keeps find_jvm_modifiers() a short per-language
dispatch that delegates the traversal, same shape it had before this fix.

Both callers size `wrappers` at MAX_ATTR_WRAPPERS. scan_route_annotations
appends the owner node after the wrappers, so its fill is capped one lower
(MAX_ATTR_WRAPPERS_MINUS_1) rather than the array being declared one larger
— the MAX_X / MAX_X_MINUS_1 pair this file already uses for MAX_BASES,
MAX_PARAMS and MAX_RETURN_TYPES.

No-op for the other languages on this switch: Java/Kotlin/Swift use the
`modifiers` wrapper, a single node that never repeats, and PHP groups a
`#[A] #[B] #[C]` stack under ONE attribute_list holding three
attribute_group children, which the previous lookup already returned whole.

Signed-off-by: Jumaga2015 <jumaga2015@gmail.com>
A C# method carrying three stacked attributes ([Foo][Bar("x")][Baz]) must
end up with three DECORATES edges, one per bracket group. The fixture is
indexed through the real production pipeline (rh_index) and asserts
rh_count_edges(..., "DECORATES") == 3, so it exercises the MCP path rather
than the extractor in isolation.

Verified to actually reproduce: with find_jvm_modifiers() reverted to its
first-child lookup the suite goes red (decorates=1, expected 3), and green
again with the fix in place.

C#-only by design. Parsing the equivalent fixtures with the vendored
grammars shows `[A] [B] [C]` yields three sibling `attribute_list` nodes in
C#, while PHP's `#[A] #[B] #[C]` yields ONE `attribute_list` holding three
`attribute_group` children — the pre-fix lookup already found all of them,
so PHP could not hit this bug and a stacked-PHP fixture would pass either
way. Java/Kotlin/Swift use the `modifiers` wrapper, a single node that never
repeats. PHP's DECORATES path stays covered by mkc_c3_php8_attribute
(tests/test_matrix_known_classes.c).

Registered in repro_main.c and Makefile.cbm's TEST_REPRO_SRCS.

Signed-off-by: Jumaga2015 <jumaga2015@gmail.com>
@Jumaga2015
Jumaga2015 requested a review from DeusData as a code owner August 20, 2026 09:35
@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@DeusData DeusData added bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Aug 24, 2026
@DeusData

Copy link
Copy Markdown
Owner

Thank you for keeping the ASP.NET route-extraction half separate from the Kafka classification defect. I checked current main: find_jvm_modifiers() obtains the matching wrapper with ts_node_child_by_field_name(), so only the first repeated C# attribute_list can reach decorator and route scanning. That grounds the stacked-attribute failure.

I have labeled this as a high-priority parsing bug and routed it for review. The small Makefile change is only the focused repro registration, not a broader build-system redesign. Our review queue is full, so detailed review may take a little time. Thank you for splitting the two #1692 defects and covering the production pipeline path.

@DeusData

DeusData commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Approved, and this is now the surviving fix for the stacked-attribute bug. #1895 addressed the same defect and has been closed in favour of this one — its author found the bug independently and their diagnosis was right; yours is the more complete repair.

Fixing the helper rather than one caller is why. find_jvm_modifiers() has two callers on main:

extract_defs.c:1714   scan_route_annotations   (route extraction)
extract_defs.c:1981   extract_decorators

The other PR iterated wrappers inside extract_decorators and left the helper returning a single node, so :1714 still saw only the first attribute_list. Since route extraction runs through scan_route_annotations, that version would have corrected the decorators array while leaving #1692's actual reported symptom — [ServiceFilter(...)] then [Route("api/[controller]")], with the route invisible — exactly where it was.

Your version fixes the source, so both callers are correct and the next one is correct without having to know it needed to iterate.

Scoping to C# with a stated reason is right. Java, Kotlin and Swift use the modifiers wrapper, which does not repeat; PHP groups a stack under a single attribute_list with several attribute_group children. I checked the PHP grammar on that point specifically — the node names bear you out, and I had previously told the other contributor the opposite, which was my error and not theirs.

And the repro is the right shape. repro_issue1692 indexing [Foo][Bar("x")][Baz] through the real production pipeline and asserting DECORATES == 3, proven RED at 1 before the fix, tests the behaviour end to end rather than the helper in isolation. A unit test on cbm_find_children_by_kind() would have passed against a pipeline that still dropped the attributes.

Splitting the Kafka misclassification out as its own atomic fix, as the issue thread asked, was also the right call.

Status

Clearance is REVIEW(1) on Makefile.cbm — registering the new repro source, the established shape here. It needs a maintainer marker rather than a change from you.

main moved three times yesterday (broken by a duplicate-symbol merge, repaired by #1993, then #1703 landed), so I will update this branch and re-run before merging. Nothing needed from you.

Brings the branch onto current main (fe85a6b) as promised on the PR: the
only conflict was internal/cbm/helpers.h, where both sides added
declarations after cbm_find_child_by_kind; both blocks are kept (the PR's
cbm_find_children_by_kind prototype, then main's Lisp-family gates).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData

DeusData commented Sep 5, 2026

Copy link
Copy Markdown
Owner

As promised on 09-01, I've updated this branch myself rather than asking you to: 683d4bc merges current main into it. The only conflict was internal/cbm/helpers.h, where both sides had added declarations after cbm_find_child_by_kind — both blocks are kept, your cbm_find_children_by_kind prototype first. Your two commits are untouched. Verified on the merged tree before pushing: the #1692 repro is red with main's extract_defs.c and green with yours, and extraction plus the Java/Kotlin/PHP/C# LSP suites and pipeline are green (1,472 cases). CI runs now; I'll merge on green. Nothing needed from you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants