solargraph 0.57.0 → 0.58.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/linting.yml +4 -2
  4. data/.github/workflows/plugins.yml +63 -28
  5. data/.github/workflows/rspec.yml +19 -4
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.gitignore +1 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +90 -1438
  10. data/CHANGELOG.md +39 -0
  11. data/Rakefile +1 -1
  12. data/bin/solargraph +3 -0
  13. data/lib/solargraph/api_map/cache.rb +110 -110
  14. data/lib/solargraph/api_map/constants.rb +279 -218
  15. data/lib/solargraph/api_map/index.rb +193 -169
  16. data/lib/solargraph/api_map/source_to_yard.rb +97 -94
  17. data/lib/solargraph/api_map/store.rb +384 -374
  18. data/lib/solargraph/api_map.rb +945 -951
  19. data/lib/solargraph/bench.rb +45 -45
  20. data/lib/solargraph/complex_type/type_methods.rb +228 -223
  21. data/lib/solargraph/complex_type/unique_type.rb +482 -475
  22. data/lib/solargraph/complex_type.rb +444 -427
  23. data/lib/solargraph/convention/active_support_concern.rb +1 -1
  24. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  25. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
  26. data/lib/solargraph/convention/data_definition.rb +105 -105
  27. data/lib/solargraph/convention/gemfile.rb +15 -15
  28. data/lib/solargraph/convention/gemspec.rb +23 -23
  29. data/lib/solargraph/convention/rakefile.rb +17 -17
  30. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
  31. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
  32. data/lib/solargraph/convention/struct_definition.rb +164 -164
  33. data/lib/solargraph/convention.rb +78 -78
  34. data/lib/solargraph/converters/dd.rb +17 -17
  35. data/lib/solargraph/converters/dl.rb +15 -15
  36. data/lib/solargraph/converters/dt.rb +15 -15
  37. data/lib/solargraph/converters/misc.rb +1 -1
  38. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  39. data/lib/solargraph/diagnostics/rubocop.rb +118 -118
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -66
  41. data/lib/solargraph/diagnostics/type_check.rb +55 -55
  42. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  43. data/lib/solargraph/doc_map.rb +439 -436
  44. data/lib/solargraph/environ.rb +1 -1
  45. data/lib/solargraph/equality.rb +34 -33
  46. data/lib/solargraph/gem_pins.rb +98 -94
  47. data/lib/solargraph/language_server/error_codes.rb +20 -20
  48. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  49. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  50. data/lib/solargraph/language_server/host/message_worker.rb +112 -112
  51. data/lib/solargraph/language_server/host/sources.rb +99 -99
  52. data/lib/solargraph/language_server/host.rb +878 -872
  53. data/lib/solargraph/language_server/message/base.rb +97 -97
  54. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  55. data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -60
  56. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
  57. data/lib/solargraph/language_server/message/extended/document.rb +23 -23
  58. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  59. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -19
  60. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  61. data/lib/solargraph/language_server/message/initialize.rb +191 -191
  62. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
  63. data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
  64. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  65. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
  66. data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -145
  67. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  68. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  69. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  70. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  71. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
  72. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
  73. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -35
  74. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -40
  75. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +26 -26
  76. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  77. data/lib/solargraph/language_server/message.rb +94 -94
  78. data/lib/solargraph/language_server/progress.rb +1 -1
  79. data/lib/solargraph/language_server/request.rb +27 -25
  80. data/lib/solargraph/language_server/transport/data_reader.rb +74 -74
  81. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  82. data/lib/solargraph/library.rb +683 -683
  83. data/lib/solargraph/location.rb +82 -81
  84. data/lib/solargraph/logging.rb +37 -37
  85. data/lib/solargraph/page.rb +92 -93
  86. data/lib/solargraph/parser/comment_ripper.rb +69 -69
  87. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
  88. data/lib/solargraph/parser/node_processor/base.rb +92 -92
  89. data/lib/solargraph/parser/node_processor.rb +62 -62
  90. data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -159
  91. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  92. data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
  93. data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -499
  94. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -21
  95. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
  96. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
  97. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  98. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
  99. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +37 -37
  100. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
  101. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
  102. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
  103. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
  104. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -42
  105. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  106. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
  107. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -43
  108. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -292
  109. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  110. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
  111. data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
  112. data/lib/solargraph/parser/parser_gem.rb +12 -12
  113. data/lib/solargraph/parser/region.rb +69 -69
  114. data/lib/solargraph/parser/snippet.rb +17 -17
  115. data/lib/solargraph/parser.rb +23 -23
  116. data/lib/solargraph/pin/base.rb +729 -708
  117. data/lib/solargraph/pin/base_variable.rb +126 -124
  118. data/lib/solargraph/pin/block.rb +104 -103
  119. data/lib/solargraph/pin/breakable.rb +9 -9
  120. data/lib/solargraph/pin/callable.rb +231 -227
  121. data/lib/solargraph/pin/closure.rb +72 -76
  122. data/lib/solargraph/pin/common.rb +79 -79
  123. data/lib/solargraph/pin/constant.rb +45 -45
  124. data/lib/solargraph/pin/conversions.rb +123 -123
  125. data/lib/solargraph/pin/delegated_method.rb +120 -121
  126. data/lib/solargraph/pin/documenting.rb +114 -114
  127. data/lib/solargraph/pin/instance_variable.rb +34 -34
  128. data/lib/solargraph/pin/keyword.rb +20 -20
  129. data/lib/solargraph/pin/local_variable.rb +75 -79
  130. data/lib/solargraph/pin/method.rb +672 -656
  131. data/lib/solargraph/pin/method_alias.rb +34 -34
  132. data/lib/solargraph/pin/namespace.rb +115 -115
  133. data/lib/solargraph/pin/parameter.rb +275 -271
  134. data/lib/solargraph/pin/proxy_type.rb +39 -36
  135. data/lib/solargraph/pin/reference/override.rb +47 -47
  136. data/lib/solargraph/pin/reference/superclass.rb +15 -15
  137. data/lib/solargraph/pin/reference.rb +39 -48
  138. data/lib/solargraph/pin/search.rb +61 -58
  139. data/lib/solargraph/pin/signature.rb +61 -61
  140. data/lib/solargraph/pin/symbol.rb +53 -53
  141. data/lib/solargraph/pin/until.rb +18 -18
  142. data/lib/solargraph/pin/while.rb +18 -18
  143. data/lib/solargraph/pin.rb +44 -44
  144. data/lib/solargraph/pin_cache.rb +245 -245
  145. data/lib/solargraph/position.rb +132 -118
  146. data/lib/solargraph/range.rb +112 -108
  147. data/lib/solargraph/rbs_map/conversions.rb +823 -802
  148. data/lib/solargraph/rbs_map/core_fills.rb +84 -66
  149. data/lib/solargraph/rbs_map/core_map.rb +58 -54
  150. data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
  151. data/lib/solargraph/rbs_map.rb +163 -163
  152. data/lib/solargraph/server_methods.rb +16 -16
  153. data/lib/solargraph/shell.rb +363 -271
  154. data/lib/solargraph/source/chain/array.rb +37 -37
  155. data/lib/solargraph/source/chain/call.rb +337 -333
  156. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  157. data/lib/solargraph/source/chain/constant.rb +26 -89
  158. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  159. data/lib/solargraph/source/chain/hash.rb +34 -34
  160. data/lib/solargraph/source/chain/if.rb +28 -28
  161. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/link.rb +109 -109
  163. data/lib/solargraph/source/chain/literal.rb +48 -48
  164. data/lib/solargraph/source/chain/or.rb +23 -23
  165. data/lib/solargraph/source/chain/q_call.rb +11 -11
  166. data/lib/solargraph/source/chain/variable.rb +13 -13
  167. data/lib/solargraph/source/chain/z_super.rb +30 -30
  168. data/lib/solargraph/source/chain.rb +291 -289
  169. data/lib/solargraph/source/change.rb +82 -82
  170. data/lib/solargraph/source/cursor.rb +166 -166
  171. data/lib/solargraph/source/encoding_fixes.rb +23 -23
  172. data/lib/solargraph/source/source_chainer.rb +194 -194
  173. data/lib/solargraph/source/updater.rb +55 -55
  174. data/lib/solargraph/source.rb +498 -498
  175. data/lib/solargraph/source_map/clip.rb +226 -234
  176. data/lib/solargraph/source_map/data.rb +34 -34
  177. data/lib/solargraph/source_map/mapper.rb +259 -261
  178. data/lib/solargraph/source_map.rb +212 -207
  179. data/lib/solargraph/type_checker/checks.rb +124 -124
  180. data/lib/solargraph/type_checker/param_def.rb +37 -37
  181. data/lib/solargraph/type_checker/problem.rb +32 -32
  182. data/lib/solargraph/type_checker/rules.rb +84 -70
  183. data/lib/solargraph/type_checker.rb +814 -752
  184. data/lib/solargraph/version.rb +5 -5
  185. data/lib/solargraph/workspace/config.rb +255 -237
  186. data/lib/solargraph/workspace/require_paths.rb +97 -98
  187. data/lib/solargraph/workspace.rb +220 -225
  188. data/lib/solargraph/yard_map/helpers.rb +44 -44
  189. data/lib/solargraph/yard_map/mapper/to_method.rb +130 -129
  190. data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -30
  191. data/lib/solargraph/yard_map/mapper.rb +79 -79
  192. data/lib/solargraph/yard_map/to_method.rb +89 -88
  193. data/lib/solargraph/yard_tags.rb +20 -20
  194. data/lib/solargraph/yardoc.rb +87 -64
  195. data/lib/solargraph.rb +105 -105
  196. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  197. data/rbs/fills/open3/0/open3.rbs +172 -0
  198. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  199. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  200. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  201. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/ast/2.4/.rbs_meta.yaml +9 -0
  204. data/rbs/shims/ast/2.4/ast.rbs +73 -0
  205. data/rbs/shims/parser/3.2.0.1/manifest.yaml +7 -0
  206. data/rbs/shims/parser/3.2.0.1/parser.rbs +201 -0
  207. data/rbs/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  208. data/rbs_collection.yaml +4 -4
  209. data/solargraph.gemspec +15 -4
  210. metadata +71 -16
  211. data/lib/solargraph/parser/node_methods.rb +0 -97
  212. /data/rbs/fills/{tuple.rbs → tuple/tuple.rbs} +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  215. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  216. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,255 +1,255 @@
