lib/solargraph/pin/base_method.rb



# frozen_string_literal: true


module Solargraph
  module Pin
    # The base class for method and attribute pins.

    #

    class BaseMethod < Closure
      # @return [::Symbol] :public, :private, or :protected

      attr_reader :visibility

      # @param visibility [::Symbol] :public, :protected, or :private

      def initialize visibility: :public, **splat
        super(splat)
        @visibility = visibility
      end

      def return_type
        @return_type ||= generate_complex_type
      end

      def path
        @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}"
      end

      def typify api_map
        decl = super
        return decl unless decl.undefined?
        type = see_reference(api_map) || typify_from_super(api_map)
        return type.qualify(api_map, namespace) unless type.nil?
        name.end_with?('?') ? ComplexType::BOOLEAN : ComplexType::UNDEFINED
      end

      # @return [Array<String>]

      def parameters
        []
      end

      # @return [Array<String>]

      def parameter_names
        []
      end

      private

      # @return [ComplexType]

      def generate_complex_type
        tag = docstring.tag(:return)
        return ComplexType::UNDEFINED if tag.nil? or tag.types.nil? or tag.types.empty?
        ComplexType.try_parse *tag.types
      end

      # @param api_map [ApiMap]

      # @return [ComplexType, nil]

      def see_reference api_map
        docstring.ref_tags.each do |ref|
          next unless ref.tag_name == 'return' && ref.owner
          result = resolve_reference(ref.owner.to_s, api_map)
          return result unless result.nil?
        end
        match = comments.match(/^[ \t]*\(see (.*)\)/m)
        return nil if match.nil?
        resolve_reference match[1], api_map
      end

      # @param api_map [ApiMap]

      # @return [ComplexType, nil]

      def typify_from_super api_map
        stack = api_map.get_method_stack(namespace, name, scope: scope).reject { |pin| pin.path == path }
        return nil if stack.empty?
        stack.each do |pin|
          return pin.return_type unless pin.return_type.undefined?
        end
        nil
      end

      # @param ref [String]

      # @param api_map [ApiMap]

      # @return [ComplexType]

      def resolve_reference ref, api_map
        parts = ref.split(/[\.#]/)
        if parts.first.empty? || parts.one?
          path = "#{namespace}#{ref}"
        else
          fqns = api_map.qualify(parts.first, namespace)
          return ComplexType::UNDEFINED if fqns.nil?
          path = fqns + ref[parts.first.length] + parts.last
        end
        pins = api_map.get_path_pins(path)
        pins.each do |pin|
          type = pin.typify(api_map)
          return type unless type.undefined?
        end
        nil
      end
    end
  end
end