Class: Tapioca::Gem::Pipeline

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Includes:
RBIHelper, Runtime::Reflection
Defined in:
lib/tapioca/gem/pipeline.rb

Constant Summary collapse

IGNORED_SYMBOLS =
T.let(["YAML", "MiniTest", "Mutex"], T::Array[String])
EVAL_SOURCE_FILE_PATTERN =

this looks something like: “(eval at /path/to/file.rb:123)” and we are just interested in the “/path/to/file.rb” part

T.let(/\(eval at (.+):\d+\)/, Regexp)

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

Constants included from Runtime::Reflection

Runtime::Reflection::ANCESTORS_METHOD, Runtime::Reflection::CLASS_METHOD, Runtime::Reflection::CONSTANTS_METHOD, Runtime::Reflection::EQUAL_METHOD, Runtime::Reflection::METHOD_METHOD, Runtime::Reflection::NAME_METHOD, Runtime::Reflection::OBJECT_ID_METHOD, Runtime::Reflection::PRIVATE_INSTANCE_METHODS_METHOD, Runtime::Reflection::PROTECTED_INSTANCE_METHODS_METHOD, Runtime::Reflection::PUBLIC_INSTANCE_METHODS_METHOD, Runtime::Reflection::REQUIRED_FROM_LABELS, Runtime::Reflection::SINGLETON_CLASS_METHOD, Runtime::Reflection::SUPERCLASS_METHOD, Runtime::Reflection::UNDEFINED_CONSTANT

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RBIHelper

#as_nilable_type, #as_non_nilable_type, #create_block_param, #create_kw_opt_param, #create_kw_param, #create_kw_rest_param, #create_opt_param, #create_param, #create_rest_param, #create_typed_param, #sanitize_signature_types, serialize_type_variable, #valid_method_name?, #valid_parameter_name?

Methods included from SorbetHelper

#sorbet, #sorbet_path, #sorbet_supports?

Methods included from Runtime::Reflection

#abstract_type_of, #ancestors_of, #are_equal?, #class_of, #constant_defined?, #constantize, #constants_of, #descendants_of, #file_candidates_for, #final_module?, #inherited_ancestors_of, #method_of, #name_of_type, #object_id_of, #private_instance_methods_of, #protected_instance_methods_of, #public_instance_methods_of, #qualified_name_of, #resolve_loc, #sealed_module?, #signature_of, #signature_of!, #singleton_class_of, #superclass_of

Methods included from Runtime::AttachedClassOf

#attached_class_of

Constructor Details

#initialize(gem, error_handler:, include_doc: false, include_loc: false) ⇒ Pipeline

Returns a new instance of Pipeline.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/tapioca/gem/pipeline.rb', line 27

def initialize(
  gem,
  error_handler:,
  include_doc: false,
  include_loc: false
)
  @root = T.let(RBI::Tree.new, RBI::Tree)
  @gem = gem
  @seen = T.let(Set.new, T::Set[String])
  @alias_namespace = T.let(Set.new, T::Set[String])
  @error_handler = error_handler

  @events = T.let([], T::Array[Gem::Event])

  @payload_symbols = T.let(Static::SymbolLoader.payload_symbols, T::Set[String])
  @bootstrap_symbols = T.let(load_bootstrap_symbols(@gem), T::Set[String])

  @bootstrap_symbols.each { |symbol| push_symbol(symbol) }

  @node_listeners = T.let([], T::Array[Gem::Listeners::Base])
  @node_listeners << Gem::Listeners::SorbetTypeVariables.new(self)
  @node_listeners << Gem::Listeners::Mixins.new(self)
  @node_listeners << Gem::Listeners::DynamicMixins.new(self)
  @node_listeners << Gem::Listeners::Methods.new(self)
  @node_listeners << Gem::Listeners::SorbetHelpers.new(self)
  @node_listeners << Gem::Listeners::SorbetEnums.new(self)
  @node_listeners << Gem::Listeners::SorbetProps.new(self)
  @node_listeners << Gem::Listeners::SorbetRequiredAncestors.new(self)
  @node_listeners << Gem::Listeners::SorbetSignatures.new(self)
  @node_listeners << Gem::Listeners::Subconstants.new(self)
  @node_listeners << Gem::Listeners::YardDoc.new(self) if include_doc
  @node_listeners << Gem::Listeners::ForeignConstants.new(self)
  @node_listeners << Gem::Listeners::SourceLocation.new(self) if include_loc
  @node_listeners << Gem::Listeners::RemoveEmptyPayloadScopes.new(self)
