solargraph 0.58.3 → 0.60.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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +12 -5
  4. data/.github/workflows/plugins.yml +54 -34
  5. data/.github/workflows/rspec.yml +18 -28
  6. data/.github/workflows/typecheck.yml +6 -3
  7. data/.rubocop.yml +39 -6
  8. data/.rubocop_todo.yml +53 -966
  9. data/CHANGELOG.md +35 -0
  10. data/Gemfile +6 -1
  11. data/README.md +3 -3
  12. data/Rakefile +26 -23
  13. data/bin/solargraph +2 -1
  14. data/lib/solargraph/api_map/cache.rb +3 -3
  15. data/lib/solargraph/api_map/constants.rb +12 -3
  16. data/lib/solargraph/api_map/index.rb +42 -20
  17. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  18. data/lib/solargraph/api_map/store.rb +61 -36
  19. data/lib/solargraph/api_map.rb +192 -78
  20. data/lib/solargraph/bench.rb +2 -3
  21. data/lib/solargraph/complex_type/conformance.rb +176 -0
  22. data/lib/solargraph/complex_type/type_methods.rb +31 -18
  23. data/lib/solargraph/complex_type/unique_type.rb +225 -63
  24. data/lib/solargraph/complex_type.rb +177 -59
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -111
  26. data/lib/solargraph/convention/base.rb +50 -50
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +1 -1
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
  29. data/lib/solargraph/convention/data_definition.rb +5 -2
  30. data/lib/solargraph/convention/gemfile.rb +1 -1
  31. data/lib/solargraph/convention/gemspec.rb +1 -1
  32. data/lib/solargraph/convention/rakefile.rb +1 -1
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
  35. data/lib/solargraph/convention/struct_definition.rb +8 -4
  36. data/lib/solargraph/convention.rb +2 -2
  37. data/lib/solargraph/converters/dd.rb +2 -0
  38. data/lib/solargraph/converters/dl.rb +2 -0
  39. data/lib/solargraph/converters/dt.rb +2 -0
  40. data/lib/solargraph/converters/misc.rb +2 -0
  41. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  42. data/lib/solargraph/diagnostics/rubocop.rb +11 -10
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  44. data/lib/solargraph/diagnostics/type_check.rb +11 -10
  45. data/lib/solargraph/diagnostics/update_errors.rb +4 -8
  46. data/lib/solargraph/diagnostics.rb +55 -55
  47. data/lib/solargraph/doc_map.rb +39 -39
  48. data/lib/solargraph/environ.rb +52 -52
  49. data/lib/solargraph/equality.rb +4 -4
  50. data/lib/solargraph/gem_pins.rb +4 -15
  51. data/lib/solargraph/language_server/error_codes.rb +10 -10
  52. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  53. data/lib/solargraph/language_server/host/dispatch.rb +3 -3
  54. data/lib/solargraph/language_server/host/message_worker.rb +4 -3
  55. data/lib/solargraph/language_server/host/sources.rb +2 -1
  56. data/lib/solargraph/language_server/host.rb +35 -28
  57. data/lib/solargraph/language_server/message/base.rb +1 -1
  58. data/lib/solargraph/language_server/message/client/register_capability.rb +1 -3
  59. data/lib/solargraph/language_server/message/completion_item/resolve.rb +6 -8
  60. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
  61. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  62. data/lib/solargraph/language_server/message/extended/document_gems.rb +7 -7
  63. data/lib/solargraph/language_server/message/extended/download_core.rb +2 -1
  64. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  65. data/lib/solargraph/language_server/message/extended/search.rb +1 -1
  66. data/lib/solargraph/language_server/message/initialize.rb +20 -14
  67. data/lib/solargraph/language_server/message/initialized.rb +28 -28
  68. data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
  69. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
  70. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +17 -10
  71. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
  72. data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
  73. data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
  74. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +13 -6
  75. data/lib/solargraph/language_server/message/text_document/references.rb +17 -10
  76. data/lib/solargraph/language_server/message/text_document/rename.rb +20 -13
  77. data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
  78. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
  79. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  80. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +34 -28
  81. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +38 -30
  82. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +23 -17
  83. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
  84. data/lib/solargraph/language_server/message.rb +1 -1
  85. data/lib/solargraph/language_server/progress.rb +143 -143
  86. data/lib/solargraph/language_server/request.rb +4 -2
  87. data/lib/solargraph/language_server/transport/adapter.rb +68 -68
  88. data/lib/solargraph/language_server/transport/data_reader.rb +11 -13
  89. data/lib/solargraph/language_server/uri_helpers.rb +2 -2
  90. data/lib/solargraph/language_server.rb +20 -20
  91. data/lib/solargraph/library.rb +57 -38
  92. data/lib/solargraph/location.rb +17 -14
  93. data/lib/solargraph/logging.rb +22 -4
  94. data/lib/solargraph/page.rb +1 -1
  95. data/lib/solargraph/parser/comment_ripper.rb +19 -4
  96. data/lib/solargraph/parser/flow_sensitive_typing.rb +324 -108
  97. data/lib/solargraph/parser/node_processor/base.rb +34 -4
  98. data/lib/solargraph/parser/node_processor.rb +8 -7
  99. data/lib/solargraph/parser/parser_gem/class_methods.rb +30 -14
  100. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -1
  101. data/lib/solargraph/parser/parser_gem/node_chainer.rb +51 -25
  102. data/lib/solargraph/parser/parser_gem/node_methods.rb +222 -72
  103. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +24 -24
  104. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  105. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
  106. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -12
  108. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +36 -36
  109. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +24 -24
  110. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
  111. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +9 -8
  112. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +24 -24
  113. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  114. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
  115. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  116. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  117. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +64 -40
  118. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
  119. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  120. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  121. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  122. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
  123. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
  124. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +20 -20
  125. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +1 -1
  126. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  127. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
  128. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  129. data/lib/solargraph/parser/parser_gem.rb +2 -0
  130. data/lib/solargraph/parser/region.rb +9 -3
  131. data/lib/solargraph/parser/snippet.rb +3 -1
  132. data/lib/solargraph/parser.rb +2 -0
  133. data/lib/solargraph/pin/base.rb +156 -84
  134. data/lib/solargraph/pin/base_variable.rb +273 -24
  135. data/lib/solargraph/pin/block.rb +29 -6
  136. data/lib/solargraph/pin/breakable.rb +7 -1
  137. data/lib/solargraph/pin/callable.rb +67 -23
  138. data/lib/solargraph/pin/closure.rb +7 -10
  139. data/lib/solargraph/pin/common.rb +36 -6
  140. data/lib/solargraph/pin/compound_statement.rb +55 -0
  141. data/lib/solargraph/pin/constant.rb +3 -5
  142. data/lib/solargraph/pin/conversions.rb +10 -4
  143. data/lib/solargraph/pin/delegated_method.rb +19 -8
  144. data/lib/solargraph/pin/documenting.rb +4 -2
  145. data/lib/solargraph/pin/instance_variable.rb +5 -1
  146. data/lib/solargraph/pin/keyword.rb +0 -4
  147. data/lib/solargraph/pin/local_variable.rb +15 -59
  148. data/lib/solargraph/pin/method.rb +205 -111
  149. data/lib/solargraph/pin/method_alias.rb +8 -0
  150. data/lib/solargraph/pin/namespace.rb +19 -12
  151. data/lib/solargraph/pin/parameter.rb +102 -36
  152. data/lib/solargraph/pin/proxy_type.rb +4 -1
  153. data/lib/solargraph/pin/reference/override.rb +1 -1
  154. data/lib/solargraph/pin/reference/require.rb +14 -14
  155. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  156. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  157. data/lib/solargraph/pin/reference.rb +20 -0
  158. data/lib/solargraph/pin/search.rb +8 -7
  159. data/lib/solargraph/pin/signature.rb +15 -12
  160. data/lib/solargraph/pin/singleton.rb +11 -11
  161. data/lib/solargraph/pin/symbol.rb +2 -1
  162. data/lib/solargraph/pin/until.rb +2 -4
  163. data/lib/solargraph/pin/while.rb +2 -4
  164. data/lib/solargraph/pin.rb +2 -0
  165. data/lib/solargraph/pin_cache.rb +22 -19
  166. data/lib/solargraph/position.rb +17 -10
  167. data/lib/solargraph/range.rb +16 -15
  168. data/lib/solargraph/rbs_map/conversions.rb +278 -191
  169. data/lib/solargraph/rbs_map/core_fills.rb +18 -11
  170. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  171. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
  172. data/lib/solargraph/rbs_map.rb +76 -32
  173. data/lib/solargraph/rbs_translator.rb +206 -0
  174. data/lib/solargraph/server_methods.rb +1 -1
  175. data/lib/solargraph/shell.rb +330 -71
  176. data/lib/solargraph/source/chain/array.rb +3 -12
  177. data/lib/solargraph/source/chain/block_symbol.rb +13 -13
  178. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  179. data/lib/solargraph/source/chain/call.rb +91 -119
  180. data/lib/solargraph/source/chain/class_variable.rb +1 -1
  181. data/lib/solargraph/source/chain/constant.rb +5 -1
  182. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  183. data/lib/solargraph/source/chain/hash.rb +8 -5
  184. data/lib/solargraph/source/chain/head.rb +19 -19
  185. data/lib/solargraph/source/chain/if.rb +12 -10
  186. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  187. data/lib/solargraph/source/chain/link.rb +12 -22
  188. data/lib/solargraph/source/chain/literal.rb +22 -15
  189. data/lib/solargraph/source/chain/or.rb +10 -4
  190. data/lib/solargraph/source/chain/q_call.rb +2 -0
  191. data/lib/solargraph/source/chain/variable.rb +3 -1
  192. data/lib/solargraph/source/chain/z_super.rb +1 -3
  193. data/lib/solargraph/source/chain.rb +51 -38
  194. data/lib/solargraph/source/change.rb +12 -5
  195. data/lib/solargraph/source/cursor.rb +33 -18
  196. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  197. data/lib/solargraph/source/source_chainer.rb +56 -32
  198. data/lib/solargraph/source/updater.rb +5 -1
  199. data/lib/solargraph/source.rb +59 -35
  200. data/lib/solargraph/source_map/clip.rb +54 -30
  201. data/lib/solargraph/source_map/data.rb +4 -1
  202. data/lib/solargraph/source_map/mapper.rb +28 -131
  203. data/lib/solargraph/source_map.rb +35 -9
  204. data/lib/solargraph/type_checker/problem.rb +3 -1
  205. data/lib/solargraph/type_checker/rules.rb +81 -8
  206. data/lib/solargraph/type_checker.rb +196 -122
  207. data/lib/solargraph/version.rb +19 -1
  208. data/lib/solargraph/workspace/config.rb +14 -11
  209. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  210. data/lib/solargraph/workspace/require_paths.rb +1 -0
  211. data/lib/solargraph/workspace.rb +50 -28
  212. data/lib/solargraph/yard_map/cache.rb +25 -25
  213. data/lib/solargraph/yard_map/directives/attribute_directive.rb +65 -0
  214. data/lib/solargraph/yard_map/directives/domain_directive.rb +30 -0
  215. data/lib/solargraph/yard_map/directives/method_directive.rb +51 -0
  216. data/lib/solargraph/yard_map/directives/override_directive.rb +30 -0
  217. data/lib/solargraph/yard_map/directives/parse_directive.rb +53 -0
  218. data/lib/solargraph/yard_map/directives/visibility_directive.rb +70 -0
  219. data/lib/solargraph/yard_map/directives.rb +35 -0
  220. data/lib/solargraph/yard_map/helpers.rb +8 -3
  221. data/lib/solargraph/yard_map/macro.rb +113 -0
  222. data/lib/solargraph/yard_map/mapper/to_constant.rb +28 -28
  223. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  224. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  225. data/lib/solargraph/yard_map/mapper.rb +31 -8
  226. data/lib/solargraph/yard_map.rb +19 -18
  227. data/lib/solargraph/yard_tags.rb +2 -2
  228. data/lib/solargraph/yardoc.rb +7 -4
  229. data/lib/solargraph.rb +34 -10
  230. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  231. data/rbs/shims/ast/0/node.rbs +1 -1
  232. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  233. data/solargraph.gemspec +38 -35
  234. metadata +64 -42
  235. data/lib/solargraph/type_checker/checks.rb +0 -124
  236. data/lib/solargraph/type_checker/param_def.rb +0 -37
  237. data/lib/solargraph/yard_map/to_method.rb +0 -89
  238. data/rbs/fills/tuple/tuple.rbs +0 -149
