diff --git a/lib/steep/ast/builtin.rb b/lib/steep/ast/builtin.rb index 77f62adb..f16a4dc5 100644 --- a/lib/steep/ast/builtin.rb +++ b/lib/steep/ast/builtin.rb @@ -65,6 +65,7 @@ def module_type?(type) Regexp = Type.new("::Regexp") NilClass = Type.new("::NilClass") Proc = Type.new("::Proc") + Kernel = Type.new("::Kernel") def self.nil_type AST::Types::Nil.instance diff --git a/lib/steep/interface/builder.rb b/lib/steep/interface/builder.rb index 87302a1b..3f4f7744 100644 --- a/lib/steep/interface/builder.rb +++ b/lib/steep/interface/builder.rb @@ -281,6 +281,7 @@ def singleton_shape(type_name) method_name = method_name_for(type_def, name) method_type = factory.method_type(type_def.type) method_type = replace_primitive_method(method_name, type_def, method_type) + method_type = replace_kernel_class(method_name, type_def, method_type) { AST::Builtin::Class.instance_type } Shape::MethodOverload.new(method_type, [type_def]) end @@ -311,6 +312,9 @@ def object_shape(type_name) method_name = method_name_for(type_def, name) method_type = factory.method_type(type_def.type) method_type = replace_primitive_method(method_name, type_def, method_type) + if type_name.class? + method_type = replace_kernel_class(method_name, type_def, method_type) { AST::Types::Name::Singleton.new(name: type_name) } + end Shape::MethodOverload.new(method_type, [type_def]) end @@ -748,7 +752,7 @@ def replace_primitive_method(method_name, method_def, method_type) return_type: AST::Types::Logic::ReceiverIsNil.instance() ) ) - end + end end when :! @@ -801,6 +805,23 @@ def replace_primitive_method(method_name, method_def, method_type) method_type end + + def replace_kernel_class(method_name, method_def, method_type) + defined_in = method_def.defined_in + member = method_def.member + + if member.is_a?(RBS::AST::Members::MethodDefinition) + case method_name.method_name + when :class + case defined_in + when AST::Builtin::Kernel.module_name + return method_type.with(type: method_type.type.with(return_type: yield)) + end + end + end + + method_type + end end end end diff --git a/lib/steep/type_construction.rb b/lib/steep/type_construction.rb index d1a1965e..d4600f23 100644 --- a/lib/steep/type_construction.rb +++ b/lib/steep/type_construction.rb @@ -2657,34 +2657,11 @@ def check(node, type, constraints: Subtyping::Constraints.empty) def synthesize_sendish(node, hint:, tapp:) case node.type when :send - yield_self do - if self_class?(node) - module_type = expand_alias(module_context.module_type) - type = if module_type.is_a?(AST::Types::Name::Singleton) - AST::Types::Name::Singleton.new(name: module_type.name) - else - module_type - end - - add_typing(node, type: type) - else - type_send(node, send_node: node, block_params: nil, block_body: nil, tapp: tapp, hint: hint) - end - end + type_send(node, send_node: node, block_params: nil, block_body: nil, tapp: tapp, hint: hint) when :csend yield_self do send_type, constr = - if self_class?(node) - module_type = expand_alias(module_context.module_type) - type = if module_type.is_a?(AST::Types::Name::Singleton) - AST::Types::Name::Singleton.new(name: module_type.name) - else - module_type - end - add_typing(node, type: type).to_ary - else - type_send(node, send_node: node, block_params: nil, block_body: nil, unwrap: true, tapp: tapp, hint: hint).to_ary - end + type_send(node, send_node: node, block_params: nil, block_body: nil, unwrap: true, tapp: tapp, hint: hint).to_ary constr .update_type_env { context.type_env.join(constr.context.type_env, context.type_env) } @@ -4687,10 +4664,6 @@ def fallback_to_any(node) add_typing node, type: AST::Builtin.any_type end - def self_class?(node) - node.type == :send && node.children[0]&.type == :self && node.children[1] == :class - end - def namespace_module?(node) # @type var nodes: Array[Parser::AST::Node] nodes = diff --git a/sig/steep/ast/builtin.rbs b/sig/steep/ast/builtin.rbs index 6205a98a..80e8ca2e 100644 --- a/sig/steep/ast/builtin.rbs +++ b/sig/steep/ast/builtin.rbs @@ -49,6 +49,8 @@ module Steep Proc: Type + Kernel: Type + def self.nil_type: () -> Types::Nil def self.any_type: () -> Types::Any diff --git a/sig/steep/interface/builder.rbs b/sig/steep/interface/builder.rbs index f31ef236..4ff746da 100644 --- a/sig/steep/interface/builder.rbs +++ b/sig/steep/interface/builder.rbs @@ -110,6 +110,8 @@ module Steep def replace_primitive_method: (method_name, RBS::Definition::Method::TypeDef, MethodType) -> MethodType + def replace_kernel_class: (method_name, RBS::Definition::Method::TypeDef, MethodType) { () -> AST::Types::t } -> MethodType + @subtyping: Subtyping::Check? def subtyping: () -> Subtyping::Check diff --git a/sig/steep/type_construction.rbs b/sig/steep/type_construction.rbs index b4b17f00..8761809d 100644 --- a/sig/steep/type_construction.rbs +++ b/sig/steep/type_construction.rbs @@ -393,9 +393,6 @@ module Steep def fallback_to_any: (Parser::AST::Node node) ?{ () -> Diagnostic::Ruby::Base } -> Pair - # Return `true` if `node` is `self.class` - def self_class?: (Parser::AST::Node node) -> bool - # Returns `true` if the given `node` is a `class` or `module` declaration that only contains module/class definitions # def namespace_module?: (Parser::AST::Node node) -> bool diff --git a/test/type_check_test.rb b/test/type_check_test.rb index 15bfc76c..3365f4da 100644 --- a/test/type_check_test.rb +++ b/test/type_check_test.rb @@ -2349,13 +2349,34 @@ def initialize: (Integer) -> void }, code: { "a.rb" => <<~RUBY - Foo.new(1).class.new(2) + Foo.new(1).class.fooo + Foo.class.barr RUBY }, expectations: <<~YAML --- - file: a.rb - diagnostics: [] + diagnostics: + - range: + start: + line: 1 + character: 17 + end: + line: 1 + character: 21 + severity: ERROR + message: Type `singleton(::Foo)` does not have method `fooo` + code: Ruby::NoMethod + - range: + start: + line: 2 + character: 10 + end: + line: 2 + character: 14 + severity: ERROR + message: Type `::Class` does not have method `barr` + code: Ruby::NoMethod YAML ) end