end

Instance Attribute Details

#error_handlerObject (readonly)

Returns the value of attribute error_handler.



17
18
19
# File 'lib/tapioca/gem/pipeline.rb', line 17

def error_handler
  @error_handler
end

#gemObject (readonly)

Returns the value of attribute gem.



14
15
16
# File 'lib/tapioca/gem/pipeline.rb', line 14

def gem
  @gem
end

Instance Method Details

#compileObject



64
65
66
67
# File 'lib/tapioca/gem/pipeline.rb', line 64

def compile
  dispatch(next_event) until @events.empty?
  @root
end

#constant_in_gem?(name) ⇒ Boolean

Returns:

  • (Boolean)


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/tapioca/gem/pipeline.rb', line 135

def constant_in_gem?(name)
  return true unless Object.respond_to?(:const_source_location)

  source_file, _ = Object.const_source_location(name)
  return true unless source_file
  # If the source location of the constant is "(eval)", all bets are off.
  return true if source_file == "(eval)"

  # Ruby 3.3 adds automatic definition of source location for evals if
  # `file` and `line` arguments are not provided. This results in the source
  # file being something like `(eval at /path/to/file.rb:123)`. We try to parse
  # this string to get the actual source file.
  source_file = source_file.sub(EVAL_SOURCE_FILE_PATTERN, "\\1")

  gem.contains_path?(source_file)
end

#method_in_gem?(method) ⇒ Boolean

Returns:

  • (Boolean)


153
154
155
156
157
158
# File 'lib/tapioca/gem/pipeline.rb', line 153

def method_in_gem?(method)
  source_location = method.source_location&.first
  return false if source_location.nil?

  @gem.contains_path?(source_location)
end

#name_of(constant) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/tapioca/gem/pipeline.rb', line 163

def name_of(constant)
  name = name_of_proxy_target(constant, super(class_of(constant)))
  return name if name

  name = super(constant)
  return if name.nil?
  return unless are_equal?(constant, constantize(name, inherit: true))

  name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
  name
end

#push_const(symbol, constant, node) ⇒ Object



87
88
89
# File 'lib/tapioca/gem/pipeline.rb', line 87

def push_const(symbol, constant, node)
  @events << Gem::ConstNodeAdded.new(symbol, constant, node)
end

#push_constant(symbol, constant) ⇒ Object



77
78
79
# File 'lib/tapioca/gem/pipeline.rb', line 77

def push_constant(symbol, constant)
  @events << Gem::ConstantFound.new(symbol, constant)
end

#push_foreign_constant(symbol, constant) ⇒ Object



82
83
84
# File 'lib/tapioca/gem/pipeline.rb', line 82

def push_foreign_constant(symbol, constant)
  @events << Gem::ForeignConstantFound.new(symbol, constant)
end

#push_foreign_scope(symbol, constant, node) ⇒ Object



101
102
103
# File 'lib/tapioca/gem/pipeline.rb', line 101

def push_foreign_scope(symbol, constant, node)
  @events << Gem::ForeignScopeNodeAdded.new(symbol, constant, node)
end

#push_method(symbol, constant, method, node, signature, parameters) ⇒ Object

rubocop:disable Metrics/ParameterLists



115
116
117
# File 'lib/tapioca/gem/pipeline.rb', line 115

def push_method(symbol, constant, method, node, signature, parameters) # rubocop:disable Metrics/ParameterLists
  @events << Gem::MethodNodeAdded.new(symbol, constant, method, node, signature, parameters)
end

#push_scope(symbol, constant, node) ⇒ Object



94
95
96
# File 'lib/tapioca/gem/pipeline.rb', line 94

def push_scope(symbol, constant, node)
  @events << Gem::ScopeNodeAdded.new(symbol, constant, node)
end

#push_symbol(symbol) ⇒ Object



72
73
74
# File 'lib/tapioca/gem/pipeline.rb', line 72

def push_symbol(symbol)
  @events << Gem::SymbolFound.new(symbol)
end

#symbol_in_payload?(symbol_name) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
# File 'lib/tapioca/gem/pipeline.rb', line 122

def symbol_in_payload?(symbol_name)
  symbol_name = symbol_name[2..-1] if symbol_name.start_with?("::")
  return false unless symbol_name

  @payload_symbols.include?(symbol_name)
end