solargraph 0.50.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +40 -0
  3. data/.github/workflows/rspec.yml +4 -8
  4. data/.github/workflows/typecheck.yml +34 -0
  5. data/.yardopts +2 -2
  6. data/CHANGELOG.md +81 -3
  7. data/README.md +13 -16
  8. data/SPONSORS.md +2 -9
  9. data/lib/solargraph/api_map/cache.rb +60 -20
  10. data/lib/solargraph/api_map/store.rb +47 -11
  11. data/lib/solargraph/api_map.rb +161 -95
  12. data/lib/solargraph/bench.rb +2 -2
  13. data/lib/solargraph/cache.rb +30 -6
  14. data/lib/solargraph/complex_type/type_methods.rb +54 -9
  15. data/lib/solargraph/complex_type/unique_type.rb +154 -57
  16. data/lib/solargraph/complex_type.rb +91 -17
  17. data/lib/solargraph/convention.rb +0 -3
  18. data/lib/solargraph/converters/dd.rb +5 -0
  19. data/lib/solargraph/converters/dl.rb +3 -0
  20. data/lib/solargraph/converters/dt.rb +3 -0
  21. data/lib/solargraph/diagnostics/rubocop.rb +8 -7
  22. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -0
  23. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  24. data/lib/solargraph/diagnostics.rb +2 -2
  25. data/lib/solargraph/doc_map.rb +146 -0
  26. data/lib/solargraph/gem_pins.rb +64 -0
  27. data/lib/solargraph/language_server/host/cataloger.rb +1 -0
  28. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  29. data/lib/solargraph/language_server/host/dispatch.rb +10 -4
  30. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  31. data/lib/solargraph/language_server/host/sources.rb +7 -4
  32. data/lib/solargraph/language_server/host.rb +26 -7
  33. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  34. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  35. data/lib/solargraph/language_server/message/initialize.rb +11 -0
  36. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  37. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +4 -1
  38. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -4
  39. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  40. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  41. data/lib/solargraph/language_server/message/text_document.rb +1 -1
  42. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  43. data/lib/solargraph/language_server/message.rb +1 -0
  44. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  45. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  46. data/lib/solargraph/library.rb +86 -16
  47. data/lib/solargraph/location.rb +1 -0
  48. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  49. data/lib/solargraph/parser/node_methods.rb +47 -7
  50. data/lib/solargraph/parser/node_processor/base.rb +9 -0
  51. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -5
  52. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  53. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +57 -41
  54. data/lib/solargraph/parser/parser_gem/node_methods.rb +499 -0
  55. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  56. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +53 -0
  57. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  58. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  59. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +2 -2
  60. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  61. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/def_node.rb +1 -1
  62. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  63. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  65. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  66. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  67. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  68. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  69. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sclass_node.rb +1 -1
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +2 -2
  71. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/sym_node.rb +1 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors.rb +54 -0
  73. data/lib/solargraph/parser/parser_gem.rb +12 -0
  74. data/lib/solargraph/parser/region.rb +1 -1
  75. data/lib/solargraph/parser/snippet.rb +2 -0
  76. data/lib/solargraph/parser.rb +9 -10
  77. data/lib/solargraph/pin/base.rb +64 -9
  78. data/lib/solargraph/pin/base_variable.rb +6 -2
  79. data/lib/solargraph/pin/block.rb +19 -9
  80. data/lib/solargraph/pin/closure.rb +17 -2
  81. data/lib/solargraph/pin/common.rb +7 -3
  82. data/lib/solargraph/pin/conversions.rb +33 -3
  83. data/lib/solargraph/pin/delegated_method.rb +97 -0
  84. data/lib/solargraph/pin/documenting.rb +25 -34
  85. data/lib/solargraph/pin/instance_variable.rb +4 -0
  86. data/lib/solargraph/pin/local_variable.rb +13 -1
  87. data/lib/solargraph/pin/method.rb +182 -18
  88. data/lib/solargraph/pin/namespace.rb +18 -5
  89. data/lib/solargraph/pin/parameter.rb +44 -14
  90. data/lib/solargraph/pin/reference/override.rb +2 -2
  91. data/lib/solargraph/pin/reference.rb +8 -0
  92. data/lib/solargraph/pin/search.rb +4 -4
  93. data/lib/solargraph/pin/signature.rb +123 -3
  94. data/lib/solargraph/pin.rb +1 -1
  95. data/lib/solargraph/range.rb +4 -6
  96. data/lib/solargraph/rbs_map/conversions.rb +294 -50
  97. data/lib/solargraph/rbs_map/core_fills.rb +7 -24
  98. data/lib/solargraph/rbs_map/core_map.rb +3 -2
  99. data/lib/solargraph/rbs_map/core_signs.rb +2 -0
  100. data/lib/solargraph/rbs_map/stdlib_map.rb +2 -5
  101. data/lib/solargraph/rbs_map.rb +20 -11
  102. data/lib/solargraph/shell.rb +62 -59
  103. data/lib/solargraph/source/chain/array.rb +32 -0
  104. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  105. data/lib/solargraph/source/chain/call.rb +99 -46
  106. data/lib/solargraph/source/chain/constant.rb +15 -1
  107. data/lib/solargraph/source/chain/if.rb +23 -0
  108. data/lib/solargraph/source/chain/link.rb +8 -2
  109. data/lib/solargraph/source/chain/or.rb +1 -1
  110. data/lib/solargraph/source/chain/z_super.rb +3 -3
  111. data/lib/solargraph/source/chain.rb +29 -14
  112. data/lib/solargraph/source/change.rb +3 -0
  113. data/lib/solargraph/source/cursor.rb +2 -0
  114. data/lib/solargraph/source/source_chainer.rb +8 -5
  115. data/lib/solargraph/source.rb +18 -19
  116. data/lib/solargraph/source_map/clip.rb +25 -23
  117. data/lib/solargraph/source_map/mapper.rb +12 -1
  118. data/lib/solargraph/source_map.rb +30 -7
  119. data/lib/solargraph/type_checker/checks.rb +10 -2
  120. data/lib/solargraph/type_checker.rb +123 -45
  121. data/lib/solargraph/version.rb +1 -1
  122. data/lib/solargraph/workspace/config.rb +14 -11
  123. data/lib/solargraph/workspace.rb +3 -2
  124. data/lib/solargraph/yard_map/cache.rb +6 -0
  125. data/lib/solargraph/yard_map/helpers.rb +1 -1
  126. data/lib/solargraph/yard_map/mapper/to_method.rb +11 -1
  127. data/lib/solargraph/yard_map/mapper.rb +1 -1
  128. data/lib/solargraph/yard_map/to_method.rb +11 -4
  129. data/lib/solargraph/yard_map.rb +1 -284
  130. data/lib/solargraph/yard_tags.rb +20 -0
  131. data/lib/solargraph/yardoc.rb +52 -0
  132. data/lib/solargraph.rb +6 -4
  133. data/solargraph.gemspec +11 -5
  134. metadata +131 -75
  135. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  136. data/lib/solargraph/convention/rspec.rb +0 -30
  137. data/lib/solargraph/documentor.rb +0 -76
  138. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  139. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  140. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  141. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  142. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +0 -23
  143. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +0 -23
  144. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +0 -18
  145. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  146. data/lib/solargraph/parser/legacy.rb +0 -12
  147. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -149
  148. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  149. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  150. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  151. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  152. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -33
  153. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +0 -75
  154. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -68
  155. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  156. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  157. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  158. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  159. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  160. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  161. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  162. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  163. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -32
  164. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  165. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -279
  166. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  167. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +0 -47
  168. data/lib/solargraph/parser/rubyvm.rb +0 -40
  169. data/lib/yard-solargraph.rb +0 -33
