Skip to content

Commit 987fed8

Browse files
committed
Fix AncestorBuilder
1 parent c0306dd commit 987fed8

File tree

5 files changed

+170
-10
lines changed

5 files changed

+170
-10
lines changed

lib/rbs/definition_builder/ancestor_builder.rb

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,60 @@ def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included
414414
end
415415
end
416416
when AST::Ruby::Declarations::Base
417-
# noop
417+
decl.members.each do |member|
418+
case member
419+
when AST::Ruby::Members::IncludeMember
420+
if included_modules
421+
module_name = member.module_name
422+
module_args = member.type_args
423+
424+
# Check if mixing in a class (not allowed)
425+
if env.class_decl?(module_name)
426+
raise MixinClassError.new(type_name: type_name, member: member)
427+
end
428+
429+
# Check if module exists
430+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
431+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
432+
module_name = env.normalize_module_name(module_name)
433+
included_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
434+
end
435+
436+
when AST::Ruby::Members::ExtendMember
437+
if extended_modules
438+
module_name = member.module_name
439+
module_args = member.type_args
440+
441+
# Check if mixing in a class (not allowed)
442+
if env.class_decl?(module_name)
443+
raise MixinClassError.new(type_name: type_name, member: member)
444+
end
445+
446+
# Check if module exists
447+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
448+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
449+
module_name = env.normalize_module_name(module_name)
450+
extended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
451+
end
452+
453+
when AST::Ruby::Members::PrependMember
454+
if prepended_modules
455+
module_name = member.module_name
456+
module_args = member.type_args
457+
458+
# Check if mixing in a class (not allowed)
459+
if env.class_decl?(module_name)
460+
raise MixinClassError.new(type_name: type_name, member: member)
461+
end
462+
463+
# Check if module exists
464+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
465+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
466+
module_name = env.normalize_module_name(module_name)
467+
prepended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
468+
end
469+
end
470+
end
418471
end
419472
end
420473

lib/rbs/errors.rb

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,31 +475,51 @@ def initialize(type_name:, member:)
475475
@type_name = type_name
476476
@member = member
477477

478-
super "#{Location.to_string member.location}: Cannot #{mixin_name} a class `#{member.name}` in the definition of `#{type_name}`"
478+
super "#{Location.to_string member.location}: Cannot #{mixin_name} a class `#{member_name(member)}` in the definition of `#{type_name}`"
479479
end
480480

481481
def location
482482
member.location
483483
end
484484

485485
def self.check!(type_name:, env:, member:)
486-
if env.class_decl?(member.name)
486+
name = case member
487+
when AST::Members::Include, AST::Members::Extend, AST::Members::Prepend
488+
member.name
489+
when AST::Ruby::Members::IncludeMember, AST::Ruby::Members::ExtendMember, AST::Ruby::Members::PrependMember
490+
member.module_name
491+
else
492+
raise "Unknown member type: #{member.class}"
493+
end
494+
495+
if env.class_decl?(name)
487496
raise new(type_name: type_name, member: member)
488497
end
489498
end
490499

491500
private
492501

502+
def member_name(member)
503+
case member
504+
when AST::Members::Include, AST::Members::Extend, AST::Members::Prepend
505+
member.name
506+
when AST::Ruby::Members::IncludeMember, AST::Ruby::Members::ExtendMember, AST::Ruby::Members::PrependMember
507+
member.module_name
508+
else
509+
raise "Unknown member type: #{member.class}"
510+
end
511+
end
512+
493513
def mixin_name
494514
case member
495-
when AST::Members::Prepend
515+
when AST::Members::Prepend, AST::Ruby::Members::PrependMember
496516
"prepend"
497-
when AST::Members::Include
517+
when AST::Members::Include, AST::Ruby::Members::IncludeMember
498518
"include"
499-
when AST::Members::Extend
519+
when AST::Members::Extend, AST::Ruby::Members::ExtendMember
500520
"extend"
501521
else
502-
raise
522+
raise "Unknown member type: #{member.class}"
503523
end
504524
end
505525
end

sig/definition.rbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ module RBS
155155
type source = :super # Inheritance
156156
| nil # Itself
157157
| AST::Members::Include | AST::Members::Extend | AST::Members::Prepend # AST
158+
| AST::Ruby::Members::IncludeMember | AST::Ruby::Members::ExtendMember | AST::Ruby::Members::PrependMember # Ruby AST
158159
| AST::Declarations::Module::Self
159160

160161
attr_reader name: TypeName

sig/errors.rbs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,16 @@ module RBS
115115
class NoMixinFoundError < DefinitionError
116116
include DetailedMessageable
117117

118+
type member = AST::Members::t | AST::Ruby::Members::IncludeMember | AST::Ruby::Members::ExtendMember | AST::Ruby::Members::PrependMember
119+
118120
attr_reader type_name: TypeName
119-
attr_reader member: AST::Members::t
121+
attr_reader member: member
120122

