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