yard-sorbet 0.6.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2eb93a66e3c6308a6e1cb10e26eae958d8b114451ee4405659f13da9d423cf
4
- data.tar.gz: 869f388d3b14dfaec0d63bb910ab039ec556f010fc586fe8d5e6234a2e1e2bac
3
+ metadata.gz: 6269732c6f6c20a5757281dea8e97537116082e9f6fcc01af8e37b375585c573
4
+ data.tar.gz: 3ba3e2a3e9caf49054d30a3e6030540804d7515eecda12eda07140402406879b
5
5
  SHA512:
6
- metadata.gz: 4ec95f037474cb2e2c95aed81e369901e945014e241ecfb5d12fd4ce1cec1871dda4599ad0915d283ca2a8355b2125e22a71eefdf23fe27d98e06573590fe79b
7
- data.tar.gz: d34b5eaabc45de6b5bb58fbc4592468cb0074872e11a6af0c062bea082a98c81687e283b66c98f786025ae5eddd1b64708932f50c6c9798d853fe2d1161b5e12
6
+ metadata.gz: aa63a4e71daaffb9e18d5613bed37d366b47f80ca040570e2195716b1ae2f275d2e1cad568df123064b0678ee9862d757e05b026efcaa164d80eaae26f2eb65a
7
+ data.tar.gz: 3d7de3147fe46b1ca6339ef32d66ae98c0f48851205a3baee320a88bbafec44921f64672c335c291a2b117895fa858e49fb254cd7ec3f2b20eb339f06e324f39
@@ -13,18 +13,13 @@ module YARDSorbet
13
13
  # seem to be an API to tweeze them from one node to another without
14
14
  # managing YARD internal state. Instead, we just extract them from
15
15
  # the raw text and re-attach them.
16
- directives = parser.raw_text&.split("\n")&.select do |line|
17
- line.start_with?('@!')
18
- end || []
19
-
16
+ directives = parser.raw_text&.split("\n")&.select { _1.start_with?('@!') } || []
20
17
  [parser.to_docstring, directives]
21
18
  end
22
19
 
23
20
  sig { params(docstring: String, directives: T::Array[String]).void }
24
21
  def self.add_directives(docstring, directives)
25
- directives.each do |directive|
26
- docstring.concat("\n#{directive}")
27
- end
22
+ directives.each { docstring.concat("\n#{_1}") }
28
23
  end
29
24
  end
30
25
  end
@@ -15,7 +15,7 @@ module YARDSorbet
15
15
  # The `@abstract` tag template
16
16
  TAG_TEXT = 'Subclasses must implement the `abstract` methods below.'
17
17
  # Extra text for class namespaces
18
- CLASS_TAG_TEXT = T.let("It cannont be directly instantiated. #{TAG_TEXT}", String)
18
+ CLASS_TAG_TEXT = T.let("It cannot be directly instantiated. #{TAG_TEXT}", String)
19
19
 
20
20
  sig { void }
21
21
  def process
@@ -16,10 +16,8 @@ module YARDSorbet
16
16
  def process
17
17
  statement.parameters(false).each do |mixin|
18
18
  obj = YARD::CodeObjects::Proxy.new(namespace, mixin.source)
19
- class_methods_namespace = MixesInClassMethodsHandler.mixed_in_class_methods(obj.to_s)
20
- next unless class_methods_namespace
21
-
22
- included_in.mixins(:class) << YARD::CodeObjects::Proxy.new(obj, class_methods_namespace)
19
+ class_methods_namespaces = MixesInClassMethodsHandler.mixed_in_class_methods(obj.to_s)
20
+ class_methods_namespaces&.each { included_in.mixins(:class) << YARD::CodeObjects::Proxy.new(obj, _1) }
23
21
  end
24
22
  end
25
23
 
@@ -12,16 +12,17 @@ module YARDSorbet
12
12
  handles method_call(:mixes_in_class_methods)
13
13
  namespace_only
14
14
 
15
- @@mix_in_class_methods = T.let({}, T::Hash[String, String]) # rubocop:disable Style/ClassVars
15
+ @@mix_in_class_methods = T.let({}, T::Hash[String, T::Array[String]]) # rubocop:disable Style/ClassVars
16
16
 
17
- sig { params(code_obj: String).returns(T.nilable(String)) }
17
+ sig { params(code_obj: String).returns(T.nilable(T::Array[String])) }
18
18
  def self.mixed_in_class_methods(code_obj)
