Module: Tapioca::RBIHelper

Constant Summary

Constants included from SorbetHelper

SorbetHelper::FEATURE_REQUIREMENTS, SorbetHelper::SORBET_BIN, SorbetHelper::SORBET_EXE_PATH_ENV_VAR, SorbetHelper::SORBET_GEM_SPEC, SorbetHelper::SORBET_PAYLOAD_URL, SorbetHelper::SPOOM_CONTEXT

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SorbetHelper

sorbet, sorbet_path, sorbet_supports?

Class Method Details

.serialize_type_variable(type, variance, fixed, upper, lower) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/tapioca/helpers/rbi_helper.rb', line 23

def serialize_type_variable(type, variance, fixed, upper, lower)
  variance = nil if variance == :invariant

  block = []
  block << "fixed: #{fixed}" if fixed
  block << "lower: #{lower}" if lower
  block << "upper: #{upper}" if upper

  parameters = []
  parameters << ":#{variance}" if variance

  serialized = type.dup
  serialized << "(#{parameters.join(", ")})" unless parameters.empty?
  serialized << " { { #{block.join(", ")} } }" unless block.empty?
  serialized
end

Instance Method Details

#as_nilable_type(type) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/tapioca/helpers/rbi_helper.rb', line 91

def as_nilable_type(type)
  if type.start_with?("T.nilable(", "::T.nilable(") || type == "T.untyped" || type == "::T.untyped"
    type
  else
    "T.nilable(#{type})"
  end
end

#create_block_param(name, type:) ⇒ Object



72
73
74
# File 'lib/tapioca/helpers/rbi_helper.rb', line 72

def create_block_param(name, type:)
  create_typed_param(RBI::BlockParam.new(name), type)
end

#create_kw_opt_param(name, type:, default:) ⇒ Object



62
63
64
# File 'lib/tapioca/helpers/rbi_helper.rb', line 62

def create_kw_opt_param(name, type:, default:)
  create_typed_param(RBI::KwOptParam.new(name, default), type)
end

#create_kw_param(name, type:) ⇒ Object



57
58
59
# File 'lib/tapioca/helpers/rbi_helper.rb', line 57

def create_kw_param(name, type:)
  create_typed_param(RBI::KwParam.new(name), type)
end

#create_kw_rest_param(name, type:) ⇒ Object



67
68
69
# File 'lib/tapioca/helpers/rbi_helper.rb', line 67

def create_kw_rest_param(name, type:)
  create_typed_param(RBI::KwRestParam.new(name), type)
end

#create_opt_param(name, type:, default:) ⇒ Object



47
48
49
# File 'lib/tapioca/helpers/rbi_helper.rb', line 47

def create_opt_param(name, type:, default:)
  create_typed_param(RBI::OptParam.new(name, default), type)
end

#create_param(name, type:) ⇒ Object



42
43
44
# File 'lib/tapioca/helpers/rbi_helper.rb', line 42

def create_param(name, type:)
  create_typed_param(RBI::ReqParam.new(name), type)
end

#create_rest_param(name, type:) ⇒ Object



52
53
54
# File 'lib/tapioca/helpers/rbi_helper.rb', line 52

def create_rest_param(name, type:)
  create_typed_param(RBI::RestParam.new(name), type)
end

#create_typed_param(param, type) ⇒ Object



77
78
79
# File 'lib/tapioca/helpers/rbi_helper.rb', line 77

def create_typed_param(param, type)
  RBI::TypedParam.new(param: param, type: sanitize_signature_types(type))
end

#sanitize_signature_types(sig_string) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/tapioca/helpers/rbi_helper.rb', line 82

def sanitize_signature_types(sig_string)
  sig_string
    .gsub(".returns(<VOID>)", ".void")
    .gsub("<VOID>", "void")
    .gsub("<NOT-TYPED>", "T.untyped")
    .gsub(".params()", "")
end

#valid_method_name?(name) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/tapioca/helpers/rbi_helper.rb', line 100

def valid_method_name?(name)
  # try to parse a method definition with this name
  iseq = RubyVM::InstructionSequence.compile("def #{name}; end", nil, nil, 0, false)
  # pull out the first operation in the instruction sequence and its first argument
  op, arg, _data = iseq.to_a.dig(-1, 0)
  # make sure that the operation is a method definition and the method that was
  # defined has the expected name, for example, for `def !foo; end` we don't get
  # a syntax error but instead get a method defined as `"foo"`
  op == :definemethod && arg == name.to_sym
rescue SyntaxError
  false
end

#valid_parameter_name?(name) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/tapioca/helpers/rbi_helper.rb', line 114

def valid_parameter_name?(name)
  sentinel_method_name = :sentinel_method_name
  # try to parse a method definition with this name as the name of a
  # keyword parameter. If we use a positional parameter, then parameter names
  # like `&` (and maybe others) will be treated like `def foo(&); end` and will
  # thus be considered valid. Using a required keyword parameter prevents that
  # confusion between Ruby syntax and parameter name.
  iseq = RubyVM::InstructionSequence.compile("def #{sentinel_method_name}(#{name}:); end", nil, nil, 0, false)
  # pull out the first operation in the instruction sequence and its first argument and data
  op, arg, data = iseq.to_a.dig(-1, 0)
  # make sure that:
  # 1. a method was defined, and
  # 2. the method has the expected method name, and
  # 3. the method has a keyword parameter with the expected name
  op == :definemethod && arg == sentinel_method_name && data.dig(11, :keyword, 0) == name.to_sym
rescue SyntaxError
  false
end