solargraph 0.58.0 → 0.59.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.gitattributes +2 -0
  4. data/.github/workflows/linting.yml +6 -5
  5. data/.github/workflows/plugins.yml +48 -35
  6. data/.github/workflows/rspec.yml +15 -28
  7. data/.github/workflows/typecheck.yml +3 -2
  8. data/.gitignore +1 -0
  9. data/.rubocop.yml +38 -6
  10. data/.rubocop_todo.yml +53 -966
  11. data/CHANGELOG.md +24 -0
  12. data/Gemfile +3 -1
  13. data/README.md +3 -3
  14. data/Rakefile +26 -23
  15. data/bin/solargraph +9 -8
  16. data/lib/solargraph/api_map/cache.rb +110 -110
  17. data/lib/solargraph/api_map/constants.rb +288 -279
  18. data/lib/solargraph/api_map/index.rb +204 -193
  19. data/lib/solargraph/api_map/source_to_yard.rb +110 -97
  20. data/lib/solargraph/api_map/store.rb +395 -384
  21. data/lib/solargraph/api_map.rb +1029 -945
  22. data/lib/solargraph/bench.rb +44 -45
  23. data/lib/solargraph/complex_type/conformance.rb +176 -0
  24. data/lib/solargraph/complex_type/type_methods.rb +240 -228
  25. data/lib/solargraph/complex_type/unique_type.rb +641 -482
  26. data/lib/solargraph/complex_type.rb +557 -444
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  29. data/lib/solargraph/convention/data_definition.rb +108 -105
  30. data/lib/solargraph/convention/gemfile.rb +15 -15
  31. data/lib/solargraph/convention/gemspec.rb +23 -23
  32. data/lib/solargraph/convention/rakefile.rb +17 -17
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  35. data/lib/solargraph/convention/struct_definition.rb +168 -164
  36. data/lib/solargraph/convention.rb +78 -78
  37. data/lib/solargraph/converters/dd.rb +19 -17
  38. data/lib/solargraph/converters/dl.rb +17 -15
  39. data/lib/solargraph/converters/dt.rb +17 -15
  40. data/lib/solargraph/converters/misc.rb +3 -1
  41. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  42. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  44. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  45. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  46. data/lib/solargraph/doc_map.rb +438 -439
  47. data/lib/solargraph/equality.rb +34 -34
  48. data/lib/solargraph/gem_pins.rb +98 -98
  49. data/lib/solargraph/language_server/error_codes.rb +20 -20
  50. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  51. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  52. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  53. data/lib/solargraph/language_server/host/sources.rb +100 -99
  54. data/lib/solargraph/language_server/host.rb +886 -878
  55. data/lib/solargraph/language_server/message/base.rb +97 -97
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  57. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  58. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +108 -114
  59. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  60. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  61. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  64. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  65. data/lib/solargraph/language_server/message/text_document/definition.rb +49 -40
  66. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  67. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +36 -26
  68. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  69. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  70. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  71. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  72. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  73. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  74. data/lib/solargraph/language_server/message/text_document/type_definition.rb +33 -25
  75. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  76. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  77. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  78. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +33 -23
  79. data/lib/solargraph/language_server/message.rb +94 -94
  80. data/lib/solargraph/language_server/request.rb +29 -27
  81. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  82. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  83. data/lib/solargraph/library.rb +702 -683
  84. data/lib/solargraph/location.rb +85 -82
  85. data/lib/solargraph/logging.rb +55 -37
  86. data/lib/solargraph/page.rb +92 -92
  87. data/lib/solargraph/parser/comment_ripper.rb +84 -69
  88. data/lib/solargraph/parser/flow_sensitive_typing.rb +471 -255
  89. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  90. data/lib/solargraph/parser/node_processor.rb +63 -62
  91. data/lib/solargraph/parser/parser_gem/class_methods.rb +165 -149
  92. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  93. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  94. data/lib/solargraph/parser/parser_gem/node_methods.rb +507 -486
  95. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  96. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  97. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  98. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +45 -46
  99. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  100. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  101. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  102. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +42 -40
  103. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  104. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  105. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  106. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  108. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  109. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +51 -52
  110. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +302 -291
  111. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  112. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  113. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  114. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  115. data/lib/solargraph/parser/parser_gem.rb +14 -12
  116. data/lib/solargraph/parser/region.rb +75 -69
  117. data/lib/solargraph/parser/snippet.rb +19 -17
  118. data/lib/solargraph/parser.rb +25 -23
  119. data/lib/solargraph/pin/base.rb +773 -729
  120. data/lib/solargraph/pin/base_variable.rb +375 -126
  121. data/lib/solargraph/pin/block.rb +127 -104
  122. data/lib/solargraph/pin/breakable.rb +15 -9
  123. data/lib/solargraph/pin/callable.rb +275 -231
  124. data/lib/solargraph/pin/closure.rb +69 -72
  125. data/lib/solargraph/pin/common.rb +97 -79
  126. data/lib/solargraph/pin/compound_statement.rb +55 -0
  127. data/lib/solargraph/pin/constant.rb +43 -45
  128. data/lib/solargraph/pin/conversions.rb +129 -123
  129. data/lib/solargraph/pin/delegated_method.rb +131 -120
  130. data/lib/solargraph/pin/documenting.rb +116 -114
  131. data/lib/solargraph/pin/instance_variable.rb +38 -34
  132. data/lib/solargraph/pin/keyword.rb +16 -20
  133. data/lib/solargraph/pin/local_variable.rb +31 -75
  134. data/lib/solargraph/pin/method.rb +724 -672
  135. data/lib/solargraph/pin/method_alias.rb +42 -34
  136. data/lib/solargraph/pin/namespace.rb +122 -115
  137. data/lib/solargraph/pin/parameter.rb +339 -275
  138. data/lib/solargraph/pin/proxy_type.rb +42 -39
  139. data/lib/solargraph/pin/reference/override.rb +47 -47
  140. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  141. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  142. data/lib/solargraph/pin/reference.rb +59 -39
  143. data/lib/solargraph/pin/search.rb +62 -61
  144. data/lib/solargraph/pin/signature.rb +64 -61
  145. data/lib/solargraph/pin/symbol.rb +54 -53
  146. data/lib/solargraph/pin/until.rb +16 -18
  147. data/lib/solargraph/pin/while.rb +16 -18
  148. data/lib/solargraph/pin.rb +46 -44
  149. data/lib/solargraph/pin_cache.rb +248 -245
  150. data/lib/solargraph/position.rb +139 -119
  151. data/lib/solargraph/range.rb +113 -112
  152. data/lib/solargraph/rbs_map/conversions.rb +952 -823
  153. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  154. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  155. data/lib/solargraph/rbs_map/stdlib_map.rb +71 -43
  156. data/lib/solargraph/rbs_map.rb +207 -163
  157. data/lib/solargraph/server_methods.rb +16 -16
  158. data/lib/solargraph/shell.rb +555 -352
  159. data/lib/solargraph/source/chain/array.rb +39 -37
  160. data/lib/solargraph/source/chain/call.rb +377 -337
  161. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/constant.rb +30 -26
  163. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  164. data/lib/solargraph/source/chain/hash.rb +37 -34
  165. data/lib/solargraph/source/chain/if.rb +30 -28
  166. data/lib/solargraph/source/chain/instance_variable.rb +36 -13
  167. data/lib/solargraph/source/chain/link.rb +99 -109
  168. data/lib/solargraph/source/chain/literal.rb +51 -48
  169. data/lib/solargraph/source/chain/or.rb +29 -23
  170. data/lib/solargraph/source/chain/q_call.rb +13 -11
  171. data/lib/solargraph/source/chain/variable.rb +15 -13
  172. data/lib/solargraph/source/chain/z_super.rb +28 -30
  173. data/lib/solargraph/source/chain.rb +304 -291
  174. data/lib/solargraph/source/change.rb +89 -82
  175. data/lib/solargraph/source/cursor.rb +172 -166
  176. data/lib/solargraph/source/encoding_fixes.rb +22 -23
  177. data/lib/solargraph/source/source_chainer.rb +218 -194
  178. data/lib/solargraph/source/updater.rb +59 -55
  179. data/lib/solargraph/source.rb +522 -498
  180. data/lib/solargraph/source_map/clip.rb +245 -226
  181. data/lib/solargraph/source_map/data.rb +37 -34
  182. data/lib/solargraph/source_map/mapper.rb +286 -259
  183. data/lib/solargraph/source_map.rb +224 -212
  184. data/lib/solargraph/type_checker/problem.rb +34 -32
  185. data/lib/solargraph/type_checker/rules.rb +157 -84
  186. data/lib/solargraph/type_checker.rb +888 -814
  187. data/lib/solargraph/version.rb +5 -5
  188. data/lib/solargraph/workspace/config.rb +258 -255
  189. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  190. data/lib/solargraph/workspace/require_paths.rb +98 -97
  191. data/lib/solargraph/workspace.rb +242 -220
  192. data/lib/solargraph/yard_map/helpers.rb +49 -44
  193. data/lib/solargraph/yard_map/mapper/to_method.rb +136 -130
  194. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  195. data/lib/solargraph/yard_map/mapper.rb +84 -79
  196. data/lib/solargraph/yard_map.rb +17 -18
  197. data/lib/solargraph/yard_tags.rb +20 -20
  198. data/lib/solargraph/yardoc.rb +90 -87
  199. data/lib/solargraph.rb +128 -105
  200. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  201. data/rbs/fills/tuple/tuple.rbs +28 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  204. data/rbs_collection.yaml +1 -1
  205. data/solargraph.gemspec +37 -35
  206. metadata +52 -51
  207. data/lib/solargraph/type_checker/checks.rb +0 -124
  208. data/lib/solargraph/type_checker/param_def.rb +0 -37
  209. data/lib/solargraph/yard_map/to_method.rb +0 -89
  210. data/sig/shims/ast/0/node.rbs +0 -5
  211. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  212. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  215. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  216. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  217. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  218. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  219. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,729 +1,773 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Pin
