Class: Tapioca::Gem::Pipeline

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

Defined Under Namespace

Classes: MethodDefinitionLookupResult, MethodInGemWithLocation, MethodInGemWithoutLocation, MethodNotInGem, MethodUnknown

Constant Summary collapse

IGNORED_SYMBOLS =

: Array

["YAML", "MiniTest", "Mutex"]

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::SignatureBlockError, 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, #const_source_location, #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

: (Gemfile::GemSpec gem, error_handler: ^(String error) -> void, ?include_doc: bool, ?include_loc: bool) -> void



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
47
48
49
50
51
52
53
54
# File 'lib/tapioca/gem/pipeline.rb', line 20

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

  @events = [] #: Array[Gem::Event]

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

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

  @node_listeners = [] #: 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)

: ^(String error) -> void



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

def error_handler
  @error_handler
end

#gemObject (readonly)

: Gemfile::GemSpec



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

def gem
  @gem
end

Instance Method Details

#compileObject

: -> RBI::Tree



57
58
59
60
# File 'lib/tapioca/gem/pipeline.rb', line 57

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

#constant_in_gem?(name) ⇒ Boolean

: ((String | Symbol) name) -> bool

Returns:

  • (Boolean)


111
112
113
114
115
116
117
118
# File 'lib/tapioca/gem/pipeline.rb', line 111

def constant_in_gem?(name)
  loc = const_source_location(name)

  # If the source location of the constant isn't available or is "(eval)", all bets are off.
  return true if loc.nil? || loc.file.nil? || loc.file == "(eval)"

  gem.contains_path?(loc.file)
end

#method_definition_in_gem(method_name, owner) ⇒ Object

: (Symbol method_name, Module owner) -> MethodDefinitionLookupResult



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/tapioca/gem/pipeline.rb', line 149

def method_definition_in_gem(method_name, owner)
  definitions = Tapioca::Runtime::Trackers::MethodDefinition.method_definitions_for(method_name, owner)

  # If the source location of the method isn't available, signal that by returning nil.
  return MethodUnknown.new if definitions.empty?

  # Look up the first entry that matches a file in the gem.
  found = definitions.find { |loc| @gem.contains_path?(loc.file) }

  unless found
    # If the source location of the method is "(eval)", err on the side of caution and include the method.
    found = definitions.find { |loc| loc.file == "(eval)" }
    # However, we can just return true to signal that the method should be included.
    # We can't provide a source location for it, but we want it to be included in the gem RBI.
    return MethodInGemWithoutLocation.new if found
  end

  # If we searched but couldn't find a source location in the gem, return false to signal that.
  return MethodNotInGem.new unless found

  MethodInGemWithLocation.new(found)
end

#name_of(constant) ⇒ Object

: (Module constant) -> String?



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/tapioca/gem/pipeline.rb', line 175

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

: (String symbol, Module constant, RBI::Const node) -> void



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

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

#push_constant(symbol, constant) ⇒ Object

: (String symbol, BasicObject constant) -> void



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

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

#push_foreign_constant(symbol, constant) ⇒ Object

: (String symbol, Module constant) -> void



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

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

#push_foreign_scope(symbol, constant, node) ⇒ Object

: (String symbol, Module constant, RBI::Scope node) -> void



91
92
93
# File 'lib/tapioca/gem/pipeline.rb', line 91

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

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

: (String symbol, Module constant, UnboundMethod method, RBI::Method node, untyped signature, Array[[Symbol, String]] parameters) -> void



96
97
98
# File 'lib/tapioca/gem/pipeline.rb', line 96

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

: (String symbol, Module constant, RBI::Scope node) -> void



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

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

#push_symbol(symbol) ⇒ Object

: (String symbol) -> void



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

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

#symbol_in_payload?(symbol_name) ⇒ Boolean

: (String symbol_name) -> bool

Returns:

  • (Boolean)


103
104
105
106
107
108
# File 'lib/tapioca/gem/pipeline.rb', line 103

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