Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions lib/solargraph/pin/callable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,11 @@ def resolve_generics_from_context generics_to_resolve,
resolved_generic_values: {}
callable = super(generics_to_resolve, return_type_context, resolved_generic_values: resolved_generic_values)
callable.parameters = callable.parameters.each_with_index.map do |param, i|
if arg_types.nil?
param.dup
else
param.resolve_generics_from_context(generics_to_resolve,
arg_types[i],
resolved_generic_values: resolved_generic_values)
end
# nil defers to any binding resolved_generic_values already has.
arg_type_for_param = arg_types&.[](i)
param.resolve_generics_from_context(generics_to_resolve,
arg_type_for_param,
resolved_generic_values: resolved_generic_values)
end
if callable.block?
callable.block = block.resolve_generics_from_context(generics_to_resolve,
Expand Down
53 changes: 53 additions & 0 deletions spec/pin/callable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

describe Solargraph::Pin::Callable do
it 'infers a method-level generic block parameter from the argument bound to it' do
# Enumerable#each_with_object is declared in RBS as
# [U] (obj U) { [U] (arg_0 Elem, obj U) -> untyped } -> U
# `U` is a method-level generic bound by the `obj` argument passed
# to each_with_object, not by the receiver's own `Elem` generic.
source = Solargraph::Source.load_string(%(
# @type [Array<Integer>]
a = [1, 2, 3]
a.each_with_object({}) do |e, memo|
memo
end
), 'test.rb')
api_map = Solargraph::ApiMap.new.map(source)
clip = api_map.clip_at('test.rb', [4, 8])
type = clip.infer
expect(type.to_s).to eq('Hash')
end

it 'infers a method-level generic block parameter from the argument bound to it (Enumerable#inject)' do
# Enumerable#inject has the same method-level-generic shape as
# each_with_object: [U] (U init) { (U, Elem) -> U } -> U
source = Solargraph::Source.load_string(%(
# @type [Array<Integer>]
a = [1, 2, 3]
a.inject(0) do |acc, e|
acc
end
), 'test.rb')
api_map = Solargraph::ApiMap.new.map(source)
clip = api_map.clip_at('test.rb', [4, 8])
type = clip.infer
expect(type.to_s).to eq('Integer')
end

it 'still infers the receiver-level generic block parameter for each_with_index' do
# Negative control: Array#each_with_index has no method-level
# generic in its block signature, only the receiver's own Elem.
source = Solargraph::Source.load_string(%(
# @type [Array<Integer>]
a = [1, 2, 3]
a.each_with_index do |e, i|
e
end
), 'test.rb')
api_map = Solargraph::ApiMap.new.map(source)
clip = api_map.clip_at('test.rb', [4, 8])
type = clip.infer
expect(type.to_s).to eq('Integer')
end
end
Loading