19
19
  @@mix_in_class_methods[code_obj]
20
20
  end
21
21
 
22
22
  sig { void }
23
23
  def process
24
- @@mix_in_class_methods[namespace.to_s] = statement.parameters(false)[0].source
24
+ @@mix_in_class_methods[namespace.to_s] ||= []
25
+ @@mix_in_class_methods.fetch(namespace.to_s) << statement.parameters(false)[0].source
25
26
  end
26
27
  end
27
28
  end
@@ -10,59 +10,104 @@ module YARDSorbet
10
10
  handles method_call(:sig)
11
11
  namespace_only
12
12
 
13
- # These node types attached to sigs represent attr_* declarations
14
- ATTR_NODE_TYPES = T.let(%i[command fcall], T::Array[Symbol])
15
- private_constant :ATTR_NODE_TYPES
13
+ # YARD types that can have docstrings attached to them
14
+ Documentable = T.type_alias do
15
+ T.any(
16
+ YARD::CodeObjects::MethodObject, YARD::Parser::Ruby::MethodCallNode, YARD::Parser::Ruby::MethodDefinitionNode
17
+ )
18
+ end
19
+ private_constant :Documentable
16
20
 
17
21
  # Swap the method definition docstring and the sig docstring.
18
22
  # Parse relevant parts of the `sig` and include them as well.
19
23
  sig { void }
20
24
  def process
21
25
  method_node = NodeUtils.get_method_node(NodeUtils.sibling_node(statement))
22
- docstring, directives = Directives.extract_directives(statement.docstring)
23
- parse_sig(method_node, docstring)
24
- method_node.docstring = docstring.to_raw
25
- Directives.add_directives(method_node.docstring, directives)
26
+ case method_node
27
+ when YARD::Parser::Ruby::MethodDefinitionNode then process_def(method_node)
28
+ when YARD::Parser::Ruby::MethodCallNode then process_attr(method_node)
29
+ end
26
30
  statement.docstring = nil
27
31
  end
28
32
 
29
33
  private
30
34
 
31
- sig { params(method_node: YARD::Parser::Ruby::AstNode, docstring: YARD::Docstring).void }
32
- def parse_sig(method_node, docstring)
33
- NodeUtils.bfs_traverse(statement) do |n|
34
- case n.source
35
- when 'abstract'
36
- YARDSorbet::TagUtils.upsert_tag(docstring, 'abstract')
37
- when 'params'
38
- parse_params(method_node, n, docstring)
39
- when 'returns', 'void'
40
- parse_return(n, docstring)
41
- end
35
+ sig { params(def_node: YARD::Parser::Ruby::MethodDefinitionNode).void }
36
+ def process_def(def_node)
37
+ separator = scope == :instance && def_node.type == :def ? '#' : '.'
38
+ registered = YARD::Registry.at("#{namespace}#{separator}#{def_node.method_name(true)}")
39
+ if registered
40
+ parse_node(registered, registered.docstring)
41
+ # Since we're probably in an RBI file, delete the def node, which could otherwise erroneously override the
42
+ # visibility setting
43
+ NodeUtils.delete_node(def_node)
44
+ else
45
+ parse_node(def_node, statement.docstring)
42
46
  end
43
47
  end
44
48
 
45
- sig do
46
- params(
47
- method_node: YARD::Parser::Ruby::AstNode,
48
- node: YARD::Parser::Ruby::AstNode,
49
- docstring: YARD::Docstring
50
- ).void
49
+ sig { params(attr_node: YARD::Parser::Ruby::MethodCallNode).void }
50
+ def process_attr(attr_node)
51
+ return if merged_into_attr?(attr_node)
52
+
53
+ parse_node(attr_node, statement.docstring, include_params: false)
51
54
  end
52
- def parse_params(method_node, node, docstring)
53
- return if ATTR_NODE_TYPES.include?(method_node.type)
54
55
 
