fix: preserve Kotlin annotations + resolve C# namespace imports (#295, #310)#353
Open
azizur100389 wants to merge 1 commit intotirth8205:mainfrom
Open
Conversation
…h8205#295, tirth8205#310) Two parser correctness fixes that restore metadata lost by v2.3.2. Fix 1: Kotlin (and Java/C#/Python) annotation preservation (tirth8205#295) ----------------------------------------------------------------- ``_extract_functions`` already walked tree-sitter ``modifiers > annotation`` children to collect decorators, but used the list ONLY for test-kind detection and then discarded it. Kotlin ``@Composable``, ``@HiltViewModel``, ``@Inject``; Java ``@Entity``, ``@Autowired``; C# ``[HttpGet]``; Python decorators — all were missing from the persisted node. ``_extract_classes`` did not extract class-level annotations at all, so ``@HiltViewModel class MyViewModel``, ``@AndroidEntryPoint``, etc. lost their annotation metadata. Fix: * New module-level ``_extract_annotation_list(node)`` helper that centralises the Java/Kotlin/C# ``modifiers`` walk and the Python ``decorated_definition`` walk. * ``_extract_functions`` now persists annotations to ``node.modifiers`` (comma-separated string) AND ``node.extra["decorators"]`` (list) on every Function / Test node. * ``_extract_classes`` calls the same helper to preserve class-level annotations. Consumers can now write queries like "show me all @composable functions" or "find @hiltviewmodel classes" — both currently impossible on Kotlin codebases. Fix 2: C# namespace-based importers_of resolution (tirth8205#310) -------------------------------------------------------- C# ``using ACME.Core;`` directives produce an IMPORTS_FROM edge whose target is the raw namespace string (``"ACME.Core"``), not a file path. ``importers_of`` looked edges up by file path and never matched a namespace, so it returned ``[]`` for every .cs file. Fix: * New ``_extract_csharp_namespaces(root_node)`` helper handles both tree-sitter shapes — block (``namespace_declaration``) and C# 10+ file-scoped (``file_scoped_namespace_declaration``). * ``parse_bytes`` tags each C# File node with the namespaces it declares in ``extra["csharp_namespaces"]``. * ``tools/query.py::query_graph`` adds a namespace fallback to ``importers_of``: when the target is a .cs file, look up its declared namespaces and also search edges_by_target for each namespace string. Deduped against the primary lookup so duplicates never appear. This mirrors the existing bare-name fallback already used by ``callers_of`` and ``inheritors_of`` (see tirth8205#87). No parser restructuring needed — just an additional lookup path in the query layer. Tests added (tests/test_multilang.py) ------------------------------------- TestKotlinAnnotations (5 tests): * test_hilt_viewmodel_annotation_on_class * test_composable_annotation_on_function * test_multiple_annotations_on_function * test_unannotated_function_has_none_modifiers (guard: no leak) * test_test_annotation_still_triggers_test_kind (guard: pre-existing @test -> Test kind behavior preserved) TestCSharpNamespaceResolution (5 tests): * test_file_scoped_namespace_tagged_on_file_node (C# 10+ form) * test_block_namespace_tagged_on_file_node (classic form) * test_multiple_namespaces_in_one_file * test_non_csharp_file_has_no_namespace_tag (guard: scope limit) * test_importers_of_resolves_namespace_to_file (end-to-end) Test results ------------ Stage 1 (new regression tests): 10/10 passed. Stage 2 (tests/test_multilang.py full): 203 passed, 38 pre-existing failures (verified identical on unchanged main). Stage 3 (tests/test_parser.py + tests/test_tools.py): adjacent tests match main baseline. Stage 4 (full suite): 1016 passed, 73 pre-existing failures (verified identical set on unchanged main via comm -23 diff). Stage 5 (ruff check on parser.py + query.py + test_multilang.py): clean. Stage 6 (mypy on parser.py + query.py): clean. Zero regressions. All fixes follow patterns already used in the codebase (inheritors_of namespace fallback, File node extra tagging mirroring the Swift swift_kind pattern).
Contributor
Author
|
Heads up on CI: the 3 failing checks (lint, type-check, test) are pre-existing baseline failures on
All three are addressed by @gzenz's existing #342 "chore: fix CI lint and type errors on main". For the actual changes in this PR:
Happy to rebase once #342 lands. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two parser correctness fixes that restore metadata and resolution paths lost by v2.3.2.
Closes #295, closes #310.
Fix 1 — Kotlin (and Java/C#/Python) annotation preservation (#295)
_extract_functionsalready walked tree-sittermodifiers > annotationchildren to collect decorators, but used the list only for test-kind detection and then discarded it. Kotlin@Composable,@HiltViewModel,@Inject; Java@Entity,@Autowired; C#[HttpGet]; Python decorators — all were missing from the persisted node._extract_classesdid not extract class-level annotations at all, so@HiltViewModel class MyViewModel,@AndroidEntryPoint, etc. lost their annotation metadata entirely.Fix:
_extract_annotation_list(node)helper that centralises the Java/Kotlin/C#modifierswalk and the Pythondecorated_definitionwalk._extract_functionsnow persists annotations tonode.modifiers(comma-separated string) ANDnode.extra["decorators"](list) on every Function / Test node._extract_classescalls the same helper to preserve class-level annotations.Consumers can now write queries like "show me all
@Composablefunctions" or "find@HiltViewModelclasses" — both currently impossible on Kotlin codebases.Fix 2 — C# namespace-based
importers_ofresolution (#310)C#
using ACME.Core;directives produce anIMPORTS_FROMedge whosetargetis the raw namespace string ("ACME.Core"), not a file path.importers_oflooked edges up by file path and never matched a namespace, so it returned[]for every.csfile — breakingget_impact_radius,detect_changes, and dead-code analysis on C# codebases.Fix:
_extract_csharp_namespaces(root_node)helper handles both tree-sitter shapes:namespace_declarationfile_scoped_namespace_declarationparse_bytestags each C# File node with the namespaces it declares inextra["csharp_namespaces"].tools/query.py::query_graphadds a namespace fallback toimporters_of: when the target is a.csfile, look up its declared namespaces and also searchedges_by_targetfor each namespace string. Deduped against the primary lookup so duplicates never appear.This mirrors the existing bare-name fallback already used by
callers_ofandinheritors_of(see #87). No parser restructuring needed — just an additional lookup path in the query layer.Tests added (10 new tests in
tests/test_multilang.py)TestKotlinAnnotations— 5 tests:test_hilt_viewmodel_annotation_on_class@HiltViewModelpersisted on Class nodetest_composable_annotation_on_function@Composablepersisted on Function nodetest_multiple_annotations_on_function@Inject+@field:JvmFieldboth preservedtest_unannotated_function_has_none_modifierstest_test_annotation_still_triggers_test_kind@Test→ Test kind promotion still worksTestCSharpNamespaceResolution— 5 tests:test_file_scoped_namespace_tagged_on_file_nodenamespace Foo;formtest_block_namespace_tagged_on_file_nodenamespace Foo { ... }formtest_multiple_namespaces_in_one_filetest_non_csharp_file_has_no_namespace_tagtest_importers_of_resolves_namespace_to_fileimporters_of Core.cs, assert App.cs is found and Unrelated.cs is notTest results
tests/test_multilang.pyfullmain)mainviacomm -23diff — zero regressions)ruff checkon all 3 changed filesmypyonparser.py+query.pyZero regressions. All fixes follow patterns already used in the codebase (inheritors_of bare-name fallback, File node extra tagging mirroring the Swift
swift_kindpattern).