solargraph 0.58.2 → 0.59.0.dev.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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +41 -34
  5. data/.github/workflows/rspec.yml +44 -23
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.rubocop.yml +32 -5
  8. data/.rubocop_todo.yml +50 -966
  9. data/Gemfile +3 -1
  10. data/README.md +3 -3
  11. data/Rakefile +26 -23
  12. data/bin/solargraph +2 -1
  13. data/lib/solargraph/api_map/cache.rb +3 -3
  14. data/lib/solargraph/api_map/constants.rb +13 -3
  15. data/lib/solargraph/api_map/index.rb +23 -18
  16. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  17. data/lib/solargraph/api_map/store.rb +33 -28
  18. data/lib/solargraph/api_map.rb +150 -82
  19. data/lib/solargraph/bench.rb +44 -45
  20. data/lib/solargraph/complex_type/conformance.rb +176 -0
  21. data/lib/solargraph/complex_type/type_methods.rb +28 -17
  22. data/lib/solargraph/complex_type/unique_type.rb +218 -57
  23. data/lib/solargraph/complex_type.rb +170 -57
  24. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  25. data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
  26. data/lib/solargraph/convention/data_definition.rb +5 -2
  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 +2 -1
  31. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
  32. data/lib/solargraph/convention/struct_definition.rb +8 -4
  33. data/lib/solargraph/convention.rb +78 -78
  34. data/lib/solargraph/converters/dd.rb +19 -17
  35. data/lib/solargraph/converters/dl.rb +17 -15
  36. data/lib/solargraph/converters/dt.rb +17 -15
  37. data/lib/solargraph/converters/misc.rb +3 -1
  38. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  39. data/lib/solargraph/diagnostics/rubocop.rb +11 -10
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  41. data/lib/solargraph/diagnostics/type_check.rb +11 -10
  42. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  43. data/lib/solargraph/doc_map.rb +133 -373
  44. data/lib/solargraph/equality.rb +4 -4
  45. data/lib/solargraph/gem_pins.rb +21 -20
  46. data/lib/solargraph/language_server/error_codes.rb +20 -20
  47. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  48. data/lib/solargraph/language_server/host/dispatch.rb +3 -3
  49. data/lib/solargraph/language_server/host/message_worker.rb +4 -3
  50. data/lib/solargraph/language_server/host/sources.rb +2 -1
  51. data/lib/solargraph/language_server/host.rb +30 -22
  52. data/lib/solargraph/language_server/message/base.rb +97 -97
  53. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  54. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  55. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
  56. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  57. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  58. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  59. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  60. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  61. data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
  62. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
  63. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  64. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
  65. data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
  66. data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
  67. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  68. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  69. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  70. data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
  71. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
  72. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  73. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  74. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  75. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
  76. data/lib/solargraph/language_server/message.rb +94 -94
  77. data/lib/solargraph/language_server/request.rb +29 -27
  78. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  79. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  80. data/lib/solargraph/library.rb +85 -44
  81. data/lib/solargraph/location.rb +17 -14
  82. data/lib/solargraph/logging.rb +24 -4
  83. data/lib/solargraph/page.rb +92 -92
  84. data/lib/solargraph/parser/comment_ripper.rb +19 -4
  85. data/lib/solargraph/parser/flow_sensitive_typing.rb +326 -108
  86. data/lib/solargraph/parser/node_processor/base.rb +34 -4
  87. data/lib/solargraph/parser/node_processor.rb +8 -7
  88. data/lib/solargraph/parser/parser_gem/class_methods.rb +32 -14
  89. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  90. data/lib/solargraph/parser/parser_gem/node_chainer.rb +50 -25
  91. data/lib/solargraph/parser/parser_gem/node_methods.rb +91 -70
  92. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  93. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
  94. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  95. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +12 -12
  96. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
  97. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  98. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  99. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
  100. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  101. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  102. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
  103. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  104. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  105. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  106. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
  107. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
  108. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  109. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  110. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
  111. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  112. data/lib/solargraph/parser/parser_gem.rb +14 -12
  113. data/lib/solargraph/parser/region.rb +9 -3
  114. data/lib/solargraph/parser/snippet.rb +3 -1
  115. data/lib/solargraph/parser.rb +25 -23
  116. data/lib/solargraph/pin/base.rb +126 -80
  117. data/lib/solargraph/pin/base_variable.rb +273 -24
  118. data/lib/solargraph/pin/block.rb +29 -6
  119. data/lib/solargraph/pin/breakable.rb +7 -1
  120. data/lib/solargraph/pin/callable.rb +65 -21
  121. data/lib/solargraph/pin/closure.rb +7 -10
  122. data/lib/solargraph/pin/common.rb +24 -6
  123. data/lib/solargraph/pin/compound_statement.rb +55 -0
  124. data/lib/solargraph/pin/constant.rb +43 -45
  125. data/lib/solargraph/pin/conversions.rb +10 -4
  126. data/lib/solargraph/pin/delegated_method.rb +19 -8
  127. data/lib/solargraph/pin/documenting.rb +4 -2
  128. data/lib/solargraph/pin/instance_variable.rb +5 -1
  129. data/lib/solargraph/pin/keyword.rb +0 -4
  130. data/lib/solargraph/pin/local_variable.rb +15 -59
  131. data/lib/solargraph/pin/method.rb +153 -104
  132. data/lib/solargraph/pin/method_alias.rb +8 -0
  133. data/lib/solargraph/pin/namespace.rb +19 -12
  134. data/lib/solargraph/pin/parameter.rb +100 -36
  135. data/lib/solargraph/pin/proxy_type.rb +4 -1
  136. data/lib/solargraph/pin/reference/override.rb +1 -1
  137. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  138. data/lib/solargraph/pin/reference.rb +19 -0
  139. data/lib/solargraph/pin/search.rb +3 -2
  140. data/lib/solargraph/pin/signature.rb +15 -12
  141. data/lib/solargraph/pin/symbol.rb +2 -1
  142. data/lib/solargraph/pin/until.rb +2 -4
  143. data/lib/solargraph/pin/while.rb +2 -4
  144. data/lib/solargraph/pin.rb +2 -0
  145. data/lib/solargraph/pin_cache.rb +490 -73
  146. data/lib/solargraph/position.rb +14 -10
  147. data/lib/solargraph/range.rb +16 -15
  148. data/lib/solargraph/rbs_map/conversions.rb +343 -214
  149. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  150. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  151. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
  152. data/lib/solargraph/rbs_map.rb +77 -32
  153. data/lib/solargraph/server_methods.rb +16 -16
  154. data/lib/solargraph/shell.rb +128 -73
  155. data/lib/solargraph/source/chain/array.rb +39 -37
  156. data/lib/solargraph/source/chain/call.rb +96 -56
  157. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  158. data/lib/solargraph/source/chain/constant.rb +5 -1
  159. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  160. data/lib/solargraph/source/chain/hash.rb +8 -5
  161. data/lib/solargraph/source/chain/if.rb +12 -10
  162. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  163. data/lib/solargraph/source/chain/link.rb +99 -109
  164. data/lib/solargraph/source/chain/literal.rb +9 -6
  165. data/lib/solargraph/source/chain/or.rb +10 -4
  166. data/lib/solargraph/source/chain/q_call.rb +13 -11
  167. data/lib/solargraph/source/chain/variable.rb +15 -13
  168. data/lib/solargraph/source/chain/z_super.rb +28 -30
  169. data/lib/solargraph/source/chain.rb +49 -38
  170. data/lib/solargraph/source/change.rb +12 -5
  171. data/lib/solargraph/source/cursor.rb +23 -17
  172. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  173. data/lib/solargraph/source/source_chainer.rb +56 -32
  174. data/lib/solargraph/source/updater.rb +5 -1
  175. data/lib/solargraph/source.rb +59 -35
  176. data/lib/solargraph/source_map/clip.rb +48 -29
  177. data/lib/solargraph/source_map/data.rb +4 -1
  178. data/lib/solargraph/source_map/mapper.rb +71 -42
  179. data/lib/solargraph/source_map.rb +21 -9
  180. data/lib/solargraph/type_checker/problem.rb +3 -1
  181. data/lib/solargraph/type_checker/rules.rb +81 -8
  182. data/lib/solargraph/type_checker.rb +195 -120
  183. data/lib/solargraph/version.rb +1 -1
  184. data/lib/solargraph/workspace/config.rb +13 -10
  185. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  186. data/lib/solargraph/workspace/require_paths.rb +1 -0
  187. data/lib/solargraph/workspace.rb +149 -30
  188. data/lib/solargraph/yard_map/helpers.rb +8 -3
  189. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  190. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  191. data/lib/solargraph/yard_map/mapper.rb +13 -8
  192. data/lib/solargraph/yard_tags.rb +20 -20
  193. data/lib/solargraph/yardoc.rb +33 -23
  194. data/lib/solargraph.rb +29 -8
  195. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  196. data/rbs/fills/tuple/tuple.rbs +28 -0
  197. data/rbs/shims/ast/0/node.rbs +1 -1
  198. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  199. data/solargraph.gemspec +36 -34
  200. metadata +38 -33
  201. data/lib/solargraph/type_checker/checks.rb +0 -124
  202. data/lib/solargraph/type_checker/param_def.rb +0 -37
  203. data/lib/solargraph/yard_map/to_method.rb +0 -89
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ripper'
2
4
 
