solargraph 0.58.3 → 0.60.1

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 +40 -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 +193 -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 +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 +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 +1 -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,59 @@ 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.warn "Candidates: #{method_candidates}"
144
+ Solargraph.logger.debug { "ApiMap#process_macros: processing source map for #{source_map.filename} with #{method_candidates.size} macro method candidates" }
145
+ method_candidates.each do |node|
146
+ closure = source_map.locate_closure_pin(node.location.line, node.location.column)
147
+ chain = Solargraph::Parser::ParserGem::NodeChainer.chain(node)
148
+ if node.children[0].nil? && store.macro_method_name_pins.key?(node.children[1].to_s)
149
+ match = store.macro_method_name_pins[node.children[1].to_s].find do |pin|
150
+ get_complex_type_methods(closure.return_type).include?(pin)
151
+ end
152
+ if match
153
+ match.macros.each do |macro|
154
+ macro_pins.concat macro.generate_pins_from(chain, match, source_map)
155
+ end
156
+ next
157
+ end
158
+ end
159
+ end
160
+ end
161
+ macro_pins
118
162
  end
119
163
 
120
164
  # @return [DocMap]
121
165
  def doc_map
122
- @doc_map ||= DocMap.new([], [])
166
+ @doc_map ||= DocMap.new([], Workspace.new('.'))
123
167
  end
124
168
 
125
169
  # @return [::Array<Gem::Specification>]
126
170
  def uncached_gemspecs
127
- @doc_map&.uncached_gemspecs || []
171
+ doc_map.uncached_gemspecs || []
128
172
  end
129
173
 
130
174
  # @return [::Array<Gem::Specification>]
@@ -142,9 +186,10 @@ module Solargraph
142
186
  @@core_map.pins
143
187
  end
144
188
 
145
- # @param name [String]
146
- # @return [YARD::Tags::MacroDirective, nil]
189
+ # @param name [String, nil]
190
+ # @return [Solargraph::YardMap::Macro, nil]
147
191
  def named_macro name
192
+ # @sg-ignore Need to add nil check here
148
193
  store.named_macros[name]
149
194
  end
150
195
 
@@ -180,10 +225,11 @@ module Solargraph
180
225
  # Create an ApiMap with a workspace in the specified directory.
181
226
  #
182
227
  # @param directory [String]
228
+ # @param loose_unions [Boolean] See #initialize
183
229
  #
184
230
  # @return [ApiMap]
185
- def self.load directory
186
- api_map = new
231
+ def self.load directory, loose_unions: true
232
+ api_map = new(loose_unions: loose_unions)
187
233
  workspace = Solargraph::Workspace.new(directory)
188
234
  # api_map.catalog Bench.new(workspace: workspace)
189
235
  library = Library.new(workspace)
@@ -192,18 +238,19 @@ module Solargraph
192
238
  api_map
193
239
  end
194
240
 
195
- # @param out [IO, nil]
241
+ # @param out [StringIO, IO, nil]
242
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
196
243
  # @return [void]
197
- def cache_all!(out)
198
- @doc_map.cache_all!(out)
244
+ def cache_all_for_doc_map! out: $stderr, rebuild: false
245
+ doc_map.cache_all!(out, rebuild: rebuild)
199
246
  end
200
247
 
201
248
  # @param gemspec [Gem::Specification]
202
249
  # @param rebuild [Boolean]
203
- # @param out [IO, nil]
250
+ # @param out [StringIO, IO, nil]
204
251
  # @return [void]
205
- def cache_gem(gemspec, rebuild: false, out: nil)
206
- @doc_map.cache(gemspec, rebuild: rebuild, out: out)
252
+ def cache_gem gemspec, rebuild: false, out: nil
253
+ doc_map.cache(gemspec, rebuild: rebuild, out: out)
207
254
  end
208
255
 
209
256
  class << self
@@ -215,18 +262,19 @@ module Solargraph
215
262
  #
216
263
  #
217
264
  # @param directory [String]
