From 989669f6e3abd38e20a92c368883c31be7fd8395 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sun, 19 Apr 2026 13:34:50 +0100 Subject: [PATCH] feat(python): walrus, qualified-call, match-case type bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the constructor-inferred family of captures with three more assignment-shaped patterns that all bind a variable to a class-like type: - Walrus: `(u := User(...))` → `u: User` via `(named_expression)`. - Qualified call RHS: `u = models.User(...)` → `u: models.User` via `(attribute)` node .text. Falls through resolveTypeRef Phase 2 (QualifiedNameIndex dotted fallback). - Match as-pattern: `case User() as u:` → `u: User` via `(as_pattern)` + `(class_pattern (dotted_name))`. Fixes 2 failures (flag-on 59 → 57): - Python walrus operator type inference - Python match/case as-pattern type binding Qualified-call constructor tests still fail because they require cross-module qualifiedName registration (models.User → models.py's User class) which isn't yet wired in the Python extractor. Tracked as follow-up alongside module-import CALLS (#337) resolution. --- .../core/ingestion/languages/python/query.ts | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/gitnexus/src/core/ingestion/languages/python/query.ts b/gitnexus/src/core/ingestion/languages/python/query.ts index 4be0b1ea0..e641285eb 100644 --- a/gitnexus/src/core/ingestion/languages/python/query.ts +++ b/gitnexus/src/core/ingestion/languages/python/query.ts @@ -50,14 +50,46 @@ export const PYTHON_SCOPE_QUERY = ` ;; Type bindings (constructor-inferred: \`u = User(...)\`) ;; Listed BEFORE the annotation pattern so \`u: User = find()\` — which -;; matches BOTH patterns — has the annotation (later match) overwrite -;; the constructor-inferred guess (earlier match) in the typeBindings -;; map. Explicit user intent beats inference. +;; matches BOTH patterns — has the annotation (stronger source) win over +;; the constructor-inferred guess via the scope-extractor's source- +;; strength tie-break in pass4CollectTypeBindings. (assignment left: (identifier) @type-binding.name right: (call function: (identifier) @type-binding.type)) @type-binding.constructor +;; Qualified constructor (\`u = models.User(...)\`). Captures the +;; attribute node as the type — its \`.text\` is the full dotted path +;; (\`models.User\`), which \`resolveTypeRef\` resolves via +;; \`QualifiedNameIndex\` Phase 2. +(assignment + left: (identifier) @type-binding.name + right: (call + function: (attribute) @type-binding.type)) @type-binding.constructor + +;; Walrus operator: \`(u := User(...))\`. Python 3.8+ named expression. +;; Shares the constructor-inferred shape so the binding lands in the +;; enclosing function/module scope's typeBindings the same way a plain +;; assignment would. +(named_expression + name: (identifier) @type-binding.name + value: (call + function: (identifier) @type-binding.type)) @type-binding.constructor + +(named_expression + name: (identifier) @type-binding.name + value: (call + function: (attribute) @type-binding.type)) @type-binding.constructor + +;; Match-case as-pattern: \`case User() as u:\` → \`u: User\`. The +;; class_pattern's dotted_name carries the type; the outer as_pattern's +;; second child is the binding name. +(as_pattern + (case_pattern + (class_pattern + (dotted_name) @type-binding.type)) + (identifier) @type-binding.name) @type-binding.constructor + ;; Type bindings (variable annotations: \`u: User\` / \`u: User = x\`) (assignment left: (identifier) @type-binding.name