solargraph 0.58.3 → 0.59.2

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +12 -5
  4. data/.github/workflows/plugins.yml +54 -34
  5. data/.github/workflows/rspec.yml +15 -28
  6. data/.github/workflows/typecheck.yml +6 -3
  7. data/.rubocop.yml +38 -6
  8. data/.rubocop_todo.yml +53 -966
  9. data/CHANGELOG.md +24 -0
  10. data/Gemfile +3 -1
  11. data/README.md +3 -3
  12. data/Rakefile +26 -23
  13. data/bin/solargraph +2 -1
  14. data/lib/solargraph/api_map/cache.rb +3 -3
  15. data/lib/solargraph/api_map/constants.rb +12 -3
  16. data/lib/solargraph/api_map/index.rb +29 -18
  17. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  18. data/lib/solargraph/api_map/store.rb +40 -30
  19. data/lib/solargraph/api_map.rb +160 -78
  20. data/lib/solargraph/bench.rb +2 -3
  21. data/lib/solargraph/complex_type/conformance.rb +176 -0
  22. data/lib/solargraph/complex_type/type_methods.rb +31 -18
  23. data/lib/solargraph/complex_type/unique_type.rb +221 -63
  24. data/lib/solargraph/complex_type.rb +173 -59
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -111
  26. data/lib/solargraph/convention/base.rb +50 -50
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +1 -1
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
  29. data/lib/solargraph/convention/data_definition.rb +5 -2
  30. data/lib/solargraph/convention/gemfile.rb +1 -1
  31. data/lib/solargraph/convention/gemspec.rb +1 -1
  32. data/lib/solargraph/convention/rakefile.rb +1 -1
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
  35. data/lib/solargraph/convention/struct_definition.rb +8 -4
  36. data/lib/solargraph/convention.rb +2 -2
  37. data/lib/solargraph/converters/dd.rb +2 -0
  38. data/lib/solargraph/converters/dl.rb +2 -0
  39. data/lib/solargraph/converters/dt.rb +2 -0
  40. data/lib/solargraph/converters/misc.rb +2 -0
  41. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  42. data/lib/solargraph/diagnostics/rubocop.rb +11 -10
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  44. data/lib/solargraph/diagnostics/type_check.rb +11 -10
  45. data/lib/solargraph/diagnostics/update_errors.rb +4 -8
  46. data/lib/solargraph/diagnostics.rb +55 -55
  47. data/lib/solargraph/doc_map.rb +38 -39
  48. data/lib/solargraph/environ.rb +52 -52
  49. data/lib/solargraph/equality.rb +4 -4
  50. data/lib/solargraph/gem_pins.rb +4 -15
  51. data/lib/solargraph/language_server/error_codes.rb +10 -10
  52. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  53. data/lib/solargraph/language_server/host/dispatch.rb +3 -3
  54. data/lib/solargraph/language_server/host/message_worker.rb +4 -3
  55. data/lib/solargraph/language_server/host/sources.rb +2 -1
  56. data/lib/solargraph/language_server/host.rb +35 -28
  57. data/lib/solargraph/language_server/message/base.rb +1 -1
  58. data/lib/solargraph/language_server/message/client/register_capability.rb +1 -3
  59. data/lib/solargraph/language_server/message/completion_item/resolve.rb +6 -8
  60. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
  61. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  62. data/lib/solargraph/language_server/message/extended/document_gems.rb +7 -7
  63. data/lib/solargraph/language_server/message/extended/download_core.rb +2 -1
  64. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  65. data/lib/solargraph/language_server/message/extended/search.rb +1 -1
  66. data/lib/solargraph/language_server/message/initialize.rb +20 -14
  67. data/lib/solargraph/language_server/message/initialized.rb +28 -28
  68. data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
  69. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
  70. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +17 -10
  71. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
  72. data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
  73. data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
  74. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +13 -6
  75. data/lib/solargraph/language_server/message/text_document/references.rb +17 -10
  76. data/lib/solargraph/language_server/message/text_document/rename.rb +20 -13
  77. data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
  78. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
  79. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  80. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +34 -28
  81. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +38 -30
  82. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +23 -17
  83. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
  84. data/lib/solargraph/language_server/message.rb +1 -1
  85. data/lib/solargraph/language_server/progress.rb +143 -143
  86. data/lib/solargraph/language_server/request.rb +4 -2
  87. data/lib/solargraph/language_server/transport/adapter.rb +68 -68
  88. data/lib/solargraph/language_server/transport/data_reader.rb +11 -13
  89. data/lib/solargraph/language_server/uri_helpers.rb +2 -2
  90. data/lib/solargraph/language_server.rb +20 -20
  91. data/lib/solargraph/library.rb +57 -38
  92. data/lib/solargraph/location.rb +17 -14
  93. data/lib/solargraph/logging.rb +22 -4
  94. data/lib/solargraph/page.rb +1 -1
  95. data/lib/solargraph/parser/comment_ripper.rb +19 -4
  96. data/lib/solargraph/parser/flow_sensitive_typing.rb +324 -108
  97. data/lib/solargraph/parser/node_processor/base.rb +34 -4
  98. data/lib/solargraph/parser/node_processor.rb +8 -7
  99. data/lib/solargraph/parser/parser_gem/class_methods.rb +30 -14
  100. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -1
  101. data/lib/solargraph/parser/parser_gem/node_chainer.rb +51 -25
  102. data/lib/solargraph/parser/parser_gem/node_methods.rb +181 -73
  103. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +24 -24
  104. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  105. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
  106. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -12
  108. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +36 -36
  109. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +24 -24
  110. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
  111. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +9 -8
  112. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +24 -24
  113. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  114. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
  115. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  116. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  117. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +40 -40
  118. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
  119. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  120. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  121. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  122. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
  123. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
  124. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +20 -20
  125. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +1 -1
  126. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  127. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
  128. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  129. data/lib/solargraph/parser/parser_gem.rb +2 -0
  130. data/lib/solargraph/parser/region.rb +9 -3
  131. data/lib/solargraph/parser/snippet.rb +3 -1
  132. data/lib/solargraph/parser.rb +2 -0
  133. data/lib/solargraph/pin/base.rb +126 -82
  134. data/lib/solargraph/pin/base_variable.rb +273 -24
  135. data/lib/solargraph/pin/block.rb +29 -6
  136. data/lib/solargraph/pin/breakable.rb +7 -1
  137. data/lib/solargraph/pin/callable.rb +65 -21
  138. data/lib/solargraph/pin/closure.rb +7 -10
  139. data/lib/solargraph/pin/common.rb +24 -6
  140. data/lib/solargraph/pin/compound_statement.rb +55 -0
  141. data/lib/solargraph/pin/constant.rb +3 -5
  142. data/lib/solargraph/pin/conversions.rb +10 -4
  143. data/lib/solargraph/pin/delegated_method.rb +19 -8
  144. data/lib/solargraph/pin/documenting.rb +4 -2
  145. data/lib/solargraph/pin/instance_variable.rb +5 -1
  146. data/lib/solargraph/pin/keyword.rb +0 -4
  147. data/lib/solargraph/pin/local_variable.rb +15 -59
  148. data/lib/solargraph/pin/method.rb +158 -104
  149. data/lib/solargraph/pin/method_alias.rb +8 -0
  150. data/lib/solargraph/pin/namespace.rb +19 -12
  151. data/lib/solargraph/pin/parameter.rb +102 -36
  152. data/lib/solargraph/pin/proxy_type.rb +4 -1
  153. data/lib/solargraph/pin/reference/override.rb +1 -1
  154. data/lib/solargraph/pin/reference/require.rb +14 -14
  155. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  156. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  157. data/lib/solargraph/pin/reference.rb +20 -0
  158. data/lib/solargraph/pin/search.rb +8 -7
  159. data/lib/solargraph/pin/signature.rb +15 -12
  160. data/lib/solargraph/pin/singleton.rb +11 -11
  161. data/lib/solargraph/pin/symbol.rb +2 -1
  162. data/lib/solargraph/pin/until.rb +2 -4
  163. data/lib/solargraph/pin/while.rb +2 -4
  164. data/lib/solargraph/pin.rb +2 -0
  165. data/lib/solargraph/pin_cache.rb +22 -19
  166. data/lib/solargraph/position.rb +17 -10
  167. data/lib/solargraph/range.rb +16 -15
  168. data/lib/solargraph/rbs_map/conversions.rb +367 -231
  169. data/lib/solargraph/rbs_map/core_fills.rb +18 -11
  170. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  171. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
  172. data/lib/solargraph/rbs_map.rb +76 -32
  173. data/lib/solargraph/server_methods.rb +1 -1
  174. data/lib/solargraph/shell.rb +258 -66
  175. data/lib/solargraph/source/chain/array.rb +3 -12
  176. data/lib/solargraph/source/chain/block_symbol.rb +13 -13
  177. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  178. data/lib/solargraph/source/chain/call.rb +96 -56
  179. data/lib/solargraph/source/chain/class_variable.rb +1 -1
  180. data/lib/solargraph/source/chain/constant.rb +5 -1
  181. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  182. data/lib/solargraph/source/chain/hash.rb +8 -5
  183. data/lib/solargraph/source/chain/head.rb +19 -19
  184. data/lib/solargraph/source/chain/if.rb +12 -10
  185. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  186. data/lib/solargraph/source/chain/link.rb +12 -22
  187. data/lib/solargraph/source/chain/literal.rb +22 -15
  188. data/lib/solargraph/source/chain/or.rb +10 -4
  189. data/lib/solargraph/source/chain/q_call.rb +2 -0
  190. data/lib/solargraph/source/chain/variable.rb +3 -1
  191. data/lib/solargraph/source/chain/z_super.rb +1 -3
  192. data/lib/solargraph/source/chain.rb +51 -38
  193. data/lib/solargraph/source/change.rb +12 -5
  194. data/lib/solargraph/source/cursor.rb +33 -18
  195. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  196. data/lib/solargraph/source/source_chainer.rb +56 -32
  197. data/lib/solargraph/source/updater.rb +5 -1
  198. data/lib/solargraph/source.rb +59 -35
  199. data/lib/solargraph/source_map/clip.rb +54 -30
  200. data/lib/solargraph/source_map/data.rb +4 -1
  201. data/lib/solargraph/source_map/mapper.rb +69 -42
  202. data/lib/solargraph/source_map.rb +21 -9
  203. data/lib/solargraph/type_checker/problem.rb +3 -1
  204. data/lib/solargraph/type_checker/rules.rb +81 -8
  205. data/lib/solargraph/type_checker.rb +196 -122
  206. data/lib/solargraph/version.rb +1 -1
  207. data/lib/solargraph/workspace/config.rb +14 -11
  208. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  209. data/lib/solargraph/workspace/require_paths.rb +1 -0
  210. data/lib/solargraph/workspace.rb +50 -28
  211. data/lib/solargraph/yard_map/cache.rb +25 -25
  212. data/lib/solargraph/yard_map/helpers.rb +8 -3
  213. data/lib/solargraph/yard_map/mapper/to_constant.rb +28 -28
  214. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  215. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  216. data/lib/solargraph/yard_map/mapper.rb +13 -8
  217. data/lib/solargraph/yard_map.rb +17 -18
  218. data/lib/solargraph/yard_tags.rb +2 -2
  219. data/lib/solargraph/yardoc.rb +7 -4
  220. data/lib/solargraph.rb +33 -10
  221. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  222. data/rbs/shims/ast/0/node.rbs +1 -1
  223. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  224. data/solargraph.gemspec +37 -35
  225. metadata +41 -42
  226. data/lib/solargraph/type_checker/checks.rb +0 -124
  227. data/lib/solargraph/type_checker/param_def.rb +0 -37
  228. data/lib/solargraph/yard_map/to_method.rb +0 -89
  229. data/rbs/fills/tuple/tuple.rbs +0 -149
