From c51536170c42641ce5bc1c56a84253d4d067a2e3 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 19 Sep 2024 16:52:51 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Replace=20`Kernel#self`=20so=20that=20it=20?= =?UTF-8?q?returns=20it=E2=80=99s=20class=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/steep/ast/builtin.rb | 1 + lib/steep/interface/builder.rb | 23 ++++++++++++++++++++++- sig/steep/ast/builtin.rbs | 2 ++ sig/steep/interface/builder.rbs | 2 ++ test/type_check_test.rb | 25 +++++++++++++++++++++++-- 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/steep/ast/builtin.rb b/lib/steep/ast/builtin.rb index 77f62adb9..f16a4dc5f 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 87302a1b6..3f4f77443 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/sig/steep/ast/builtin.rbs b/sig/steep/ast/builtin.rbs index 6205a98ae..80e8ca2e1 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 f31ef2367..4ff746da4 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/test/type_check_test.rb b/test/type_check_test.rb index 15bfc76c2..3365f4daa 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 From 76fb79d9817294bd1dde5e083cbef8070f573358 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 19 Sep 2024 17:01:16 +0900 Subject: [PATCH 2/2] Delete unused `self_class?` method The type of `#class` returns correct types now. --- lib/steep/type_construction.rb | 31 ++----------------------------- sig/steep/type_construction.rbs | 3 --- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/lib/steep/type_construction.rb b/lib/steep/type_construction.rb index d1a1965e0..d4600f23e 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/type_construction.rbs b/sig/steep/type_construction.rbs index b4b17f00e..8761809d2 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