lib/tapioca/gem/pipeline.rb in tapioca-0.8.3 vs lib/tapioca/gem/pipeline.rb in tapioca-0.9.0
- old
+ new
@@ -1,16 +1,14 @@
# typed: strict
# frozen_string_literal: true
-require "pathname"
-
module Tapioca
module Gem
class Pipeline
extend T::Sig
include Runtime::Reflection
- include SignaturesHelper
+ include RBIHelper
IGNORED_SYMBOLS = T.let(["YAML", "MiniTest", "Mutex"], T::Array[String])
sig { returns(Gemfile::GemSpec) }
attr_reader :gem
@@ -23,12 +21,12 @@
@alias_namespace = T.let(Set.new, T::Set[String])
@events = T.let([], T::Array[Gem::Event])
@payload_symbols = T.let(Static::SymbolLoader.payload_symbols, T::Set[String])
- @bootstrap_symbols = T.let(Static::SymbolLoader.gem_symbols(@gem).union(Static::SymbolLoader.engine_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)
@@ -39,10 +37,11 @@
@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::RemoveEmptyPayloadScopes.new(self)
end
sig { returns(RBI::Tree) }
def compile
@@ -58,21 +57,35 @@
sig { params(symbol: String, constant: BasicObject).void.checked(:never) }
def push_constant(symbol, constant)
@events << Gem::ConstantFound.new(symbol, constant)
end
+ sig { params(symbol: String, constant: Module).void.checked(:never) }
+ def push_foreign_constant(symbol, constant)
+ @events << Gem::ForeignConstantFound.new(symbol, constant)
+ end
+
sig { params(symbol: String, constant: Module, node: RBI::Const).void.checked(:never) }
def push_const(symbol, constant, node)
@events << Gem::ConstNodeAdded.new(symbol, constant, node)
end
- sig { params(symbol: String, constant: Module, node: RBI::Scope).void.checked(:never) }
+ sig do
+ params(symbol: String, constant: Module, node: RBI::Scope).void.checked(:never)
+ end
def push_scope(symbol, constant, node)
@events << Gem::ScopeNodeAdded.new(symbol, constant, node)
end
sig do
+ params(symbol: String, constant: Module, node: RBI::Scope).void.checked(:never)
+ end
+ def push_foreign_scope(symbol, constant, node)
+ @events << Gem::ForeignScopeNodeAdded.new(symbol, constant, node)
+ end
+
+ sig do
params(
symbol: String,
constant: Module,
node: RBI::Method,
signature: T.untyped,
@@ -112,10 +125,18 @@
name
end
private
+ sig { params(gem: Gemfile::GemSpec).returns(T::Set[String]) }
+ def load_bootstrap_symbols(gem)
+ engine_symbols = Static::SymbolLoader.engine_symbols(gem)
+ gem_symbols = Static::SymbolLoader.gem_symbols(gem)
+
+ gem_symbols.union(engine_symbols)
+ end
+
sig { returns(Gem::Event) }
def next_event
T.must(@events.shift)
end
@@ -148,26 +169,32 @@
return if name.strip.empty?
return if name.start_with?("#<")
return if name.downcase == name
return if alias_namespaced?(name)
- return if seen?(name)
- constant = event.constant
- return if T::Enum === constant # T::Enum instances are defined via `compile_enums`
+ return if T::Enum === event.constant # T::Enum instances are defined via `compile_enums`
- mark_seen(name)
- compile_constant(name, constant)
+ if event.is_a?(Gem::ForeignConstantFound)
+ compile_foreign_constant(name, event.constant)
+ else
+ compile_constant(name, event.constant)
+ end
end
sig { params(event: Gem::NodeAdded).void }
def on_node(event)
@node_listeners.each { |listener| listener.dispatch(event) }
end
# Compile
+ sig { params(symbol: String, constant: Module).void }
+ def compile_foreign_constant(symbol, constant)
+ compile_module(symbol, constant, foreign_constant: true)
+ end
+
sig { params(symbol: String, constant: BasicObject).void.checked(:never) }
def compile_constant(symbol, constant)
case constant
when Module
if name_of(constant) != symbol
@@ -180,10 +207,14 @@
end
end
sig { params(name: String, constant: Module).void }
def compile_alias(name, constant)
+ return if seen?(name)
+
+ mark_seen(name)
+
return if symbol_in_payload?(name)
target = name_of(constant)
# If target has no name, let's make it an anonymous class or module with `Class.new` or `Module.new`
target = "#{constant.class}.new" unless target
@@ -197,10 +228,14 @@
@root << node
end
sig { params(name: String, value: BasicObject).void.checked(:never) }
def compile_object(name, value)
+ return if seen?(name)
+
+ mark_seen(name)
+
return if symbol_in_payload?(name)
klass = class_of(value)
klass_name = if klass == ObjectSpace::WeakMap
@@ -226,23 +261,31 @@
node = RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})")
push_const(name, klass, node)
@root << node
end
- sig { params(name: String, constant: Module).void }
- def compile_module(name, constant)
- return unless defined_in_gem?(constant, strict: false)
+ sig { params(name: String, constant: Module, foreign_constant: T::Boolean).void }
+ def compile_module(name, constant, foreign_constant: false)
+ return unless defined_in_gem?(constant, strict: false) || foreign_constant
return if Tapioca::TypeVariableModule === constant
+ return if seen?(name)
+ mark_seen(name)
+
scope =
if constant.is_a?(Class)
superclass = compile_superclass(constant)
RBI::Class.new(name, superclass_name: superclass)
else
RBI::Module.new(name)
end
- push_scope(name, constant, scope)
+ if foreign_constant
+ push_foreign_scope(name, constant, scope)
+ else
+ push_scope(name, constant, scope)
+ end
+
@root << scope
end
sig { params(constant: Class).returns(T.nilable(String)) }
def compile_superclass(constant)