solargraph 0.54.4 → 0.56.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +3 -1
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +62 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/index.rb +24 -16
  8. data/lib/solargraph/api_map/store.rb +48 -23
  9. data/lib/solargraph/api_map.rb +175 -77
  10. data/lib/solargraph/bench.rb +17 -1
  11. data/lib/solargraph/complex_type/type_methods.rb +6 -1
  12. data/lib/solargraph/complex_type/unique_type.rb +98 -9
  13. data/lib/solargraph/complex_type.rb +35 -6
  14. data/lib/solargraph/convention/base.rb +3 -3
  15. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
  16. data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
  17. data/lib/solargraph/convention/data_definition.rb +104 -0
  18. data/lib/solargraph/convention/gemspec.rb +2 -1
  19. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  20. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  21. data/lib/solargraph/convention/struct_definition.rb +141 -0
  22. data/lib/solargraph/convention.rb +5 -3
  23. data/lib/solargraph/doc_map.rb +277 -57
  24. data/lib/solargraph/gem_pins.rb +53 -37
  25. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  26. data/lib/solargraph/language_server/host.rb +12 -2
  27. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  28. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  29. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  30. data/lib/solargraph/library.rb +45 -17
  31. data/lib/solargraph/location.rb +21 -0
  32. data/lib/solargraph/logging.rb +1 -0
  33. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  34. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  35. data/lib/solargraph/parser/node_methods.rb +14 -0
  36. data/lib/solargraph/parser/node_processor/base.rb +9 -4
  37. data/lib/solargraph/parser/node_processor.rb +21 -8
  38. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  39. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  40. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  41. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  44. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  46. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  47. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  48. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  49. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  51. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  52. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  53. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  57. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  58. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  59. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  60. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  61. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  63. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  64. data/lib/solargraph/parser/region.rb +1 -1
  65. data/lib/solargraph/parser.rb +1 -0
  66. data/lib/solargraph/pin/base.rb +316 -28
  67. data/lib/solargraph/pin/base_variable.rb +16 -9
  68. data/lib/solargraph/pin/block.rb +2 -0
  69. data/lib/solargraph/pin/breakable.rb +9 -0
  70. data/lib/solargraph/pin/callable.rb +74 -3
  71. data/lib/solargraph/pin/closure.rb +18 -1
  72. data/lib/solargraph/pin/common.rb +5 -0
  73. data/lib/solargraph/pin/delegated_method.rb +20 -1
  74. data/lib/solargraph/pin/documenting.rb +16 -0
  75. data/lib/solargraph/pin/keyword.rb +7 -2
  76. data/lib/solargraph/pin/local_variable.rb +15 -6
  77. data/lib/solargraph/pin/method.rb +169 -43
  78. data/lib/solargraph/pin/namespace.rb +17 -9
  79. data/lib/solargraph/pin/parameter.rb +60 -11
  80. data/lib/solargraph/pin/proxy_type.rb +12 -6
  81. data/lib/solargraph/pin/reference/override.rb +10 -6
  82. data/lib/solargraph/pin/reference/require.rb +2 -2
  83. data/lib/solargraph/pin/signature.rb +42 -0
  84. data/lib/solargraph/pin/singleton.rb +1 -1
  85. data/lib/solargraph/pin/symbol.rb +3 -2
  86. data/lib/solargraph/pin/until.rb +18 -0
  87. data/lib/solargraph/pin/while.rb +18 -0
  88. data/lib/solargraph/pin.rb +4 -1
  89. data/lib/solargraph/pin_cache.rb +185 -0
  90. data/lib/solargraph/position.rb +9 -0
  91. data/lib/solargraph/range.rb +9 -0
  92. data/lib/solargraph/rbs_map/conversions.rb +221 -67
  93. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  94. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  95. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  96. data/lib/solargraph/rbs_map.rb +74 -17
  97. data/lib/solargraph/shell.rb +17 -18
  98. data/lib/solargraph/source/chain/array.rb +11 -7
  99. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  100. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  101. data/lib/solargraph/source/chain/call.rb +53 -23
  102. data/lib/solargraph/source/chain/constant.rb +1 -1
  103. data/lib/solargraph/source/chain/hash.rb +4 -3
  104. data/lib/solargraph/source/chain/head.rb +1 -1
  105. data/lib/solargraph/source/chain/if.rb +1 -1
  106. data/lib/solargraph/source/chain/link.rb +2 -0
  107. data/lib/solargraph/source/chain/literal.rb +22 -2
  108. data/lib/solargraph/source/chain/or.rb +1 -1
  109. data/lib/solargraph/source/chain/z_super.rb +1 -1
  110. data/lib/solargraph/source/chain.rb +78 -48
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source_map/clip.rb +3 -1
  113. data/lib/solargraph/source_map/mapper.rb +9 -5
  114. data/lib/solargraph/source_map.rb +0 -17
  115. data/lib/solargraph/type_checker/checks.rb +4 -0
  116. data/lib/solargraph/type_checker.rb +35 -8
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/views/_method.erb +10 -10
  119. data/lib/solargraph/views/_namespace.erb +3 -3
  120. data/lib/solargraph/views/document.erb +10 -10
  121. data/lib/solargraph/workspace/config.rb +1 -1
  122. data/lib/solargraph/workspace.rb +23 -5
  123. data/lib/solargraph/yard_map/helpers.rb +29 -1
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  127. data/lib/solargraph/yard_map/mapper.rb +4 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +7 -8
  130. data/lib/solargraph.rb +32 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +2 -1
  134. metadata +37 -9
  135. data/lib/solargraph/cache.rb +0 -77
@@ -15,7 +15,7 @@ module Solargraph
15
15
  # @param with_block [Boolean] True if the chain is inside a block
16
16
  # @param head [Boolean] True if the call is the start of its chain
17
17
  def initialize word, with_block = false
18
- super(word, [], with_block)
18
+ super(word, nil, [], with_block)
19
19
  end
20
20
 
21
21
  # @param api_map [ApiMap]
@@ -39,7 +39,7 @@ module Solargraph
39
39
  @@inference_invalidation_key = nil
40
40
  @@inference_cache = {}
41
41
 
42
- UNDEFINED_CALL = Chain::Call.new('<undefined>')
42
+ UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
43
43
  UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
44
44
 
45
45
  # @return [::Array<Source::Chain::Link>]
@@ -75,16 +75,27 @@ module Solargraph
75
75
 
76
76
  # Determine potential Pins returned by this chain of words
77
77
  #
78
- # @param api_map [ApiMap]
79
- # @param name_pin [Pin::Closure] the surrounding closure pin for
80
- # the statement represented by this chain for type resolution
81
- # and method pin lookup.
78
+ # @param api_map [ApiMap] @param name_pin [Pin::Base] A pin
79
+ # representing the place in which expression is evaluated (e.g.,
80
+ # a Method pin, or a Module or Class pin if not run within a
81
+ # method - both in terms of the closure around the chain, as well
82
+ # as the self type used for any method calls in head position.
83
+ #
84
+ # Requirements for name_pin:
85
+ #
86
+ # * name_pin.context: This should be a type representing the
87
+ # namespace where we can look up non-local variables and
88
+ # method names. If it is a Class<X>, we will look up
89
+ # :class scoped methods/variables.
82
90
  #
83
- # For method calls (Chain::Call objects) as the first element
84
- # in the chain, 'name_pin.binder' should return the
85
- # ComplexType representing the LHS / "self type" of the call.
91
+ # * name_pin.binder: Used for method call lookups only
92
+ # (Chain::Call links). For method calls as the first
93
+ # element in the chain, 'name_pin.binder' should be the
94
+ # same as name_pin.context above. For method calls later
95
+ # in the chain (e.g., 'b' in a.b.c), it should represent
96
+ # 'a'.
86
97
  #
87
- # @param locals [::Enumerable<Pin::LocalVariable>] Any local
98
+ # @param locals [::Array<Pin::LocalVariable>] Any local
88
99
  # variables / method parameters etc visible by the statement
89
100
  #
90
101
  # @return [::Array<Pin::Base>] Pins representing possible return
@@ -101,16 +112,25 @@ module Solargraph
101
112
  working_pin = name_pin
102
113
  links[0..-2].each do |link|
103
114
  pins = link.resolve(api_map, working_pin, locals)
104
- type = infer_first_defined(pins, working_pin, api_map, locals)
105
- return [] if type.undefined?
106
- working_pin = Pin::ProxyType.anonymous(type)
115
+ type = infer_from_definitions(pins, working_pin, api_map, locals)
116
+ if type.undefined?
117
+ logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" }
118
+ return []
119
+ end
120
+ # We continue to use the context from the head pin, in case
121
+ # we need it to, for instance, provide context for a block
122
+ # evaluation. However, we use the last link's return type
123
+ # for the binder, as this is chaining off of it, and the
124
+ # binder is now the lhs of the rhs we are evaluating.
125
+ working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain)
126
+ logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" }
107
127
  end
108
128
  links.last.last_context = working_pin
109
129
  links.last.resolve(api_map, working_pin, locals)
110
130
  end
111
131
 
112
132
  # @param api_map [ApiMap]
113
- # @param name_pin [Pin::Base]
133
+ # @param name_pin [Pin::Base] The pin for the closure in which this code runs
114
134
  # @param locals [::Array<Pin::LocalVariable>]
115
135
  # @return [ComplexType]
116
136
  # @sg-ignore
@@ -123,7 +143,7 @@ module Solargraph
123
143
  @@inference_invalidation_key = api_map.hash
124
144
  @@inference_cache = {}
125
145
  end
126
- out = infer_uncached api_map, name_pin, locals
146
+ out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
127
147
  logger.debug { "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" }
128
148
  @@inference_cache[cache_key] = out
129
149
  end
@@ -134,8 +154,14 @@ module Solargraph
134
154
  # @return [ComplexType]
135
155
  def infer_uncached api_map, name_pin, locals
136
156
  pins = define(api_map, name_pin, locals)
137
- type = infer_first_defined(pins, links.last.last_context, api_map, locals)
138
- maybe_nil(type)
157
+ if pins.empty?
158
+ logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" }
159
+ return ComplexType::UNDEFINED
160
+ end
161
+ type = infer_from_definitions(pins, links.last.last_context, api_map, locals)
162
+ out = maybe_nil(type)
163
+ logger.debug { "Chain#infer_uncached(links=#{self.links.map(&:desc)}, locals=#{locals.map(&:desc)}, name_pin=#{name_pin}, name_pin.closure=#{name_pin.closure.inspect}, name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" }
164
+ out
139
165
  end
140
166
 
141
167
  # @return [Boolean]
@@ -174,6 +200,8 @@ module Solargraph
174
200
  desc
175
201
  end
176
202
 
203
+ include Logging
204
+
177
205
  private
178
206
 
179
207
  # @param pins [::Array<Pin::Base>]
@@ -181,8 +209,10 @@ module Solargraph
181
209
  # @param api_map [ApiMap]
182
210
  # @param locals [::Enumerable<Pin::LocalVariable>]
183
211
  # @return [ComplexType]
184
- def infer_first_defined pins, context, api_map, locals
185
- possibles = []
212
+ def infer_from_definitions pins, context, api_map, locals
213
+ # @type [::Array<ComplexType>]
214
+ types = []
215
+ unresolved_pins = []
186
216
  # @todo this param tag shouldn't be needed to probe the type
187
217
  # @todo ...but given it is needed, typecheck should complain that it is needed
188
218
  # @param pin [Pin::Base]
@@ -200,41 +230,41 @@ module Solargraph
200
230
  # that accepts only [Pin::Namespace] as an argument
201
231
  type = type.resolve_generics(pin.closure, context.binder)
202
232
  end
203
- if type.defined?
204
- possibles.push type
205
- break if pin.is_a?(Pin::Method)
206
- end
233
+ types << type
234
+ else
235
+ unresolved_pins << pin
207
236
  end
208
237
  end
209
- if possibles.empty?
210
- # Limit method inference recursion
211
- return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
212
-
213
- @@inference_depth += 1
214
- # @param pin [Pin::Base]
215
- pins.each do |pin|
216
- # Avoid infinite recursion
217
- next if @@inference_stack.include?(pin)
218
-
219
- @@inference_stack.push pin
220
- type = pin.probe(api_map)
221
- @@inference_stack.pop
222
- if type.defined?
223
- possibles.push type
224
- break if pin.is_a?(Pin::Method)
225
- end
226
- end
227
- @@inference_depth -= 1
238
+
239
+ # Limit method inference recursion
240
+ if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
241
+ return ComplexType::UNDEFINED
228
242
  end
229
- return ComplexType::UNDEFINED if possibles.empty?
230
243
 
231
- type = if possibles.length > 1
232
- # Move nil to the end by convention
233
- sorted = possibles.sort { |a, _| a.tag == 'nil' ? 1 : 0 }
234
- ComplexType.new(sorted.uniq)
235
- else
236
- ComplexType.new(possibles)
244
+ @@inference_depth += 1
245
+ # @param pin [Pin::Base]
246
+ unresolved_pins.each do |pin|
247
+ # Avoid infinite recursion
248
+ if @@inference_stack.include?(pin.identity)
249
+ next
250
+ end
251
+
252
+ @@inference_stack.push(pin.identity)
253
+ type = pin.probe(api_map)
254
+ @@inference_stack.pop
255
+ types.push type if type
237
256
  end
257
+ @@inference_depth -= 1
258
+
259
+ type = if types.empty?
260
+ ComplexType::UNDEFINED
261
+ elsif types.length > 1
262
+ # Move nil to the end by convention
263
+ sorted = types.flat_map(&:items).sort { |a, _| a.tag == 'nil' ? 1 : 0 }
264
+ ComplexType.new(sorted.uniq)
265
+ else
266
+ ComplexType.new(types)
267
+ end
238
268
  return type if context.nil? || context.return_type.undefined?
239
269
 
240
270
  type.self_to_type(context.return_type)
@@ -32,8 +32,8 @@ module Solargraph
32
32
  # @return [Source::Chain]
33
33
  def chain
34
34
  # Special handling for files that end with an integer and a period
35
- return Chain.new([Chain::Literal.new('Integer'), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
36
- return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
35
+ return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
36
+ return Chain.new([Chain::Literal.new('Symbol', phrase[1..].to_sym)]) if phrase.start_with?(':') && !phrase.start_with?('::')
37
37
  return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i)
38
38
  begin
39
39
  return Chain.new([]) if phrase.end_with?('..')
@@ -11,7 +11,9 @@ module Solargraph
11
11
  def initialize api_map, cursor
12
12
  @api_map = api_map
13
13
  @cursor = cursor
14
- block.rebind(api_map) if block.is_a?(Pin::Block)
14
+ block_pin = block
15
+ block_pin.rebind(api_map) if block_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(block_pin.receiver).contain?(cursor.range.start)
16
+ @in_block = nil
15
17
  end
16
18
 
17
19
  # @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
@@ -41,7 +41,7 @@ module Solargraph
41
41
  s = Position.new(0, 0)
42
42
  e = Position.from_offset(code, code.length)
43
43
  location = Location.new(filename, Range.new(s, e))
44
- [[Pin::Namespace.new(location: location, name: '')], []]
44
+ [[Pin::Namespace.new(location: location, name: '', source: :source_map)], []]
45
45
  end
46
46
 
47
47
  class << self
@@ -55,6 +55,7 @@ module Solargraph
55
55
 
56
56
  # @return [Array<Solargraph::Pin::Base>]
57
57
  def pins
58
+ # @type [Array<Solargraph::Pin::Base>]
58
59
  @pins ||= []
59
60
  end
60
61
 
@@ -140,7 +141,8 @@ module Solargraph
140
141
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
141
142
  visibility: :public,
142
143
  explicit: false,
143
- attribute: true
144
+ attribute: true,
145
+ source: :source_map
144
146
  )
145
147
  end
146
148
  if t.nil? || t.include?('w')
@@ -151,10 +153,11 @@ module Solargraph
151
153
  comments: docstring.all.to_s,
152
154
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
153
155
  visibility: :public,
154
- attribute: true
156
+ attribute: true,
157
+ source: :source_map
155
158
  )
156
159
  pins.push method_pin
157
- method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
160
+ method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map)
158
161
  if pins.last.return_type.defined?
159
162
  pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
160
163
  end
@@ -205,7 +208,8 @@ module Solargraph
205
208
  namespace = closure_at(source_position) || Pin::ROOT_PIN
206
209
  namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
207
210
  when 'override'
208
- pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags)
211
+ pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
212
+ source: :source_map)
209
213
  when 'macro'
210
214
  # @todo Handle macros
211
215
  end
@@ -118,23 +118,6 @@ module Solargraph
118
118
  _locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
119
119
  end
120
120
 
121
- # @todo Candidate for deprecation
122
- #
123
- # @param other_map [SourceMap]
124
- # @return [Boolean]
125
- def try_merge! other_map
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
-
128
- pins.each_index do |i|
129
- return false unless pins[i].try_merge!(other_map.pins[i])
130
- end
131
- locals.each_index do |i|
132
- return false unless locals[i].try_merge!(other_map.locals[i])
133
- end
134
- @source = other_map.source
135
- true
136
- end
137
-
138
121
  # @param name [String]
139
122
  # @return [Array<Location>]
140
123
  def references name
@@ -50,6 +50,8 @@ module Solargraph
50
50
  # @param inferred [ComplexType]
51
51
  # @return [Boolean]
52
52
  def any_types_match? api_map, expected, inferred
53
+ expected = expected.downcast_to_literal_if_possible
54
+ inferred = inferred.downcast_to_literal_if_possible
53
55
  return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
54
56
  # walk through the union expected type and see if any members
55
57
  # of the union match the inferred type
@@ -71,6 +73,8 @@ module Solargraph
71
73
  # @param expected [ComplexType]
72
74
  # @return [Boolean]
73
75
  def all_types_match? api_map, inferred, expected
76
+ expected = expected.downcast_to_literal_if_possible
77
+ inferred = inferred.downcast_to_literal_if_possible
74
78
  return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
75
79
  inferred.each do |inf|
76
80
  next if inf.duck_type?
@@ -470,12 +470,22 @@ module Solargraph
470
470
  # @param pin [Pin::Method]
471
471
  # @return [Hash{String => Hash{Symbol => String, ComplexType}}]
472
472
  def param_hash(pin)
473
- tags = pin.docstring.tags(:param)
474
- return {} if tags.empty?
475
473
  # @type [Hash{String => Hash{Symbol => String, ComplexType}}]
476
474
  result = {}
475
+ pin.parameters.each do |param|
476
+ type = param.typify(api_map)
477
+ next if type.nil? || type.undefined?
478
+ result[param.name.to_s] = {
479
+ tagged: type.tags,
480
+ qualified: type
481
+ }
482
+ end
483
+ # see if we have additional tags to pay attention to from YARD -
484
+ # e.g., kwargs in a **restkwargs splat
485
+ tags = pin.docstring.tags(:param)
477
486
  tags.each do |tag|
478
- next if tag.types.nil? || tag.types.empty?
487
+ next if result.key? tag.name.to_s
488
+ next if tag.types.nil?
479
489
  result[tag.name.to_s] = {
480
490
  tagged: tag.types.join(', '),
481
491
  qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
@@ -487,11 +497,27 @@ module Solargraph
487
497
  # @param pins [Array<Pin::Method>]
488
498
  # @return [Hash{String => Hash{Symbol => String, ComplexType}}]
489
499
  def first_param_hash(pins)
490
- pins.each do |pin|
491
- result = param_hash(pin)
492
- return result unless result.empty?
500
+ return {} if pins.empty?
501
+ first_pin_type = pins.first.typify(api_map)
502
+ first_pin = pins.first.proxy first_pin_type
503
+ param_names = first_pin.parameter_names
504
+ results = param_hash(first_pin)
505
+ pins[1..].each do |pin|
506
+ # @todo this assignment from parametric use of Hash should not lose its generic
507
+ # @type [Hash{String => Hash{Symbol => BasicObject}}]
508
+
509
+ # documentation of types in superclasses should fail back to
510
+ # subclasses if the subclass hasn't documented something
511
+ superclass_results = param_hash(pin)
512
+ superclass_results.each do |param_name, details|
513
+ next unless param_names.include?(param_name)
514
+
515
+ results[param_name] ||= {}
516
+ results[param_name][:tagged] ||= details[:tagged]
517
+ results[param_name][:qualified] ||= details[:qualified]
518
+ end
493
519
  end
494
- {}
520
+ results
495
521
  end
496
522
 
497
523
  # @param pin [Pin::Base]
@@ -584,7 +610,8 @@ module Solargraph
584
610
  kwargs.delete param.name.to_sym
585
611
  settled_kwargs += 1
586
612
  elsif param.decl == :kwarg
587
- return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
613
+ last_arg_last_link = arguments.last.links.last
614
+ return [] if last_arg_last_link.is_a?(Solargraph::Source::Chain::Hash) && last_arg_last_link.splatted?
588
615
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
589
616
  end
590
617
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.54.4'
4
+ VERSION = '0.56.2'
5
5
  end
@@ -2,33 +2,33 @@
2
2
  Namespace:
3
3
  </h2>
4
4
  <p>
5
- <a href="solargraph:/document?query=<%= CGI.escape object.namespace.path %>"><%= object.namespace %></a>
5
+ <a href="solargraph:/document?query=<%= CGI.escape pin.namespace.path %>"><%= pin.namespace %></a>
6
6
  </p>
7
7
  <h2>
8
8
  Overview:
9
9
  </h2>
10
- <%= htmlify object.docstring %>
10
+ <%= htmlify pin.docstring %>
11
11
  <p class="document-section">
12
- <big><strong>Visibility:</strong></big> <%= object.visibility %>
12
+ <big><strong>Visibility:</strong></big> <%= pin.visibility %>
13
13
  </p>
14
- <% unless object.tags(:param).empty? %>
14
+ <% unless pin.docstring.tags(:param).empty? %>
15
15
  <h2>
16
16
  Parameters:
17
17
  </h2>
18
18
  <ul>
19
- <% object.tags(:param).each do |tag| %>
19
+ <% pin.docstring.tags(:param).each do |tag| %>
20
20
  <li>
21
21
  <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
22
22
  </li>
23
23
  <% end %>
24
24
  </ul>
25
25
  <% end %>
26
- <% unless object.tags(:raise).empty? %>
26
+ <% unless pin.docstring.tags(:raise).empty? %>
27
27
  <h2>
28
28
  Raises:
29
29
  </h2>
30
30
  <ul>
31
- <% object.tags(:raise).each do |tag| %>
31
+ <% pin.docstring.tags(:raise).each do |tag| %>
32
32
  <li>
33
33
  <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
34
34
  </li>
@@ -38,20 +38,20 @@
38
38
  <h2>
39
39
  Returns:
40
40
  </h2>
41
- <% if object.tag(:return).nil? %>
41
+ <% if pin.docstring.tag(:return).nil? %>
42
42
  <p>
43
43
  Undefined/unknown
44
44
  </p>
45
45
  <% else %>
46
46
  <ul>
47
- <% object.tags(:return).each do |tag| %>
47
+ <% pin.tags(:return).each do |tag| %>
48
48
  <li>
49
49
  <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
50
50
  </li>
51
51
  <% end %>
52
52
  </ul>
53
53
  <% end %>
54
- <% examples = object.tags(:example) %>
54
+ <% examples = pin.docstring.tags(:example) %>
55
55
  <% unless examples.nil? %>
56
56
  <% examples.each do |example| %>
57
57
  <h2>
@@ -1,12 +1,12 @@
1
1
  <h2>
2
2
  Overview:
3
3
  </h2>
4
- <%= htmlify object.docstring %>
4
+ <%= htmlify pin.docstring %>
5
5
  <h2>
6
6
  Class Methods
7
7
  </h2>
8
8
  <ul class="doc-list">
9
- <% object.meths(scope: :class).sort{|a, b| a.name <=> b.name}.each do |meth| %>
9
+ <% api_map.get_methods(pin.path, scope: :class, deep: false).sort{|a, b| a.name <=> b.name}.each do |meth| %>
10
10
  <li>
11
11
  <a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
12
12
  </li>
@@ -16,7 +16,7 @@
16
16
  Instance Methods
17
17
  </h2>
18
18
  <ul class="doc-list">
19
- <% object.meths(scope: :instance).sort{|a, b| a.name <=> b.name}.each do |meth| %>
19
+ <% api_map.get_methods(pin.path, scope: :instance, deep: false).sort{|a, b| a.name <=> b.name}.each do |meth| %>
20
20
  <li>
21
21
  <a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
22
22
  </li>
@@ -1,23 +1,23 @@
1
- <% objects.reverse.each do |object| %>
1
+ <% pins.each do |pin| %>
2
2
  <h1>
3
- <%= object.name %>
4
- <% if object.is_a?(YARD::CodeObjects::MethodObject) and !object.parameters.empty? %>
5
- <small>(<%= object.parameters.map {|p| "#{p[0]}#{p[1] and p[0].end_with?(':') ? ' ' : (p[1] ? ' = ' : '')}#{p[1]}"}.join(', ') %>)</small>
3
+ <%= pin.name %>
4
+ <% if pin.is_a?(Solargraph::Pin::Method) && !pin.parameters.empty? %>
5
+ <small>(<%= pin.parameters.map {|p| "#{p[0]}#{p[1] and p[0].end_with?(':') ? ' ' : (p[1] ? ' = ' : '')}#{p[1]}"}.join(', ') %>)</small>
6
6
  <% end %>
7
7
  </h1>
8
- <% unless object.files.empty? %>
8
+ <% unless pins.map(&:location).compact.empty? %>
9
9
  <h2>
10
10
  Defined in:
11
11
  </h2>
12
12
  <ul>
13
- <% object.files.each do |f| %>
13
+ <% pins.map(&:location).compact.map(&:filename).each do |f| %>
14
14
  <li><%= f %></li>
15
15
  <% end %>
16
16
  </ul>
17
17
  <% end %>
18
- <% if object.is_a?(YARD::CodeObjects::NamespaceObject) %>
19
- <%= erb :_namespace, layout: false, locals: {object: object} %>
20
- <% elsif object.is_a?(YARD::CodeObjects::MethodObject) %>
21
- <%= erb :_method, layout: false, locals: {object: object} %>
18
+ <% if pin.is_a?(Solargraph::Pin::Namespace) %>
19
+ <%= erb :_namespace, layout: false, locals: {api_map: api_map, pin: pin} %>
20
+ <% elsif pin.is_a?(Solargraph::Pin::Method) %>
21
+ <%= erb :_method, layout: false, locals: {api_map: api_map, pin: pin} %>
22
22
  <% end %>
23
23
  <% end %>
@@ -151,7 +151,7 @@ module Solargraph
151
151
  # @return [Hash{String => Array, Hash, Integer}]
152
152
  def default_config
153
153
  {
154
- 'include' => ['**/*.rb'],
154
+ 'include' => ['Rakefile', 'Gemfile', '*.gemspec', '**/*.rb'],
155
155
  'exclude' => ['spec/**/*', 'test/**/*', 'vendor/**/*', '.bundle/**/*'],
156
156
  'require' => [],
157
157
  'domains' => [],
@@ -106,7 +106,7 @@ module Solargraph
106
106
  def would_require? path
107
107
  require_paths.each do |rp|
108
108
  full = File.join rp, path
109
- return true if File.exist?(full) or File.exist?(full << ".rb")
109
+ return true if File.file?(full) || File.file?(full << ".rb")
110
110
  end
111
111
  false
112
112
  end
@@ -133,6 +133,15 @@ module Solargraph
133
133
  @gem_rbs_collection ||= read_rbs_collection_path
134
134
  end
135
135
 
136
+ def rbs_collection_config_path
137
+ @rbs_collection_config_path ||= begin
138
+ unless directory.empty? || directory == '*'
139
+ yaml_file = File.join(directory, 'rbs_collection.yaml')
140
+ yaml_file if File.file?(yaml_file)
141
+ end
142
+ end
143
+ end
144
+
136
145
  # Synchronize the workspace from the provided updater.
137
146
  #
138
147
  # @param updater [Source::Updater]
@@ -146,6 +155,14 @@ module Solargraph
146
155
  server['commandPath'] || 'solargraph'
147
156
  end
148
157
 
158
+ # True if the workspace has a root Gemfile.
159
+ #
160
+ # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler)
161
+ #
162
+ def gemfile?
163
+ directory && File.file?(File.join(directory, 'Gemfile'))
164
+ end
165
+
149
166
  private
150
167
 
151
168
  # The language server configuration (or an empty hash if the workspace was
@@ -214,7 +231,7 @@ module Solargraph
214
231
  def configured_require_paths
215
232
  return ['lib'] if directory.empty?
216
233
  return [File.join(directory, 'lib')] if config.require_paths.empty?
217
- config.require_paths.map{|p| File.join(directory, p)}
234
+ config.require_paths.map { |p| File.join(directory, p) }
218
235
  end
219
236
 
220
237
  # @return [void]
@@ -230,10 +247,11 @@ module Solargraph
230
247
 
231
248
  # @return [String, nil]
232
249
  def read_rbs_collection_path
233
- yaml_file = File.join(directory, 'rbs_collection.yaml')
234
- return unless File.file?(yaml_file)
250
+ return unless rbs_collection_config_path
235
251
 
236
- YAML.load_file(yaml_file)&.fetch('path')
252
+ path = YAML.load_file(rbs_collection_config_path)&.fetch('path')
253
+ # make fully qualified
254
+ File.expand_path(path, directory)
237
255
  end
238
256
  end
239
257
  end
@@ -7,10 +7,38 @@ module Solargraph
7
7
  # @param spec [Gem::Specification, nil]
8
8
  # @return [Solargraph::Location, nil]
9
9
  def object_location code_object, spec
10
- return nil if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
10
+ if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
11
+ if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
12
+ # If the code object is a namespace, use the namespace's location
13
+ return object_location(code_object.namespace, spec)
14
+ end
15
+ return Solargraph::Location.new(__FILE__, Solargraph::Range.from_to(__LINE__ - 1, 0, __LINE__ - 1, 0))
16
+ end
11
17
  file = File.join(spec.full_gem_path, code_object.file)
12
18
  Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0))
13
19
  end
20
+
21
+ # @param code_object [YARD::CodeObjects::Base]
22
+ # @param spec [Gem::Specification, nil]
23
+ # @return [Solargraph::Pin::Namespace]
24
+ def create_closure_namespace_for(code_object, spec)
25
+ code_object_for_location = code_object
26
+ # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new")
27
+ code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
28
+ namespace_location = object_location(code_object_for_location, spec)
29
+ ns_name = code_object.namespace.to_s
30
+ if ns_name.empty?
31
+ Solargraph::Pin::ROOT_PIN
32
+ else
33
+ Solargraph::Pin::Namespace.new(
34
+ name: ns_name,
35
+ closure: Pin::ROOT_PIN,
36
+ gates: [code_object.namespace.to_s],
37
+ source: :yardoc,
38
+ location: namespace_location
39
+ )
40
+ end
41
+ end
14
42
  end
15
43
  end
16
44
  end
@@ -7,17 +7,19 @@ module Solargraph
7
7
  extend YardMap::Helpers
8
8
 
9
9
  # @param code_object [YARD::CodeObjects::Base]
10
+ # @param closure [Pin::Closure, nil]
11
+ # @param spec [Gem::Specification, nil]
12
+ # @return [Pin::Constant]
10
13
  def self.make code_object, closure = nil, spec = nil
11
- closure ||= Solargraph::Pin::Namespace.new(
12
- name: code_object.namespace.to_s,
13
- gates: [code_object.namespace.to_s]
14
- )
14
+ closure ||= create_closure_namespace_for(code_object, spec)
15
+
15
16
  Pin::Constant.new(
16
17
  location: object_location(code_object, spec),
17
18
  closure: closure,
18
19
  name: code_object.name.to_s,
19
20
  comments: code_object.docstring ? code_object.docstring.all.to_s : '',
20
- visibility: code_object.visibility
21
+ visibility: code_object.visibility,
22
+ source: :yardoc
21
23
  )
22
24
  end
23
25
  end