solargraph 0.51.2 → 0.54.2

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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +40 -0
  3. data/.github/workflows/rspec.yml +1 -3
  4. data/.github/workflows/typecheck.yml +34 -0
  5. data/.yardopts +2 -2
  6. data/CHANGELOG.md +127 -5
  7. data/README.md +13 -16
  8. data/SPONSORS.md +1 -7
  9. data/lib/solargraph/api_map/cache.rb +50 -20
  10. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  11. data/lib/solargraph/api_map/store.rb +60 -15
  12. data/lib/solargraph/api_map.rb +282 -123
  13. data/lib/solargraph/bench.rb +3 -2
  14. data/lib/solargraph/cache.rb +29 -5
  15. data/lib/solargraph/complex_type/type_methods.rb +122 -39
  16. data/lib/solargraph/complex_type/unique_type.rb +310 -76
  17. data/lib/solargraph/complex_type.rb +166 -44
  18. data/lib/solargraph/convention.rb +0 -1
  19. data/lib/solargraph/converters/dd.rb +5 -0
  20. data/lib/solargraph/converters/dl.rb +3 -0
  21. data/lib/solargraph/converters/dt.rb +3 -0
  22. data/lib/solargraph/diagnostics/rubocop.rb +8 -7
  23. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -0
  24. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  25. data/lib/solargraph/diagnostics.rb +2 -2
  26. data/lib/solargraph/doc_map.rb +187 -0
  27. data/lib/solargraph/gem_pins.rb +72 -0
  28. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  29. data/lib/solargraph/language_server/host/dispatch.rb +22 -5
  30. data/lib/solargraph/language_server/host/message_worker.rb +49 -5
  31. data/lib/solargraph/language_server/host/sources.rb +8 -65
  32. data/lib/solargraph/language_server/host.rb +65 -84
  33. data/lib/solargraph/language_server/message/base.rb +19 -12
  34. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  35. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  36. data/lib/solargraph/language_server/message/initialize.rb +19 -2
  37. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  38. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  39. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  40. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
  41. data/lib/solargraph/language_server/message/text_document/hover.rb +3 -1
  42. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  43. data/lib/solargraph/language_server/message/text_document.rb +0 -1
  44. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  45. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  46. data/lib/solargraph/language_server/progress.rb +135 -0
  47. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  48. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  49. data/lib/solargraph/language_server.rb +1 -0
  50. data/lib/solargraph/library.rb +207 -111
  51. data/lib/solargraph/location.rb +15 -1
  52. data/lib/solargraph/page.rb +6 -0
  53. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  54. data/lib/solargraph/parser/node_methods.rb +47 -7
  55. data/lib/solargraph/parser/node_processor/base.rb +11 -1
  56. data/lib/solargraph/parser/node_processor.rb +1 -0
  57. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -9
  58. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  59. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +62 -43
  60. data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
  61. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  62. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +57 -0
  63. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  65. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +2 -2
  66. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  67. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/def_node.rb +7 -20
  68. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  69. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  71. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +4 -4
  72. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +53 -0
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sclass_node.rb +1 -1
  77. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +8 -6
  78. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/sym_node.rb +1 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
  80. data/lib/solargraph/parser/parser_gem.rb +12 -0
  81. data/lib/solargraph/parser/region.rb +1 -1
  82. data/lib/solargraph/parser/snippet.rb +2 -0
  83. data/lib/solargraph/parser.rb +8 -12
  84. data/lib/solargraph/pin/base.rb +78 -10
  85. data/lib/solargraph/pin/base_variable.rb +40 -7
  86. data/lib/solargraph/pin/block.rb +69 -46
  87. data/lib/solargraph/pin/callable.rb +147 -0
  88. data/lib/solargraph/pin/closure.rb +23 -3
  89. data/lib/solargraph/pin/common.rb +6 -6
  90. data/lib/solargraph/pin/conversions.rb +36 -5
  91. data/lib/solargraph/pin/delegated_method.rb +6 -2
  92. data/lib/solargraph/pin/documenting.rb +25 -32
  93. data/lib/solargraph/pin/instance_variable.rb +6 -2
  94. data/lib/solargraph/pin/local_variable.rb +13 -1
  95. data/lib/solargraph/pin/method.rb +205 -32
  96. data/lib/solargraph/pin/namespace.rb +20 -7
  97. data/lib/solargraph/pin/parameter.rb +41 -36
  98. data/lib/solargraph/pin/proxy_type.rb +1 -1
  99. data/lib/solargraph/pin/reference/override.rb +2 -2
  100. data/lib/solargraph/pin/reference.rb +8 -0
  101. data/lib/solargraph/pin/search.rb +3 -3
  102. data/lib/solargraph/pin/signature.rb +8 -14
  103. data/lib/solargraph/pin.rb +4 -2
  104. data/lib/solargraph/range.rb +4 -6
  105. data/lib/solargraph/rbs_map/conversions.rb +326 -76
  106. data/lib/solargraph/rbs_map/core_fills.rb +16 -33
  107. data/lib/solargraph/rbs_map/core_map.rb +3 -13
  108. data/lib/solargraph/rbs_map/stdlib_map.rb +2 -8
  109. data/lib/solargraph/rbs_map.rb +32 -13
  110. data/lib/solargraph/shell.rb +95 -72
  111. data/lib/solargraph/source/chain/array.rb +33 -0
  112. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  113. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  114. data/lib/solargraph/source/chain/call.rb +152 -69
  115. data/lib/solargraph/source/chain/constant.rb +15 -1
  116. data/lib/solargraph/source/chain/if.rb +23 -0
  117. data/lib/solargraph/source/chain/link.rb +17 -2
  118. data/lib/solargraph/source/chain/or.rb +2 -2
  119. data/lib/solargraph/source/chain/z_super.rb +3 -3
  120. data/lib/solargraph/source/chain.rb +85 -26
  121. data/lib/solargraph/source/change.rb +3 -0
  122. data/lib/solargraph/source/cursor.rb +16 -2
  123. data/lib/solargraph/source/source_chainer.rb +8 -5
  124. data/lib/solargraph/source/updater.rb +1 -0
  125. data/lib/solargraph/source.rb +120 -148
  126. data/lib/solargraph/source_map/clip.rb +16 -27
  127. data/lib/solargraph/source_map/data.rb +30 -0
  128. data/lib/solargraph/source_map/mapper.rb +15 -3
  129. data/lib/solargraph/source_map.rb +48 -24
  130. data/lib/solargraph/type_checker/checks.rb +10 -2
  131. data/lib/solargraph/type_checker/rules.rb +6 -1
  132. data/lib/solargraph/type_checker.rb +150 -39
  133. data/lib/solargraph/version.rb +1 -1
  134. data/lib/solargraph/views/environment.erb +3 -5
  135. data/lib/solargraph/workspace/config.rb +9 -6
  136. data/lib/solargraph/workspace.rb +30 -3
  137. data/lib/solargraph/yard_map/cache.rb +6 -0
  138. data/lib/solargraph/yard_map/helpers.rb +1 -1
  139. data/lib/solargraph/yard_map/mapper/to_method.rb +16 -3
  140. data/lib/solargraph/yard_map/mapper.rb +1 -1
  141. data/lib/solargraph/yard_map/to_method.rb +11 -4
  142. data/lib/solargraph/yard_map.rb +1 -292
  143. data/lib/solargraph/yard_tags.rb +20 -0
  144. data/lib/solargraph/yardoc.rb +52 -0
  145. data/lib/solargraph.rb +6 -4
  146. data/solargraph.gemspec +7 -6
  147. metadata +71 -82
  148. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  149. data/lib/solargraph/documentor.rb +0 -76
  150. data/lib/solargraph/language_server/host/cataloger.rb +0 -56
  151. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  152. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  153. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -50
  154. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  155. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +0 -18
  156. data/lib/solargraph/parser/legacy/node_processors.rb +0 -55
  157. data/lib/solargraph/parser/legacy.rb +0 -12
  158. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -153
  159. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  160. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -317
  161. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  162. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  163. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -33
  164. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  165. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +0 -75
  166. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -68
  167. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  168. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  169. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  170. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  171. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  172. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  173. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  174. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  175. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -51
  176. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -32
  177. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  178. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -279
  179. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -64
  180. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +0 -47
  181. data/lib/solargraph/parser/rubyvm.rb +0 -40
  182. data/lib/solargraph/rbs_map/core_signs.rb +0 -33
  183. data/lib/yard-solargraph.rb +0 -33
@@ -64,6 +64,10 @@ module Solargraph
64
64
  pins.select{|pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position)}.last
65
65
  end
66
66
 
67
+ # @param source_position [Position]
68
+ # @param comment_position [Position]
69
+ # @param comment [String]
70
+ # @return [void]
67
71
  def process_comment source_position, comment_position, comment
68
72
  return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
69
73
  cmnt = remove_inline_comment_hashes(comment)
@@ -79,6 +83,8 @@ module Solargraph
79
83
  end
80
84
 
81
85
  # @param comment [String]
86
+ # @param tag [String]
87
+ # @param start [Integer]
82
88
  # @return [Integer]
83
89
  def find_directive_line_number comment, tag, start
84
90
  # Avoid overruning the index
@@ -107,7 +113,8 @@ module Solargraph
107
113
  begin
108
114
  src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
109
115
  region = Parser::Region.new(source: src, closure: namespace)
110
- gen_pin = Parser.process_node(src.node, region).first.last
116
+ method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
117
+ gen_pin = method_gen_pins.last
111
118
  return if gen_pin.nil?
112
119
  # Move the location to the end of the line so it gets recognized
113
120
  # as originating from a comment
@@ -137,7 +144,7 @@ module Solargraph
137
144
  )
138
145
  end
139
146
  if t.nil? || t.include?('w')
140
- pins.push Solargraph::Pin::Method.new(
147
+ method_pin = Solargraph::Pin::Method.new(
141
148
  location: location,
142
149
  closure: namespace,
143
150
  name: "#{directive.tag.name}=",
@@ -146,7 +153,8 @@ module Solargraph
146
153
  visibility: :public,
147
154
  attribute: true
148
155
  )
149
- pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
156
+ pins.push method_pin
157
+ method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
150
158
  if pins.last.return_type.defined?
151
159
  pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
152
160
  end
@@ -203,10 +211,14 @@ module Solargraph
203
211
  end
204
212
  end
205
213
 
214
+ # @param line1 [Integer]
215
+ # @param line2 [Integer]
206
216
  def no_empty_lines?(line1, line2)
207
217
  @code.lines[line1..line2].none? { |line| line.strip.empty? }
208
218
  end
209
219
 
220
+ # @param comment [String]
221
+ # @return [String]
210
222
  def remove_inline_comment_hashes comment
211
223
  ctxt = ''
212
224
  num = nil
@@ -1,49 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
- require 'yard-solargraph'
5
- require 'set'
4
+ require 'solargraph/yard_tags'
6
5
 
7
6
  module Solargraph
8
- # An index of pins and other ApiMap-related data for a Source.
7
+ # An index of Pins and other ApiMap-related data for a single Source
8
+ # that can be queried.
9
9
  #
10
10
  class SourceMap
11
11
  autoload :Mapper, 'solargraph/source_map/mapper'
12
12
  autoload :Clip, 'solargraph/source_map/clip'
13
13
  autoload :Completion, 'solargraph/source_map/completion'
14
+ autoload :Data, 'solargraph/source_map/data'
14
15
 
15
16
  # @return [Source]
16
17
  attr_reader :source
17
18
 
18
19
  # @return [Array<Pin::Base>]
19
- attr_reader :pins
20
+ def pins
21
+ data.pins
22
+ end
20
23
 
21
- # @return [Array<Pin::Base>]
22
- attr_reader :locals
24
+ # @return [Array<Pin::LocalVariable>]
25
+ def locals
26
+ data.locals
27
+ end
23
28
 
24
29
  # @param source [Source]
25
- # @param pins [Array<Pin::Base>]
26
- # @param locals [Array<Pin::Base>]
27
- def initialize source, pins, locals
28
- # HACK: Keep the library from changing this
29
- @source = source.dup
30
- @pins = pins
31
- @locals = locals
30
+ def initialize source
31
+ @source = source
32
+
32
33
  environ.merge Convention.for_local(self) unless filename.nil?
33
34
  self.convention_pins = environ.pins
34
- @pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
35
35
  @pin_select_cache = {}
36
36
  end
37
37
 
38
+ # @param klass [Class]
39
+ # @return [Array<Pin::Base>]
38
40
  def pins_by_class klass
39
- @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
41
+ @pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten
40
42
  end
41
43
 
42
- def rebindable_method_names
43
- @rebindable_method_names ||= pins_by_class(Pin::Method)
44
- .select { |pin| pin.comments && pin.comments.include?('@yieldself') }
45
- .map(&:name)
46
- .to_set
44
+ # A hash representing the state of the source map's API.
45
+ #
46
+ # ApiMap#catalog uses this value to determine whether it needs to clear its
47
+ # cache.
48
+ #
49
+ # @return [Integer]
50
+ def api_hash
51
+ @api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select { |pin| pin.namespace.to_s > '' } + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node) + locals).hash
47
52
  end
48
53
 
49
54
  # @return [String]
@@ -99,18 +104,27 @@ module Solargraph
99
104
  (pins + locals).select { |pin| pin.location == location }
100
105
  end
101
106
 
107
+ # @param line [Integer]
108
+ # @param character [Integer]
109
+ # @return [Pin::Method,Pin::Namespace]
102
110
  def locate_named_path_pin line, character
103
111
  _locate_pin line, character, Pin::Namespace, Pin::Method
104
112
  end
105
113
 
114
+ # @param line [Integer]
115
+ # @param character [Integer]
116
+ # @return [Pin::Namespace,Pin::Method,Pin::Block]
106
117
  def locate_block_pin line, character
107
118
  _locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
108
119
  end
109
120
 
121
+ # @todo Candidate for deprecation
122
+ #
110
123
  # @param other_map [SourceMap]
111
124
  # @return [Boolean]
112
125
  def try_merge! other_map
113
126
  return false if pins.length != other_map.pins.length || locals.length != other_map.locals.length || requires.map(&:name).uniq.sort != other_map.requires.map(&:name).uniq.sort
127
+
114
128
  pins.each_index do |i|
115
129
  return false unless pins[i].try_merge!(other_map.pins[i])
116
130
  end
@@ -151,23 +165,33 @@ module Solargraph
151
165
  SourceMap.map(source)
152
166
  end
153
167
 
168
+ # @deprecated
154
169
  # @param source [Source]
155
170
  # @return [SourceMap]
156
171
  def map source
157
- result = SourceMap::Mapper.map(source)
158
- new(source, *result)
172
+ new(source)
159
173
  end
160
174
  end
161
175
 
162
176
  private
163
177
 
178
+ def pin_class_hash
179
+ @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
180
+ end
181
+
182
+ def data
183
+ @data ||= Data.new(source)
184
+ end
185
+
164
186
  # @return [Array<Pin::Base>]
165
187
  def convention_pins
166
188
  @convention_pins || []
167
189
  end
168
190
 
191
+ # @param pins [Array<Pin::Base>]
192
+ # @return [Array<Pin::Base>]
169
193
  def convention_pins=(pins)
170
- # unmemoizing the document_symbols in case it was called from any of convnetions
194
+ # unmemoizing the document_symbols in case it was called from any of conventions
171
195
  @document_symbols = nil
172
196
  @convention_pins = pins
173
197
  end
@@ -175,7 +199,7 @@ module Solargraph
175
199
  # @param line [Integer]
176
200
  # @param character [Integer]
177
201
  # @param klasses [Array<Class>]
178
- # @return [Pin::Base]
202
+ # @return [Pin::Base, nil]
179
203
  def _locate_pin line, character, *klasses
180
204
  position = Position.new(line, character)
181
205
  found = nil
@@ -51,8 +51,13 @@ module Solargraph
51
51
  # @return [Boolean]
52
52
  def any_types_match? api_map, expected, inferred
53
53
  return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
54
+ # walk through the union expected type and see if any members
55
+ # of the union match the inferred type
54
56
  expected.each do |exp|
55
57
  next if exp.duck_type?
58
+ # @todo: there should be a level of typechecking where all
59
+ # unique types in the inferred must match one of the
60
+ # expected unique types
56
61
  inferred.each do |inf|
57
62
  # return true if exp == inf || api_map.super_and_sub?(fuzz(inf), fuzz(exp))
58
63
  return true if exp == inf || either_way?(api_map, inf, exp)
@@ -103,9 +108,12 @@ module Solargraph
103
108
  # @param cls2 [ComplexType::UniqueType]
104
109
  # @return [Boolean]
105
110
  def either_way?(api_map, cls1, cls2)
106
- f1 = fuzz(cls1)
107
- f2 = fuzz(cls2)
111
+ # @todo there should be a level of typechecking which uses the
112
+ # full tag with parameters to determine compatibility
113
+ f1 = cls1.name
114
+ f2 = cls2.name
108
115
  api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)
116
+ # api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)
109
117
  end
110
118
  end
111
119
  end
@@ -9,7 +9,8 @@ module Solargraph
9
9
  normal: 0,
10
10
  typed: 1,
11
11
  strict: 2,
12
- strong: 3
12
+ strong: 3,
13
+ alpha: 4
13
14
  }.freeze
14
15
 
15
16
  # @return [Symbol]
@@ -52,6 +53,10 @@ module Solargraph
52
53
  def validate_tags?
53
54
  rank > LEVELS[:normal]
54
55
  end
56
+
57
+ def require_all_return_types_match_inferred?
58
+ rank >= LEVELS[:alpha]
59
+ end
55
60
  end
56
61
  end
57
62
  end
@@ -22,7 +22,7 @@ module Solargraph
22
22
  attr_reader :api_map
23
23
 
24
24
  # @param filename [String]
25
- # @param api_map [ApiMap]
25
+ # @param api_map [ApiMap, nil]
26
26
  # @param level [Symbol]
27
27
  def initialize filename, api_map: nil, level: :normal
28
28
  @filename = filename
@@ -51,6 +51,7 @@ module Solargraph
51
51
 
52
52
  class << self
53
53
  # @param filename [String]
54
+ # @param level [Symbol]
54
55
  # @return [self]
55
56
  def load filename, level = :normal
56
57
  source = Solargraph::Source.load(filename)
@@ -61,6 +62,7 @@ module Solargraph
61
62
 
62
63
  # @param code [String]
63
64
  # @param filename [String, nil]
65
+ # @param level [Symbol]
64
66
  # @return [self]
65
67
  def load_string code, filename = nil, level = :normal
66
68
  source = Solargraph::Source.load_string(code, filename)
@@ -88,10 +90,15 @@ module Solargraph
88
90
  def method_return_type_problems_for pin
89
91
  return [] if pin.is_a?(Pin::MethodAlias)
90
92
  result = []
91
- declared = pin.typify(api_map).self_to(pin.full_context.namespace)
93
+ declared = pin.typify(api_map).self_to_type(pin.full_context).qualify(api_map, pin.full_context.tag)
92
94
  if declared.undefined?
93
95
  if pin.return_type.undefined? && rules.require_type_tags?
94
- result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
96
+ if pin.attribute?
97
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
98
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined?
99
+ else
100
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
101
+ end
95
102
  elsif pin.return_type.defined? && !resolved_constant?(pin)
96
103
  result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
97
104
  elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
@@ -99,14 +106,14 @@ module Solargraph
99
106
  end
100
107
  elsif rules.validate_tags?
101
108
  unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
102
- inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
109
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
103
110
  if inferred.undefined?
104
111
  unless rules.ignore_all_undefined? || external?(pin)
105
112
  result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
106
113
  end
107
114
  else
108
- unless (rules.rank > 1 ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
109
- result.push Problem.new(pin.location, "Declared return type #{declared} does not match inferred type #{inferred} for #{pin.path}", pin: pin)
115
+ unless (rules.require_all_return_types_match_inferred? ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
116
+ result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin)
110
117
  end
111
118
  end
112
119
  end
@@ -121,11 +128,16 @@ module Solargraph
121
128
  # @return [Boolean]
122
129
  def resolved_constant? pin
123
130
  return true if pin.typify(api_map).defined?
124
- api_map.get_constants('', *pin.closure.gates)
125
- .select { |p| p.name == pin.return_type.namespace }
126
- .any? { |p| p.infer(api_map).defined? }
131
+ constant_pins = api_map.get_constants('', *pin.closure.gates)
132
+ .select { |p| p.name == pin.return_type.namespace }
133
+ return true if constant_pins.find { |p| p.typify(api_map).defined? }
134
+ # will need to probe when a constant name is assigned to a
135
+ # class/module (alias)
136
+ return true if constant_pins.find { |p| p.probe(api_map).defined? }
137
+ false
127
138
  end
128
139
 
140
+ # @param pin [Pin::Base]
129
141
  def virtual_pin? pin
130
142
  pin.location && source_map.source.comment_at?(pin.location.range.ending)
131
143
  end
@@ -141,12 +153,23 @@ module Solargraph
141
153
  sig.parameters.each do |par|
142
154
  break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
143
155
  unless params[par.name]
144
- result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
156
+ if pin.attribute?
157
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
158
+ if inferred.undefined?
159
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
160
+ end
161
+ else
162
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
163
+ end
145
164
  end
146
165
  end
147
166
  end
148
167
  end
168
+ # @todo Should be able to probe type of name and data here
169
+ # @param name [String]
170
+ # @param data [Hash{Symbol => BasicObject}]
149
171
  params.each_pair do |name, data|
172
+ # @type [ComplexType]
150
173
  type = data[:qualified]
151
174
  if type.undefined?
152
175
  result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
@@ -155,6 +178,7 @@ module Solargraph
155
178
  result
156
179
  end
157
180
 
181
+ # @return [Array<Pin::Base>]
158
182
  def ignored_pins
159
183
  @ignored_pins ||= []
160
184
  end
@@ -202,6 +226,7 @@ module Solargraph
202
226
  source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
203
227
  end
204
228
 
229
+ # @return [Array<Problem>]
205
230
  def const_problems
206
231
  return [] unless rules.validate_consts?
207
232
  result = []
@@ -220,6 +245,7 @@ module Solargraph
220
245
  result
221
246
  end
222
247
 
248
+ # @return [Array<Problem>]
223
249
  def call_problems
224
250
  result = []
225
251
  Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
@@ -242,8 +268,9 @@ module Solargraph
242
268
  base = base.base
243
269
  end
244
270
  closest = found.typify(api_map) if found
271
+ # @todo remove the internal_or_core? check at a higher-than-strict level
245
272
  if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
246
- unless closest.parameterized? || ignored_pins.include?(found)
273
+ unless closest.generic? || ignored_pins.include?(found)
247
274
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
248
275
  @marked_ranges.push rng
249
276
  end
@@ -259,49 +286,98 @@ module Solargraph
259
286
  # @param block_pin [Solargraph::Pin::Base]
260
287
  # @param locals [Array<Solargraph::Pin::Base>]
261
288
  # @param location [Solargraph::Location]
289
+ # @return [Array<Problem>]
262
290
  def argument_problems_for chain, api_map, block_pin, locals, location
263
291
  result = []
264
292
  base = chain
265
293
  until base.links.length == 1 && base.undefined?
294
+ last_base_link = base.links.last
295
+ break unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
296
+
297
+ arguments = last_base_link.arguments
298
+
266
299
  pins = base.define(api_map, block_pin, locals)
267
300
 
268
- if pins.first.is_a?(Pin::DelegatedMethod) && !pins.first.resolvable?(api_map)
301
+ first_pin = pins.first
302
+ if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
269
303
  # Do nothing, as we can't find the actual method implementation
270
- elsif pins.first.is_a?(Pin::Method)
304
+ elsif first_pin.is_a?(Pin::Method)
271
305
  # @type [Pin::Method]
272
- pin = pins.first
306
+ pin = first_pin
273
307
  ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
274
308
  arity_problems_for(pin, fake_args_for(block_pin), location)
309
+ elsif pin.path == 'Class#new'
310
+ fqns = if base.links.one?
311
+ block_pin.namespace
312
+ else
313
+ base.base.infer(api_map, block_pin, locals).namespace
314
+ end
315
+ init = api_map.get_method_stack(fqns, 'initialize').first
316
+ init ? arity_problems_for(init, arguments, location) : []
275
317
  else
276
- arity_problems_for(pin, base.links.last.arguments, location)
318
+ arity_problems_for(pin, arguments, location)
277
319
  end
278
320
  unless ap.empty?
279
321
  result.concat ap
280
322
  break
281
323
  end
282
- break unless rules.validate_calls?
324
+ break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
325
+
283
326
  params = first_param_hash(pins)
284
327
 
285
328
  all_errors = []
286
329
  pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
287
330
  errors = []
288
331
  sig.parameters.each_with_index do |par, idx|
289
- argchain = base.links.last.arguments[idx]
332
+ # @todo add logic mapping up restarg parameters with
333
+ # arguments (including restarg arguments). Use tuples
334
+ # when possible, and when not, ensure provably
335
+ # incorrect situations are detected.
336
+ break if par.decl == :restarg # bail out pending better arg processing
337
+ argchain = arguments[idx]
290
338
  if argchain.nil?
291
339
  if par.decl == :arg
292
- errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
293
- next
340
+ final_arg = arguments.last
341
+ if final_arg && final_arg.node.type == :splat
342
+ argchain = final_arg
343
+ next # don't try to apply the type of the splat - unlikely to be specific enough
344
+ else
345
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
346
+ next
347
+ end
294
348
  else
295
- last = base.links.last.arguments.last
296
- argchain = last if last && [:kwsplat, :hash].include?(last.node.type)
349
+ final_arg = arguments.last
350
+ argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
297
351
  end
298
352
  end
299
353
  if argchain
300
354
  if par.decl != :arg
301
- errors.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
355
+ errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
302
356
  next
303
357
  else
358
+ if argchain.node.type == :splat && argchain == arguments.last
359
+ final_arg = argchain
360
+ end
361
+ if (final_arg && final_arg.node.type == :splat)
362
+ # The final argument given has been seen and was a
363
+ # splat, which doesn't give us useful types or
364
+ # arities against positional parameters, so let's
365
+ # continue on in case there are any required
366
+ # kwargs we should warn about
367
+ next
368
+ end
369
+
370
+ if argchain.node.type == :splat && par != sig.parameters.last
371
+ # we have been given a splat and there are more
372
+ # arguments to come.
373
+
374
+ # @todo Improve this so that we can skip past the
375
+ # rest of the positional parameters here but still
376
+ # process the kwargs
377
+ break
378
+ end
304
379
  ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
380
+ ptype = ptype.self_to_type(par.context)
305
381
  if ptype.nil?
306
382
  # @todo Some level (strong, I guess) should require the param here
307
383
  else
@@ -330,10 +406,21 @@ module Solargraph
330
406
  result
331
407
  end
332
408
 
333
- def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
409
+ # @param sig [Pin::Signature]
410
+ # @param argchain [Source::Chain]
411
+ # @param api_map [ApiMap]
412
+ # @param block_pin [Pin::Block]
413
+ # @param locals [Array<Pin::LocalVariable>]
414
+ # @param location [Location]
415
+ # @param pin [Pin::Method]
416
+ # @param params [Hash{String => [nil, Hash]}]
417
+ # @param idx [Integer]
418
+ #
419
+ # @return [Array<Problem>]
420
+ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
334
421
  result = []
335
422
  kwargs = convert_hash(argchain.node)
336
- par = pin.signatures.first.parameters[idx]
423
+ par = sig.parameters[idx]
337
424
  argchain = kwargs[par.name.to_sym]
338
425
  if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
339
426
  result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
@@ -358,6 +445,14 @@ module Solargraph
358
445
  result
359
446
  end
360
447
 
448
+ # @param api_map [ApiMap]
449
+ # @param block_pin [Pin::Block]
450
+ # @param locals [Array<Pin::LocalVariable>]
451
+ # @param location [Location]
452
+ # @param pin [Pin::Method]
453
+ # @param params [Hash{String => [nil, Hash]}]
454
+ # @param kwargs [Hash{Symbol => Source::Chain}]
455
+ # @return [Array<Problem>]
361
456
  def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
362
457
  result = []
363
458
  kwargs.each_pair do |pname, argchain|
@@ -371,8 +466,8 @@ module Solargraph
371
466
  result
372
467
  end
373
468
 
374
- # @param [Pin::Method]
375
- # @return [Hash]
469
+ # @param pin [Pin::Method]
470
+ # @return [Hash{String => Hash{Symbol => BaseObject}}]
376
471
  def param_hash(pin)
377
472
  tags = pin.docstring.tags(:param)
378
473
  return {} if tags.empty?
@@ -387,10 +482,12 @@ module Solargraph
387
482
  result
388
483
  end
389
484
 
390
- # @param [Array<Pin::Method>]
391
- # @return [Hash]
485
+ # @param pins [Array<Pin::Method>]
486
+ # @return [Hash{String => Hash{Symbol => BasicObject}}]
392
487
  def first_param_hash(pins)
393
488
  pins.each do |pin|
489
+ # @todo this assignment from parametric use of Hash should not lose its generic
490
+ # @type [Hash{String => Hash{Symbol => BasicObject}}]
394
491
  result = param_hash(pin)
395
492
  return result unless result.empty?
396
493
  end
@@ -404,6 +501,7 @@ module Solargraph
404
501
  end
405
502
 
406
503
  # True if the pin is either internal (part of the workspace) or from the core/stdlib
504
+ # @param pin [Pin::Base]
407
505
  def internal_or_core? pin
408
506
  # @todo RBS pins are not necessarily core/stdlib pins
409
507
  internal?(pin) || pin.source == :rbs
@@ -414,6 +512,7 @@ module Solargraph
414
512
  !internal? pin
415
513
  end
416
514
 
515
+ # @param pin [Pin::Base]
417
516
  def declared_externally? pin
418
517
  return true if pin.assignment.nil?
419
518
  chain = Solargraph::Parser.chain(pin.assignment, filename)
@@ -441,6 +540,10 @@ module Solargraph
441
540
  true
442
541
  end
443
542
 
543
+ # @param pin [Pin::Method]
544
+ # @param arguments [Array<Source::Chain>]
545
+ # @param location [Location]
546
+ # @return [Array<Problem>]
444
547
  def arity_problems_for pin, arguments, location
445
548
  results = pin.signatures.map do |sig|
446
549
  r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
@@ -450,16 +553,15 @@ module Solargraph
450
553
  results.first
451
554
  end
452
555
 
556
+ # @param pin [Pin::Method]
557
+ # @param parameters [Array<Pin::Parameter>]
558
+ # @param arguments [Array<Source::Chain>]
559
+ # @param location [Location]
560
+ # @return [Array<Problem>]
453
561
  def parameterized_arity_problems_for(pin, parameters, arguments, location)
454
562
  return [] unless pin.explicit?
455
563
  return [] if parameters.empty? && arguments.empty?
456
564
  return [] if pin.anon_splat?
457
- if parameters.empty?
458
- # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
459
- return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
460
- return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
461
- return [Problem.new(location, "Too many arguments to #{pin.path}")]
462
- end
463
565
  unchecked = arguments.clone
464
566
  add_params = 0
465
567
  if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
@@ -499,9 +601,6 @@ module Solargraph
499
601
  return [] if parameters.any?(&:rest?)
500
602
  opt = optional_param_count(parameters)
501
603
  return [] if unchecked.length <= req + opt
502
- if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
503
- return []
504
- end
505
604
  if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
506
605
  return []
507
606
  end
@@ -517,20 +616,30 @@ module Solargraph
517
616
  []
518
617
  end
519
618
 
619
+ # @param parameters [Enumerable<Pin::Parameter>]
620
+ # @todo need to use generic types in method to choose correct
621
+ # signature and generate Integer as return type
622
+ # @sg-ignore
623
+ # @return [Integer]
520
624
  def required_param_count(parameters)
521
625
  parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
522
626
  end
523
627
 
628
+ # @param parameters [Enumerable<Pin::Parameter>]
524
629
  # @param pin [Pin::Method]
630
+ # @return [Integer]
525
631
  def optional_param_count(parameters)
526
632
  parameters.select { |p| p.decl == :optarg }.length
527
633
  end
528
634
 
635
+ # @param pin [Pin::Method]
529
636
  def abstract? pin
530
- pin.docstring.has_tag?(:abstract) ||
531
- (pin.closure && pin.closure.docstring.has_tag?(:abstract))
637
+ pin.docstring.has_tag?('abstract') ||
638
+ (pin.closure && pin.closure.docstring.has_tag?('abstract'))
532
639
  end
533
640
 
641
+ # @param pin [Pin::Base]
642
+ # @return [Array<Source::Chain>]
534
643
  def fake_args_for(pin)
535
644
  args = []
536
645
  with_opts = false
@@ -551,6 +660,8 @@ module Solargraph
551
660
  args
552
661
  end
553
662
 
663
+ # @param problems [Array<Problem>]
664
+ # @return [Array<Problem>]
554
665
  def without_ignored problems
555
666
  problems.reject do |problem|
556
667
  node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.51.2'
4
+ VERSION = '0.54.2'
5
5
  end