fix(ruby): cover braced factory blocks

This commit is contained in:
kigland 2026-09-25 07:52:43 +08:00
parent e27532e158
commit e78b9b0a39
4 changed files with 46 additions and 3 deletions

View file

@ -15,7 +15,7 @@ export type RubyFactoryType = ClassLikeNodeLabel | 'Trait';
* type container.
*/
export const rubyFactoryType = (node: SyntaxNode): RubyFactoryType | undefined => {
if (node.type !== 'do_block') return undefined;
if (node.type !== 'do_block' && node.type !== 'block') return undefined;
const call = node.parent;
if (call?.type !== 'call' || call.childForFieldName?.('block') !== node) return undefined;

View file

@ -2238,6 +2238,15 @@ export const RUBY_QUERIES = `
(#eq? @_factory.receiver "Struct")
(#eq? @_factory.method "new"))
((assignment
left: (constant) @name
right: (call
receiver: (constant) @_factory.receiver
method: (identifier) @_factory.method
block: (block) @definition.struct))
(#eq? @_factory.receiver "Struct")
(#eq? @_factory.method "new"))
((assignment
left: (constant) @name
right: (call
@ -2247,6 +2256,15 @@ export const RUBY_QUERIES = `
(#eq? @_factory.receiver "Data")
(#eq? @_factory.method "define"))
((assignment
left: (constant) @name
right: (call
receiver: (constant) @_factory.receiver
method: (identifier) @_factory.method
block: (block) @definition.class))
(#eq? @_factory.receiver "Data")
(#eq? @_factory.method "define"))
((assignment
left: (constant) @name
right: (call
@ -2256,6 +2274,15 @@ export const RUBY_QUERIES = `
(#eq? @_factory.receiver "Class")
(#eq? @_factory.method "new"))
((assignment
left: (constant) @name
right: (call
receiver: (constant) @_factory.receiver
method: (identifier) @_factory.method
block: (block) @definition.class))
(#eq? @_factory.receiver "Class")
(#eq? @_factory.method "new"))
((assignment
left: (constant) @name
right: (call
@ -2265,6 +2292,15 @@ export const RUBY_QUERIES = `
(#eq? @_factory.receiver "Module")
(#eq? @_factory.method "new"))
((assignment
left: (constant) @name
right: (call
receiver: (constant) @_factory.receiver
method: (identifier) @_factory.method
block: (block))) @definition.module
(#eq? @_factory.receiver "Module")
(#eq? @_factory.method "new"))
; ── Instance methods ─────────────────────────────────────────────────────────
(method
name: (identifier) @name) @definition.method

View file

@ -1015,7 +1015,7 @@ export const findEnclosingClassInfo = (
// the global container set. Let the Ruby hooks jointly opt an exact factory
// shape in; arbitrary blocks fall through to their lexical owner unchanged.
if (
current.type === 'do_block' &&
(current.type === 'do_block' || current.type === 'block') &&
resolveEnclosingOwner !== undefined &&
resolveContainerTypeOwner !== undefined &&
resolveEnclosingOwner(current) === current

View file

@ -33,6 +33,10 @@ class Outer
def module_method = 1
end
BraceType = Class.new {
def brace_method = 1
}
ArbitraryType = Builder.make do
def arbitrary_method = 1
end
@ -57,6 +61,7 @@ end
['DataType', 'Class', 'data_method'],
['ClassType', 'Class', 'class_method'],
['ModuleType', 'Trait', 'module_method'],
['BraceType', 'Class', 'brace_method'],
])(
'materializes %s as a %s and attributes its factory-block method',
(owner, ownerLabel, method) => {
@ -96,6 +101,8 @@ end
expect(owners).toHaveLength(2);
expect(new Set(owners.map((node) => node.id)).size).toBe(2);
expect(ownership.map((edge) => edge.source)).toEqual(['ClassType']);
const nestedOwner = owners.find((node) => node.id.includes('Outer.Nested.ClassType'));
expect(nestedOwner).toBeDefined();
expect(ownership.map((edge) => edge.rel.sourceId)).toEqual([nestedOwner?.id]);
});
});