solargraph 0.58.2 → 0.59.0.dev.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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +40 -36
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.gitignore +0 -1
  8. data/.rubocop_todo.yml +27 -49
  9. data/CHANGELOG.md +1 -7
  10. data/README.md +3 -3
  11. data/Rakefile +1 -0
  12. data/lib/solargraph/api_map/cache.rb +3 -3
  13. data/lib/solargraph/api_map/constants.rb +13 -3
  14. data/lib/solargraph/api_map/index.rb +22 -11
  15. data/lib/solargraph/api_map/source_to_yard.rb +13 -1
  16. data/lib/solargraph/api_map/store.rb +11 -8
  17. data/lib/solargraph/api_map.rb +105 -50
  18. data/lib/solargraph/complex_type/conformance.rb +176 -0
  19. data/lib/solargraph/complex_type/type_methods.rb +16 -2
  20. data/lib/solargraph/complex_type/unique_type.rb +170 -20
  21. data/lib/solargraph/complex_type.rb +119 -14
  22. data/lib/solargraph/convention/data_definition/data_definition_node.rb +3 -1
  23. data/lib/solargraph/convention/data_definition.rb +4 -1
  24. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
  25. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +1 -0
  26. data/lib/solargraph/convention/struct_definition.rb +5 -1
  27. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  28. data/lib/solargraph/diagnostics/rubocop.rb +1 -0
  29. data/lib/solargraph/diagnostics/rubocop_helpers.rb +2 -0
  30. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  31. data/lib/solargraph/doc_map.rb +134 -373
  32. data/lib/solargraph/equality.rb +1 -1
  33. data/lib/solargraph/gem_pins.rb +14 -15
  34. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  35. data/lib/solargraph/language_server/host/dispatch.rb +1 -0
  36. data/lib/solargraph/language_server/host/message_worker.rb +2 -1
  37. data/lib/solargraph/language_server/host/sources.rb +1 -0
  38. data/lib/solargraph/language_server/host.rb +6 -1
  39. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -7
  40. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  41. data/lib/solargraph/language_server/message/text_document/completion.rb +2 -0
  42. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  43. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -0
  44. data/lib/solargraph/language_server/message/text_document/formatting.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  46. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -0
  47. data/lib/solargraph/language_server/message/text_document/type_definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -0
  49. data/lib/solargraph/library.rb +59 -13
  50. data/lib/solargraph/location.rb +9 -4
  51. data/lib/solargraph/logging.rb +21 -1
  52. data/lib/solargraph/parser/comment_ripper.rb +7 -0
  53. data/lib/solargraph/parser/flow_sensitive_typing.rb +330 -102
  54. data/lib/solargraph/parser/node_processor/base.rb +32 -2
  55. data/lib/solargraph/parser/node_processor.rb +7 -6
  56. data/lib/solargraph/parser/parser_gem/class_methods.rb +28 -10
  57. data/lib/solargraph/parser/parser_gem/node_chainer.rb +31 -6
  58. data/lib/solargraph/parser/parser_gem/node_methods.rb +27 -7
  59. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  60. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +2 -0
  61. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -11
  63. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +7 -0
  64. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  65. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +3 -2
  66. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  68. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +2 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  70. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +1 -0
  73. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +12 -7
  74. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  75. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +5 -1
  76. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  77. data/lib/solargraph/parser/region.rb +9 -3
  78. data/lib/solargraph/parser/snippet.rb +1 -1
  79. data/lib/solargraph/pin/base.rb +53 -21
  80. data/lib/solargraph/pin/base_variable.rb +312 -20
  81. data/lib/solargraph/pin/block.rb +26 -4
  82. data/lib/solargraph/pin/breakable.rb +5 -1
  83. data/lib/solargraph/pin/callable.rb +50 -3
  84. data/lib/solargraph/pin/closure.rb +2 -6
  85. data/lib/solargraph/pin/common.rb +20 -5
  86. data/lib/solargraph/pin/compound_statement.rb +55 -0
  87. data/lib/solargraph/pin/conversions.rb +2 -1
  88. data/lib/solargraph/pin/delegated_method.rb +15 -4
  89. data/lib/solargraph/pin/documenting.rb +1 -0
  90. data/lib/solargraph/pin/instance_variable.rb +5 -1
  91. data/lib/solargraph/pin/keyword.rb +0 -4
  92. data/lib/solargraph/pin/local_variable.rb +13 -57
  93. data/lib/solargraph/pin/method.rb +90 -42
  94. data/lib/solargraph/pin/method_alias.rb +8 -0
  95. data/lib/solargraph/pin/namespace.rb +7 -1
  96. data/lib/solargraph/pin/parameter.rb +76 -13
  97. data/lib/solargraph/pin/proxy_type.rb +2 -1
  98. data/lib/solargraph/pin/reference/override.rb +1 -1
  99. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  100. data/lib/solargraph/pin/reference.rb +2 -0
  101. data/lib/solargraph/pin/search.rb +1 -0
  102. data/lib/solargraph/pin/signature.rb +8 -0
  103. data/lib/solargraph/pin/symbol.rb +1 -1
  104. data/lib/solargraph/pin/until.rb +1 -1
  105. data/lib/solargraph/pin/while.rb +1 -1
  106. data/lib/solargraph/pin.rb +2 -0
  107. data/lib/solargraph/pin_cache.rb +477 -57
  108. data/lib/solargraph/position.rb +12 -26
  109. data/lib/solargraph/range.rb +6 -6
  110. data/lib/solargraph/rbs_map/conversions.rb +33 -10
  111. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  112. data/lib/solargraph/rbs_map/stdlib_map.rb +34 -5
  113. data/lib/solargraph/rbs_map.rb +74 -20
  114. data/lib/solargraph/shell.rb +73 -28
  115. data/lib/solargraph/source/chain/call.rb +52 -17
  116. data/lib/solargraph/source/chain/constant.rb +2 -0
  117. data/lib/solargraph/source/chain/hash.rb +1 -0
  118. data/lib/solargraph/source/chain/if.rb +1 -0
  119. data/lib/solargraph/source/chain/instance_variable.rb +22 -1
  120. data/lib/solargraph/source/chain/literal.rb +5 -0
  121. data/lib/solargraph/source/chain/or.rb +9 -1
  122. data/lib/solargraph/source/chain.rb +25 -22
  123. data/lib/solargraph/source/change.rb +9 -2
  124. data/lib/solargraph/source/cursor.rb +7 -1
  125. data/lib/solargraph/source/source_chainer.rb +13 -3
  126. data/lib/solargraph/source/updater.rb +4 -0
  127. data/lib/solargraph/source.rb +33 -7
  128. data/lib/solargraph/source_map/clip.rb +13 -2
  129. data/lib/solargraph/source_map/data.rb +4 -1
  130. data/lib/solargraph/source_map/mapper.rb +24 -1
  131. data/lib/solargraph/source_map.rb +14 -6
  132. data/lib/solargraph/type_checker/problem.rb +3 -1
  133. data/lib/solargraph/type_checker/rules.rb +75 -2
  134. data/lib/solargraph/type_checker.rb +111 -30
  135. data/lib/solargraph/version.rb +1 -1
  136. data/lib/solargraph/workspace/config.rb +3 -1
  137. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  138. data/lib/solargraph/workspace/require_paths.rb +1 -0
  139. data/lib/solargraph/workspace.rb +158 -16
  140. data/lib/solargraph/yard_map/helpers.rb +2 -1
  141. data/lib/solargraph/yard_map/mapper/to_method.rb +5 -1
  142. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  143. data/lib/solargraph/yard_map/mapper.rb +5 -0
  144. data/lib/solargraph/yardoc.rb +33 -23
  145. data/lib/solargraph.rb +24 -3
  146. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  147. data/rbs/fills/tuple/tuple.rbs +28 -0
  148. data/rbs/shims/ast/0/node.rbs +1 -1
  149. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  150. data/solargraph.gemspec +2 -1
  151. metadata +12 -7
  152. data/lib/solargraph/type_checker/checks.rb +0 -124
  153. data/lib/solargraph/type_checker/param_def.rb +0 -37
  154. data/lib/solargraph/yard_map/to_method.rb +0 -89