3
5
  module Solargraph
@@ -23,37 +25,50 @@ module Solargraph
23
25
  # @sg-ignore
24
26
  # @type [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
25
27
  result = super
28
+ # @sg-ignore Need to add nil check here
26
29
  if @buffer_lines[result[2][0]][0..result[2][1]].strip =~ /^#/
27
30
  chomped = result[1].chomp
28
- if result[2][0] == 0 && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '').match(/^#\s*frozen_string_literal:/)
31
+ if result[2][0].zero? && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace,
32
+ replace: '').match(/^#\s*frozen_string_literal:/)
29
33
  chomped = '#'
30
34
  end
31
- @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
35
+ @comments[result[2][0]] =
36
+ Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
32
37
  end
33
38
  result
34
39
  end
35
40
 
36
41
  # @param result [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
37
42
  # @return [void]
38
- def create_snippet(result)
43
+ def create_snippet result
39
44
  chomped = result[1].chomp
40
- @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0] || 0, result[2][1] || 0, result[2][0] || 0, (result[2][1] || 0) + chomped.length), chomped)
45
+ @comments[result[2][0]] =
46
+ Snippet.new(
47
+ Range.from_to(result[2][0] || 0, result[2][1] || 0, result[2][0] || 0,
48
+ (result[2][1] || 0) + chomped.length), chomped
49
+ )
41
50
  end
