mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-21 00:21:30 +00:00
* feat: Phase 8 field/property type resolution — resolve chained member access
Add field/property type extraction to the type resolution system so that
chained member access like `user.address.save()` resolves the intermediate
receiver type (`address → Address`) through Property symbols in SymbolTable.
Key changes:
- SymbolTable: add `declaredType` field, `fieldByOwner` O(1) index,
`lookupFieldByOwner()` method, P0 conditional callableIndex invalidation,
P2 exclude Properties from globalIndex to prevent namespace pollution
- tree-sitter queries: add `definition.property` for TypeScript, Java, Go
- parse-worker: extract declared types for Property nodes via
`extractPropertyDeclaredType()`, capture field-access receiver info
- call-processor: add `resolveFieldAccessType()` helper and field-access
branch in both sequential and worker receiver resolution paths
- Integration tests: new field-types test suite verifying end-to-end
`user.address.save() → Address#save` resolution
* fix: Go tree-sitter query captures field_declaration not field_declaration_list
Post-review fix: the Go struct field query incorrectly put @definition.property
on field_declaration_list (the list container) instead of field_declaration
(the individual field). Also removed unused `language` parameter from
extractPropertyDeclaredType.
* feat: expand field-type tests to 6 languages, fix Go ownerId and Kotlin navigation_expression
- Add integration test fixtures for Java, C#, Go, Kotlin, PHP (alongside existing TS)
- Fix Go: add type_declaration handling in findEnclosingClassId for struct fields
(field_declaration → field_declaration_list → struct_type → type_spec → type_declaration)
- Fix Kotlin: add navigation_expression handling in field-access resolution
(Kotlin uses navigation_expression + navigation_suffix, not member_expression)
- Add extractMemberAccessParts helper in call-processor for cross-language member access
- All 24 field-type tests pass across 6 languages, 181 Go+Kotlin tests pass with no regressions
* refactor: split HAS_METHOD into HAS_METHOD + HAS_PROPERTY edge types
Property nodes now use HAS_PROPERTY edges instead of HAS_METHOD, giving
the graph schema proper semantic separation between methods and fields.
- HAS_METHOD: Method, Constructor, Function (when inside a class)
- HAS_PROPERTY: Property nodes (class fields, struct fields, attributes)
MRO processor only reads HAS_METHOD — properties correctly excluded from
method resolution order. Impact analysis accepts both edge types.
Updated 12 files: graph types, schema, tools docs, parse-worker,
parsing-processor, call-processor, and 6 test files.
* fix(test): update security test to expect 7 VALID_RELATION_TYPES (added HAS_PROPERTY)
* test: add unit tests for Phase 8 SymbolTable features (39 tests, up from 19)
Cover all new branches: declaredType metadata, Property exclusion from
globalIndex, conditional callableIndex invalidation, lookupFieldByOwner
(happy path + edge cases), lookupFuzzyCallable filtering, and clear()
with fieldByOwner. Fixes branch coverage threshold (21.8% → 23%+).
* feat: Phase 8B mixed field+method chain resolution, C++/Rust chain fixes
Unify field and method chain resolution into a single `extractMixedChain`
walker that handles interleaved patterns like `svc.getUser().address.save()`.
Fix C++ chain calls (tree-sitter-cpp `field_expression` uses `argument` not
`object`), Rust unit struct instantiation (`let svc = TypeName;`), and add
stdlib passthrough for `unwrap()`/`clone()`/`expect()` in chain loops.
Key changes:
- Replace `receiverCallChain` + `receiverFieldAccess` with unified
`receiverMixedChain: MixedChainStep[]` on ExtractedCall
- Add `extractMixedChain` in utils.ts (handles both call_expression and
field_expression nodes, including C++ `argument` field)
- Add `TYPE_PRESERVING_METHODS` set for stdlib identity operations
- Add C++ inline method double-indexing guard in parsing-processor.ts
and parse-worker.ts
- Add Rust unit struct recognition in type-extractors/rust.ts
- Split field-types.test.ts into per-language test files
- Add ts-mixed-chain fixture and integration tests
- Resolve rust.test.ts todo: Option<T>.unwrap().save() now works
- Update roadmap: Phases 7+8 complete, Phase 9 is next
* 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
* fix: Python/Ruby field type disambiguation and Rust chain test
Three fixes from PR #354 third review:
1. Python typed_parameter name extraction: tree-sitter-python's
typed_parameter uses positional children for the name, not a named
field. TypeEnv and extractParameter now fall back to firstNamedChild.
2. Ruby/Python call-step field resolution: Ruby's AST uses `call` nodes
for both property access and method calls. The chain walker now tries
resolveFieldAccessType before resolveCallTarget for call steps, so
attr_accessor properties resolve via declaredType.
3. Rust chain resolution test: added missing integration test asserting
user.address.save() resolves to Address#save.
Also splits C/C++ and TS/JS columns in type-resolution-system.md
language matrix with footnotes for accuracy.
1062 resolver integration tests passing, 0 failures.
* refactor: Phase 8 code review cleanup — extract walkMixedChain, fix MCP agent gaps
- Extract duplicated chain resolution loop into shared walkMixedChain() helper,
eliminating ~60 lines of copy-pasted code between sequential and worker paths
- Add returnType to ResolveResult, removing redundant lookupFuzzy+find per chain step
- Fix context() tool to include HAS_METHOD, HAS_PROPERTY, OVERRIDES in queries
so agents can discover class members
- Fix p.declaredType Cypher example (column doesn't exist) → p.description
- Add HAS_METHOD, HAS_PROPERTY, OVERRIDES to schema resource
- Document HAS_METHOD/HAS_PROPERTY in impact tool description
- Delete dead code extractMemberAccessParts (superseded by extractMixedChain)
- Replace any with SyntaxNode on extractPropertyDeclaredType
- Add Rust deep-field-chain test (5 tests), Java mixed-chain (4), Go mixed-chain (4)
- All 1075 tests pass (13 new, 0 regressions)
* refactor: type SymbolDefinition.type as NodeLabel, add O(1) receiver index
- Change SymbolDefinition.type from string to NodeLabel union (35 members)
across symbol-table.ts, parse-worker.ts, parsing-processor.ts — compiler
now enforces correctness at all comparison/assignment sites
- Replace O(N*M) linear scan in lookupReceiverType with pre-built
ReceiverTypeIndex (Map<funcName, Map<varName, Entry>>) for O(1) lookups
with proper ambiguity handling and file-level fallback
- All 1075 tests pass, 0 regressions
* fix: capture C++ pointer/ref fields, Kotlin data class props, PHP constructor promotion
Add tree-sitter query patterns for three previously missed property declaration
forms: C++ pointer/reference member fields (Address* addr; Address& ref;),
Kotlin primary constructor val/var parameters (data class User(val name: String)),
and PHP 8.0+ constructor property promotion (public Address $address).
Fix "10 languages" off-by-one in docs (Ruby is single-level only, not deep chain).
Update Python feature matrix cell from No* to Yes* after 31b95f0 fix.
11 new integration tests with per-language fixtures verify property capture,
HAS_PROPERTY edge emission, and field-access chain resolution.
185 lines
6.5 KiB
TypeScript
185 lines
6.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
NODE_TABLES,
|
|
REL_TABLE_NAME,
|
|
REL_TYPES,
|
|
EMBEDDING_TABLE_NAME,
|
|
NODE_SCHEMA_QUERIES,
|
|
REL_SCHEMA_QUERIES,
|
|
SCHEMA_QUERIES,
|
|
FILE_SCHEMA,
|
|
FOLDER_SCHEMA,
|
|
FUNCTION_SCHEMA,
|
|
CLASS_SCHEMA,
|
|
INTERFACE_SCHEMA,
|
|
METHOD_SCHEMA,
|
|
CODE_ELEMENT_SCHEMA,
|
|
COMMUNITY_SCHEMA,
|
|
PROCESS_SCHEMA,
|
|
RELATION_SCHEMA,
|
|
EMBEDDING_SCHEMA,
|
|
CREATE_VECTOR_INDEX_QUERY,
|
|
} from '../../src/core/lbug/schema.js';
|
|
|
|
describe('LadybugDB Schema', () => {
|
|
describe('NODE_TABLES', () => {
|
|
it('includes all core node types', () => {
|
|
const core = ['File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process'];
|
|
for (const t of core) {
|
|
expect(NODE_TABLES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes multi-language node types', () => {
|
|
const multiLang = ['Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
|
|
'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'];
|
|
for (const t of multiLang) {
|
|
expect(NODE_TABLES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('has expected total count', () => {
|
|
// 9 core + 18 multi-language = 27
|
|
expect(NODE_TABLES).toHaveLength(27);
|
|
});
|
|
});
|
|
|
|
describe('REL_TYPES', () => {
|
|
it('includes all expected relationship types', () => {
|
|
const expected = ['CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', 'MEMBER_OF', 'STEP_IN_PROCESS'];
|
|
for (const t of expected) {
|
|
expect(REL_TYPES).toContain(t);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('node schema DDL', () => {
|
|
it.each([
|
|
['FILE_SCHEMA', FILE_SCHEMA, 'File'],
|
|
['FOLDER_SCHEMA', FOLDER_SCHEMA, 'Folder'],
|
|
['FUNCTION_SCHEMA', FUNCTION_SCHEMA, 'Function'],
|
|
['CLASS_SCHEMA', CLASS_SCHEMA, 'Class'],
|
|
['INTERFACE_SCHEMA', INTERFACE_SCHEMA, 'Interface'],
|
|
['METHOD_SCHEMA', METHOD_SCHEMA, 'Method'],
|
|
['CODE_ELEMENT_SCHEMA', CODE_ELEMENT_SCHEMA, 'CodeElement'],
|
|
['COMMUNITY_SCHEMA', COMMUNITY_SCHEMA, 'Community'],
|
|
['PROCESS_SCHEMA', PROCESS_SCHEMA, 'Process'],
|
|
])('%s contains CREATE NODE TABLE for %s', (_, schema, tableName) => {
|
|
expect(schema).toContain('CREATE NODE TABLE');
|
|
expect(schema).toContain(tableName);
|
|
expect(schema).toContain('PRIMARY KEY');
|
|
});
|
|
|
|
it('Function schema has startLine and endLine', () => {
|
|
expect(FUNCTION_SCHEMA).toContain('startLine INT64');
|
|
expect(FUNCTION_SCHEMA).toContain('endLine INT64');
|
|
});
|
|
|
|
it('Function schema has isExported', () => {
|
|
expect(FUNCTION_SCHEMA).toContain('isExported BOOLEAN');
|
|
});
|
|
|
|
it('Community schema has heuristicLabel and cohesion', () => {
|
|
expect(COMMUNITY_SCHEMA).toContain('heuristicLabel STRING');
|
|
expect(COMMUNITY_SCHEMA).toContain('cohesion DOUBLE');
|
|
});
|
|
|
|
it('Process schema has processType and stepCount', () => {
|
|
expect(PROCESS_SCHEMA).toContain('processType STRING');
|
|
expect(PROCESS_SCHEMA).toContain('stepCount INT32');
|
|
});
|
|
});
|
|
|
|
describe('relation schema', () => {
|
|
it('creates a single REL TABLE named CodeRelation', () => {
|
|
expect(RELATION_SCHEMA).toContain(`CREATE REL TABLE ${REL_TABLE_NAME}`);
|
|
});
|
|
|
|
it('has type, confidence, reason, step properties', () => {
|
|
expect(RELATION_SCHEMA).toContain('type STRING');
|
|
expect(RELATION_SCHEMA).toContain('confidence DOUBLE');
|
|
expect(RELATION_SCHEMA).toContain('reason STRING');
|
|
expect(RELATION_SCHEMA).toContain('step INT32');
|
|
});
|
|
|
|
it('connects Function to Function (CALLS)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM Function TO Function');
|
|
});
|
|
|
|
it('connects File to Function (CONTAINS/DEFINES)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM File TO Function');
|
|
});
|
|
|
|
it('connects symbols to Community (MEMBER_OF)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM Function TO Community');
|
|
expect(RELATION_SCHEMA).toContain('FROM Class TO Community');
|
|
});
|
|
|
|
it('connects symbols to Process (STEP_IN_PROCESS)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM Function TO Process');
|
|
expect(RELATION_SCHEMA).toContain('FROM Method TO Process');
|
|
});
|
|
|
|
it('has all FROM/TO pairs needed for HAS_METHOD edges', () => {
|
|
// HAS_METHOD sources: Class, Interface, Struct, Trait, Impl, Record
|
|
// HAS_METHOD targets: Method, Constructor (Property is now HAS_PROPERTY)
|
|
const sources = ['Class', 'Interface'];
|
|
const backtickSources = ['Struct', 'Trait', 'Impl', 'Record'];
|
|
const targets = ['Method'];
|
|
const backtickTargets = ['Constructor'];
|
|
|
|
// Non-backtick source → non-backtick target
|
|
for (const src of sources) {
|
|
for (const tgt of targets) {
|
|
expect(RELATION_SCHEMA).toContain(`FROM ${src} TO ${tgt}`);
|
|
}
|
|
for (const tgt of backtickTargets) {
|
|
expect(RELATION_SCHEMA).toContain(`FROM ${src} TO \`${tgt}\``);
|
|
}
|
|
}
|
|
|
|
// Backtick source → all targets
|
|
for (const src of backtickSources) {
|
|
for (const tgt of targets) {
|
|
expect(RELATION_SCHEMA).toContain(`FROM \`${src}\` TO ${tgt}`);
|
|
}
|
|
for (const tgt of backtickTargets) {
|
|
expect(RELATION_SCHEMA).toContain(`FROM \`${src}\` TO \`${tgt}\``);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('embedding schema', () => {
|
|
it('creates CodeEmbedding table', () => {
|
|
expect(EMBEDDING_SCHEMA).toContain(`CREATE NODE TABLE ${EMBEDDING_TABLE_NAME}`);
|
|
expect(EMBEDDING_SCHEMA).toContain('embedding FLOAT[384]');
|
|
});
|
|
|
|
it('has vector index query', () => {
|
|
expect(CREATE_VECTOR_INDEX_QUERY).toContain('CREATE_VECTOR_INDEX');
|
|
expect(CREATE_VECTOR_INDEX_QUERY).toContain('cosine');
|
|
});
|
|
});
|
|
|
|
describe('schema query ordering', () => {
|
|
it('NODE_SCHEMA_QUERIES has correct count', () => {
|
|
expect(NODE_SCHEMA_QUERIES).toHaveLength(27);
|
|
});
|
|
|
|
it('REL_SCHEMA_QUERIES has one relation table', () => {
|
|
expect(REL_SCHEMA_QUERIES).toHaveLength(1);
|
|
});
|
|
|
|
it('SCHEMA_QUERIES includes all node + rel + embedding schemas', () => {
|
|
// 27 node + 1 rel + 1 embedding = 29
|
|
expect(SCHEMA_QUERIES).toHaveLength(29);
|
|
});
|
|
|
|
it('node schemas come before relation schemas in SCHEMA_QUERIES', () => {
|
|
const relIndex = SCHEMA_QUERIES.indexOf(RELATION_SCHEMA);
|
|
const lastNodeIndex = SCHEMA_QUERIES.indexOf(NODE_SCHEMA_QUERIES[NODE_SCHEMA_QUERIES.length - 1]);
|
|
expect(relIndex).toBeGreaterThan(lastNodeIndex);
|
|
});
|
|
});
|
|
});
|