@@ -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
@@ -39,7 +53,7 @@ module Solargraph
39
53
  # @param other [Object]
40
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
 
@@ -48,10 +62,13 @@ module Solargraph
48
62
  self.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
@@ -98,11 +115,11 @@ module Solargraph
98
115
  end
99
116
  unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
100
117
  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
118
+ workspace.rbs_collection_path != bench.workspace.rbs_collection_path ||
119
+ @doc_map.any_uncached?
120
+
104
121
  if recreate_docmap
105
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
122
+ @doc_map = DocMap.new(unresolved_requires, bench.workspace, out: nil) # @todo Implement gem preferences
106
123
  @unresolved_requires = @doc_map.unresolved_requires
107
124
  end
108
125
  @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
@@ -114,27 +131,17 @@ module Solargraph
114
131
  # that this overload of 'protected' will typecheck @sg-ignore
115
132
  # @sg-ignore
116
133
  protected def equality_fields
117
- [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
134
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs, @loose_unions]
118
135
  end
119
136
 
120
137
  # @return [DocMap]
121
138
  def doc_map
122
- @doc_map ||= DocMap.new([], [])
139
+ @doc_map ||= DocMap.new([], Workspace.new('.'))
123
140
  end
124
141
 
125
142
  # @return [::Array<Gem::Specification>]