@@ -1,20 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Solargraph
2
4
  module Parser
3
5
  class FlowSensitiveTyping
4
6
  include Solargraph::Parser::NodeMethods
5
7
 
6
- # @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
8
+ # @param locals [Array<Solargraph::Pin::LocalVariable>]
9
+ # @param ivars [Array<Solargraph::Pin::InstanceVariable>]
7
10
  # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
8
- def initialize(locals, enclosing_breakable_pin = nil)
11
+ # @param enclosing_compound_statement_pin [Solargraph::Pin::CompoundStatement, nil]
12
+ def initialize locals, ivars, enclosing_breakable_pin, enclosing_compound_statement_pin
9
13
  @locals = locals
14
+ @ivars = ivars
10
15
  @enclosing_breakable_pin = enclosing_breakable_pin
16
+ @enclosing_compound_statement_pin = enclosing_compound_statement_pin
11
17
  end
12
18
 
13
19
  # @param and_node [Parser::AST::Node]
14
20
  # @param true_ranges [Array<Range>]
21
+ # @param false_ranges [Array<Range>]
15
22
  #
16
23
  # @return [void]
17
- def process_and(and_node, true_ranges = [])
24
+ def process_and and_node, true_ranges = [], false_ranges = []
25
+ return unless and_node.type == :and
26
+
18
27
  # @type [Parser::AST::Node]