@@ -2,9 +2,23 @@
2
2
 
3
3
  module Solargraph
4
4
  class ComplexType
5
- # Methods for accessing type data.
5
+ # Methods for accessing type data available from
6
+ # both ComplexType and UniqueType.
6
7
  #
8
+ # @abstract This mixin relies on these -
9
+ # instance variables:
10
+ # @name: String
11
+ # @subtypes: Array<ComplexType>
12
+ # @rooted: boolish
13
+ # methods:
14
+ # transform()
7
15
  module TypeMethods
16
+ # @!method transform(new_name = nil, &transform_type)
17
+ # @param new_name [String, nil]
18
+ # @yieldparam t [UniqueType]
19
+ # @yieldreturn [UniqueType]
20
+ # @return [UniqueType, nil]
21
+
8
22
  # @return [String]
9
23
  attr_reader :name
10
24
 
@@ -24,8 +38,7 @@ module Solargraph
24
38
 
25
39
  # @return [Boolean]
26
40
  def nil_type?
27
- @nil_type = (name.casecmp('nil') == 0) if @nil_type.nil?
28
- @nil_type
41
+ @nil_type ||= (name.casecmp('nil') == 0)
29
42
  end
30
43
 
31
44
  # @return [Boolean]
@@ -45,6 +58,22 @@ module Solargraph
45
58
  name == 'undefined'
46
59
  end
47
60
 
61
+ # @param generics_to_erase [Enumerable<String>]
62
+ # @return [self]
63
+ def erase_generics(generics_to_erase)
64
+ transform do |type|
65
+ if type.name == ComplexType::GENERIC_TAG_NAME
66
+ if type.all_params.length == 1 && generics_to_erase.include?(type.all_params.first.to_s)
67
+ ComplexType::UNDEFINED
68
+ else
69
+ type
70
+ end
71
+ else
72
+ type
73
+ end
74
+ end
75
+ end
76
+
48
77
  # @return [Boolean]
49
78
  def list_parameters?
50
79
  substring.start_with?('<')
@@ -80,12 +109,25 @@ module Solargraph
80
109
  end.call
81
110
  end
82
111
 
83
- # @return [Symbol] :class or :instance
112
+ # @return [String]
113
+ def rooted_namespace
114
+ return namespace unless rooted?
115
+ "::#{namespace}"
116
+ end
117
+
118
+ # @return [String]
119
+ def rooted_name
120
+ return name unless rooted?
121
+ "::#{name}"
122
+ end
123
+
124
+ # @return [::Symbol] :class or :instance
84
125
  def scope
85
126
  @scope ||= :instance if duck_type? || nil_type?
86
127
  @scope ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? :class : :instance
87
128
  end
88
129
 
130
+ # @param other [Object]
89
131
  def == other
90
132
  return false unless self.class == other.class
91
133
  tag == other.tag
@@ -99,9 +141,9 @@ module Solargraph
99
141
  #
100
142
  # @param api_map [ApiMap] The ApiMap that performs qualification
101
143
  # @param context [String] The namespace from which to resolve names
102
- # @return [ComplexType] The generated ComplexType
144
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
103
145
  def qualify api_map, context = ''
104
- return self if name == 'param'
146
+ return self if name == GENERIC_TAG_NAME
105
147
  return ComplexType.new([self]) if duck_type? || void? || undefined?
106
148
  recon = (rooted? ? '' : context)
107
149
  fqns = api_map.qualify(name, recon)
@@ -110,13 +152,16 @@ module Solargraph
110
152
  return UniqueType::UNDEFINED
111
153
  end
112
154
  fqns = "::#{fqns}" # Ensure the resulting complex type is rooted
113
- ltypes = key_types.map { |t| t.qualify api_map, context }.uniq
114
- rtypes = value_types.map { |t| t.qualify api_map, context }.uniq
155
+ all_ltypes = key_types.map { |t| t.qualify api_map, context }.uniq
156
+ all_rtypes = value_types.map { |t| t.qualify api_map, context }
115
157
  if list_parameters?
158
+ rtypes = all_rtypes.uniq
116
159
  Solargraph::ComplexType.parse("#{fqns}<#{rtypes.map(&:tag).join(', ')}>")
117
160
  elsif fixed_parameters?
118
- Solargraph::ComplexType.parse("#{fqns}(#{rtypes.map(&:tag).join(', ')})")
161
+ Solargraph::ComplexType.parse("#{fqns}(#{all_rtypes.map(&:tag).join(', ')})")
119
162
  elsif hash_parameters?
163
+ ltypes = all_ltypes.uniq
164
+ rtypes = all_rtypes.uniq
120
165
  Solargraph::ComplexType.parse("#{fqns}{#{ltypes.map(&:tag).join(', ')} => #{rtypes.map(&:tag).join(', ')}}")
121
166
  else
122
167
  Solargraph::ComplexType.parse(fqns)
@@ -27,18 +27,27 @@ module Solargraph
27
27
  end
28
28
  @substring = substring
29
29
  @tag = @name + substring
30
+ # @type [Array<ComplexType>]
30
31
  @key_types = []
32
+ # @type [Array<ComplexType>]
31
33
  @subtypes = []
34
+ # @type [Array<ComplexType>]
32
35
  @all_params = []
33
36
  return unless parameters?
34
- if @substring.start_with?('<(') && @substring.end_with?(')>')
35
- subs = ComplexType.parse(substring[2..-3], partial: true)
36
- else
37
- subs = ComplexType.parse(substring[1..-2], partial: true)
38
- end
37
+ # @todo we should be able to probe the type of 'subs' without
38
+ # hoisting the definition outside of the if statement
39
+ subs = if @substring.start_with?('<(') && @substring.end_with?(')>')
40
+ ComplexType.parse(substring[2..-3], partial: true)
41
+ else
42
+ ComplexType.parse(substring[1..-2], partial: true)
43
+ end
39
44
  if hash_parameters?
40
45
  raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
46
+ # @todo should be able to resolve map; both types have it
47
+ # with same return type
48
+ # @sg-ignore
41
49
  @key_types.concat subs[0].map { |u| ComplexType.new([u]) }
50
+ # @sg-ignore
42
51
  @subtypes.concat subs[1].map { |u| ComplexType.new([u]) }
43
52
  else
44
53
  @subtypes.concat subs
@@ -51,73 +60,161 @@ module Solargraph
51
60
  tag
52
61
  end
53
62
 
54
- def to_rbs
55
- "#{namespace}#{parameters? ? "[#{subtypes.map { |s| s.to_rbs }.join(', ')}]" : ''}"
56
- end
57
-
58
- def parameterized?
59
- name == 'param' || all_params.any?(&:parameterized?)
63
+ # @return [Array<UniqueType>]
64
+ def items
65
+ [self]
60
66
  end
61
67
 
62
- def resolve_parameters definitions, context
63
- new_name = if name == 'param'
64
- idx = definitions.parameters.index(subtypes.first.name)
65
- return ComplexType::UNDEFINED if idx.nil?
66
- param_type = context.return_type.all_params[idx]
67
- return ComplexType::UNDEFINED unless param_type
68
- param_type.to_s
69
- else
70
- name
71
- end
72
- new_key_types = if name != 'param'
73
- @key_types.map { |t| t.resolve_parameters(definitions, context) }.select(&:defined?)
68
+ # @return [String]
69
+ def rbs_name
70
+ if name == 'undefined'
71
+ 'untyped'
74
72
  else
75
- []
73
+ rooted_name
76
74
  end
77
- new_subtypes = if name != 'param'
78
- @subtypes.map { |t| t.resolve_parameters(definitions, context) }.select(&:defined?)
75
+ end
76
+
77
+ # @return [String]
78
+ def to_rbs
79
+ if ['Tuple', 'Array'].include?(name) && fixed_parameters?
80
+ # tuples don't have a name; they're just [foo, bar, baz].
81
+ if substring == '()'
82
+ # but there are no zero element tuples, so we go with an array
83
+ 'Array[]'
84
+ else
85
+ # already generated surrounded by []
86
+ parameters_as_rbs
87
+ end
79
88
  else
80
- []
89
+ "#{rbs_name}#{parameters_as_rbs}"
81
90
  end
82
- if name != 'param' && !(new_key_types.empty? && new_subtypes.empty?)
83
- if hash_parameters?
84
- UniqueType.new(new_name, "{#{new_key_types.join(', ')} => #{new_subtypes.join(', ')}}")
85
- elsif parameters?
86
- if @substring.start_with?'<('
87
- UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
88
- else
89
- UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
91
+ end
92
+
93
+ # @return [String]
94
+ def parameters_as_rbs
95
+ parameters? ? "[#{all_params.map { |s| s.to_rbs }.join(', ')}]" : ''
96
+ end
97
+
98
+ def generic?
99
+ name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
100
+ end
101
+
102
+
103
+ # @param generics_to_resolve [Enumerable<String>]
104
+ # @param context_type [UniqueType, nil]
105
+ # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
106
+ # @return [UniqueType, ComplexType]
107
+ def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
108
+ transform(name) do |t|
109
+ next t unless t.name == ComplexType::GENERIC_TAG_NAME
110
+
111
+ new_binding = false
112
+
113
+ type_param = t.subtypes.first&.name
114
+ next t unless generics_to_resolve.include? type_param
115
+ unless context_type.nil? || !resolved_generic_values[type_param].nil?
116
+ new_binding = true
117
+ resolved_generic_values[type_param] = context_type
118
+ end
119
+ if new_binding
120
+ resolved_generic_values.transform_values! do |complex_type|
121
+ complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
90
122
  end
123
+ end
124
+ resolved_generic_values[type_param] || t
125
+ end
126
+ end
127
+
128
+ # Probe the concrete type for each of the generic type
129
+ # parameters used in this type, and return a new type if
130
+ # possible.
131
+ #
132
+ # @param definitions [Pin::Namespace, Pin::Method] The module/class/method which uses generic types
133
+ # @param context_type [ComplexType] The receiver type
134
+ # @return [UniqueType, ComplexType]
135
+ def resolve_generics definitions, context_type
136
+ return self if definitions.nil? || definitions.generics.empty?
137
+
138
+ transform(name) do |t|
139
+ if t.name == GENERIC_TAG_NAME
140
+ idx = definitions.generics.index(t.subtypes.first&.name)
141
+ next t if idx.nil?
142
+ context_type.all_params[idx] || ComplexType::UNDEFINED
91
143
  else
92
- UniqueType.new(new_name)
144
+ t
93
145
  end
94
- else
95
- UniqueType.new(new_name)
96
146
  end
147
+ end
97
148
 
98
- # idx = definitions.parameters.index(subtypes.first.name)
99
- # STDERR.puts "Index: #{idx}"
100
- # return ComplexType::UNDEFINED if idx.nil?
101
- # param_type = context.return_type.all_params[idx]
102
- # return ComplexType::UNDEFINED unless param_type
103
- # ComplexType.try_parse(param_type.to_s)
149
+ # @yieldparam t [self]
150
+ # @yieldreturn [self]
151
+ # @return [Array<self>]
152
+ def map &block
153
+ [block.yield(self)]
104
154
  end
105
155
 
106
- def self_to dst
107
- return self unless selfy?
108
- new_name = (@name == 'self' ? dst : @name)
109
- new_key_types = @key_types.map { |t| t.self_to dst }
110
- new_subtypes = @subtypes.map { |t| t.self_to dst }
111
- if hash_parameters?
156
+ # @return [Array<UniqueType>]
157
+ def to_a
158
+ [self]
159
+ end
160
+
161
+ # @param new_name [String, nil]
162
+ # @param new_key_types [Array<UniqueType>, nil]
163
+ # @param new_subtypes [Array<UniqueType>, nil]
164
+ # @return [self]
165
+ def recreate(new_name: nil, new_key_types: nil, new_subtypes: nil)
166
+ new_name ||= name
167
+ new_key_types ||= @key_types
168
+ new_subtypes ||= @subtypes
169
+ if new_key_types.none?(&:defined?) && new_subtypes.none?(&:defined?)
170
+ # if all subtypes are undefined, erase down to the non-parametric type
171
+ UniqueType.new(new_name)
172
+ elsif new_key_types.empty? && new_subtypes.empty?
173
+ UniqueType.new(new_name)
174
+ elsif hash_parameters?
112
175
  UniqueType.new(new_name, "{#{new_key_types.join(', ')} => #{new_subtypes.join(', ')}}")
113
- elsif parameters?
114
- if @substring.start_with?'<('
115
- UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
116
- else
117
- UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
118
- end
176
+ elsif @substring.start_with?('<(')
177
+ # @todo This clause is probably wrong, and if so, fixing it
178
+ # will be some level of breaking change. Probably best
179
+ # handled before real tuple support is rolled out and
180
+ # folks start relying on it more.
181
+ #
182
+ # (String) is a one element tuple in https://yardoc.org/types
183
+ # <String> is an array of zero or more Strings in https://yardoc.org/types
184
+ # Array<(String)> could be an Array of one-element tuples or a
185
+ # one element tuple. https://yardoc.org/types treats it
186
+ # as the former.
187
+ # Array<(String), Integer> is not ambiguous if we accept
188
+ # (String) as a tuple type, but not currently understood
189
+ # by Solargraph.
190
+ UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
191
+ elsif fixed_parameters?
192
+ UniqueType.new(new_name, "(#{new_subtypes.join(', ')})")
119
193
  else
120
- UniqueType.new(new_name)
194
+ UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
195
+ end
196
+ end
197
+
198
+ # Apply the given transformation to each subtype and then finally to this type
199
+ #
200
+ # @param new_name [String, nil]
201
+ # @yieldparam t [UniqueType]
202
+ # @yieldreturn [self]
203
+ # @return [self]
204
+ def transform(new_name = nil, &transform_type)
205
+ new_key_types = @key_types.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
206
+ new_subtypes = @subtypes.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
207
+ new_type = recreate(new_name: new_name || rooted_name, new_key_types: new_key_types, new_subtypes: new_subtypes)
208
+ yield new_type
209
+ end
210
+
211
+ # Transform references to the 'self' type to the specified concrete namespace
212
+ # @param dst [String]
213
+ # @return [UniqueType]
214
+ def self_to dst
215
+ transform do |t|
216
+ next t if t.name != 'self'
217
+ t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
121
218
  end
122
219
  end
123
220
 
@@ -4,50 +4,70 @@ module Solargraph
4
4
  # A container for type data based on YARD type tags.
5
5
  #
6
6
  class ComplexType
7
+ GENERIC_TAG_NAME = 'generic'.freeze
7
8
  # @!parse
8
9
  # include TypeMethods
9
10
 
10
11
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
11
12
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
12
13
 
13
- # @param types [Array<UniqueType>]
14
+ # @param types [Array<[UniqueType, ComplexType]>]
14
15
  def initialize types = [UniqueType::UNDEFINED]
15
- @items = types.uniq(&:to_s)
16
+ # @todo @items here should not need an annotation
17
+ # @type [Array<UniqueType>]
18
+ @items = types.flat_map(&:items).uniq(&:to_s)
16
19
  end
17
20
 
18
21
  # @param api_map [ApiMap]
19
22
  # @param context [String]
20
23
  # @return [ComplexType]
21
24
  def qualify api_map, context = ''
22
- types = @items.map do |t|
25
+ red = reduce_object
26
+ types = red.items.map do |t|
23
27
  next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
24
28
  t.qualify api_map, context
25
29
  end
26
- ComplexType.new(types)
30
+ ComplexType.new(types).reduce_object
27
31
  end
28
32
 
33
+ # @param generics_to_resolve [Enumerable<String>]]
34
+ # @param context_type [UniqueType, nil]
35
+ # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
36
+ # @return [self]
37
+ def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
38
+ return self unless generic?
39
+
40
+ ComplexType.new(@items.map { |i| i.resolve_generics_from_context(generics_to_resolve, context_type, resolved_generic_values: resolved_generic_values) })
41
+ end
42
+
43
+ # @return [UniqueType]
29
44
  def first
30
45
  @items.first
31
46
  end
32
47
 
48
+ # @return [String]
33
49
  def to_rbs
34
- ((@items.length > 1 ? '(' : '') + @items.map do |item|
35
- "#{item.namespace}#{item.parameters? ? "[#{item.subtypes.map { |s| s.to_rbs }.join(', ')}]" : ''}"
36
- end.join(' | ') + (@items.length > 1 ? ')' : '')).gsub(/undefined/, 'untyped')
50
+ ((@items.length > 1 ? '(' : '') +
51
+ @items.map(&:to_rbs).join(' | ') +
52
+ (@items.length > 1 ? ')' : ''))
37
53
  end
38
54
 
55
+ # @yieldparam [UniqueType]
56
+ # @return [Array]
39
57
  def map &block
40
58
  @items.map &block
41
59
  end
42
60
 
43
61
  # @yieldparam [UniqueType]
44
- # @return [Array]
62
+ # @return [Enumerable<UniqueType>]
45
63
  def each &block
46
64
  @items.each &block
47
65
  end
48
66
 
49
67
  # @yieldparam [UniqueType]
50
- # @return [Enumerator<UniqueType>]
68
+ # @return [void]
69
+ # @overload each_unique_type()
70
+ # @return [Enumerator<UniqueType>]
51
71
  def each_unique_type &block
52
72
  return enum_for(__method__) unless block_given?
53
73
 
@@ -56,28 +76,48 @@ module Solargraph
56
76
  end
57
77
  end
58
78
 
79
+ # @return [Integer]
59
80
  def length
60
81
  @items.length
61
82
  end
62
83
 
84
+ # @return [Array<UniqueType>]
85
+ def to_a
86
+ @items
87
+ end
88
+
89
+ # @param index [Integer]
90
+ # @return [UniqueType]
63
91
  def [](index)
64
92
  @items[index]
65
93
  end
66
94
 
95
+ # @return [Array<UniqueType>]
67
96
  def select &block
68
97
  @items.select &block
69
98
  end
99
+
100
+ # @return [String]
70
101
  def namespace
71
102
  # cache this attr for high frequency call
72
103
  @namespace ||= method_missing(:namespace).to_s
73
104
  end
74
105
 
106
+ # @return [Array<String>]
107
+ def namespaces
108
+ @items.map(&:namespace)
109
+ end
110
+
111
+ # @param name [Symbol]
112
+ # @return [Object, nil]
75
113
  def method_missing name, *args, &block
76
114
  return if @items.first.nil?
77
115
  return @items.first.send(name, *args, &block) if respond_to_missing?(name)
78
116
  super
79
117
  end
80
118
 
119
+ # @param name [Symbol]
120
+ # @param include_private [Boolean]
81
121
  def respond_to_missing?(name, include_private = false)
82
122
  TypeMethods.public_instance_methods.include?(name) || super
83
123
  end
@@ -98,12 +138,23 @@ module Solargraph
98
138
  @items.any?(&:selfy?)
99
139
  end
100
140
 
101
- def parameterized?
102
- any?(&:parameterized?)
141
+ def generic?
142
+ any?(&:generic?)
103
143
  end
104
144
 
105
- def resolve_parameters definitions, context
106
- result = @items.map { |i| i.resolve_parameters(definitions, context) }
145
+ # @param new_name [String, nil]
146
+ # @yieldparam t [UniqueType]
147
+ # @yieldreturn [UniqueType]
148
+ # @return [ComplexType]
149
+ def transform(new_name = nil, &transform_type)
150
+ ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) })
151
+ end
152
+
153
+ # @param definitions [Pin::Namespace, Pin::Method]
154
+ # @param context_type [ComplexType]
155
+ # @return [ComplexType]
156
+ def resolve_generics definitions, context_type
157
+ result = @items.map { |i| i.resolve_generics(definitions, context_type) }
107
158
  ComplexType.parse(*result.map(&:tag))
108
159
  end
109
160
 
@@ -113,17 +164,32 @@ module Solargraph
113
164
  return self unless selfy?
114
165
  red = reduce_class(dst)
115
166
  result = @items.map { |i| i.self_to red }
116
- ComplexType.parse(*result.map(&:to_s))
167
+ ComplexType.parse(*result.map(&:tag))
117
168
  end
118
169
 
119
170
  def nullable?
120
171
  @items.any?(&:nil_type?)
121
172
  end
122
173
 
174
+ # @return [Array<ComplexType>]
123
175
  def all_params
124
176
  @items.first.all_params || []
125
177
  end
126
178
 
179
+ attr_reader :items
180
+
181
+ protected
182
+
183
+ # @return [ComplexType]
184
+ def reduce_object
185
+ return self if name != 'Object' || subtypes.empty?
186
+ ComplexType.try_parse(reduce_class(subtypes.join(', ')))
187
+ end
188
+
189
+ def bottom?
190
+ @items.all?(&:bot?)
191
+ end
192
+
127
193
  private
128
194
 
129
195
  # @todo This is a quick and dirty hack that forces `self` keywords
@@ -153,9 +219,13 @@ module Solargraph
153
219
  # used internally.
154
220
  #
155
221
  # @param *strings [Array<String>] The type definitions to parse
156
- # @param partial [Boolean] True if the string is part of a another type
157
- # @return [ComplexType, Array, nil]
222
+ # @return [ComplexType]
223
+ # @overload parse(*strings, partial: false)
224
+ # @todo Need ability to use a literal true as a type below
225
+ # @param partial [Boolean] True if the string is part of a another type
226
+ # @return [Array<UniqueType>]
158
227
  def parse *strings, partial: false
228
+ # @type [Hash{Array<String> => ComplexType}]
159
229
  @cache ||= {}
160
230
  unless partial
161
231
  cached = @cache[strings]
@@ -181,6 +251,9 @@ module Solargraph
181
251
  raise ComplexTypeError, "Invalid hash thing" unless key_types.nil?
182
252
  # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
183
253
  types.push UniqueType.new(base[0..-2].strip)
254
+ # @todo this should either expand key_type's type
255
+ # automatically or complain about not being
256
+ # compatible with key_type's type in type checking
184
257
  key_types = types
185
258
  types = []
186
259
  base.clear
@@ -203,7 +276,7 @@ module Solargraph
203
276
  paren_stack += 1
204
277
  elsif char == ')'
205
278
  paren_stack -= 1
206
- subtype_string += char if paren_stack == 0
279
+ subtype_string += char
207
280
  raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack < 0
208
281
  next
209
282
  elsif char == ',' && point_stack == 0 && curly_stack == 0 && paren_stack == 0
@@ -250,5 +323,6 @@ module Solargraph
250
323
  NIL = ComplexType.parse('nil')
251
324
  SELF = ComplexType.parse('self')
252
325
  BOOLEAN = ComplexType.parse('Boolean')
326
+ BOT = ComplexType.parse('bot')
253
327
  end
254
328
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
 
5
4
  module Solargraph
6
5
  # Conventions provide a way to modify an ApiMap based on expectations about
@@ -9,7 +8,6 @@ module Solargraph
9
8
  module Convention
10
9
  autoload :Base, 'solargraph/convention/base'
11
10
  autoload :Gemfile, 'solargraph/convention/gemfile'
12
- autoload :Rspec, 'solargraph/convention/rspec'
13
11
  autoload :Gemspec, 'solargraph/convention/gemspec'
14
12
  autoload :Rakefile, 'solargraph/convention/rakefile'
15
13
 
@@ -43,7 +41,6 @@ module Solargraph
43
41
 
44
42
  register Gemfile
45
43
  register Gemspec
46
- register Rspec
47
44
  register Rakefile
48
45
  end
49
46
  end
@@ -1,6 +1,11 @@
1
+ require 'nokogiri'
2
+
1
3
  module ReverseMarkdown
2
4
  module Converters
3
5
  class Dd < Base
6
+ # @return [String]
7
+ # @param node [Nokogiri::XML::Element]
8
+ # @param state [Hash]
4
9
  def convert node, state = {}
5
10
  content = treat_children(node, state)
6
11
  ": #{content.strip}\n"
@@ -1,6 +1,9 @@
1
1
  module ReverseMarkdown
2
2
  module Converters
3
3
  class Dl < Base
4
+ # @return [String]
5
+ # @param node [Nokogiri::XML::Element]
6
+ # @param state [Hash]
4
7
  def convert node, state = {}
5
8
  content = treat_children(node, state).strip
6
9
  "\n\n#{content}\n"
@@ -1,6 +1,9 @@
1
1
  module ReverseMarkdown
2
2
  module Converters
3
3
  class Dt < Base
4
+ # @return [String]
5
+ # @param node [Nokogiri::XML::Element]
6
+ # @param state [Hash]
4
7
  def convert node, state = {}
5
8
  content = treat_children(node, state)
6
9
  "\n#{content.strip}\n"