lib/solargraph/pin/parameter.rb
# frozen_string_literal: true module Solargraph module Pin class Parameter < LocalVariable def return_type if @return_type.nil? @return_type = ComplexType.new found = param_tag @return_type = ComplexType.try_parse(*found.types) unless found.nil? or found.types.nil? end super @return_type end # The parameter's zero-based location in the block's signature. # # @return [Integer] def index closure.parameter_names.index(name) end # @param api_map [ApiMap] def typify api_map return return_type.qualify(api_map, closure.context.namespace) unless return_type.undefined? closure.is_a?(Pin::Block) ? typify_block_param(api_map) : typify_method_param(api_map) end def documentation tag = param_tag return '' if tag.nil? || tag.text.nil? tag.text end def try_merge! pin return false unless super && closure == pin.closure true end def probe api_map typify api_map end private # @return [YARD::Tags::Tag] def param_tag found = nil params = closure.docstring.tags(:param) params.each do |p| next unless p.name == name found = p break end if found.nil? and !index.nil? found = params[index] if params[index] && (params[index].name.nil? || params[index].name.empty?) end found end # @param api_map [ApiMap] # @return [ComplexType] def typify_block_param api_map if closure.is_a?(Pin::Block) && closure.receiver chain = Source::NodeChainer.chain(closure.receiver, filename) clip = api_map.clip_at(location.filename, location.range.start) locals = clip.locals - [self] meths = chain.define(api_map, closure, locals) meths.each do |meth| if meth.docstring.has_tag?(:yieldparam_single_parameter) type = chain.base.infer(api_map, closure, locals) if type.defined? && !type.subtypes.empty? bmeth = chain.base.define(api_map, closure, locals).first return type.subtypes.first.qualify(api_map, bmeth.context.namespace) end else yps = meth.docstring.tags(:yieldparam) unless yps[index].nil? or yps[index].types.nil? or yps[index].types.empty? return ComplexType.try_parse(yps[index].types.first).self_to(chain.base.infer(api_map, closure, locals).namespace).qualify(api_map, meth.context.namespace) end end end end ComplexType::UNDEFINED end # @param api_map [ApiMap] # @return [ComplexType] def typify_method_param api_map meths = api_map.get_method_stack(closure.full_context.namespace, closure.name, scope: closure.scope) # meths.shift # Ignore the first one meths.each do |meth| found = nil params = meth.docstring.tags(:param) if params.empty? params = see_reference(docstring, api_map) end params.each do |p| next unless p.name == name found = p break end if found.nil? and !index.nil? found = params[index] if params[index] && (params[index].name.nil? || params[index].name.empty?) end return ComplexType.try_parse(*found.types).qualify(api_map, meth.context.namespace) unless found.nil? || found.types.nil? end ComplexType::UNDEFINED end # @param heredoc [YARD::Docstring] # @param api_map [ApiMap] # @param skip [Array] # @return [Array<YARD::Tags::Tag>] def see_reference heredoc, api_map, skip = [] heredoc.ref_tags.each do |ref| next unless ref.tag_name == 'param' && ref.owner result = resolve_reference(ref.owner.to_s, api_map, skip) return result unless result.nil? end [] end # @param ref [String] # @param api_map [ApiMap] # @param skip [Array] # @return [Array<YARD::Tags::Tag>, nil] def resolve_reference ref, api_map, skip return nil if skip.include?(ref) skip.push ref parts = ref.split(/[\.#]/) if parts.first.empty? path = "#{namespace}#{ref}" else fqns = api_map.qualify(parts.first, namespace) return nil if fqns.nil? path = fqns + ref[parts.first.length] + parts.last end pins = api_map.get_path_pins(path) pins.each do |pin| params = pin.docstring.tags(:param) return params unless params.empty? end pins.each do |pin| params = see_reference(pin.docstring, api_map, skip) return params unless params.empty? end nil end end end end