mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-20 00:11:37 +00:00
feat(python-scope): capture function return-type annotations
Unit 3 of the python migration architectural plan (docs/plans/2026-04-19-001-refactor-python-migration-architectural-plan.md). Wires the `def get_user() -> User` return-type annotation into the typeBindings stream so the existing constructor-inferred + transitive chain machinery can resolve `u = get_user(); u.save()` to `User#save` without any orchestrator change. Changes: - `query.ts` + `scopes.scm`: new `@type-binding.return` pattern keyed by the function name (matches RFC §5.1 canonical vocabulary). - `interpret.ts`: maps `@type-binding.return` to the existing `'return-annotation'` source label (no shared change needed). - `scope-extractor.ts pass4CollectTypeBindings`: extends the Pass 2 auto-hoist (anchor range == innermost scope range → bind in parent) to type bindings as well — return-type bindings whose anchor IS the function_definition land in the function's enclosing scope so callers see them. Same-file return-type inference is now end-to-end: `def get_user() -> User: ...` + `u = get_user()` produces `u: User (return-annotation)` in the caller's scope via `followChainedRef`. Verification: - Flag-off: 191/191 (identical baseline). - Flag-on: 31 fail / 160 pass (no change — every remaining return-type test in this fixture set is *cross-file*; carrying `get_user → User` across module boundaries lands with the cross-file typeBinding propagation work in Unit 5/7). - tsc --noEmit clean.
This commit is contained in:
parent
206c18f836
commit
f9ff390456
4 changed files with 34 additions and 1 deletions
|
|
@ -122,6 +122,7 @@ export function interpretPythonTypeBinding(captures: CaptureMatch): ParsedTypeBi
|
|||
else if (captures['@type-binding.constructor'] !== undefined) source = 'constructor-inferred';
|
||||
else if (captures['@type-binding.annotation'] !== undefined) source = 'annotation';
|
||||
else if (captures['@type-binding.alias'] !== undefined) source = 'assignment-inferred';
|
||||
else if (captures['@type-binding.return'] !== undefined) source = 'return-annotation';
|
||||
|
||||
return { boundName: nameCap.text, rawTypeName: rawType, source };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,16 @@ export const PYTHON_SCOPE_QUERY = `
|
|||
left: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.annotation
|
||||
|
||||
;; Return-type annotation: \`def get_user() -> User:\` binds the
|
||||
;; FUNCTION'S NAME to its return type in the enclosing scope. Combined
|
||||
;; with the constructor-inferred + chain-follow path, \`u = get_user()\`
|
||||
;; then resolves \`u: User\` cross-call. The Python provider hoists the
|
||||
;; binding via \`pythonBindingScopeFor\` to the function's parent scope
|
||||
;; so callers in module/class scope see it.
|
||||
(function_definition
|
||||
name: (identifier) @type-binding.name
|
||||
return_type: (type) @type-binding.type) @type-binding.return
|
||||
|
||||
;; References — calls
|
||||
(call
|
||||
function: (identifier) @reference.name) @reference.call.free
|
||||
|
|
|
|||
|
|
@ -125,6 +125,18 @@
|
|||
left: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.annotation
|
||||
|
||||
; ─── Type bindings: function return-type annotations ─────────────────────
|
||||
;
|
||||
; `def get_user() -> User:` — binds the function's NAME to its return
|
||||
; type in the enclosing scope. Combined with the constructor-inferred +
|
||||
; chain-follow path, `u = get_user()` then resolves `u: User` cross-
|
||||
; call. Python provider hoists the binding via `pythonBindingScopeFor`
|
||||
; to the function's parent scope so callers in module/class scope see it.
|
||||
|
||||
(function_definition
|
||||
name: (identifier) @type-binding.name
|
||||
return_type: (type) @type-binding.type) @type-binding.return
|
||||
|
||||
; ─── References: calls ─────────────────────────────────────────────────────
|
||||
;
|
||||
; Free call: `print(x)` — function is a bare identifier
|
||||
|
|
|
|||
|
|
@ -666,9 +666,19 @@ function pass4CollectTypeBindings(
|
|||
const innermost = draftById.get(innermostId);
|
||||
if (innermost === undefined) continue;
|
||||
|
||||
// Auto-hoist for scope-creating type bindings (e.g. Python's
|
||||
// `@type-binding.return` whose anchor is the function_definition
|
||||
// itself). Same condition as Pass 2 — when the anchor coincides
|
||||
// with the innermost scope's range, the binding belongs in the
|
||||
// enclosing scope (callers, not the function body, look up the
|
||||
// return type by the function's name).
|
||||
const autoHostedId =
|
||||
innermost.parent !== null && rangesEqual(anchor.range, innermost.range)
|
||||
? innermost.parent
|
||||
: innermost.id;
|
||||
// `bindingScopeFor` may hoist the type binding to an outer scope.
|
||||
const hostId =
|
||||
provider.bindingScopeFor?.(match, draftToScope(innermost), scopeTree) ?? innermost.id;
|
||||
provider.bindingScopeFor?.(match, draftToScope(innermost), scopeTree) ?? autoHostedId;
|
||||
const host = draftById.get(hostId) ?? innermost;
|
||||
|
||||
const typeRef: TypeRef = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue