solargraph 0.61.0 → 0.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +24 -2
  3. data/.github/workflows/plugins.yml +18 -0
  4. data/.github/workflows/rspec.yml +14 -1
  5. data/.github/workflows/typecheck.yml +6 -0
  6. data/CHANGELOG.md +38 -0
  7. data/lib/solargraph/api_map/index.rb +14 -3
  8. data/lib/solargraph/api_map/store.rb +6 -0
  9. data/lib/solargraph/api_map.rb +34 -2
  10. data/lib/solargraph/collection/gem.rb +12 -10
  11. data/lib/solargraph/collection.rb +0 -1
  12. data/lib/solargraph/complex_type/unique_type.rb +13 -0
  13. data/lib/solargraph/complex_type.rb +23 -3
  14. data/lib/solargraph/convention.rb +0 -4
  15. data/lib/solargraph/diagnostics/type_check.rb +0 -1
  16. data/lib/solargraph/external.rb +56 -28
  17. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  18. data/lib/solargraph/language_server/host/dispatch.rb +4 -2
  19. data/lib/solargraph/language_server/host.rb +10 -2
  20. data/lib/solargraph/language_server/message/completion_item/resolve.rb +10 -3
  21. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  22. data/lib/solargraph/library.rb +19 -33
  23. data/lib/solargraph/parser/node_processor/base.rb +6 -0
  24. data/lib/solargraph/parser/parser_gem/node_methods.rb +29 -6
  25. data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb +98 -0
  26. data/lib/solargraph/parser/parser_gem/node_processors/class_definition.rb +54 -0
  27. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +1 -0
  28. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node.rb +65 -0
  29. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node.rb +97 -0
  30. data/lib/solargraph/parser/parser_gem/node_processors/data_definition.rb +110 -0
  31. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +1 -0
  32. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -0
  33. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  34. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +185 -0
  35. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_assignment_node.rb +66 -0
  36. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_definition_node.rb +108 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition.rb +170 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors.rb +33 -29
  39. data/lib/solargraph/pin/base.rb +25 -1
  40. data/lib/solargraph/pin/base_variable.rb +31 -1
  41. data/lib/solargraph/pin/callable.rb +11 -2
  42. data/lib/solargraph/pin/delegated_method.rb +5 -0
  43. data/lib/solargraph/pin/duck_method.rb +36 -16
  44. data/lib/solargraph/pin/instance_variable.rb +45 -0
  45. data/lib/solargraph/pin/method.rb +10 -0
  46. data/lib/solargraph/pin/method_alias.rb +4 -0
  47. data/lib/solargraph/pin/parameter.rb +1 -1
  48. data/lib/solargraph/pin/reference/included_mixin.rb +22 -0
  49. data/lib/solargraph/pin/reference.rb +1 -0
  50. data/lib/solargraph/plugin/active_support.rb +100 -0
  51. data/lib/solargraph/plugin/addon.rb +16 -0
  52. data/lib/solargraph/plugin/base.rb +25 -0
  53. data/lib/solargraph/plugin/panel.rb +37 -0
  54. data/lib/solargraph/plugin.rb +26 -0
  55. data/lib/solargraph/rbs_collection.rb +119 -0
  56. data/lib/solargraph/rbs_map/conversions.rb +106 -47
  57. data/lib/solargraph/rbs_map/gem.rb +4 -1
  58. data/lib/solargraph/rbs_map/helpers.rb +7 -6
  59. data/lib/solargraph/rbs_translator.rb +105 -70
  60. data/lib/solargraph/repo.rb +1 -1
  61. data/lib/solargraph/source/chain/call.rb +32 -13
  62. data/lib/solargraph/source/node_map.rb +27 -0
  63. data/lib/solargraph/source.rb +17 -0
  64. data/lib/solargraph/source_map/call.rb +18 -0
  65. data/lib/solargraph/source_map/mapper.rb +1 -1
  66. data/lib/solargraph/source_map.rb +18 -4
  67. data/lib/solargraph/type_checker/rules.rb +2 -2
  68. data/lib/solargraph/type_checker.rb +4 -1
  69. data/lib/solargraph/version.rb +1 -1
  70. data/lib/solargraph/yard_map/macro.rb +51 -1
  71. data/lib/solargraph.rb +2 -0
  72. data/rbs/fills/boolean/0/boolean.rbs +12 -0
  73. data/solargraph.gemspec +1 -1
  74. metadata +21 -11
  75. data/lib/solargraph/collection/rbs.rb +0 -49
  76. data/lib/solargraph/convention/active_support_concern.rb +0 -111
  77. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +0 -61
  78. data/lib/solargraph/convention/data_definition/data_definition_node.rb +0 -93
  79. data/lib/solargraph/convention/data_definition.rb +0 -108
  80. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +0 -62
  81. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +0 -103
  82. data/lib/solargraph/convention/struct_definition.rb +0 -168
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b386e8d7447871b7850f167cab1483351050f92debdc40bb4b2ee9538e820e1
4
- data.tar.gz: 3b88afe53d55f28b96004941ff37c2c8c9254c7e83ed597d90122aa1b6e374c2
3
+ metadata.gz: 8b2496884d72c8abed7340388dc6bd09f8be728ba93724c6146366b51a12c595
4
+ data.tar.gz: b8f1be6afe684fc5a50349958937a12073dab1b7f5ff4574234e3ffaa57fc164
5
5
  SHA512:
6
- metadata.gz: 97f94421c920c20c8c5e2d5d8deae43ef7559cd082cedc022041ebc22252c04cb713e25fb8d5e96af1c258fe7aecffbc527c88829876944868911e31f20990ee
7
- data.tar.gz: ad5d661d4340531998b669b8d499922f2c05c2fc94d04f1cdb9c2ed3ce207d0aa08875fd18e9a63f6e5a9c0688354e8ea8ada6bd865f76eaaa5a92393d2c6917
6
+ metadata.gz: 3414b07c09f728a8418bf1fb97d58eeb57a8496819c4a8340595750e6f59d952f77ea0cc3ce2e44705894905f773d3073c793e987d5b0e92aeea8ed4c29e835d
7
+ data.tar.gz: eb38a46cf34401299101a91be9b1c113dd977784e6c17c25aae78e2554a932bb46ac7d822dd303753e353f2b1daba6cf8186d26807b418d2804e725f60dc2a14
@@ -28,6 +28,12 @@ jobs:
28
28
  with:
29
29
  fetch-depth: 0
30
30
 
31
+ - name: Install libjitterentropy.a for rugged
32
+ # Ubuntu 26.04's libcrypto.pc links it statically
33
+ run: |
34
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
35
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
36
+ fi
31
37
  - uses: ruby/setup-ruby@v1
32
38
  with:
33
39
  ruby-version: 3.4
@@ -66,6 +72,12 @@ jobs:
66
72
  runs-on: ubuntu-latest
67
73
  steps:
68
74
  - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
75
+ - name: Install libjitterentropy.a for rugged
76
+ # Ubuntu 26.04's libcrypto.pc links it statically
77
+ run: |
78
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
79
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
80
+ fi
69
81
  - uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0
70
82
  with:
71
83
  ruby-version: '3.3'
@@ -86,6 +98,12 @@ jobs:
86
98
 
87
99
  steps:
88
100
  - uses: actions/checkout@v3
101
+ - name: Install libjitterentropy.a for rugged
102
+ # Ubuntu 26.04's libcrypto.pc links it statically
103
+ run: |
104
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
105
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
106
+ fi
89
107
  - name: Set up Ruby
90
108
  uses: ruby/setup-ruby@v1
91
109
  with:
@@ -97,14 +115,18 @@ jobs:
97
115
 
98
116
  - name: Run rbs validate
99
117
  run: bundle exec rbs validate
100
- # @todo Temporary, expect to revert in 0.60
101
- continue-on-error: true
102
118
  rubocop_todo:
103
119
  name: .rubocop_todo.yml
104
120
  runs-on: ubuntu-latest
105
121
 
106
122
  steps:
107
123
  - uses: actions/checkout@v3
124
+ - name: Install libjitterentropy.a for rugged
125
+ # Ubuntu 26.04's libcrypto.pc links it statically
126
+ run: |
127
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
128
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
129
+ fi
108
130
  - name: Set up Ruby
109
131
  uses: ruby/setup-ruby@v1
110
132
  with:
@@ -20,6 +20,12 @@ jobs:
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
23
+ - name: Install libjitterentropy.a for rugged
24
+ # Ubuntu 26.04's libcrypto.pc links it statically
25
+ run: |
26
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
27
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
28
+ fi
23
29
  - name: Set up Ruby
24
30
  uses: ruby/setup-ruby@v1
25
31
  with:
@@ -54,6 +60,12 @@ jobs:
54
60
 
55
61
  steps:
56
62
  - uses: actions/checkout@v3
63
+ - name: Install libjitterentropy.a for rugged
64
+ # Ubuntu 26.04's libcrypto.pc links it statically
65
+ run: |
66
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
67
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
68
+ fi
57
69
  - name: Set up Ruby
58
70
  uses: ruby/setup-ruby@v1
59
71
  with:
@@ -89,6 +101,12 @@ jobs:
89
101
 
90
102
  steps:
91
103
  - uses: actions/checkout@v3
104
+ - name: Install libjitterentropy.a for rugged
105
+ # Ubuntu 26.04's libcrypto.pc links it statically
106
+ run: |
107
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
108
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
109
+ fi
92
110
  - name: Set up Ruby
93
111
  uses: ruby/setup-ruby@v1
94
112
  with:
@@ -18,7 +18,8 @@ permissions:
18
18
 
19
19
  jobs:
20
20
  rspec:
21
- runs-on: ubuntu-latest
21
+ # ruby/setup-ruby has no Ruby 3.1 build for Ubuntu 26.04
22
+ runs-on: ${{ matrix.ruby-version == '3.1' && 'ubuntu-24.04' || 'ubuntu-latest' }}
22
23
  strategy:
23
24
  matrix:
24
25
  # @todo Restore 'head' once ruby/setup-ruby publishes it for ubuntu-24.04.
@@ -35,6 +36,12 @@ jobs:
35
36
  rbs-version: '4.0.2'
36
37
  steps:
37
38
  - uses: actions/checkout@v3
39
+ - name: Install libjitterentropy.a for rugged
40
+ # Ubuntu 26.04's libcrypto.pc links it statically
41
+ run: |
42
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
43
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
44
+ fi
38
45
  - name: Set up Ruby
39
46
  uses: ruby/setup-ruby@v1
40
47
  with:
@@ -65,6 +72,12 @@ jobs:
65
72
  # fetch all history for all branches and tags so we can
66
73
  # compare against origin/master
67
74
  fetch-depth: 0
75
+ - name: Install libjitterentropy.a for rugged
76
+ # Ubuntu 26.04's libcrypto.pc links it statically
77
+ run: |
78
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
79
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
80
+ fi
68
81
  - name: Set up Ruby
69
82
  uses: ruby/setup-ruby@v1
70
83
  with:
@@ -24,6 +24,12 @@ jobs:
24
24
 
25
25
  steps:
26
26
  - uses: actions/checkout@v3
27
+ - name: Install libjitterentropy.a for rugged
28
+ # Ubuntu 26.04's libcrypto.pc links it statically
29
+ run: |
30
+ if pkg-config --libs --static libcrypto | grep -q jitterentropy; then
31
+ sudo apt-get update && sudo apt-get install -y libjitterentropy3-dev
32
+ fi
27
33
  - name: Set up Ruby
28
34
  uses: ruby/setup-ruby@v1
29
35
  with:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ ## 0.62.0 - October 30, 2026
