You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Shared import-capture vocabulary instead of ~21 per-language import extractors #358
Part of the language-architecture cleanup tracked in #354 ("common stays common"). This is the largest step, so it goes last.
Problem.src/ingest_relations.h has about 21 per-language import-target extractors (csharpUsingTarget, luaRequireTarget, phpUseTarget, rubyRequireTarget, jsModuleLoadTarget, elixirDirectiveTarget, …). Most do the same thing: find the import statement's target text and normalise it.
Change. A shared import capture vocabulary in tags.scm (for example @import.path, @import.alias, @import.names) plus one small specifier normaliser per DepDialect, instead of an extractor per language. Dependency resolution stays in src/resolve.h.
Acceptance. Dependency edges byte-identical on every dependency-capable language (existing resolve/deps gates); the per-language extractors removed as each language moves over. It can land one language at a time.
Size: about 5 days in total, but splittable per language. Comment here to claim a language.
Claiming C-family (#include / #import for c, cpp, objc, cuda, metal) for the first slice.
Rationale: it is the largest family, its extractors are the simplest ones (preprocIncludeTarget, preprocImportTarget, includePathOf in src/ingest_relations.h), and it exercises both hard parts of the proposed design — the quote-vs-angle delimiter and a directive-text guard (#import vs #pragma) that a tree-sitter query cannot express. If the shared vocabulary works here it works everywhere; a language that needs a workaround would be a bad first choice.
Two things I found reading the code that shape the implementation, and that the issue does not mention:
Tags-pass predicates never run here (stated in queries/python/tags.scm, pinned by test/matchgrammarcheck.sh). So the #import vs #pragma/#error discrimination cannot move into tags.scm as an #eq? — it stays a C++ gate reading the captured directive text. Same for the require/import bare-callee guard later.
captureIncludes runs before the tags pass (src/ingest_sidecap.h:1550 vs :1706) and carries walk-frame state the tags pass has no access to (insideFn for lazy requires, depth for the ImportNestingTooDeep disclosure, openIdx for the Ruby receiver dedupe). For C-family none of that is set, so this slice can move cleanly, but the shared plumbing has to re-derive frame state from ancestry per captured node for the languages that need it.
Acceptance as stated: dependency edges byte-identical. For C-family that means test/preproccondcheck.sh, test/includeprecisecheck.sh, test/includeanglecheck.sh, test/crossdirincludecheck.sh, test/depsprecisecheck.sh, test/resolvecheck.sh plus the full suite, and a test/importcapturecheck.sh added to test/regression.sh in the same commit.
Not claiming: the languages where the walk-frame state is load-bearing (TS/JS lazy requires, Ruby receivers) — those need the ancestry re-derivation to land first.
Thanks @lennix1337 — #368 (C-family on one @import.path capture) is merged, with byte-identical dependency edges exactly as the acceptance asked. Nicely done.
The remaining slices are open. If you — or anyone — would like one, comment here to claim it; same acceptance as above (dependency edges byte-identical on the existing resolve/deps gates; the per-language extractor removed as each language moves over). Go, JS/TS and Python are the ones we'd most like to see next, ideally with @import.alias / @import.names alongside @import.path, since those are our most-used languages.
If a slice is still unclaimed by Thursday 2026-10-08, we'll start picking up the unclaimed ones ourselves. A claimed slice stays yours for as long as you're working on it.
I’ll keep this scoped to Python: move its import extraction onto the shared capture vocabulary (@import.path, and @import.alias / @import.names where the current Python semantics support them), remove only the Python-specific extractor that becomes redundant, and preserve existing dependency/resolve behavior byte-for-byte.
I’ll stop rather than broaden scope if Python exposes shared-plumbing requirements that belong in a separate slice.
Thanks @LaimaWu — the Python slice is yours. Your scoping is exactly right: Python only, @import.path plus @import.alias / @import.names where the current semantics support them, remove only the Python extractor that becomes redundant, dependency/resolve output byte-identical, and stop rather than broaden if shared plumbing turns up (a short note here is the perfect way to flag that).
The Go and JS/TS slices are still open to anyone until Thursday 2026-10-08, as posted above; after that we'll pick up whatever is unclaimed ourselves.
@LaimaWu a heads-up for the Python slice, so you don't hit surprises: our JS/TS slice (branch lane/import-capture-jsts-358, reviewed, merging with the next train) changes the shared emission in src/ingest_importcap.h.
importContainerReach now returns {depth, reachable, insideFn} from one descent from the root (the old upward ts_node_parent walk was quadratic on deeply nested files). The normaliser runs before the depth test, so return empty only for non-imports and never depend on depth.
To switch the old walk off for Python, add DepDialect::Python to importsFromCapture(), not to captureSideFacts.
importDirectiveOf picks the node the Include is sited at and whose ancestry is read: for Python that should be the import_statement / import_from_statement, not an inner dotted_name under aliased_import.
captureIncludes also writes Python's bound names (capturePythonImportBinds → RawBind), and emitCapturedImport has no binds channel yet. That is exactly the shared-plumbing point your scoping says to flag — happy to sort it out with you here before you build on it.
If your branch predates the JS/TS merge, expect conflicts in those spots; we're glad to help with the rebase.
@LaimaWu a heads-up for the Python slice, so you don't hit surprises: our JS/TS slice (branch lane/import-capture-jsts-358, reviewed, merging with the next train) changes the shared emission in src/ingest_importcap.h.
importContainerReach now returns {depth, reachable, insideFn} from one descent from the root (the old upward ts_node_parent walk was quadratic on deeply nested files). The normaliser runs before the depth test, so return empty only for non-imports and never depend on depth.
To switch the old walk off for Python, add DepDialect::Python to importsFromCapture(), not to captureSideFacts.
importDirectiveOf picks the node the Include is sited at and whose ancestry is read: for Python that should be the import_statement / import_from_statement, not an inner dotted_name under aliased_import.
captureIncludes also writes Python's bound names (capturePythonImportBinds → RawBind), and emitCapturedImport has no binds channel yet. That is exactly the shared-plumbing point your scoping says to flag — happy to sort it out with you here before you build on it.
If your branch predates the JS/TS merge, expect conflicts in those spots; we're glad to help with the rebase.
Thanks for the heads-up. The Python slice is now in PR #382, based on 60dd3b3.
The current implementation emits Python dependencies through @import.path and bindings through a Python-specific RawBind helper using @import.names / @import.alias. It retains the bounded Python walk solely for nesting disclosure, including empty deep containers.
I can see the overlap with your incoming importContainerReach, importDirectiveOf, and importsFromCapture changes. I’ll hold off on rebasing against the unmerged JS/TS branch or making speculative shared-plumbing changes.
Would you prefer keeping the Python-specific binding/disclosure handling in #382 while adapting to the new shared interfaces, or moving binding emission into the shared API as part of integration? Happy to align with whichever approach preserves existing behavior with the smallest scope.
Once coordinated, I’ll rebase and rerun the differential gates.
Hi @joyful-ii-V-I, I'd like to claim the C# slice 1a of #358, following the merged Python slice #382.
I'll keep this limited to moving C# import extraction onto @import.path and removing the redundant extractor, preserving existing dependency and binding behavior without touching the shared binds API.
Preflight identified two points to resolve before implementation:
A possible discrepancy in C# using-alias extraction. I'll first establish the actual behavior on current main and preserve it in this refactor.
C# using directives move onto the shared @import.path capture plus a normaliser arm.
csharpUsingTarget is deleted from directiveTargetOf.
Dependency edges stay byte-identical.
Leave using X = … aliases where they are for now. They belong to slice 2b, which follows the shared binds channel (2a).
One new tool since #382: test/langidentitycheck.sh BASE HEAD (from #399, now on main) runs the byte-identity sweep for you. Its output is the evidence the definition of done in CONTRIBUTING asks for.
Activity
lennix1337 commentedon Oct 1, 2026
Claiming C-family (
#include/#importfor c, cpp, objc, cuda, metal) for the first slice.Rationale: it is the largest family, its extractors are the simplest ones (
preprocIncludeTarget,preprocImportTarget,includePathOfinsrc/ingest_relations.h), and it exercises both hard parts of the proposed design — the quote-vs-angle delimiter and a directive-text guard (#importvs#pragma) that a tree-sitter query cannot express. If the shared vocabulary works here it works everywhere; a language that needs a workaround would be a bad first choice.Two things I found reading the code that shape the implementation, and that the issue does not mention:
queries/python/tags.scm, pinned bytest/matchgrammarcheck.sh). So the#importvs#pragma/#errordiscrimination cannot move intotags.scmas an#eq?— it stays a C++ gate reading the captured directive text. Same for therequire/importbare-callee guard later.captureIncludesruns before the tags pass (src/ingest_sidecap.h:1550vs:1706) and carries walk-frame state the tags pass has no access to (insideFnfor lazy requires,depthfor theImportNestingTooDeepdisclosure,openIdxfor the Ruby receiver dedupe). For C-family none of that is set, so this slice can move cleanly, but the shared plumbing has to re-derive frame state from ancestry per captured node for the languages that need it.Acceptance as stated: dependency edges byte-identical. For C-family that means
test/preproccondcheck.sh,test/includeprecisecheck.sh,test/includeanglecheck.sh,test/crossdirincludecheck.sh,test/depsprecisecheck.sh,test/resolvecheck.shplus the full suite, and atest/importcapturecheck.shadded totest/regression.shin the same commit.Not claiming: the languages where the walk-frame state is load-bearing (TS/JS lazy requires, Ruby receivers) — those need the ancestry re-derivation to land first.
refactor(redhat-et#358): the C-family import edges come from one @imp…
test(redhat-et#358): the gate says what it checks — a real quote-vs-a…
chore(redhat-et#358): rebase onto the new main — gate count re-derive…
fix(redhat-et#358): restore the walk's import REACH — an unanchored q…
fix(redhat-et#358): the gate must not be able to DIE SILENTLY — it wa…
fix(redhat-et#358): record the --hot DECISION for the new ingest_impo…
fix(redhat-et#358): the nesting bound was off by one -- 256 container…
fix(redhat-et#358): the new bound arm compared a count as a STRING --…
Merge PR #368 (ed4a818): C-family @import/#import edges from one shar…
joyful-ii-V-I commentedon Oct 4, 2026
Thanks @lennix1337 — #368 (C-family on one
@import.pathcapture) is merged, with byte-identical dependency edges exactly as the acceptance asked. Nicely done.The remaining slices are open. If you — or anyone — would like one, comment here to claim it; same acceptance as above (dependency edges byte-identical on the existing resolve/deps gates; the per-language extractor removed as each language moves over). Go, JS/TS and Python are the ones we'd most like to see next, ideally with
@import.alias/@import.namesalongside@import.path, since those are our most-used languages.If a slice is still unclaimed by Thursday 2026-10-08, we'll start picking up the unclaimed ones ourselves. A claimed slice stays yours for as long as you're working on it.
LaimaWu commentedon Oct 7, 2026
I’d like to take the Python slice.
I’ll keep this scoped to Python: move its import extraction onto the shared capture vocabulary (
@import.path, and@import.alias/@import.nameswhere the current Python semantics support them), remove only the Python-specific extractor that becomes redundant, and preserve existing dependency/resolve behavior byte-for-byte.I’ll stop rather than broaden scope if Python exposes shared-plumbing requirements that belong in a separate slice.
joyful-ii-V-I commentedon Oct 7, 2026
Thanks @LaimaWu — the Python slice is yours. Your scoping is exactly right: Python only,
@import.pathplus@import.alias/@import.nameswhere the current semantics support them, remove only the Python extractor that becomes redundant, dependency/resolve output byte-identical, and stop rather than broaden if shared plumbing turns up (a short note here is the perfect way to flag that).The Go and JS/TS slices are still open to anyone until Thursday 2026-10-08, as posted above; after that we'll pick up whatever is unclaimed ourselves.
6 remaining items
docs(#358): CHANGELOG states the final disclosure differences and the…
joyful-ii-V-I commentedon Oct 8, 2026
@LaimaWu a heads-up for the Python slice, so you don't hit surprises: our JS/TS slice (branch
lane/import-capture-jsts-358, reviewed, merging with the next train) changes the shared emission insrc/ingest_importcap.h.importContainerReachnow returns{depth, reachable, insideFn}from one descent from the root (the old upwardts_node_parentwalk was quadratic on deeply nested files). The normaliser runs before the depth test, so return empty only for non-imports and never depend on depth.DepDialect::PythontoimportsFromCapture(), not tocaptureSideFacts.importDirectiveOfpicks the node the Include is sited at and whose ancestry is read: for Python that should be theimport_statement/import_from_statement, not an innerdotted_nameunderaliased_import.captureIncludesalso writes Python's bound names (capturePythonImportBinds→ RawBind), andemitCapturedImporthas no binds channel yet. That is exactly the shared-plumbing point your scoping says to flag — happy to sort it out with you here before you build on it.If your branch predates the JS/TS merge, expect conflicts in those spots; we're glad to help with the rebase.
test(#358): ratio arms B37/B38 pin the one-descent ancestry read (JS …
LaimaWu commentedon Oct 8, 2026
Thanks for the heads-up. The Python slice is now in PR #382, based on 60dd3b3.
The current implementation emits Python dependencies through @import.path and bindings through a Python-specific RawBind helper using @import.names / @import.alias. It retains the bounded Python walk solely for nesting disclosure, including empty deep containers.
I can see the overlap with your incoming importContainerReach, importDirectiveOf, and importsFromCapture changes. I’ll hold off on rebasing against the unmerged JS/TS branch or making speculative shared-plumbing changes.
Would you prefer keeping the Python-specific binding/disclosure handling in #382 while adapting to the new shared interfaces, or moving binding emission into the shared API as part of integration? Happy to align with whichever approach preserves existing behavior with the smallest scope.
Once coordinated, I’ll rebase and rerun the differential gates.
test(importcapcheck): J1 looks for the walk's JS/TS branch with its l…
refactor(redhat-et#358): Go import edges from one @import.path captur…
refactor(redhat-et#358): TS/JS import edges from the shared @import.p…
refactor(redhat-et#358): self-checks on the TS/JS import-capture arm
refactor(redhat-et#358): keep the normaliser dispatch at 4 params; la…
refactor(redhat-et#358): importsFromCapture as a switch, one case per…
test(redhat-et#358): JS/TS arms for import attributes, default+named,…
refactor(redhat-et#358): move Python import edges to shared captures
Merge pull request #382 from LaimaWu/lane/python-importcap-358
LaimaWu commentedon Oct 13, 2026
Hi @joyful-ii-V-I, I'd like to claim the C# slice 1a of #358, following the merged Python slice #382.
I'll keep this limited to moving C# import extraction onto
@import.pathand removing the redundant extractor, preserving existing dependency and binding behavior without touching the shared binds API.Preflight identified two points to resolve before implementation:
mainand preserve it in this refactor.I'll keep implementation on hold until the baseline is verified and the scope is clear. Thanks!
joyful-ii-V-I commentedon Oct 15, 2026
Thanks @LaimaWu, and sorry for the slow reply. The C# slice of 1a is yours.
Same acceptance as your Python slice (#382):
usingdirectives move onto the shared@import.pathcapture plus a normaliser arm.csharpUsingTargetis deleted fromdirectiveTargetOf.Leave
using X = …aliases where they are for now. They belong to slice 2b, which follows the shared binds channel (2a).One new tool since #382:
test/langidentitycheck.sh BASE HEAD(from #399, now onmain) runs the byte-identity sweep for you. Its output is the evidence the definition of done in CONTRIBUTING asks for.