diff --git a/.rubocop.yml b/.rubocop.yml index b6575acd..53d0ca94 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,4 @@ --- -inherit_from: .rubocop_todo.yml - require: - rubocop-performance - rubocop-rake diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index 76f6d621..00000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,22 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config` -# on 2021-06-13 18:12:55 UTC using RuboCop version 1.16.1. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 1 -# Configuration parameters: IgnoredMethods, CountRepeatedAttributes. -Metrics/AbcSize: - Max: 23 - -# Offense count: 1 -# Configuration parameters: IgnoredMethods. -Metrics/CyclomaticComplexity: - Max: 9 - -# Offense count: 2 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. -Metrics/MethodLength: - Max: 14 diff --git a/CHANGELOG.md b/CHANGELOG.md index ec1a2f64..9d1a5624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Bug fixes * [#61](https://github.com/dduugg/yard-sorbet/pull/61) Fix parsing of `sig` with nested return types +* [#62](https://github.com/dduugg/yard-sorbet/pull/61) Fix parsing of `top_const_ref` nodes (e.g. `::Foo`) ### Changes diff --git a/lib/yard-sorbet/sig_to_yard.rb b/lib/yard-sorbet/sig_to_yard.rb index c8ef290d..6b6a144b 100644 --- a/lib/yard-sorbet/sig_to_yard.rb +++ b/lib/yard-sorbet/sig_to_yard.rb @@ -8,30 +8,33 @@ module YARDSorbet::SigToYARD # @see https://yardoc.org/types.html sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } def self.convert(node) - types = convert_type(node) + types = convert_node(node) # scrub newlines, as they break the YARD parser types.map { |type| type.gsub(/\n\s*/, ' ') } end sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } - private_class_method def self.convert_type(node) - return handle_call(node) if node.is_a? YARD::Parser::Ruby::MethodCallNode + private_class_method def self.convert_node(node) + case node + when YARD::Parser::Ruby::MethodCallNode then handle_call(node) + when YARD::Parser::Ruby::ReferenceNode then handle_ref(node) + else convert_node_type(node) + end + end + sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } + private_class_method def self.convert_node_type(node) case node.type when :aref then handle_aref(node) when :arg_paren then handle_arg_paren(node) when :array then handle_array(node) - when :const_path_ref, :const then handle_const(node) + when :const then handle_ref(node) # Fixed hashes as return values are unsupported: # https://github.com/lsegal/yard/issues/425 # # Hash key params can be individually documented with `@option`, but - # sig translation is unsupported. + # sig translation is currently unsupported. when :hash, :list then ['Hash'] - when :var_ref then handle_var_ref(node) - # A top-level constant reference, such as ::Klass - # It contains a child node of type :const - when :top_const_ref then convert(node.first) else log.warn("Unsupported sig #{node.type} node #{node.source}") [node.source] @@ -52,21 +55,34 @@ def self.convert(node) private_class_method def self.handle_aref(node) # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Parametrized_Types case node.first.source - when 'T::Array', 'T::Enumerable', 'T::Range', 'T::Set' - collection_type = node.first.source.split('::').last - member_type = convert(node.last.first).join(', ') - ["#{collection_type}<#{member_type}>"] - when 'T::Hash' - kv = node.last.children - key_type = convert(kv.first).join(', ') - value_type = convert(kv.last).join(', ') - ["Hash{#{key_type} => #{value_type}}"] + when 'T::Array', 'T::Enumerable', 'T::Range', 'T::Set' then handle_collection(node) + when 'T::Hash' then handle_hash(node) else log.info("Unsupported sig aref node #{node.source}") [build_generic_type(node)] end end + sig { params(node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) } + private_class_method def self.handle_call(node) + node.namespace.source == 'T' ? handle_t_method(node.method_name(true), node) : [node.source] + end + + sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } + private_class_method def self.handle_collection(node) + collection_type = node.first.source.split('::').last + member_type = convert(node.last.first).join(', ') + ["#{collection_type}<#{member_type}>"] + end + + sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } + private_class_method def self.handle_hash(node) + kv = node.last.children + key_type = convert(kv.first).join(', ') + value_type = convert(kv.last).join(', ') + ["Hash{#{key_type} => #{value_type}}"] + end + sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } private_class_method def self.handle_arg_paren(node) convert(node.first.first) @@ -80,24 +96,19 @@ def self.convert(node) end sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } - private_class_method def self.handle_const(node) + private_class_method def self.handle_ref(node) source = node.source case source - # YARD convention for booleans - when 'T::Boolean' then ['Boolean'] + when 'T::Boolean' then ['Boolean'] # YARD convention for booleans + # YARD convention is use singleton objects when applicable: + # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals + when 'FalseClass' then ['false'] + when 'NilClass' then ['nil'] + when 'TrueClass' then ['true'] else [source] end end - sig { params(node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) } - private_class_method def self.handle_call(node) - if node.namespace.source == 'T' - handle_t_method(node.method_name(true), node) - else - [node.source] - end - end - sig { params(method_name: Symbol, node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } private_class_method def self.handle_t_method(method_name, node) case method_name @@ -115,14 +126,8 @@ def self.convert(node) end sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) } - private_class_method def self.handle_var_ref(node) - # YARD convention is use singleton objects when applicable: - # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals - case node.source - when 'FalseClass' then ['false'] - when 'NilClass' then ['nil'] - when 'TrueClass' then ['true'] - else [node.source] - end + private_class_method def self.handle_unknown(node) + log.warn("Unsupported sig #{node.type} node #{node.source}") + [node.source] end end diff --git a/spec/yard_sorbet/handlers/sig_handler_spec.rb b/spec/yard_sorbet/handlers/sig_handler_spec.rb index f6da8495..d576b90f 100644 --- a/spec/yard_sorbet/handlers/sig_handler_spec.rb +++ b/spec/yard_sorbet/handlers/sig_handler_spec.rb @@ -337,7 +337,7 @@ it 'top_const_ref' do node = YARD::Registry.at('VariousTypedSigs#top_const_ref') - expect(node.tag(:return).types).to eq(['Foo']) + expect(node.tag(:return).types).to eq(['::Foo']) end it 'handles inline visibility modifiers' do