diff --git a/lib/rbs/definition_builder/ancestor_builder.rb b/lib/rbs/definition_builder/ancestor_builder.rb index 62448acc2..597164f67 100644 --- a/lib/rbs/definition_builder/ancestor_builder.rb +++ b/lib/rbs/definition_builder/ancestor_builder.rb @@ -211,6 +211,9 @@ def one_instance_ancestors(type_name) end NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location) + if super_class + InheritModuleError.check!(super_class, env: env) + end ancestors = OneAncestors.class_instance( type_name: type_name, @@ -268,6 +271,9 @@ def one_singleton_ancestors(type_name) end NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location) + if super_class + InheritModuleError.check!(super_class, env: env) + end ancestors = OneAncestors.singleton( type_name: type_name, diff --git a/lib/rbs/errors.rb b/lib/rbs/errors.rb index 2e1450518..b7d047ad3 100644 --- a/lib/rbs/errors.rb +++ b/lib/rbs/errors.rb @@ -148,7 +148,27 @@ def initialize(type_name:, location:) end def self.check!(type_name, env:, location:) - env.class_decls.key?(type_name) or raise new(type_name: type_name, location: location) + if decl = env.class_decls[type_name] + return + end + + raise new(type_name: type_name, location: location) + end + end + + class InheritModuleError < DefinitionError + attr_reader :super_decl + + def initialize(super_decl) + @super_decl = super_decl + + super "#{Location.to_string(super_decl.location)}: Cannot inherit a module: #{super_decl.name}" + end + + def self.check!(super_decl, env:) + return if env.class_decls[super_decl.name].is_a?(Environment::ClassEntry) + + raise new(super_decl) end end diff --git a/sig/errors.rbs b/sig/errors.rbs index 7a99f34ee..c78b1bfe7 100644 --- a/sig/errors.rbs +++ b/sig/errors.rbs @@ -197,6 +197,8 @@ module RBS def location: () -> Location[untyped, untyped]? end + # MixinClassError is raised if a include/prepend/extend has a class (not a module) to mix-in + # class MixinClassError < DefinitionError type member = AST::Members::Include | AST::Members::Prepend | AST::Members::Extend @@ -212,6 +214,18 @@ module RBS def mixin_name: () -> String end + # InheritModuleError is raised if a class definition inherits a module (not a class) + # + class InheritModuleError < DefinitionError + attr_reader super_decl: AST::Declarations::Class::Super + + def initialize: (AST::Declarations::Class::Super) -> void + + def location: () -> Location[untyped, untyped]? + + def self.check!: (AST::Declarations::Class::Super, env: Environment) -> void + end + class RecursiveTypeAliasError < BaseError attr_reader alias_names: Array[TypeName] attr_reader location: Location[untyped, untyped]? diff --git a/test/rbs/definition_builder_test.rb b/test/rbs/definition_builder_test.rb index 7b0ef0df2..ee05afb31 100644 --- a/test/rbs/definition_builder_test.rb +++ b/test/rbs/definition_builder_test.rb @@ -2243,4 +2243,23 @@ class Foo end end end + + def test_class_definition_inheriting_module + SignatureManager.new do |manager| + manager.files.merge!(Pathname("foo.rbs") => <<~EOF) + module Mod + end + + class Foo < Mod + end + EOF + + manager.build do |env| + builder = DefinitionBuilder.new(env: env) + + assert_raises(RBS::InheritModuleError) { builder.build_instance(type_name("::Foo")) } + assert_raises(RBS::InheritModuleError) { builder.build_singleton(type_name("::Foo")) } + end + end + end end