19
28
  lhs = and_node.children[0]
20
29
  # @type [Parser::AST::Node]
@@ -25,13 +34,64 @@ module Solargraph
25
34
 
26
35
  rhs_presence = Range.new(before_rhs_pos,
27
36
  get_node_end_position(rhs))
28
- process_isa(lhs, true_ranges + [rhs_presence])
37
+
38
+ # can't assume if an and is false that every single condition
39
+ # is false, so don't provide any false ranges to assert facts
40
+ # on
41
+ process_expression(lhs, true_ranges + [rhs_presence], [])
42
+ process_expression(rhs, true_ranges, [])
43
+ end
44
+
45
+ # @param or_node [Parser::AST::Node]
46
+ # @param true_ranges [Array<Range>]
47
+ # @param false_ranges [Array<Range>]
48
+ #
49
+ # @return [void]
50
+ def process_or or_node, true_ranges = [], false_ranges = []
51
+ return unless or_node.type == :or
52
+
53
+ # @type [Parser::AST::Node]
54
+ lhs = or_node.children[0]
55
+ # @type [Parser::AST::Node]
56
+ rhs = or_node.children[1]
57
+
58
+ before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
59
+ before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
60
+
61
+ rhs_presence = Range.new(before_rhs_pos,
62
+ get_node_end_position(rhs))
63
+
64
+ # can assume if an or is false that every single condition is
65
+ # false, so provide false ranges to assert facts on
66
+
67
+ # can't assume if an or is true that every single condition is
68
+ # true, so don't provide true ranges to assert facts on
69
+
70
+ process_expression(lhs, [], false_ranges + [rhs_presence])
71
+ process_expression(rhs, [], false_ranges)
72
+ end
73
+
74
+ # @param node [Parser::AST::Node]
75
+ # @param true_presences [Array<Range>]
76
+ # @param false_presences [Array<Range>]
77
+ #
78
+ # @return [void]
79
+ def process_calls node, true_presences, false_presences
80
+ return unless node.type == :send
81
+
82
+ process_isa(node, true_presences, false_presences)
83
+ process_nilp(node, true_presences, false_presences)
84
+ process_bang(node, true_presences, false_presences)
29
85
  end