5
- # The base class for map pins.
6
- #
7
- class Base
8
- include Common
9
- include Conversions
10
- include Documenting
11
- include Logging
12
-
13
- # @return [YARD::CodeObjects::Base]
14
- attr_reader :code_object
15
-
16
- # @return [Solargraph::Location, nil]
17
- attr_reader :location
18
-
19
- # @return [Solargraph::Location, nil]
20
- attr_reader :type_location
21
-
22
- # @return [String]
23
- attr_reader :name
24
-
25
- # @return [String]
26
- attr_reader :path
27
-
28
- # @return [::Symbol]
29
- attr_accessor :source
30
-
31
- # @type [::Numeric, nil] A priority for determining if pins should be combined or not
32
- # A nil priority is considered the be the lowest. All code, yard & rbs pins have nil priority
33
- # Between 2 pins, the one with the higher priority gets chosen. If the priorities are equal, they are combined.
34
- attr_reader :combine_priority
35
-
36
- def presence_certain?
37
- true
38
- end
39
-
40
- # @param location [Solargraph::Location, nil]
41
- # @param type_location [Solargraph::Location, nil]
42
- # @param closure [Solargraph::Pin::Closure, nil]
43
- # @param name [String]
44
- # @param comments [String]
45
- # @param source [Symbol, nil]
46
- # @param docstring [YARD::Docstring, nil]
47
- # @param directives [::Array<YARD::Tags::Directive>, nil]
48
- # @param combine_priority [::Numeric, nil] See attr_reader for combine_priority
49
- def initialize location: nil, type_location: nil, closure: nil, source: nil, name: '', comments: '', docstring: nil, directives: nil, combine_priority: nil
50
- @location = location
51
- @type_location = type_location
52
- @closure = closure
53
- @name = name
54
- @comments = comments
55
- @source = source
56
- @identity = nil
57
- @docstring = docstring
58
- @directives = directives
59
- @combine_priority = combine_priority
60
-
61
- assert_source_provided
62
- assert_location_provided
63
- end
64
-
65
- # @return [void]
66
- def assert_location_provided
67
- return unless best_location.nil? && %i[yardoc source rbs].include?(source)
68
-
69
- Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}")
70
- end
71
-
72
- # @return [Pin::Closure, nil]
73
- def closure
74
- Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure
75
- # @type [Pin::Closure, nil]
76
- @closure
77
- end
78
-
79
- # @param other [self]
80
- # @param attrs [Hash{::Symbol => Object}]
81
- #
82
- # @return [self]
83
- def combine_with(other, attrs={})
84
- raise "tried to combine #{other.class} with #{self.class}" unless other.class == self.class
85
- priority_choice = choose_priority(other)
86
- return priority_choice unless priority_choice.nil?
87
-
88
- type_location = choose(other, :type_location)
89
- location = choose(other, :location)
90
- combined_name = combine_name(other)
91
- new_attrs = {
92
- location: location,
93
- type_location: type_location,
94
- name: combined_name,
95
- closure: choose_pin_attr_with_same_name(other, :closure),
96
- comments: choose_longer(other, :comments),
97
- source: :combined,
98
- docstring: choose(other, :docstring),
99
- directives: combine_directives(other),
100
- combine_priority: combine_priority
101
- }.merge(attrs)
102
- assert_same_macros(other)
103
- logger.debug { "Base#combine_with(path=#{path}) - other.comments=#{other.comments.inspect}, self.comments = #{self.comments}" }
104
- out = self.class.new(**new_attrs)
105
- out.reset_generated!
106
- out
107
- end
108
-
109
- # @param other [self]
110
- # @return [self, nil] Returns either the pin chosen based on priority or nil
111
- # A nil return means that the combination process must proceed
112
- def choose_priority(other)
113
- if combine_priority.nil? && !other.combine_priority.nil?
114
- return other
115
- elsif other.combine_priority.nil? && !combine_priority.nil?
116
- return self
117
- elsif !combine_priority.nil? && !other.combine_priority.nil?
118
- if combine_priority > other.combine_priority
119
- return self
120
- elsif combine_priority < other.combine_priority
121
- return other
122
- end
123
- end
124
-
125
- nil
126
- end
127
-
128
- # @param other [self]
129
- # @param attr [::Symbol]
130
- # @sg-ignore
131
- # @return [undefined]
132
- def choose_longer(other, attr)
133
- # @type [undefined]
134
- val1 = send(attr)
135
- # @type [undefined]
136
- val2 = other.send(attr)
137
- return val1 if val1 == val2
138
- return val2 if val1.nil?
139
- val1.length > val2.length ? val1 : val2
140
- end
141
-
142
- # @param other [self]
143
- # @return [::Array<YARD::Tags::Directive>, nil]
144
- def combine_directives(other)
145
- return self.directives if other.directives.empty?
146
- return other.directives if directives.empty?
147
- [directives + other.directives].uniq
148
- end
149
-
150
- # @param other [self]
151
- # @return [String]
152
- def combine_name(other)
153
- if needs_consistent_name? || other.needs_consistent_name?
154
- assert_same(other, :name)
155
- else
156
- choose(other, :name)
157
- end
158
- end
159
-
160
- # @return [void]
161
- def reset_generated!
162
- # @return_type doesn't go here as subclasses tend to assign it
163
- # themselves in constructors, and they will deal with setting
164
- # it in any methods that call this
165
- #
166
- # @docstring also doesn't go here, as there is code which
167
- # directly manipulates docstring without editing comments
168
- # (e.g., Api::Map::Store#index processes overrides that way
169
- #
170
- # Same with @directives, @macros, @maybe_directives, which
171
- # regenerate docstring
172
- @deprecated = nil
173
- reset_conversions
174
- end
175
-
176
- def needs_consistent_name?
177
- true
178
- end
179
-
180
- # @sg-ignore def should infer as symbol - "Not enough arguments to Module#protected"
181
- protected def equality_fields
182
- [name, location, type_location, closure, source]
183
- end
184
-
185
- # @param other [self]
186
- # @return [ComplexType]
187
- def combine_return_type(other)
188
- if return_type.undefined?
189
- other.return_type
190
- elsif other.return_type.undefined?
191
- return_type
192
- elsif dodgy_return_type_source? && !other.dodgy_return_type_source?
193
- other.return_type
194
- elsif other.dodgy_return_type_source? && !dodgy_return_type_source?
195
- return_type
196
- else
197
- all_items = return_type.items + other.return_type.items
198
- if all_items.any? { |item| item.selfy? } && all_items.any? { |item| item.rooted_tag == context.reduce_class_type.rooted_tag }
199
- # assume this was a declaration that should have said 'self'
200
- all_items.delete_if { |item| item.rooted_tag == context.reduce_class_type.rooted_tag }
201
- end
202
- ComplexType.new(all_items)
203
- end
204
- end
205
-
206
- def dodgy_return_type_source?
207
- # uses a lot of 'Object' instead of 'self'
208
- location&.filename&.include?('core_ext/object/')
209
- end
210
-
211
- # when choices are arbitrary, make sure the choice is consistent
212
- #
213
- # @param other [Pin::Base]
214
- # @param attr [::Symbol]
215
- #
216
- # @return [Object, nil]
217
- def choose(other, attr)
218
- results = [self, other].map(&attr).compact
219
- # true and false are different classes and can't be sorted
220
- return true if results.any? { |r| r == true || r == false }
221
- results.min
222
- rescue
223
- STDERR.puts("Problem handling #{attr} for \n#{self.inspect}\n and \n#{other.inspect}\n\n#{self.send(attr).inspect} vs #{other.send(attr).inspect}")
224
- raise
225
- end
226
-
227
- # @param other [self]
228
- # @param attr [::Symbol]
229
- # @sg-ignore
230
- # @return [undefined]
231
- def choose_node(other, attr)
232
- if other.object_id < attr.object_id
233
- other.send(attr)
234
- else
235
- send(attr)
236
- end
237
- end
238
-
239
- # @param other [self]
240
- # @param attr [::Symbol]
241
- # @sg-ignore
242
- # @return [undefined]
243
- def prefer_rbs_location(other, attr)
244
- if rbs_location? && !other.rbs_location?
245
- self.send(attr)
246
- elsif !rbs_location? && other.rbs_location?
247
- other.send(attr)
248
- else
249
- choose(other, attr)
250
- end
251
- end
252
-
253
- def rbs_location?
254
- type_location&.rbs?
255
- end
256
-
257
- # @param other [self]
258
- # @return [void]
259
- def assert_same_macros(other)
260
- return unless self.source == :yardoc && other.source == :yardoc
261
- assert_same_count(other, :macros)
262
- # @param [YARD::Tags::MacroDirective]
263
- assert_same_array_content(other, :macros) { |macro| macro.tag.name }
264
- end
265
-
266
- # @param other [self]
267
- # @param attr [::Symbol]
268
- # @return [void]
269
- # @todo strong typechecking should complain when there are no block-related tags
270
- def assert_same_array_content(other, attr, &block)
271
- arr1 = send(attr)
272
- raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable)
273
- # @type arr1 [::Enumerable]
274
- arr2 = other.send(attr)
275
- raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable)
276
- # @type arr2 [::Enumerable]
277
-
278
- # @type [undefined]
279
- values1 = arr1.map(&block)
280
- # @type [undefined]
281
- values2 = arr2.map(&block)
282
- # @sg-ignore
283
- return arr1 if values1 == values2
284
- Solargraph.assert_or_log("combine_with_#{attr}".to_sym,
285
- "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self values = #{values1}\nother values =#{attr} = #{values2}")
286
- arr1
287
- end
288
-
289
- # @param other [self]
290
- # @param attr [::Symbol]
291
- #
292
- # @return [::Enumerable]
293
- def assert_same_count(other, attr)
294
- # @type [::Enumerable]
295
- arr1 = self.send(attr)
296
- raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable)
297
- # @type [::Enumerable]
298
- arr2 = other.send(attr)
299
- raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable)
300
- return arr1 if arr1.count == arr2.count
301
- Solargraph.assert_or_log("combine_with_#{attr}".to_sym,
302
- "Inconsistent #{attr.inspect} count value between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{arr1.inspect}\nother.#{attr} = #{arr2.inspect}")
303
- arr1
304
- end
305
-
306
- # @param other [self]
307
- # @param attr [::Symbol]
308
- #
309
- # @sg-ignore
310
- # @return [undefined]
311
- def assert_same(other, attr)
312
- return false if other.nil?
313
- val1 = send(attr)
314
- val2 = other.send(attr)
315
- return val1 if val1 == val2
316
- Solargraph.assert_or_log("combine_with_#{attr}".to_sym,
317
- "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
318
- val1
319
- end
320
-
321
- # @param other [self]
322
- # @param attr [::Symbol]
323
- # @sg-ignore
324
- # @return [undefined]
325
- def choose_pin_attr_with_same_name(other, attr)
326
- # @type [Pin::Base, nil]
327
- val1 = send(attr)
328
- # @type [Pin::Base, nil]
329
- val2 = other.send(attr)
330
- raise "Expected pin for #{attr} on\n#{self.inspect},\ngot #{val1.inspect}" unless val1.nil? || val1.is_a?(Pin::Base)
331
- raise "Expected pin for #{attr} on\n#{other.inspect},\ngot #{val2.inspect}" unless val2.nil? || val2.is_a?(Pin::Base)
332
- if val1&.name != val2&.name
333
- Solargraph.assert_or_log("combine_with_#{attr}_name".to_sym,
334
- "Inconsistent #{attr.inspect} name values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
335
- end
336
- choose_pin_attr(other, attr)
337
- end
338
-
339
- # @param other [self]
340
- # @param attr [::Symbol]
341
- #
342
- # @sg-ignore Missing @return tag for Solargraph::Pin::Base#choose_pin_attr
343
- # @return [undefined]
344
- def choose_pin_attr(other, attr)
345
- # @type [Pin::Base, nil]
346
- val1 = send(attr)
347
- # @type [Pin::Base, nil]
348
- val2 = other.send(attr)
349
- if val1.class != val2.class
350
- # :nocov:
351
- Solargraph.assert_or_log("combine_with_#{attr}_class".to_sym,
352
- "Inconsistent #{attr.inspect} class values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
353
- return val1
354
- # :nocov:
355
- end
356
- # arbitrary way of choosing a pin
357
- [val1, val2].compact.max_by do |closure|
358
- [
359
- # maximize number of gates, as types in other combined pins may
360
- # depend on those gates
361
- closure.gates.length,
362
- # use basename so that results don't vary system to system
363
- File.basename(closure.best_location.to_s)
364
- ]
365
- end
366
- end
367
-
368
- # @return [void]
369
- def assert_source_provided
370
- Solargraph.assert_or_log(:source, "source not provided - #{@path} #{@source} #{self.class}") if source.nil?
371
- end
372
-
373
- # @return [String]
374
- def comments
375
- @comments ||= ''
376
- end
377
-
378
- # @param generics_to_resolve [Enumerable<String>]
379
- # @param return_type_context [ComplexType, nil]
380
- # @param context [ComplexType]
381
- # @param resolved_generic_values [Hash{String => ComplexType}]
382
- # @return [self]
383
- def resolve_generics_from_context(generics_to_resolve, return_type_context = nil, resolved_generic_values: {})
384
- proxy return_type.resolve_generics_from_context(generics_to_resolve,
385
- return_type_context,
386
- resolved_generic_values: resolved_generic_values)
387
- end
388
-
389
- # @yieldparam [ComplexType]
390
- # @yieldreturn [ComplexType]
391
- # @return [self]
392
- def transform_types(&transform)
393
- proxy return_type.transform(&transform)
394
- end
395
-
396
- # Determine the concrete type for each of the generic type
397
- # parameters used in this method based on the parameters passed
398
- # into the its class and return a new method pin.
399
- #
400
- # @param definitions [Pin::Namespace] The module/class which uses generic types
401
- # @param context_type [ComplexType] The receiver type
402
- # @return [self]
403
- def resolve_generics definitions, context_type
404
- transform_types { |t| t.resolve_generics(definitions, context_type) if t }
405
- end
406
-
407
- def all_rooted?
408
- !return_type || return_type.all_rooted?
409
- end
410
-
411
- # @param generics_to_erase [::Array<String>]
412
- # @return [self]
413
- def erase_generics(generics_to_erase)
414
- return self if generics_to_erase.empty?
415
- transform_types { |t| t.erase_generics(generics_to_erase) }
416
- end
417
-
418
- # @return [String, nil]
419
- def filename
420
- return nil if location.nil?
421
- location.filename
422
- end
423
-
424
- # @return [Integer]
425
- def completion_item_kind
426
- LanguageServer::CompletionItemKinds::KEYWORD
427
- end
428
-
429
- # @return [Integer, nil]
430
- def symbol_kind
431
- nil
432
- end
433
-
434
- def to_s
435
- desc
436
- end
437
-
438
- # @return [Boolean]
439
- def variable?
440
- false
441
- end
442
-
443
- # @return [Location, nil]
444
- def best_location
445
- location || type_location
446
- end
447
-
448
- # True if the specified pin is a near match to this one. A near match
449
- # indicates that the pins contain mostly the same data. Any differences
450
- # between them should not have an impact on the API surface.
451
- #
452
- # @param other [Solargraph::Pin::Base, Object]
453
- # @return [Boolean]
454
- def nearly? other
455
- self.class == other.class &&
456
- name == other.name &&
457
- (closure == other.closure || (closure && closure.nearly?(other.closure))) &&
458
- (comments == other.comments ||
459
- (((maybe_directives? == false && other.maybe_directives? == false) || compare_directives(directives, other.directives)) &&
460
- compare_docstring_tags(docstring, other.docstring))
461
- )
462
- end
463
-
464
- # Pin equality is determined using the #nearly? method and also
465
- # requiring both pins to have the same location.
466
- #
467
- # @param other [Object]
468
- def == other
469
- return false unless nearly? other
470
- # @sg-ignore Should add more explicit type check on other
471
- comments == other.comments && location == other.location
472
- end
473
-
474
- # The pin's return type.
475
- #
476
- # @return [ComplexType]
477
- def return_type
478
- @return_type ||= ComplexType::UNDEFINED
479
- end
480
-
481
- # @return [YARD::Docstring]
482
- def docstring
483
- parse_comments unless @docstring
484
- @docstring ||= Solargraph::Source.parse_docstring('').to_docstring
485
- end
486
-
487
- # @return [::Array<YARD::Tags::Directive>]
488
- def directives
489
- parse_comments unless @directives
490
- @directives
491
- end
492
-
493
- # @return [::Array<YARD::Tags::MacroDirective>]
494
- def macros
495
- @macros ||= collect_macros
496
- end
497
-
498
- # Perform a quick check to see if this pin possibly includes YARD
499
- # directives. This method does not require parsing the comments.
500
- #
501
- # After the comments have been parsed, this method will return false if
502
- # no directives were found, regardless of whether it previously appeared
503
- # possible.
504
- #
505
- # @return [Boolean]
506
- def maybe_directives?
507
- return !@directives.empty? if defined?(@directives) && @directives
508
- @maybe_directives ||= comments.include?('@!')
509
- end
510
-
511
- # @return [Boolean]
512
- def deprecated?
513
- @deprecated ||= docstring.has_tag?('deprecated')
514
- end
515
-
516
- # Get a fully qualified type from the pin's return type.
517
- #
518
- # The relative type is determined from YARD documentation (@return,
519
- # @param, @type, etc.) and its namespaces are fully qualified using the
520
- # provided ApiMap.
521
- #
522
- # @param api_map [ApiMap]
523
- # @return [ComplexType]
524
- def typify api_map
525
- return_type.qualify(api_map, *(closure&.gates || ['']))
526
- end
527
-
528
- # Infer the pin's return type via static code analysis.
529
- #
530
- # @param api_map [ApiMap]
531
- # @return [ComplexType]
532
- def probe api_map
533
- typify api_map
534
- end
535
-
536
- # @deprecated Use #typify and/or #probe instead
537
- # @param api_map [ApiMap]
538
- # @return [ComplexType]
539
- def infer api_map
540
- Solargraph::Logging.logger.warn "WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead."
541
- type = typify(api_map)
542
- return type unless type.undefined?
543
- probe api_map
544
- end
545
-
546
- def proxied?
547
- @proxied ||= false
548
- end
549
-
550
- def probed?
551
- @probed ||= false
552
- end
553
-
554
- # @param api_map [ApiMap]
555
- # @return [self]
556
- def realize api_map
557
- return self if return_type.defined?
558
- type = typify(api_map)
559
- return proxy(type) if type.defined?
560
- type = probe(api_map)
561
- return self if type.undefined?
562
- result = proxy(type)
563
- result.probed = true
564
- result
565
- end
566
-
567
- # Return a proxy for this pin with the specified return type. Other than
568
- # the return type and the #proxied? setting, the proxy should be a clone
569
- # of the original.
570
- #
571
- # @param return_type [ComplexType]
572
- # @return [self]
573
- def proxy return_type
574
- result = dup
575
- result.return_type = return_type
576
- result.proxied = true
577
- result
578
- end
579
-
580
- # @deprecated
581
- # @return [String]
582
- def identity
583
- @identity ||= "#{closure&.path}|#{name}|#{location}"
584
- end
585
-
586
- # The namespaces available for resolving the current namespace. Each gate
587
- # should be a fully qualified namespace or the root namespace (i.e., an
588
- # empty string.)
589
- #
590
- # Example: Given the name 'Bar' and the gates ['Foo', ''],
591
- # the fully qualified namespace should be 'Foo::Bar' or 'Bar'.
592
- #
593
- # @return [Array<String>]
594
- def gates
595
- @gates ||= closure&.gates || ['']
596
- end
597
-
598
- # @return [String, nil]
599
- def to_rbs
600
- return_type.to_rbs
601
- end
602
-
603
- # @return [String, nil]
604
- def type_desc
605
- rbs = to_rbs
606
- # RBS doesn't have a way to represent a Class<x> type
607
- rbs = return_type.rooted_tags if return_type.name == 'Class'
608
- if path
609
- if rbs
610
- path + ' ' + rbs
611
- else
612
- path
613
- end
614
- else
615
- rbs
616
- end
617
- end
618
-
619
- # @return [String]
620
- def inner_desc
621
- closure_info = closure&.desc
622
- binder_info = binder&.desc
623
- "name=#{name.inspect} return_type=#{type_desc}, context=#{context.rooted_tags}, closure=#{closure_info}, binder=#{binder_info}"
624
- end
625
-
626
- # @return [String]
627
- def desc
628
- "[#{inner_desc}]"
629
- end
630
-
631
- # @return [String]
632
- def inspect
633
- "#<#{self.class} `#{self.inner_desc}`#{all_location_text} via #{source.inspect}>"
634
- end
635
-
636
- # @return [String]
637
- def all_location_text
638
- if location.nil? && type_location.nil?
639
- ''
640
- elsif !location.nil? && type_location.nil?
641
- " at #{location.inspect})"
642
- elsif !type_location.nil? && location.nil?
643
- " at #{type_location.inspect})"
644
- else
645
- " at (#{location.inspect} and #{type_location.inspect})"
646
- end
647
- end
648
-
649
- # @return [void]
650
- def reset_generated!
651
- end
652
-
653
- protected
654
-
655
- # @return [Boolean]
656
- attr_writer :probed
657
-
658
- # @return [Boolean]
659
- attr_writer :proxied
660
-
661
- # @return [ComplexType]
662
- attr_writer :return_type
663
-
664
- attr_writer :docstring
665
-
666
- attr_writer :directives
667
-
668
- private
669
-
670
- # @return [void]
671
- def parse_comments
672
- # HACK: Avoid a NoMethodError on nil with empty overload tags
673
- if comments.nil? || comments.empty? || comments.strip.end_with?('@overload')
674
- @docstring = nil
675
- @directives = []
676
- else
677
- # HACK: Pass a dummy code object to the parser for plugins that
678
- # expect it not to be nil
679
- parse = Solargraph::Source.parse_docstring(comments)
680
- @docstring = parse.to_docstring
681
- @directives = parse.directives
682
- end
683
- end
684
-
685
- # True if two docstrings have the same tags, regardless of any other
686
- # differences.
687
- #
688
- # @param d1 [YARD::Docstring]
689
- # @param d2 [YARD::Docstring]
690
- # @return [Boolean]
691
- def compare_docstring_tags d1, d2
692
- return false if d1.tags.length != d2.tags.length
693
- d1.tags.each_index do |i|
694
- return false unless compare_tags(d1.tags[i], d2.tags[i])
695
- end
696
- true
697
- end
698
-
699
- # @param dir1 [::Array<YARD::Tags::Directive>]
700
- # @param dir2 [::Array<YARD::Tags::Directive>]
701
- # @return [Boolean]
702
- def compare_directives dir1, dir2
703
- return false if dir1.length != dir2.length
704
- dir1.each_index do |i|
705
- return false unless compare_tags(dir1[i].tag, dir2[i].tag)
706
- end
707
- true
708
- end
709
-
710
- # @param tag1 [YARD::Tags::Tag]
711
- # @param tag2 [YARD::Tags::Tag]
712
- # @return [Boolean]
713
- def compare_tags tag1, tag2
714
- tag1.class == tag2.class &&
715
- tag1.tag_name == tag2.tag_name &&
716
- tag1.text == tag2.text &&
717
- tag1.name == tag2.name &&
718
- tag1.types == tag2.types
719
- end
720
-
721
- # @return [::Array<YARD::Tags::Handlers::Directive>]
722
- def collect_macros
723
- return [] unless maybe_directives?
724
- parse = Solargraph::Source.parse_docstring(comments)
725
- parse.directives.select{ |d| d.tag.tag_name == 'macro' }
726
- end
727
- end
728
- end
729
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Pin
5
+ # The base class for map pins.
6
+ #
7
+ class Base
8
+ include Common
9
+ include Conversions
10
+ include Documenting
11
+ include Logging
12
+
13
+ # @return [YARD::CodeObjects::Base]
14
+ attr_reader :code_object
15
+
16
+ # @return [Solargraph::Location, nil]
17
+ attr_reader :location
18
+
19
+ # @return [Solargraph::Location, nil]
20
+ attr_reader :type_location
21
+
22
+ # @return [String]
23
+ attr_reader :name
24
+
25
+ # @return [String]
26
+ attr_reader :path
27
+
28
+ # @return [::Symbol]
29
+ attr_accessor :source
30
+
31
+ # @type [::Numeric, nil] A priority for determining if pins should be combined or not
32
+ # A nil priority is considered the be the lowest. All code, yard & rbs pins have nil priority
33
+ # Between 2 pins, the one with the higher priority gets chosen. If the priorities are equal, they are combined.
34
+ attr_reader :combine_priority
35
+
36
+ def presence_certain?
37
+ true
38
+ end
39
+
40
+ # @param location [Solargraph::Location, nil]
41
+ # @param type_location [Solargraph::Location, nil]
42
+ # @param closure [Solargraph::Pin::Closure, nil]
43
+ # @param name [String]
44
+ # @param comments [String, nil]
45
+ # @param source [Symbol, nil]
46
+ # @param docstring [YARD::Docstring, nil]
47
+ # @param directives [::Array<YARD::Tags::Directive>, nil]
48
+ # @param combine_priority [::Numeric, nil] See attr_reader for combine_priority
49
+ def initialize location: nil, type_location: nil, closure: nil, source: nil, name: '', comments: '',
50
+ docstring: nil, directives: nil, combine_priority: nil
51
+ @location = location
52
+ @type_location = type_location
53
+ @closure = closure
54
+ @name = name
55
+ @comments = comments
56
+ @source = source
57
+ @identity = nil
58
+ @docstring = docstring
59
+ @directives = directives
60
+ @combine_priority = combine_priority
61
+ # @type [ComplexType, ComplexType::UniqueType, nil]
62
+ @binder = nil
63
+
64
+ assert_source_provided
65
+ assert_location_provided
66
+ end
67
+
68
+ # @return [void]
69
+ def assert_location_provided
70
+ return unless best_location.nil? && %i[yardoc source rbs].include?(source)
71
+
72
+ Solargraph.assert_or_log(:best_location,
73
+ "Neither location nor type_location provided - #{path} #{source} #{self.class}")
74
+ end
75
+
76
+ # @return [Pin::Closure, nil]
77
+ def closure
78
+ unless @closure
79
+ Solargraph.assert_or_log(:closure,
80
+ "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}")
81
+ end
82
+ @closure
83
+ end
84
+
85
+ # @param other [self]
86
+ # @param attrs [Hash{::Symbol => Object}]
87
+ #
88
+ # @return [self]
89
+ def combine_with other, attrs = {}
90
+ priority_choice = choose_priority(other)
91
+ return priority_choice unless priority_choice.nil?
92
+
93
+ type_location = choose(other, :type_location)
94
+ location = choose(other, :location)
95
+ combined_name = combine_name(other)
96
+ new_attrs = {
97
+ location: location,
98
+ type_location: type_location,
99
+ name: combined_name,
100
+ closure: combine_closure(other),
101
+ comments: choose_longer(other, :comments),
102
+ source: :combined,
103
+ docstring: choose(other, :docstring),
104
+ directives: combine_directives(other),
105
+ combine_priority: combine_priority
106
+ }.merge(attrs)
107
+ assert_same_macros(other)
108
+ logger.debug do
109
+ "Base#combine_with(path=#{path}) - other.comments=#{other.comments.inspect}, self.comments = #{comments}"
110
+ end
111
+ out = self.class.new(**new_attrs)
112
+ out.reset_generated!
113
+ out
114
+ end
115
+
116
+ # @param other [self]
117
+ # @return [self, nil] Returns either the pin chosen based on priority or nil
118
+ # A nil return means that the combination process must proceed
119
+ def choose_priority other
120
+ if combine_priority.nil? && !other.combine_priority.nil?
121
+ return other
122
+ elsif other.combine_priority.nil? && !combine_priority.nil?
123
+ return self
124
+ elsif !combine_priority.nil? && !other.combine_priority.nil?
125
+ if combine_priority > other.combine_priority
126
+ return self
127
+ elsif combine_priority < other.combine_priority
128
+ return other
129
+ end
130
+ end
131
+
132
+ nil
133
+ end
134
+
135
+ # @param other [self]
136
+ # @param attr [::Symbol]
137
+ # @sg-ignore
138
+ # @return [undefined]
139
+ def choose_longer other, attr
140
+ # @type [undefined]
141
+ val1 = send(attr)
142
+ # @type [undefined]
143
+ val2 = other.send(attr)
144
+ return val1 if val1 == val2
145
+ return val2 if val1.nil?
146
+ val1.length > val2.length ? val1 : val2
147
+ end
148
+
149
+ # @param other [self]
150
+ #
151
+ # @return [::Array<YARD::Tags::Directive>, nil]
152
+ def combine_directives other
153
+ return directives if other.directives.empty?
154
+ return other.directives if directives.empty?
155
+ (directives + other.directives).uniq
156
+ end
157
+
158
+ # @param other [self]
159
+ # @return [Pin::Closure, nil]
160
+ def combine_closure other
161
+ choose_pin_attr_with_same_name(other, :closure)
162
+ end
163
+
164
+ # @param other [self]
165
+ # @sg-ignore @type should override probed type
166
+ # @return [String]
167
+ def combine_name other
168
+ if needs_consistent_name? || other.needs_consistent_name?
169
+ assert_same(other, :name)
170
+ else
171
+ choose(other, :name)
172
+ end
173
+ end
174
+
175
+ # @return [void]
176
+ def reset_generated!
177
+ # @return_type doesn't go here as subclasses tend to assign it
178
+ # themselves in constructors, and they will deal with setting
179
+ # it in any methods that call this
180
+ #
181
+ # @docstring also doesn't go here, as there is code which
182
+ # directly manipulates docstring without editing comments
183
+ # (e.g., Api::Map::Store#index processes overrides that way
184
+ #
185
+ # Same with @directives, @macros, @maybe_directives, which
186
+ # regenerate docstring
187
+ @deprecated = nil
188
+ @context = nil
189
+ @binder = nil
190
+ @path = nil
191
+ reset_conversions
192
+ end
193
+
194
+ def needs_consistent_name?
195
+ true
196
+ end
197
+
198
+ # @param other [self]
199
+ # @return [ComplexType]
200
+ def combine_return_type other
201
+ if return_type.undefined?
202
+ other.return_type
203
+ elsif other.return_type.undefined?
204
+ return_type
205
+ elsif return_type.erased_version_of?(other.return_type)
206
+ other.return_type
207
+ elsif other.return_type.erased_version_of?(return_type)
208
+ return_type
209
+ elsif dodgy_return_type_source? && !other.dodgy_return_type_source?
210
+ other.return_type
211
+ elsif other.dodgy_return_type_source? && !dodgy_return_type_source?
212
+ return_type
213
+ else
214
+ all_items = return_type.items + other.return_type.items
215
+ if all_items.any?(&:selfy?) && all_items.any? do |item|
216
+ item.rooted_tag == context.reduce_class_type.rooted_tag
217
+ end
218
+ # assume this was a declaration that should have said 'self'
219
+ all_items.delete_if { |item| item.rooted_tag == context.reduce_class_type.rooted_tag }
220
+ end
221
+ ComplexType.new(all_items)
222
+ end
223
+ end
224
+
225
+ # @sg-ignore need boolish support for ? methods
226
+ def dodgy_return_type_source?
227
+ # uses a lot of 'Object' instead of 'self'
228
+ location&.filename&.include?('core_ext/object/') ||
229
+ # ditto
230
+ location&.filename&.include?('stdlib/date/0/date.rbs')
231
+ end
232
+
233
+ # when choices are arbitrary, make sure the choice is consistent
234
+ #
235
+ # @param other [Pin::Base]
236
+ # @param attr [::Symbol]
237
+ #
238
+ # @sg-ignore
239
+ # @return [undefined, nil]
240
+ def choose other, attr
241
+ results = [self, other].map(&attr).compact
242
+ # true and false are different classes and can't be sorted
243
+
244
+ return true if results.any? { |r| [true, false].include?(r) }
245
+ return results.first if results.any? { |r| r.is_a? AST::Node }
246
+ results.min
247
+ rescue StandardError
248
+ warn("Problem handling #{attr} for \n#{inspect}\n and \n#{other.inspect}\n\n#{send(attr).inspect} vs #{other.send(attr).inspect}")
249
+ raise
250
+ end
251
+
252
+ # @param other [self]
253
+ # @param attr [::Symbol]
254
+ # @sg-ignore
255
+ # @return [undefined]
256
+ def choose_node other, attr
257
+ if other.object_id < attr.object_id
258
+ other.send(attr)
259
+ else
260
+ send(attr)
261
+ end
262
+ end
263
+
264
+ # @param other [self]
265
+ # @param attr [::Symbol]
266
+ # @sg-ignore
267
+ # @return [undefined]
268
+ def prefer_rbs_location other, attr
269
+ if rbs_location? && !other.rbs_location?
270
+ send(attr)
271
+ elsif !rbs_location? && other.rbs_location?
272
+ other.send(attr)
273
+ else
274
+ choose(other, attr)
275
+ end
276
+ end
277
+
278
+ # @sg-ignore need boolish support for ? methods
279
+ def rbs_location?
280
+ type_location&.rbs?
281
+ end
282
+
283
+ # @param other [self]
284
+ # @return [void]
285
+ def assert_same_macros other
286
+ return unless source == :yardoc && other.source == :yardoc
287
+ assert_same_count(other, :macros)
288
+ # @param [YARD::Tags::MacroDirective]
289
+ assert_same_array_content(other, :macros) { |macro| macro.tag.name }
290
+ end
291
+
292
+ # @param other [self]
293
+ # @param attr [::Symbol]
294
+ # @return [void]
295
+ # @todo strong typechecking should complain when there are no block-related tags
296
+ def assert_same_array_content other, attr, &block
297
+ arr1 = send(attr)
298
+ raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable)
299
+ # @type arr1 [::Enumerable]
300
+ arr2 = other.send(attr)
301
+ raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable)
302
+ # @type arr2 [::Enumerable]
303
+
304
+ # @type [undefined]
305
+ values1 = arr1.map(&block)
306
+ # @type [undefined]
307
+ values2 = arr2.map(&block)
308
+ # @sg-ignore
309
+ return arr1 if values1 == values2
310
+ Solargraph.assert_or_log(:"combine_with_#{attr}",
311
+ "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self values = #{values1}\nother values =#{attr} = #{values2}")
312
+ arr1
313
+ end
314
+
315
+ # @param other [self]
316
+ # @param attr [::Symbol]
317
+ #
318
+ # @return [::Enumerable]
319
+ def assert_same_count other, attr
320
+ # @type [::Enumerable]
321
+ arr1 = send(attr)
322
+ raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable)
323
+ # @type [::Enumerable]
324
+ arr2 = other.send(attr)
325
+ raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable)
326
+ return arr1 if arr1.count == arr2.count
327
+ Solargraph.assert_or_log(:"combine_with_#{attr}",
328
+ "Inconsistent #{attr.inspect} count value between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{arr1.inspect}\nother.#{attr} = #{arr2.inspect}")
329
+ arr1
330
+ end
331
+
332
+ # @param other [self]
333
+ # @param attr [::Symbol]
334
+ #
335
+ # @sg-ignore
336
+ # @return [undefined]
337
+ def assert_same other, attr
338
+ if other.nil?
339
+ Solargraph.assert_or_log(:"combine_with_#{attr}_nil",
340
+ "Other was passed in nil in assert_same on #{self}")
341
+ return send(attr)
342
+ end
343
+ val1 = send(attr)
344
+ val2 = other.send(attr)
345
+ return val1 if val1 == val2
346
+ Solargraph.assert_or_log(:"combine_with_#{attr}",
347
+ "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
348
+ val1
349
+ end
350
+
351
+ # @param other [self]
352
+ # @param attr [::Symbol]
353
+ # @sg-ignore
354
+ # @return [undefined]
355
+ def choose_pin_attr_with_same_name other, attr
356
+ # @type [Pin::Base, nil]
357
+ val1 = send(attr)
358
+ # @type [Pin::Base, nil]
359
+ val2 = other.send(attr)
360
+ raise "Expected pin for #{attr} on\n#{inspect},\ngot #{val1.inspect}" unless val1.nil? || val1.is_a?(Pin::Base)
361
+ unless val2.nil? || val2.is_a?(Pin::Base)
362
+ raise "Expected pin for #{attr} on\n#{other.inspect},\ngot #{val2.inspect}"
363
+ end
364
+ if val1&.name != val2&.name
365
+ Solargraph.assert_or_log(:"combine_with_#{attr}_name",
366
+ "Inconsistent #{attr.inspect} name values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
367
+ end
368
+ choose_pin_attr(other, attr)
369
+ end
370
+
371
+ # @param other [self]
372
+ # @param attr [::Symbol]
373
+ #
374
+ # @sg-ignore Missing @return tag for Solargraph::Pin::Base#choose_pin_attr
375
+ # @return [undefined]
376
+ def choose_pin_attr other, attr
377
+ # @type [Pin::Base, nil]
378
+ val1 = send(attr)
379
+ # @type [Pin::Base, nil]
380
+ val2 = other.send(attr)
381
+ if val1.class != val2.class
382
+ # :nocov:
383
+ Solargraph.assert_or_log(:"combine_with_#{attr}_class",
384
+ "Inconsistent #{attr.inspect} class values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}")
385
+ return val1
386
+ # :nocov:
387
+ end
388
+ # arbitrary way of choosing a pin
389
+ [val1, val2].compact.max_by do |closure|
390
+ [
391
+ # maximize number of gates, as types in other combined pins may
392
+ # depend on those gates
393
+
394
+ # @sg-ignore Need better handling of #compact
395
+ closure.gates.length,
396
+ # use basename so that results don't vary system to system
397
+ # @sg-ignore Need better handling of #compact
398
+ File.basename(closure.best_location.to_s)
399
+ ]
400
+ end
401
+ end
402
+
403
+ # @return [void]
404
+ def assert_source_provided
405
+ Solargraph.assert_or_log(:source, "source not provided - #{@path} #{@source} #{self.class}") if source.nil?
406
+ end
407
+
408
+ # @return [String]
409
+ def comments
410
+ @comments ||= ''
411
+ end
412
+
413
+ # @param generics_to_resolve [Enumerable<String>]
414
+ # @param return_type_context [ComplexType, ComplexType::UniqueType, nil]
415
+ # @param resolved_generic_values [Hash{String => ComplexType}]
416
+ # @return [self]
417
+ def resolve_generics_from_context generics_to_resolve, return_type_context = nil, resolved_generic_values: {}
418
+ proxy return_type.resolve_generics_from_context(generics_to_resolve,
419
+ return_type_context,
420
+ resolved_generic_values: resolved_generic_values)
421
+ end
422
+
423
+ # @yieldparam [ComplexType]
424
+ # @yieldreturn [ComplexType]
425
+ # @return [self]
426
+ def transform_types &transform
427
+ proxy return_type.transform(&transform)
428
+ end
429
+
430
+ # Determine the concrete type for each of the generic type
431
+ # parameters used in this method based on the parameters passed
432
+ # into the its class and return a new method pin.
433
+ #
434
+ # @param definitions [Pin::Namespace] The module/class which uses generic types
435
+ # @param context_type [ComplexType] The receiver type
436
+ # @return [self]
437
+ def resolve_generics definitions, context_type
438
+ transform_types { |t| t&.resolve_generics(definitions, context_type) }
439
+ end
440
+
441
+ def all_rooted?
442
+ !return_type || return_type.all_rooted?
443
+ end
444
+
445
+ # @param generics_to_erase [::Array<String>]
446
+ # @return [self]
447
+ def erase_generics generics_to_erase
448
+ return self if generics_to_erase.empty?
449
+ transform_types { |t| t.erase_generics(generics_to_erase) }
450
+ end
451
+
452
+ # @return [String, nil]
453
+ def filename
454
+ return nil if location.nil?
455
+ # @sg-ignore flow sensitive typing needs to handle attrs
456
+ location.filename
457
+ end
458
+
459
+ # @return [Integer]
460
+ def completion_item_kind
461
+ LanguageServer::CompletionItemKinds::KEYWORD
462
+ end
463
+
464
+ # @return [Integer, nil]
465
+ def symbol_kind
466
+ nil
467
+ end
468
+
469
+ def to_s
470
+ desc
471
+ end
472
+
473
+ # @return [Boolean]
474
+ def variable?
475
+ false
476
+ end
477
+
478
+ # @return [Location, nil]
479
+ def best_location
480
+ location || type_location
481
+ end
482
+
483
+ # True if the specified pin is a near match to this one. A near match
484
+ # indicates that the pins contain mostly the same data. Any differences
485
+ # between them should not have an impact on the API surface.
486
+ #
487
+ # @param other [Solargraph::Pin::Base, Object]
488
+ # @return [Boolean]
489
+ def nearly? other
490
+ instance_of?(other.class) &&
491
+ # @sg-ignore Translate to something flow sensitive typing understands
492
+ name == other.name &&
493
+ # @sg-ignore flow sensitive typing needs to handle attrs
494
+ (closure == other.closure || (closure && closure.nearly?(other.closure))) &&
495
+ # @sg-ignore Translate to something flow sensitive typing understands
496
+ (comments == other.comments ||
497
+ # @sg-ignore Translate to something flow sensitive typing understands
498
+ (((maybe_directives? == false && other.maybe_directives? == false) ||
499
+ compare_directives(directives,
500
+ # @sg-ignore Translate to something flow sensitive typing understands
501
+ other.directives)) &&
502
+ # @sg-ignore Translate to something flow sensitive typing understands
503
+ compare_docstring_tags(docstring, other.docstring))
504
+ )
505
+ end
506
+
507
+ # Pin equality is determined using the #nearly? method and also
508
+ # requiring both pins to have the same location.
509
+ #
510
+ # @param other [Object]
511
+ def == other
512
+ return false unless nearly? other
513
+ # @sg-ignore Should add more explicit type check on other
514
+ comments == other.comments && location == other.location
515
+ end
516
+
517
+ # The pin's return type.
518
+ #
519
+ # @return [ComplexType]
520
+ def return_type
521
+ @return_type ||= ComplexType::UNDEFINED
522
+ end
523
+
524
+ # @return [YARD::Docstring]
525
+ def docstring
526
+ parse_comments unless @docstring
527
+ @docstring ||= Solargraph::Source.parse_docstring('').to_docstring
528
+ end
529
+
530
+ # @sg-ignore parse_comments will always set @directives
531
+ # @return [::Array<YARD::Tags::Directive>]
532
+ def directives
533
+ parse_comments unless @directives
534
+ @directives
535
+ end
536
+
537
+ # @return [::Array<YARD::Tags::MacroDirective>]
538
+ def macros
539
+ @macros ||= collect_macros
540
+ end
541
+
542
+ # Perform a quick check to see if this pin possibly includes YARD
543
+ # directives. This method does not require parsing the comments.
544
+ #
545
+ # After the comments have been parsed, this method will return false if
546
+ # no directives were found, regardless of whether it previously appeared
547
+ # possible.
548
+ #
549
+ # @return [Boolean]
550
+ def maybe_directives?
551
+ return !@directives.empty? if defined?(@directives) && @directives
552
+ @maybe_directives ||= comments.include?('@!')
553
+ end
554
+
555
+ # @return [Boolean]
556
+ def deprecated?
557
+ @deprecated ||= docstring.has_tag?('deprecated')
558
+ end
559
+
560
+ # Get a fully qualified type from the pin's return type.
561
+ #
562
+ # The relative type is determined from YARD documentation (@return,
563
+ # @param, @type, etc.) and its namespaces are fully qualified using the
564
+ # provided ApiMap.
565
+ #
566
+ # @param api_map [ApiMap]
567
+ # @return [ComplexType, ComplexType::UniqueType]
568
+ def typify api_map
569
+ return_type.qualify(api_map, *(closure&.gates || ['']))
570
+ end
571
+
572
+ # Infer the pin's return type via static code analysis.
573
+ #
574
+ # @param api_map [ApiMap]
575
+ # @return [ComplexType, ComplexType::UniqueType]
576
+ def probe api_map
577
+ typify api_map
578
+ end
579
+
580
+ # @deprecated Use #typify and/or #probe instead
581
+ # @param api_map [ApiMap]
582
+ # @return [ComplexType, ComplexType::UniqueType]
583
+ def infer api_map
584
+ Solargraph.assert_or_log(:pin_infer,
585
+ 'WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead.')
586
+ type = typify(api_map)
587
+ return type unless type.undefined?
588
+ probe api_map
589
+ end
590
+
591
+ def proxied?
592
+ @proxied ||= false
593
+ end
594
+
595
+ def probed?
596
+ @probed ||= false
597
+ end
598
+
599
+ # @param api_map [ApiMap]
600
+ # @return [self]
601
+ def realize api_map
602
+ return self if return_type.defined?
603
+ type = typify(api_map)
604
+ return proxy(type) if type.defined?
605
+ type = probe(api_map)
606
+ return self if type.undefined?
607
+ result = proxy(type)
608
+ result.probed = true
609
+ result
610
+ end
611
+
612
+ # Return a proxy for this pin with the specified return type. Other than
613
+ # the return type and the #proxied? setting, the proxy should be a clone
614
+ # of the original.
615
+ #
616
+ # @param return_type [ComplexType, ComplexType::UniqueType, nil]
617
+ # @return [self]
618
+ def proxy return_type
619
+ result = dup
620
+ result.return_type = return_type
621
+ result.proxied = true
622
+ result
623
+ end
624
+
625
+ # @deprecated
626
+ # @return [String]
627
+ def identity
628
+ @identity ||= "#{closure&.path}|#{name}|#{location}"
629
+ end
630
+
631
+ # The namespaces available for resolving the current namespace. Each gate
632
+ # should be a fully qualified namespace or the root namespace (i.e., an
633
+ # empty string.)
634
+ #
635
+ # Example: Given the name 'Bar' and the gates ['Foo', ''],
636
+ # the fully qualified namespace should be 'Foo::Bar' or 'Bar'.
637
+ #
638
+ # @return [Array<String>]
639
+ def gates
640
+ @gates ||= closure&.gates || ['']
641
+ end
642
+
643
+ # @return [String, nil]
644
+ def to_rbs
645
+ return_type.to_rbs
646
+ end
647
+
648
+ # @return [String, nil]
649
+ def type_desc
650
+ rbs = to_rbs
651
+ # RBS doesn't have a way to represent a Class<x> type
652
+ rbs = return_type.rooted_tags if return_type.name == 'Class'
653
+ if path
654
+ if rbs
655
+ "#{path} #{rbs}"
656
+ else
657
+ path
658
+ end
659
+ else
660
+ rbs
661
+ end
662
+ end
663
+
664
+ # @return [String]
665
+ def inner_desc
666
+ closure_info = closure&.name.inspect
667
+ binder_info = binder&.desc
668
+ "name=#{name.inspect} return_type=#{type_desc}, context=#{context.rooted_tags}, closure=#{closure_info}, binder=#{binder_info}"
669
+ end
670
+
671
+ # @return [String]
672
+ def desc
673
+ "[#{inner_desc}]"
674
+ end
675
+
676
+ # @return [String]
677
+ def inspect
678
+ "#<#{self.class} `#{inner_desc}`#{all_location_text} via #{source.inspect}>"
679
+ end
680
+
681
+ # @return [String]
682
+ def all_location_text
683
+ if location.nil? && type_location.nil?
684
+ ''
685
+ elsif !location.nil? && type_location.nil?
686
+ " at #{location.inspect})"
687
+ elsif !type_location.nil? && location.nil?
688
+ " at #{type_location.inspect})"
689
+ else
690
+ " at (#{location.inspect} and #{type_location.inspect})"
691
+ end
692
+ end
693
+
694
+ protected
695
+
696
+ # @sg-ignore def should infer as symbol - "Not enough arguments to Module#protected"
697
+ def equality_fields
698
+ [name, location, type_location, closure, source]
699
+ end
700
+
701
+ # @return [Boolean]
702
+ attr_writer :probed
703
+
704
+ # @return [Boolean]
705
+ attr_writer :proxied
706
+
707
+ # @return [ComplexType, ComplexType::UniqueType, nil]
708
+ attr_writer :return_type
709
+
710
+ attr_writer :docstring, :directives
711
+
712
+ private
713
+
714
+ # @return [void]
715
+ def parse_comments
716
+ # HACK: Avoid a NoMethodError on nil with empty overload tags
717
+ if comments.nil? || comments.empty? || comments.strip.end_with?('@overload')
718
+ @docstring = nil
719
+ @directives = []
720
+ else
721
+ # HACK: Pass a dummy code object to the parser for plugins that
722
+ # expect it not to be nil
723
+ parse = Solargraph::Source.parse_docstring(comments)
724
+ @docstring = parse.to_docstring
725
+ @directives = parse.directives
726
+ end
727
+ end
728
+
729
+ # True if two docstrings have the same tags, regardless of any other
730
+ # differences.
731
+ #
732
+ # @param docstring1 [YARD::Docstring]
733
+ # @param docstring2 [YARD::Docstring]
734
+ # @return [Boolean]
735
+ def compare_docstring_tags docstring1, docstring2
736
+ return false if docstring1.tags.length != docstring2.tags.length
737
+ docstring1.tags.each_index do |i|
738
+ return false unless compare_tags(docstring1.tags[i], docstring2.tags[i])
739
+ end
740
+ true
741
+ end
742
+
743
+ # @param dir1 [::Array<YARD::Tags::Directive>]
744
+ # @param dir2 [::Array<YARD::Tags::Directive>]
745
+ # @return [Boolean]
746
+ def compare_directives dir1, dir2
747
+ return false if dir1.length != dir2.length
748
+ dir1.each_index do |i|
749
+ return false unless compare_tags(dir1[i].tag, dir2[i].tag)
750
+ end
751
+ true
752
+ end
753
+
754
+ # @param tag1 [YARD::Tags::Tag]
755
+ # @param tag2 [YARD::Tags::Tag]
756
+ # @return [Boolean]
757
+ def compare_tags tag1, tag2
758
+ tag1.instance_of?(tag2.class) &&
759
+ tag1.tag_name == tag2.tag_name &&
760
+ tag1.text == tag2.text &&
761
+ tag1.name == tag2.name &&
762
+ tag1.types == tag2.types
763
+ end
764
+
765
+ # @return [::Array<YARD::Tags::Handlers::Directive>]
766
+ def collect_macros
767
+ return [] unless maybe_directives?
768
+ parse = Solargraph::Source.parse_docstring(comments)
769
+ parse.directives.select { |d| d.tag.tag_name == 'macro' }
770
+ end
771
+ end
772
+ end
773
+ end