126
143
  def uncached_gemspecs
127
- @doc_map&.uncached_gemspecs || []
128
- end
129
-
130
- # @return [::Array<Gem::Specification>]
131
- def uncached_rbs_collection_gemspecs
132
- @doc_map.uncached_rbs_collection_gemspecs
133
- end
134
-
135
- # @return [::Array<Gem::Specification>]
136
- def uncached_yard_gemspecs
137
- @doc_map.uncached_yard_gemspecs
144
+ doc_map.uncached_gemspecs || []
138
145
  end
139
146
 
140
147
  # @return [Enumerable<Pin::Base>]
@@ -142,9 +149,10 @@ module Solargraph
142
149
  @@core_map.pins
143
150
  end
144
151
 
145
- # @param name [String]
152
+ # @param name [String, nil]
146
153
  # @return [YARD::Tags::MacroDirective, nil]
147
154
  def named_macro name
155
+ # @sg-ignore Need to add nil check here
148
156
  store.named_macros[name]
149
157
  end
150
158
 
@@ -180,10 +188,11 @@ module Solargraph
180
188
  # Create an ApiMap with a workspace in the specified directory.
181
189
  #
182
190
  # @param directory [String]
191
+ # @param loose_unions [Boolean] See #initialize
183
192
  #
184
193
  # @return [ApiMap]
185
- def self.load directory
186
- api_map = new
194
+ def self.load directory, loose_unions: true
195
+ api_map = new(loose_unions: loose_unions)
187
196
  workspace = Solargraph::Workspace.new(directory)
188
197
  # api_map.catalog Bench.new(workspace: workspace)
189
198
  library = Library.new(workspace)
@@ -192,18 +201,19 @@ module Solargraph
192
201
  api_map
193
202
  end
194
203
 
195
- # @param out [IO, nil]
204
+ # @param out [StringIO, IO, nil]
205
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
196
206
  # @return [void]
197
- def cache_all!(out)
198
- @doc_map.cache_all!(out)
207
+ def cache_all_for_doc_map! out: $stderr, rebuild: false
208
+ doc_map.cache_doc_map_gems!(out, rebuild: rebuild)
199
209
  end
200
210
 
201
211
  # @param gemspec [Gem::Specification]
202
212
  # @param rebuild [Boolean]
203
- # @param out [IO, nil]
213
+ # @param out [StringIO, IO, nil]
204
214
  # @return [void]
205
215
  def cache_gem(gemspec, rebuild: false, out: nil)
206
- @doc_map.cache(gemspec, rebuild: rebuild, out: out)
216
+ doc_map.cache(gemspec, rebuild: rebuild, out: out)
207
217
  end
208
218
 
209
219
  class << self
@@ -215,18 +225,19 @@ module Solargraph
215
225
  #
216
226
  #
217
227
  # @param directory [String]
218
- # @param out [IO] The output stream for messages
228
+ # @param out [IO, StringIO, nil] The output stream for messages
229
+ # @param loose_unions [Boolean] See #initialize
219
230
  #
220
231
  # @return [ApiMap]