121-
def initialize: (type_name: TypeName, member: AST::Members::t) -> void
123+
def initialize: (type_name: TypeName, member: member) -> void
122124

123125
def location: () -> Location[untyped, untyped]?
124126

125-
def self.check!: (TypeName, env: Environment, member: AST::Members::t) -> void
127+
def self.check!: (TypeName, env: Environment, member: member) -> void
126128
end
127129

128130
class DuplicatedMethodDefinitionError < DefinitionError
@@ -287,6 +289,7 @@ module RBS
287289
include DetailedMessageable
288290

289291
type member = AST::Members::Include | AST::Members::Prepend | AST::Members::Extend
292+
| AST::Ruby::Members::IncludeMember | AST::Ruby::Members::PrependMember | AST::Ruby::Members::ExtendMember
290293

291294
attr_reader type_name: TypeName
292295
attr_reader member: member
@@ -297,6 +300,8 @@ module RBS
297300

298301
def self.check!: (type_name: TypeName, env: Environment, member: member) -> void
299302

303+
def member_name: (member) -> TypeName
304+
300305
def mixin_name: () -> String
301306
end
302307

test/rbs/ancestor_builder_test.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,4 +1214,85 @@ class Foo
12141214
end
12151215
end
12161216
end
1217+
1218+
def test__one_ancestors__ruby_include_extend_prepend
1219+
SignatureManager.new(system_builtin: true) do |manager|
1220+
manager.files[Pathname("mixin.rbs")] = <<~EOF
1221+
module TestMixin
1222+
end
1223+
EOF
1224+
1225+
manager.ruby_files[Pathname("lib/test.rb")] = <<~EOF
1226+
module TestMixin
1227+
end
1228+
1229+
class TestClass
1230+
include TestMixin
1231+
end
1232+
1233+
class TestExtendClass
1234+
extend TestMixin
1235+
end
1236+
1237+
class TestPrependClass
1238+
prepend TestMixin
1239+
end
1240+
EOF
1241+
1242+
manager.build do |env|
1243+
builder = DefinitionBuilder::AncestorBuilder.new(env: env)
1244+
1245+
# Test include
1246+
builder.one_instance_ancestors(type_name("::TestClass")).tap do |a|
1247+
assert_equal type_name("::TestClass"), a.type_name
1248+
assert_equal [], a.params
1249+
1250+
assert_equal Ancestor::Instance.new(name: type_name("::Object"), args: [], source: :super), a.super_class
1251+
assert_equal 1, a.included_modules.size
1252+
assert_equal type_name("::TestMixin"), a.included_modules[0].name
1253+
assert_equal [], a.included_modules[0].args
1254+
assert_instance_of AST::Ruby::Members::IncludeMember, a.included_modules[0].source
1255+
assert_equal [], a.included_interfaces
1256+
assert_equal [], a.prepended_modules
1257+
assert_nil a.extended_modules
1258+
assert_nil a.extended_interfaces
1259+
assert_nil a.self_types
1260+
end
1261+
1262+
# Test extend
1263+
builder.one_singleton_ancestors(type_name("::TestExtendClass")).tap do |a|
1264+
assert_equal type_name("::TestExtendClass"), a.type_name
1265+
assert_nil a.params
1266+
1267+
assert_equal Ancestor::Singleton.new(name: type_name("::Object")), a.super_class
1268+
assert_nil a.included_modules
1269+
assert_nil a.included_interfaces
1270+
assert_nil a.prepended_modules
1271+
assert_equal 1, a.extended_modules.size
1272+
assert_equal type_name("::TestMixin"), a.extended_modules[0].name
1273+
assert_equal [], a.extended_modules[0].args
1274+
assert_instance_of AST::Ruby::Members::ExtendMember, a.extended_modules[0].source
1275+
assert_equal [], a.extended_interfaces
1276+
assert_nil a.self_types
1277+
end
1278+
1279+
# Test prepend
1280+
builder.one_instance_ancestors(type_name("::TestPrependClass")).tap do |a|
1281+
assert_equal type_name("::TestPrependClass"), a.type_name
1282+
assert_equal [], a.params
1283+
1284+
assert_equal Ancestor::Instance.new(name: type_name("::Object"), args: [], source: :super), a.super_class
1285+
assert_equal [], a.included_modules
1286+
assert_equal [], a.included_interfaces
1287+
assert_equal 1, a.prepended_modules.size
1288+
assert_equal type_name("::TestMixin"), a.prepended_modules[0].name
1289+
assert_equal [], a.prepended_modules[0].args
1290+
assert_instance_of AST::Ruby::Members::PrependMember, a.prepended_modules[0].source
1291+
assert_nil a.extended_modules
1292+
assert_nil a.extended_interfaces
1293+
assert_nil a.self_types
1294+
end
1295+
end
1296+
end
1297+
end
12171298
end

0 commit comments

Comments
 (0)