218
- # @param out [IO] The output stream for messages
265
+ # @param out [IO, StringIO, nil] The output stream for messages
266
+ # @param loose_unions [Boolean] See #initialize
219
267
  #
220
268
  # @return [ApiMap]
221
- def self.load_with_cache directory, out
222
- api_map = load(directory)
269
+ def self.load_with_cache directory, out = $stderr, loose_unions: true
270
+ api_map = load(directory, loose_unions: loose_unions)
223
271
  if api_map.uncached_gemspecs.empty?
224
272
  logger.info { "All gems cached for #{directory}" }
225
273
  return api_map
226
274
  end
227
275
 
228
- api_map.cache_all!(out)
229
- load(directory)
276
+ api_map.cache_all_for_doc_map!(out: out)
277
+ load(directory, loose_unions: loose_unions)
230
278
  end
231
279
 
232
280
  # @return [Array<Solargraph::Pin::Base>]
@@ -241,6 +289,12 @@ module Solargraph
241
289
  store.pins_by_class(Pin::Keyword)
242
290
  end
243
291
 
292
+ # @param name [String]
293
+ # @return [ComplexType, nil]
294
+ def unalias name
295
+ store.unalias(name)
296
+ end
297
+
244
298
  # True if the namespace exists.
245
299
  #
246
300
  # @param name [String] The namespace to match
@@ -306,19 +360,19 @@ module Solargraph
306
360
  #
307
361
  # @param pin [Pin::Reference]
308
362
  # @return [String, nil]
309
- def dereference(pin)
363
+ def dereference pin
310
364
  store.constants.dereference(pin)
311
365
  end
312
366
 
313
367
  # @param fqns [String]
314
368
  # @return [Array<Pin::Reference::Extend>]
315
- def get_extends(fqns)
369
+ def get_extends fqns
316
370
  store.get_extends(fqns)
317
371
  end
318
372
 
319
373
  # @param fqns [String]
320
374
  # @return [Array<Pin::Reference::Include>]
321
- def get_includes(fqns)
375
+ def get_includes fqns
322
376
  store.get_includes(fqns)
323
377
  end
324
378
 
@@ -328,29 +382,50 @@ module Solargraph
328
382
  # @param namespace [String] A fully qualified namespace
329
383
  # @param scope [Symbol] :instance or :class
330
384
  # @return [Array<Solargraph::Pin::InstanceVariable>]
331
- def get_instance_variable_pins(namespace, scope = :instance)
385
+ def get_instance_variable_pins namespace, scope = :instance
332
386
  result = []
333
- used = [namespace]
387
+ [namespace]
334
388
  result.concat store.get_instance_variables(namespace, scope)
335
389
  sc_fqns = namespace
336
390
  while (sc = store.get_superclass(sc_fqns))
391
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
337
392
  sc_fqns = store.constants.dereference(sc)
338
393
  result.concat store.get_instance_variables(sc_fqns, scope)
339
394
  end
340
395
  result
341
396
  end
342
397
 
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)
398
+ # Find a variable pin by name and where it is used.
399
+ #
400
+ # Resolves our most specific view of this variable's type by
401
+ # preferring pins created by flow-sensitive typing when we have
402
+ # them based on the Closure and Location.
403
+ #
404
+ # @param candidates [Array<Pin::BaseVariable>]
405
+ # @param name [String]
406
+ # @param closure [Pin::Closure]
407
+ # @param location [Location]
408
+ #
409
+ # @return [Pin::BaseVariable, nil]
410
+ def var_at_location candidates, name, closure, location
411
+ # @todo Location can be nil if clips have trouble finding node recipients
412
+ return unless location
413
+
414
+ with_correct_name = candidates.select { |pin| pin.name == name }
415
+ vars_at_location = with_correct_name.reject do |pin|
416
+ # visible_at? excludes the starting position, but we want to
417
+ # include it for this purpose
418
+ !pin.visible_at?(closure, location) && !pin.starts_at?(location)
419
+ end
420
+
421
+ vars_at_location.inject(&:combine_with)
347
422
  end
348
423
 
