solargraph 0.54.4 → 0.57.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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +125 -0
  3. data/.github/workflows/plugins.yml +149 -5
  4. data/.github/workflows/rspec.yml +39 -4
  5. data/.github/workflows/typecheck.yml +8 -3
  6. data/.gitignore +7 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +2627 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +104 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/lib/solargraph/api_map/cache.rb +3 -2
  16. data/lib/solargraph/api_map/constants.rb +218 -0
  17. data/lib/solargraph/api_map/index.rb +44 -42
  18. data/lib/solargraph/api_map/source_to_yard.rb +10 -4
  19. data/lib/solargraph/api_map/store.rb +165 -32
  20. data/lib/solargraph/api_map.rb +319 -243
  21. data/lib/solargraph/bench.rb +18 -1
  22. data/lib/solargraph/complex_type/type_methods.rb +7 -1
  23. data/lib/solargraph/complex_type/unique_type.rb +105 -16
  24. data/lib/solargraph/complex_type.rb +40 -7
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  26. data/lib/solargraph/convention/base.rb +20 -3
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  29. data/lib/solargraph/convention/data_definition.rb +105 -0
  30. data/lib/solargraph/convention/gemspec.rb +3 -2
  31. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  32. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  33. data/lib/solargraph/convention/struct_definition.rb +164 -0
  34. data/lib/solargraph/convention.rb +35 -4
  35. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  36. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  37. data/lib/solargraph/doc_map.rb +313 -65
  38. data/lib/solargraph/environ.rb +9 -2
  39. data/lib/solargraph/gem_pins.rb +60 -38
  40. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  41. data/lib/solargraph/language_server/host/message_worker.rb +13 -7
  42. data/lib/solargraph/language_server/host.rb +14 -3
  43. data/lib/solargraph/language_server/message/base.rb +2 -1
  44. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  45. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  46. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -2
  49. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  50. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  51. data/lib/solargraph/language_server/progress.rb +8 -0
  52. data/lib/solargraph/language_server/request.rb +1 -0
  53. data/lib/solargraph/library.rb +53 -32
  54. data/lib/solargraph/location.rb +23 -0
  55. data/lib/solargraph/logging.rb +12 -2
  56. data/lib/solargraph/page.rb +4 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  59. data/lib/solargraph/parser/node_methods.rb +16 -2
  60. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  61. data/lib/solargraph/parser/node_processor.rb +26 -9
  62. data/lib/solargraph/parser/parser_gem/class_methods.rb +17 -15
  63. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  64. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +8 -4
  66. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  70. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  73. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  74. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  75. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  76. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  80. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  81. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  83. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  84. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +63 -30
  85. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  86. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  89. data/lib/solargraph/parser/region.rb +4 -1
  90. data/lib/solargraph/parser/snippet.rb +2 -0
  91. data/lib/solargraph/parser.rb +1 -0
  92. data/lib/solargraph/pin/base.rb +360 -30
  93. data/lib/solargraph/pin/base_variable.rb +16 -10
  94. data/lib/solargraph/pin/block.rb +2 -0
  95. data/lib/solargraph/pin/breakable.rb +9 -0
  96. data/lib/solargraph/pin/callable.rb +83 -3
  97. data/lib/solargraph/pin/closure.rb +20 -1
  98. data/lib/solargraph/pin/common.rb +10 -1
  99. data/lib/solargraph/pin/constant.rb +2 -0
  100. data/lib/solargraph/pin/delegated_method.rb +21 -1
  101. data/lib/solargraph/pin/documenting.rb +16 -0
  102. data/lib/solargraph/pin/keyword.rb +7 -2
  103. data/lib/solargraph/pin/local_variable.rb +18 -6
  104. data/lib/solargraph/pin/method.rb +175 -46
  105. data/lib/solargraph/pin/method_alias.rb +3 -0
  106. data/lib/solargraph/pin/namespace.rb +17 -9
  107. data/lib/solargraph/pin/parameter.rb +78 -19
  108. data/lib/solargraph/pin/proxy_type.rb +13 -6
  109. data/lib/solargraph/pin/reference/override.rb +24 -6
  110. data/lib/solargraph/pin/reference/require.rb +2 -2
  111. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  112. data/lib/solargraph/pin/reference.rb +26 -0
  113. data/lib/solargraph/pin/search.rb +3 -1
  114. data/lib/solargraph/pin/signature.rb +44 -0
  115. data/lib/solargraph/pin/singleton.rb +1 -1
  116. data/lib/solargraph/pin/symbol.rb +8 -2
  117. data/lib/solargraph/pin/until.rb +18 -0
  118. data/lib/solargraph/pin/while.rb +18 -0
  119. data/lib/solargraph/pin.rb +4 -1
  120. data/lib/solargraph/pin_cache.rb +245 -0
  121. data/lib/solargraph/position.rb +11 -0
  122. data/lib/solargraph/range.rb +10 -0
  123. data/lib/solargraph/rbs_map/conversions.rb +226 -70
  124. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  125. data/lib/solargraph/rbs_map/core_map.rb +37 -11
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  127. data/lib/solargraph/rbs_map.rb +88 -18
  128. data/lib/solargraph/shell.rb +20 -18
  129. data/lib/solargraph/source/chain/array.rb +11 -7
  130. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  131. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  132. data/lib/solargraph/source/chain/call.rb +53 -23
  133. data/lib/solargraph/source/chain/constant.rb +1 -1
  134. data/lib/solargraph/source/chain/hash.rb +4 -3
  135. data/lib/solargraph/source/chain/head.rb +1 -1
  136. data/lib/solargraph/source/chain/if.rb +1 -1
  137. data/lib/solargraph/source/chain/link.rb +12 -1
  138. data/lib/solargraph/source/chain/literal.rb +22 -2
  139. data/lib/solargraph/source/chain/or.rb +1 -1
  140. data/lib/solargraph/source/chain/z_super.rb +1 -1
  141. data/lib/solargraph/source/chain.rb +84 -47
  142. data/lib/solargraph/source/change.rb +2 -2
  143. data/lib/solargraph/source/cursor.rb +2 -3
  144. data/lib/solargraph/source/source_chainer.rb +3 -3
  145. data/lib/solargraph/source.rb +5 -2
  146. data/lib/solargraph/source_map/clip.rb +4 -2
  147. data/lib/solargraph/source_map/data.rb +4 -0
  148. data/lib/solargraph/source_map/mapper.rb +13 -7
  149. data/lib/solargraph/source_map.rb +21 -31
  150. data/lib/solargraph/type_checker/checks.rb +4 -0
  151. data/lib/solargraph/type_checker/param_def.rb +2 -0
  152. data/lib/solargraph/type_checker/rules.rb +8 -0
  153. data/lib/solargraph/type_checker.rb +208 -128
  154. data/lib/solargraph/version.rb +1 -1
  155. data/lib/solargraph/views/_method.erb +10 -10
  156. data/lib/solargraph/views/_namespace.erb +3 -3
  157. data/lib/solargraph/views/document.erb +10 -10
  158. data/lib/solargraph/workspace/config.rb +1 -3
  159. data/lib/solargraph/workspace/require_paths.rb +98 -0
  160. data/lib/solargraph/workspace.rb +38 -52
  161. data/lib/solargraph/yard_map/helpers.rb +29 -1
  162. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  163. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  164. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  165. data/lib/solargraph/yard_map/mapper.rb +4 -3
  166. data/lib/solargraph/yard_map/to_method.rb +4 -2
  167. data/lib/solargraph/yardoc.rb +22 -10
  168. data/lib/solargraph.rb +34 -1
  169. data/rbs/fills/tuple.rbs +149 -0
  170. data/rbs_collection.yaml +19 -0
  171. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  172. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  173. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  174. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  175. data/solargraph.gemspec +15 -4
  176. metadata +157 -15
  177. data/lib/.rubocop.yml +0 -22
  178. data/lib/solargraph/cache.rb +0 -77
@@ -17,7 +17,13 @@ module Solargraph
17
17
  @word = word
18
18
  end
19
19
 
20
- # @sg-ignore Fix "Not enough arguments to Module#protected"
20
+ # @sg-ignore two problems - Declared return type
21
+ # ::Solargraph::Source::Chain::Array does not match inferred
22
+ # type ::Array(::Class<::Solargraph::Source::Chain::Link>,
23
+ # ::String) for
24
+ # Solargraph::Source::Chain::Link#equality_fields
25
+ # and
26
+ # Not enough arguments to Module#protected
21
27
  protected def equality_fields
22
28
  [self.class, word]
23
29
  end
@@ -39,6 +45,7 @@ module Solargraph
39
45
  end
40
46
 
41
47
  # debugging description of contents; not for machine use
48
+ # @return [String]
42
49
  def desc
43
50
  word
44
51
  end
@@ -74,6 +81,8 @@ module Solargraph
74
81
  end
75
82
 
76
83
  # debugging description of contents; not for machine use
84
+ #
85
+ # @return [String]
77
86
  def desc
78
87
  word
79
88
  end
@@ -82,6 +91,8 @@ module Solargraph
82
91
  "#<#{self.class} - `#{self.desc}`>"
83
92
  end
84
93
 
94
+ include Logging
95
+
85
96
  protected
86
97
 
87
98
  # Mark whether this link is the head of a chain
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'parser'
4
+
3
5
  module Solargraph
4
6
  class Source
5
7
  class Chain
@@ -8,9 +10,22 @@ module Solargraph
8
10
  @word ||= "<#{@type}>"
9
11
  end
10
12
 
13
+ attr_reader :value
14
+
11
15
  # @param type [String]
12
- def initialize type
16
+ # @param node [Parser::AST::Node, Object]
17
+ def initialize type, node
18
+ if node.is_a?(::Parser::AST::Node)
19
+ if node.type == :true
20
+ @value = true
21
+ elsif node.type == :false
22
+ @value = false
23
+ elsif [:int, :sym].include?(node.type)
24
+ @value = node.children.first
25
+ end
26
+ end
13
27
  @type = type
28
+ @literal_type = ComplexType.try_parse(@value.inspect)
14
29
  @complex_type = ComplexType.try_parse(type)
15
30
  end
16
31
 
@@ -20,7 +35,12 @@ module Solargraph
20
35
  end
21
36
 
22
37
  def resolve api_map, name_pin, locals
23
- [Pin::ProxyType.anonymous(@complex_type)]
38
+ if api_map.super_and_sub?(@complex_type.name, @literal_type.name)
39
+ [Pin::ProxyType.anonymous(@literal_type, source: :chain)]
40
+ else
41
+ # we don't support this value as a literal type
42
+ [Pin::ProxyType.anonymous(@complex_type, source: :chain)]
43
+ end
24
44
  end
25
45
  end
26
46
  end
@@ -15,7 +15,7 @@ module Solargraph
15
15
 
16
16
  def resolve api_map, name_pin, locals
17
17
  types = @links.map { |link| link.infer(api_map, name_pin, locals) }
18
- [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq))]
18
+ [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq), source: :chain)]
19
19
  end
20
20
  end
21
21
  end
@@ -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]
@@ -15,6 +15,7 @@ module Solargraph
15
15
  #
16
16
  class Chain
17
17
  include Equality
18
+ include Logging
18
19
 
19
20
  autoload :Link, 'solargraph/source/chain/link'
20
21
  autoload :Call, 'solargraph/source/chain/call'
@@ -39,7 +40,7 @@ module Solargraph
39
40
  @@inference_invalidation_key = nil
40
41
  @@inference_cache = {}
41
42
 
42
- UNDEFINED_CALL = Chain::Call.new('<undefined>')
43
+ UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
43
44
  UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
44
45
 
45
46
  # @return [::Array<Source::Chain::Link>]
@@ -76,15 +77,28 @@ module Solargraph
76
77
  # Determine potential Pins returned by this chain of words
77
78
  #
78
79
  # @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.
82
80
  #
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.
81
+ # @param name_pin [Pin::Base] A pin
82
+ # representing the place in which expression is evaluated (e.g.,
83
+ # a Method pin, or a Module or Class pin if not run within a
84
+ # method - both in terms of the closure around the chain, as well
85
+ # as the self type used for any method calls in head position.
86
+ #
87
+ # Requirements for name_pin:
88
+ #
89
+ # * name_pin.context: This should be a type representing the
90
+ # namespace where we can look up non-local variables and
91
+ # method names. If it is a Class<X>, we will look up
92
+ # :class scoped methods/variables.
86
93
  #
87
- # @param locals [::Enumerable<Pin::LocalVariable>] Any local
94
+ # * name_pin.binder: Used for method call lookups only
95
+ # (Chain::Call links). For method calls as the first
96
+ # element in the chain, 'name_pin.binder' should be the
97
+ # same as name_pin.context above. For method calls later
98
+ # in the chain (e.g., 'b' in a.b.c), it should represent
99
+ # 'a'.
100
+ #
101
+ # @param locals [::Array<Pin::LocalVariable>] Any local
88
102
  # variables / method parameters etc visible by the statement
89
103
  #
90
104
  # @return [::Array<Pin::Base>] Pins representing possible return
@@ -101,16 +115,25 @@ module Solargraph
101
115
  working_pin = name_pin
102
116
  links[0..-2].each do |link|
103
117
  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)
118
+ type = infer_from_definitions(pins, working_pin, api_map, locals)
119
+ if type.undefined?
120
+ logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" }
121
+ return []
122
+ end
123
+ # We continue to use the context from the head pin, in case
124
+ # we need it to, for instance, provide context for a block
125
+ # evaluation. However, we use the last link's return type
126
+ # for the binder, as this is chaining off of it, and the
127
+ # binder is now the lhs of the rhs we are evaluating.
128
+ working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain)
129
+ 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
130
  end
108
131
  links.last.last_context = working_pin
109
132
  links.last.resolve(api_map, working_pin, locals)
110
133
  end
111
134
 
112
135
  # @param api_map [ApiMap]
113
- # @param name_pin [Pin::Base]
136
+ # @param name_pin [Pin::Base] The pin for the closure in which this code runs
114
137
  # @param locals [::Array<Pin::LocalVariable>]
115
138
  # @return [ComplexType]
116
139
  # @sg-ignore
@@ -123,7 +146,7 @@ module Solargraph
123
146
  @@inference_invalidation_key = api_map.hash
124
147
  @@inference_cache = {}
125
148
  end
126
- out = infer_uncached api_map, name_pin, locals
149
+ out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
127
150
  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
151
  @@inference_cache[cache_key] = out
129
152
  end
@@ -134,8 +157,14 @@ module Solargraph
134
157
  # @return [ComplexType]
135
158
  def infer_uncached api_map, name_pin, locals
136
159
  pins = define(api_map, name_pin, locals)
137
- type = infer_first_defined(pins, links.last.last_context, api_map, locals)
138
- maybe_nil(type)
160
+ if pins.empty?
161
+ logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" }
162
+ return ComplexType::UNDEFINED
163
+ end
164
+ type = infer_from_definitions(pins, links.last.last_context, api_map, locals)
165
+ out = maybe_nil(type)
166
+ 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}" }
167
+ out
139
168
  end
140
169
 
141
170
  # @return [Boolean]
@@ -166,6 +195,7 @@ module Solargraph
166
195
 
167
196
  include Logging
168
197
 
198
+ # @return [String]
169
199
  def desc
170
200
  links.map(&:desc).to_s
171
201
  end
@@ -174,6 +204,8 @@ module Solargraph
174
204
  desc
175
205
  end
176
206
 
207
+ include Logging
208
+
177
209
  private
178
210
 
179
211
  # @param pins [::Array<Pin::Base>]
@@ -181,8 +213,10 @@ module Solargraph
181
213
  # @param api_map [ApiMap]
182
214
  # @param locals [::Enumerable<Pin::LocalVariable>]
183
215
  # @return [ComplexType]
184
- def infer_first_defined pins, context, api_map, locals
185
- possibles = []
216
+ def infer_from_definitions pins, context, api_map, locals
217
+ # @type [::Array<ComplexType>]
218
+ types = []
219
+ unresolved_pins = []
186
220
  # @todo this param tag shouldn't be needed to probe the type
187
221
  # @todo ...but given it is needed, typecheck should complain that it is needed
188
222
  # @param pin [Pin::Base]
@@ -200,42 +234,45 @@ module Solargraph
200
234
  # that accepts only [Pin::Namespace] as an argument
201
235
  type = type.resolve_generics(pin.closure, context.binder)
202
236
  end
203
- if type.defined?
204
- possibles.push type
205
- break if pin.is_a?(Pin::Method)
206
- end
237
+ types << type
238
+ else
239
+ unresolved_pins << pin
207
240
  end
208
241
  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
242
+
243
+ # Limit method inference recursion
244
+ if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
245
+ return ComplexType::UNDEFINED
246
+ end
247
+
248
+ @@inference_depth += 1
249
+ # @param pin [Pin::Base]
250
+ unresolved_pins.each do |pin|
251
+ # Avoid infinite recursion
252
+ if @@inference_stack.include?(pin.identity)
253
+ next
226
254
  end
227
- @@inference_depth -= 1
255
+
256
+ @@inference_stack.push(pin.identity)
257
+ type = pin.probe(api_map)
258
+ @@inference_stack.pop
259
+ types.push type if type
228
260
  end
229
- return ComplexType::UNDEFINED if possibles.empty?
261
+ @@inference_depth -= 1
230
262
 
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)
263
+ type = if types.empty?
264
+ ComplexType::UNDEFINED
265
+ elsif types.length > 1
266
+ # Move nil to the end by convention
267
+ sorted = types.flat_map(&:items).sort { |a, _| a.tag == 'nil' ? 1 : 0 }
268
+ ComplexType.new(sorted.uniq)
269
+ else
270
+ ComplexType.new(types)
271
+ end
272
+ if context.nil? || context.return_type.undefined?
273
+ # up to downstream to resolve self type
274
+ return type
237
275
  end
238
- return type if context.nil? || context.return_type.undefined?
239
276
 
240
277
  type.self_to_type(context.return_type)
241
278
  end
@@ -28,7 +28,7 @@ module Solargraph
28
28
  # syntax errors will be repaired.
29
29
  # @return [String] The updated text.
30
30
  def write text, nullable = false
31
- if nullable and !range.nil? and new_text.match(/[\.\[\{\(@\$:]$/)
31
+ if nullable and !range.nil? and new_text.match(/[.\[{(@$:]$/)
32
32
  [':', '@'].each do |dupable|
33
33
  next unless new_text == dupable
34
34
  offset = Position.to_offset(text, range.start)
@@ -59,7 +59,7 @@ module Solargraph
59
59
  else
60
60
  result = commit text, fixed
61
61
  off = Position.to_offset(text, range.start)
62
- match = result[0, off].match(/[\.:]+\z/)
62
+ match = result[0, off].match(/[.:]+\z/)
63
63
  if match
64
64
  result = result[0, off].sub(/#{match[0]}\z/, ' ' * match[0].length) + result[off..-1]
65
65
  end
@@ -35,7 +35,6 @@ module Solargraph
35
35
  # The part of the word before the current position. Given the text
36
36
  # `foo.bar`, the start_of_word at position(0, 6) is `ba`.
37
37
  #
38
- # @sg-ignore Improve resolution of String#match below
39
38
  # @return [String]
40
39
  def start_of_word
41
40
  @start_of_word ||= begin
@@ -125,7 +124,7 @@ module Solargraph
125
124
  def node_position
126
125
  @node_position ||= begin
127
126
  if start_of_word.empty?
128
- match = source.code[0, offset].match(/[\s]*(\.|:+)[\s]*$/)
127
+ match = source.code[0, offset].match(/\s*(\.|:+)\s*$/)
129
128
  if match
130
129
  Position.from_offset(source.code, offset - match[0].length)
131
130
  else
@@ -160,7 +159,7 @@ module Solargraph
160
159
  #
161
160
  # @return [Regexp]
162
161
  def end_word_pattern
163
- /^([a-z0-9_]|[^\u0000-\u007F])*[\?\!]?/i
162
+ /^([a-z0-9_]|[^\u0000-\u007F])*[?!]?/i
164
163
  end
165
164
  end
166
165
  end
@@ -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?('..')
@@ -97,7 +97,7 @@ module Solargraph
97
97
  # @return [String]
98
98
  def end_of_phrase
99
99
  @end_of_phrase ||= begin
100
- match = phrase.match(/[\s]*(\.{1}|::)[\s]*$/)
100
+ match = phrase.match(/\s*(\.{1}|::)\s*$/)
101
101
  if match
102
102
  match[0]
103
103
  else
@@ -30,7 +30,7 @@ module Solargraph
30
30
  @node
31
31
  end
32
32
 
33
- # @return [Hash{Integer => Array<String>}]
33
+ # @return [Hash{Integer => Solargraph::Parser::Snippet}]
34
34
  def comments
35
35
  finalize
36
36
  @comments
@@ -235,6 +235,7 @@ module Solargraph
235
235
  # @return [Hash{Integer => String}]
236
236
  def associated_comments
237
237
  @associated_comments ||= begin
238
+ # @type [Hash{Integer => String}]
238
239
  result = {}
239
240
  buffer = String.new('')
240
241
  # @type [Integer, nil]
@@ -317,7 +318,7 @@ module Solargraph
317
318
  @string_nodes ||= string_nodes_in(node)
318
319
  end
319
320
 
320
- # @return [Array<Range>]
321
+ # @return [Array<Solargraph::Range>]
321
322
  def comment_ranges
322
323
  @comment_ranges ||= comments.values.map(&:range)
323
324
  end
@@ -386,6 +387,7 @@ module Solargraph
386
387
  # @return [Integer]
387
388
  attr_writer :version
388
389
 
390
+ # @return [void]
389
391
  def finalize
390
392
  return if @finalized && changes.empty?
391
393
 
@@ -440,6 +442,7 @@ module Solargraph
440
442
  # @return [String]
441
443
  attr_writer :repaired
442
444
 
445
+ # @return [String]
443
446
  def repaired
444
447
  finalize
445
448
  @repaired
@@ -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
@@ -63,7 +65,7 @@ module Solargraph
63
65
  # position. Locals can be local variables, method parameters, or block
64
66
  # parameters. The array starts with the nearest local pin.
65
67
  #
66
- # @return [::Array<Solargraph::Pin::Base>]
68
+ # @return [::Array<Solargraph::Pin::LocalVariable>]
67
69
  def locals
68
70
  @locals ||= source_map.locals_at(location)
69
71
  end
@@ -3,15 +3,18 @@
3
3
  module Solargraph
4
4
  class SourceMap
5
5
  class Data
6
+ # @param source [Solargraph::Source]
6
7
  def initialize source
7
8
  @source = source
8
9
  end
9
10
 
11
+ # @return [Array<Solargraph::Pin::Base>]
10
12
  def pins
11
13
  generate
12
14
  @pins || []
13
15
  end
14
16
 
17
+ # @return [Array<Solargraph::LocalVariable>]
15
18
  def locals
16
19
  generate
17
20
  @locals || []
@@ -19,6 +22,7 @@ module Solargraph
19
22
 
20
23
  private
21
24
 
25
+ # @return [void]
22
26
  def generate
23
27
  return if @generated
24
28
 
@@ -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
 
@@ -69,6 +70,7 @@ module Solargraph
69
70
  # @param comment [String]
70
71
  # @return [void]
71
72
  def process_comment source_position, comment_position, comment
73
+ # @sg-ignore Wrong argument type for String#=~: object expected String::_MatchAgainst<String, undefined>, received Regexp
72
74
  return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
73
75
  cmnt = remove_inline_comment_hashes(comment)
74
76
  parse = Solargraph::Source.parse_docstring(cmnt)
@@ -140,7 +142,8 @@ module Solargraph
140
142
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
141
143
  visibility: :public,
142
144
  explicit: false,
143
- attribute: true
145
+ attribute: true,
146
+ source: :source_map
144
147
  )
145
148
  end
146
149
  if t.nil? || t.include?('w')
@@ -151,16 +154,17 @@ module Solargraph
151
154
  comments: docstring.all.to_s,
152
155
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
153
156
  visibility: :public,
154
- attribute: true
157
+ attribute: true,
158
+ source: :source_map
155
159
  )
156
160
  pins.push method_pin
157
- method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
161
+ method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map)
158
162
  if pins.last.return_type.defined?
159
163
  pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
160
164
  end
161
165
  end
162
166
  when 'visibility'
163
- begin
167
+
164
168
  kind = directive.tag.text&.to_sym
165
169
  return unless [:private, :protected, :public].include?(kind)
166
170
 
@@ -179,7 +183,7 @@ module Solargraph
179
183
  pin.instance_variable_set(:@visibility, kind)
180
184
  end
181
185
  end
182
- end
186
+
183
187
  when 'parse'
184
188
  begin
185
189
  ns = closure_at(source_position)
@@ -205,7 +209,8 @@ module Solargraph
205
209
  namespace = closure_at(source_position) || Pin::ROOT_PIN
206
210
  namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
207
211
  when 'override'
208
- pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags)
212
+ pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
213
+ source: :source_map)
209
214
  when 'macro'
210
215
  # @todo Handle macros
211
216
  end
@@ -240,6 +245,7 @@ module Solargraph
240
245
 
241
246
  # @return [void]
242
247
  def process_comment_directives
248
+ # @sg-ignore Wrong argument type for String#=~: object expected String::_MatchAgainst<String, undefined>, received Regexp
243
249
  return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
244
250
  code_lines = @code.lines
245
251
  @source.associated_comments.each do |line, comments|
@@ -21,6 +21,11 @@ module Solargraph
21
21
  data.pins
22
22
  end
23
23
 
24
+ # @return [Array<Pin::Base>]
25
+ def all_pins
26
+ pins + convention_pins
27
+ end
28
+
24
29
  # @return [Array<Pin::LocalVariable>]
25
30
  def locals
26
31
  data.locals
@@ -30,13 +35,18 @@ module Solargraph
30
35
  def initialize source
31
36
  @source = source
32
37
 
33
- environ.merge Convention.for_local(self) unless filename.nil?
34
- self.convention_pins = environ.pins
38
+ conventions_environ.merge Convention.for_local(self) unless filename.nil?
39
+ # FIXME: unmemoizing the document_symbols in case it was called and memoized from any of conventions above
40
+ # this is to ensure that the convention_pins from all conventions are used in the document_symbols.
41
+ # solargraph-rails is known to use this method to get the document symbols. It should probably be removed.
42
+ @document_symbols = nil
43
+ self.convention_pins = conventions_environ.pins
35
44
  @pin_select_cache = {}
36
45
  end
37
46
 
38
- # @param klass [Class]
39
- # @return [Array<Pin::Base>]
47
+ # @generic T
48
+ # @param klass [Class<generic<T>>]
49
+ # @return [Array<generic<T>>]
40
50
  def pins_by_class klass
41
51
  @pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten
42
52
  end
@@ -67,8 +77,8 @@ module Solargraph
67
77
  end
68
78
 
69
79
  # @return [Environ]
70
- def environ
71
- @environ ||= Environ.new
80
+ def conventions_environ
81
+ @conventions_environ ||= Environ.new
72
82
  end
73
83
 
74
84
  # all pins except Solargraph::Pin::Reference::Reference
@@ -118,23 +128,6 @@ module Solargraph
118
128
  _locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
119
129
  end
120
130
 
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
131
  # @param name [String]
139
132
  # @return [Array<Location>]
140
133
  def references name
@@ -175,10 +168,15 @@ module Solargraph
175
168
 
176
169
  private
177
170
 
171
+ # @return [Hash{Class => Array<Pin::Base>}]
172
+ # @return [Array<Pin::Base>]
173
+ attr_writer :convention_pins
174
+
178
175
  def pin_class_hash
179
176
  @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
180
177
  end
181
178
 
179
+ # @return [Data]
182
180
  def data
183
181
  @data ||= Data.new(source)
184
182
  end
@@ -188,14 +186,6 @@ module Solargraph
188
186
  @convention_pins || []
189
187
  end
190
188
 
191
- # @param pins [Array<Pin::Base>]
192
- # @return [Array<Pin::Base>]
193
- def convention_pins=(pins)
194
- # unmemoizing the document_symbols in case it was called from any of conventions
195
- @document_symbols = nil
196
- @convention_pins = pins
197
- end
198
-
199
189
  # @param line [Integer]
200
190
  # @param character [Integer]
201
191
  # @param klasses [Array<Class>]
@@ -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?