221
- def self.load_with_cache directory, out
222
- api_map = load(directory)
232
+ def self.load_with_cache directory, out = $stderr, loose_unions: true
233
+ api_map = load(directory, loose_unions: loose_unions)
223
234
  if api_map.uncached_gemspecs.empty?
224
235
  logger.info { "All gems cached for #{directory}" }
225
236
  return api_map
226
237
  end
227
238
 
228
- api_map.cache_all!(out)
229
- load(directory)
239
+ api_map.cache_all_for_doc_map!(out: out)
240
+ load(directory, loose_unions: loose_unions)
230
241
  end
231
242
 
232
243
  # @return [Array<Solargraph::Pin::Base>]
@@ -328,29 +339,47 @@ module Solargraph
328
339
  # @param namespace [String] A fully qualified namespace
329
340
  # @param scope [Symbol] :instance or :class
330
341
  # @return [Array<Solargraph::Pin::InstanceVariable>]
331
- def get_instance_variable_pins(namespace, scope = :instance)
342
+ def get_instance_variable_pins namespace, scope = :instance
332
343
  result = []
333
344
  used = [namespace]
334
345
  result.concat store.get_instance_variables(namespace, scope)
335
346
  sc_fqns = namespace
336
347
  while (sc = store.get_superclass(sc_fqns))
348
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
337
349
  sc_fqns = store.constants.dereference(sc)
338
350
  result.concat store.get_instance_variables(sc_fqns, scope)
339
351
  end
340
352
  result
341
353
  end
342
354
 
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)
355
+ # Find a variable pin by name and where it is used.
356
+ #
357
+ # Resolves our most specific view of this variable's type by
358
+ # preferring pins created by flow-sensitive typing when we have
359
+ # them based on the Closure and Location.
360
+ #
361
+ # @param candidates [Array<Pin::BaseVariable>]
362
+ # @param name [String]
363
+ # @param closure [Pin::Closure]
364
+ # @param location [Location]
365
+ #
366
+ # @return [Pin::BaseVariable, nil]
367
+ def var_at_location(candidates, name, closure, location)
368
+ with_correct_name = candidates.select { |pin| pin.name == name}
369
+ vars_at_location = with_correct_name.reject do |pin|
370
+ # visible_at? excludes the starting position, but we want to
371
+ # include it for this purpose
372
+ (!pin.visible_at?(closure, location) && !pin.starts_at?(location))
373
+ end
374
+
375
+ vars_at_location.inject(&:combine_with)
347
376
  end
348
377
 
349
378
  # Get an array of class variable pins for a namespace.
350
379
  #
351
380
  # @param namespace [String] A fully qualified namespace
352
381
  # @return [Enumerable<Solargraph::Pin::ClassVariable>]
353
- def get_class_variable_pins(namespace)
382
+ def get_class_variable_pins namespace
354
383
  prefer_non_nil_variables(store.get_class_variables(namespace))
355
384
  end
356
385
 
@@ -512,7 +541,8 @@ module Solargraph
512
541
  fqns = rooted_type.namespace
513
542
  namespace_pin = store.get_path_pins(fqns).first
514
543
  methods = if namespace_pin.is_a?(Pin::Constant)
515
- type = namespace_pin.infer(self)
544
+ type = namespace_pin.typify(self)
545
+ type = namespace_pin.probe(self) unless type.defined?
516
546
  if type.defined?
517
547
  namespace_pin = store.get_path_pins(type.namespace).first
518
548
  get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
@@ -589,6 +619,7 @@ module Solargraph
589
619
  # @param cursor [Source::Cursor]
590
620
  # @return [SourceMap::Clip]
591
621
  def clip cursor
622
+ # @sg-ignore Need to add nil check here
592
623
  raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
593
624
 
594
625
  SourceMap::Clip.new(self, cursor)
@@ -636,12 +667,16 @@ module Solargraph
636
667
  # @todo If two literals are different values of the same type, it would
637
668
  # make more sense for super_and_sub? to return true, but there are a
638
669
  # few callers that currently expect this to be false.
670
+ # @sg-ignore flow-sensitive typing should be able to handle redefinition
639
671
  return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
672
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
640
673
  sup = sup.simplify_literals.to_s
674
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
641
675
  sub = sub.simplify_literals.to_s
