class Solargraph::Source::Chain::Call
Handles both method calls and local variable references by first looking for a variable with the name ‘word’, then proceeding to method signature resolution if not found.
Attributes
@return [::Array<Chain>]
@return [Chain, nil]
@return [Location]
@return [String]
Public Class Methods
Source
# File lib/solargraph/source/chain/call.rb, line 30 def initialize word, location = nil, arguments = [], block = nil @word = word @location = location @arguments = arguments @block = block fix_block_pass end
@param word [String] @param location [Location, nil] @param arguments [::Array<Chain>] @param block [Chain, nil]
Public Instance Methods
Source
# File lib/solargraph/source/chain/call.rb, line 50 def resolve api_map, name_pin, locals return super_pins(api_map, name_pin) if word == 'super' return yield_pins(api_map, name_pin) if word == 'yield' found = if head? api_map.visible_pins(locals, word, name_pin, location) else [] end return inferred_pins(found, api_map, name_pin, locals) unless found.empty? pins = name_pin.binder.each_unique_type.flat_map do |context| ns_tag = context.namespace == '' ? '' : context.namespace_type.tag stack = api_map.get_method_stack(ns_tag, word, scope: context.scope) [stack.first].compact end return [] if pins.empty? inferred_pins(pins, api_map, name_pin, locals) end
@param api_map [ApiMap] @param name_pin [Pin::Closure] name_pin.binder should give us the type of the object on which ‘word’ will be invoked @param locals [::Array<Pin::LocalVariable>]
Source
# File lib/solargraph/source/chain/call.rb, line 43 def with_block? !!@block end
Protected Instance Methods
Source
# File lib/solargraph/source/chain/call.rb, line 39 def equality_fields super + [arguments, block] end
@sg-ignore Fix “Not enough arguments to Module#protected”
Solargraph::Source::Chain::Link#equality_fields
Private Instance Methods
Source
# File lib/solargraph/source/chain/call.rb, line 326 def block_call_type(api_map, name_pin, locals) return nil unless with_block? block_context_pin = name_pin block_pin = find_block_pin(api_map) block_context_pin = block_pin.closure if block_pin block.infer(api_map, block_context_pin, locals) end
@param api_map [ApiMap] @param name_pin [Pin::Base] @param block_parameter_types [::Array<ComplexType>] @param locals [::Array<Pin::LocalVariable>] @return [ComplexType, nil]
Source
# File lib/solargraph/source/chain/call.rb, line 298 def block_symbol_call_type(api_map, context, block_parameter_types, locals) # Ruby's shorthand for sending the passed in method name # to the first yield parameter with no arguments block_symbol_name = block.links.first.word block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}" callee = api_map.get_path_pins(block_symbol_call_path).first return_type = callee&.return_type # @todo: Figure out why we get unresolved generics at # this point and need to assume method return types # based on the generic type return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type return_type || ComplexType::UNDEFINED end
@param api_map [ApiMap] @param context [ComplexType] @param block_parameter_types [::Array<ComplexType>] @param locals [::Array<Pin::LocalVariable>] @return [ComplexType, nil]
Source
# File lib/solargraph/source/chain/call.rb, line 235 def extra_return_type docstring, context if docstring.has_tag?('return_single_parameter') #&& context.subtypes.one? return context.subtypes.first || ComplexType::UNDEFINED elsif docstring.has_tag?('return_value_parameter') && context.value_types.one? return context.value_types.first end nil end
@param docstring [YARD::Docstring] @param context [ComplexType] @return [ComplexType, nil]
Source
# File lib/solargraph/source/chain/call.rb, line 314 def find_block_pin(api_map) node_location = Solargraph::Location.from_node(block.node) return if node_location.nil? block_pins = api_map.get_block_pins block_pins.find { |pin| pin.location.contain?(node_location) } end
@param api_map [ApiMap] @return [Pin::Block, nil]
Source
# File lib/solargraph/source/chain/call.rb, line 246 def find_method_pin(name_pin) method_pin = name_pin until method_pin.is_a?(Pin::Method) method_pin = method_pin.closure return if method_pin.nil? end method_pin end
@param name_pin [Pin::Base] @return [Pin::Method, nil]
Source
# File lib/solargraph/source/chain/call.rb, line 288 def fix_block_pass argument = @arguments.last&.links&.first @block = @arguments.pop if argument.is_a?(BlockSymbol) || argument.is_a?(BlockVariable) end
@return [void]
Source
# File lib/solargraph/source/chain/call.rb, line 75 def inferred_pins pins, api_map, name_pin, locals result = pins.map do |p| next p unless p.is_a?(Pin::Method) overloads = p.signatures # next p if overloads.empty? type = ComplexType::UNDEFINED # start with overloads that require blocks; if we are # passing a block, we want to find a signature that will # use it. If we didn't pass a block, the logic below will # reject it regardless with_block, without_block = overloads.partition(&:block?) sorted_overloads = with_block + without_block # @type [Pin::Signature, nil] new_signature_pin = nil sorted_overloads.each do |ol| next unless ol.arity_matches?(arguments, with_block?) match = true atypes = [] arguments.each_with_index do |arg, idx| param = ol.parameters[idx] if param.nil? match = ol.parameters.any?(&:restarg?) break end arg_name_pin = Pin::ProxyType.anonymous(name_pin.context, gates: name_pin.gates, source: :chain) atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals) unless param.compatible_arg?(atype, api_map) || param.restarg? match = false break end end if match if ol.block && with_block? block_atypes = ol.block.parameters.map(&:return_type) if block.links.map(&:class) == [BlockSymbol] # like the bar in foo(&:bar) blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals) else blocktype = block_call_type(api_map, name_pin, locals) end end # @type new_signature_pin [Pin::Signature] new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype) new_return_type = new_signature_pin.return_type if head? # If we're at the head of the chain, we called a # method somewhere that marked itself as returning # self. Given we didn't invoke this on an object, # this must be a method in this same class - so we # use our own self type self_type = name_pin.context else # if we're past the head in the chain, whatever the # type of the lhs side is what 'self' will be in its # declaration - we can't just use the type of the # method pin, as this might be a subclass of the # place where the method is defined self_type = name_pin.binder end # This same logic applies to the YARD work done by # 'with_params()'. # # qualify(), however, happens in the namespace where # the docs were written - from the method pin. type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates) if new_return_type.defined? type ||= ComplexType::UNDEFINED end break if type.defined? end p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil? next p.proxy(type) if type.defined? if !p.macros.empty? result = process_macro(p, api_map, name_pin.context, locals) next result unless result.return_type.undefined? elsif !p.directives.empty? result = process_directive(p, api_map, name_pin.context, locals) next result unless result.return_type.undefined? end p end logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" } out = result.map do |pin| if pin.path == 'Class#new' && name_pin.binder.tag != 'Class' reduced_context = name_pin.binder.reduce_class_type pin.proxy(reduced_context) else next pin if pin.return_type.undefined? selfy = pin.return_type.self_to_type(name_pin.binder) selfy == pin.return_type ? pin : pin.proxy(selfy) end end end
@param pins [::Enumerable<Pin::Method>] @param api_map [ApiMap] @param name_pin [Pin::Base] @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>] @return [::Array<Pin::Base>]
Source
# File lib/solargraph/source/chain/call.rb, line 212 def inner_process_macro pin, macro, api_map, context, locals vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) } txt = macro.tag.text.clone if txt.empty? && macro.tag.name named = api_map.named_macro(macro.tag.name) txt = named.tag.text.clone if named end i = 1 vals.each do |v| txt.gsub!(/\$#{i}/, v.context.namespace) i += 1 end docstring = Solargraph::Source.parse_docstring(txt).to_docstring tag = docstring.tag(:return) unless tag.nil? || tag.types.nil? return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain) end Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain) end
@param pin [Pin::Base] @param macro [YARD::Tags::MacroDirective] @param api_map [ApiMap] @param context [ComplexType] @param locals [::Array<Pin::LocalVariable, Pin::Parameter>] @return [Pin::ProxyType]
Source
# File lib/solargraph/source/chain/call.rb, line 196 def process_directive pin, api_map, context, locals pin.directives.each do |dir| macro = api_map.named_macro(dir.tag.name) next if macro.nil? result = inner_process_macro(pin, macro, api_map, context, locals) return result unless result.return_type.undefined? end Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain end
@param pin [Pin::Method] @param api_map [ApiMap] @param context [ComplexType] @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>] @return [Pin::ProxyType]
Source
# File lib/solargraph/source/chain/call.rb, line 177 def process_macro pin, api_map, context, locals pin.macros.each do |macro| # @todo 'Wrong argument type for # Solargraph::Source::Chain::Call#inner_process_macro: # macro expected YARD::Tags::MacroDirective, received # generic<Elem>' is because we lose 'rooted' information # in the 'Chain::Array' class internally, leaving # ::Array#each shadowed when it shouldn't be. result = inner_process_macro(pin, macro, api_map, context, locals) return result unless result.return_type.undefined? end Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain) end
@param pin [Pin::Base] @param api_map [ApiMap] @param context [ComplexType] @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>] @return [Pin::Base]
Source
# File lib/solargraph/source/chain/call.rb, line 258 def super_pins api_map, name_pin method_pin = find_method_pin(name_pin) return [] if method_pin.nil? pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope) pins.reject{|p| p.path == name_pin.path} end
@param api_map [ApiMap] @param name_pin [Pin::Base] @return [::Array<Pin::Base>]
Source
# File lib/solargraph/source/chain/call.rb, line 282 def with_params type, context return type unless type.to_s.include?('$') ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:rooted_tag).join(', ')).gsub('<>', '')) end
@param type [ComplexType] @param context [ComplexType] @return [ComplexType]
Source
# File lib/solargraph/source/chain/call.rb, line 268 def yield_pins api_map, name_pin method_pin = find_method_pin(name_pin) return [] unless method_pin # @param signature_pin [Pin::Signature] method_pin.signatures.map(&:block).compact.map do |signature_pin| return_type = signature_pin.return_type.qualify(api_map, *name_pin.gates) signature_pin.proxy(return_type) end end
@param api_map [ApiMap] @param name_pin [Pin::Base] @return [::Array<Pin::Base>]