56
+ # An attr* sig can be merged into a previous attr* docstring if it is the only parameter passed to the attr*
57
+ # declaration. This is to avoid needing to rewrite the source code to separate merged and unmerged attr*
58
+ # declarations.
59
+ sig { params(attr_node: YARD::Parser::Ruby::MethodCallNode).returns(T::Boolean) }
60
+ def merged_into_attr?(attr_node)
61
+ names = NodeUtils.validated_attribute_names(attr_node)
62
+ return false if names.size != 1
63
+
64
+ attrs = namespace.attributes[scope][names[0]]
65
+ return false if attrs.nil? || attrs.empty?
66
+
67
+ document_attr_methods(attrs.values.compact)
68
+ attr_node.docstring = nil
69
+ true
70
+ end
71
+
72
+ sig { params(method_objects: T::Array[YARD::CodeObjects::MethodObject]).void }
73
+ def document_attr_methods(method_objects)
74
+ method_objects.each { parse_node(_1, _1.docstring, include_params: false) }
75
+ end
76
+
77
+ sig { params(attach_to: Documentable, docstring: T.nilable(String), include_params: T::Boolean).void }
78
+ def parse_node(attach_to, docstring, include_params: true)
79
+ existing_docstring = docstring.is_a?(YARD::Docstring)
80
+ docstring, directives = Directives.extract_directives(docstring) unless existing_docstring
81
+ parse_sig(docstring, include_params: include_params)
82
+ attach_to.docstring = docstring.to_raw
83
+ Directives.add_directives(attach_to.docstring, directives) unless existing_docstring
84
+ end
85
+
86
+ sig { params(docstring: YARD::Docstring, include_params: T::Boolean).void }
87
+ def parse_sig(docstring, include_params: true)
88
+ NodeUtils.bfs_traverse(statement) do |node|
89
+ case node.source
90
+ when 'returns' then parse_return(node, docstring)
91
+ when 'params' then parse_params(node, docstring) if include_params
92
+ when 'void' then TagUtils.upsert_tag(docstring, 'return', TagUtils::VOID_RETURN_TYPE)
93
+ when 'abstract' then TagUtils.upsert_tag(docstring, 'abstract')
94
+ end
95
+ end
96
+ end
97
+
98
+ sig { params(node: YARD::Parser::Ruby::AstNode, docstring: YARD::Docstring).void }
99
+ def parse_params(node, docstring)
55
100
  sibling = NodeUtils.sibling_node(node)
56
- sibling[0][0].each do |p|
57
- param_name = p[0][0]
58
- types = SigToYARD.convert(p.last)
101
+ sibling[0][0].each do |param|
102
+ param_name = param[0][0]
103
+ types = SigToYARD.convert(param.last)
59
104
  TagUtils.upsert_tag(docstring, 'param', types, param_name)
60
105
  end
61
106
  end
62
107
 
63
108
  sig { params(node: YARD::Parser::Ruby::AstNode, docstring: YARD::Docstring).void }
64
109
  def parse_return(node, docstring)
65
- type = node.source == 'void' ? ['void'] : SigToYARD.convert(NodeUtils.sibling_node(node))
110
+ type = SigToYARD.convert(NodeUtils.sibling_node(node))
66
111
  TagUtils.upsert_tag(docstring, 'return', type)
67
112
  end
68
113
  end
@@ -33,11 +33,9 @@ module YARDSorbet
33
33
  # There is a chance that there is a custom initializer, so make sure we steal the existing docstring
34
34
  # and source
35
35
  docstring, directives = Directives.extract_directives(object.docstring)
36
- object.tags.each { |tag| docstring.add_tag(tag) }
37
- props.each do |prop|
38
- TagUtils.upsert_tag(docstring, 'param', prop.types, prop.prop_name, prop.doc)
39
- end
40
- TagUtils.upsert_tag(docstring, 'return', ['void'])
36
+ object.tags.each { docstring.add_tag(_1) }
37
+ props.each { TagUtils.upsert_tag(docstring, 'param', _1.types, _1.prop_name, _1.doc) }
38
+ TagUtils.upsert_tag(docstring, 'return', TagUtils::VOID_RETURN_TYPE)
41
39
  decorate_t_struct_init(object, props, docstring, directives)
42
40
  end
43
41
 
@@ -1,4 +1,6 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
3
+
2
4
  # This is in an rbi so the runtime doesn't depend on experimental sorbet features
3
5
  module YARDSorbet::Handlers::StructClassHandler
4
6
  requires_ancestor { YARD::Handlers::Ruby::ClassHandler }
@@ -13,7 +13,7 @@ module YARDSorbet
13
13
 
14
14
  sig { void }
15
15
  def process
16
- name = statement.parameters.first.last.last.source
16
+ name = params[0][-1][-1].source
17
17
  prop = make_prop(name)
18
18
  update_state(prop)
19
19
  object = YARD::CodeObjects::MethodObject.new(namespace, name, scope)
@@ -27,37 +27,45 @@ module YARDSorbet
27
27
  sig { params(object: YARD::CodeObjects::MethodObject, prop: TStructProp).void }
28
28
  def decorate_object(object, prop)
29
29
  object.source = prop.source
30
- # TODO: this should use `+` to delimit the attribute name when markdown is disabled
31
- reader_docstring = prop.doc.empty? ? "Returns the value of attribute `#{prop.prop_name}`." : prop.doc
30
+ # TODO: this should use `+` to delimit the prop name when markdown is disabled
31
+ reader_docstring = prop.doc.empty? ? "Returns the value of prop `#{prop.prop_name}`." : prop.doc
32
32
  docstring = YARD::DocstringParser.new.parse(reader_docstring).to_docstring
33
33
  docstring.add_tag(YARD::Tags::Tag.new(:return, '', prop.types))
34
34
  object.docstring = docstring.to_raw
35
35
  end
36
36
 
37
- # Get the default prop value
38
- sig { returns(T.nilable(String)) }
39
- def default_value
40
- default_node = statement.traverse { |n| break n if n.type == :label && n.source == 'default:' }
41
- default_node.parent[1].source if default_node
37
+ sig { returns(T::Boolean) }
38
+ def immutable?
39
+ statement.method_name(true) == :const || kw_arg('immutable:') == 'true'
40
+ end
41
+
42
+ # @return the value passed to the keyword argument, or nil
43
+ sig { params(kwd: String).returns(T.nilable(String)) }
44
+ def kw_arg(kwd)
45
+ params[2]&.find { _1[0].source == kwd }&.[](1)&.source
42
46
  end
43
47
 
44
48
  sig { params(name: String).returns(TStructProp) }
45
49
  def make_prop(name)
46
50
  TStructProp.new(
47
- default: default_value,
51
+ default: kw_arg('default:'),
48
52
  doc: statement.docstring.to_s,
49
53
  prop_name: name,
50
54
  source: statement.source,
51
- types: SigToYARD.convert(statement.parameters[1])
55
+ types: SigToYARD.convert(params[1])
52
56
  )
53
57
  end
54
58
 
59
+ sig { returns(T::Array[T.untyped]) }
60
+ def params
61
+ @params ||= T.let(statement.parameters(false), T.nilable(T::Array[T.untyped]))
62
+ end
63
+
55
64
  # Register the field explicitly as an attribute.
56
- # While `const` attributes are immutable, `prop` attributes may be reassigned.
57
65
  sig { params(object: YARD::CodeObjects::MethodObject, name: String).void }
58
66
  def register_attrs(object, name)
59
- # Create the virtual method in our current scope
60
- write = statement.method_name(true) == :prop ? object : nil
67
+ write = immutable? ? nil : object
68
+ # Create the virtual attribute in our current scope
61
69
  namespace.attributes[scope][name] ||= SymbolHash[read: object, write: write]
62
70
  end
63
71
 
@@ -8,45 +8,35 @@ module YARDSorbet
8
8
 
9
9
  # Command node types that can have type signatures
10
10
  ATTRIBUTE_METHODS = T.let(%i[attr attr_accessor attr_reader attr_writer].freeze, T::Array[Symbol])
11
- # Node types that can have type signatures
12
- SIGABLE_NODE = T.type_alias do
13
- T.any(YARD::Parser::Ruby::MethodDefinitionNode, YARD::Parser::Ruby::MethodCallNode)
14
- end
15
11
  # Skip these method contents during BFS node traversal, they can have their own nested types via `T.Proc`
16
- SKIP_METHOD_CONTENTS = T.let(%i[params returns], T::Array[Symbol])
17
-
18
- private_constant :ATTRIBUTE_METHODS, :SIGABLE_NODE
12
+ SKIP_METHOD_CONTENTS = T.let(%i[params returns].freeze, T::Array[Symbol])
13
+ # Node types that can have type signatures
14
+ SigableNode = T.type_alias { T.any(YARD::Parser::Ruby::MethodDefinitionNode, YARD::Parser::Ruby::MethodCallNode) }
15
+ private_constant :ATTRIBUTE_METHODS, :SKIP_METHOD_CONTENTS, :SigableNode
19
16
 
20
- # Traverese AST nodes in breadth-first order
17
+ # Traverse AST nodes in breadth-first order
21
18
  # @note This will skip over some node types.
22
19
  # @yield [YARD::Parser::Ruby::AstNode]
23
- sig do
24
- params(
25
- node: YARD::Parser::Ruby::AstNode,
26
- _blk: T.proc.params(n: YARD::Parser::Ruby::AstNode).void
27
- ).void
28
- end
20
+ sig { params(node: YARD::Parser::Ruby::AstNode, _blk: T.proc.params(n: YARD::Parser::Ruby::AstNode).void).void }
29
21
  def self.bfs_traverse(node, &_blk)
30
22
  queue = [node]
31
23
  until queue.empty?
32
24
  n = T.must(queue.shift)
33
25
  yield n
34
- n.children.each { |c| queue.push(c) }
26
+ n.children.each { queue.push(_1) }
35
27
  queue.pop if n.is_a?(YARD::Parser::Ruby::MethodCallNode) && SKIP_METHOD_CONTENTS.include?(n.method_name(true))
36
28
  end
37
29
  end
38
30
 
31
+ sig { params(node: YARD::Parser::Ruby::AstNode).void }
32
+ def self.delete_node(node)
33
+ node.parent.children.delete(node)
34
+ end
35
+
39
36
  # Gets the node that a sorbet `sig` can be attached do, bypassing visisbility modifiers and the like
40
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(SIGABLE_NODE) }
37
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns(SigableNode) }
41
38
  def self.get_method_node(node)
42
- case node
43
- when YARD::Parser::Ruby::MethodDefinitionNode
44
- return node
45
- when YARD::Parser::Ruby::MethodCallNode
46
- return node if ATTRIBUTE_METHODS.include?(node.method_name(true))
47
- end
48
-
49
- node.jump(:def, :defs)
39
+ sigable_node?(node) ? node : node.jump(:def, :defs)
50
40
  end
51
41
 
52
42
  # Find and return the adjacent node (ascending)
@@ -54,8 +44,28 @@ module YARDSorbet
54
44
  sig { params(node: YARD::Parser::Ruby::AstNode).returns(YARD::Parser::Ruby::AstNode) }
55
45
  def self.sibling_node(node)
56
46
  siblings = node.parent.children
57
- siblings.each_with_index.find do |sibling, i|
58
- return siblings.fetch(i + 1) if sibling.equal?(node)
47
+ node_index = siblings.find_index { _1.equal?(node) }
48
+ siblings.fetch(node_index + 1)
49
+ end
50
+
51
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Boolean) }
52
+ def self.sigable_node?(node)
53
+ case node
54
+ when YARD::Parser::Ruby::MethodDefinitionNode then true
55
+ when YARD::Parser::Ruby::MethodCallNode then ATTRIBUTE_METHODS.include?(node.method_name(true))
56
+ else false
57
+ end
58
+ end
59
+
60
+ # @see https://github.com/lsegal/yard/blob/main/lib/yard/handlers/ruby/attribute_handler.rb
61
+ # YARD::Handlers::Ruby::AttributeHandler.validated_attribute_names
62
+ sig { params(attr_node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) }
63
+ def self.validated_attribute_names(attr_node)
64
+ attr_node.parameters(false).map do |obj|
65
+ case obj
66
+ when YARD::Parser::Ruby::LiteralNode then obj[0][0].source
67
+ else raise YARD::Parser::UndocumentableError, obj.source
68
+ end
59
69
  end
60
70
  end
61
71
  end
@@ -6,18 +6,29 @@ module YARDSorbet
6
6
  module SigToYARD
7
7
  extend T::Sig
8
8
 
9
+ # Map of common types to YARD conventions (in order to reduce allocations)
10
+ REF_TYPES = T.let({
11
+ 'T::Boolean' => ['Boolean'].freeze, # YARD convention for booleans
12
+ # YARD convention is use singleton objects when applicable:
13
+ # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals
14
+ 'FalseClass' => ['false'].freeze,
15
+ 'NilClass' => ['nil'].freeze,
16
+ 'TrueClass' => ['true'].freeze
17
+ }.freeze, T::Hash[String, [String]])
18
+ private_constant :REF_TYPES
19
+
9
20
  # @see https://yardoc.org/types.html
10
21
  sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
11
22
  def self.convert(node)
12
23
  # scrub newlines, as they break the YARD parser
13
- convert_node(node).map { |type| type.gsub(/\n\s*/, ' ') }
24
+ convert_node(node).map { _1.gsub(/\n\s*/, ' ') }
14
25
  end