30
86
 
31
87
  # @param if_node [Parser::AST::Node]
88
+ # @param true_ranges [Array<Range>]
89
+ # @param false_ranges [Array<Range>]
32
90
  #
33
91
  # @return [void]
34
- def process_if(if_node)
92
+ def process_if if_node, true_ranges = [], false_ranges = []
93
+ return if if_node.type != :if
94
+
35
95
  #
36
96
  # See if we can refine a type based on the result of 'if foo.nil?'
37
97
  #
@@ -44,23 +104,37 @@ module Solargraph
44
104
  # s(:send, nil, :bar))
45
105
  # [4] pry(main)>
46
106
  conditional_node = if_node.children[0]
47
- # @type [Parser::AST::Node]
107
+ # @type [Parser::AST::Node, nil]
48
108
  then_clause = if_node.children[1]
49
- # @type [Parser::AST::Node]
109
+ # @type [Parser::AST::Node, nil]
50
110
  else_clause = if_node.children[2]
51
111
 
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
112
+ unless enclosing_breakable_pin.nil?
113
+ rest_of_breakable_body = Range.new(get_node_end_position(if_node),
114
+ get_node_end_position(enclosing_breakable_pin.node))
115
+
116
+ false_ranges << rest_of_breakable_body if always_breaks?(then_clause)
117
+
118
+ true_ranges << rest_of_breakable_body if always_breaks?(else_clause)
119
+ end
120
+
121
+ unless enclosing_compound_statement_pin.node.nil?
122
+ rest_of_returnable_body = Range.new(get_node_end_position(if_node),
123
+ get_node_end_position(enclosing_compound_statement_pin.node))
124
+
125
+ #
126
+ # if one of the clauses always leaves the compound
127
+ # statement, we can assume things about the rest of the
128
+ # compound statement
129
+ #
130
+ false_ranges << rest_of_returnable_body if always_leaves_compound_statement?(then_clause)
131
+
132
+ true_ranges << rest_of_returnable_body if always_leaves_compound_statement?(else_clause)
59
133
  end
60
134
 
61
135
  unless then_clause.nil?
62
136
  #
63
- # Add specialized locals for the then clause range
137
+ # If the condition is true we can assume things about the then clause
64
138
  #
65
139
  before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
66
140
  before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
@@ -68,170 +142,307 @@ module Solargraph
68
142
  get_node_end_position(then_clause))
69
143
  end
70
144
 