@@ -24,12 +24,26 @@ module Solargraph
24
24
  attr_reader :missing_docs
25
25
 
26
26
  # @param pins [Array<Solargraph::Pin::Base>]
27
- def initialize pins: []
27
+ # @param loose_unions [Boolean] if true, a potential type can be
28
+ # inferred if ANY of the UniqueTypes in the base chain's
29
+ # ComplexType match it. If false, every single UniqueTypes in
30
+ # the base must be ALL able to independently provide this
31
+ # type. The former is useful during completion, but the
32
+ # latter is best for typechecking at higher levels.
33
+ #
34
+ def initialize pins: [], loose_unions: true
28
35
  @source_map_hash = {}
29
36
  @cache = Cache.new
37
+ @loose_unions = loose_unions
30
38
  index pins
31
39
  end
32
40
 
41
+ # @param out [StringIO, IO, nil] output stream for logging
42
+ # @return [void]
43
+ def self.reset_core out: nil
44
+ @@core_map = RbsMap::CoreMap.new
45
+ end
46
+
33
47
  #
34
48
  # This is a mutable object, which is cached in the Chain class -
35
49
  # if you add any fields which change the results of calls (not
@@ -37,21 +51,24 @@ module Solargraph
37
51
  #
38
52
 
39
53
  # @param other [Object]
40
- def eql?(other)
54
+ def eql? other
41
55
  self.class == other.class &&
42
- # @sg-ignore Flow sensitive typing needs to handle self.class == other.class
56
+ # @sg-ignore flow sensitive typing needs to handle self.class == other.class
43
57
  equality_fields == other.equality_fields
44
58
  end
45
59
 
46
60
  # @param other [Object]
47
- def ==(other)
48
- self.eql?(other)
61
+ def == other
62
+ eql?(other)
49
63
  end
50
64
 
65
+ # @return [Integer]
51
66
  def hash
52
67
  equality_fields.hash
53
68
  end
54
69
 
70
+ attr_reader :loose_unions
71
+
55
72
  def to_s
56
73
  self.class.to_s
57
74
  end
@@ -90,6 +107,7 @@ module Solargraph
90
107
  # @return [self]
91
108
  def catalog bench
92
109
  @source_map_hash = bench.source_map_hash
110
+ # @type [Array<Pin::Base>]
93
111
  iced_pins = bench.icebox.flat_map(&:pins)
94
112
  live_pins = bench.live_map&.all_pins || []
95
113
  conventions_environ.clear
@@ -98,33 +116,58 @@ module Solargraph
98
116
  end
99
117
  unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
100
118
  recreate_docmap = @unresolved_requires != unresolved_requires ||
101
- @doc_map&.uncached_yard_gemspecs&.any? ||
102
- @doc_map&.uncached_rbs_collection_gemspecs&.any? ||
103
- @doc_map&.rbs_collection_path != bench.workspace.rbs_collection_path
119
+ # @sg-ignore Unresolved call to rbs_collection_path on Solargraph::Workspace, nil
120
+ workspace.rbs_collection_path != bench.workspace.rbs_collection_path ||
121
+ @doc_map.uncached_gemspecs.any?
122
+
104
123
  if recreate_docmap
105
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
124
+ @doc_map = DocMap.new(unresolved_requires, bench.workspace, out: nil) # @todo Implement gem preferences
106
125
  @unresolved_requires = @doc_map.unresolved_requires
107
126
  end
108
- @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
127
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
128
+ Solargraph.logger.info 'Cataloging ApiMap started'
129
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins) { process_macros }
109
130
  @missing_docs = [] # @todo Implement missing docs
131
+ Solargraph.logger.info "Cataloging ApiMap finished in #{Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time} seconds"
110
132
  self
111
133
  end
112
134
 
113
- # @todo need to model type def statement in chains as a symbol so
114
- # that this overload of 'protected' will typecheck @sg-ignore
115
- # @sg-ignore
116
- protected def equality_fields
117
- [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
135
+ # @return [Array<Pin::Base>]
136
+ def process_macros
137
+ macro_pins = []
138
+ Solargraph.logger.debug { "ApiMap#process_macros: processing macros for #{source_maps.size} source maps" }
139
+ Solargraph.logger.debug { "ApiMap#process_macros: store has #{store.macro_method_name_pins.size} macro method name pins" }
140
+ Solargraph.logger.debug { "ApiMap#process_macros: named macros: #{store.named_macros.keys.join(', ')}" }
141
+ source_maps.each do |source_map|
142
+ method_candidates = source_map.macro_method_candidates(store.macro_method_names)
143
+ Solargraph.logger.debug { "ApiMap#process_macros: processing source map for #{source_map.filename} with #{method_candidates.size} macro method candidates" }
144
+ method_candidates.each do |node|
145
+ closure = source_map.locate_closure_pin(node.location.line, node.location.column)
146
+ chain = Solargraph::Parser::ParserGem::NodeChainer.chain(node)
147
+ if node.children[0].nil? && store.macro_method_name_pins.key?(node.children[1].to_s)
148
+ match = store.macro_method_name_pins[node.children[1].to_s].find do |pin|
149
+ super_and_sub?(pin.namespace, closure.name)
150
+ end
151
+ if match
152
+ match.macros.each do |macro|
153
+ macro_pins.concat macro.generate_pins_from(chain, match, source_map)
154
+ end
155
+ next
156
+ end
157
+ end
158
+ end
159
+ end
160
+ macro_pins
118
161
  end
119
162
 
120
163
  # @return [DocMap]
121
164
  def doc_map
122
- @doc_map ||= DocMap.new([], [])
165
+ @doc_map ||= DocMap.new([], Workspace.new('.'))
123
166
  end
124
167
 
125
168
  # @return [::Array<Gem::Specification>]
126
169
  def uncached_gemspecs
127
- @doc_map&.uncached_gemspecs || []
170
+ doc_map.uncached_gemspecs || []
128
171
  end
129
172
 
130
173
  # @return [::Array<Gem::Specification>]
@@ -142,9 +185,10 @@ module Solargraph
142
185
  @@core_map.pins
143
186
  end
144
187
 
145
- # @param name [String]
146
- # @return [YARD::Tags::MacroDirective, nil]
188
+ # @param name [String, nil]
189
+ # @return [Solargraph::YardMap::Macro, nil]
147
190
  def named_macro name
191
+ # @sg-ignore Need to add nil check here
148
192
  store.named_macros[name]
149
193
  end
150
194
 
@@ -180,10 +224,11 @@ module Solargraph
180
224
  # Create an ApiMap with a workspace in the specified directory.
181
225
  #
182
226
  # @param directory [String]
227
+ # @param loose_unions [Boolean] See #initialize
183
228
  #
184
229
  # @return [ApiMap]
185
- def self.load directory
186
- api_map = new
230
+ def self.load directory, loose_unions: true
231
+ api_map = new(loose_unions: loose_unions)
187
232
  workspace = Solargraph::Workspace.new(directory)
188
233
  # api_map.catalog Bench.new(workspace: workspace)
189
234
  library = Library.new(workspace)
@@ -192,18 +237,19 @@ module Solargraph
192
237
  api_map
193
238
  end
194
239
 
195
- # @param out [IO, nil]
240
+ # @param out [StringIO, IO, nil]
241
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
196
242
  # @return [void]
197
- def cache_all!(out)
198
- @doc_map.cache_all!(out)
243
+ def cache_all_for_doc_map! out: $stderr, rebuild: false
244
+ doc_map.cache_all!(out, rebuild: rebuild)
199
245
  end
200
246
 
201
247
  # @param gemspec [Gem::Specification]
202
248
  # @param rebuild [Boolean]
203
- # @param out [IO, nil]
249
+ # @param out [StringIO, IO, nil]
204
250
  # @return [void]
205
- def cache_gem(gemspec, rebuild: false, out: nil)
206
- @doc_map.cache(gemspec, rebuild: rebuild, out: out)
251
+ def cache_gem gemspec, rebuild: false, out: nil
252
+ doc_map.cache(gemspec, rebuild: rebuild, out: out)
207
253
  end
208
254
 
209
255
  class << self
@@ -215,18 +261,19 @@ module Solargraph
215
261
  #
216
262
  #
217
263
  # @param directory [String]
218
- # @param out [IO] The output stream for messages
264
+ # @param out [IO, StringIO, nil] The output stream for messages
265
+ # @param loose_unions [Boolean] See #initialize
219
266
  #
220
267
  # @return [ApiMap]
221
- def self.load_with_cache directory, out
222
- api_map = load(directory)
268
+ def self.load_with_cache directory, out = $stderr, loose_unions: true
269
+ api_map = load(directory, loose_unions: loose_unions)
223
270
  if api_map.uncached_gemspecs.empty?
224
271
  logger.info { "All gems cached for #{directory}" }
225
272
  return api_map
226
273
  end
227
274
 
228
- api_map.cache_all!(out)
229
- load(directory)
275
+ api_map.cache_all_for_doc_map!(out: out)
276
+ load(directory, loose_unions: loose_unions)
230
277
  end
231
278
 
232
279
  # @return [Array<Solargraph::Pin::Base>]
@@ -241,6 +288,12 @@ module Solargraph
241
288
  store.pins_by_class(Pin::Keyword)
242
289
  end
243
290
 
291
+ # @param name [String]
292
+ # @return [ComplexType, nil]
293
+ def unalias name
294
+ store.unalias(name)
295
+ end
296
+
244
297
  # True if the namespace exists.
245
298
  #
246
299
  # @param name [String] The namespace to match
@@ -306,19 +359,19 @@ module Solargraph
306
359
  #
307
360
  # @param pin [Pin::Reference]
308
361
  # @return [String, nil]
309
- def dereference(pin)
362
+ def dereference pin
310
363
  store.constants.dereference(pin)
311
364
  end
312
365
 
313
366
  # @param fqns [String]
314
367
  # @return [Array<Pin::Reference::Extend>]
315
- def get_extends(fqns)
368
+ def get_extends fqns
316
369
  store.get_extends(fqns)
317
370
  end
318
371
 
319
372
  # @param fqns [String]
320
373
  # @return [Array<Pin::Reference::Include>]
321
- def get_includes(fqns)
374
+ def get_includes fqns
322
375
  store.get_includes(fqns)
323
376
  end
324
377
 
@@ -328,29 +381,50 @@ module Solargraph
328
381
  # @param namespace [String] A fully qualified namespace
329
382
  # @param scope [Symbol] :instance or :class
330
383
  # @return [Array<Solargraph::Pin::InstanceVariable>]
331
- def get_instance_variable_pins(namespace, scope = :instance)
384
+ def get_instance_variable_pins namespace, scope = :instance
332
385
  result = []
333
- used = [namespace]
386
+ [namespace]
334
387
  result.concat store.get_instance_variables(namespace, scope)
335
388
  sc_fqns = namespace
336
389
  while (sc = store.get_superclass(sc_fqns))
390
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
337
391
  sc_fqns = store.constants.dereference(sc)
338
392
  result.concat store.get_instance_variables(sc_fqns, scope)
339
393
  end
340
394
  result
341
395
  end
342
396
 
343
- # @sg-ignore Missing @return tag for Solargraph::ApiMap#visible_pins
344
- # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
345
- def visible_pins(*args, **kwargs, &blk)
346
- Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
397
+ # Find a variable pin by name and where it is used.
398
+ #
399
+ # Resolves our most specific view of this variable's type by
400
+ # preferring pins created by flow-sensitive typing when we have
401
+ # them based on the Closure and Location.
402
+ #
403
+ # @param candidates [Array<Pin::BaseVariable>]
404
+ # @param name [String]
405
+ # @param closure [Pin::Closure]
406
+ # @param location [Location]
407
+ #
408
+ # @return [Pin::BaseVariable, nil]
409
+ def var_at_location candidates, name, closure, location
410
+ # @todo Location can be nil if clips have trouble finding node recipients
411
+ return unless location
412
+
413
+ with_correct_name = candidates.select { |pin| pin.name == name }
414
+ vars_at_location = with_correct_name.reject do |pin|
415
+ # visible_at? excludes the starting position, but we want to
416
+ # include it for this purpose
417
+ !pin.visible_at?(closure, location) && !pin.starts_at?(location)
418
+ end
419
+
420
+ vars_at_location.inject(&:combine_with)
347
421
  end
348
422
 
349
423
  # Get an array of class variable pins for a namespace.
350
424
  #
351
425
  # @param namespace [String] A fully qualified namespace
352
426
  # @return [Enumerable<Solargraph::Pin::ClassVariable>]
353
- def get_class_variable_pins(namespace)
427
+ def get_class_variable_pins namespace
354
428
  prefer_non_nil_variables(store.get_class_variables(namespace))
355
429
  end
356
430
 
@@ -377,11 +451,6 @@ module Solargraph
377
451
  # @param deep [Boolean] True to include superclasses, mixins, etc.
378
452
  # @return [Array<Solargraph::Pin::Method>]
379
453
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
380
- if rooted_tag.start_with? 'Array('
381
- # Array() are really tuples - use our fill, as the RBS repo
382
- # does not give us definitions for it
383
- rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})"
384
- end
385
454
  rooted_type = ComplexType.try_parse(rooted_tag)
386
455
  fqns = rooted_type.namespace
387
456
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -417,7 +486,7 @@ module Solargraph
417
486
  comments: init_pin.comments,
418
487
  closure: init_pin.closure,
419
488
  source: init_pin.source,
420
- type_location: init_pin.type_location,
489
+ type_location: init_pin.type_location
421
490
  )
422
491
  new_pin.parameters = init_pin.parameters.map do |init_param|
423
492
  param = init_param.clone
@@ -476,7 +545,7 @@ module Solargraph
476
545
  result = Set.new
477
546
  complex_type.each do |type|
478
547
  if type.duck_type?
479
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
548
+ result.add Pin::DuckMethod.new(name: type.to_s[1..], source: :api_map)
480
549
  result.merge get_methods('Object')
481
550
  else
482
551
  unless type.nil? || type.name == 'void'
@@ -507,12 +576,14 @@ module Solargraph
507
576
  # @param preserve_generics [Boolean] True to preserve any
508
577
  # unresolved generic parameters, false to erase them
509
578
  # @return [Array<Solargraph::Pin::Method>]
510
- def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
579
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: %i[private protected public],
580
+ preserve_generics: false
511
581
  rooted_type = ComplexType.parse(rooted_tag)
512
582
  fqns = rooted_type.namespace
513
583
  namespace_pin = store.get_path_pins(fqns).first
514
584
  methods = if namespace_pin.is_a?(Pin::Constant)
515
- type = namespace_pin.infer(self)
585
+ type = namespace_pin.typify(self)
586
+ type = namespace_pin.probe(self) unless type.defined?
516
587
  if type.defined?
517
588
  namespace_pin = store.get_path_pins(type.namespace).first
518
589
  get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
@@ -630,18 +701,22 @@ module Solargraph
630
701
  # @param sup [String] The superclass
631
702
  # @param sub [String] The subclass
632
703
  # @return [Boolean]
633
- def super_and_sub?(sup, sub)
704
+ def super_and_sub? sup, sub
634
705
  sup = ComplexType.try_parse(sup)
635
706
  sub = ComplexType.try_parse(sub)
636
707
  # @todo If two literals are different values of the same type, it would
637
708
  # make more sense for super_and_sub? to return true, but there are a
638
709
  # few callers that currently expect this to be false.
710
+ # @sg-ignore flow-sensitive typing should be able to handle redefinition
639
711
  return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
712
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
640
713
  sup = sup.simplify_literals.to_s
714
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
641
715
  sub = sub.simplify_literals.to_s
642
716
  return true if sup == sub
643
717
  sc_fqns = sub
644
718
  while (sc = store.get_superclass(sc_fqns))
719
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
645
720
  sc_new = store.constants.dereference(sc)
646
721
  # Cyclical inheritance is invalid
647
722
  return false if sc_new == sc_fqns
@@ -658,22 +733,30 @@ module Solargraph
658
733
  # @param module_ns [String] The module namespace (no type parameters)
659
734
  #
660
735
  # @return [Boolean]
661
- def type_include?(host_ns, module_ns)
736
+ def type_include? host_ns, module_ns
662
737
  store.get_includes(host_ns).map { |inc_tag| inc_tag.type.name }.include?(module_ns)
663
738
  end
664
739
 
665
740
  # @param pins [Enumerable<Pin::Base>]
666
741
  # @param visibility [Enumerable<Symbol>]
667
742
  # @return [Array<Pin::Base>]
668
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
743
+ def resolve_method_aliases pins, visibility = %i[public private protected]
669
744
  with_resolved_aliases = pins.map do |pin|
670
745
  next pin unless pin.is_a?(Pin::MethodAlias)
671
746
  resolved = resolve_method_alias(pin)
747
+ # @sg-ignore Need to add nil check here
672
748
  next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
673
749
  resolved
674
750
  end.compact
675
- logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" }
676
- GemPins.combine_method_pins_by_path(with_resolved_aliases)
751
+ logger.debug do
752
+ "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}"
753
+ end
754
+ with_resolved_aliases
755
+ end
756
+
757
+ # @return [Workspace, nil]
758
+ def workspace
759
+ doc_map.workspace
677
760
  end
678
761
 
679
762
  # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
@@ -687,7 +770,7 @@ module Solargraph
687
770
  # @param skip [Set<String>]
688
771
  # @param no_core [Boolean] Skip core classes if true
689
772
  # @return [Array<Pin::Base>]
690
- def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
773
+ def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core
691
774
  logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
692
775
 
693
776
  # Ensure the types returned by the methods in the referenced
@@ -743,7 +826,7 @@ module Solargraph
743
826
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
744
827
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
745
828
  fqns = rooted_type.namespace
746
- fqns_generic_params = rooted_type.all_params
829
+ rooted_type.all_params
747
830
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
748
831
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
749
832
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
@@ -754,7 +837,9 @@ module Solargraph
754
837
  # ensure we start out with any immediate methods in this
755
838
  # namespace so we roughly match the same ordering of get_methods
756
839
  # and obey the 'deep' instruction
757
- direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag }
840
+ direct_convention_methods, convention_methods_by_reference = environ.pins.partition do |p|
841
+ p.namespace == rooted_tag
842
+ end
758
843
  result.concat direct_convention_methods
759
844
 
760
845
  if deep && scope == :instance
@@ -766,8 +851,10 @@ module Solargraph
766
851
  # Store#get_methods doesn't know about full tags, just
767
852
  # namespaces; resolving the generics in the method pins is this
768
853
  # class' responsibility
769
- methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
770
- logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" }
854
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort { |a, b| a.name <=> b.name }
855
+ logger.info do
856
+ "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}"
857
+ end
771
858
  result.concat methods
772
859
  if deep
773
860
  result.concat convention_methods_by_reference
@@ -775,21 +862,27 @@ module Solargraph
775
862
  if scope == :instance
776
863
  store.get_includes(fqns).reverse.each do |ref|
777
864
  in_tag = dereference(ref)
778
- result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
865
+ # @sg-ignore Need to add nil check here
866
+ result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep,
867
+ skip, true)
779
868
  end
780
869
  rooted_sc_tag = qualify_superclass(rooted_tag)
781
870
  unless rooted_sc_tag.nil?
782
- result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
871
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
872
+ visibility, true, skip, no_core)
783
873
  end
784
874
  else
785
- logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
875
+ logger.info do
876
+ "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}"
877
+ end
786
878
  store.get_extends(fqns).reverse.each do |em|
787
879
  fqem = dereference(em)
788
880
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
789
881
  end
790
882
  rooted_sc_tag = qualify_superclass(rooted_tag)
791
883
  unless rooted_sc_tag.nil?
792
- result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true)
884
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
885
+ visibility, true, skip, true)
793
886
  end
794
887
  unless no_core || fqns.empty?
795
888
  type = get_namespace_type(fqns)
@@ -817,7 +910,7 @@ module Solargraph
817
910
  def get_namespace_type fqns
818
911
  return nil if fqns.nil?
819
912
  # @type [Pin::Namespace, nil]
820
- pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
913
+ pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
821
914
  return nil if pin.nil?
822
915
  pin.type
823
916
  end
@@ -841,18 +934,21 @@ module Solargraph
841
934
 
842
935
  include Logging
843
936
 
844
- private
845
-
846
937
  # @param alias_pin [Pin::MethodAlias]
847
938
  # @return [Pin::Method, nil]
848
- def resolve_method_alias(alias_pin)
939
+ def resolve_method_alias alias_pin
849
940
  ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
941
+ # @type [Pin::Method, nil]
850
942
  original = nil
851
943
 
852
944
  # Search each ancestor for the original method
853
945
  ancestors.each do |ancestor_fqns|
854
946
  next if ancestor_fqns.nil?
855
- ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
947
+ ancestor_method_path = if alias_pin.original == 'new' && alias_pin.scope == :class
948
+ "#{ancestor_fqns}#initialize"
949
+ else
950
+ "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
951
+ end
856
952
 
857
953
  # Search for the original method in the ancestor
858
954
  original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
@@ -864,21 +960,29 @@ module Solargraph
864
960
  break resolved if resolved
865
961
  end
866
962
 
867
- candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
963
+ candidate_pin.is_a?(Pin::Method)
868
964
  end
869
965
 
870
966
  break if original
871
967
  end
968
+ if original.nil?
969
+ # :nocov:
970
+ Solargraph.assert_or_log(:alias_target_missing) do
971
+ "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}"
972
+ end
973
+ return nil
974
+ # :nocov:
975
+ end
872
976
 
873
977
  # @sg-ignore ignore `received nil` for original
874
- create_resolved_alias_pin(alias_pin, original) if original
978
+ create_resolved_alias_pin(alias_pin, original)
875
979
  end
876
980
 
877
981
  # Fast path for creating resolved alias pins without individual method stack lookups
878
982
  # @param alias_pin [Pin::MethodAlias] The alias pin to resolve
879
983
  # @param original [Pin::Method] The original method pin that was already found
880
984
  # @return [Pin::Method] The resolved method pin
881
- def create_resolved_alias_pin(alias_pin, original)
985
+ def create_resolved_alias_pin alias_pin, original
882
986
  # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup)
883
987
  args = {
884
988
  location: alias_pin.location,
@@ -894,7 +998,7 @@ module Solargraph
894
998
  return_type: original.return_type,
895
999
  source: :resolve_method_alias
896
1000
  }
897
- resolved_pin = Pin::Method.new **args
1001
+ resolved_pin = Pin::Method.new(**args)
898
1002
 
899
1003
  # Clone signatures and parameters
900
1004
  resolved_pin.signatures.each do |sig|
@@ -916,7 +1020,7 @@ module Solargraph
916
1020
  # @param rooted_type [ComplexType]
917
1021
  # @param pins [Enumerable<Pin::Base>]
918
1022
  # @return [Array<Pin::Base>]
919
- def erase_generics(namespace_pin, rooted_type, pins)
1023
+ def erase_generics namespace_pin, rooted_type, pins
920
1024
  return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
921
1025
 
922
1026
  logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
@@ -927,19 +1031,29 @@ module Solargraph
927
1031
 
928
1032
  # @param namespace_pin [Pin::Namespace]
929
1033
  # @param rooted_type [ComplexType]
930
- def should_erase_generics_when_done?(namespace_pin, rooted_type)
1034
+ def should_erase_generics_when_done? namespace_pin, rooted_type
931
1035
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
932
1036
  end
933
1037
 
934
1038
  # @param namespace_pin [Pin::Namespace, Pin::Constant]
935
- def has_generics?(namespace_pin)
1039
+ def has_generics? namespace_pin
936
1040
  namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
937
1041
  end
938
1042
 
939
1043
  # @param namespace_pin [Pin::Namespace]
940
1044
  # @param rooted_type [ComplexType]
941
- def can_resolve_generics?(namespace_pin, rooted_type)
1045
+ def can_resolve_generics? namespace_pin, rooted_type
942
1046
  has_generics?(namespace_pin) && !rooted_type.all_params.empty?
943
1047
  end
1048
+
1049
+ protected
1050
+
1051
+ # @todo need to model type def statement in chains as a symbol so
1052
+ # that this overload of 'protected' will typecheck @sg-ignore
1053
+ # @sg-ignore
1054
+ def equality_fields
1055
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs,
1056
+ @loose_unions]
1057
+ end
944
1058
  end
945
1059
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Solargraph
5
4
  # A container of source maps and workspace data to be cataloged in an ApiMap.
6
5
  #
@@ -30,11 +29,11 @@ module Solargraph
30
29
  .to_set
31
30
  end
32
31
 
32
+ # @sg-ignore flow sensitive typing needs better handling of ||= on lvars
33
33
  # @return [Hash{String => SourceMap}]
34
34
  def source_map_hash
35
35
  # @todo Work around #to_h bug in current Ruby head (3.5) with #map#to_h
36
- @source_map_hash ||= source_maps.map { |s| [s.filename, s] }
37
- .to_h
36
+ @source_map_hash ||= source_maps.to_h { |s| [s.filename, s] }
38
37
  end
39
38
 
40
39
  # @return [Set<SourceMap>]