42
51
 
52
+ # @sg-ignore @override is adding, not overriding
43
53
  def on_embdoc_beg *args
44
54
  result = super
55
+ # @sg-ignore @override is adding, not overriding
45
56
  create_snippet(result)
46
57
  result
47
58
  end
48
59
 
60
+ # @sg-ignore @override is adding, not overriding
49
61
  def on_embdoc *args
50
62
  result = super
63
+ # @sg-ignore @override is adding, not overriding
51
64
  create_snippet(result)
52
65
  result
53
66
  end
54
67
 
68
+ # @sg-ignore @override is adding, not overriding
55
69
  def on_embdoc_end *args
56
70
  result = super
71
+ # @sg-ignore @override is adding, not overriding
57
72
  create_snippet(result)
58
73
  result
59
74
  end
@@ -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,308 @@ 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
+ # @sg-ignore Need to look at Tuple#include? handling
279
+ variable_name = call_receiver.children[0].to_s if %i[lvar ivar].include?(call_receiver&.type)
194
280
  return unless variable_name
195
281
 
196
- [isa_type_name, variable_name]
282
+ [call_arg, variable_name]
283
+ end
284
+
285
+ # @param isa_node [Parser::AST::Node]
286
+ # @return [Array(String, String), nil]
287
+ def parse_isa isa_node
288
+ call_type_name, variable_name = parse_call(isa_node, :is_a?)
289
+
290
+ return unless call_type_name
291
+
292
+ [call_type_name, variable_name]
197
293
  end