1
- module Solargraph
2
- module Parser
3
- class FlowSensitiveTyping
4
- include Solargraph::Parser::NodeMethods
5
-
6
- # @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
7
- # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
8
- def initialize(locals, enclosing_breakable_pin = nil)
9
- @locals = locals
10
- @enclosing_breakable_pin = enclosing_breakable_pin
11
- end
12
-
13
- # @param and_node [Parser::AST::Node]
14
- # @param true_ranges [Array<Range>]
15
- #
16
- # @return [void]
17
- def process_and(and_node, true_ranges = [])
18
- # @type [Parser::AST::Node]
19
- lhs = and_node.children[0]
20
- # @type [Parser::AST::Node]
21
- rhs = and_node.children[1]
22
-
23
- before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
24
- before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
25
-
26
- rhs_presence = Range.new(before_rhs_pos,
27
- get_node_end_position(rhs))
28
- process_isa(lhs, true_ranges + [rhs_presence])
29
- end
30
-
31
- # @param if_node [Parser::AST::Node]
32
- #
33
- # @return [void]
34
- def process_if(if_node)
35
- #
36
- # See if we can refine a type based on the result of 'if foo.nil?'
37
- #
38
- # [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
39
- # => s(:if,
40
- # s(:send,
41
- # s(:send, nil, :foo), :is_a?,
42
- # s(:const, nil, :Baz)),
43
- # s(:send, nil, :foo),
44
- # s(:send, nil, :bar))
45
- # [4] pry(main)>
46
- conditional_node = if_node.children[0]
47
- # @type [Parser::AST::Node]
48
- then_clause = if_node.children[1]
49
- # @type [Parser::AST::Node]
50
- else_clause = if_node.children[2]
51
-
52
- true_ranges = []
53
- if always_breaks?(else_clause)
54
- unless enclosing_breakable_pin.nil?
55
- rest_of_breakable_body = Range.new(get_node_end_position(if_node),
56
- get_node_end_position(enclosing_breakable_pin.node))
57
- true_ranges << rest_of_breakable_body
58
- end
59
- end
60
-
61
- unless then_clause.nil?
62
- #
63
- # Add specialized locals for the then clause range
64
- #
65
- before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
66
- before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
67
- true_ranges << Range.new(before_then_clause_pos,
68
- get_node_end_position(then_clause))
69
- end
70
-
71
- process_conditional(conditional_node, true_ranges)
72
- end
73
-
74
- class << self
75
- include Logging
76
- end
77
-
78
- # Find a variable pin by name and where it is used.
79
- #
80
- # Resolves our most specific view of this variable's type by
81
- # preferring pins created by flow-sensitive typing when we have
82
- # them based on the Closure and Location.
83
- #
84
- # @param pins [Array<Pin::LocalVariable>]
85
- # @param name [String]
86
- # @param closure [Pin::Closure]
87
- # @param location [Location]
88
- #
89
- # @return [Array<Pin::LocalVariable>]
90
- def self.visible_pins(pins, name, closure, location)
91
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
92
- pins_with_name = pins.select { |p| p.name == name }
93
- if pins_with_name.empty?
94
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
95
- return []
96
- end
97
- pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
98
- if pins_with_specific_visibility.empty?
99
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
100
- return pins_with_name
101
- end
102
- visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
103
- if visible_pins_specific_to_this_closure.empty?
104
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
105
- return pins_with_specific_visibility
106
- end
107
- flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
108
- if flow_defined_pins.empty?
109
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
110
- return visible_pins_specific_to_this_closure
111
- end
112
-
113
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
114
-
115
- flow_defined_pins
116
- end
117
-
118
- include Logging
119
-
120
- private
121
-
122
- # @param pin [Pin::LocalVariable]
123
- # @param downcast_type_name [String]
124
- # @param presence [Range]
125
- #
126
- # @return [void]
127
- def add_downcast_local(pin, downcast_type_name, presence)
128
- # @todo Create pin#update method
129
- new_pin = Solargraph::Pin::LocalVariable.new(
130
- location: pin.location,
131
- closure: pin.closure,
132
- name: pin.name,
133
- assignment: pin.assignment,
134
- comments: pin.comments,
135
- presence: presence,
136
- return_type: ComplexType.try_parse(downcast_type_name),
137
- presence_certain: true,
138
- source: :flow_sensitive_typing
139
- )
140
- locals.push(new_pin)
141
- end
142
-
143
- # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
144
- # @param presences [Array<Range>]
145
- #
146
- # @return [void]
147
- def process_facts(facts_by_pin, presences)
148
- #
149
- # Add specialized locals for the rest of the block
150
- #
151
- facts_by_pin.each_pair do |pin, facts|
152
- facts.each do |fact|
153
- downcast_type_name = fact.fetch(:type)
154
- presences.each do |presence|
155
- add_downcast_local(pin, downcast_type_name, presence)
156
- end
157
- end
158
- end
159
- end
160
-
161
- # @param conditional_node [Parser::AST::Node]
162
- # @param true_ranges [Array<Range>]
163
- #
164
- # @return [void]
165
- def process_conditional(conditional_node, true_ranges)
166
- if conditional_node.type == :send
167
- process_isa(conditional_node, true_ranges)
168
- elsif conditional_node.type == :and
169
- process_and(conditional_node, true_ranges)
170
- end
171
- end
172
-
173
- # @param isa_node [Parser::AST::Node]
174
- # @return [Array(String, String)]
175
- def parse_isa(isa_node)
176
- return unless isa_node&.type == :send && isa_node.children[1] == :is_a?
177
- # Check if conditional node follows this pattern:
178
- # s(:send,
179
- # s(:send, nil, :foo), :is_a?,
180
- # s(:const, nil, :Baz)),
181
- isa_receiver = isa_node.children[0]
182
- isa_type_name = type_name(isa_node.children[2])
183
- return unless isa_type_name
184
-
185
- # check if isa_receiver looks like this:
186
- # s(:send, nil, :foo)
187
- # and set variable_name to :foo
188
- if isa_receiver&.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
189
- variable_name = isa_receiver.children[1].to_s
190
- end
191
- # or like this:
192
- # (lvar :repr)
193
- variable_name = isa_receiver.children[0].to_s if isa_receiver&.type == :lvar
194
- return unless variable_name
195
-
196
- [isa_type_name, variable_name]
197
- end
198
-
199
- # @param variable_name [String]
200
- # @param position [Position]
201
- #
202
- # @return [Solargraph::Pin::LocalVariable, nil]
203
- def find_local(variable_name, position)
204
- pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
205
- return unless pins.length == 1
206
- pins.first
207
- end
208
-
209
- # @param isa_node [Parser::AST::Node]
210
- # @param true_presences [Array<Range>]
211
- #
212
- # @return [void]
213
- def process_isa(isa_node, true_presences)
214
- isa_type_name, variable_name = parse_isa(isa_node)
215
- return if variable_name.nil? || variable_name.empty?
216
- isa_position = Range.from_node(isa_node).start
217
-
218
- pin = find_local(variable_name, isa_position)
219
- return unless pin
220
-
221
- if_true = {}
222
- if_true[pin] ||= []
223
- if_true[pin] << { type: isa_type_name }
224
- process_facts(if_true, true_presences)
225
- end
226
-
227
- # @param node [Parser::AST::Node]
228
- #
229
- # @return [String, nil]
230
- def type_name(node)
231
- # e.g.,
232
- # s(:const, nil, :Baz)
233
- return unless node&.type == :const
234
- module_node = node.children[0]
235
- class_node = node.children[1]
236
-
237
- return class_node.to_s if module_node.nil?
238
-
239
- module_type_name = type_name(module_node)
240
- return unless module_type_name
241
-
242
- "#{module_type_name}::#{class_node}"
243
- end
244
-
245
- # @param clause_node [Parser::AST::Node]
246
- def always_breaks?(clause_node)
247
- clause_node&.type == :break
248
- end
249
-
250
- attr_reader :locals
251
-
252
- attr_reader :enclosing_breakable_pin
253
- end
254
- end
255
- end
1
+ module Solargraph
2
+ module Parser
3
+ class FlowSensitiveTyping
4
+ include Solargraph::Parser::NodeMethods
5
+
6
+ # @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
7
+ # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
8
+ def initialize(locals, enclosing_breakable_pin = nil)
9
+ @locals = locals
10
+ @enclosing_breakable_pin = enclosing_breakable_pin
11
+ end
12
+
13
+ # @param and_node [Parser::AST::Node]
14
+ # @param true_ranges [Array<Range>]
15
+ #
16
+ # @return [void]
17
+ def process_and(and_node, true_ranges = [])
18
+ # @type [Parser::AST::Node]
19
+ lhs = and_node.children[0]
20
+ # @type [Parser::AST::Node]
21
+ rhs = and_node.children[1]
22
+
23
+ before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
24
+ before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
25
+
26
+ rhs_presence = Range.new(before_rhs_pos,
27
+ get_node_end_position(rhs))
28
+ process_isa(lhs, true_ranges + [rhs_presence])
29
+ end
30
+
31
+ # @param if_node [Parser::AST::Node]
32
+ #
33
+ # @return [void]
34
+ def process_if(if_node)
35
+ #
36
+ # See if we can refine a type based on the result of 'if foo.nil?'
37
+ #
38
+ # [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
39
+ # => s(:if,
40
+ # s(:send,
41
+ # s(:send, nil, :foo), :is_a?,
42
+ # s(:const, nil, :Baz)),
43
+ # s(:send, nil, :foo),
44
+ # s(:send, nil, :bar))
45
+ # [4] pry(main)>
46
+ conditional_node = if_node.children[0]
47
+ # @type [Parser::AST::Node]
48
+ then_clause = if_node.children[1]
49
+ # @type [Parser::AST::Node]
50
+ else_clause = if_node.children[2]
51
+
52
+ true_ranges = []
53
+ if always_breaks?(else_clause)
54
+ unless enclosing_breakable_pin.nil?
55
+ rest_of_breakable_body = Range.new(get_node_end_position(if_node),
56
+ get_node_end_position(enclosing_breakable_pin.node))
57
+ true_ranges << rest_of_breakable_body
58
+ end
59
+ end
60
+
61
+ unless then_clause.nil?
62
+ #
63
+ # Add specialized locals for the then clause range
64
+ #
65
+ before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
66
+ before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
67
+ true_ranges << Range.new(before_then_clause_pos,
68
+ get_node_end_position(then_clause))
69
+ end
70
+
71
+ process_conditional(conditional_node, true_ranges)
72
+ end
73
+
74
+ class << self
75
+ include Logging
76
+ end
77
+
78
+ # Find a variable pin by name and where it is used.
79
+ #
80
+ # Resolves our most specific view of this variable's type by
81
+ # preferring pins created by flow-sensitive typing when we have
82
+ # them based on the Closure and Location.
83
+ #
84
+ # @param pins [Array<Pin::LocalVariable>]
85
+ # @param name [String]
86
+ # @param closure [Pin::Closure]
87
+ # @param location [Location]
88
+ #
89
+ # @return [Array<Pin::LocalVariable>]
90
+ def self.visible_pins(pins, name, closure, location)
91
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
92
+ pins_with_name = pins.select { |p| p.name == name }
93
+ if pins_with_name.empty?
94
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
95
+ return []
96
+ end
97
+ pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
98
+ if pins_with_specific_visibility.empty?
99
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
100
+ return pins_with_name
101
+ end
102
+ visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
103
+ if visible_pins_specific_to_this_closure.empty?
104
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
105
+ return pins_with_specific_visibility
106
+ end
107
+ flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
108
+ if flow_defined_pins.empty?
109
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
110
+ return visible_pins_specific_to_this_closure
111
+ end
112
+
113
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
114
+
115
+ flow_defined_pins
116
+ end
117
+
118
+ include Logging
119
+
120
+ private
121
+
122
+ # @param pin [Pin::LocalVariable]
123
+ # @param downcast_type_name [String]
124
+ # @param presence [Range]
125
+ #
126
+ # @return [void]
127
+ def add_downcast_local(pin, downcast_type_name, presence)
128
+ # @todo Create pin#update method
129
+ new_pin = Solargraph::Pin::LocalVariable.new(
130
+ location: pin.location,
131
+ closure: pin.closure,
132
+ name: pin.name,
133
+ assignment: pin.assignment,
134
+ comments: pin.comments,
135
+ presence: presence,
136
+ return_type: ComplexType.try_parse(downcast_type_name),
137
+ presence_certain: true,
138
+ source: :flow_sensitive_typing
139
+ )
140
+ locals.push(new_pin)
141
+ end
142
+
143
+ # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
144
+ # @param presences [Array<Range>]
145
+ #
146
+ # @return [void]
147
+ def process_facts(facts_by_pin, presences)
148
+ #
149
+ # Add specialized locals for the rest of the block
150
+ #
151
+ facts_by_pin.each_pair do |pin, facts|
152
+ facts.each do |fact|
153
+ downcast_type_name = fact.fetch(:type)
154
+ presences.each do |presence|
155
+ add_downcast_local(pin, downcast_type_name, presence)
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ # @param conditional_node [Parser::AST::Node]
162
+ # @param true_ranges [Array<Range>]
163
+ #
164
+ # @return [void]
165
+ def process_conditional(conditional_node, true_ranges)
166
+ if conditional_node.type == :send
167
+ process_isa(conditional_node, true_ranges)
168
+ elsif conditional_node.type == :and
169
+ process_and(conditional_node, true_ranges)
170
+ end
171
+ end
172
+
173
+ # @param isa_node [Parser::AST::Node]
174
+ # @return [Array(String, String), nil]
175
+ def parse_isa(isa_node)
176
+ return unless isa_node&.type == :send && isa_node.children[1] == :is_a?
177
+ # Check if conditional node follows this pattern:
178
+ # s(:send,
179
+ # s(:send, nil, :foo), :is_a?,
180
+ # s(:const, nil, :Baz)),
181
+ isa_receiver = isa_node.children[0]
182
+ isa_type_name = type_name(isa_node.children[2])
183
+ return unless isa_type_name
184
+
185
+ # check if isa_receiver looks like this:
186
+ # s(:send, nil, :foo)
187
+ # and set variable_name to :foo
188
+ if isa_receiver&.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
189
+ variable_name = isa_receiver.children[1].to_s
190
+ end
191
+ # or like this:
192
+ # (lvar :repr)
193
+ variable_name = isa_receiver.children[0].to_s if isa_receiver&.type == :lvar
194
+ return unless variable_name
195
+
196
+ [isa_type_name, variable_name]
197
+ end
198
+
199
+ # @param variable_name [String]
200
+ # @param position [Position]
201
+ #
202
+ # @return [Solargraph::Pin::LocalVariable, nil]
203
+ def find_local(variable_name, position)
204
+ pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
205
+ return unless pins.length == 1
206
+ pins.first
207
+ end
208
+
209
+ # @param isa_node [Parser::AST::Node]
210
+ # @param true_presences [Array<Range>]
211
+ #
212
+ # @return [void]
213
+ def process_isa(isa_node, true_presences)
214
+ isa_type_name, variable_name = parse_isa(isa_node)
215
+ return if variable_name.nil? || variable_name.empty?
216
+ isa_position = Range.from_node(isa_node).start
217
+
218
+ pin = find_local(variable_name, isa_position)
219
+ return unless pin
220
+
221
+ if_true = {}
222
+ if_true[pin] ||= []
223
+ if_true[pin] << { type: isa_type_name }
224
+ process_facts(if_true, true_presences)
225
+ end
226
+
227
+ # @param node [Parser::AST::Node]
228
+ #
229
+ # @return [String, nil]
230
+ def type_name(node)
231
+ # e.g.,
232
+ # s(:const, nil, :Baz)
233
+ return unless node&.type == :const
234
+ module_node = node.children[0]
235
+ class_node = node.children[1]
236
+
237
+ return class_node.to_s if module_node.nil?
238
+
239
+ module_type_name = type_name(module_node)
240
+ return unless module_type_name
241
+
242
+ "#{module_type_name}::#{class_node}"
243
+ end
244
+
245
+ # @param clause_node [Parser::AST::Node]
246
+ def always_breaks?(clause_node)
247
+ clause_node&.type == :break
248
+ end
249
+
250
+ attr_reader :locals
251
+
252
+ attr_reader :enclosing_breakable_pin
253
+ end
254
+ end
255
+ end