15
26
 
16
27
  sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
17
28
  private_class_method def self.convert_node(node)
18
29
  case node
19
30
  when YARD::Parser::Ruby::MethodCallNode then convert_call(node)
20
- when YARD::Parser::Ruby::ReferenceNode then convert_ref(node)
31
+ when YARD::Parser::Ruby::ReferenceNode then convert_ref(node.source)
21
32
  else convert_node_type(node)
22
33
  end
23
34
  end
@@ -45,8 +56,7 @@ module YARDSorbet
45
56
  return node.source if node.empty? || node.type != :aref
46
57
 
47
58
  collection_type = node.first.source
48
- member_type = node.last.children.map { |child| build_generic_type(child) }.join(', ')
49
-
59
+ member_type = node.last.children.map { build_generic_type(_1) }.join(', ')
50
60
  "#{collection_type}[#{member_type}]"
51
61
  end
52
62
 
@@ -62,11 +72,11 @@ module YARDSorbet
62
72
  end
63
73
  end
64
74
 
65
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
75
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns([String]) }
66
76
  private_class_method def self.convert_array(node)
67
77
  # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Order-Dependent_Lists
68
- member_types = node.first.children.map { |n| convert_node(n) }
69
- sequence = member_types.map { |mt| mt.size == 1 ? mt[0] : mt.to_s.tr('"', '') }.join(', ')
78
+ member_types = node.first.children.map { convert_node(_1) }
79
+ sequence = member_types.map { _1.size == 1 ? _1[0] : _1.to_s.tr('"', '') }.join(', ')
70
80
  ["Array(#{sequence})"]
71
81
  end
72
82
 
@@ -75,14 +85,14 @@ module YARDSorbet
75
85
  node.namespace.source == 'T' ? convert_t_method(node) : [node.source]
76
86
  end
77
87
 
78
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
88
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns([String]) }
79
89
  private_class_method def self.convert_collection(node)
80
90
  collection_type = node.first.source.split('::').last
81
- member_type = convert_node(node.last.first).join(', ')
91
+ member_type = convert_node(node[-1][0]).join(', ')
82
92
  ["#{collection_type}<#{member_type}>"]
83
93
  end
84
94
 
85
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
95
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns([String]) }
86
96
  private_class_method def self.convert_hash(node)
87
97
  kv = node.last.children
88
98
  key_type = convert_node(kv.first).join(', ')
@@ -95,33 +105,23 @@ module YARDSorbet
95
105
  node.children.size == 1 ? convert_node(node.children.first) : [node.source]
96
106
  end
97
107
 
98
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
99
- private_class_method def self.convert_ref(node)
100
- source = node.source
101
- case source
102
- when 'T::Boolean' then ['Boolean'] # YARD convention for booleans
103
- # YARD convention is use singleton objects when applicable:
104
- # https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals
105
- when 'FalseClass' then ['false']
106
- when 'NilClass' then ['nil']
107
- when 'TrueClass' then ['true']
108
- else [source]
109
- end
108
+ sig { params(node_source: String).returns([String]) }
109
+ private_class_method def self.convert_ref(node_source)
110
+ REF_TYPES[node_source] || [node_source]
110
111
  end
111
112
 
112
113
  sig { params(node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) }
113
114
  private_class_method def self.convert_t_method(node)
114
115
  case node.method_name(true)
115
- when :any then node.last.first.children.map { |n| convert_node(n) }.flatten
116
- # Order matters here, putting `nil` last results in a more concise
117
- # return syntax in the UI (superscripted `?`)
116
+ # Order matters here, putting `nil` last results in a more concise return syntax in the UI (superscripted `?`):
118
117
  # https://github.com/lsegal/yard/blob/cfa62ae/lib/yard/templates/helpers/html_helper.rb#L499-L500
119
- when :nilable then convert_node(node.last) + ['nil']
118
+ when :nilable then convert_node(node.last) + REF_TYPES.fetch('NilClass')
119
+ when :any then node[-1][0].children.flat_map { convert_node(_1) }
120
120
  else [node.source]
121
121
  end
122
122
  end
123
123
 
124
- sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
124
+ sig { params(node: YARD::Parser::Ruby::AstNode).returns([String]) }
125
125
  private_class_method def self.convert_unknown(node)
126
126
  log.warn("Unsupported sig #{node.type} node #{node.source}")
