solargraph 0.53.0 → 0.54.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 +45 -0
- data/lib/solargraph/api_map/cache.rb +2 -12
- data/lib/solargraph/api_map/source_to_yard.rb +17 -10
- data/lib/solargraph/api_map/store.rb +11 -6
- data/lib/solargraph/api_map.rb +80 -38
- data/lib/solargraph/bench.rb +1 -0
- data/lib/solargraph/complex_type/type_methods.rb +62 -30
- data/lib/solargraph/complex_type/unique_type.rb +149 -75
- data/lib/solargraph/complex_type.rb +41 -25
- data/lib/solargraph/doc_map.rb +65 -24
- data/lib/solargraph/gem_pins.rb +10 -2
- data/lib/solargraph/language_server/host/dispatch.rb +13 -2
- data/lib/solargraph/language_server/host/sources.rb +1 -61
- data/lib/solargraph/language_server/host.rb +41 -69
- data/lib/solargraph/language_server/message/base.rb +1 -1
- data/lib/solargraph/language_server/message/initialize.rb +14 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
- data/lib/solargraph/language_server/progress.rb +118 -0
- data/lib/solargraph/language_server.rb +1 -0
- data/lib/solargraph/library.rb +149 -97
- data/lib/solargraph/page.rb +6 -0
- data/lib/solargraph/parser/node_processor/base.rb +3 -2
- data/lib/solargraph/parser/node_processor.rb +1 -0
- data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
- data/lib/solargraph/parser/parser_gem/node_methods.rb +0 -4
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -19
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
- data/lib/solargraph/pin/base_variable.rb +35 -6
- data/lib/solargraph/pin/block.rb +70 -32
- data/lib/solargraph/pin/delegated_method.rb +5 -1
- data/lib/solargraph/pin/documenting.rb +2 -0
- data/lib/solargraph/pin/method.rb +4 -2
- data/lib/solargraph/pin/parameter.rb +5 -28
- data/lib/solargraph/rbs_map/conversions.rb +16 -47
- data/lib/solargraph/rbs_map/core_fills.rb +12 -6
- data/lib/solargraph/rbs_map/core_map.rb +2 -13
- data/lib/solargraph/rbs_map.rb +17 -7
- data/lib/solargraph/shell.rb +18 -13
- data/lib/solargraph/source/chain.rb +20 -0
- data/lib/solargraph/source/updater.rb +1 -0
- data/lib/solargraph/source.rb +0 -44
- data/lib/solargraph/source_map/clip.rb +3 -2
- data/lib/solargraph/source_map/mapper.rb +3 -2
- data/lib/solargraph/source_map.rb +8 -6
- data/lib/solargraph/type_checker.rb +58 -13
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +2 -1
- data/lib/solargraph/workspace.rb +27 -1
- data/lib/solargraph/yard_map/mapper/to_method.rb +5 -2
- metadata +4 -4
- data/lib/solargraph/language_server/host/cataloger.rb +0 -57
- data/lib/solargraph/rbs_map/core_signs.rb +0 -35
|
@@ -17,6 +17,7 @@ module Solargraph
|
|
|
17
17
|
autoload :LvasgnNode, 'solargraph/parser/parser_gem/node_processors/lvasgn_node'
|
|
18
18
|
autoload :GvasgnNode, 'solargraph/parser/parser_gem/node_processors/gvasgn_node'
|
|
19
19
|
autoload :CasgnNode, 'solargraph/parser/parser_gem/node_processors/casgn_node'
|
|
20
|
+
autoload :MasgnNode, 'solargraph/parser/parser_gem/node_processors/masgn_node'
|
|
20
21
|
autoload :AliasNode, 'solargraph/parser/parser_gem/node_processors/alias_node'
|
|
21
22
|
autoload :ArgsNode, 'solargraph/parser/parser_gem/node_processors/args_node'
|
|
22
23
|
autoload :BlockNode, 'solargraph/parser/parser_gem/node_processors/block_node'
|
|
@@ -43,6 +44,7 @@ module Solargraph
|
|
|
43
44
|
register :lvasgn, ParserGem::NodeProcessors::LvasgnNode
|
|
44
45
|
register :gvasgn, ParserGem::NodeProcessors::GvasgnNode
|
|
45
46
|
register :casgn, ParserGem::NodeProcessors::CasgnNode
|
|
47
|
+
register :masgn, ParserGem::NodeProcessors::MasgnNode
|
|
46
48
|
register :alias, ParserGem::NodeProcessors::AliasNode
|
|
47
49
|
register :args, ParserGem::NodeProcessors::ArgsNode
|
|
48
50
|
register :forward_args, ParserGem::NodeProcessors::ArgsNode
|
|
@@ -9,10 +9,14 @@ module Solargraph
|
|
|
9
9
|
# @return [Parser::AST::Node, nil]
|
|
10
10
|
attr_reader :assignment
|
|
11
11
|
|
|
12
|
+
attr_accessor :mass_assignment
|
|
13
|
+
|
|
12
14
|
# @param assignment [Parser::AST::Node, nil]
|
|
13
15
|
def initialize assignment: nil, **splat
|
|
14
16
|
super(**splat)
|
|
15
17
|
@assignment = assignment
|
|
18
|
+
# @type [nil, ::Array(Parser::AST::Node, Integer)]
|
|
19
|
+
@mass_assignment = nil
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
def completion_item_kind
|
|
@@ -36,10 +40,12 @@ module Solargraph
|
|
|
36
40
|
true
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
# @param parent_node [Parser::AST::Node]
|
|
44
|
+
# @param api_map [ApiMap]
|
|
45
|
+
# @return [::Array<ComplexType>]
|
|
46
|
+
def return_types_from_node(parent_node, api_map)
|
|
41
47
|
types = []
|
|
42
|
-
value_position_nodes_only(
|
|
48
|
+
value_position_nodes_only(parent_node).each do |node|
|
|
43
49
|
# Nil nodes may not have a location
|
|
44
50
|
if node.nil? || node.type == :NIL || node.type == :nil
|
|
45
51
|
types.push ComplexType::NIL
|
|
@@ -51,12 +57,35 @@ module Solargraph
|
|
|
51
57
|
# Use the return node for inference. The clip might infer from the
|
|
52
58
|
# first node in a method call instead of the entire call.
|
|
53
59
|
chain = Parser.chain(node, nil, nil)
|
|
54
|
-
result = chain.infer(api_map, closure, clip.locals)
|
|
60
|
+
result = chain.infer(api_map, closure, clip.locals).self_to(closure.context.tag)
|
|
55
61
|
types.push result unless result.undefined?
|
|
56
62
|
end
|
|
57
63
|
end
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
types
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param api_map [ApiMap]
|
|
68
|
+
# @return [ComplexType]
|
|
69
|
+
def probe api_map
|
|
70
|
+
unless @assignment.nil?
|
|
71
|
+
types = return_types_from_node(@assignment, api_map)
|
|
72
|
+
return ComplexType.new(types.uniq) unless types.empty?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
unless @mass_assignment.nil?
|
|
76
|
+
mass_node, index = @mass_assignment
|
|
77
|
+
types = return_types_from_node(mass_node, api_map)
|
|
78
|
+
types.map! do |type|
|
|
79
|
+
if type.tuple?
|
|
80
|
+
type.all_params[index]
|
|
81
|
+
elsif ['::Array', '::Set', '::Enumerable'].include?(type.rooted_name)
|
|
82
|
+
type.all_params.first
|
|
83
|
+
end
|
|
84
|
+
end.compact!
|
|
85
|
+
return ComplexType.new(types.uniq) unless types.empty?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
ComplexType::UNDEFINED
|
|
60
89
|
end
|
|
61
90
|
|
|
62
91
|
def == other
|
data/lib/solargraph/pin/block.rb
CHANGED
|
@@ -18,17 +18,18 @@ module Solargraph
|
|
|
18
18
|
@receiver = receiver
|
|
19
19
|
@context = context
|
|
20
20
|
@parameters = args
|
|
21
|
+
@return_type = ComplexType.parse('::Proc')
|
|
21
22
|
@node = node
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
# @param api_map [ApiMap]
|
|
25
26
|
# @return [void]
|
|
26
27
|
def rebind api_map
|
|
27
|
-
@
|
|
28
|
+
@rebind ||= maybe_rebind(api_map)
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def binder
|
|
31
|
-
@
|
|
32
|
+
@rebind&.defined? ? @rebind : closure.binder
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# @return [::Array<Parameter>]
|
|
@@ -41,42 +42,79 @@ module Solargraph
|
|
|
41
42
|
@parameter_names ||= parameters.map(&:name)
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
# @param yield_types [::Array<ComplexType>]
|
|
46
|
+
# @param parameters [::Array<Parameter>]
|
|
47
|
+
#
|
|
48
|
+
# @return [::Array<ComplexType>]
|
|
49
|
+
def destructure_yield_types(yield_types, parameters)
|
|
50
|
+
return yield_types if yield_types.length == parameters.length
|
|
51
|
+
|
|
52
|
+
# yielding a tuple into a block will destructure the tuple
|
|
53
|
+
if yield_types.length == 1
|
|
54
|
+
yield_type = yield_types.first
|
|
55
|
+
return yield_type.all_params if yield_type.tuple? && yield_type.all_params.length == parameters.length
|
|
56
|
+
end
|
|
57
|
+
parameters.map { ComplexType::UNDEFINED }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @todo the next step with parameters, arguments, destructuring,
|
|
61
|
+
# kwargs, etc logic is probably either creating a Parameters
|
|
62
|
+
# or Callable pin that encapsulates and shares the logic
|
|
63
|
+
# between methods, blocks and signatures. It could live in
|
|
64
|
+
# Signature if Method didn't also own potentially different
|
|
65
|
+
# set of parameters, generics and return types.
|
|
66
|
+
|
|
67
|
+
# @param api_map [ApiMap]
|
|
68
|
+
# @return [::Array<ComplexType>]
|
|
69
|
+
def typify_parameters(api_map)
|
|
70
|
+
chain = Parser.chain(receiver, filename, node)
|
|
71
|
+
clip = api_map.clip_at(location.filename, location.range.start)
|
|
72
|
+
locals = clip.locals - [self]
|
|
73
|
+
meths = chain.define(api_map, closure, locals)
|
|
74
|
+
# @todo Convert logic to use signatures
|
|
75
|
+
meths.each do |meth|
|
|
76
|
+
next if meth.block.nil?
|
|
77
|
+
|
|
78
|
+
yield_types = meth.block.parameters.map(&:return_type)
|
|
79
|
+
# 'arguments' is what the method says it will yield to the
|
|
80
|
+
# block; 'parameters' is what the block accepts
|
|
81
|
+
argument_types = destructure_yield_types(yield_types, parameters)
|
|
82
|
+
param_types = argument_types.each_with_index.map do |arg_type, idx|
|
|
83
|
+
param = parameters[idx]
|
|
84
|
+
param_type = chain.base.infer(api_map, param, locals)
|
|
85
|
+
unless arg_type.nil?
|
|
86
|
+
if arg_type.generic? && param_type.defined?
|
|
87
|
+
namespace_pin = api_map.get_namespace_pins(meth.namespace, closure.namespace).first
|
|
88
|
+
arg_type.resolve_generics(namespace_pin, param_type)
|
|
89
|
+
else
|
|
90
|
+
arg_type.self_to(chain.base.infer(api_map, self, locals).namespace).qualify(api_map, meth.context.namespace)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
return param_types if param_types.all?(&:defined?)
|
|
95
|
+
end
|
|
96
|
+
parameters.map { ComplexType::UNDEFINED }
|
|
97
|
+
end
|
|
98
|
+
|
|
44
99
|
private
|
|
45
100
|
|
|
46
101
|
# @param api_map [ApiMap]
|
|
47
|
-
# @return [ComplexType
|
|
48
|
-
def
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
return nil unless api_map.rebindable_method_names.include?(word)
|
|
102
|
+
# @return [ComplexType]
|
|
103
|
+
def maybe_rebind api_map
|
|
104
|
+
return ComplexType::UNDEFINED unless receiver
|
|
105
|
+
|
|
52
106
|
chain = Parser.chain(receiver, location.filename)
|
|
53
107
|
locals = api_map.source_map(location.filename).locals_at(location)
|
|
54
|
-
links_last_word = chain.links.last.word
|
|
55
|
-
if %w[instance_eval instance_exec class_eval class_exec module_eval module_exec].include?(links_last_word)
|
|
56
|
-
return chain.base.infer(api_map, self, locals)
|
|
57
|
-
end
|
|
58
|
-
if 'define_method' == links_last_word and chain.define(api_map, self, locals).first&.path == 'Module#define_method' # change class type to instance type
|
|
59
|
-
if chain.links.size > 1 # Class.define_method
|
|
60
|
-
ty = chain.base.infer(api_map, self, locals)
|
|
61
|
-
return Solargraph::ComplexType.parse(ty.namespace)
|
|
62
|
-
else # define_method without self
|
|
63
|
-
return Solargraph::ComplexType.parse(closure.binder.namespace)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
# other case without early return, read block yieldreceiver tags
|
|
67
108
|
receiver_pin = chain.define(api_map, self, locals).first
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
nil
|
|
109
|
+
return ComplexType::UNDEFINED unless receiver_pin
|
|
110
|
+
|
|
111
|
+
types = receiver_pin.docstring.tag(:yieldreceiver)&.types
|
|
112
|
+
return ComplexType::UNDEFINED unless types&.any?
|
|
113
|
+
|
|
114
|
+
target = chain.base.infer(api_map, receiver_pin, locals)
|
|
115
|
+
target = full_context unless target.defined?
|
|
116
|
+
|
|
117
|
+
ComplexType.try_parse(*types).qualify(api_map, receiver_pin.context.namespace).self_to(target.to_s)
|
|
80
118
|
end
|
|
81
119
|
end
|
|
82
120
|
end
|
|
@@ -11,8 +11,9 @@ module Solargraph
|
|
|
11
11
|
# given closure/scope, and the delegated method will then be resolved
|
|
12
12
|
# to a method pin on that type.
|
|
13
13
|
#
|
|
14
|
-
# @param
|
|
14
|
+
# @param method [Method, nil] an already resolved method pin.
|
|
15
15
|
# @param receiver [Source::Chain, nil] the source code used to resolve the receiver for this delegated method.
|
|
16
|
+
# @param name [String]
|
|
16
17
|
# @param receiver_method_name [String] the method name that will be called on the receiver (defaults to :name).
|
|
17
18
|
def initialize(method: nil, receiver: nil, name: method&.name, receiver_method_name: name, **splat)
|
|
18
19
|
raise ArgumentError, 'either :method or :receiver is required' if (method && receiver) || (!method && !receiver)
|
|
@@ -37,6 +38,7 @@ module Solargraph
|
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
# @param api_map [ApiMap]
|
|
40
42
|
def resolvable?(api_map)
|
|
41
43
|
resolve_method(api_map)
|
|
42
44
|
!!@resolved_method
|
|
@@ -79,6 +81,7 @@ module Solargraph
|
|
|
79
81
|
# helper to print a source chain as code, probably not 100% correct.
|
|
80
82
|
#
|
|
81
83
|
# @param chain [Source::Chain]
|
|
84
|
+
# @return [String]
|
|
82
85
|
def print_chain(chain)
|
|
83
86
|
out = +''
|
|
84
87
|
chain.links.each_with_index do |link, index|
|
|
@@ -91,6 +94,7 @@ module Solargraph
|
|
|
91
94
|
end
|
|
92
95
|
out << link.word
|
|
93
96
|
end
|
|
97
|
+
out
|
|
94
98
|
end
|
|
95
99
|
end
|
|
96
100
|
end
|
|
@@ -135,7 +135,7 @@ module Solargraph
|
|
|
135
135
|
result = []
|
|
136
136
|
result.push generate_signature(parameters, top_type) if top_type.defined?
|
|
137
137
|
result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty?
|
|
138
|
-
result.push generate_signature(parameters,
|
|
138
|
+
result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
|
|
139
139
|
result
|
|
140
140
|
end
|
|
141
141
|
end
|
|
@@ -288,7 +288,9 @@ module Solargraph
|
|
|
288
288
|
|
|
289
289
|
# @return [::Array<Pin::Method>]
|
|
290
290
|
def overloads
|
|
291
|
-
|
|
291
|
+
# Ignore overload tags with nil parameters. If it's not an array, the
|
|
292
|
+
# tag's source is likely malformed.
|
|
293
|
+
@overloads ||= docstring.tags(:overload).select(&:parameters).map do |tag|
|
|
292
294
|
Pin::Signature.new(
|
|
293
295
|
generics,
|
|
294
296
|
tag.parameters.map do |src|
|
|
@@ -76,6 +76,7 @@ module Solargraph
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
# @return [ComplexType]
|
|
79
80
|
def return_type
|
|
80
81
|
if @return_type.nil?
|
|
81
82
|
@return_type = ComplexType::UNDEFINED
|
|
@@ -99,7 +100,9 @@ module Solargraph
|
|
|
99
100
|
#
|
|
100
101
|
# @return [Integer]
|
|
101
102
|
def index
|
|
102
|
-
|
|
103
|
+
# @type [Method, Block]
|
|
104
|
+
method_pin = closure
|
|
105
|
+
method_pin.parameter_names.index(name)
|
|
103
106
|
end
|
|
104
107
|
|
|
105
108
|
# @param api_map [ApiMap]
|
|
@@ -140,33 +143,7 @@ module Solargraph
|
|
|
140
143
|
# @return [ComplexType]
|
|
141
144
|
def typify_block_param api_map
|
|
142
145
|
if closure.is_a?(Pin::Block) && closure.receiver
|
|
143
|
-
|
|
144
|
-
clip = api_map.clip_at(location.filename, location.range.start)
|
|
145
|
-
locals = clip.locals - [self]
|
|
146
|
-
meths = chain.define(api_map, closure, locals)
|
|
147
|
-
receiver_type = chain.base.infer(api_map, closure, locals)
|
|
148
|
-
meths.each do |meth|
|
|
149
|
-
block_signature = meth.block
|
|
150
|
-
if block_signature
|
|
151
|
-
yield_type = block_signature.parameters[index]&.return_type
|
|
152
|
-
else
|
|
153
|
-
# @todo move the yieldparam tag parsing logic into the
|
|
154
|
-
# creation of Method pins so we don't need this else
|
|
155
|
-
# statement
|
|
156
|
-
yps = meth.docstring.tags(:yieldparam)
|
|
157
|
-
unless yps[index].nil? or yps[index].types.nil? or yps[index].types.empty?
|
|
158
|
-
yield_type = ComplexType.try_parse(yps[index].types.first)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
unless yield_type.nil?
|
|
162
|
-
if yield_type.generic? && receiver_type.defined?
|
|
163
|
-
namespace_pin = api_map.get_namespace_pins(meth.namespace, closure.namespace).first
|
|
164
|
-
return yield_type.resolve_generics(namespace_pin, receiver_type)
|
|
165
|
-
else
|
|
166
|
-
return yield_type.self_to(chain.base.infer(api_map, closure, locals).namespace).qualify(api_map, meth.context.namespace)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
146
|
+
return closure.typify_parameters(api_map)[index]
|
|
170
147
|
end
|
|
171
148
|
ComplexType::UNDEFINED
|
|
172
149
|
end
|
|
@@ -39,24 +39,7 @@ module Solargraph
|
|
|
39
39
|
cursor = pins.length
|
|
40
40
|
environment.declarations.each { |decl| convert_decl_to_pin(decl, Solargraph::Pin::ROOT_PIN) }
|
|
41
41
|
added_pins = pins[cursor..-1]
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# @param added_pins [::Enumerable<Pin>]
|
|
46
|
-
# @return [void]
|
|
47
|
-
def add_back_implicit_pins(added_pins)
|
|
48
|
-
added_pins.each do |pin|
|
|
49
|
-
pin.source = :rbs
|
|
50
|
-
next unless pin.is_a?(Pin::Namespace) && pin.type == :class
|
|
51
|
-
next if pins.any? { |p| p.path == "#{pin.path}.new"}
|
|
52
|
-
pins.push Solargraph::Pin::Method.new(
|
|
53
|
-
location: nil,
|
|
54
|
-
closure: pin,
|
|
55
|
-
name: 'new',
|
|
56
|
-
comments: pin.comments,
|
|
57
|
-
scope: :class
|
|
58
|
-
)
|
|
59
|
-
end
|
|
42
|
+
added_pins.each { |pin| pin.source = :rbs }
|
|
60
43
|
end
|
|
61
44
|
|
|
62
45
|
# @param decl [RBS::AST::Declarations::Base]
|
|
@@ -173,7 +156,7 @@ module Solargraph
|
|
|
173
156
|
name: decl.super_class.name.relative!.to_s
|
|
174
157
|
)
|
|
175
158
|
end
|
|
176
|
-
add_mixins decl, class_pin
|
|
159
|
+
add_mixins decl, class_pin
|
|
177
160
|
convert_members_to_pins decl, class_pin
|
|
178
161
|
end
|
|
179
162
|
|
|
@@ -303,26 +286,8 @@ module Solargraph
|
|
|
303
286
|
pin.signatures.concat method_def_to_sigs(decl, pin)
|
|
304
287
|
pins.push pin
|
|
305
288
|
if pin.name == 'initialize'
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
closure: pin.closure,
|
|
309
|
-
name: 'new',
|
|
310
|
-
comments: pin.comments,
|
|
311
|
-
scope: :class,
|
|
312
|
-
signatures: pin.signatures
|
|
313
|
-
)
|
|
314
|
-
pins.last.signatures.replace(
|
|
315
|
-
pin.signatures.map do |p|
|
|
316
|
-
Pin::Signature.new(
|
|
317
|
-
p.generics,
|
|
318
|
-
p.parameters,
|
|
319
|
-
ComplexType::SELF
|
|
320
|
-
)
|
|
321
|
-
end
|
|
322
|
-
)
|
|
323
|
-
# @todo Is this necessary?
|
|
324
|
-
# pin.instance_variable_set(:@visibility, :private)
|
|
325
|
-
# pin.instance_variable_set(:@return_type, ComplexType::VOID)
|
|
289
|
+
pin.instance_variable_set(:@visibility, :private)
|
|
290
|
+
pin.instance_variable_set(:@return_type, ComplexType::VOID)
|
|
326
291
|
end
|
|
327
292
|
end
|
|
328
293
|
if decl.singleton?
|
|
@@ -551,13 +516,17 @@ module Solargraph
|
|
|
551
516
|
|
|
552
517
|
# @param type_name [RBS::TypeName]
|
|
553
518
|
# @param type_args [Enumerable<RBS::Types::Bases::Base>]
|
|
554
|
-
# @return [ComplexType]
|
|
519
|
+
# @return [ComplexType::UniqueType]
|
|
555
520
|
def generic_type(type_name, type_args)
|
|
556
521
|
base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s
|
|
557
|
-
params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
522
|
+
params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }.map do |t|
|
|
523
|
+
ComplexType.try_parse(t)
|
|
524
|
+
end
|
|
525
|
+
if base == 'Hash' && params.length == 2
|
|
526
|
+
ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: true, parameters_type: :hash)
|
|
527
|
+
else
|
|
528
|
+
ComplexType::UniqueType.new(base, [], params, rooted: true, parameters_type: :list)
|
|
529
|
+
end
|
|
561
530
|
end
|
|
562
531
|
|
|
563
532
|
# @param type_name [RBS::TypeName]
|
|
@@ -621,15 +590,15 @@ module Solargraph
|
|
|
621
590
|
end
|
|
622
591
|
|
|
623
592
|
# @param decl [RBS::AST::Declarations::Class, RBS::AST::Declarations::Module]
|
|
624
|
-
# @param
|
|
593
|
+
# @param namespace [Pin::Namespace]
|
|
625
594
|
# @return [void]
|
|
626
|
-
def add_mixins decl,
|
|
595
|
+
def add_mixins decl, namespace
|
|
627
596
|
decl.each_mixin do |mixin|
|
|
628
597
|
klass = mixin.is_a?(RBS::AST::Members::Include) ? Pin::Reference::Include : Pin::Reference::Extend
|
|
629
598
|
pins.push klass.new(
|
|
630
599
|
name: mixin.name.relative!.to_s,
|
|
631
600
|
location: rbs_location_to_location(mixin.location),
|
|
632
|
-
closure:
|
|
601
|
+
closure: namespace
|
|
633
602
|
)
|
|
634
603
|
end
|
|
635
604
|
end
|
|
@@ -22,11 +22,17 @@ module Solargraph
|
|
|
22
22
|
closure: Solargraph::Pin::Namespace.new(name: 'Object'), comments: '@return [Class<self>]')
|
|
23
23
|
]
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
Override.
|
|
27
|
-
Override.
|
|
28
|
-
Override.
|
|
29
|
-
Override.
|
|
25
|
+
OVERRIDES = [
|
|
26
|
+
Override.from_comment('BasicObject#instance_eval', '@yieldreceiver [self]'),
|
|
27
|
+
Override.from_comment('BasicObject#instance_exec', '@yieldreceiver [self]'),
|
|
28
|
+
Override.from_comment('Module#define_method', '@yieldreceiver [Object<self>]'),
|
|
29
|
+
Override.from_comment('Module#class_eval', '@yieldreceiver [Class<self>]'),
|
|
30
|
+
Override.from_comment('Module#class_exec', '@yieldreceiver [Class<self>]'),
|
|
31
|
+
Override.from_comment('Module#module_eval', '@yieldreceiver [Module<self>]'),
|
|
32
|
+
Override.from_comment('Module#module_exec', '@yieldreceiver [Module<self>]'),
|
|
33
|
+
# RBS does not define Class with a generic, so all calls to
|
|
34
|
+
# generic() return an 'untyped'. We can do better:
|
|
35
|
+
Override.method_return('Class#allocate', 'self')
|
|
30
36
|
]
|
|
31
37
|
|
|
32
38
|
# HACK: Add Errno exception classes
|
|
@@ -38,7 +44,7 @@ module Solargraph
|
|
|
38
44
|
end
|
|
39
45
|
ERRNOS = errnos
|
|
40
46
|
|
|
41
|
-
ALL = KEYWORDS + MISSING +
|
|
47
|
+
ALL = KEYWORDS + MISSING + OVERRIDES + ERRNOS
|
|
42
48
|
end
|
|
43
49
|
end
|
|
44
50
|
end
|
|
@@ -13,27 +13,16 @@ module Solargraph
|
|
|
13
13
|
pins.replace cache
|
|
14
14
|
else
|
|
15
15
|
loader = RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false))
|
|
16
|
-
|
|
16
|
+
RBS::Environment.from_loader(loader).resolve_type_names
|
|
17
17
|
load_environment_to_pins(loader)
|
|
18
18
|
pins.concat RbsMap::CoreFills::ALL
|
|
19
19
|
processed = ApiMap::Store.new(pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) }
|
|
20
|
+
processed.each { |pin| pin.source = :rbs }
|
|
20
21
|
pins.replace processed
|
|
21
22
|
|
|
22
23
|
Cache.save('core.ser', pins)
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
|
-
|
|
26
|
-
def method_def_to_sigs decl, pin
|
|
27
|
-
stubs = CoreSigns.sign(pin.path)
|
|
28
|
-
return super unless stubs
|
|
29
|
-
stubs.map do |stub|
|
|
30
|
-
Pin::Signature.new(
|
|
31
|
-
[],
|
|
32
|
-
[],
|
|
33
|
-
ComplexType.try_parse(stub.return_type)
|
|
34
|
-
)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
26
|
end
|
|
38
27
|
end
|
|
39
28
|
end
|
data/lib/solargraph/rbs_map.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'pathname'
|
|
3
4
|
require 'rbs'
|
|
4
5
|
|
|
5
6
|
module Solargraph
|
|
@@ -7,7 +8,6 @@ module Solargraph
|
|
|
7
8
|
autoload :Conversions, 'solargraph/rbs_map/conversions'
|
|
8
9
|
autoload :CoreMap, 'solargraph/rbs_map/core_map'
|
|
9
10
|
autoload :CoreFills, 'solargraph/rbs_map/core_fills'
|
|
10
|
-
autoload :CoreSigns, 'solargraph/rbs_map/core_signs'
|
|
11
11
|
autoload :StdlibMap, 'solargraph/rbs_map/stdlib_map'
|
|
12
12
|
|
|
13
13
|
include Conversions
|
|
@@ -18,20 +18,26 @@ module Solargraph
|
|
|
18
18
|
attr_reader :library
|
|
19
19
|
|
|
20
20
|
# @param library [String]
|
|
21
|
-
|
|
21
|
+
# @param version [String, nil]
|
|
22
|
+
# @param directories [Array<Pathname, String>]
|
|
23
|
+
def initialize library, version = nil, directories: []
|
|
22
24
|
@library = library
|
|
23
25
|
@version = version
|
|
24
26
|
@collection = nil
|
|
27
|
+
@directories = directories
|
|
25
28
|
loader = RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
|
|
26
29
|
add_library loader, library, version
|
|
27
30
|
return unless resolved?
|
|
28
31
|
load_environment_to_pins(loader)
|
|
29
32
|
end
|
|
30
33
|
|
|
34
|
+
# @generic T
|
|
31
35
|
# @param path [String]
|
|
32
|
-
# @
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
# @param klass [Class<generic<T>>]
|
|
37
|
+
# @return [generic<T>, nil]
|
|
38
|
+
def path_pin path, klass = Pin::Base
|
|
39
|
+
pin = pins.find { |p| p.path == path }
|
|
40
|
+
pin if pin&.is_a?(klass)
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
# @param path [String]
|
|
@@ -45,7 +51,11 @@ module Solargraph
|
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
def repository
|
|
48
|
-
@repository ||= RBS::Repository.new(no_stdlib: false)
|
|
54
|
+
@repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo|
|
|
55
|
+
# @todo Temporarily ignoring external directories due to issues with
|
|
56
|
+
# incomplete/broken gem_rbs_collection installations
|
|
57
|
+
# @directories.each { |dir| repo.add(Pathname.new(dir)) }
|
|
58
|
+
end
|
|
49
59
|
end
|
|
50
60
|
|
|
51
61
|
# @param library [String]
|
|
@@ -69,7 +79,7 @@ module Solargraph
|
|
|
69
79
|
Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
|
|
70
80
|
true
|
|
71
81
|
else
|
|
72
|
-
Solargraph.logger.
|
|
82
|
+
Solargraph.logger.info "#{short_name} failed to load library #{library}"
|
|
73
83
|
false
|
|
74
84
|
end
|
|
75
85
|
end
|
data/lib/solargraph/shell.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'benchmark'
|
|
3
4
|
require 'thor'
|
|
4
5
|
require 'yard'
|
|
5
6
|
|
|
@@ -149,23 +150,28 @@ module Solargraph
|
|
|
149
150
|
# @return [void]
|
|
150
151
|
def typecheck *files
|
|
151
152
|
directory = File.realpath(options[:directory])
|
|
152
|
-
api_map = Solargraph::ApiMap.load_with_cache(directory)
|
|
153
|
+
api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
|
|
154
|
+
probcount = 0
|
|
153
155
|
if files.empty?
|
|
154
156
|
files = api_map.source_maps.map(&:filename)
|
|
155
157
|
else
|
|
156
158
|
files.map! { |file| File.realpath(file) }
|
|
157
159
|
end
|
|
158
|
-
probcount = 0
|
|
159
160
|
filecount = 0
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
|
|
162
|
+
time = Benchmark.measure {
|
|
163
|
+
files.each do |file|
|
|
164
|
+
checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
|
|
165
|
+
problems = checker.problems
|
|
166
|
+
next if problems.empty?
|
|
167
|
+
problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
|
|
168
|
+
puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
|
|
169
|
+
filecount += 1
|
|
170
|
+
probcount += problems.length
|
|
171
|
+
end
|
|
172
|
+
# "
|
|
173
|
+
}
|
|
174
|
+
puts "Typecheck finished in #{time.real} seconds."
|
|
169
175
|
puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
|
|
170
176
|
# "
|
|
171
177
|
exit 1 if probcount > 0
|
|
@@ -182,11 +188,10 @@ module Solargraph
|
|
|
182
188
|
option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
|
|
183
189
|
# @return [void]
|
|
184
190
|
def scan
|
|
185
|
-
require 'benchmark'
|
|
186
191
|
directory = File.realpath(options[:directory])
|
|
187
192
|
api_map = nil
|
|
188
193
|
time = Benchmark.measure {
|
|
189
|
-
api_map = Solargraph::ApiMap.load_with_cache(directory)
|
|
194
|
+
api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
|
|
190
195
|
api_map.pins.each do |pin|
|
|
191
196
|
begin
|
|
192
197
|
puts pin_description(pin) if options[:verbose]
|
|
@@ -29,6 +29,8 @@ module Solargraph
|
|
|
29
29
|
|
|
30
30
|
@@inference_stack = []
|
|
31
31
|
@@inference_depth = 0
|
|
32
|
+
@@inference_invalidation_key = nil
|
|
33
|
+
@@inference_cache = {}
|
|
32
34
|
|
|
33
35
|
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
|
34
36
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
|
@@ -81,7 +83,25 @@ module Solargraph
|
|
|
81
83
|
# @param name_pin [Pin::Base]
|
|
82
84
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
|
83
85
|
# @return [ComplexType]
|
|
86
|
+
# @sg-ignore
|
|
84
87
|
def infer api_map, name_pin, locals
|
|
88
|
+
out = nil
|
|
89
|
+
cached = @@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] unless node.nil?
|
|
90
|
+
return cached if cached && @@inference_invalidation_key == api_map.hash
|
|
91
|
+
out = infer_uncached api_map, name_pin, locals
|
|
92
|
+
if @@inference_invalidation_key != api_map.hash
|
|
93
|
+
@@inference_cache = {}
|
|
94
|
+
@@inference_invalidation_key = api_map.hash
|
|
95
|
+
end
|
|
96
|
+
@@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] = out unless node.nil?
|
|
97
|
+
out
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @param api_map [ApiMap]
|
|
101
|
+
# @param name_pin [Pin::Base]
|
|
102
|
+
# @param locals [::Enumerable<Pin::LocalVariable>]
|
|
103
|
+
# @return [ComplexType]
|
|
104
|
+
def infer_uncached api_map, name_pin, locals
|
|
85
105
|
from_here = base.infer(api_map, name_pin, locals) unless links.length == 1
|
|
86
106
|
if from_here
|
|
87
107
|
name_pin = name_pin.proxy(from_here)
|