solargraph 0.54.1 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +167 -0
- data/lib/solargraph/api_map/store.rb +71 -122
- data/lib/solargraph/api_map.rb +97 -36
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +7 -0
- data/lib/solargraph/complex_type/unique_type.rb +109 -8
- data/lib/solargraph/complex_type.rb +48 -18
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +101 -0
- data/lib/solargraph/convention.rb +2 -0
- data/lib/solargraph/doc_map.rb +43 -18
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/language_server/host/message_worker.rb +31 -11
- data/lib/solargraph/language_server/host.rb +13 -11
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/initialize.rb +3 -1
- data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
- data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
- data/lib/solargraph/library.rb +7 -8
- data/lib/solargraph/location.rb +15 -0
- data/lib/solargraph/parser/comment_ripper.rb +11 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
- data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +35 -19
- data/lib/solargraph/pin/base_variable.rb +10 -3
- data/lib/solargraph/pin/block.rb +3 -3
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +3 -3
- data/lib/solargraph/pin/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +26 -18
- data/lib/solargraph/pin/namespace.rb +10 -7
- data/lib/solargraph/pin/parameter.rb +15 -6
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +3 -0
- data/lib/solargraph/position.rb +7 -0
- data/lib/solargraph/range.rb +7 -0
- data/lib/solargraph/rbs_map/conversions.rb +16 -10
- data/lib/solargraph/rbs_map/core_fills.rb +10 -3
- data/lib/solargraph/rbs_map.rb +1 -0
- data/lib/solargraph/shell.rb +2 -0
- data/lib/solargraph/source/chain/array.rb +5 -4
- data/lib/solargraph/source/chain/call.rb +55 -19
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +8 -2
- data/lib/solargraph/source/chain/if.rb +5 -0
- data/lib/solargraph/source/chain/link.rb +19 -5
- data/lib/solargraph/source/chain/literal.rb +27 -2
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +107 -63
- data/lib/solargraph/source/cursor.rb +1 -11
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source.rb +2 -1
- data/lib/solargraph/source_map/clip.rb +4 -2
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +41 -14
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
- data/lib/solargraph/yard_map/mapper.rb +1 -0
- data/lib/solargraph/yardoc.rb +1 -1
- data/lib/solargraph.rb +1 -0
- data/solargraph.gemspec +5 -5
- metadata +39 -25
data/lib/solargraph/library.rb
CHANGED
|
@@ -132,6 +132,7 @@ module Solargraph
|
|
|
132
132
|
result = false
|
|
133
133
|
filenames.each do |filename|
|
|
134
134
|
detach filename
|
|
135
|
+
source_map_hash.delete(filename)
|
|
135
136
|
result ||= workspace.remove(filename)
|
|
136
137
|
end
|
|
137
138
|
result
|
|
@@ -192,11 +193,7 @@ module Solargraph
|
|
|
192
193
|
else
|
|
193
194
|
mutex.synchronize do
|
|
194
195
|
clip = api_map.clip(cursor)
|
|
195
|
-
|
|
196
|
-
[Pin::ProxyType.new(name: cursor.word, return_type: clip.infer)]
|
|
197
|
-
else
|
|
198
|
-
clip.define.map { |pin| pin.realize(api_map) }
|
|
199
|
-
end
|
|
196
|
+
clip.define.map { |pin| pin.realize(api_map) }
|
|
200
197
|
end
|
|
201
198
|
end
|
|
202
199
|
rescue FileNotFoundError => e
|
|
@@ -240,7 +237,7 @@ module Solargraph
|
|
|
240
237
|
# @param column [Integer]
|
|
241
238
|
# @param strip [Boolean] Strip special characters from variable names
|
|
242
239
|
# @param only [Boolean] Search for references in the current file only
|
|
243
|
-
# @return [Array<Solargraph::
|
|
240
|
+
# @return [Array<Solargraph::Location>]
|
|
244
241
|
# @todo Take a Location instead of filename/line/column
|
|
245
242
|
def references_from filename, line, column, strip: false, only: false
|
|
246
243
|
sync_catalog
|
|
@@ -398,6 +395,8 @@ module Solargraph
|
|
|
398
395
|
return [] unless open?(filename)
|
|
399
396
|
result = []
|
|
400
397
|
source = read(filename)
|
|
398
|
+
|
|
399
|
+
# @type [Hash{Class<Solargraph::Diagnostics::Base> => Array<String>}]
|
|
401
400
|
repargs = {}
|
|
402
401
|
workspace.config.reporters.each do |line|
|
|
403
402
|
if line == 'all!'
|
|
@@ -431,7 +430,8 @@ module Solargraph
|
|
|
431
430
|
Bench.new(
|
|
432
431
|
source_maps: source_map_hash.values,
|
|
433
432
|
workspace: workspace,
|
|
434
|
-
external_requires: external_requires
|
|
433
|
+
external_requires: external_requires,
|
|
434
|
+
live_map: @current ? source_map_hash[@current.filename] : nil
|
|
435
435
|
)
|
|
436
436
|
end
|
|
437
437
|
|
|
@@ -650,7 +650,6 @@ module Solargraph
|
|
|
650
650
|
return if @sync_count == 0
|
|
651
651
|
|
|
652
652
|
mutex.synchronize do
|
|
653
|
-
logger.warn "CATALOG"
|
|
654
653
|
logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
|
|
655
654
|
api_map.catalog bench
|
|
656
655
|
source_map_hash.values.each { |map| find_external_requires(map) }
|
data/lib/solargraph/location.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Solargraph
|
|
|
5
5
|
# and Range.
|
|
6
6
|
#
|
|
7
7
|
class Location
|
|
8
|
+
include Equality
|
|
9
|
+
|
|
8
10
|
# @return [String]
|
|
9
11
|
attr_reader :filename
|
|
10
12
|
|
|
@@ -18,11 +20,24 @@ module Solargraph
|
|
|
18
20
|
@range = range
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
24
|
+
protected def equality_fields
|
|
25
|
+
[filename, range]
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
# @param location [self]
|
|
22
29
|
def contain? location
|
|
23
30
|
range.contain?(location.range.start) && range.contain?(location.range.ending) && filename == location.filename
|
|
24
31
|
end
|
|
25
32
|
|
|
33
|
+
def inspect
|
|
34
|
+
"<#{self.class.name}: filename=#{filename}, range=#{range.inspect}>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_s
|
|
38
|
+
inspect
|
|
39
|
+
end
|
|
40
|
+
|
|
26
41
|
# @return [Hash]
|
|
27
42
|
def to_hash
|
|
28
43
|
{
|
|
@@ -13,6 +13,7 @@ module Solargraph
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def on_comment *args
|
|
16
|
+
# @type [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
|
|
16
17
|
result = super
|
|
17
18
|
if @buffer_lines[result[2][0]][0..result[2][1]].strip =~ /^#/
|
|
18
19
|
chomped = result[1].chomp
|
|
@@ -24,24 +25,28 @@ module Solargraph
|
|
|
24
25
|
result
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
# @param result [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
|
|
29
|
+
# @return [void]
|
|
30
|
+
def create_snippet(result)
|
|
31
|
+
chomped = result[1].chomp
|
|
32
|
+
@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)
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
def on_embdoc_beg *args
|
|
28
36
|
result = super
|
|
29
|
-
|
|
30
|
-
@comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
|
|
37
|
+
create_snippet(result)
|
|
31
38
|
result
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
def on_embdoc *args
|
|
35
42
|
result = super
|
|
36
|
-
|
|
37
|
-
@comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
|
|
43
|
+
create_snippet(result)
|
|
38
44
|
result
|
|
39
45
|
end
|
|
40
46
|
|
|
41
47
|
def on_embdoc_end *args
|
|
42
48
|
result = super
|
|
43
|
-
|
|
44
|
-
@comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
|
|
49
|
+
create_snippet(result)
|
|
45
50
|
result
|
|
46
51
|
end
|
|
47
52
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
module Solargraph
|
|
2
|
+
module Parser
|
|
3
|
+
class FlowSensitiveTyping
|
|
4
|
+
include Solargraph::Parser::NodeMethods
|
|
5
|
+
|
|
6
|
+
# @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
7
|
+
def initialize(locals, enclosing_breakable_pin = nil)
|
|
8
|
+
@locals = locals
|
|
9
|
+
@enclosing_breakable_pin = enclosing_breakable_pin
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @param and_node [Parser::AST::Node]
|
|
13
|
+
def process_and(and_node, true_ranges = [])
|
|
14
|
+
lhs = and_node.children[0]
|
|
15
|
+
rhs = and_node.children[1]
|
|
16
|
+
|
|
17
|
+
before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
|
|
18
|
+
before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
|
|
19
|
+
|
|
20
|
+
rhs_presence = Range.new(before_rhs_pos,
|
|
21
|
+
get_node_end_position(rhs))
|
|
22
|
+
process_isa(lhs, true_ranges + [rhs_presence])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param if_node [Parser::AST::Node]
|
|
26
|
+
def process_if(if_node)
|
|
27
|
+
#
|
|
28
|
+
# See if we can refine a type based on the result of 'if foo.nil?'
|
|
29
|
+
#
|
|
30
|
+
# [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
|
|
31
|
+
# => s(:if,
|
|
32
|
+
# s(:send,
|
|
33
|
+
# s(:send, nil, :foo), :is_a?,
|
|
34
|
+
# s(:const, nil, :Baz)),
|
|
35
|
+
# s(:send, nil, :foo),
|
|
36
|
+
# s(:send, nil, :bar))
|
|
37
|
+
# [4] pry(main)>
|
|
38
|
+
conditional_node = if_node.children[0]
|
|
39
|
+
then_clause = if_node.children[1]
|
|
40
|
+
else_clause = if_node.children[2]
|
|
41
|
+
|
|
42
|
+
true_ranges = []
|
|
43
|
+
if always_breaks?(else_clause)
|
|
44
|
+
unless enclosing_breakable_pin.nil?
|
|
45
|
+
rest_of_breakable_body = Range.new(get_node_end_position(if_node),
|
|
46
|
+
get_node_end_position(enclosing_breakable_pin.node))
|
|
47
|
+
true_ranges << rest_of_breakable_body
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
unless then_clause.nil?
|
|
52
|
+
#
|
|
53
|
+
# Add specialized locals for the then clause range
|
|
54
|
+
#
|
|
55
|
+
before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
|
|
56
|
+
before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
|
|
57
|
+
true_ranges << Range.new(before_then_clause_pos,
|
|
58
|
+
get_node_end_position(then_clause))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
process_conditional(conditional_node, true_ranges)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class << self
|
|
65
|
+
include Logging
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Find a variable pin by name and where it is used.
|
|
69
|
+
#
|
|
70
|
+
# Resolves our most specific view of this variable's type by
|
|
71
|
+
# preferring pins created by flow-sensitive typing when we have
|
|
72
|
+
# them based on the Closure and Location.
|
|
73
|
+
#
|
|
74
|
+
# @param pins [Array<Pin::LocalVariable>]
|
|
75
|
+
# @param closure [Pin::Closure]
|
|
76
|
+
# @param location [Location]
|
|
77
|
+
def self.visible_pins(pins, name, closure, location)
|
|
78
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
|
|
79
|
+
pins_with_name = pins.select { |p| p.name == name }
|
|
80
|
+
if pins_with_name.empty?
|
|
81
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
|
|
82
|
+
return []
|
|
83
|
+
end
|
|
84
|
+
pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
|
|
85
|
+
if pins_with_specific_visibility.empty?
|
|
86
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
|
|
87
|
+
return pins_with_name
|
|
88
|
+
end
|
|
89
|
+
visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
|
|
90
|
+
if visible_pins_specific_to_this_closure.empty?
|
|
91
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
|
|
92
|
+
return pins_with_specific_visibility
|
|
93
|
+
end
|
|
94
|
+
flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
|
|
95
|
+
if flow_defined_pins.empty?
|
|
96
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
|
|
97
|
+
return visible_pins_specific_to_this_closure
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
|
|
101
|
+
|
|
102
|
+
flow_defined_pins
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
include Logging
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# @param pin [Pin::LocalVariable]
|
|
110
|
+
# @param if_node [Parser::AST::Node]
|
|
111
|
+
def add_downcast_local(pin, downcast_type_name, presence)
|
|
112
|
+
# @todo Create pin#update method
|
|
113
|
+
new_pin = Solargraph::Pin::LocalVariable.new(
|
|
114
|
+
location: pin.location,
|
|
115
|
+
closure: pin.closure,
|
|
116
|
+
name: pin.name,
|
|
117
|
+
assignment: pin.assignment,
|
|
118
|
+
comments: pin.comments,
|
|
119
|
+
presence: presence,
|
|
120
|
+
return_type: ComplexType.try_parse(downcast_type_name),
|
|
121
|
+
presence_certain: true
|
|
122
|
+
)
|
|
123
|
+
locals.push(new_pin)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
|
|
127
|
+
# @param presences [Array<Range>]
|
|
128
|
+
# @return [void]
|
|
129
|
+
def process_facts(facts_by_pin, presences)
|
|
130
|
+
#
|
|
131
|
+
# Add specialized locals for the rest of the block
|
|
132
|
+
#
|
|
133
|
+
facts_by_pin.each_pair do |pin, facts|
|
|
134
|
+
facts.each do |fact|
|
|
135
|
+
downcast_type_name = fact.fetch(:type)
|
|
136
|
+
presences.each do |presence|
|
|
137
|
+
add_downcast_local(pin, downcast_type_name, presence)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @param conditional_node [Parser::AST::Node]
|
|
144
|
+
def process_conditional(conditional_node, true_ranges)
|
|
145
|
+
if conditional_node.type == :send
|
|
146
|
+
process_isa(conditional_node, true_ranges)
|
|
147
|
+
elsif conditional_node.type == :and
|
|
148
|
+
process_and(conditional_node, true_ranges)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @param isa_node [Parser::AST::Node]
|
|
153
|
+
# @return [Array(String, String)]
|
|
154
|
+
def parse_isa(isa_node)
|
|
155
|
+
return unless isa_node.type == :send && isa_node.children[1] == :is_a?
|
|
156
|
+
# Check if conditional node follows this pattern:
|
|
157
|
+
# s(:send,
|
|
158
|
+
# s(:send, nil, :foo), :is_a?,
|
|
159
|
+
# s(:const, nil, :Baz)),
|
|
160
|
+
isa_receiver = isa_node.children[0]
|
|
161
|
+
isa_type_name = type_name(isa_node.children[2])
|
|
162
|
+
return unless isa_type_name
|
|
163
|
+
|
|
164
|
+
# check if isa_receiver looks like this:
|
|
165
|
+
# s(:send, nil, :foo)
|
|
166
|
+
# and set variable_name to :foo
|
|
167
|
+
if isa_receiver.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
|
|
168
|
+
variable_name = isa_receiver.children[1].to_s
|
|
169
|
+
end
|
|
170
|
+
# or like this:
|
|
171
|
+
# (lvar :repr)
|
|
172
|
+
variable_name = isa_receiver.children[0].to_s if isa_receiver.type == :lvar
|
|
173
|
+
return unless variable_name
|
|
174
|
+
|
|
175
|
+
[isa_type_name, variable_name]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def find_local(variable_name, position)
|
|
179
|
+
pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
|
|
180
|
+
return unless pins.length == 1
|
|
181
|
+
pins.first
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def process_isa(isa_node, true_presences)
|
|
185
|
+
isa_type_name, variable_name = parse_isa(isa_node)
|
|
186
|
+
return if variable_name.nil? || variable_name.empty?
|
|
187
|
+
isa_position = Range.from_node(isa_node).start
|
|
188
|
+
|
|
189
|
+
pin = find_local(variable_name, isa_position)
|
|
190
|
+
return unless pin
|
|
191
|
+
|
|
192
|
+
if_true = {}
|
|
193
|
+
if_true[pin] ||= []
|
|
194
|
+
if_true[pin] << { type: isa_type_name }
|
|
195
|
+
process_facts(if_true, true_presences)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @param node [Parser::AST::Node]
|
|
199
|
+
def type_name(node)
|
|
200
|
+
# e.g.,
|
|
201
|
+
# s(:const, nil, :Baz)
|
|
202
|
+
return unless node.type == :const
|
|
203
|
+
module_node = node.children[0]
|
|
204
|
+
class_node = node.children[1]
|
|
205
|
+
|
|
206
|
+
return class_node.to_s if module_node.nil?
|
|
207
|
+
|
|
208
|
+
module_type_name = type_name(module_node)
|
|
209
|
+
return unless module_type_name
|
|
210
|
+
|
|
211
|
+
"#{module_type_name}::#{class_node}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @todo "return type could not be inferred" should not trigger here
|
|
215
|
+
# @sg-ignore
|
|
216
|
+
# @param clause_node [Parser::AST::Node]
|
|
217
|
+
def always_breaks?(clause_node)
|
|
218
|
+
clause_node&.type == :break
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
attr_reader :locals
|
|
222
|
+
|
|
223
|
+
attr_reader :enclosing_breakable_pin
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -74,10 +74,24 @@ module Solargraph
|
|
|
74
74
|
|
|
75
75
|
# @abstract
|
|
76
76
|
# @param node [Parser::AST::Node]
|
|
77
|
-
# @return [Hash{Parser::AST::Node => Chain}]
|
|
77
|
+
# @return [Hash{Parser::AST::Node => Source::Chain}]
|
|
78
78
|
def convert_hash node
|
|
79
79
|
raise NotImplementedError
|
|
80
80
|
end
|
|
81
|
+
|
|
82
|
+
# @abstract
|
|
83
|
+
# @param node [Parser::AST::Node]
|
|
84
|
+
# @return [Position]
|
|
85
|
+
def get_node_start_position(node)
|
|
86
|
+
raise NotImplementedError
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @abstract
|
|
90
|
+
# @param node [Parser::AST::Node]
|
|
91
|
+
# @return [Position]
|
|
92
|
+
def get_node_end_position(node)
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
end
|
|
81
95
|
end
|
|
82
96
|
end
|
|
83
97
|
end
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
require 'parser/current'
|
|
4
4
|
require 'parser/source/buffer'
|
|
5
5
|
|
|
6
|
+
# Awaiting ability to use a version containing https://github.com/whitequark/parser/pull/1076
|
|
7
|
+
#
|
|
8
|
+
# @!parse
|
|
9
|
+
# class ::Parser::Base < ::Parser::Builder
|
|
10
|
+
# # @return [Integer]
|
|
11
|
+
# def version; end
|
|
12
|
+
# end
|
|
13
|
+
# class ::Parser::CurrentRuby < ::Parser::Base; end
|
|
14
|
+
|
|
6
15
|
module Solargraph
|
|
7
16
|
module Parser
|
|
8
17
|
module ParserGem
|
|
@@ -11,13 +20,9 @@ module Solargraph
|
|
|
11
20
|
# @param filename [String, nil]
|
|
12
21
|
# @return [Array(Parser::AST::Node, Hash{Integer => String})]
|
|
13
22
|
def parse_with_comments code, filename = nil
|
|
14
|
-
|
|
15
|
-
buffer.source = code
|
|
16
|
-
node = parser.parse(buffer)
|
|
23
|
+
node = parse(code, filename)
|
|
17
24
|
comments = CommentRipper.new(code, filename, 0).parse
|
|
18
25
|
[node, comments]
|
|
19
|
-
rescue ::Parser::SyntaxError => e
|
|
20
|
-
raise Parser::SyntaxError, e.message
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
# @param code [String]
|
|
@@ -28,7 +33,7 @@ module Solargraph
|
|
|
28
33
|
buffer = ::Parser::Source::Buffer.new(filename, line)
|
|
29
34
|
buffer.source = code
|
|
30
35
|
parser.parse(buffer)
|
|
31
|
-
rescue ::Parser::SyntaxError => e
|
|
36
|
+
rescue ::Parser::SyntaxError, ::Parser::UnknownEncodingInMagicComment => e
|
|
32
37
|
raise Parser::SyntaxError, e.message
|
|
33
38
|
end
|
|
34
39
|
|
|
@@ -57,22 +57,22 @@ module Solargraph
|
|
|
57
57
|
elsif n.type == :send
|
|
58
58
|
if n.children[0].is_a?(::Parser::AST::Node)
|
|
59
59
|
result.concat generate_links(n.children[0])
|
|
60
|
-
result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
|
|
60
|
+
result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
|
|
61
61
|
elsif n.children[0].nil?
|
|
62
62
|
args = []
|
|
63
63
|
n.children[2..-1].each do |c|
|
|
64
64
|
args.push NodeChainer.chain(c, @filename, n)
|
|
65
65
|
end
|
|
66
|
-
result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
|
|
66
|
+
result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
|
|
67
67
|
else
|
|
68
68
|
raise "No idea what to do with #{n}"
|
|
69
69
|
end
|
|
70
70
|
elsif n.type == :csend
|
|
71
71
|
if n.children[0].is_a?(::Parser::AST::Node)
|
|
72
72
|
result.concat generate_links(n.children[0])
|
|
73
|
-
result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
|
|
73
|
+
result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
|
|
74
74
|
elsif n.children[0].nil?
|
|
75
|
-
result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
|
|
75
|
+
result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
|
|
76
76
|
else
|
|
77
77
|
raise "No idea what to do with #{n}"
|
|
78
78
|
end
|
|
@@ -82,28 +82,22 @@ module Solargraph
|
|
|
82
82
|
result.push Chain::ZSuper.new('super')
|
|
83
83
|
elsif n.type == :super
|
|
84
84
|
args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
|
|
85
|
-
result.push Chain::Call.new('super', args)
|
|
85
|
+
result.push Chain::Call.new('super', Location.from_node(n), args)
|
|
86
86
|
elsif n.type == :yield
|
|
87
87
|
args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
|
|
88
|
-
result.push Chain::Call.new('yield', args)
|
|
88
|
+
result.push Chain::Call.new('yield', Location.from_node(n), args)
|
|
89
89
|
elsif n.type == :const
|
|
90
90
|
const = unpack_name(n)
|
|
91
91
|
result.push Chain::Constant.new(const)
|
|
92
|
-
elsif [:
|
|
93
|
-
result.
|
|
94
|
-
elsif n.type
|
|
95
|
-
result.push Chain::
|
|
96
|
-
elsif n.type
|
|
97
|
-
|
|
98
|
-
elsif n.type
|
|
99
|
-
|
|
100
|
-
elsif n.type == :gvar
|
|
101
|
-
result.push Chain::GlobalVariable.new(n.children[0].to_s)
|
|
92
|
+
elsif [:lvar, :lvasgn].include?(n.type)
|
|
93
|
+
result.push Chain::Call.new(n.children[0].to_s, Location.from_node(n))
|
|
94
|
+
elsif [:ivar, :ivasgn].include?(n.type)
|
|
95
|
+
result.push Chain::InstanceVariable.new(n.children[0].to_s)
|
|
96
|
+
elsif [:cvar, :cvasgn].include?(n.type)
|
|
97
|
+
result.push Chain::ClassVariable.new(n.children[0].to_s)
|
|
98
|
+
elsif [:gvar, :gvasgn].include?(n.type)
|
|
99
|
+
result.push Chain::GlobalVariable.new(n.children[0].to_s)
|
|
102
100
|
elsif n.type == :or_asgn
|
|
103
|
-
# @todo: Need a new Link class here that evaluates the
|
|
104
|
-
# existing variable type with the RHS, and generates a
|
|
105
|
-
# union type of the LHS alone if never nil, or minus nil +
|
|
106
|
-
# RHS if it is nilable.
|
|
107
101
|
result.concat generate_links n.children[1]
|
|
108
102
|
elsif [:class, :module, :def, :defs].include?(n.type)
|
|
109
103
|
# @todo Undefined or what?
|
|
@@ -130,13 +124,13 @@ module Solargraph
|
|
|
130
124
|
end
|
|
131
125
|
end
|
|
132
126
|
elsif n.type == :hash
|
|
133
|
-
result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
|
|
127
|
+
result.push Chain::Hash.new('::Hash', n, hash_is_splatted?(n))
|
|
134
128
|
elsif n.type == :array
|
|
135
129
|
chained_children = n.children.map { |c| NodeChainer.chain(c) }
|
|
136
|
-
result.push Source::Chain::Array.new(chained_children)
|
|
130
|
+
result.push Source::Chain::Array.new(chained_children, n)
|
|
137
131
|
else
|
|
138
132
|
lit = infer_literal_node_type(n)
|
|
139
|
-
result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
|
|
133
|
+
result.push (lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
|
|
140
134
|
end
|
|
141
135
|
result
|
|
142
136
|
end
|
|
@@ -12,7 +12,7 @@ require 'ast'
|
|
|
12
12
|
# class Node
|
|
13
13
|
# # New children
|
|
14
14
|
#
|
|
15
|
-
# # @return [Array<
|
|
15
|
+
# # @return [Array<self>]
|
|
16
16
|
# attr_reader :children
|
|
17
17
|
# end
|
|
18
18
|
# end
|
|
@@ -40,7 +40,7 @@ module Solargraph
|
|
|
40
40
|
if n.is_a?(AST::Node)
|
|
41
41
|
if n.type == :cbase
|
|
42
42
|
parts = [''] + pack_name(n)
|
|
43
|
-
|
|
43
|
+
elsif n.type == :const
|
|
44
44
|
parts += pack_name(n)
|
|
45
45
|
end
|
|
46
46
|
else
|
|
@@ -59,6 +59,8 @@ module Solargraph
|
|
|
59
59
|
return '::String'
|
|
60
60
|
elsif node.type == :array
|
|
61
61
|
return '::Array'
|
|
62
|
+
elsif node.type == :nil
|
|
63
|
+
return '::NilClass'
|
|
62
64
|
elsif node.type == :hash
|
|
63
65
|
return '::Hash'
|
|
64
66
|
elsif node.type == :int
|
|
@@ -462,7 +464,7 @@ module Solargraph
|
|
|
462
464
|
result
|
|
463
465
|
end
|
|
464
466
|
|
|
465
|
-
# @param nodes [Enumerable<Parser::AST::Node,
|
|
467
|
+
# @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
|
|
466
468
|
# @return [Array<Parser::AST::Node, nil>]
|
|
467
469
|
def reduce_to_value_nodes nodes
|
|
468
470
|
result = []
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
class AndNode < Parser::NodeProcessor::Base
|
|
8
|
+
include ParserGem::NodeMethods
|
|
9
|
+
|
|
10
|
+
def process
|
|
11
|
+
process_children
|
|
12
|
+
|
|
13
|
+
position = get_node_start_position(node)
|
|
14
|
+
enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last
|
|
15
|
+
FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_and(node)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -8,6 +8,17 @@ module Solargraph
|
|
|
8
8
|
include ParserGem::NodeMethods
|
|
9
9
|
|
|
10
10
|
def process
|
|
11
|
+
if Convention::StructDefinition::StructAssignmentNode.valid?(node)
|
|
12
|
+
process_struct_assignment
|
|
13
|
+
else
|
|
14
|
+
process_constant_assignment
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# @return [void]
|
|
21
|
+
def process_constant_assignment
|
|
11
22
|
pins.push Solargraph::Pin::Constant.new(
|
|
12
23
|
location: get_node_location(node),
|
|
13
24
|
closure: region.closure,
|
|
@@ -18,7 +29,16 @@ module Solargraph
|
|
|
18
29
|
process_children
|
|
19
30
|
end
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
# TODO: Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports
|
|
33
|
+
# multiple processors.
|
|
34
|
+
def process_struct_assignment
|
|
35
|
+
processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
|
|
36
|
+
processor = processor_klass.new(node, region, pins, locals)
|
|
37
|
+
processor.process
|
|
38
|
+
|
|
39
|
+
@pins = processor.pins
|
|
40
|
+
@locals = processor.locals
|
|
41
|
+
end
|
|
22
42
|
|
|
23
43
|
# @return [String]
|
|
24
44
|
def const_name
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
class IfNode < Parser::NodeProcessor::Base
|
|
8
|
+
include ParserGem::NodeMethods
|
|
9
|
+
|
|
10
|
+
def process
|
|
11
|
+
process_children
|
|
12
|
+
|
|
13
|
+
position = get_node_start_position(node)
|
|
14
|
+
enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last
|
|
15
|
+
FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_if(node)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -8,8 +8,8 @@ module Solargraph
|
|
|
8
8
|
include ParserGem::NodeMethods
|
|
9
9
|
|
|
10
10
|
def process
|
|
11
|
-
|
|
12
|
-
presence = Range.new(
|
|
11
|
+
here = get_node_start_position(node)
|
|
12
|
+
presence = Range.new(here, region.closure.location.range.ending)
|
|
13
13
|
loc = get_node_location(node)
|
|
14
14
|
locals.push Solargraph::Pin::LocalVariable.new(
|
|
15
15
|
location: loc,
|