198
294
 
199
295
  # @param variable_name [String]
200
296
  # @param position [Position]
201
297
  #
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
298
+ # @sg-ignore Solargraph::Parser::FlowSensitiveTyping#find_var
299
+ # return type could not be inferred
300
+ # @return [Solargraph::Pin::LocalVariable, Solargraph::Pin::InstanceVariable, nil]
301
+ def find_var variable_name, position
302
+ if variable_name.start_with?('@')
303
+ # @sg-ignore flow sensitive typing needs to handle attrs
304
+ ivars.find { |ivar| ivar.name == variable_name && (!ivar.presence || ivar.presence.include?(position)) }
305
+ else
306
+ # @sg-ignore flow sensitive typing needs to handle attrs
307
+ locals.find { |pin| pin.name == variable_name && (!pin.presence || pin.presence.include?(position)) }
308
+ end
207
309
  end
208
310
 
209
311
  # @param isa_node [Parser::AST::Node]
210
312
  # @param true_presences [Array<Range>]
313
+ # @param false_presences [Array<Range>]
211
314
  #
212
315
  # @return [void]
213
- def process_isa(isa_node, true_presences)
316
+ def process_isa isa_node, true_presences, false_presences
214
317
  isa_type_name, variable_name = parse_isa(isa_node)
215
318
  return if variable_name.nil? || variable_name.empty?
319
+ # @sg-ignore Need to add nil check here
216
320
  isa_position = Range.from_node(isa_node).start
217
321
 
218
- pin = find_local(variable_name, isa_position)
322
+ pin = find_var(variable_name, isa_position)
323
+ return unless pin
324
+
325
+ # @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
326
+ if_true = {}
327
+ if_true[pin] ||= []
328
+ if_true[pin] << { type: ComplexType.parse(isa_type_name) }
329
+ process_facts(if_true, true_presences)
330
+
331
+ # @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
332
+ if_false = {}
333
+ if_false[pin] ||= []
334
+ if_false[pin] << { not_type: ComplexType.parse(isa_type_name) }
335
+ process_facts(if_false, false_presences)
336
+ end
337
+
338
+ # @param nilp_node [Parser::AST::Node]
339
+ # @return [Array(String, String), nil]
340
+ def parse_nilp nilp_node
341
+ parse_call(nilp_node, :nil?)
342
+ end
343
+
344
+ # @param nilp_node [Parser::AST::Node]
345
+ # @param true_presences [Array<Range>]
346
+ # @param false_presences [Array<Range>]
347
+ #
348
+ # @return [void]
349
+ def process_nilp nilp_node, true_presences, false_presences
350
+ nilp_arg, variable_name = parse_nilp(nilp_node)
351
+ return if variable_name.nil? || variable_name.empty?
352
+ # if .nil? got an argument, move on, this isn't the situation
353
+ # we're looking for and typechecking will cover any invalid
354
+ # ones
355
+ return unless nilp_arg.nil?
356
+ # @sg-ignore Need to add nil check here
357
+ nilp_position = Range.from_node(nilp_node).start
358
+
359
+ pin = find_var(variable_name, nilp_position)
219
360
  return unless pin
220
361
 
362
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
221
363
  if_true = {}
222
364
  if_true[pin] ||= []
223
- if_true[pin] << { type: isa_type_name }
365
+ if_true[pin] << { type: ComplexType::NIL }
224
366
  process_facts(if_true, true_presences)