349
424
  # Get an array of class variable pins for a namespace.
350
425
  #
351
426
  # @param namespace [String] A fully qualified namespace
352
427
  # @return [Enumerable<Solargraph::Pin::ClassVariable>]
353
- def get_class_variable_pins(namespace)
428
+ def get_class_variable_pins namespace
354
429
  prefer_non_nil_variables(store.get_class_variables(namespace))
355
430
  end
356
431
 
@@ -377,11 +452,6 @@ module Solargraph
377
452
  # @param deep [Boolean] True to include superclasses, mixins, etc.
378
453
  # @return [Array<Solargraph::Pin::Method>]
379
454
  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
455
  rooted_type = ComplexType.try_parse(rooted_tag)
386
456
  fqns = rooted_type.namespace
387
457
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -417,7 +487,7 @@ module Solargraph
417
487
  comments: init_pin.comments,
418
488
  closure: init_pin.closure,
419
489
  source: init_pin.source,
420
- type_location: init_pin.type_location,
490
+ type_location: init_pin.type_location
421
491
  )
422
492
  new_pin.parameters = init_pin.parameters.map do |init_param|
423
493
  param = init_param.clone
@@ -476,7 +546,7 @@ module Solargraph
476
546
  result = Set.new
477
547
  complex_type.each do |type|
478
548
  if type.duck_type?
479
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
549
+ result.add Pin::DuckMethod.new(name: type.to_s[1..], source: :api_map)
480
550
  result.merge get_methods('Object')
481
551
  else
482
552
  unless type.nil? || type.name == 'void'
@@ -507,12 +577,14 @@ module Solargraph
507
577
  # @param preserve_generics [Boolean] True to preserve any
508
578
  # unresolved generic parameters, false to erase them
509
579
  # @return [Array<Solargraph::Pin::Method>]
510
- def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
580
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: %i[private protected public],
581
+ preserve_generics: false
511
582
  rooted_type = ComplexType.parse(rooted_tag)
512
583
  fqns = rooted_type.namespace
513
584
  namespace_pin = store.get_path_pins(fqns).first
514
585
  methods = if namespace_pin.is_a?(Pin::Constant)
515
- type = namespace_pin.infer(self)
586
+ type = namespace_pin.typify(self)
587
+ type = namespace_pin.probe(self) unless type.defined?
516
588
  if type.defined?
517
589
  namespace_pin = store.get_path_pins(type.namespace).first
518
590
  get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
@@ -630,18 +702,22 @@ module Solargraph
630
702
  # @param sup [String] The superclass
631
703
  # @param sub [String] The subclass
632
704
  # @return [Boolean]
633
- def super_and_sub?(sup, sub)
705
+ def super_and_sub? sup, sub
634
706
  sup = ComplexType.try_parse(sup)
635
707
  sub = ComplexType.try_parse(sub)
636
708
  # @todo If two literals are different values of the same type, it would
637
709
  # make more sense for super_and_sub? to return true, but there are a
638
710
  # few callers that currently expect this to be false.
711
+ # @sg-ignore flow-sensitive typing should be able to handle redefinition
639
712
  return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
713
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
640
714
  sup = sup.simplify_literals.to_s
715
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
641
716
  sub = sub.simplify_literals.to_s
642
717
  return true if sup == sub
643
718
  sc_fqns = sub
644
719
  while (sc = store.get_superclass(sc_fqns))
720
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
645
721
  sc_new = store.constants.dereference(sc)
646
722
  # Cyclical inheritance is invalid
647
723
  return false if sc_new == sc_fqns
@@ -658,22 +734,30 @@ module Solargraph
658
734
  # @param module_ns [String] The module namespace (no type parameters)
659
735
  #
660
736
  # @return [Boolean]
661
- def type_include?(host_ns, module_ns)
737
+ def type_include? host_ns, module_ns
662
738
  store.get_includes(host_ns).map { |inc_tag| inc_tag.type.name }.include?(module_ns)
663
739
  end
664
740
 
665
741
  # @param pins [Enumerable<Pin::Base>]
