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

Parameters:

Returns:

  • (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

Parameters:

Returns:

  • (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

Parameters:

Returns:

  • (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

Parameters:

Returns:

  • (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

Parameters:

Returns:

  • (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.

Parameters:

Returns:

  • (Boolean)


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