127
127
  [node.source]
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module YARDSorbet
@@ -6,13 +6,15 @@ module YARDSorbet
6
6
  module TagUtils
7
7
  extend T::Sig
8
8
 
9
+ # The `void` return type, as a constant to reduce array allocations
10
+ VOID_RETURN_TYPE = T.let(['void'].freeze, [String])
11
+
9
12
  # @return the tag with the matching `tag_name` and `name`, or `nil`
10
13
  sig do
11
- params(docstring: YARD::Docstring, tag_name: String, name: T.nilable(String))
12
- .returns(T.nilable(YARD::Tags::Tag))
14
+ params(docstring: YARD::Docstring, tag_name: String, name: T.nilable(String)).returns(T.nilable(YARD::Tags::Tag))
13
15
  end
14
16
  def self.find_tag(docstring, tag_name, name)
15
- docstring.tags.find { |t| t.tag_name == tag_name && t.name == name }
17
+ docstring.tags.find { _1.tag_name == tag_name && _1.name == name }
16
18
  end
17
19
 
18
20
  # Create or update a `YARD` tag with type information
@@ -31,7 +33,7 @@ module YARDSorbet
31
33
  return unless types
32
34
 
33
35
  # Updating a tag in place doesn't seem to work, so we'll delete it, add the types, and re-add it
34
- docstring.delete_tag_if { |t| t == tag }
36
+ docstring.delete_tag_if { _1 == tag }
35
37
  # overwrite any existing type annotation (sigs should win)
36
38
  tag.types = types
37
39
  tag.text = text unless text.empty?
@@ -4,5 +4,5 @@
4
4
  # Types are documentation
5
5
  module YARDSorbet
6
6
  # {https://rubygems.org/gems/yard-sorbet Version history}
7
- VERSION = '0.6.1'
7
+ VERSION = '0.8.0'
8
8
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yard-sorbet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Douglas Eichelberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-01 00:00:00.000000000 Z
11
+ date: 2023-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.9.1
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.9.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: codecov
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.6.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.6.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +86,28 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 1.22.0
89
+ version: 1.42.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 1.22.0
96
+ version: 1.42.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop-performance
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 1.12.0
103
+ version: 1.15.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 1.12.0
110
+ version: 1.15.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rubocop-rake
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 2.5.0
131
+ version: 2.16.0
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 2.5.0
138
+ version: 2.16.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rubocop-sorbet
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -151,33 +151,33 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: 0.6.0
153
153
  - !ruby/object:Gem::Dependency
154
- name: simplecov
154
+ name: sorbet
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - ">="
157
+ - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '0'
159
+ version: 0.5.9204
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - ">="
164
+ - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '0'
166
+ version: 0.5.9204
167
167
  - !ruby/object:Gem::Dependency
168
- name: sorbet
168
+ name: tapioca
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: 0.5.9204
173
+ version: 0.10.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: 0.5.9204
180
+ version: 0.10.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: sorbet-runtime
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -206,9 +206,7 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: '0.9'
209
- description: 'A YARD plugin that incorporates Sorbet type information
210
-
211
- '
209
+ description: A YARD plugin that incorporates Sorbet type information
212
210
  email: dduugg@gmail.com
213
211
  executables: []
214
212
  extensions: []
@@ -239,6 +237,7 @@ metadata:
239
237
  changelog_uri: https://github.com/dduugg/yard-sorbet/blob/main/CHANGELOG.md
240
238
  documentation_uri: https://dduugg.github.io/yard-sorbet/
241
239
  homepage_uri: https://github.com/dduugg/yard-sorbet
240
+ rubygems_mfa_required: 'true'
242
241
  source_code_uri: https://github.com/dduugg/yard-sorbet
243
242
  wiki_uri: https://github.com/dduugg/yard-sorbet/wiki
244
243
  post_install_message:
@@ -249,14 +248,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
249
248
  requirements:
250
249
  - - ">="
251
250
  - !ruby/object:Gem::Version
252
- version: 2.5.0
251
+ version: 2.7.0
253
252
  required_rubygems_version: !ruby/object:Gem::Requirement
254
253
  requirements:
255
254
  - - ">="
256
255
  - !ruby/object:Gem::Version
257
256
  version: '0'
258
257
  requirements: []
259
- rubygems_version: 3.2.22
258
+ rubygems_version: 3.2.33
260
259
  signing_key:
261
260
  specification_version: 4
262
261
  summary: Create YARD docs from Sorbet type signatures