666
742
  # @param visibility [Enumerable<Symbol>]
667
743
  # @return [Array<Pin::Base>]
668
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
744
+ def resolve_method_aliases pins, visibility = %i[public private protected]
669
745
  with_resolved_aliases = pins.map do |pin|
670
746
  next pin unless pin.is_a?(Pin::MethodAlias)
671
747
  resolved = resolve_method_alias(pin)
748
+ # @sg-ignore Need to add nil check here
672
749
  next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
673
750
  resolved
674
751
  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)
752
+ logger.debug do
753
+ "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}"
754
+ end
755
+ with_resolved_aliases
756
+ end
757
+
758
+ # @return [Workspace, nil]
759
+ def workspace
760
+ doc_map.workspace
677
761
  end
678
762
 
679
763
  # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
@@ -687,7 +771,7 @@ module Solargraph
687
771
  # @param skip [Set<String>]
688
772
  # @param no_core [Boolean] Skip core classes if true
689
773
  # @return [Array<Pin::Base>]
690
- def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
774
+ def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core
691
775
  logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
692
776
 
693
777
  # Ensure the types returned by the methods in the referenced
@@ -743,7 +827,7 @@ module Solargraph
743
827
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
744
828
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
745
829
  fqns = rooted_type.namespace
746
- fqns_generic_params = rooted_type.all_params
830
+ rooted_type.all_params
747
831
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
748
832
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
749
833
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
@@ -754,7 +838,9 @@ module Solargraph
754
838
  # ensure we start out with any immediate methods in this
755
839
  # namespace so we roughly match the same ordering of get_methods
756
840
  # and obey the 'deep' instruction
757
- direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag }
841
+ direct_convention_methods, convention_methods_by_reference = environ.pins.partition do |p|
842
+ p.namespace == rooted_tag
843
+ end
758
844
  result.concat direct_convention_methods
759
845
 
760
846
  if deep && scope == :instance
@@ -766,8 +852,10 @@ module Solargraph
766
852
  # Store#get_methods doesn't know about full tags, just
767
853
  # namespaces; resolving the generics in the method pins is this
768
854
  # 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}" }
855
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort { |a, b| a.name <=> b.name }
856
+ logger.info do
857
+ "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}"
858
+ end
771
859
  result.concat methods
772
860
  if deep
773
861
  result.concat convention_methods_by_reference
@@ -775,21 +863,27 @@ module Solargraph
775
863
  if scope == :instance
776
864
  store.get_includes(fqns).reverse.each do |ref|
777
865
  in_tag = dereference(ref)
778
- result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
866
+ # @sg-ignore Need to add nil check here
867
+ result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep,
868
+ skip, true)
779
869
  end
780
870
  rooted_sc_tag = qualify_superclass(rooted_tag)
781
871
  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)
872
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
873
+ visibility, true, skip, no_core)
783
874
  end
784
875
  else
785
- logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
876
+ logger.info do
877
+ "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}"
878
+ end
786
879
  store.get_extends(fqns).reverse.each do |em|
787
880
  fqem = dereference(em)
788
881
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
789
882
  end
790
883
  rooted_sc_tag = qualify_superclass(rooted_tag)
791
884
  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)
885
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
886
+ visibility, true, skip, true)
793
887
  end
794
888
  unless no_core || fqns.empty?
795
889
  type = get_namespace_type(fqns)
@@ -817,7 +911,7 @@ module Solargraph
817
911
  def get_namespace_type fqns
818
912
  return nil if fqns.nil?
819
913
  # @type [Pin::Namespace, nil]
820
- pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
914
+ pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
821
915
  return nil if pin.nil?
822
916
  pin.type
823
917
  end
@@ -841,18 +935,21 @@ module Solargraph
841
935
 
842
936
  include Logging
843
937
 
844
- private
845
-
846
938
  # @param alias_pin [Pin::MethodAlias]
847
939
  # @return [Pin::Method, nil]
848
- def resolve_method_alias(alias_pin)
940
+ def resolve_method_alias alias_pin
849
941
  ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