642
676
  return true if sup == sub
643
677
  sc_fqns = sub
644
678
  while (sc = store.get_superclass(sc_fqns))
679
+ # @sg-ignore flow sensitive typing needs to handle "if foo = bar"
645
680
  sc_new = store.constants.dereference(sc)
646
681
  # Cyclical inheritance is invalid
647
682
  return false if sc_new == sc_fqns
@@ -669,13 +704,21 @@ module Solargraph
669
704
  with_resolved_aliases = pins.map do |pin|
670
705
  next pin unless pin.is_a?(Pin::MethodAlias)
671
706
  resolved = resolve_method_alias(pin)
707
+ # @sg-ignore Need to add nil check here
672
708
  next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
673
709
  resolved
674
710
  end.compact
675
- logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" }
711
+ logger.debug do
712
+ "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}"
713
+ end
676
714
  GemPins.combine_method_pins_by_path(with_resolved_aliases)
677
715
  end
678
716
 
717
+ # @return [Workspace]
718
+ def workspace
719
+ doc_map.workspace
720
+ end
721
+
679
722
  # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
680
723
  # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
681
724
  # parameter - used to pull generics information
@@ -775,11 +818,13 @@ module Solargraph
775
818
  if scope == :instance
776
819
  store.get_includes(fqns).reverse.each do |ref|
777
820
  in_tag = dereference(ref)
821
+ # @sg-ignore Need to add nil check here
778
822
  result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
779
823
  end
780
824
  rooted_sc_tag = qualify_superclass(rooted_tag)
781
825
  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)
826
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
827
+ visibility, true, skip, no_core)
783
828
  end
784
829
  else
785
830
  logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
@@ -789,7 +834,8 @@ module Solargraph
789
834
  end
790
835
  rooted_sc_tag = qualify_superclass(rooted_tag)
791
836
  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)
837
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
838
+ visibility, true, skip, true)
793
839
  end
794
840
  unless no_core || fqns.empty?
795
841
  type = get_namespace_type(fqns)
@@ -841,18 +887,21 @@ module Solargraph
841
887
 
842
888
  include Logging
843
889
 
844
- private
845
-
846
890
  # @param alias_pin [Pin::MethodAlias]
847
891
  # @return [Pin::Method, nil]
848
892
  def resolve_method_alias(alias_pin)
849
893
  ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
894
+ # @type [Pin::Method, nil]
850
895
  original = nil
851
896
 
852
897
  # Search each ancestor for the original method
853
898
  ancestors.each do |ancestor_fqns|
854
899
  next if ancestor_fqns.nil?
855
- ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
900
+ ancestor_method_path = if alias_pin.original == 'new' && alias_pin.scope == :class
901
+ "#{ancestor_fqns}#initialize"
902
+ else
903
+ "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
904
+ end
856
905
 
857
906
  # Search for the original method in the ancestor
858
907
  original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
@@ -864,14 +913,20 @@ module Solargraph
864
913
  break resolved if resolved
865
914
  end
866
915
 
867
- candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
916
+ candidate_pin.is_a?(Pin::Method)
868
917
  end
869
918
 
870
919
  break if original
871
920
  end
921
+ if original.nil?
922
+ # :nocov:
923
+ Solargraph.assert_or_log(:alias_target_missing) { "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}" }
924
+ return nil
925
+ # :nocov:
926
+ end
872
927
 
873
928
  # @sg-ignore ignore `received nil` for original
874
- create_resolved_alias_pin(alias_pin, original) if original
929
+ create_resolved_alias_pin(alias_pin, original)
875
930
  end
876
931
 
877
932
  # Fast path for creating resolved alias pins without individual method stack lookups
@@ -916,7 +971,7 @@ module Solargraph
916
971
  # @param rooted_type [ComplexType]
917
972
  # @param pins [Enumerable<Pin::Base>]
918
973
  # @return [Array<Pin::Base>]
919
- def erase_generics(namespace_pin, rooted_type, pins)
974
+ def erase_generics namespace_pin, rooted_type, pins
920
975
  return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
921
976
 
922
977
  logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
@@ -927,7 +982,7 @@ module Solargraph
927
982
 
928
983
  # @param namespace_pin [Pin::Namespace]
