feat(LANG-rust): add scope-resolution hooks and register Rust ScopeResolver

Implements RFC #909 Ring 3 deliverables:
- query.ts: tree-sitter scope query for Rust
- captures.ts: emitRustScopeCaptures with method reclassification
- import-decomposer.ts: use statement decomposition (groups, renames, globs)
- interpret.ts: interpretRustImport + interpretRustTypeBinding
- import-target.ts: crate/module/super/self path resolution
- receiver-binding.ts: self/&self/&mut self receiver synthesis
- method-owners.ts: impl block → struct ownership bridging
- arity.ts: no-overloading arity check
- merge-bindings.ts: local > import > wildcard binding precedence
- simple-hooks.ts: binding/import scope, receiver binding
- scope-resolver.ts: ScopeResolver contract implementation
- Wired into rustProvider (rust.ts) with scope hooks
- Registered in SCOPE_RESOLVERS (pipeline/registry.ts)
- NOT yet added to MIGRATED_LANGUAGES (29 advanced pattern tests pending)

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/8f14b730-79d4-4356-9505-325750d71f84

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-16 10:47:43 +00:00 committed by GitHub
parent ef43b2e452
commit e93f332d58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 12 deletions

View file

@ -65,6 +65,20 @@ export function emitRustScopeCaptures(
if (declAnchor !== undefined) {
const fnNode = findNodeAtRange(tree.rootNode, declAnchor.range, 'function_item');
if (fnNode !== null) {
// Reclassify as method if inside an impl block
if (findEnclosingImpl(fnNode) !== null) {
const nameCap = grouped['@declaration.name'];
delete (grouped as Record<string, Capture | undefined>)['@declaration.function'];
grouped['@declaration.method'] = syntheticCapture(
'@declaration.method',
fnNode,
fnNode.text,
);
if (nameCap !== undefined) {
grouped['@declaration.name'] = nameCap;
}
}
const arity = computeRustDeclarationArity(fnNode);
if (arity.parameterCount !== undefined) {
grouped['@declaration.parameter-count'] = syntheticCapture(

View file

@ -194,15 +194,16 @@ function makeImportCapture(
name: string,
alias: string | undefined,
): CaptureMatch {
const result: CaptureMatch = {
return {
'@import.statement': syntheticCapture('@import.statement', anchor, anchor.text),
'@import.kind': syntheticCapture('@import.kind', anchor, kind),
'@import.source': syntheticCapture('@import.source', anchor, source),
'@import.name': syntheticCapture('@import.name', anchor, alias ?? name),
...(alias !== undefined
? {
'@import.alias': syntheticCapture('@import.alias', anchor, alias),
'@import.original-name': syntheticCapture('@import.original-name', anchor, name),
}
: {}),
};
if (alias !== undefined) {
result['@import.alias'] = syntheticCapture('@import.alias', anchor, alias);
result['@import.original-name'] = syntheticCapture('@import.original-name', anchor, name);
}
return result;
}

View file

@ -18,11 +18,10 @@ export function interpretRustImport(captures: CaptureMatch): ParsedImport | null
if (name === undefined) return null;
const originalName = captures['@import.original-name']?.text;
return {
kind: 'named',
kind: 'reexport',
localName: alias ?? name,
importedName: originalName ?? name,
targetRaw: source,
isReexport: true,
};
}
// kind === 'named'

View file

@ -28,8 +28,8 @@ function populateRustImplOwners(parsed: ParsedFile): void {
const structByName = new Map<string, string>();
for (const scope of parsed.scopes) {
for (const def of scope.ownedDefs) {
if (isClassLike(def.type) && (def.type === 'Struct' || def.type === 'Trait')) {
structByName.set(def.name, def.nodeId);
if (isClassLike(def.type) && def.qualifiedName) {
structByName.set(def.qualifiedName, def.nodeId);
}
}
}
@ -53,7 +53,16 @@ function populateRustImplOwners(parsed: ParsedFile): void {
}
if (receiverType === undefined) continue;
const ownerId = structByName.get(receiverType);
let ownerId = structByName.get(receiverType);
if (ownerId === undefined) {
// Try suffix match (qualifiedName might be `module.StructName`)
for (const [qname, nodeId] of structByName) {
if (qname.endsWith('.' + receiverType) || qname === receiverType) {
ownerId = nodeId;
break;
}
}
}
if (ownerId !== undefined) {
for (const def of methodDefs) {
(def as { ownerId?: string }).ownerId = ownerId;

View file

@ -74,7 +74,6 @@ export const MIGRATED_LANGUAGES: ReadonlySet<SupportedLanguages> = new Set<Suppo
SupportedLanguages.C,
SupportedLanguages.CPlusPlus,
SupportedLanguages.PHP,
SupportedLanguages.Rust,
]);
/**