fix: Python declaredType extraction and sequential-path property registration

- Move @definition.property capture from expression_statement to assignment
  node in Python queries so Strategy 1 childForFieldName('type') succeeds
- Pass item.declaredType through ctx.symbols.add in sequential call-processor
  path, matching worker path behavior (fixes Ruby YARD declaredType drop)
- Add Python chain resolution integration test (user.address.save → Address#save)
- Update Rust/Python status in roadmap and system docs to reflect actual coverage
This commit is contained in:
Gergo Magyar 2026-03-18 14:58:00 +00:00
parent 52c3297143
commit 31b95f08d0
5 changed files with 17 additions and 6 deletions

View file

@ -234,8 +234,10 @@ export const processCalls = async (
description: item.accessorType, description: item.accessorType,
}, },
}); });
ctx.symbols.add(file.path, item.propName, nodeId, 'Property', ctx.symbols.add(file.path, item.propName, nodeId, 'Property', {
propEnclosingClassId ? { ownerId: propEnclosingClassId } : undefined); ...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
...(item.declaredType ? { declaredType: item.declaredType } : {}),
});
const relId = generateId('DEFINES', `${fileId}->${nodeId}`); const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
graph.addRelationship({ graph.addRelationship({
id: relId, sourceId: fileId, targetId: nodeId, id: relId, sourceId: fileId, targetId: nodeId,

View file

@ -183,7 +183,7 @@ export const PYTHON_QUERIES = `
(expression_statement (expression_statement
(assignment (assignment
left: (identifier) @name left: (identifier) @name
type: (type))) @definition.property type: (type)) @definition.property)
; Heritage queries - Python class inheritance ; Heritage queries - Python class inheritance
(class_definition (class_definition

View file

@ -1314,4 +1314,13 @@ describe('Field type resolution (Python)', () => {
expect(edgeSet(propEdges)).toContain('User → name'); expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city'); expect(edgeSet(propEdges)).toContain('Address → city');
}); });
it('resolves user.address.save() → Address#save via field type', () => {
const calls = getRelationships(result, 'CALLS');
const saveCalls = calls.filter(e => e.target === 'save');
const addressSave = saveCalls.find(
e => e.source === 'process_user' && e.targetFilePath.includes('models'),
);
expect(addressSave).toBeDefined();
});
}); });

View file

@ -146,8 +146,8 @@ Model class / struct fields so chained member access can be resolved more accura
| Go | ✅ `field_declaration` | ✅ Strategy 1 (type field) | ✅ | | | Go | ✅ `field_declaration` | ✅ Strategy 1 (type field) | ✅ | |
| Kotlin | ✅ `property_declaration` | ✅ Strategy 4 (variable_declaration) | ✅ | New strategy added | | Kotlin | ✅ `property_declaration` | ✅ Strategy 4 (variable_declaration) | ✅ | New strategy added |
| PHP | ✅ `property_declaration` | ✅ Strategy 1 + PHPDoc @var fallback | ✅ | Strategy 5 for pre-7.4 | | PHP | ✅ `property_declaration` | ✅ Strategy 1 + PHPDoc @var fallback | ✅ | Strategy 5 for pre-7.4 |
| Rust | ✅ `field_declaration` | ✅ Strategy 1 (type field) | — | Capture only (no field-access call resolution) | | Rust | ✅ `field_declaration` | ✅ Strategy 1 (type field) | ✅ | `extractMemberAccessParts` handles `field_expression` via `value`/`field` |
| Python | ✅ `assignment` with `type` | ⚠️ Class-level only | — | `self.x` pattern needs work | | Python | ✅ `assignment` with `type` | ✅ Class-level annotations | ✅ | `self.x` instance pattern not yet supported |
| Ruby | ✅ `attr_*` via call routing | ✅ YARD `@return [Type]` | — | YARD fallback for dynamically typed properties | | Ruby | ✅ `attr_*` via call routing | ✅ YARD `@return [Type]` | — | YARD fallback for dynamically typed properties |
| C++ | ✅ `field_declaration` via `field_identifier` | ✅ Strategy 1 (type field) | ✅ | | | C++ | ✅ `field_declaration` via `field_identifier` | ✅ Strategy 1 (type field) | ✅ | |
| Swift | ✅ `property_declaration` | ⚠️ Untested | — | | | Swift | ✅ `property_declaration` | ⚠️ Untested | — | |

View file

@ -378,7 +378,7 @@ So return-type-aware receiver inference already exists in a constrained downstre
| Comment-based types | JSDoc | No | No | No | No | No | No | PHPDoc | YARD | No | No | | Comment-based types | JSDoc | No | No | No | No | No | No | PHPDoc | YARD | No | No |
| Return type extraction | JSDoc | No | No | No | No | No | No | PHPDoc | YARD | No | No | | Return type extraction | JSDoc | No | No | No | No | No | No | PHPDoc | YARD | No | No |
\* Python has a type annotation query for fields but no `declaredType` extraction for the `self.x` pattern yet. \* Python class-level annotated attributes (`address: Address`) now resolve `declaredType` correctly. The `self.x` instance attribute pattern is not yet supported.
--- ---