71
- process_conditional(conditional_node, true_ranges)
72
- end
145
+ unless else_clause.nil?
146
+ #
147
+ # If the condition is true we can assume things about the else clause
148
+ #
149
+ before_else_clause_loc = else_clause.location.expression.adjust(begin_pos: -1)
150
+ before_else_clause_pos = Position.new(before_else_clause_loc.line, before_else_clause_loc.column)
151
+ false_ranges << Range.new(before_else_clause_pos,
152
+ get_node_end_position(else_clause))
153
+ end
73
154
 
74
- class << self
75
- include Logging
155
+ process_expression(conditional_node, true_ranges, false_ranges)
76
156
  end
77
157
 
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]
158
+ # @param while_node [Parser::AST::Node]
159
+ # @param true_ranges [Array<Range>]
160
+ # @param false_ranges [Array<Range>]
88
161
  #
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
162
+ # @return [void]
163
+ def process_while while_node, true_ranges = [], false_ranges = []
164
+ return if while_node.type != :while
165
+
166
+ #
167
+ # See if we can refine a type based on the result of 'if foo.nil?'
168
+ #
169
+ # [3] pry(main)> Parser::CurrentRuby.parse("while a; b; c; end")
170
+ # => s(:while,
171
+ # s(:send, nil, :a),
172
+ # s(:begin,
173
+ # s(:send, nil, :b),
174
+ # s(:send, nil, :c)))
175
+ # [4] pry(main)>
176
+ conditional_node = while_node.children[0]
177
+ # @type [Parser::AST::Node, nil]
178
+ do_clause = while_node.children[1]
179
+
180
+ unless do_clause.nil?
181
+ #
182
+ # If the condition is true we can assume things about the do clause
183
+ #
184
+ before_do_clause_loc = do_clause.location.expression.adjust(begin_pos: -1)
185
+ before_do_clause_pos = Position.new(before_do_clause_loc.line, before_do_clause_loc.column)
186
+ true_ranges << Range.new(before_do_clause_pos,
187
+ get_node_end_position(do_clause))
111
188
  end
112
189
 
113
- logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
190
+ process_expression(conditional_node, true_ranges, false_ranges)
191
+ end
114
192
 
115
- flow_defined_pins
193
+ class << self
194
+ include Logging
116
195
  end
117
196
 
118
197
  include Logging
119
198
 
120
199
  private
121
200
 
122
- # @param pin [Pin::LocalVariable]
123
- # @param downcast_type_name [String]
201
+ # @param pin [Pin::BaseVariable]
124
202
  # @param presence [Range]
203
+ # @param downcast_type [ComplexType, nil]
204
+ # @param downcast_not_type [ComplexType, nil]
125
205
  #
126
206
  # @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}>}]
207
+ def add_downcast_var pin, presence:, downcast_type:, downcast_not_type:
208
+ new_pin = pin.downcast(exclude_return_type: downcast_not_type,
209
+ intersection_return_type: downcast_type,
210
+ source: :flow_sensitive_typing,
211
+ presence: presence)
212
+ if pin.is_a?(Pin::LocalVariable)
213
+ locals.push(new_pin)
214
+ elsif pin.is_a?(Pin::InstanceVariable)
215
+ ivars.push(new_pin)
216
+ else
217
+ raise "Tried to add invalid pin type #{pin.class} in FlowSensitiveTyping"
218
+ end
219
+ end
220
+
221
+ # @param facts_by_pin [Hash{Pin::BaseVariable => Array<Hash{:type, :not_type => ComplexType}>}]
144
222
  # @param presences [Array<Range>]
145
223
  #
146
224
  # @return [void]
147
- def process_facts(facts_by_pin, presences)
225
+ def process_facts facts_by_pin, presences
148
226
  #
149
- # Add specialized locals for the rest of the block
227
+ # Add specialized vars for the rest of the block
150
228
  #
151
229
  facts_by_pin.each_pair do |pin, facts|
152
230
  facts.each do |fact|
153
- downcast_type_name = fact.fetch(:type)
231
+ downcast_type = fact.fetch(:type, nil)
232
+ downcast_not_type = fact.fetch(:not_type, nil)
154
233
  presences.each do |presence|
155
- add_downcast_local(pin, downcast_type_name, presence)
234
+ add_downcast_var(pin,
235
+ presence: presence,
236
+ downcast_type: downcast_type,
237
+ downcast_not_type: downcast_not_type)
156
238
  end
157
239
  end
158
240
  end
159
241
  end
160
242
 
161
- # @param conditional_node [Parser::AST::Node]
243
+ # @param expression_node [Parser::AST::Node]
162
244
  # @param true_ranges [Array<Range>]
