Module: Tapioca::RBIHelper

Extended by:
T::Sig, RBIHelper, SorbetHelper
Includes:
SorbetHelper
Included in:
Dsl::Compiler, Gem::Listeners::Methods, Gem::Listeners::SorbetSignatures, Gem::Pipeline, RBIHelper
Defined in:
lib/tapioca/helpers/rbi_helper.rb

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

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



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/tapioca/helpers/rbi_helper.rb', line 69

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

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

  parameters = []
  block = []

  parameters << ":#{variance}" if variance

  if sorbet_supports?(:type_variable_block_syntax)
    block = bounds
  else
    parameters.concat(bounds)
  end

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

Instance Method Details

#create_block_param(name, type:) ⇒ Object



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

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

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



32
33
34
# File 'lib/tapioca/helpers/rbi_helper.rb', line 32

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

#create_kw_param(name, type:) ⇒ Object



27
28
29
# File 'lib/tapioca/helpers/rbi_helper.rb', line 27

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

#create_kw_rest_param(name, type:) ⇒ Object



37
38
39
# File 'lib/tapioca/helpers/rbi_helper.rb', line 37

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

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



17
18
19
# File 'lib/tapioca/helpers/rbi_helper.rb', line 17

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

#create_param(name, type:) ⇒ Object



12
13
14
# File 'lib/tapioca/helpers/rbi_helper.rb', line 12

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

#create_rest_param(name, type:) ⇒ Object



22
23
24
# File 'lib/tapioca/helpers/rbi_helper.rb', line 22

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

#create_typed_param(param, type) ⇒ Object



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

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

#sanitize_signature_types(sig_string) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/tapioca/helpers/rbi_helper.rb', line 52

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)


95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/tapioca/helpers/rbi_helper.rb', line 95

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)


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/tapioca/helpers/rbi_helper.rb', line 109

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