929
984
  # @param rooted_type [ComplexType]
930
- def should_erase_generics_when_done?(namespace_pin, rooted_type)
985
+ def should_erase_generics_when_done? namespace_pin, rooted_type
931
986
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
932
987
  end
933
988
 
@@ -938,7 +993,7 @@ module Solargraph
938
993
 
939
994
  # @param namespace_pin [Pin::Namespace]
940
995
  # @param rooted_type [ComplexType]
941
- def can_resolve_generics?(namespace_pin, rooted_type)
996
+ def can_resolve_generics? namespace_pin, rooted_type
942
997
  has_generics?(namespace_pin) && !rooted_type.all_params.empty?
943
998
  end
944
999
  end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class ComplexType
5
+ # Checks whether a type can be used in a given situation
6
+ class Conformance
7
+ # @param api_map [ApiMap]
8
+ # @param inferred [ComplexType::UniqueType]
9
+ # @param expected [ComplexType::UniqueType]
10
+ # @param situation [:method_call, :return_type]
11
+ # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match,
12
+ # :allow_any_match, :allow_undefined, :allow_unresolved_generic,
13
+ # :allow_unmatched_interface>]
14
+ # @param variance [:invariant, :covariant, :contravariant]
15
+ def initialize api_map, inferred, expected,
16
+ situation = :method_call, rules = [],
17
+ variance: inferred.erased_variance(situation)
18
+ @api_map = api_map
19
+ @inferred = inferred
20
+ @expected = expected
21
+ @situation = situation
22
+ @rules = rules
23
+ @variance = variance
24
+ # :nocov:
25
+ unless expected.is_a?(UniqueType)
26
+ # @sg-ignore This should never happen and the typechecker is angry about it
27
+ raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}"
28
+ end
29
+ # :nocov:
30
+ return if inferred.is_a?(UniqueType)
31
+ # :nocov:
32
+ # @sg-ignore This should never happen and the typechecker is angry about it
33
+ raise "Inferred type must be a UniqueType, got #{inferred.class} in #{inferred.inspect}"
34
+ # :nocov:
35
+ end
36
+
37
+ def conforms_to_unique_type?
38
+ unless expected.is_a?(UniqueType)
39
+ # :nocov:
40
+ raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}"
41
+ # :nocov:
42
+ end
43
+
44
+ return true if ignore_interface?
45
+ return true if conforms_via_reverse_match?
46
+
47
+ downcast_inferred = inferred.downcast_to_literal_if_possible
48
+ downcast_expected = expected.downcast_to_literal_if_possible
49
+ if (downcast_inferred.name != inferred.name) || (downcast_expected.name != expected.name)
50
+ return with_new_types(downcast_inferred, downcast_expected).conforms_to_unique_type?
51
+ end
52
+
53
+ if rules.include?(:allow_subtype_skew) && !expected.all_params.empty?
54
+ # parameters are not considered in this case
55
+ return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type?
56
+ end
57
+
58
+ return with_new_types(inferred.erase_parameters, expected).conforms_to_unique_type? if only_inferred_parameters?
59
+
60
+ return conforms_via_stripped_expected_parameters? if can_strip_expected_parameters?
61
+
62
+ return true if inferred == expected
63
+
64
+ return false unless erased_type_conforms?
65
+
66
+ return true if inferred.all_params.empty? && rules.include?(:allow_empty_params)
67
+
68
+ # at this point we know the erased type is fine - time to look at parameters
69
+
70
+ # there's an implicit 'any' on the expectation parameters
71
+ # if there are none specified
72
+ return true if expected.all_params.empty?
73
+
74
+ return false unless key_types_conform?
75
+
76
+ subtypes_conform?
77
+ end
78
+
79
+ private
80
+
81
+ def only_inferred_parameters?
82
+ !expected.parameters? && inferred.parameters?
83
+ end
84
+
85
+ def conforms_via_stripped_expected_parameters?
86
+ with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type?
87
+ end
88
+
89
+ def ignore_interface?
90
+ (expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface)) ||
91
+ (inferred.interface? && rules.include?(:allow_unmatched_interface))
92
+ end
93
+
94
+ def can_strip_expected_parameters?
95
+ expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params)
96
+ end
97
+
98
+ def conforms_via_reverse_match?
99
+ return false unless rules.include? :allow_reverse_match
100
+
101
+ expected.conforms_to?(api_map, inferred, situation,
102
+ rules - [:allow_reverse_match],
103
+ variance: variance)
104
+ end
105
+
106
+ def erased_type_conforms?
107
+ case variance
108
+ when :invariant
109
+ return false unless inferred.name == expected.name
110
+ when :covariant
111
+ # covariant: we can pass in a more specific type
112
+ # we contain the expected mix-in, or we have a more specific type
113
+ return false unless api_map.type_include?(inferred.name, expected.name) ||
114
+ api_map.super_and_sub?(expected.name, inferred.name) ||
115
+ inferred.name == expected.name
116
+ when :contravariant
117
+ # contravariant: we can pass in a more general type
118
+ # we contain the expected mix-in, or we have a more general type
119
+ return false unless api_map.type_include?(inferred.name, expected.name) ||
120
+ api_map.super_and_sub?(inferred.name, expected.name) ||
121
+ inferred.name == expected.name
122
+ else
123
+ # :nocov:
124
+ raise "Unknown variance: #{variance.inspect}"
125
+ # :nocov:
126
+ end
127
+ true
128
+ end
129
+
130
+ def key_types_conform?
131
+ return true if expected.key_types.empty?
132
+
133
+ return false if inferred.key_types.empty?
134
+
135
+ unless ComplexType.new(inferred.key_types).conforms_to?(api_map,
136
+ ComplexType.new(expected.key_types),
137
+ situation,
138
+ rules,
139
+ variance: inferred.parameter_variance(situation))
140
+ return false
141
+ end
142
+
143
+ true
144
+ end
145
+
146
+ def subtypes_conform?
147
+ return true if expected.subtypes.empty?
148
+
149
+ return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined)
150
+
151
+ return true if inferred.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined)
152
+
153
+ return true if inferred.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic)
154
+
155
+ return true if expected.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic)
156
+
157
+ return false if inferred.subtypes.empty?
158
+
159
+ ComplexType.new(inferred.subtypes).conforms_to?(api_map,
160
+ ComplexType.new(expected.subtypes),
161
+ situation,
162
+ rules,
163
+ variance: inferred.parameter_variance(situation))
164
+ end
165
+
166
+ # @return [self]
167
+ # @param inferred [ComplexType::UniqueType]
168
+ # @param expected [ComplexType::UniqueType]
169
+ def with_new_types inferred, expected
170
+ self.class.new(api_map, inferred, expected, situation, rules, variance: variance)
171
+ end
172
+
173
+ attr_reader :api_map, :inferred, :expected, :situation, :rules, :variance
174
+ end
175
+ end
176
+ end
@@ -73,6 +73,18 @@ module Solargraph
73
73
  name == 'undefined'
