Skip to content

Commit 1e2caae

Browse files
committed
Align type params in AncestorBuilder#validate_super_class!
The superclass comparison across multiple declarations compared the superclass args as written, so declarations that declare the same superclass with different type parameter names (`class C[A] < Base[A]` and `class C[B] < Base[B]`) raised a false SuperclassMismatchError. Align the args to the entry's type parameter names before comparing, like ModuleEntry#self_types and mixin_ancestors do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TBY8ct4HpkVkZNPdsNHEDE
1 parent b954d29 commit 1e2caae

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

lib/rbs/definition_builder/ancestor_builder.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,22 @@ def validate_super_class!(type_name, entry)
177177

178178
return if with_super_classes.size <= 1
179179

180+
entry_param_names = entry.type_params.map(&:name)
181+
180182
super_types = with_super_classes.map do |decl|
181183
super_class = decl.super_class or raise
182-
Types::ClassInstance.new(name: super_class.name, args: super_class.args, location: nil)
184+
args = super_class.args
185+
186+
decl_param_names = decl.type_params.map(&:name)
187+
unless decl_param_names == entry_param_names || args.empty?
188+
align_params = Substitution.build(
189+
decl_param_names,
190+
entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
191+
)
192+
args = args.map {|type| type.sub(align_params) }
193+
end
194+
195+
Types::ClassInstance.new(name: super_class.name, args: args, location: nil)
183196
end
184197

185198
super_types.uniq!

test/rbs/ancestor_builder_test.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,45 @@ class B < ::String
452452
end
453453
end
454454

455+
def test_instance_ancestors_super_class_validation_renamed_params
456+
SignatureManager.new do |manager|
457+
manager.files.merge!(Pathname("foo.rbs") => <<-EOF)
458+
class Base[T]
459+
end
460+
461+
class A[X] < Base[X]
462+
end
463+
464+
class B[X] < Base[X]
465+
end
466+
467+
class B[Y] < Base[Integer]
468+
end
469+
EOF
470+
471+
manager.files.merge!(Pathname("foo2.rbs") => <<-EOF)
472+
class A[Y] < Base[Y]
473+
end
474+
EOF
475+
476+
manager.build do |env|
477+
builder = DefinitionBuilder::AncestorBuilder.new(env: env)
478+
479+
# ::A is valid: the declarations declare the same superclass modulo type parameter renaming.
480+
builder.one_instance_ancestors(type_name("::A")).tap do |a|
481+
assert_equal Ancestor::Instance.new(name: type_name("::Base"), args: [parse_type("X", variables: [:X])], source: :super),
482+
a.super_class
483+
end
484+
485+
# ::B is invalid: the superclass args are different.
486+
error = assert_raises SuperclassMismatchError do
487+
builder.one_instance_ancestors(type_name("::B"))
488+
end
489+
assert_equal error.name, type_name("::B")
490+
end
491+
end
492+
end
493+
455494
def test_singleton_ancestors
456495
SignatureManager.new do |manager|
457496
manager.files[Pathname("foo.rbs")] = <<EOF

0 commit comments

Comments
 (0)