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)