solargraph 0.53.4 → 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 +22 -0
- data/lib/solargraph/api_map/cache.rb +2 -12
- data/lib/solargraph/api_map/store.rb +11 -6
- data/lib/solargraph/api_map.rb +53 -17
- data/lib/solargraph/complex_type/type_methods.rb +62 -30
- data/lib/solargraph/complex_type/unique_type.rb +117 -66
- data/lib/solargraph/complex_type.rb +41 -25
- data/lib/solargraph/doc_map.rb +19 -3
- data/lib/solargraph/gem_pins.rb +9 -1
- data/lib/solargraph/language_server/host/dispatch.rb +8 -1
- data/lib/solargraph/language_server/host/sources.rb +1 -61
- data/lib/solargraph/language_server/host.rb +21 -68
- 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 +136 -96
- 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/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 +34 -5
- data/lib/solargraph/pin/block.rb +55 -0
- data/lib/solargraph/pin/delegated_method.rb +5 -1
- data/lib/solargraph/pin/documenting.rb +2 -0
- data/lib/solargraph/pin/method.rb +3 -1
- data/lib/solargraph/pin/parameter.rb +5 -28
- data/lib/solargraph/rbs_map/conversions.rb +10 -6
- data/lib/solargraph/rbs_map.rb +11 -3
- 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/mapper.rb +3 -2
- data/lib/solargraph/source_map.rb +10 -0
- data/lib/solargraph/type_checker.rb +43 -18
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +2 -1
- data/lib/solargraph/workspace.rb +13 -0
- metadata +4 -3
- data/lib/solargraph/language_server/host/cataloger.rb +0 -57
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Parser
|
5
|
+
module ParserGem
|
6
|
+
module NodeProcessors
|
7
|
+
class MasgnNode < Parser::NodeProcessor::Base
|
8
|
+
include ParserGem::NodeMethods
|
9
|
+
|
10
|
+
def process
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# s(:masgn,
|
14
|
+
# s(:mlhs,
|
15
|
+
# s(:send,
|
16
|
+
# s(:send, nil, :a), :b=),
|
17
|
+
# s(:lvasgn, :b),
|
18
|
+
# s(:ivasgn, :@c)),
|
19
|
+
# s(:array,
|
20
|
+
# s(:int, 1),
|
21
|
+
# s(:int, 2),
|
22
|
+
# s(:int, 3)))
|
23
|
+
masgn = node
|
24
|
+
mlhs = masgn.children.fetch(0)
|
25
|
+
lhs_arr = mlhs.children
|
26
|
+
mass_rhs = node.children.fetch(1)
|
27
|
+
|
28
|
+
# Get pins created for the mlhs node
|
29
|
+
process_children
|
30
|
+
|
31
|
+
lhs_arr.each_with_index do |lhs, i|
|
32
|
+
location = get_node_location(lhs)
|
33
|
+
# @todo in line below, nothing in typechecking alerts
|
34
|
+
# when a non-existant method is called on 'l'
|
35
|
+
pin = locals.find { |l| l.location == location }
|
36
|
+
if pin.nil?
|
37
|
+
Solargraph.logger.debug "Could not find pin in location #{location}"
|
38
|
+
next
|
39
|
+
end
|
40
|
+
pin.mass_assignment = [mass_rhs, i]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -80,7 +80,7 @@ module Solargraph
|
|
80
80
|
)
|
81
81
|
end
|
82
82
|
if node.children[1] == :attr_writer || node.children[1] == :attr_accessor
|
83
|
-
|
83
|
+
method_pin = Solargraph::Pin::Method.new(
|
84
84
|
location: loc,
|
85
85
|
closure: clos,
|
86
86
|
name: "#{a.children[0]}=",
|
@@ -89,8 +89,9 @@ module Solargraph
|
|
89
89
|
visibility: region.visibility,
|
90
90
|
attribute: true
|
91
91
|
)
|
92
|
-
pins.
|
93
|
-
|
92
|
+
pins.push method_pin
|
93
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
94
|
+
if method_pin.return_type.defined?
|
94
95
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
95
96
|
end
|
96
97
|
end
|
@@ -112,6 +113,7 @@ module Solargraph
|
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
116
|
+
# @return [void]
|
115
117
|
def process_prepend
|
116
118
|
if node.children[2].is_a?(AST::Node) && node.children[2].type == :const
|
117
119
|
cp = region.closure
|
@@ -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
|
@@ -55,8 +61,31 @@ module Solargraph
|
|
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,6 +18,7 @@ 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
|
|
@@ -41,6 +42,60 @@ 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]
|
@@ -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
|
@@ -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
|
@@ -516,13 +516,17 @@ module Solargraph
|
|
516
516
|
|
517
517
|
# @param type_name [RBS::TypeName]
|
518
518
|
# @param type_args [Enumerable<RBS::Types::Bases::Base>]
|
519
|
-
# @return [ComplexType]
|
519
|
+
# @return [ComplexType::UniqueType]
|
520
520
|
def generic_type(type_name, type_args)
|
521
521
|
base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s
|
522
|
-
params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }
|
523
|
-
|
524
|
-
|
525
|
-
|
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
|
526
530
|
end
|
527
531
|
|
528
532
|
# @param type_name [RBS::TypeName]
|
@@ -586,7 +590,7 @@ module Solargraph
|
|
586
590
|
end
|
587
591
|
|
588
592
|
# @param decl [RBS::AST::Declarations::Class, RBS::AST::Declarations::Module]
|
589
|
-
# @param
|
593
|
+
# @param namespace [Pin::Namespace]
|
590
594
|
# @return [void]
|
591
595
|
def add_mixins decl, namespace
|
592
596
|
decl.each_mixin do |mixin|
|
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
|
@@ -17,10 +18,13 @@ module Solargraph
|
|
17
18
|
attr_reader :library
|
18
19
|
|
19
20
|
# @param library [String]
|
20
|
-
|
21
|
+
# @param version [String, nil]
|
22
|
+
# @param directories [Array<Pathname, String>]
|
23
|
+
def initialize library, version = nil, directories: []
|
21
24
|
@library = library
|
22
25
|
@version = version
|
23
26
|
@collection = nil
|
27
|
+
@directories = directories
|
24
28
|
loader = RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
|
25
29
|
add_library loader, library, version
|
26
30
|
return unless resolved?
|
@@ -47,7 +51,11 @@ module Solargraph
|
|
47
51
|
end
|
48
52
|
|
49
53
|
def repository
|
50
|
-
@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
|
51
59
|
end
|
52
60
|
|
53
61
|
# @param library [String]
|
@@ -71,7 +79,7 @@ module Solargraph
|
|
71
79
|
Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
|
72
80
|
true
|
73
81
|
else
|
74
|
-
Solargraph.logger.
|
82
|
+
Solargraph.logger.info "#{short_name} failed to load library #{library}"
|
75
83
|
false
|
76
84
|
end
|
77
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)
|
data/lib/solargraph/source.rb
CHANGED
@@ -92,50 +92,6 @@ module Solargraph
|
|
92
92
|
stack
|
93
93
|
end
|
94
94
|
|
95
|
-
# Start synchronizing the source. This method updates the code without
|
96
|
-
# parsing a new AST. The resulting Source object will be marked not
|
97
|
-
# synchronized (#synchronized? == false).
|
98
|
-
#
|
99
|
-
# @param updater [Source::Updater]
|
100
|
-
# @return [Source]
|
101
|
-
def start_synchronize updater
|
102
|
-
raise 'Invalid synchronization' unless updater.filename == filename
|
103
|
-
real_code = updater.write(@code)
|
104
|
-
src = Source.allocate
|
105
|
-
src.filename = filename
|
106
|
-
src.code = real_code
|
107
|
-
src.version = updater.version
|
108
|
-
src.parsed = parsed?
|
109
|
-
src.repaired = updater.repair(@repaired)
|
110
|
-
src.synchronized = false
|
111
|
-
src.node = @node
|
112
|
-
src.comments = @comments
|
113
|
-
src.error_ranges = error_ranges
|
114
|
-
src.last_updater = updater
|
115
|
-
return src.finish_synchronize unless real_code.lines.length == @code.lines.length
|
116
|
-
src
|
117
|
-
end
|
118
|
-
|
119
|
-
# Finish synchronizing a source that was updated via #start_synchronize.
|
120
|
-
# This method returns self if the source is already synchronized. Otherwise
|
121
|
-
# it parses the AST and returns a new synchronized Source.
|
122
|
-
#
|
123
|
-
# @return [Source]
|
124
|
-
def finish_synchronize
|
125
|
-
return self if synchronized?
|
126
|
-
synced = Source.new(@code, filename)
|
127
|
-
if synced.parsed?
|
128
|
-
synced.version = version
|
129
|
-
return synced
|
130
|
-
end
|
131
|
-
synced = Source.new(@repaired, filename)
|
132
|
-
synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
|
133
|
-
synced.code = @code
|
134
|
-
synced.synchronized = true
|
135
|
-
synced.version = version
|
136
|
-
synced
|
137
|
-
end
|
138
|
-
|
139
95
|
# Synchronize the Source with an update. This method applies changes to the
|
140
96
|
# code, parses the new code's AST, and returns the resulting Source object.
|
141
97
|
#
|
@@ -144,7 +144,7 @@ module Solargraph
|
|
144
144
|
)
|
145
145
|
end
|
146
146
|
if t.nil? || t.include?('w')
|
147
|
-
|
147
|
+
method_pin = Solargraph::Pin::Method.new(
|
148
148
|
location: location,
|
149
149
|
closure: namespace,
|
150
150
|
name: "#{directive.tag.name}=",
|
@@ -153,7 +153,8 @@ module Solargraph
|
|
153
153
|
visibility: :public,
|
154
154
|
attribute: true
|
155
155
|
)
|
156
|
-
pins.
|
156
|
+
pins.push method_pin
|
157
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
157
158
|
if pins.last.return_type.defined?
|
158
159
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
159
160
|
end
|
@@ -40,6 +40,16 @@ module Solargraph
|
|
40
40
|
@pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
|
41
41
|
end
|
42
42
|
|
43
|
+
# A hash representing the state of the source map's API.
|
44
|
+
#
|
45
|
+
# ApiMap#catalog uses this value to determine whether it needs to clear its
|
46
|
+
# cache.
|
47
|
+
#
|
48
|
+
# @return [Integer]
|
49
|
+
def api_hash
|
50
|
+
@api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select { |pin| pin.namespace.to_s > '' } + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node)).hash
|
51
|
+
end
|
52
|
+
|
43
53
|
# @return [String]
|
44
54
|
def filename
|
45
55
|
source.filename
|