Skip to content

Commit

Permalink
Support any upper bound
Browse files Browse the repository at this point in the history
  • Loading branch information
soutaro committed Sep 6, 2024
1 parent ea699b9 commit 16362db
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 16 deletions.
2 changes: 1 addition & 1 deletion lib/steep/ast/types/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def params(type)
def type_param(type_param)
Interface::TypeParam.new(
name: type_param.name,
upper_bound: type_opt(type_param.upper_bound),
upper_bound: type_opt(type_param.upper_bound_type),
variance: type_param.variance,
unchecked: type_param.unchecked?
)
Expand Down
4 changes: 2 additions & 2 deletions lib/steep/server/lsp_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ def name_and_params(name, params)
end
s << param.name.to_s

if param.upper_bound
s << " < #{param.upper_bound.to_s}"
if param.upper_bound_type
s << " < #{param.upper_bound_type.to_s}"
end

s
Expand Down
12 changes: 6 additions & 6 deletions lib/steep/signature/validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ def validate_type_application_constraints(type_name, type_params, type_args, loc
type_params.zip(type_args).each do |param, arg|
arg or raise

if param.upper_bound
upper_bound_type = factory.type(param.upper_bound).subst(subst)
if param.upper_bound_type
upper_bound_type = factory.type(param.upper_bound_type).subst(subst)
arg_type = factory.type(arg)

constraints = Subtyping::Constraints.empty
Expand Down Expand Up @@ -236,7 +236,7 @@ def each_variable_type(definition)
def validate_definition_type(definition)
each_method_type(definition) do |method_type|
upper_bounds = method_type.type_params.each.with_object({}) do |param, hash|
hash[param.name] = factory.type_opt(param.upper_bound)
hash[param.name] = factory.type_opt(param.upper_bound_type)
end

checker.push_variable_bounds(upper_bounds) do
Expand Down Expand Up @@ -264,7 +264,7 @@ def validate_one_class_decl(name, entry)
Steep.logger.tagged "#{name}" do
builder.build_instance(name).tap do |definition|
upper_bounds = definition.type_params_decl.each.with_object({}) do |param, bounds|
bounds[param.name] = factory.type_opt(param.upper_bound)
bounds[param.name] = factory.type_opt(param.upper_bound_type)
end

self_type = AST::Types::Name::Instance.new(
Expand Down Expand Up @@ -480,7 +480,7 @@ def validate_one_interface(name)
definition = builder.build_interface(name)

upper_bounds = definition.type_params_decl.each.with_object({}) do |param, bounds|
bounds[param.name] = factory.type_opt(param.upper_bound)
bounds[param.name] = factory.type_opt(param.upper_bound_type)
end

self_type = AST::Types::Name::Interface.new(
Expand Down Expand Up @@ -575,7 +575,7 @@ def validate_one_alias(name, entry = env.type_alias_decls[name])
end

upper_bounds = entry.decl.type_params.each.with_object({}) do |param, bounds|
bounds[param.name] = factory.type_opt(param.upper_bound)
bounds[param.name] = factory.type_opt(param.upper_bound_type)
end

validator.validate_type_alias(entry: entry) do |type|
Expand Down
14 changes: 7 additions & 7 deletions lib/steep/type_construction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ def for_new_method(method_name, node, args:, self_type:, definition:)
end

method_params =
if method_type
TypeInference::MethodParams.build(node: node, method_type: method_type)
else
TypeInference::MethodParams.empty(node: node)
end
if method_type
TypeInference::MethodParams.build(node: node, method_type: method_type)
else
TypeInference::MethodParams.empty(node: node)
end

method_context = TypeInference::Context::MethodContext.new(
name: method_name,
Expand Down Expand Up @@ -405,7 +405,7 @@ def for_module(node, new_module_name)
type_params = definition.type_params_decl.map do |param|
Interface::TypeParam.new(
name: param.name,
upper_bound: checker.factory.type_opt(param.upper_bound),
upper_bound: checker.factory.type_opt(param.upper_bound_type),
variance: param.variance,
unchecked: param.unchecked?
)
Expand Down Expand Up @@ -494,7 +494,7 @@ def for_class(node, new_class_name, super_class_name)
type_params = definition.type_params_decl.map do |type_param|
Interface::TypeParam.new(
name: type_param.name,
upper_bound: type_param.upper_bound&.yield_self {|t| checker.factory.type(t) },
upper_bound: type_param.upper_bound_type&.yield_self {|t| checker.factory.type(t) },
variance: type_param.variance,
unchecked: type_param.unchecked?,
location: type_param.location
Expand Down
4 changes: 4 additions & 0 deletions lib/steep/type_inference/logic_type_interpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ def evaluate_node(env:, node:, type: typing.type_of(node: node))
]
end

if type.is_a?(AST::Types::Var)
type = config.upper_bound(type.name) || type
end

case node.type
when :lvar
name = node.children[0]
Expand Down
45 changes: 45 additions & 0 deletions test/type_check_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2141,4 +2141,49 @@ def self.new: (Integer size) -> instance
YAML
)
end

def test_any_upperbound
run_type_check_test(
signatures: {
"a.rbs" => <<~RBS
class Foo
def foo: [X < String?] (X) -> void
end
RBS
},
code: {
"a.rb" => <<~RUBY
class Foo
def foo(x)
if x
x.encoding
end
x.encoding
end
end
foo = Foo.new
foo.foo("123") #$ String
foo.foo("foo") #$ String?
foo.foo(nil)
RUBY
},
expectations: <<~YAML
---
- file: a.rb
diagnostics:
- range:
start:
line: 7
character: 6
end:
line: 7
character: 14
severity: ERROR
message: Type `(::String | nil)` does not have method `encoding`
code: Ruby::NoMethod
YAML
)
end
end

0 comments on commit 16362db

Please sign in to comment.