Module: Solargraph::TypeChecker::Checks

Included in:
Solargraph::TypeChecker
Defined in:
lib/solargraph/type_checker/checks.rb

Overview

Helper methods for performing type checks

Class Method Summary collapse

Class Method Details

.all_types_match?(api_map, inferred, expected) ⇒ Boolean



75
76
77
78
79
80
81
82
83
84
# File 'lib/solargraph/type_checker/checks.rb', line 75

def all_types_match? api_map, inferred, expected
  expected = expected.downcast_to_literal_if_possible
  inferred = inferred.downcast_to_literal_if_possible
  return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
  inferred.each do |inf|
    next if inf.duck_type?
    return false unless expected.any? { |exp| exp == inf || either_way?(api_map, inf, exp) }
  end
  true
end

.any_types_match?(api_map, expected, inferred) ⇒ Boolean



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/solargraph/type_checker/checks.rb', line 52

def any_types_match? api_map, expected, inferred
  expected = expected.downcast_to_literal_if_possible
  inferred = inferred.downcast_to_literal_if_possible
  return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
  # walk through the union expected type and see if any members

  # of the union match the inferred type

  expected.each do |exp|
    next if exp.duck_type?
    # @todo: there should be a level of typechecking where all

    #   unique types in the inferred must match one of the

    #   expected unique types

    inferred.each do |inf|
      # return true if exp == inf || api_map.super_and_sub?(fuzz(inf), fuzz(exp))

      return true if exp == inf || either_way?(api_map, inf, exp)
    end
  end
  false
end

.duck_types_match?(api_map, expected, inferred) ⇒ Boolean

Raises:

  • (ArgumentError)


90
91
92
93
94
95
96
97
98
# File 'lib/solargraph/type_checker/checks.rb', line 90

def duck_types_match? api_map, expected, inferred
  raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
  expected.each do |exp|
    next unless exp.duck_type?
    quack = exp.to_s[1..-1]
    return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty?
  end
  true
end

.either_way?(api_map, cls1, cls2) ⇒ Boolean



114
115
116
117
118
119
120
121
# File 'lib/solargraph/type_checker/checks.rb', line 114

def either_way?(api_map, cls1, cls2)
  # @todo there should be a level of typechecking which uses the

  #   full tag with parameters to determine compatibility

  f1 = cls1.name
  f2 = cls2.name
  api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)
  # api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)

end

.fuzz(type) ⇒ String



102
103
104
105
106
107
108
# File 'lib/solargraph/type_checker/checks.rb', line 102

def fuzz type
  if type.parameters?
    type.name
  else
    type.tag
  end
end

.types_match?(api_map, expected, inferred) ⇒ Boolean

Compare an expected type with an inferred type. Common usage is to check if the type declared in a method’s @return tag matches the type inferred from static analysis of the code.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/solargraph/type_checker/checks.rb', line 18

def types_match? api_map, expected, inferred
  return true if expected.to_s == inferred.to_s
  matches = []
  expected.each do |exp|
    found = false
    inferred.each do |inf|
      # if api_map.super_and_sub?(fuzz(inf), fuzz(exp))

      if either_way?(api_map, inf, exp)
        found = true
        matches.push inf
        break
      end
    end
    return false unless found
  end
  inferred.each do |inf|
    next if matches.include?(inf)
    found = false
    expected.each do |exp|
      # if api_map.super_and_sub?(fuzz(inf), fuzz(exp))

      if either_way?(api_map, inf, exp)
        found = true
        break
      end
    end
    return false unless found
  end
  true
end