lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.4.0 vs lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.4.1
- old
+ new
@@ -174,17 +174,47 @@
methods = compile_methods(name, constant)
return if symbol_ignored?(name) && methods.nil?
[
+ compile_module_helpers(constant),
compile_mixins(constant),
compile_mixes_in_class_methods(constant),
+ compile_props(constant),
methods,
].select { |b| b != "" }.join("\n\n")
end
end
+ sig { params(constant: Module).returns(String) }
+ def compile_module_helpers(constant)
+ abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type)
+
+ if abstract_type
+ indented("#{abstract_type}!")
+ elsif T::Private::Final.final_module?(constant)
+ indented("final!")
+ elsif T::Private::Sealed.sealed_module?(constant)
+ indented("sealed!")
+ else
+ ""
+ end
+ end
+
+ sig { params(constant: Module).returns(String) }
+ def compile_props(constant)
+ return "" unless T::Props::ClassMethods === constant
+
+ constant.props.map do |name, prop|
+ method = "prop"
+ method = "const" if prop.fetch(:immutable, false)
+ type = prop.fetch(:type_object, "T.untyped")
+
+ indented("#{method} :#{name}, #{type}")
+ end.join("\n")
+ end
+
sig { params(name: String, constant: Module).returns(T.nilable(String)) }
def compile_subconstants(name, constant)
output = constants_of(constant).sort.uniq.map do |constant_name|
symbol = (name == "Object" ? "" : name) + "::#{constant_name}"
subconstant = resolve_constant(symbol)
@@ -377,11 +407,11 @@
constant,
initialize_method_for(constant)
)
instance_methods = compile_directly_owned_methods(name, constant)
- singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant), [:public])
+ singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant))
return if symbol_ignored?(name) && instance_methods.empty? && singleton_methods.empty?
[
initialize_method || "",
@@ -390,28 +420,48 @@
].select { |b| b.strip != "" }.join("\n\n")
end
sig { params(module_name: String, mod: Module, for_visibility: T::Array[Symbol]).returns(String) }
def compile_directly_owned_methods(module_name, mod, for_visibility = [:public, :protected, :private])
- method_names_by_visibility(mod)
- .delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
- .flat_map do |visibility, method_list|
- compiled = method_list.sort!.map do |name|
- next if name == :initialize
- compile_method(module_name, mod, mod.instance_method(name))
- end
- compiled.compact!
+ indent_step = 0
+ preamble = nil
+ postamble = nil
- unless compiled.empty? || visibility == :public
- # add visibility badge
- compiled.unshift('', indented(visibility.to_s), '')
+ if mod.singleton_class?
+ indent_step = 1
+ preamble = indented("class << self")
+ postamble = indented("end")
+ end
+
+ methods = with_indentation(indent_step) do
+ method_names_by_visibility(mod)
+ .delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
+ .flat_map do |visibility, method_list|
+ compiled = method_list.sort!.map do |name|
+ next if name == :initialize
+ compile_method(module_name, mod, mod.instance_method(name))
+ end
+ compiled.compact!
+
+ unless compiled.empty? || visibility == :public
+ # add visibility badge
+ compiled.unshift('', indented(visibility.to_s), '')
+ end
+
+ compiled
end
+ .compact
+ .join("\n")
+ end
- compiled
- end
- .compact
- .join("\n")
+ return "" if methods.strip == ""
+
+ [
+ preamble,
+ methods,
+ postamble,
+ ].compact.join("\n")
end
sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
def method_names_by_visibility(mod)
{
@@ -419,10 +469,20 @@
protected: Module.instance_method(:protected_instance_methods).bind(mod).call,
private: Module.instance_method(:private_instance_methods).bind(mod).call,
}
end
+ sig { params(constant: Module, method_name: String).returns(T::Boolean) }
+ def struct_method?(constant, method_name)
+ return false unless T::Props::ClassMethods === constant
+
+ constant
+ .props
+ .keys
+ .include?(method_name.gsub(/=$/, '').to_sym)
+ end
+
sig do
params(
symbol_name: String,
constant: Module,
method: T.nilable(UnboundMethod)
@@ -431,12 +491,17 @@
def compile_method(symbol_name, constant, method)
return unless method
return unless method.owner == constant
return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
+ signature = signature_of(method)
+ method = signature.method if signature
+
method_name = method.name.to_s
return unless valid_method_name?(method_name)
+ return if struct_method?(constant, method_name)
+ return if method_name.start_with?("__t_props_generated_")
params = T.let(method.parameters, T::Array[T::Array[Symbol]])
parameters = params.map do |(type, name)|
name ||= :_
@@ -445,30 +510,74 @@
case type
when :req
name
when :opt
- "#{name} = _"
+ "#{name} = T.unsafe(nil)"
when :rest
"*#{name}"
when :keyreq
"#{name}:"
when :key
- "#{name}: _"
+ "#{name}: T.unsafe(nil)"
when :keyrest
"**#{name}"
when :block
"&#{name}"
end
end.join(', ')
- method_name = "#{'self.' if constant.singleton_class?}#{method_name}"
parameters = "(#{parameters})" if parameters != ""
- indented("def #{method_name}#{parameters}; end")
+ signature_str = indented(compile_signature(signature)) if signature
+ [
+ signature_str,
+ indented("def #{method_name}#{parameters}; end"),
+ ].compact.join("\n")
end
+ TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
+
+ sig { params(signature: T.untyped).returns(String) }
+ def compile_signature(signature)
+ params = signature.arg_types
+ params += signature.kwarg_types.to_a
+ params << [signature.rest_name, signature.rest_type] if signature.has_rest
+ params << [signature.block_name, signature.block_type] if signature.block_name
+
+ params = params.compact.map { |name, type| "#{name}: #{type}" }.join(", ")
+ returns = signature.return_type.to_s
+
+ type_parameters = (params + returns).scan(TYPE_PARAMETER_MATCHER).flatten.uniq.map { |p| ":#{p}" }.join(", ")
+ type_parameters = ".type_parameters(#{type_parameters})" unless type_parameters.empty?
+
+ mode = case signature.mode
+ when "abstract"
+ ".abstract"
+ when "override"
+ ".override"
+ when "overridable_override"
+ ".overridable.override"
+ when "overridable"
+ ".overridable"
+ else
+ ""
+ end
+
+ signature_body = +""
+ signature_body << mode
+ signature_body << type_parameters
+ signature_body << ".params(#{params})" unless params.empty?
+ signature_body << ".returns(#{returns})"
+ signature_body = signature_body
+ .gsub(".returns(<VOID>)", ".void")
+ .gsub("<NOT-TYPED>", "T.untyped")
+ .gsub(TYPE_PARAMETER_MATCHER, "T.type_parameter(:\\1)")[1..-1]
+
+ "sig { #{signature_body} }"
+ end
+
sig { params(symbol_name: String).returns(T::Boolean) }
def symbol_ignored?(symbol_name)
SymbolLoader.ignore_symbol?(symbol_name)
end
@@ -481,20 +590,21 @@
end
sig do
type_parameters(:U)
.params(
+ step: Integer,
_blk: T.proc
.returns(T.type_parameter(:U))
)
.returns(T.type_parameter(:U))
end
- def with_indentation(&_blk)
- @indent += 2
+ def with_indentation(step = 1, &_blk)
+ @indent += 2 * step
yield
ensure
- @indent -= 2
+ @indent -= 2 * step
end
sig { params(str: String).returns(String) }
def indented(str)
" " * @indent + str
@@ -625,11 +735,11 @@
target = Kernel.instance_method(:send).bind(constant).call(:target)
rescue NoMethodError
return nil
end
- name_of(target)
+ raw_name_of(target)
end
sig { params(constant: Module).returns(T.nilable(String)) }
def qualified_name_of(constant)
name = name_of(constant)
@@ -643,9 +753,16 @@
end
sig { params(constant: Class).returns(T.nilable(Class)) }
def superclass_of(constant)
Class.instance_method(:superclass).bind(constant).call
+ end
+
+ sig { params(method: T.any(UnboundMethod, Method)).returns(T.untyped) }
+ def signature_of(method)
+ T::Private::Methods.signature_for_method(method)
+ rescue LoadError, StandardError
+ nil
end
sig { params(constant: Module, other: BasicObject).returns(T::Boolean).checked(:never) }
def are_equal?(constant, other)
BasicObject.instance_method(:equal?).bind(constant).call(other)