245
+ # @param false_ranges [Array<Range>]
163
246
  #
164
247
  # @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
248
+ def process_expression expression_node, true_ranges, false_ranges
249
+ process_calls(expression_node, true_ranges, false_ranges)
250
+ process_and(expression_node, true_ranges, false_ranges)
251
+ process_or(expression_node, true_ranges, false_ranges)
252
+ process_variable(expression_node, true_ranges, false_ranges)
171
253
  end
172
254
 
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?
255
+ # @param call_node [Parser::AST::Node]
256
+ # @param method_name [Symbol]
257
+ # @return [Array(String, String), nil] Tuple of rgument to
258
+ # function, then receiver of function if it's a variable,
259
+ # otherwise nil if no simple variable receiver
260
+ def parse_call call_node, method_name
261
+ return unless call_node&.type == :send && call_node.children[1] == method_name
177
262
  # Check if conditional node follows this pattern:
178
263
  # s(:send,
179
264
  # s(:send, nil, :foo), :is_a?,
180
265
  # 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
266
+ #
267
+ call_receiver = call_node.children[0]
268
+ call_arg = type_name(call_node.children[2])
184
269
 
185
- # check if isa_receiver looks like this:
270
+ # check if call_receiver looks like this:
186
271
  # s(:send, nil, :foo)
187
272
  # 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
273
+ if call_receiver&.type == :send && call_receiver.children[0].nil? && call_receiver.children[1].is_a?(Symbol)
274
+ variable_name = call_receiver.children[1].to_s
190
275
  end
191
276
  # or like this:
192
277
  # (lvar :repr)
193
- variable_name = isa_receiver.children[0].to_s if isa_receiver&.type == :lvar
278
+ variable_name = call_receiver.children[0].to_s if %i[lvar ivar].include?(call_receiver&.type)
194
279
  return unless variable_name
195
280
 
196
- [isa_type_name, variable_name]
281
+ [call_arg, variable_name]
282
+ end
283
+
284
+ # @param isa_node [Parser::AST::Node]
285
+ # @return [Array(String, String), nil]
286
+ def parse_isa isa_node
287
+ call_type_name, variable_name = parse_call(isa_node, :is_a?)
288
+
289
+ return unless call_type_name
290
+
291
+ [call_type_name, variable_name]
197
292
  end
198
293
 
199
294
  # @param variable_name [String]
200
295
  # @param position [Position]
201
296
  #
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
297
+ # @sg-ignore Solargraph::Parser::FlowSensitiveTyping#find_var
298
+ # return type could not be inferred
299
+ # @return [Solargraph::Pin::LocalVariable, Solargraph::Pin::InstanceVariable, nil]
300
+ def find_var variable_name, position
301
+ if variable_name.start_with?('@')
302
+ # @sg-ignore flow sensitive typing needs to handle attrs
303
+ ivars.find { |ivar| ivar.name == variable_name && (!ivar.presence || ivar.presence.include?(position)) }
304
+ else
305
+ # @sg-ignore flow sensitive typing needs to handle attrs
306
+ locals.find { |pin| pin.name == variable_name && (!pin.presence || pin.presence.include?(position)) }
307
+ end
207
308
  end
208
309
 
209
310
  # @param isa_node [Parser::AST::Node]
210
311
  # @param true_presences [Array<Range>]
312
+ # @param false_presences [Array<Range>]
211
313
  #
212
314
  # @return [void]
213
- def process_isa(isa_node, true_presences)
315
+ def process_isa isa_node, true_presences, false_presences
214
316
  isa_type_name, variable_name = parse_isa(isa_node)
215
317
  return if variable_name.nil? || variable_name.empty?
318
+ # @sg-ignore Need to add nil check here
216
319
  isa_position = Range.from_node(isa_node).start
217
320
 
