mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-20 00:11:37 +00:00
feat(python): capture constructor-inferred + annotated type bindings
Extends the Python scope-extractor with two new type-binding capture
patterns so receiver-typed method dispatch has concrete type bindings
to work from:
1. `u: User = ...` / `u: User` — variable annotations. `@type-binding.annotation`
anchor, `source: 'annotation'`.
2. `u = User("alice")` — assignment RHS is a bare-identifier call (Python
has no `new` keyword; constructor-shaped calls are syntactically
identical to function calls). `@type-binding.constructor` anchor,
`source: 'constructor-inferred'`.
The runtime query lives in `query.ts` (the `.scm` file is documentation
per the comment at its top); both are updated.
Fixes 19 failures across these resolver fixtures (flag-on 82 → 63):
- Python constructor-inferred type resolution (3)
- Python class-level annotation resolution (3)
- Python nullable receiver resolution (3)
- Python member-call / receiver-constrained / constructor-call (3)
- Python assignment chain propagation (2)
- Python walrus / match-case / chained method (3)
- Python member access iterable for-loop (2)
This commit is contained in:
parent
8a7d42c1fd
commit
2275a1ce97
3 changed files with 51 additions and 10 deletions
|
|
@ -11,12 +11,7 @@
|
|||
* already attached) so these functions are straight-line tag readers.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CaptureMatch,
|
||||
ParsedImport,
|
||||
ParsedTypeBinding,
|
||||
TypeRef,
|
||||
} from 'gitnexus-shared';
|
||||
import type { CaptureMatch, ParsedImport, ParsedTypeBinding, TypeRef } from 'gitnexus-shared';
|
||||
|
||||
// ─── interpretImport ──────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -108,11 +103,19 @@ export function interpretPythonTypeBinding(captures: CaptureMatch): ParsedTypeBi
|
|||
// `def f(x: "User")`.
|
||||
const rawType = stripForwardRefQuotes(typeCap.text.trim());
|
||||
|
||||
// Order matters: more specific anchor captures take precedence. `self`
|
||||
// and `cls` are synthesized with their own marker captures; the SCM
|
||||
// anchor topic captures (`@type-binding.parameter`,
|
||||
// `@type-binding.annotation`, `@type-binding.constructor`) distinguish
|
||||
// the variable-annotation and constructor-inferred forms from the
|
||||
// classic parameter annotation.
|
||||
let source: TypeRef['source'] = 'parameter-annotation';
|
||||
if (captures['@type-binding.self'] !== undefined) source = 'self';
|
||||
// `cls` is a self-like receiver; share the source label so downstream
|
||||
// `Registry.lookup` Step 2 treats them identically.
|
||||
else if (captures['@type-binding.cls'] !== undefined) source = 'self';
|
||||
else if (captures['@type-binding.constructor'] !== undefined) source = 'constructor-inferred';
|
||||
else if (captures['@type-binding.annotation'] !== undefined) source = 'annotation';
|
||||
|
||||
return { boundName: nameCap.text, rawTypeName: rawType, source };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,17 @@ export const PYTHON_SCOPE_QUERY = `
|
|||
name: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.parameter
|
||||
|
||||
;; Type bindings (variable annotations: \`u: User\` / \`u: User = x\`)
|
||||
(assignment
|
||||
left: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.annotation
|
||||
|
||||
;; Type bindings (constructor-inferred: \`u = User(...)\`)
|
||||
(assignment
|
||||
left: (identifier) @type-binding.name
|
||||
right: (call
|
||||
function: (identifier) @type-binding.type)) @type-binding.constructor
|
||||
|
||||
;; References — calls
|
||||
(call
|
||||
function: (identifier) @reference.name) @reference.call.free
|
||||
|
|
@ -71,10 +82,7 @@ export function getPythonParser(): Parser {
|
|||
|
||||
export function getPythonScopeQuery(): Parser.Query {
|
||||
if (_query === null) {
|
||||
_query = new Parser.Query(
|
||||
Python as Parameters<Parser['setLanguage']>[0],
|
||||
PYTHON_SCOPE_QUERY,
|
||||
);
|
||||
_query = new Parser.Query(Python as Parameters<Parser['setLanguage']>[0], PYTHON_SCOPE_QUERY);
|
||||
}
|
||||
return _query;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,36 @@
|
|||
name: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.parameter
|
||||
|
||||
; ─── Type bindings: variable annotations ───────────────────────────────────
|
||||
;
|
||||
; `u: User` or `u: User = some_value` — `u` is explicitly annotated. Both
|
||||
; forms parse under tree-sitter-python as `(assignment left: type:)` with
|
||||
; an optional `right:`. Module-, class-, and function-scope annotations
|
||||
; all land here; scope attachment is handled by the central extractor
|
||||
; via the anchor's innermost-containing scope.
|
||||
;
|
||||
; Emits `source: 'annotation'`.
|
||||
|
||||
(assignment
|
||||
left: (identifier) @type-binding.name
|
||||
type: (type) @type-binding.type) @type-binding.annotation
|
||||
|
||||
; ─── Type bindings: constructor-inferred assignments ───────────────────────
|
||||
;
|
||||
; `u = User("alice")` — `u`'s type is inferred from the RHS call's target.
|
||||
; Python has no `new` keyword, so the pattern matches any `assignment`
|
||||
; whose RHS is a `call` with a bare-identifier function (constructor-
|
||||
; shaped). The registry resolves the raw name through the scope chain at
|
||||
; lookup time, so imported classes, local classes, and aliased imports
|
||||
; all work without query-time knowledge.
|
||||
;
|
||||
; Emits `source: 'constructor-inferred'`.
|
||||
|
||||
(assignment
|
||||
left: (identifier) @type-binding.name
|
||||
right: (call
|
||||
function: (identifier) @type-binding.type)) @type-binding.constructor
|
||||
|
||||
; ─── References: calls ─────────────────────────────────────────────────────
|
||||
;
|
||||
; Free call: `print(x)` — function is a bare identifier
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue