solargraph 0.58.3 → 0.60.3

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 +57 -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 +13 -4
  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 +191 -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 +63 -40
  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 +157 -85
  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 +203 -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 +227 -238
  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 +92 -120
  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 +19 -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 +71 -43
  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 +30 -133
  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 +208 -125
  207. data/lib/solargraph/version.rb +1 -1
  208. data/lib/solargraph/workspace/config.rb +14 -11
  209. data/lib/solargraph/workspace/gemspecs.rb +373 -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 +39 -35
  234. metadata +77 -41
  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
+ get_complex_type_methods(closure.return_type).include?(pin)
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,49 @@ 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, nil]
407
+ #
408
+ # @return [Pin::BaseVariable, nil]
409
+ def var_at_location candidates, name, closure, location
410
+ return unless location
411
+
412
+ with_correct_name = candidates.select { |pin| pin.name == name }
413
+ vars_at_location = with_correct_name.reject do |pin|
414
+ # visible_at? excludes the starting position, but we want to
415
+ # include it for this purpose
416
+ !pin.visible_at?(closure, location) && !pin.starts_at?(location)
417
+ end
418
+
419
+ vars_at_location.inject(&:combine_with)
347
420
  end
348
421
 
349
422
  # Get an array of class variable pins for a namespace.
350
423
  #
351
424
  # @param namespace [String] A fully qualified namespace
352
425
  # @return [Enumerable<Solargraph::Pin::ClassVariable>]
353
- def get_class_variable_pins(namespace)
426
+ def get_class_variable_pins namespace
354
427
  prefer_non_nil_variables(store.get_class_variables(namespace))
355
428
  end
356
429
 
@@ -377,11 +450,6 @@ module Solargraph
377
450
  # @param deep [Boolean] True to include superclasses, mixins, etc.
378
451
  # @return [Array<Solargraph::Pin::Method>]
379
452
  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
453
  rooted_type = ComplexType.try_parse(rooted_tag)
386
454
  fqns = rooted_type.namespace
387
455
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -417,7 +485,7 @@ module Solargraph
417
485
  comments: init_pin.comments,
418
486
  closure: init_pin.closure,
419
487
  source: init_pin.source,
420
- type_location: init_pin.type_location,
488
+ type_location: init_pin.type_location
421
489
  )
422
490
  new_pin.parameters = init_pin.parameters.map do |init_param|
423
491
  param = init_param.clone
@@ -476,7 +544,7 @@ module Solargraph
476
544
  result = Set.new
477
545
  complex_type.each do |type|
478
546
  if type.duck_type?
479
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
547
+ result.add Pin::DuckMethod.new(name: type.to_s[1..], source: :api_map)
480
548
  result.merge get_methods('Object')
481
549
  else
482
550
  unless type.nil? || type.name == 'void'
@@ -507,12 +575,14 @@ module Solargraph
507
575
  # @param preserve_generics [Boolean] True to preserve any
508
576
  # unresolved generic parameters, false to erase them
509
577
  # @return [Array<Solargraph::Pin::Method>]
510
- def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
578
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: %i[private protected public],
579
+ preserve_generics: false
511
580
  rooted_type = ComplexType.parse(rooted_tag)
512
581
  fqns = rooted_type.namespace
513
582
  namespace_pin = store.get_path_pins(fqns).first
514
583
  methods = if namespace_pin.is_a?(Pin::Constant)
515
- type = namespace_pin.infer(self)
584
+ type = namespace_pin.typify(self)
585
+ type = namespace_pin.probe(self) unless type.defined?
516
586
  if type.defined?
517
587
  namespace_pin = store.get_path_pins(type.namespace).first
518
588
  get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
@@ -630,18 +700,22 @@ module Solargraph
630
700
  # @param sup [String] The superclass
631
701
  # @param sub [String] The subclass
632
702
  # @return [Boolean]
633
- def super_and_sub?(sup, sub)
703
+ def super_and_sub? sup, sub
634
704
  sup = ComplexType.try_parse(sup)
635
705
  sub = ComplexType.try_parse(sub)
636
706
  # @todo If two literals are different values of the same type, it would
637
707
  # make more sense for super_and_sub? to return true, but there are a
638
708
  # few callers that currently expect this to be false.
709
+ # @sg-ignore flow-sensitive typing should be able to handle redefinition
639
710
  return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
711
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
640
712
  sup = sup.simplify_literals.to_s
713
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
641
714
  sub = sub.simplify_literals.to_s
642
715
  return true if sup == sub
643
716
  sc_fqns = sub
644
717
  while (sc = store.get_superclass(sc_fqns))
718
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
645
719
  sc_new = store.constants.dereference(sc)
646
720
  # Cyclical inheritance is invalid
647
721
  return false if sc_new == sc_fqns
@@ -658,22 +732,30 @@ module Solargraph
658
732
  # @param module_ns [String] The module namespace (no type parameters)
659
733
  #
660
734
  # @return [Boolean]
661
- def type_include?(host_ns, module_ns)
735
+ def type_include? host_ns, module_ns
662
736
  store.get_includes(host_ns).map { |inc_tag| inc_tag.type.name }.include?(module_ns)
663
737
  end
664
738
 
665
739
  # @param pins [Enumerable<Pin::Base>]
666
740
  # @param visibility [Enumerable<Symbol>]
667
741
  # @return [Array<Pin::Base>]
668
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
742
+ def resolve_method_aliases pins, visibility = %i[public private protected]
669
743
  with_resolved_aliases = pins.map do |pin|
670
744
  next pin unless pin.is_a?(Pin::MethodAlias)
671
745
  resolved = resolve_method_alias(pin)
746
+ # @sg-ignore Need to add nil check here
672
747
  next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
673
748
  resolved
674
749
  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)
750
+ logger.debug do
751
+ "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}"
752
+ end
753
+ with_resolved_aliases
754
+ end
755
+
756
+ # @return [Workspace, nil]
757
+ def workspace
758
+ doc_map.workspace
677
759
  end
678
760
 
679
761
  # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
@@ -687,7 +769,7 @@ module Solargraph
687
769
  # @param skip [Set<String>]
688
770
  # @param no_core [Boolean] Skip core classes if true
689
771
  # @return [Array<Pin::Base>]
690
- def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
772
+ def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core
691
773
  logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
692
774
 
693
775
  # Ensure the types returned by the methods in the referenced
@@ -743,7 +825,7 @@ module Solargraph
743
825
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
744
826
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
745
827
  fqns = rooted_type.namespace
746
- fqns_generic_params = rooted_type.all_params
828
+ rooted_type.all_params
747
829
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
748
830
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
749
831
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
@@ -754,7 +836,9 @@ module Solargraph
754
836
  # ensure we start out with any immediate methods in this
755
837
  # namespace so we roughly match the same ordering of get_methods
756
838
  # and obey the 'deep' instruction
757
- direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag }
839
+ direct_convention_methods, convention_methods_by_reference = environ.pins.partition do |p|
840
+ p.namespace == rooted_tag
841
+ end
758
842
  result.concat direct_convention_methods
759
843
 
760
844
  if deep && scope == :instance
@@ -766,8 +850,10 @@ module Solargraph
766
850
  # Store#get_methods doesn't know about full tags, just
767
851
  # namespaces; resolving the generics in the method pins is this
768
852
  # 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}" }
853
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort { |a, b| a.name <=> b.name }
854
+ logger.info do
855
+ "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}"
856
+ end
771
857
  result.concat methods
772
858
  if deep
773
859
  result.concat convention_methods_by_reference
@@ -775,21 +861,27 @@ module Solargraph
775
861
  if scope == :instance
776
862
  store.get_includes(fqns).reverse.each do |ref|
777
863
  in_tag = dereference(ref)
778
- result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
864
+ # @sg-ignore Need to add nil check here
865
+ result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep,
866
+ skip, true)
779
867
  end
780
868
  rooted_sc_tag = qualify_superclass(rooted_tag)
781
869
  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)
870
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
871
+ visibility, true, skip, no_core)
783
872
  end
784
873
  else
785
- logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
874
+ logger.info do
875
+ "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}"
876
+ end
786
877
  store.get_extends(fqns).reverse.each do |em|
787
878
  fqem = dereference(em)
788
879
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
789
880
  end
790
881
  rooted_sc_tag = qualify_superclass(rooted_tag)
791
882
  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)
883
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
884
+ visibility, true, skip, true)
793
885
  end
794
886
  unless no_core || fqns.empty?
795
887
  type = get_namespace_type(fqns)
@@ -817,7 +909,7 @@ module Solargraph
817
909
  def get_namespace_type fqns
818
910
  return nil if fqns.nil?
819
911
  # @type [Pin::Namespace, nil]
820
- pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
912
+ pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
821
913
  return nil if pin.nil?
822
914
  pin.type
823
915
  end
@@ -841,18 +933,21 @@ module Solargraph
841
933
 
842
934
  include Logging
843
935
 
844
- private
845
-
846
936
  # @param alias_pin [Pin::MethodAlias]
847
937
  # @return [Pin::Method, nil]
848
- def resolve_method_alias(alias_pin)
938
+ def resolve_method_alias alias_pin
849
939
  ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
940
+ # @type [Pin::Method, nil]
850
941
  original = nil
851
942
 
852
943
  # Search each ancestor for the original method
853
944
  ancestors.each do |ancestor_fqns|
854
945
  next if ancestor_fqns.nil?
855
- ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
946
+ ancestor_method_path = if alias_pin.original == 'new' && alias_pin.scope == :class
947
+ "#{ancestor_fqns}#initialize"
948
+ else
949
+ "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
950
+ end
856
951
 
857
952
  # Search for the original method in the ancestor
858
953
  original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
@@ -864,21 +959,29 @@ module Solargraph
864
959
  break resolved if resolved
865
960
  end
866
961
 
867
- candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
962
+ candidate_pin.is_a?(Pin::Method)
868
963
  end
869
964
 
870
965
  break if original
871
966
  end
967
+ if original.nil?
968
+ # :nocov:
969
+ Solargraph.assert_or_log(:alias_target_missing) do
970
+ "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}"
971
+ end
972
+ return nil
973
+ # :nocov:
974
+ end
872
975
 
873
976
  # @sg-ignore ignore `received nil` for original
874
- create_resolved_alias_pin(alias_pin, original) if original
977
+ create_resolved_alias_pin(alias_pin, original)
875
978
  end
876
979
 
877
980
  # Fast path for creating resolved alias pins without individual method stack lookups
878
981
  # @param alias_pin [Pin::MethodAlias] The alias pin to resolve
879
982
  # @param original [Pin::Method] The original method pin that was already found
880
983
  # @return [Pin::Method] The resolved method pin
881
- def create_resolved_alias_pin(alias_pin, original)
984
+ def create_resolved_alias_pin alias_pin, original
882
985
  # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup)
883
986
  args = {
884
987
  location: alias_pin.location,
@@ -894,7 +997,7 @@ module Solargraph
894
997
  return_type: original.return_type,
895
998
  source: :resolve_method_alias
896
999
  }
897
- resolved_pin = Pin::Method.new **args
1000
+ resolved_pin = Pin::Method.new(**args)
898
1001
 
899
1002
  # Clone signatures and parameters
900
1003
  resolved_pin.signatures.each do |sig|
@@ -916,7 +1019,7 @@ module Solargraph
916
1019
  # @param rooted_type [ComplexType]
917
1020
  # @param pins [Enumerable<Pin::Base>]
918
1021
  # @return [Array<Pin::Base>]
919
- def erase_generics(namespace_pin, rooted_type, pins)
1022
+ def erase_generics namespace_pin, rooted_type, pins
920
1023
  return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
921
1024
 
922
1025
  logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
@@ -927,19 +1030,29 @@ module Solargraph
927
1030
 
928
1031
  # @param namespace_pin [Pin::Namespace]
929
1032
  # @param rooted_type [ComplexType]
930
- def should_erase_generics_when_done?(namespace_pin, rooted_type)
1033
+ def should_erase_generics_when_done? namespace_pin, rooted_type
931
1034
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
932
1035
  end
933
1036
 
934
1037
  # @param namespace_pin [Pin::Namespace, Pin::Constant]
935
- def has_generics?(namespace_pin)
1038
+ def has_generics? namespace_pin
936
1039
  namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
937
1040
  end
938
1041
 
939
1042
  # @param namespace_pin [Pin::Namespace]
940
1043
  # @param rooted_type [ComplexType]
941
- def can_resolve_generics?(namespace_pin, rooted_type)
1044
+ def can_resolve_generics? namespace_pin, rooted_type
942
1045
  has_generics?(namespace_pin) && !rooted_type.all_params.empty?
943
1046
  end
1047
+
1048
+ protected
1049
+
1050
+ # @todo need to model type def statement in chains as a symbol so
1051
+ # that this overload of 'protected' will typecheck @sg-ignore
1052
+ # @sg-ignore
1053
+ def equality_fields
1054
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs,
1055
+ @loose_unions]
1056
+ end
944
1057
  end
945
1058
  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>]