942
+ # @type [Pin::Method, nil]
850
943
  original = nil
851
944
 
852
945
  # Search each ancestor for the original method
853
946
  ancestors.each do |ancestor_fqns|
854
947
  next if ancestor_fqns.nil?
855
- ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
948
+ ancestor_method_path = if alias_pin.original == 'new' && alias_pin.scope == :class
949
+ "#{ancestor_fqns}#initialize"
950
+ else
951
+ "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
952
+ end
856
953
 
857
954
  # Search for the original method in the ancestor
858
955
  original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
@@ -864,21 +961,29 @@ module Solargraph
864
961
  break resolved if resolved
865
962
  end
866
963
 
867
- candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
964
+ candidate_pin.is_a?(Pin::Method)
868
965
  end
869
966
 
870
967
  break if original
871
968
  end
969
+ if original.nil?
970
+ # :nocov:
971
+ Solargraph.assert_or_log(:alias_target_missing) do
972
+ "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}"
973
+ end
974
+ return nil
975
+ # :nocov:
976
+ end
872
977
 
873
978
  # @sg-ignore ignore `received nil` for original
874
- create_resolved_alias_pin(alias_pin, original) if original
979
+ create_resolved_alias_pin(alias_pin, original)
875
980
  end
876
981
 
877
982
  # Fast path for creating resolved alias pins without individual method stack lookups
878
983
  # @param alias_pin [Pin::MethodAlias] The alias pin to resolve
879
984
  # @param original [Pin::Method] The original method pin that was already found
880
985
  # @return [Pin::Method] The resolved method pin
881
- def create_resolved_alias_pin(alias_pin, original)
986
+ def create_resolved_alias_pin alias_pin, original
882
987
  # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup)
883
988
  args = {
884
989
  location: alias_pin.location,
@@ -894,7 +999,7 @@ module Solargraph
894
999
  return_type: original.return_type,
895
1000
  source: :resolve_method_alias
896
1001
  }
897
- resolved_pin = Pin::Method.new **args
1002
+ resolved_pin = Pin::Method.new(**args)
898
1003
 
899
1004
  # Clone signatures and parameters
900
1005
  resolved_pin.signatures.each do |sig|
@@ -916,7 +1021,7 @@ module Solargraph
916
1021
  # @param rooted_type [ComplexType]
917
1022
  # @param pins [Enumerable<Pin::Base>]
918
1023
  # @return [Array<Pin::Base>]
919
- def erase_generics(namespace_pin, rooted_type, pins)
1024
+ def erase_generics namespace_pin, rooted_type, pins
920
1025
  return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
921
1026
 
922
1027
  logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
@@ -927,19 +1032,29 @@ module Solargraph
927
1032
 
928
1033
  # @param namespace_pin [Pin::Namespace]
929
1034
  # @param rooted_type [ComplexType]
930
- def should_erase_generics_when_done?(namespace_pin, rooted_type)
1035
+ def should_erase_generics_when_done? namespace_pin, rooted_type
931
1036
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
932
1037
  end
933
1038
 
934
1039
  # @param namespace_pin [Pin::Namespace, Pin::Constant]
935
- def has_generics?(namespace_pin)
1040
+ def has_generics? namespace_pin
936
1041
  namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
937
1042
  end
938
1043
 
939
1044
  # @param namespace_pin [Pin::Namespace]
940
1045
  # @param rooted_type [ComplexType]
941
- def can_resolve_generics?(namespace_pin, rooted_type)
1046
+ def can_resolve_generics? namespace_pin, rooted_type
942
1047
  has_generics?(namespace_pin) && !rooted_type.all_params.empty?
943
1048
  end
1049
+
1050
+ protected
1051
+
1052
+ # @todo need to model type def statement in chains as a symbol so
1053
+ # that this overload of 'protected' will typecheck @sg-ignore
1054
+ # @sg-ignore
1055
+ def equality_fields
1056
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs,
1057
+ @loose_unions]
1058
+ end
944
1059
  end
945
1060
  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>]