218
- pin = find_local(variable_name, isa_position)
321
+ pin = find_var(variable_name, isa_position)
322
+ return unless pin
323
+
324
+ # @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
325
+ if_true = {}
326
+ if_true[pin] ||= []
327
+ if_true[pin] << { type: ComplexType.parse(isa_type_name) }
328
+ process_facts(if_true, true_presences)
329
+
330
+ # @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
331
+ if_false = {}
332
+ if_false[pin] ||= []
333
+ if_false[pin] << { not_type: ComplexType.parse(isa_type_name) }
334
+ process_facts(if_false, false_presences)
335
+ end
336
+
337
+ # @param nilp_node [Parser::AST::Node]
338
+ # @return [Array(String, String), nil]
339
+ def parse_nilp nilp_node
340
+ parse_call(nilp_node, :nil?)
341
+ end
342
+
343
+ # @param nilp_node [Parser::AST::Node]
344
+ # @param true_presences [Array<Range>]
345
+ # @param false_presences [Array<Range>]
346
+ #
347
+ # @return [void]
348
+ def process_nilp nilp_node, true_presences, false_presences
349
+ nilp_arg, variable_name = parse_nilp(nilp_node)
350
+ return if variable_name.nil? || variable_name.empty?
351
+ # if .nil? got an argument, move on, this isn't the situation
352
+ # we're looking for and typechecking will cover any invalid
353
+ # ones
354
+ return unless nilp_arg.nil?
355
+ # @sg-ignore Need to add nil check here
356
+ nilp_position = Range.from_node(nilp_node).start
357
+
358
+ pin = find_var(variable_name, nilp_position)
219
359
  return unless pin
220
360
 
361
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
221
362
  if_true = {}
222
363
  if_true[pin] ||= []
223
- if_true[pin] << { type: isa_type_name }
364
+ if_true[pin] << { type: ComplexType::NIL }
224
365
  process_facts(if_true, true_presences)
366
+
367
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
368
+ if_false = {}
369
+ if_false[pin] ||= []
370
+ if_false[pin] << { not_type: ComplexType::NIL }
371
+ process_facts(if_false, false_presences)
372
+ end
373
+
374
+ # @param bang_node [Parser::AST::Node]
375
+ # @return [Array(String, String), nil]
376
+ def parse_bang bang_node
377
+ parse_call(bang_node, :!)
378
+ end
379
+
380
+ # @param bang_node [Parser::AST::Node]
381
+ # @param true_presences [Array<Range>]
382
+ # @param false_presences [Array<Range>]
383
+ #
384
+ # @return [void]
385
+ def process_bang bang_node, true_presences, false_presences
386
+ # pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("!2")
387
+ # => s(:send,
388
+ # s(:int, 2), :!)
389
+ # end
390
+ return unless bang_node.type == :send && bang_node.children[1] == :!
391
+
392
+ receiver = bang_node.children[0]
393
+
394
+ # swap the two presences
395
+ process_expression(receiver, false_presences, true_presences)
396
+ end
397
+
398
+ # @param var_node [Parser::AST::Node]
399
+ #
400
+ # @return [String, nil] Variable name referenced
401
+ def parse_variable var_node
402
+ return if var_node.children.length != 1
403
+
404
+ var_node.children[0]&.to_s
405
+ end
406
+
407
+ # @return [void]
408
+ # @param node [Parser::AST::Node]
409
+ # @param true_presences [Array<Range>]
410
+ # @param false_presences [Array<Range>]
411
+ def process_variable node, true_presences, false_presences
412
+ return unless %i[lvar ivar cvar gvar].include?(node.type)
413
+
414
+ variable_name = parse_variable(node)
415
+ return if variable_name.nil?
416
+
417
+ # @sg-ignore Need to add nil check here
418
+ var_position = Range.from_node(node).start
419
+
420
+ pin = find_var(variable_name, var_position)
421
+ return unless pin
422
+
423
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
424
+ if_true = {}
425
+ if_true[pin] ||= []
426
+ if_true[pin] << { not_type: ComplexType::NIL }
427
+ process_facts(if_true, true_presences)
428
+
429
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
430
+ if_false = {}
431
+ if_false[pin] ||= []
432
+ if_false[pin] << { type: ComplexType.parse('nil, false') }
433
+ process_facts(if_false, false_presences)
225
434
  end
226
435
 
227
436
  # @param node [Parser::AST::Node]
228
437
  #
229
438
  # @return [String, nil]
230
- def type_name(node)
439
+ def type_name node
231
440
  # e.g.,
232
441
  # s(:const, nil, :Baz)
233
442
  return unless node&.type == :const
443
+ # @type [Parser::AST::Node, nil]
234
444
  module_node = node.children[0]
445
+ # @type [Parser::AST::Node, nil]
235
446
  class_node = node.children[1]
236
447
 
237
448
  return class_node.to_s if module_node.nil?
@@ -242,14 +453,19 @@ module Solargraph
242
453
  "#{module_type_name}::#{class_node}"
243
454
  end
244
455
 
245
- # @param clause_node [Parser::AST::Node]
246
- def always_breaks?(clause_node)
456
+ # @param clause_node [Parser::AST::Node, nil]
457
+ # @sg-ignore need boolish support for ? methods
458
+ def always_breaks? clause_node
247
459
  clause_node&.type == :break
