fix nested Ruby factory ownership

This commit is contained in:
kigland 2026-09-25 09:14:38 +08:00
parent b78bba98fa
commit 9046175902
3 changed files with 38 additions and 2 deletions

View file

@ -46,10 +46,14 @@ export const rubyFactoryBindingName = (node: SyntaxNode): string | undefined =>
export const rubyClassConfig: ClassExtractionConfig = {
language: SupportedLanguages.Ruby,
typeDeclarationNodes: ['class', 'do_block', 'block'],
ancestorScopeNodeTypes: ['module', 'class'],
ancestorScopeNodeTypes: ['module', 'class', 'do_block', 'block'],
// #1978: key nested-type nodes by their fully-qualified path (Outer.Inner) so
// same-tail classes nested under different modules stay distinct.
qualifiedNodeId: true,
extractScopeSegments: (node) => {
const factoryName = rubyFactoryBindingName(node);
return factoryName ? [factoryName] : undefined;
},
extractName: (node) => rubyFactoryBindingName(node),
extractType: (node) => {
const type = rubyFactoryType(node);

View file

@ -146,7 +146,7 @@ const BUILT_INS: ReadonlySet<string> = new Set([
* All other container types are returned as-is.
*/
const rubyResolveEnclosingOwner = (node: SyntaxNode): SyntaxNode | null => {
if (node.type === 'do_block') {
if (node.type === 'do_block' || node.type === 'block') {
return rubyFactoryType(node) === undefined ? null : node;
}
if (node.type === 'singleton_class') {

View file

@ -46,6 +46,18 @@ class Outer
def nested_class_method = 1
end
end
First = Class.new do
Item = Class.new do
def first_item_method = 1
end
end
Second = Class.new do
Item = Class.new do
def second_item_method = 1
end
end
end
`,
});
@ -105,4 +117,24 @@ end
expect(nestedOwner).toBeDefined();
expect(ownership.map((edge) => edge.rel.sourceId)).toEqual([nestedOwner?.id]);
});
it('keeps same-tail factories distinct when nested inside factory blocks', () => {
const owners = result.graph.nodes.filter(
(node) => node.label === 'Class' && node.properties.name === 'Item',
);
const firstOwner = owners.find((node) => node.id.includes('Outer.First.Item'));
const secondOwner = owners.find((node) => node.id.includes('Outer.Second.Item'));
const firstOwnership = getRelationships(result, 'HAS_METHOD').filter(
(edge) => edge.target === 'first_item_method',
);
const secondOwnership = getRelationships(result, 'HAS_METHOD').filter(
(edge) => edge.target === 'second_item_method',
);
expect(owners).toHaveLength(2);
expect(firstOwner).toBeDefined();
expect(secondOwner).toBeDefined();
expect(firstOwnership.map((edge) => edge.rel.sourceId)).toEqual([firstOwner?.id]);
expect(secondOwnership.map((edge) => edge.rel.sourceId)).toEqual([secondOwner?.id]);
});
});