74
74
  end
75
75
 
76
+ # Variance of the type ignoring any type parameters
77
+ # @return [Symbol]
78
+ # @param situation [Symbol] The situation in which the variance is being considered.
79
+ def erased_variance situation = :method_call
80
+ # :nocov:
81
+ unless %i[method_call return_type assignment].include?(situation)
82
+ raise "Unknown situation: #{situation.inspect}"
83
+ end
84
+ # :nocov:
85
+ :covariant
86
+ end
87
+
76
88
  # @param generics_to_erase [Enumerable<String>]
77
89
  # @return [self]
78
90
  def erase_generics(generics_to_erase)
@@ -194,7 +206,7 @@ module Solargraph
194
206
  # @param other [Object]
195
207
  def == other
196
208
  return false unless self.class == other.class
197
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
209
+ # @sg-ignore flow sensitive typing should support .class == .class
198
210
  tag == other.tag
199
211
  end
200
212
 
@@ -218,7 +230,9 @@ module Solargraph
218
230
  end
219
231
 
220
232
  # @yieldparam [UniqueType]
221
- # @return [Enumerator<UniqueType>]
233
+ # @return [void]
234
+ # @overload each_unique_type()
235
+ # @return [Enumerator<UniqueType>]
222
236
  def each_unique_type &block
223
237
  return enum_for(__method__) unless block_given?
224
238
  yield self