feat: PowerShell language support (.ps1/.psm1) - #58
Draft
qoole wants to merge 11 commits into
Draft
Conversation
Newer grammar releases in the tree-sitter ecosystem require the 0.25 runtime, so bump tree-sitter ^0.21.1→^0.25.0 and the Python/Go grammars to ^0.25.0 to unlock languages whose grammars need it. tree-sitter-typescript stays pinned at ^0.23.2 (still the latest release) — it declares peerOptional tree-sitter@^0.21.0, so an npm `overrides` entry pins its nested tree-sitter to the root one so the repo-root install resolves cleanly; verified the grammar's native binding still loads and parses correctly under tree-sitter 0.25.1. Also updates the stale-parse-limit comment in extract.ts: the chunked callback parse predates 0.25 (which lifted the 32KB string-parse limit) and is kept because it's behavior-identical and exercised by existing tests. No behavior change; full suite green (493/493).
Use stable syntax-node IDs to avoid duplicate reference edges and align the supported Node version with native Windows prebuilds.
Adds the PowerShell language to extract.ts: describePowerShell recognizes function/class/enum/method statements, psCalleeName/psImportKind/ psImportSpecifier classify commands (dot-source, Import-Module, using module) versus real calls, and psExported covers PowerShell's lack of a file-local export marker. bindings.ts's FileBindings gains a case-insensitive mode and PowerShell-specific defName/resolveRecvType handling ($this, scope-qualified definition names).
…, builtin cmdlets resolve.ts bundles the name-lookup and owner-index maps (NameIndex/ OwnerIndex) and adds a PowerShell case-folded tier alongside the existing exact-match one: same-file exact wins first, then same-file case-folded, then whole-repo exact, then whole-repo case-folded. A PowerShell caller that isn't itself a test file demotes test-path candidates at the two global tiers before the uniqueness check, so Pester's habit of redeclaring a prod name across several *.Tests.ps1 files doesn't drop every cross-file call as ambiguous. PS_BUILTIN_CMDLETS denylists the core cmdlet/Pester-DSL surface so a call to a builtin never wires to an unrelated in-repo definition of the same name. $PSScriptRoot resolves as a literal for import paths, and PS_IMPORT_EXTS keeps PowerShell's module extensions from cross-matching TS/Python specifiers. isTestPath moves to its own src/util/testpath.ts (a hard graph-topology input for resolve.ts now, not just ask.ts's soft rank-penalty); ask.ts re-exports it for backward compatibility.
CODE_EXTENSIONS picks up .ps1/.psm1/.psd1; isTestPath recognizes .tests.ps1 (Pester's naming convention). New test/graph-powershell.test.ts, test/testpath.test.ts, and test/utf16-routing.test.ts cover PowerShell graph extraction/resolution end-to-end and the UTF-16LE decode path. Every existing test file touched by the case-folding, test-aware tie-break, builtin-cmdlet denylist, and ingest changes is extended with PowerShell-specific coverage alongside its existing cases.
Contributor
Author
|
Added a domain-isolation fix found while extending the language work: PowerShell method nodes were entering the shared member-owner index, so a typed member call from another language could in principle wire cross-language to a PS class method. PS methods (and PS heritage edges) now live only in their own indexes, mirroring how PS bare names were already isolated. Suite is 621/621 on the branch. |
Assaf750
added a commit
to Assaf750/Graft
that referenced
this pull request
Aug 12, 2026
…and Rust behaviour Records the verified starting point before any Rust work: PR NanoNets#40/NanoNets#58/NanoNets#59 heads double-checked via the GitHub API and git ls-remote, strict-containment proof of the stack, and the finding that the stack carries no upstream CI signal at all. Suite on the pinned head is 638/645 with 3 failures. All three are an upstream locale defect (bare toLocaleString() resolving to ar-SA digits), reproduced verbatim on main and untouched by any Rust commit, so they are attributed upstream rather than counted as a Rust regression. Fix is deliberately deferred to its own commit. Also records measured behaviour on a Cargo workspace fixture: what already resolves, and the six gaps that Batches B-J exist to close — chiefly that cross-crate symbol imports never resolve because module-path resolution maps paths to files only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Tier-1 graph extraction for PowerShell via tree-sitter-powershell (airbus-cert; prebuilds for all six platform/arch combos — the reason #40's 0.25 runtime bump is the prerequisite).
What
filter/workflow), classes, methods & constructors, enums.exported=truefor top-level defs and class members; functions nested inside functions/methods are scope-local → false. (PowerShell has no file-local export syntax;Export-ModuleMember/manifest interpretation is cross-file and deferred — see limitations.)script:Foo,Mod\Get-Foo), member calls$obj.M(), static calls[Type]::M(), call-operator statics& Get-Foo. Dynamic invocation (& $var) produces no edge.. ./x.ps1, quoted paths, backslash-normalized),Import-Modulewith relative paths (module names stay external specifiers, like Go stdlib imports),using module(extracted from its command shape — the grammar has no dedicated node).FOOcan never link to a Pythonfoo), original casing preserved everywhere, lookup order exact-local → folded-local → exact-global → folded-global (local scope wins, matching PS semantics). Folded matches surface asconfidence: "inferred".[Parameter(...)][Widget]$w), typed properties, cast declarations,[Widget]::new(),New-Object(positional and-TypeName),$this→ enclosing class.Get-ChildItem,Describe, …) never bind to a same-named in-repo definition (usually a Pester mock) — dropped-edge-is-safe, the same posture the resolver already takes for unresolvable member calls..ps1(Windows PowerShell 5.1Out-Filedefault) decoded at the sharedreadSourceFilesite (hash-what-you-parse holds); Pester*.Tests.ps1classified as test files and de-ranked accordingly;.psd1deliberately not graph-parsed (manifests yield no definitions).tree-sitter-powershell/bindings/node/index.js) avoids a DEP0151 warning on every CLI run (package has extensionlessmain+ noexportsmap).Fit with recent main
This was originally built against pre-0.9.0 main and re-anchored after the recent landings; the seams were adapted rather than ported blind:
initwrites for Claude Code #36): PowerShell slots into theEXTENSIONStable as two rows (.ps1/.psm1→ grammarpowershell, labelpowershell), so the banner/map coverage report is correct by construction.callersmisses consumers that reference a function without calling it directly #34): the "don't descend into import statements" rule gets one PS carve-out — a dot-source can wrap a whole script block (. { ... }), whose body must still be walked for its own calls. Pinned by test.mapclusters one entry per file instead of per directory #35): PS member calls inherit the drop-don't-guess rule unchanged; the PS case-folding applies insideresolveTypedMember's owner-qualified lookup, never as a bare-name fallback.readSourceFilecontracts as every other language — no PS-specific read or walk path exists.Evidence
Stop-Function, 2,857 call sites, shadowed by 10 Pester mocks) resolves 726 incoming call edges to its production definition.$PSScriptRootresolution (was 0/30); the 6 unresolved are genuinely dynamic (runtime variables) and stay raw by design. Re-verified on this exact branch post-rebase, including byte-identical cold-rebuild determinism.Known limitations (deliberate)
Export-ModuleMember/ manifestFunctionsToExportnot interpreted (uniformexported=trueinstead) — the common unquoted multi-name idiom doesn't parse in the current grammar anyway.$PSScriptRoot-interpolated,Join-Path-computed, and wildcard import arguments degrade to raw specifiers..psd1manifest relationships (RootModule/NestedModules) not graphed.extends(PS cannot define interfaces; targets stay external names).