248
460
  end
249
461
 
250
- attr_reader :locals
462
+ # @param clause_node [Parser::AST::Node, nil]
463
+ def always_leaves_compound_statement? clause_node
464
+ # https://docs.ruby-lang.org/en/2.2.0/keywords_rdoc.html
465
+ %i[return raise next redo retry].include?(clause_node&.type)
466
+ end
251
467
 
252
- attr_reader :enclosing_breakable_pin
468
+ attr_reader :locals, :ivars, :enclosing_breakable_pin, :enclosing_compound_statement_pin
253
469
  end
254
470
  end
255
471
  end
@@ -16,15 +16,20 @@ module Solargraph
16
16
  # @return [Array<Pin::LocalVariable>]
17
17
  attr_reader :locals
18
18
 
19
+ # @return [Array<Pin::InstanceVariable>]
20
+ attr_reader :ivars
21
+
19
22
  # @param node [Parser::AST::Node]
20
23
  # @param region [Region]
21
24
  # @param pins [Array<Pin::Base>]
22
25
  # @param locals [Array<Pin::LocalVariable>]
23
- def initialize node, region, pins, locals
26
+ # @param ivars [Array<Pin::InstanceVariable>]
27
+ def initialize node, region, pins, locals, ivars
24
28
  @node = node
25
29
  @region = region
26
30
  @pins = pins
27
31
  @locals = locals
32
+ @ivars = ivars
28
33
  @processed_children = false
29
34
  end
30
35
 
@@ -40,6 +45,28 @@ module Solargraph
40
45
 
41
46
  private
42
47
 
48
+ # @return [Solargraph::Location]
49
+ def location
50
+ get_node_location(node)
51
+ end
52
+
53
+ # @return [Solargraph::Position]
54
+ def position
55
+ Position.new(node.loc.line, node.loc.column)
56
+ end
57
+
58
+ # @sg-ignore downcast output of Enumerable#select
59
+ # @return [Solargraph::Pin::Breakable, nil]
60
+ def enclosing_breakable_pin
61
+ pins.select { |pin| pin.is_a?(Pin::Breakable) && pin.location&.range&.contain?(position) }.last
62
+ end
63
+
64
+ # @todo downcast output of Enumerable#select
65
+ # @return [Solargraph::Pin::CompoundStatement, nil]
66
+ def enclosing_compound_statement_pin
67
+ pins.select { |pin| pin.is_a?(Pin::CompoundStatement) && pin.location&.range&.contain?(position) }.last
68
+ end
69
+
43
70
  # @param subregion [Region]
44
71
  # @return [void]
45
72
  def process_children subregion = region
@@ -47,20 +74,20 @@ module Solargraph
47
74
  @processed_children = true
48
75
  node.children.each do |child|
49
76
  next unless Parser.is_ast_node?(child)
50
- NodeProcessor.process(child, subregion, pins, locals)
77
+ NodeProcessor.process(child, subregion, pins, locals, ivars)
51
78
  end
52
79
  end
53
80
 
54
81
  # @param node [Parser::AST::Node]
55
82
  # @return [Solargraph::Location]
56
- def get_node_location(node)
83
+ def get_node_location node
57
84
  range = Parser.node_range(node)
58
85
  Location.new(region.filename, range)
59
86
  end
60
87
 
61
88
  # @param node [Parser::AST::Node]
62
89
  # @return [String, nil]
63
- def comments_for(node)
90
+ def comments_for node
64
91
  region.source.comments_for(node)
65
92
  end
66
93
 
@@ -68,6 +95,7 @@ module Solargraph
68
95
  # @return [Pin::Closure, nil]
69
96
  def named_path_pin position
70
97
  pins.select do |pin|
98
+ # @sg-ignore Need to add nil check here
71
99
  pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)
72
100
  end.last
73
101
  end
@@ -77,6 +105,7 @@ module Solargraph
77
105
  # @return [Pin::Closure, nil]
78
106
  def block_pin position
79
107
  # @todo determine if this can return a Pin::Block
108
+ # @sg-ignore Need to add nil check here
80
109
  pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
81
110
  end
82
111
 
@@ -84,6 +113,7 @@ module Solargraph
84
113
  # @param position [Solargraph::Position]
85
114
  # @return [Pin::Closure, nil]
86
115
  def closure_pin position
116
+ # @sg-ignore Need to add nil check here
87
117
  pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
88
118
  end
89
119
  end