lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.4.21 vs lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.4.22

- old
+ new

@@ -21,28 +21,38 @@ def initialize(gem, indent = 0) @gem = gem @indent = indent @seen = Set.new @alias_namespace ||= Set.new + @symbol_queue = T.let(symbols.sort.dup, T::Array[String]) end sig { returns(String) } def generate - symbols - .sort - .map { |symbol| generate_from_symbol(symbol) } - .compact - .join("\n\n") - .concat("\n") + rbi = RBI::Tree.new + + generate_from_symbol(rbi, T.must(@symbol_queue.shift)) until @symbol_queue.empty? + + rbi.nest_singleton_methods! + rbi.nest_non_public_methods! + rbi.group_nodes! + rbi.sort_nodes! + rbi.string end private + def add_to_symbol_queue(name) + @symbol_queue << name unless symbols.include?(name) || symbol_ignored?(name) + end + sig { returns(T::Set[String]) } def symbols - symbols = Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(gem.files) - symbols.union(engine_symbols(symbols)) + @symbols ||= begin + symbols = Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(gem.files) + symbols.union(engine_symbols(symbols)) + end end sig { params(symbols: T::Set[String]).returns(T::Set[String]) } def engine_symbols(symbols) return Set.new unless Object.const_defined?("Rails::Engine") @@ -63,17 +73,17 @@ Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(paths) rescue Set.new end - sig { params(symbol: String).returns(T.nilable(String)) } - def generate_from_symbol(symbol) + sig { params(tree: RBI::Tree, symbol: String).void } + def generate_from_symbol(tree, symbol) constant = resolve_constant(symbol) return unless constant - compile(symbol, constant) + compile(tree, symbol, constant) end sig do params( symbol: String, @@ -85,266 +95,211 @@ namespace.const_get(symbol, inherit) rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError nil end - sig do - params(name: T.nilable(String), constant: BasicObject) - .returns(T.nilable(String)) - .checked(:never) - end - def compile(name, constant) + sig { params(tree: RBI::Tree, name: T.nilable(String), constant: BasicObject).void.checked(:never) } + def compile(tree, name, constant) return unless constant return unless name return if name.strip.empty? return if name.start_with?('#<') return if name.downcase == name return if alias_namespaced?(name) return if seen?(name) - return unless parent_declares_constant?(name) return if T::Enum === constant # T::Enum instances are defined via `compile_enums` mark_seen(name) - compile_constant(name, constant) + compile_constant(tree, name, constant) end - sig do - params(name: String, constant: BasicObject) - .returns(T.nilable(String)) - .checked(:never) - end - def compile_constant(name, constant) + sig { params(tree: RBI::Tree, name: String, constant: BasicObject).void.checked(:never) } + def compile_constant(tree, name, constant) case constant when Module if name_of(constant) != name - compile_alias(name, constant) + compile_alias(tree, name, constant) else - compile_module(name, constant) + compile_module(tree, name, constant) end else - compile_object(name, constant) + compile_object(tree, name, constant) end end - sig { params(name: String, constant: Module).returns(T.nilable(String)) } - def compile_alias(name, constant) + sig { params(tree: RBI::Tree, name: String, constant: Module).void } + def compile_alias(tree, name, constant) return if symbol_ignored?(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 add_to_alias_namespace(name) return if IGNORED_SYMBOLS.include?(name) - indented("#{name} = #{target}") + tree << RBI::Const.new(name, target) end - sig do - params(name: String, value: BasicObject) - .returns(T.nilable(String)) - .checked(:never) - end - def compile_object(name, value) + sig { params(tree: RBI::Tree, name: String, value: BasicObject).void.checked(:never) } + def compile_object(tree, name, value) return if symbol_ignored?(name) klass = class_of(value) klass_name = name_of(klass) + if klass_name == "T::Private::Types::TypeAlias" + tree << RBI::Const.new(name, "T.type_alias { #{T.unsafe(value).aliased_type} }") + return + end + return if klass_name&.start_with?("T::Types::", "T::Private::") - type_name = public_module?(klass) && klass_name || "T.untyped" - indented("#{name} = T.let(T.unsafe(nil), #{type_name})") + type_name = klass_name || "T.untyped" + # TODO: Do this in a more generic and clean way. + type_name = "#{type_name}[T.untyped]" if type_name == "ObjectSpace::WeakMap" + + tree << RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})") end - sig { params(name: String, constant: Module).returns(T.nilable(String)) } - def compile_module(name, constant) - return unless public_module?(constant) + sig { params(tree: RBI::Tree, name: String, constant: Module).void } + def compile_module(tree, name, constant) return unless defined_in_gem?(constant, strict: false) - header = + scope = if constant.is_a?(Class) - indented("class #{name}#{compile_superclass(constant)}") + superclass = compile_superclass(constant) + RBI::Class.new(name, superclass_name: superclass) else - indented("module #{name}") + RBI::Module.new(name) end - body = compile_body(name, constant) + compile_body(scope, name, constant) - return if symbol_ignored?(name) && body.nil? + return if symbol_ignored?(name) && scope.empty? - [ - header, - body, - indented("end"), - compile_subconstants(name, constant), - ].select { |b| !b.nil? && b.strip != "" }.join("\n") + tree << scope + compile_subconstants(tree, name, constant) end - sig { params(name: String, constant: Module).returns(T.nilable(String)) } - def compile_body(name, constant) - with_indentation do - methods = compile_methods(name, constant) - - return if symbol_ignored?(name) && methods.nil? - - [ - compile_module_helpers(constant), - compile_type_variables(constant), - compile_mixins(constant), - compile_mixes_in_class_methods(constant), - compile_props(constant), - compile_enums(constant), - methods, - ].select { |b| b && !b.empty? }.join("\n\n") - end + sig { params(tree: RBI::Tree, name: String, constant: Module).void } + def compile_body(tree, name, constant) + compile_methods(tree, name, constant) + compile_module_helpers(tree, constant) + compile_type_variables(tree, constant) + compile_mixins(tree, constant) + compile_mixes_in_class_methods(tree, constant) + compile_props(tree, constant) + compile_enums(tree, constant) end - sig { params(constant: Module).returns(T.nilable(String)) } - def compile_module_helpers(constant) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_module_helpers(tree, constant) abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type) - helpers = [] - helpers << indented("#{abstract_type}!") if abstract_type - helpers << indented("final!") if T::Private::Final.final_module?(constant) - helpers << indented("sealed!") if T::Private::Sealed.sealed_module?(constant) - - return if helpers.empty? - - helpers.join("\n") + tree << RBI::Helper.new(abstract_type.to_s) if abstract_type + tree << RBI::Helper.new("final") if T::Private::Final.final_module?(constant) + tree << RBI::Helper.new("sealed") if T::Private::Sealed.sealed_module?(constant) end - sig { params(constant: Module).returns(T.nilable(String)) } - def compile_props(constant) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_props(tree, 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").to_s.gsub(".returns(<VOID>)", ".void") - if prop.key?(:default) - indented("#{method} :#{name}, #{type}, default: T.unsafe(nil)") + default = prop.key?(:default) ? "T.unsafe(nil)" : nil + tree << if prop.fetch(:immutable, false) + RBI::TStructConst.new(name.to_s, type, default: default) else - indented("#{method} :#{name}, #{type}") + RBI::TStructProp.new(name.to_s, type, default: default) end - end.join("\n") + end end - sig { params(constant: Module).returns(T.nilable(String)) } - def compile_enums(constant) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_enums(tree, constant) return unless T::Enum > constant enums = T.unsafe(constant).values.map do |enum_type| enum_type.instance_variable_get(:@const_name).to_s end - content = [ - indented('enums do'), - *enums.map { |e| indented(" #{e} = new") }.join("\n"), - indented('end'), - ] - - content.join("\n") + tree << RBI::TEnumBlock.new(enums) 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| + sig { params(tree: RBI::Tree, name: String, constant: Module).void } + def compile_subconstants(tree, name, constant) + constants_of(constant).sort.uniq.map do |constant_name| symbol = (name == "Object" ? "" : name) + "::#{constant_name}" subconstant = resolve_constant(symbol) # Don't compile modules of Object because Object::Foo == Foo # Don't compile modules of BasicObject because BasicObject::BasicObject == BasicObject next if (Object == constant || BasicObject == constant) && Module === subconstant next unless subconstant - compile(symbol, subconstant) - end.compact - - return if output.empty? - - "\n" + output.join("\n\n") + compile(tree, symbol, subconstant) + end end - sig { params(constant: Module).returns(T.nilable(String)) } - def compile_type_variables(constant) - type_variables = compile_type_variable_declarations(constant) - singleton_class_type_variables = compile_type_variable_declarations(singleton_class_of(constant)) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_type_variables(tree, constant) + compile_type_variable_declarations(tree, constant) - return if !type_variables && !singleton_class_type_variables - - type_variables += "\n" if type_variables - singleton_class_type_variables += "\n" if singleton_class_type_variables - - [ - type_variables, - singleton_class_type_variables, - ].compact.join("\n").rstrip + sclass = RBI::SingletonClass.new + compile_type_variable_declarations(sclass, singleton_class_of(constant)) + tree << sclass if sclass.nodes.length > 1 end - sig { params(constant: Module).returns(T.nilable(String)) } - def compile_type_variable_declarations(constant) - with_indentation_for_constant(constant) do - # Try to find the type variables defined on this constant, bail if we can't - type_variables = GenericTypeRegistry.lookup_type_variables(constant) - return unless type_variables + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_type_variable_declarations(tree, constant) + # Try to find the type variables defined on this constant, bail if we can't + type_variables = GenericTypeRegistry.lookup_type_variables(constant) + return unless type_variables - # Create a map of subconstants (via their object ids) to their names. - # We need this later when we want to lookup the name of the registered type - # variable via the value of the type variable constant. - subconstant_to_name_lookup = constants_of(constant).map do |constant_name| - [ - object_id_of(resolve_constant(constant_name.to_s, namespace: constant)), - constant_name, - ] - end.to_h + # Create a map of subconstants (via their object ids) to their names. + # We need this later when we want to lookup the name of the registered type + # variable via the value of the type variable constant. + subconstant_to_name_lookup = constants_of(constant).map do |constant_name| + [ + object_id_of(resolve_constant(constant_name.to_s, namespace: constant)), + constant_name, + ] + end.to_h - # Map each type variable to its string representation. - # - # Each entry of `type_variables` maps an object_id to a String, - # and the order they are inserted into the hash is the order they should be - # defined in the source code. - # - # By looping over these entries and then getting the actual constant name - # from the `subconstant_to_name_lookup` we defined above, gives us all the - # information we need to serialize type variable definitions. - type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable| - constant_name = subconstant_to_name_lookup[type_variable_id] - # Here, we know that constant_value will be an instance of - # T::Types::CustomTypeVariable, which knows how to serialize - # itself to a type_member/type_template - indented("#{constant_name} = #{serialized_type_variable}") - end.compact + # Map each type variable to its string representation. + # + # Each entry of `type_variables` maps an object_id to a String, + # and the order they are inserted into the hash is the order they should be + # defined in the source code. + # + # By looping over these entries and then getting the actual constant name + # from the `subconstant_to_name_lookup` we defined above, gives us all the + # information we need to serialize type variable definitions. + type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable| + constant_name = subconstant_to_name_lookup[type_variable_id] + # Here, we know that constant_value will be an instance of + # T::Types::CustomTypeVariable, which knows how to serialize + # itself to a type_member/type_template + tree << RBI::TypeMember.new(constant_name.to_s, serialized_type_variable) + end - return if type_variable_declarations.empty? + return if type_variable_declarations.empty? - [ - indented("extend T::Generic"), - "", - *type_variable_declarations, - ].compact.join("\n") - end + tree << RBI::Extend.new("T::Generic") end - sig { params(constant: Class).returns(String) } + sig { params(constant: Class).returns(T.nilable(String)) } def compile_superclass(constant) superclass = T.let(nil, T.nilable(Class)) # rubocop:disable Lint/UselessAssignment while (superclass = superclass_of(constant)) constant_name = name_of(constant) constant = superclass - # Some classes have superclasses that are private constants - # so if we generate code with that superclass, the output - # will not be compilable (since private constants are not - # publicly visible). - # - # So we skip superclasses that are not public and walk up the - # chain. - next unless public_module?(superclass) - # Some types have "themselves" as their superclass # which can happen via: # # class A < Numeric; end # A = Class.new(A) @@ -360,74 +315,82 @@ # B = Class.new # class A < B; end # B = A # A.superclass.name #=> "B" # B #=> A - superclass_name = T.must(name_of(superclass)) + superclass_name = name_of(superclass) + next unless superclass_name + resolved_superclass = resolve_constant(superclass_name) next unless Module === resolved_superclass next if name_of(resolved_superclass) == constant_name # We found a suitable superclass break end - return "" if superclass == ::Object || superclass == ::Delegator - return "" if superclass.nil? + return if superclass == ::Object || superclass == ::Delegator + return if superclass.nil? name = name_of(superclass) - return "" if name.nil? || name.empty? + return if name.nil? || name.empty? - " < ::#{name}" + add_to_symbol_queue(name) + + "::#{name}" end - sig { params(constant: Module).returns(String) } - def compile_mixins(constant) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_mixins(tree, constant) singleton_class = singleton_class_of(constant) interesting_ancestors = interesting_ancestors_of(constant) interesting_singleton_class_ancestors = interesting_ancestors_of(singleton_class) prepend = interesting_ancestors.take_while { |c| !are_equal?(constant, c) } include = interesting_ancestors.drop(prepend.size + 1) extend = interesting_singleton_class_ancestors.reject do |mod| - !public_module?(mod) || Module != class_of(mod) || are_equal?(mod, singleton_class) + Module != class_of(mod) || are_equal?(mod, singleton_class) end - prepends = prepend + prepend .reverse .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") } - .select(&method(:public_module?)) .map do |mod| + add_to_symbol_queue(name_of(mod)) + # TODO: Sorbet currently does not handle prepend # properly for method resolution, so we generate an # include statement instead - indented("include(#{qualified_name_of(mod)})") + qname = qualified_name_of(mod) + tree << RBI::Include.new(T.must(qname)) end - includes = include + include .reverse .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") } - .select(&method(:public_module?)) .map do |mod| - indented("include(#{qualified_name_of(mod)})") + add_to_symbol_queue(name_of(mod)) + + qname = qualified_name_of(mod) + tree << RBI::Include.new(T.must(qname)) end - extends = extend + extend .reverse .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") } - .select(&method(:public_module?)) .map do |mod| - indented("extend(#{qualified_name_of(mod)})") - end + add_to_symbol_queue(name_of(mod)) - (prepends + includes + extends).join("\n") + qname = qualified_name_of(mod) + tree << RBI::Extend.new(T.must(qname)) + end end - sig { params(constant: Module).returns(String) } - def compile_mixes_in_class_methods(constant) - return "" if constant.is_a?(Class) + sig { params(tree: RBI::Tree, constant: Module).void } + def compile_mixes_in_class_methods(tree, constant) + return if constant.is_a?(Class) mixins_from_modules = {} Class.new do # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing @@ -455,15 +418,17 @@ all_dynamic_extends = mixins_from_modules.delete(constant) all_dynamic_includes = mixins_from_modules.keys dynamic_extends_from_dynamic_includes = mixins_from_modules.values.flatten dynamic_extends = all_dynamic_extends - dynamic_extends_from_dynamic_includes - result = all_dynamic_includes + all_dynamic_includes .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") } - .select(&method(:public_module?)) .map do |mod| - indented("include(#{qualified_name_of(mod)})") + add_to_symbol_queue(name_of(mod)) + + qname = qualified_name_of(mod) + tree << RBI::Include.new(T.must(qname)) end.join("\n") ancestors = singleton_class_of(constant).ancestors extends_as_concern = ancestors.any? do |mod| qualified_name_of(mod) == "::ActiveSupport::Concern" @@ -471,73 +436,61 @@ class_methods_module = resolve_constant("#{name_of(constant)}::ClassMethods") mixed_in_module = if extends_as_concern && Module === class_methods_module class_methods_module else - dynamic_extends.find do |mod| - mod != constant && public_module?(mod) - end + dynamic_extends.find { |mod| mod != constant } end - return result if mixed_in_module.nil? + return if mixed_in_module.nil? qualified_name = qualified_name_of(mixed_in_module) - return result if qualified_name == "" + return if qualified_name.nil? || qualified_name == "" - [ - result, - indented("mixes_in_class_methods(#{qualified_name})"), - ].select { |b| b != "" }.join("\n\n") + tree << RBI::MixesInClassMethods.new(qualified_name) rescue - "" + nil # silence errors end - sig { params(name: String, constant: Module).returns(T.nilable(String)) } - def compile_methods(name, constant) - initialize_method = compile_method( + sig { params(tree: RBI::Tree, name: String, constant: Module).void } + def compile_methods(tree, name, constant) + compile_method( + tree, name, constant, initialize_method_for(constant) ) - instance_methods = compile_directly_owned_methods(name, constant) - singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant)) - - return if symbol_ignored?(name) && !instance_methods && !singleton_methods - - [ - initialize_method, - instance_methods, - singleton_methods, - ].select { |b| b && !b.strip.empty? }.join("\n\n") + compile_directly_owned_methods(tree, name, constant) + compile_directly_owned_methods(tree, name, singleton_class_of(constant)) end - sig { params(module_name: String, mod: Module, for_visibility: T::Array[Symbol]).returns(T.nilable(String)) } - def compile_directly_owned_methods(module_name, mod, for_visibility = [:public, :protected, :private]) - with_indentation_for_constant(mod) do - methods = 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)) + sig do + params( + tree: RBI::Tree, + module_name: String, + mod: Module, + for_visibility: T::Array[Symbol] + ).void + end + def compile_directly_owned_methods(tree, module_name, mod, for_visibility = [:public, :protected, :private]) + method_names_by_visibility(mod) + .delete_if { |visibility, _method_list| !for_visibility.include?(visibility) } + .each do |visibility, method_list| + method_list.sort!.map do |name| + next if name == :initialize + vis = case visibility + when :protected + RBI::Visibility::Protected + when :private + RBI::Visibility::Private + else + RBI::Visibility::Public end - compiled.compact! - - unless compiled.empty? || visibility == :public - # add visibility badge - compiled.unshift('', indented(visibility.to_s), '') - end - - compiled + compile_method(tree, module_name, mod, mod.instance_method(name), vis) end - .compact - - return if methods.empty? - - methods.join("\n") - end + end end sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) } def method_names_by_visibility(mod) { @@ -557,16 +510,18 @@ .include?(method_name.gsub(/=$/, '').to_sym) end sig do params( + tree: RBI::Tree, symbol_name: String, constant: Module, - method: T.nilable(UnboundMethod) - ).returns(T.nilable(String)) + method: T.nilable(UnboundMethod), + visibility: RBI::Visibility + ).void end - def compile_method(symbol_name, constant, method) + def compile_method(tree, symbol_name, constant, method, visibility = RBI::Visibility::Public) return unless method return unless method.owner == constant return if symbol_ignored?(symbol_name) && !method_in_gem?(method) signature = signature_of(method) @@ -609,83 +564,74 @@ name = name.to_s.gsub(/[^a-zA-Z0-9_]/, fallback_arg_name) [type, name] end - parameter_list = sanitized_parameters.map do |type, name| + rbi_method = RBI::Method.new(method_name, is_singleton: constant.singleton_class?, visibility: visibility) + rbi_method.sigs << compile_signature(signature, sanitized_parameters) if signature + + sanitized_parameters.each do |type, name| case type when :req - name + rbi_method << RBI::Param.new(name) when :opt - "#{name} = T.unsafe(nil)" + rbi_method << RBI::OptParam.new(name, "T.unsafe(nil)") when :rest - "*#{name}" + rbi_method << RBI::RestParam.new(name) when :keyreq - "#{name}:" + rbi_method << RBI::KwParam.new(name) when :key - "#{name}: T.unsafe(nil)" + rbi_method << RBI::KwOptParam.new(name, "T.unsafe(nil)") when :keyrest - "**#{name}" + rbi_method << RBI::KwRestParam.new(name) when :block - "&#{name}" + rbi_method << RBI::BlockParam.new(name) end - end.join(', ') + end - parameter_list = "(#{parameter_list})" if parameter_list != "" - signature_str = indented(compile_signature(signature, sanitized_parameters)) if signature - - [ - signature_str, - indented("def #{method_name}#{parameter_list}; end"), - ].compact.join("\n") + tree << rbi_method end TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/ - sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(String) } + sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(RBI::Sig) } def compile_signature(signature, parameters) parameter_types = T.let(signature.arg_types.to_h, T::Hash[Symbol, T::Types::Base]) parameter_types.merge!(signature.kwarg_types) parameter_types[signature.rest_name] = signature.rest_type if signature.has_rest parameter_types[signature.keyrest_name] = signature.keyrest_type if signature.has_keyrest parameter_types[signature.block_name] = signature.block_type if signature.block_name - params = parameters.map do |_, name| - type = parameter_types[name.to_sym] - "#{name}: #{type}" - end.join(", ") + sig = RBI::Sig.new - returns = type_of(signature.return_type) + parameters.each do |_, name| + type = sanitize_signature_types(parameter_types[name.to_sym].to_s) + add_to_symbol_queue(type) + sig << RBI::SigParam.new(name, type) + end - type_parameters = (params + returns).scan(TYPE_PARAMETER_MATCHER).flatten.uniq.map { |p| ":#{p}" }.join(", ") - type_parameters = ".type_parameters(#{type_parameters})" unless type_parameters.empty? + return_type = type_of(signature.return_type) + sig.return_type = sanitize_signature_types(return_type) + add_to_symbol_queue(sig.return_type) - mode = case signature.mode + parameter_types.values.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq.each do |k, _| + sig.type_params << k + end + + case signature.mode when "abstract" - ".abstract" + sig.is_abstract = true when "override" - ".override" + sig.is_override = true when "overridable_override" - ".overridable.override" + sig.is_overridable = true + sig.is_override = true when "overridable" - ".overridable" - else - "" + sig.is_overridable = true 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(".params()", "") - .gsub(TYPE_PARAMETER_MATCHER, "T.type_parameter(:\\1)")[1..-1] - - "sig { #{signature_body} }" + sig end sig { params(symbol_name: String).returns(T::Boolean) } def symbol_ignored?(symbol_name) SymbolLoader.ignore_symbol?(symbol_name) @@ -697,58 +643,10 @@ def valid_method_name?(name) return true if SPECIAL_METHOD_NAMES.include?(name) !!name.match(/^[[:word:]]+[?!=]?$/) 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(step = 1, &_blk) - @indent += 2 * step - yield - ensure - @indent -= 2 * step - end - - sig do - params( - constant: Module, - blk: T.proc - .returns(T.nilable(String)) - ) - .returns(T.nilable(String)) - end - def with_indentation_for_constant(constant, &blk) - step = if constant.singleton_class? - 1 - else - 0 - end - - result = with_indentation(step, &blk) - - return result unless result - return result unless constant.singleton_class? - - [ - indented("class << self"), - result, - indented("end"), - ].compact.join("\n") - end - - sig { params(str: String).returns(String) } - def indented(str) - " " * @indent + str - end - sig { params(method: UnboundMethod).returns(T::Boolean) } def method_in_gem?(method) source_location = method.source_location&.first return false if source_location.nil? @@ -802,37 +700,10 @@ constant.instance_method(:initialize) rescue nil end - def parent_declares_constant?(name) - name_parts = name.split("::") - - parent_name = name_parts[0...-1].join("::") - parent_name = parent_name[2..-1] if parent_name.start_with?("::") - parent_name = 'Object' if parent_name == "" - parent = T.cast(resolve_constant(parent_name), T.nilable(Module)) - - return false unless parent - - constants_of(parent).include?(name_parts.last.to_sym) - end - - sig { params(constant: Module).returns(T::Boolean) } - def public_module?(constant) - constant_name = name_of(constant) - return false unless constant_name - return false if constant_name.start_with?('T::Private') - - begin - # can't use !! here because the constant might override ! and mess with us - Module === eval(constant_name) # rubocop:disable Security/Eval - rescue NameError - false - end - end - sig { params(constant: BasicObject).returns(Class).checked(:never) } def class_of(constant) Kernel.instance_method(:class).bind(constant).call end @@ -910,11 +781,11 @@ # We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy # so try to get the name of the target class begin target = Kernel.instance_method(:send).bind(constant).call(:target) rescue NoMethodError - return nil + return end raw_name_of(target) end @@ -938,9 +809,18 @@ 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(sig_string: String).returns(String) } + def sanitize_signature_types(sig_string) + sig_string + .gsub(".returns(<VOID>)", ".void") + .gsub("<VOID>", "void") + .gsub("<NOT-TYPED>", "T.untyped") + .gsub(".params()", "") end sig { params(constant: T::Types::Base).returns(String) } def type_of(constant) constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")