lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.6.1 vs lib/tapioca/compilers/symbol_table/symbol_generator.rb in tapioca-0.6.2

- old
+ new

@@ -139,11 +139,10 @@ 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) - return if klass == TypeMember || klass == TypeTemplate klass_name = if klass == ObjectSpace::WeakMap # WeakMap is an implicit generic with one type variable "ObjectSpace::WeakMap[T.untyped]" elsif T::Generic === klass @@ -170,10 +169,11 @@ end sig { params(tree: RBI::Tree, name: String, constant: Module).void } def compile_module(tree, name, constant) return unless defined_in_gem?(constant, strict: false) + return if Tapioca::TypeVariableModule === constant comments = documentation_comments(name) scope = if constant.is_a?(Class) superclass = compile_superclass(constant) @@ -280,34 +280,20 @@ 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) - .each_with_object({}.compare_by_identity) do |constant_name, table| - table[constantize(constant_name.to_s, namespace: constant)] = constant_name.to_s - end - # Map each type variable to its string representation. # - # Each entry of `type_variables` maps an object_id to a String, + # Each entry of `type_variables` maps a Module 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, serialized_type_variable| - constant_name = subconstant_to_name_lookup[type_variable] - type_variable.name = constant_name - # 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, serialized_type_variable) + type_variable_declarations = type_variables.map do |type_variable| + type_variable_name = type_variable.name + next unless type_variable_name + + tree << RBI::TypeMember.new(type_variable_name, type_variable.serialize) end return if type_variable_declarations.empty? tree << RBI::Extend.new("T::Generic") @@ -390,11 +376,11 @@ def add_mixins(tree, mods, mixin_type) mods .select do |mod| name = name_of(mod) - name && !name.start_with?("T::") + name && !filtered_mixin?(name) end .map do |mod| add_to_symbol_queue(name_of(mod)) qname = qualified_name_of(mod) @@ -494,11 +480,13 @@ parameters = T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]]) sanitized_parameters = parameters.each_with_index.map do |(type, name), index| fallback_arg_name = "_arg#{index}" - unless name + name = if name + name.to_s + else # For attr_writer methods, Sorbet signatures have the name # of the method (without the trailing = sign) as the name of # the only parameter. So, if the parameter does not have a name # then the replacement name should be the name of the method # (minus trailing =) if and only if there is a signature for the @@ -510,19 +498,19 @@ parameters.size == 1 && signature.arg_types.size == 1 && method_name[-1] == "=" ) - name = if writer_method_with_sig - T.must(method_name[0...-1]).to_sym + if writer_method_with_sig + method_name.delete_suffix("=") else fallback_arg_name end end # Sanitize param names - name = name.to_s.gsub(/[^a-zA-Z0-9_]/, fallback_arg_name) + name = fallback_arg_name unless valid_parameter_name?(name) [type, name] end separator = constant.singleton_class? ? "." : "#" @@ -611,18 +599,30 @@ sig { params(symbol_name: String).returns(T::Boolean) } def symbol_ignored?(symbol_name) SymbolLoader.ignore_symbol?(symbol_name) end + sig { params(mixin_name: String).returns(T::Boolean) } + def filtered_mixin?(mixin_name) + # filter T:: namespace mixins that aren't T::Props + # T::Props and subconstants have semantic value + mixin_name.start_with?("T::") && !mixin_name.start_with?("T::Props") + end + SPECIAL_METHOD_NAMES = T.let([ "!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^", "<", "<=", "=>", ">", ">=", "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`" ], T::Array[String]) sig { params(name: String).returns(T::Boolean) } def valid_method_name?(name) return true if SPECIAL_METHOD_NAMES.include?(name) !!name.match(/^[[:word:]]+[?!=]?$/) + end + + sig { params(name: String).returns(T::Boolean) } + def valid_parameter_name?(name) + name.match?(/^[[[:alnum:]]_]+$/) end sig { params(method: UnboundMethod).returns(T::Boolean) } def method_in_gem?(method) source_location = method.source_location&.first