367
+
368
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
369
+ if_false = {}
370
+ if_false[pin] ||= []
371
+ if_false[pin] << { not_type: ComplexType::NIL }
372
+ process_facts(if_false, false_presences)
373
+ end
374
+
375
+ # @param bang_node [Parser::AST::Node]
376
+ # @return [Array(String, String), nil]
377
+ def parse_bang bang_node
378
+ parse_call(bang_node, :!)
379
+ end
380
+
381
+ # @param bang_node [Parser::AST::Node]
382
+ # @param true_presences [Array<Range>]
383
+ # @param false_presences [Array<Range>]
384
+ #
385
+ # @return [void]
386
+ def process_bang bang_node, true_presences, false_presences
387
+ # pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("!2")
388
+ # => s(:send,
389
+ # s(:int, 2), :!)
390
+ # end
391
+ return unless bang_node.type == :send && bang_node.children[1] == :!
392
+
393
+ receiver = bang_node.children[0]
394
+
395
+ # swap the two presences
396
+ process_expression(receiver, false_presences, true_presences)
397
+ end
398
+
399
+ # @param var_node [Parser::AST::Node]
400
+ #
401
+ # @return [String, nil] Variable name referenced
402
+ def parse_variable var_node
403
+ return if var_node.children.length != 1
404
+
405
+ var_node.children[0]&.to_s
406
+ end
407
+
408
+ # @return [void]
409
+ # @param node [Parser::AST::Node]
410
+ # @param true_presences [Array<Range>]
411
+ # @param false_presences [Array<Range>]
412
+ def process_variable node, true_presences, false_presences
413
+ return unless %i[lvar ivar cvar gvar].include?(node.type)
414
+
415
+ variable_name = parse_variable(node)
416
+ return if variable_name.nil?
417
+
418
+ # @sg-ignore Need to add nil check here
419
+ var_position = Range.from_node(node).start
420
+
421
+ pin = find_var(variable_name, var_position)
422
+ return unless pin
423
+
424
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
425
+ if_true = {}
426
+ if_true[pin] ||= []
427
+ if_true[pin] << { not_type: ComplexType::NIL }
428
+ process_facts(if_true, true_presences)
429
+
430
+ # @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
431
+ if_false = {}
432
+ if_false[pin] ||= []
433
+ if_false[pin] << { type: ComplexType.parse('nil, false') }
434
+ process_facts(if_false, false_presences)
225
435
  end
226
436
 
227
437
  # @param node [Parser::AST::Node]
228
438
  #
229
439
  # @return [String, nil]
230
- def type_name(node)
440
+ def type_name node
231
441
  # e.g.,
232
442
  # s(:const, nil, :Baz)
233
443
  return unless node&.type == :const
444
+ # @type [Parser::AST::Node, nil]
234
445
  module_node = node.children[0]
446
+ # @type [Parser::AST::Node, nil]
235
447
  class_node = node.children[1]
236
448
 
237
449
  return class_node.to_s if module_node.nil?
@@ -242,14 +454,20 @@ module Solargraph
242
454
  "#{module_type_name}::#{class_node}"
243
455
  end
244
456
 
245
- # @param clause_node [Parser::AST::Node]
246
- def always_breaks?(clause_node)
457
+ # @param clause_node [Parser::AST::Node, nil]
458
+ # @sg-ignore need boolish support for ? methods
459
+ def always_breaks? clause_node
247
460
  clause_node&.type == :break
248
461
  end
249
462
 
250
- attr_reader :locals
463
+ # @param clause_node [Parser::AST::Node, nil]
464
+ def always_leaves_compound_statement? clause_node
465
+ # https://docs.ruby-lang.org/en/2.2.0/keywords_rdoc.html
466
+ # @sg-ignore Need to look at Tuple#include? handling
467
+ %i[return raise next redo retry].include?(clause_node&.type)
468
+ end
251
469
 
252
- attr_reader :enclosing_breakable_pin
470
+ attr_reader :locals, :ivars, :enclosing_breakable_pin, :enclosing_compound_statement_pin
253
471
  end
254
472
  end
255
473
  end