2
+ - Fix false positive: Struct.new(keyword_init: true) members are optional (#1269)
3
+ - Strip nil from or-expression fallbacks in conditional branch values (#1309)
4
+ - Map Forwardable delegations to method pins (#1311)
5
+ - Resolve a self return type per union member (#1314)
6
+ - Read implicitly-returns-nil per RBS overload, not from the first one (#1316)
7
+ - Make `rbs validate` a blocking CI check again (#1317)
8
+ - Add nil protection to definition (#1427)
9
+ - Index `X = Class.new(Super)` constant assignments as classes (#1306)
10
+ - Map gems without YARD (#1378)
11
+ - Loosen the Rubocop development dependency (#1428)
12
+ - [regression fix] Build rugged on Ubuntu 26.04 CI runners (#1436)
13
+ - Skip Diagnoser#tick when the queue is drained (#1426)
14
+ - Stop flagging anonymous argument forwarding (#1400)
15
+ - Resolve instance variable type when assigned only in an ancestor class's method (#1345)
16
+ - Apply @!override to constants instead of crashing (#1305)
17
+ - Resolve generic type variables against union @param types (#1299)
18
+ - ensure clause breaks return type inference (#1285)
19
+ - Improved RBS type alias support (#1281)
20
+ - Bind reopened generic parameters by position (#1411)
21
+ - Apply mixins from self.included hooks to includers (#1405)
22
+ - Define Boolean so its operators resolve (#1351)
23
+ - Stop reporting a duck type's own method as unresolved (#1280)
24
+ - Keep the generic return type when a method takes a block (#1274)
25
+ - [regression fix] Stop rerunning a gem cache command that failed (#1442)
26
+ - Remove Dispatch#update's hidden assumption that self is a Host (#1440)
27
+ - Default anonymous type shorthand names (#1454)
28
+ - New Plugin architecture with ActiveSupport implementation (#1435)
29
+ - Keep the adapter spec from hanging in Backport.run (#1449)
30
+ - Stop mapping a gem's non-sig RBS as its API (#1453)
31
+ - Type variables from rbs-inline #: assertions on assignments (#1444)
32
+ - Resolve inline RBS names in enclosing namespace (#1447)
33
+ - Enable pending clip spec (#1462)
34
+ - Remove Convention::ActiveSupportConcern (#1469)
35
+ - Move Class, Struct, and Data definition processors to NodeProcessors (#1468)
36
+ - Preserve immutable completion item properties during resolve (#1471)
37
+ - [regression fix] Cache gems that have a leftover YARD lock (#1465)
38
+
1
39
  ## 0.61.0 - October 7, 2026
2
40
  - Clips qualify constant pins for completion (#1368)
3
41
  - Unused spec tags (#1370)
@@ -85,6 +85,13 @@ module Solargraph
85
85
  @prepend_references ||= Hash.new { |h, k| h[k] = [] }
86
86
  end
87
87
 
88
+ # @return [Hash{String => Array<Pin::Reference::IncludedMixin>}]
89
+ def included_mixin_references
90
+ # @param h [String]
91
+ # @param k [Array<Pin::Reference::IncludedMixin>]
92
+ @included_mixin_references ||= Hash.new { |h, k| h[k] = [] }
93
+ end
94
+
88
95
  # @return [Hash{String => Array<Pin::Reference::Superclass>}]
89
96
  def superclass_references
90
97
  # @param h [String]
@@ -107,7 +114,7 @@ module Solargraph
107
114
 
108
115
  attr_writer :pins, :pin_select_cache, :namespace_hash, :pin_class_hash, :path_pin_hash, :include_references,
109
116
  :extend_references, :prepend_references, :superclass_references, :factory_parameter_hash,
110
- :macro_method_names, :macro_method_name_pins
117
+ :macro_method_names, :macro_method_name_pins, :included_mixin_references
111
118
 
112
119
  # @return [self]
113
120
  def deep_clone
@@ -117,7 +124,7 @@ module Solargraph
117
124
  copy.macro_method_names = macro_method_names
118
125
  %i[
119
126
  namespace_hash pin_class_hash path_pin_hash include_references extend_references prepend_references
120
- superclass_references macro_method_name_pins
127
+ superclass_references macro_method_name_pins included_mixin_references
121
128
  ].each do |sym|
122
129
  copy.send("#{sym}=", send(sym).clone)
123
130
  copy.send(sym)&.transform_values!(&:clone)
@@ -150,6 +157,7 @@ module Solargraph
150
157
  map_references Pin::Reference::Prepend, prepend_references
151
158
  map_references Pin::Reference::Extend, extend_references
152
159
  map_references Pin::Reference::Superclass, superclass_references
160
+ map_references Pin::Reference::IncludedMixin, included_mixin_references
153
161
  macro_pins = pins_by_class(Pin::Method).select { |pin| pin.macros.any? }
154
162
  @macro_method_names = macro_pins.to_set(&:name)
155
163
  @macro_method_name_pins = macro_pins.to_set.classify(&:name)
@@ -204,7 +212,7 @@ module Solargraph
204
212
  end
205
213
  end
206
214
 
207
- # @param pin [Pin::Method]
215
+ # @param pin [Pin::Base]
208
216
  # @param tag [YARD::Tags::Tag]
209
217
  # @return [void]
210
218
  def redefine_return_type pin, tag
@@ -212,6 +220,9 @@ module Solargraph
212
220
  # proxy() / proxy_with_signatures() instead?
213
221
  return unless pin && tag.tag_name == 'return'
214
222
  pin.instance_variable_set(:@return_type, ComplexType.try_parse(tag.type))
223
+ # Only methods carry signatures; constants and other pins have
224
+ # just the one return type set above.
225
+ return unless pin.is_a?(Pin::Method)
215
226
  pin.signatures.each do |sig|
216
227
  sig.instance_variable_set(:@return_type, ComplexType.try_parse(tag.type))
217
228
  end
@@ -124,6 +124,12 @@ module Solargraph
124
124
  extend_references[fqns] || []
125
125
  end
126
126
 
127
+ # @param fqns [String]
128
+ # @return [Array<Pin::Reference::IncludedMixin>]
129
+ def get_included_mixins fqns
130
+ index.included_mixin_references[fqns] || []
131
+ end
132
+
127
133
  # @param path [String]
128
134
  # @return [Array<Solargraph::Pin::Base>]
129
135
  def get_path_pins path
@@ -106,10 +106,20 @@ module Solargraph
106
106
  source_map_hash.each_value do |map|
107
107
  conventions_environ.merge map.conventions_environ
108
108
  end
109
+ addon = Plugin::Addon.new
110
+ Plugin.registry.each do |plugin|
111
+ panel ||= Plugin::Panel.new(source_maps: bench.source_maps, live_map: bench.live_map, gem_names: external.configured_gem_names)
112
+ added = plugin.new(panel).addon
113
+ addon.gem_names.concat added.gem_names
114
+ addon.pins.concat added.pins
115
+ rescue StandardError => e
116
+ Solargraph.logger.warn "#{plugin} failed - [#{e.class}] #{e.message}"
117
+ end
118
+
109
119
  # @todo Determine what needs to be sent to global conventions
110
120
  conventions_environ.merge Convention.for_global(nil)
111
- external_changed = external.update(bench.external_requires.to_a + conventions_environ.requires)
112
- store_changed = store.update(@@core_pins, external.pins.clone, conventions_environ.pins, iced_pins, live_pins) { process_macros }
121
+ external_changed = external.update(bench.external_requires.to_a + conventions_environ.requires, addon.gem_names)
122
+ store_changed = store.update(@@core_pins, external.pins.clone, conventions_environ.pins, addon.pins, iced_pins, live_pins) { process_macros }
113
123
  @cache.clear if external_changed || store_changed
114
124
  Solargraph.logger.info "Cataloging ApiMap finished in #{Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time} seconds"
115
125
  self
@@ -769,6 +779,22 @@ module Solargraph
769
779
  methods
770
780
  end
771
781
 
782
+ # Mixins that +self.included+ hooks of the modules included in fqns apply
783
+ # to it, e.g. +base.extend ClassMethods+
784
+ #
785
+ # @param fqns [String]
786
+ # @param keywords [Array<::Symbol>] which of :include, :prepend and :extend
787
+ # @return [Array<String>]
788
+ def included_hook_mixins fqns, keywords
789
+ store.get_includes(fqns).flat_map do |ref|
790
+ included = dereference(ref)
791
+ next [] if included.nil?
792
+ store.get_included_mixins(ComplexType.parse(included).namespace)
793
+ .select { |mixin| keywords.include?(mixin.keyword) }
794
+ .filter_map { |mixin| dereference(mixin) }
795
+ end
796
+ end
797
+
772
798
  # @param fq_sub_tag [String]
773
799
  # @return [String, nil]
774
800
  def qualify_superclass fq_sub_tag
@@ -841,6 +867,9 @@ module Solargraph
841
867
  result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep,
842
868
  skip, true)
843
869
  end
870
+ included_hook_mixins(fqns, %i[include prepend]).each do |mixin|
871
+ result.concat inner_get_methods(mixin, scope, visibility, deep, skip, true)
872
+ end
844
873
  rooted_sc_tag = qualify_superclass(rooted_tag)
845
874
  unless rooted_sc_tag.nil?
846
875
  result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
@@ -854,6 +883,9 @@ module Solargraph
854
883
  fqem = dereference(em)
855
884
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
856
885
  end
886
+ included_hook_mixins(fqns, [:extend]).each do |mixin|
887
+ result.concat inner_get_methods(mixin, :instance, visibility, deep, skip, true)
888
+ end
857
889
  rooted_sc_tag = qualify_superclass(rooted_tag)
858
890
  unless rooted_sc_tag.nil?
859
891
  result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
@@ -9,6 +9,8 @@ module Solargraph
9
9
 
10
10
  attr_reader :metagem
11
11
 
12
+ @@path_source_cache = {}
13
+
12
14
  # @param metagem [Metagem]
13
15
  def initialize metagem
14
16
  super()
@@ -20,16 +22,12 @@ module Solargraph
20
22
  end
21
23
 
22
24
  def load
23
- return pins unless metagem.cacheable?
24
- super
25
+ return super if metagem.cacheable?
26
+ @@path_source_cache[metagem.full_path] ||= pins
25
27
  end
26
28
 
27
29
  def pins
28
- @pins ||= if metagem.cacheable?
29
- cacheable_pins
30
- else
31
- uncacheable_pins
32
- end
30
+ @pins ||= without_yard
33
31
  end
34
32
 
35
33
  def self.cached? metagem
@@ -38,17 +36,21 @@ module Solargraph
38
36
 
39
37
  private
40
38
 
41
- def cacheable_pins
39
+ def with_yard
42
40
  code_objects = Yardoc.load!(metagem)
43
41
  yard_pins = YardMap::Mapper.new(code_objects, metagem).map
44
42
  rbs_pins = RbsMap::Gem.pins(metagem)
45
43
  RbsMap::Helpers.combine(yard_pins, rbs_pins)
46
44
  end
47
45
 
48
- def uncacheable_pins
46
+ def without_yard
49
47
  files = metagem.require_paths.flat_map { |path| Dir.glob(File.join(metagem.full_path, path, '**', '*.rb')) }
50
48
  source_maps = files.map { |file| Solargraph::SourceMap.load(file) }
51
- source_pins = source_maps.flat_map(&:pins)
49
+
50
+ # Generating an ApiMap is necessary for processing macros
51
+ bench = Bench.new(source_maps: source_maps)
52
+ source_pins = ApiMap.new.catalog(bench).pins.select { |pin| files.include?(pin.filename) }
53
+
52
54
  rbs_pins = RbsMap::Gem.pins(metagem)
53
55
  RbsMap::Helpers.combine(source_pins, rbs_pins)
54
56
  end
@@ -5,7 +5,6 @@ module Solargraph
5
5
  autoload :Base, 'solargraph/collection/base'
6
6
  autoload :Core, 'solargraph/collection/core'
7
7
  autoload :Gem, 'solargraph/collection/gem'
8
- autoload :Rbs, 'solargraph/collection/rbs'
9
8
  autoload :Stdlib, 'solargraph/collection/stdlib'
10
9
 
11
10
  # A hash for storing pin collections in memory.
@@ -11,6 +11,13 @@ module Solargraph
11
11
 
12
12
  attr_reader :all_params, :subtypes, :key_types
13
13
 
14
+ # @type [Hash{String => String}]
15
+ ANONYMOUS_NAME_BY_STARTING_TAG = {
16
+ '{' => 'Hash',
17
+ '(' => 'Array',
18
+ '<' => 'Array'
19
+ }.freeze
20
+
14
21
  # Create a UniqueType with the specified name and an optional substring.
15
22
  # The substring is the parameter section of a parametrized type, e.g.,
16
23
  # for the type `Array<String>`, the name is `Array` and the substring is
@@ -22,6 +29,9 @@ module Solargraph
22
29
  # @return [UniqueType]
23
30
  def self.parse name, substring = '', make_rooted: nil
24
31
  raise ComplexTypeError, "Illegal prefix: #{name}" if name.start_with?(':::')
32
+ # Anonymous shorthand (`<A>`, `(A)`, `{A=>B}`) defaults the
33
+ # omitted type name to Array or Hash, before the rooted check below.
34
+ name = ANONYMOUS_NAME_BY_STARTING_TAG.fetch(substring.chars.fetch(0)) if name.empty? && !substring.empty?
25
35
  if name.start_with?('::')
26
36
  name = name[2..]
27
37
  rooted = true
@@ -443,6 +453,9 @@ module Solargraph
443
453
  # @param context_type [ComplexType] The receiver type
444
454
  # @return [UniqueType, ComplexType]
445
455
  def resolve_generics definitions, context_type
456
+ # Method-scoped generics bind per call site via
457
+ # resolve_generics_from_context, not against a receiver's type params.
458
+ return self if definitions.is_a?(Pin::Callable)
446
459
  return self if definitions.nil? || definitions.generics.empty?
447
460
 
448
461
  transform(name) do |t|
@@ -50,9 +50,10 @@ module Solargraph
50
50
  def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
51
51
  return self unless generic?
52
52
 
53
- ComplexType.new(@items.map do |i|
54
- i.resolve_generics_from_context(generics_to_resolve, context_type,
55
- resolved_generic_values: resolved_generic_values)
53
+ ComplexType.new(@items.map do |unique_type|
54
+ unique_type.resolve_generics_from_context(generics_to_resolve,
55
+ destructure_context_type(unique_type, context_type),
56
+ resolved_generic_values: resolved_generic_values)
56
57
  end)
57
58
  end
58
59
 
@@ -561,6 +562,25 @@ module Solargraph
561
562
 
562
563
  private
563
564
 
565
+ # Narrows what a generic member binds against: context members this type
566
+ # already matches concretely are dropped. Nothing left over means no value
567
+ # can reach the generic, which is bot.
568
+ #
569
+ # @param unique_type [UniqueType] A member of @items
570
+ # @param context_type [ComplexType, UniqueType, nil]
571
+ # @return [ComplexType, UniqueType, nil]
572
+ def destructure_context_type unique_type, context_type
573
+ return context_type unless unique_type.generic? && context_type.is_a?(ComplexType) && @items.length > 1
574
+
575
+ concrete_items = @items.reject(&:generic?)
576
+ return context_type if concrete_items.empty?
577
+
578
+ remaining_items = context_type.items.reject { |ct| concrete_items.any? { |ci| ci.name == ct.name } }
579
+ return BOT if remaining_items.empty?
580
+
581
+ ComplexType.new(remaining_items)
582
+ end
583
+
564
584
  # @todo This is a quick and dirty hack that forces `self` keywords
565
585
  # to reference an instance of their class and never the class itself.
566
586
  # This behavior may change depending on which result is expected
@@ -9,9 +9,6 @@ module Solargraph
9
9
  autoload :Gemfile, 'solargraph/convention/gemfile'
10
10
  autoload :Gemspec, 'solargraph/convention/gemspec'
11
11
  autoload :Rakefile, 'solargraph/convention/rakefile'
12
- autoload :StructDefinition, 'solargraph/convention/struct_definition'
13
- autoload :DataDefinition, 'solargraph/convention/data_definition'
14
- autoload :ActiveSupportConcern, 'solargraph/convention/active_support_concern'
15
12
 
16
13
  # @type [Set<Convention::Base>]
17
14
  @@conventions = Set.new
@@ -75,6 +72,5 @@ module Solargraph
75
72
  register Gemfile
76
73
  register Gemspec
77
74
  register Rakefile
78
- register ActiveSupportConcern
79
75
  end
80
76
  end
@@ -11,7 +11,6 @@ module Solargraph
11
11
  # return [] unless args.include?('always') || api_map.workspaced?(source.filename)
12
12
  severity = Diagnostics::Severities::ERROR
13
13
  level = args.reverse.find { |a| %w[normal typed strict strong].include?(a) } || :normal
14
- # @sg-ignore sensitive typing needs to handle || on nil types
15
14
  checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym)
16
15
  checker.problems
17
16
  .sort { |a, b| a.location.range.start.line <=> b.location.range.start.line }
@@ -13,15 +13,23 @@ module Solargraph
13
13
  # @return [Array<String>]
14
14
  attr_reader :requires
15
15
 
16
+ attr_reader :extra_gem_names
17
+
16
18
  # @param directory [String]
17
19
  # @param requires [Array<String>]
18
- def initialize directory, requires
20
+ # @param extra_gem_names [Array<String>]
21
+ def initialize directory, requires, extra_gem_names = []
19
22
  @repo = Repo.new(directory)
20
23
  @directory = directory
21
24
  @requires = requires
25
+ @extra_gem_names = extra_gem_names
22
26
  update!
23
27
  end
24
28
 
29
+ def configured_gem_names
30
+ loaded_gems.map(&:name) + unloaded_gems.map(&:name) + extra_gem_names
31
+ end
32
+
25
33
  def unresolved_requires
26
34
  @unresolved_requires ||= []
27
35
  end
@@ -38,6 +46,14 @@ module Solargraph
38
46
  @unloaded_gems ||= Set.new
39
47
  end
40
48
 
49
+ def unfound_gem_names
50
+ @unfound_gem_names ||= []
51
+ end
52
+
53
+ def loaded_stdlibs
54
+ @loaded_stdlibs ||= Set.new
55
+ end
56
+
41
57
  def pins
42
58
  @pins ||= []
43
59
  end
@@ -49,10 +65,11 @@ module Solargraph
49
65
 
50
66
  # @param new_requires [Array<String>]
51
67
  # @return [Boolean]
52
- def update new_requires
53
- return false if requires == new_requires && !cache_changed?
68
+ def update new_requires, new_gem_names = []
69
+ return false if requires == new_requires && new_gem_names == extra_gem_names && !cache_changed?
54
70
 
55
71
  requires.replace new_requires
72
+ extra_gem_names.replace new_gem_names
56
73
  update!
57
74
  true
58
75
  end
@@ -77,7 +94,8 @@ module Solargraph
77
94
  @generation = generation + 1
78
95
  clear_all
79
96
  load_requires
80
- load_rbs_collection
97
+ load_gem_names
98
+ # load_rbs_collection
81
99
  end
82
100
 
83
101
  def cache_changed?
@@ -92,7 +110,7 @@ module Solargraph
92
110
  bundler_require = true
93
111
  end
94
112
  if RbsMap::Stdlib.has?(path)
95
- pins.concat Collection::Stdlib.load(path)
113
+ pins.concat Collection::Stdlib.load(path) if loaded_stdlibs.add?(path)
96
114
  else
97
115
  metagem = @repo.find_by_path(path)
98
116
  next unresolved_requires.push(path) unless metagem
@@ -105,9 +123,22 @@ module Solargraph
105
123
  @repo.find_by_group(:default).each { |metagem| process_gem metagem }
106
124
  end
107
125
 
108
- def load_rbs_collection
109
- rbs_collection_pins = rbs_collection_paths.flat_map { |path| Collection::Rbs.load(path) }
110
- pins.replace RbsMap::Helpers.combine(pins, rbs_collection_pins)
126
+ def load_gem_names
127
+ extra_gem_names.uniq.each do |gem|
128
+ metagem = @repo.find_by_name(gem)
129
+ next unfound_gem_names.push(gem) unless metagem
130
+ process_gem metagem
131
+ end
132
+ end
133
+
134
+ def rbs_collection
135
+ @rbs_collection ||= RbsCollection.new(rbs_collection_lockfile)
136
+ end
137
+
138
+ def rbs_collection_lockfile
139
+ return if directory.nil? || directory.empty? || directory == '*'
140
+ lockfile = File.join(directory, 'rbs_collection.lock.yaml')
141
+ lockfile if File.file?(lockfile)
111
142
  end
112
143
 
113
144
  def clear_all
@@ -116,21 +147,30 @@ module Solargraph
116
147
  unresolved_dependencies.clear
117
148
  loaded_gems.clear
118
149
  unloaded_gems.clear
150
+ loaded_stdlibs.clear
151
+ unfound_gem_names.clear
119
152
  end
120
153
 
121
154
  def process_gem metagem
122
155
  return if loaded_gems.include?(metagem) || unloaded_gems.include?(metagem)
123
156
 
124
- if metagem.cacheable?
125
- if Collection::Gem.cached?(metagem)
126
- loaded_gems.add metagem
127
- pins.concat Collection::Gem.load(metagem)
128
- else
129
- unloaded_gems.add metagem
130
- end
157
+ if metagem.cacheable? && !Collection::Gem.cached?(metagem)
158
+ unloaded_gems.add metagem
131
159
  else
132
160
  loaded_gems.add metagem
133
- pins.concat Collection::Gem.load(metagem)
161
+ base_pins = Collection::Gem.load(metagem)
162
+ rbsc_pins = rbs_collection.load(metagem)
163
+ if rbsc_pins.empty?
164
+ pins.concat base_pins
165
+ else
166
+ pins.concat RbsMap::Helpers.combine(base_pins, rbsc_pins)
167
+ end
168
+ # Gems might not declare stdlib dependencies as gem dependencies
169
+ # (e.g., `activesupport` requires `date`)
170
+ base_pins.select { |pin| pin.is_a?(Pin::Reference::Require) && RbsMap::Stdlib.has?(pin.name) }
171
+ .each do |pin|
172
+ pins.concat Collection::Stdlib.load(pin.name) if loaded_stdlibs.add?(pin.name)
173
+ end
134
174
  end
135
175
  load_dependencies metagem
136
176
  end
@@ -143,17 +183,5 @@ module Solargraph
143
183
  process_gem metagem
144
184
  end
145
185
  end
146
-
147
- # @return [Array<String>]
148
- def read_rbs_collection_paths
149
- return [] unless rbs_collection_config_path
150
-
151
- yaml = YAML.load_file(rbs_collection_config_path)
152
- [File.expand_path(yaml.fetch('path'), directory)].concat(
153
- yaml.fetch('sources', [])
154
- .select { |source| source['type'] == 'local' && source['path'] }
155
- .map { |source| File.expand_path(source['path'], directory) }
156
- ).compact
157
- end
158
186
  end
159
187
  end
@@ -61,7 +61,7 @@ module Solargraph
61
61
  return
62
62
  end
63
63
  current = mutex.synchronize { queue.shift }
64
- return if queue.include?(current)
64
+ return if current.nil? || queue.include?(current)
65
65
  begin
66
66
  host.diagnose current
67
67
  rescue InvalidOffsetError