solargraph 0.54.1 → 0.56.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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +3 -1
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +70 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/cache.rb +10 -1
  8. data/lib/solargraph/api_map/index.rb +175 -0
  9. data/lib/solargraph/api_map/store.rb +79 -126
  10. data/lib/solargraph/api_map.rb +247 -93
  11. data/lib/solargraph/bench.rb +17 -1
  12. data/lib/solargraph/complex_type/type_methods.rb +13 -1
  13. data/lib/solargraph/complex_type/unique_type.rb +118 -9
  14. data/lib/solargraph/complex_type.rb +48 -18
  15. data/lib/solargraph/convention/base.rb +3 -3
  16. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  17. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  18. data/lib/solargraph/convention/struct_definition.rb +101 -0
  19. data/lib/solargraph/convention.rb +5 -3
  20. data/lib/solargraph/doc_map.rb +274 -56
  21. data/lib/solargraph/equality.rb +33 -0
  22. data/lib/solargraph/gem_pins.rb +53 -37
  23. data/lib/solargraph/language_server/host/message_worker.rb +31 -11
  24. data/lib/solargraph/language_server/host.rb +24 -13
  25. data/lib/solargraph/language_server/message/base.rb +19 -12
  26. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  27. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  28. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  29. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  30. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  31. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  32. data/lib/solargraph/library.rb +13 -11
  33. data/lib/solargraph/location.rb +28 -0
  34. data/lib/solargraph/logging.rb +1 -0
  35. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  36. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  37. data/lib/solargraph/parser/node_methods.rb +15 -1
  38. data/lib/solargraph/parser/node_processor.rb +3 -1
  39. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  40. data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
  41. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
  42. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  43. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  46. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
  47. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  48. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  49. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  51. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  52. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  53. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +4 -3
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  57. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  58. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  59. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
  60. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  61. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  63. data/lib/solargraph/parser/region.rb +1 -1
  64. data/lib/solargraph/parser.rb +1 -0
  65. data/lib/solargraph/pin/base.rb +326 -43
  66. data/lib/solargraph/pin/base_variable.rb +18 -10
  67. data/lib/solargraph/pin/block.rb +3 -3
  68. data/lib/solargraph/pin/breakable.rb +9 -0
  69. data/lib/solargraph/pin/callable.rb +77 -6
  70. data/lib/solargraph/pin/closure.rb +18 -1
  71. data/lib/solargraph/pin/common.rb +5 -0
  72. data/lib/solargraph/pin/delegated_method.rb +20 -1
  73. data/lib/solargraph/pin/documenting.rb +16 -0
  74. data/lib/solargraph/pin/keyword.rb +7 -2
  75. data/lib/solargraph/pin/local_variable.rb +15 -6
  76. data/lib/solargraph/pin/method.rb +172 -42
  77. data/lib/solargraph/pin/namespace.rb +17 -9
  78. data/lib/solargraph/pin/parameter.rb +61 -11
  79. data/lib/solargraph/pin/proxy_type.rb +12 -6
  80. data/lib/solargraph/pin/reference/override.rb +10 -6
  81. data/lib/solargraph/pin/reference/require.rb +2 -2
  82. data/lib/solargraph/pin/signature.rb +42 -0
  83. data/lib/solargraph/pin/singleton.rb +1 -1
  84. data/lib/solargraph/pin/symbol.rb +3 -2
  85. data/lib/solargraph/pin/until.rb +18 -0
  86. data/lib/solargraph/pin/while.rb +18 -0
  87. data/lib/solargraph/pin.rb +4 -1
  88. data/lib/solargraph/pin_cache.rb +185 -0
  89. data/lib/solargraph/position.rb +16 -0
  90. data/lib/solargraph/range.rb +16 -0
  91. data/lib/solargraph/rbs_map/conversions.rb +199 -66
  92. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  93. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  94. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  95. data/lib/solargraph/rbs_map.rb +74 -16
  96. data/lib/solargraph/shell.rb +19 -18
  97. data/lib/solargraph/source/chain/array.rb +12 -8
  98. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  99. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  100. data/lib/solargraph/source/chain/call.rb +62 -25
  101. data/lib/solargraph/source/chain/constant.rb +1 -1
  102. data/lib/solargraph/source/chain/hash.rb +9 -3
  103. data/lib/solargraph/source/chain/head.rb +1 -1
  104. data/lib/solargraph/source/chain/if.rb +6 -1
  105. data/lib/solargraph/source/chain/link.rb +19 -5
  106. data/lib/solargraph/source/chain/literal.rb +27 -2
  107. data/lib/solargraph/source/chain/or.rb +1 -1
  108. data/lib/solargraph/source/chain/z_super.rb +1 -1
  109. data/lib/solargraph/source/chain.rb +106 -62
  110. data/lib/solargraph/source/cursor.rb +1 -11
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source.rb +2 -1
  113. data/lib/solargraph/source_map/clip.rb +4 -2
  114. data/lib/solargraph/source_map/mapper.rb +9 -5
  115. data/lib/solargraph/source_map.rb +0 -17
  116. data/lib/solargraph/type_checker/checks.rb +4 -0
  117. data/lib/solargraph/type_checker.rb +41 -14
  118. data/lib/solargraph/version.rb +1 -1
  119. data/lib/solargraph/views/_method.erb +10 -10
  120. data/lib/solargraph/views/_namespace.erb +3 -3
  121. data/lib/solargraph/views/document.erb +10 -10
  122. data/lib/solargraph/workspace/config.rb +7 -3
  123. data/lib/solargraph/workspace.rb +16 -6
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -2
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +5 -2
  127. data/lib/solargraph/yard_map/mapper.rb +5 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +6 -9
  130. data/lib/solargraph.rb +20 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +6 -5
  134. metadata +56 -29
  135. data/lib/solargraph/cache.rb +0 -77
@@ -7,19 +7,29 @@ module Solargraph
7
7
  # Ruby stdlib pins
8
8
  #
9
9
  class StdlibMap < RbsMap
10
+ include Logging
11
+
10
12
  # @type [Hash{String => RbsMap}]
11
13
  @stdlib_maps_hash = {}
12
14
 
13
15
  # @param library [String]
14
16
  def initialize library
15
- cache = Cache.load('stdlib', "#{library}.ser")
16
- if cache
17
- pins.replace cache
17
+ cached_pins = PinCache.deserialize_stdlib_require library
18
+ if cached_pins
19
+ @pins = cached_pins
18
20
  @resolved = true
21
+ @loaded = true
22
+ logger.debug { "Deserialized #{cached_pins.length} cached pins for stdlib require #{library.inspect}" }
19
23
  else
20
24
  super
21
- return unless resolved?
22
- Cache.save('stdlib', "#{library}.ser", pins)
25
+ unless resolved?
26
+ @pins = []
27
+ logger.info { "Could not resolve #{library.inspect}" }
28
+ return
29
+ end
30
+ generated_pins = pins
31
+ logger.debug { "Found #{generated_pins.length} pins for stdlib library #{library}" }
32
+ PinCache.serialize_stdlib_require library, generated_pins
23
33
  end
24
34
  end
25
35
 
@@ -10,25 +10,78 @@ module Solargraph
10
10
  autoload :CoreFills, 'solargraph/rbs_map/core_fills'
11
11
  autoload :StdlibMap, 'solargraph/rbs_map/stdlib_map'
12
12
 
13
- include Conversions
13
+ include Logging
14
14
 
15
15
  # @type [Hash{String => RbsMap}]
16
16
  @@rbs_maps_hash = {}
17
17
 
18
18
  attr_reader :library
19
19
 
20
+ attr_reader :rbs_collection_paths
21
+
22
+ attr_reader :rbs_collection_config_path
23
+
20
24
  # @param library [String]
21
25
  # @param version [String, nil]
22
- # @param directories [Array<Pathname, String>]
23
- def initialize library, version = nil, directories: []
26
+ # @param rbs_collection_paths [Array<Pathname, String>]
27
+ def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: []
28
+ if rbs_collection_config_path.nil? && !rbs_collection_paths.empty?
29
+ raise 'Please provide rbs_collection_config_path if you provide rbs_collection_paths'
30
+ end
24
31
  @library = library
25
32
  @version = version
26
- @collection = nil
27
- @directories = directories
28
- loader = RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
33
+ @rbs_collection_config_path = rbs_collection_config_path
34
+ @rbs_collection_paths = rbs_collection_paths
29
35
  add_library loader, library, version
30
- return unless resolved?
31
- load_environment_to_pins(loader)
36
+ end
37
+
38
+ def loader
39
+ @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
40
+ end
41
+
42
+ # @return string representing the version of the RBS info fetched
43
+ # for the given library. Must change when the RBS info is
44
+ # updated upstream for the same library and version. May change
45
+ # if the config for where information comes form changes.
46
+ def cache_key
47
+ @hextdigest ||= begin
48
+ data = nil
49
+ if rbs_collection_config_path
50
+ lockfile_path = RBS::Collection::Config.to_lockfile_path(Pathname.new(rbs_collection_config_path))
51
+ if lockfile_path.exist?
52
+ collection_config = RBS::Collection::Config.from_path lockfile_path
53
+ gem_config = collection_config.gem(library)
54
+ data = gem_config&.to_s
55
+ end
56
+ end
57
+ if data.nil? || data.empty?
58
+ if resolved?
59
+ # definitely came from the gem itself and not elsewhere -
60
+ # only one version per gem
61
+ 'gem-export'
62
+ else
63
+ 'unresolved'
64
+ end
65
+ else
66
+ Digest::SHA1.hexdigest(data)
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path
72
+ rbs_map = RbsMap.new(gemspec.name, gemspec.version,
73
+ rbs_collection_paths: [rbs_collection_path].compact,
74
+ rbs_collection_config_path: rbs_collection_config_path)
75
+ return rbs_map if rbs_map.resolved?
76
+
77
+ # try any version of the gem in the collection
78
+ RbsMap.new(gemspec.name, nil,
79
+ rbs_collection_paths: [rbs_collection_path].compact,
80
+ rbs_collection_config_path: rbs_collection_config_path)
81
+ end
82
+
83
+ def pins
84
+ @pins ||= resolved? ? conversions.pins : []
32
85
  end
33
86
 
34
87
  # @generic T
@@ -52,9 +105,10 @@ module Solargraph
52
105
 
53
106
  def repository
54
107
  @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)) }
108
+ @rbs_collection_paths.each do |dir|
109
+ dir_path = Pathname.new(dir)
110
+ repo.add(dir_path) if dir_path.exist? && dir_path.directory?
111
+ end
58
112
  end
59
113
  end
60
114
 
@@ -64,11 +118,15 @@ module Solargraph
64
118
  @@rbs_maps_hash[library] ||= RbsMap.new(library)
65
119
  end
66
120
 
67
- def self.from_gemspec(gemspec)
68
- RbsMap.new(gemspec.name, gemspec.version)
121
+ private
122
+
123
+ def loader
124
+ @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
69
125
  end
70
126
 
71
- private
127
+ def conversions
128
+ @conversions ||= Conversions.new(loader: loader)
129
+ end
72
130
 
73
131
  # @param loader [RBS::EnvironmentLoader]
74
132
  # @param library [String]
@@ -76,10 +134,10 @@ module Solargraph
76
134
  def add_library loader, library, version
77
135
  @resolved = if loader.has_library?(library: library, version: version)
78
136
  loader.add library: library, version: version
79
- Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
137
+ logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" }
80
138
  true
81
139
  else
82
- Solargraph.logger.info "#{short_name} failed to load library #{library}"
140
+ logger.info { "#{short_name} did not find data for library #{library}:#{version}" }
83
141
  false
84
142
  end
85
143
  end
@@ -90,19 +90,20 @@ module Solargraph
90
90
  # @return [void]
91
91
  def clear
92
92
  puts "Deleting all cached documentation (gems, core and stdlib)"
93
- Solargraph::Cache.clear
93
+ Solargraph::PinCache.clear
94
94
  end
95
95
  map 'clear-cache' => :clear
96
96
  map 'clear-cores' => :clear
97
97
 
98
98
  desc 'cache', 'Cache a gem', hide: true
99
+ option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
99
100
  # @return [void]
100
101
  # @param gem [String]
101
102
  # @param version [String, nil]
102
103
  def cache gem, version = nil
104
+ api_map = Solargraph::ApiMap.load(Dir.pwd)
103
105
  spec = Gem::Specification.find_by_name(gem, version)
104
- pins = GemPins.build(spec)
105
- Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins)
106
+ api_map.cache_gem(spec, rebuild: options[:rebuild], out: $stdout)
106
107
  end
107
108
 
108
109
  desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
@@ -111,39 +112,43 @@ module Solargraph
111
112
  also be specified to clear cached system documentation.
112
113
  Documentation will be regenerated as needed.
113
114
  )
115
+ # @param gems [Array<String>]
114
116
  # @return [void]
115
117
  def uncache *gems
116
118
  raise ArgumentError, 'No gems specified.' if gems.empty?
117
119
  gems.each do |gem|
118
120
  if gem == 'core'
119
- Cache.uncache("core.ser")
121
+ PinCache.uncache_core
120
122
  next
121
123
  end
122
124
 
123
125
  if gem == 'stdlib'
124
- Cache.uncache("stdlib")
126
+ PinCache.uncache_stdlib
125
127
  next
126
128
  end
127
129
 
128
130
  spec = Gem::Specification.find_by_name(gem)
129
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.ser")
130
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.yardoc")
131
+ PinCache.uncache_gem(spec, out: $stdout)
131
132
  end
132
133
  end
133
134
 
134
135
  desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
135
136
  option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
137
+ # @param names [Array<String>]
136
138
  # @return [void]
137
139
  def gems *names
140
+ api_map = ApiMap.load('.')
138
141
  if names.empty?
139
- Gem::Specification.to_a.each { |spec| do_cache spec }
142
+ Gem::Specification.to_a.each { |spec| do_cache spec, api_map }
143
+ STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems."
140
144
  else
141
145
  names.each do |name|
142
146
  spec = Gem::Specification.find_by_name(*name.split('='))
143
- do_cache spec
147
+ do_cache spec, api_map
144
148
  rescue Gem::MissingSpecError
145
149
  warn "Gem '#{name}' not found"
146
150
  end
151
+ STDERR.puts "Documentation cached for #{names.count} gems."
147
152
  end
148
153
  end
149
154
 
@@ -204,6 +209,7 @@ module Solargraph
204
209
  # @return [void]
205
210
  def scan
206
211
  directory = File.realpath(options[:directory])
212
+ # @type [Solargraph::ApiMap, nil]
207
213
  api_map = nil
208
214
  time = Benchmark.measure {
209
215
  api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
@@ -253,15 +259,10 @@ module Solargraph
253
259
 
254
260
  # @param gemspec [Gem::Specification]
255
261
  # @return [void]
256
- def do_cache gemspec
257
- cached = Yardoc.cached?(gemspec)
258
- if cached && !options.rebuild
259
- puts "Cache already exists for #{gemspec.name} #{gemspec.version}"
260
- else
261
- puts "#{cached ? 'Rebuilding' : 'Caching'} gem documentation for #{gemspec.name} #{gemspec.version}"
262
- pins = GemPins.build(gemspec)
263
- Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
264
- end
262
+ def do_cache gemspec, api_map
263
+ # @todo if the rebuild: option is passed as a positional arg,
264
+ # typecheck doesn't complain on the below line
265
+ api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout)
265
266
  end
266
267
  end
267
268
  end
@@ -3,8 +3,9 @@ module Solargraph
3
3
  class Chain
4
4
  class Array < Literal
5
5
  # @param children [::Array<Chain>]
6
- def initialize children
7
- super('::Array')
6
+ # @param node [Parser::AST::Node]
7
+ def initialize children, node
8
+ super('::Array', node)
8
9
  @children = children
9
10
  end
10
11
 
@@ -14,18 +15,21 @@ module Solargraph
14
15
 
15
16
  # @param api_map [ApiMap]
16
17
  # @param name_pin [Pin::Base]
17
- # @param locals [Enumerable<Pin::LocalVariable>]
18
+ # @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
18
19
  def resolve api_map, name_pin, locals
19
20
  child_types = @children.map do |child|
20
- child.infer(api_map, name_pin, locals)
21
+ child.infer(api_map, name_pin, locals).simplify_literals
21
22
  end
22
-
23
- type = if child_types.uniq.length == 1 && child_types.first.defined?
23
+ type = if child_types.length == 0 || child_types.any?(&:undefined?)
24
+ ComplexType::UniqueType.new('Array', rooted: true)
25
+ elsif child_types.uniq.length == 1 && child_types.first.defined?
24
26
  ComplexType::UniqueType.new('Array', [], child_types.uniq, rooted: true, parameters_type: :list)
27
+ elsif child_types.length == 0
28
+ ComplexType::UniqueType.new('Array', rooted: true, parameters_type: :list)
25
29
  else
26
- ComplexType::UniqueType.new('Array', rooted: true)
30
+ ComplexType::UniqueType.new('Array', [], child_types, rooted: true, parameters_type: :fixed)
27
31
  end
28
- [Pin::ProxyType.anonymous(type)]
32
+ [Pin::ProxyType.anonymous(type, source: :chain)]
29
33
  end
30
34
  end
31
35
  end
@@ -5,7 +5,7 @@ module Solargraph
5
5
  class Chain
6
6
  class BlockSymbol < Link
7
7
  def resolve api_map, name_pin, locals
8
- [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'))]
8
+ [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
9
  end
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module Solargraph
5
5
  class Chain
6
6
  class BlockVariable < Link
7
7
  def resolve api_map, name_pin, locals
8
- [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'))]
8
+ [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
9
  end
10
10
  end
11
11
  end
@@ -3,12 +3,20 @@
3
3
  module Solargraph
4
4
  class Source
5
5
  class Chain
6
+ #
7
+ # Handles both method calls and local variable references by
8
+ # first looking for a variable with the name 'word', then
9
+ # proceeding to method signature resolution if not found.
10
+ #
6
11
  class Call < Chain::Link
7
12
  include Solargraph::Parser::NodeMethods
8
13
 
9
14
  # @return [String]
10
15
  attr_reader :word
11
16
 
17
+ # @return [Location]
18
+ attr_reader :location
19
+
12
20
  # @return [::Array<Chain>]
13
21
  attr_reader :arguments
14
22
 
@@ -16,34 +24,42 @@ module Solargraph
16
24
  attr_reader :block
17
25
 
18
26
  # @param word [String]
27
+ # @param location [Location, nil]
19
28
  # @param arguments [::Array<Chain>]
20
29
  # @param block [Chain, nil]
21
- def initialize word, arguments = [], block = nil
30
+ def initialize word, location = nil, arguments = [], block = nil
22
31
  @word = word
32
+ @location = location
23
33
  @arguments = arguments
24
34
  @block = block
25
35
  fix_block_pass
26
36
  end
27
37
 
38
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
39
+ protected def equality_fields
40
+ super + [arguments, block]
41
+ end
42
+
28
43
  def with_block?
29
44
  !!@block
30
45
  end
31
46
 
32
47
  # @param api_map [ApiMap]
33
- # @param name_pin [Pin::Closure] name_pin.binder should give us the object on which 'word' will be invoked
48
+ # @param name_pin [Pin::Closure] name_pin.binder should give us the type of the object on which 'word' will be invoked
34
49
  # @param locals [::Array<Pin::LocalVariable>]
35
50
  def resolve api_map, name_pin, locals
36
51
  return super_pins(api_map, name_pin) if word == 'super'
37
52
  return yield_pins(api_map, name_pin) if word == 'yield'
38
53
  found = if head?
39
- locals.select { |p| p.name == word }
54
+ api_map.visible_pins(locals, word, name_pin, location)
40
55
  else
41
56
  []
42
57
  end
43
58
  return inferred_pins(found, api_map, name_pin, locals) unless found.empty?
44
59
  pins = name_pin.binder.each_unique_type.flat_map do |context|
45
- method_context = context.namespace == '' ? '' : context.tag
46
- api_map.get_method_stack(method_context, word, scope: context.scope)
60
+ ns_tag = context.namespace == '' ? '' : context.namespace_type.tag
61
+ stack = api_map.get_method_stack(ns_tag, word, scope: context.scope)
62
+ [stack.first].compact
47
63
  end
48
64
  return [] if pins.empty?
49
65
  inferred_pins(pins, api_map, name_pin, locals)
@@ -54,7 +70,7 @@ module Solargraph
54
70
  # @param pins [::Enumerable<Pin::Method>]
55
71
  # @param api_map [ApiMap]
56
72
  # @param name_pin [Pin::Base]
57
- # @param locals [::Array<Pin::LocalVariable>]
73
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
58
74
  # @return [::Array<Pin::Base>]
59
75
  def inferred_pins pins, api_map, name_pin, locals
60
76
  result = pins.map do |p|
@@ -67,7 +83,9 @@ module Solargraph
67
83
  # use it. If we didn't pass a block, the logic below will
68
84
  # reject it regardless
69
85
 
70
- sorted_overloads = overloads.sort { |ol| ol.block? ? -1 : 1 }
86
+ with_block, without_block = overloads.partition(&:block?)
87
+ sorted_overloads = with_block + without_block
88
+ # @type [Pin::Signature, nil]
71
89
  new_signature_pin = nil
72
90
  sorted_overloads.each do |ol|
73
91
  next unless ol.arity_matches?(arguments, with_block?)
@@ -80,11 +98,8 @@ module Solargraph
80
98
  match = ol.parameters.any?(&:restarg?)
81
99
  break
82
100
  end
83
- atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context), locals)
84
- ptype = param.return_type
85
- # @todo Weak type comparison
86
- # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
87
- unless param.return_type.undefined? || atype.name == param.return_type.name || api_map.super_and_sub?(param.return_type.name, atype.name) || param.return_type.generic?
101
+ atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context, source: :chain), locals)
102
+ unless param.compatible_arg?(atype, api_map) || param.restarg?
88
103
  match = false
89
104
  break
90
105
  end
@@ -99,9 +114,30 @@ module Solargraph
99
114
  blocktype = block_call_type(api_map, name_pin, locals)
100
115
  end
101
116
  end
117
+ # @type new_signature_pin [Pin::Signature]
102
118
  new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
103
119
  new_return_type = new_signature_pin.return_type
104
- type = with_params(new_return_type.self_to_type(name_pin.context), name_pin.context).qualify(api_map, name_pin.context.namespace) if new_return_type.defined?
120
+ if head?
121
+ # If we're at the head of the chain, we called a
122
+ # method somewhere that marked itself as returning
123
+ # self. Given we didn't invoke this on an object,
124
+ # this must be a method in this same class - so we
125
+ # use our own self type
126
+ self_type = name_pin.context
127
+ else
128
+ # if we're past the head in the chain, whatever the
129
+ # type of the lhs side is what 'self' will be in its
130
+ # declaration - we can't just use the type of the
131
+ # method pin, as this might be a subclass of the
132
+ # place where the method is defined
133
+ self_type = name_pin.binder
134
+ end
135
+ # This same logic applies to the YARD work done by
136
+ # 'with_params()'.
137
+ #
138
+ # qualify(), however, happens in the namespace where
139
+ # the docs were written - from the method pin.
140
+ type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, p.namespace) if new_return_type.defined?
105
141
  type ||= ComplexType::UNDEFINED
106
142
  end
107
143
  break if type.defined?
@@ -117,13 +153,14 @@ module Solargraph
117
153
  end
118
154
  p
119
155
  end
120
- result.map do |pin|
121
- if pin.path == 'Class#new' && name_pin.context.tag != 'Class'
122
- reduced_context = name_pin.context.reduce_class_type
156
+ logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" }
157
+ out = result.map do |pin|
158
+ if pin.path == 'Class#new' && name_pin.binder.tag != 'Class'
159
+ reduced_context = name_pin.binder.reduce_class_type
123
160
  pin.proxy(reduced_context)
124
161
  else
125
162
  next pin if pin.return_type.undefined?
126
- selfy = pin.return_type.self_to_type(name_pin.context)
163
+ selfy = pin.return_type.self_to_type(name_pin.binder)
127
164
  selfy == pin.return_type ? pin : pin.proxy(selfy)
128
165
  end
129
166
  end
@@ -132,7 +169,7 @@ module Solargraph
132
169
  # @param pin [Pin::Base]
133
170
  # @param api_map [ApiMap]
134
171
  # @param context [ComplexType]
135
- # @param locals [Enumerable<Pin::Base>]
172
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
136
173
  # @return [Pin::Base]
137
174
  def process_macro pin, api_map, context, locals
138
175
  pin.macros.each do |macro|
@@ -145,13 +182,13 @@ module Solargraph
145
182
  result = inner_process_macro(pin, macro, api_map, context, locals)
146
183
  return result unless result.return_type.undefined?
147
184
  end
148
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
185
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
149
186
  end
150
187
 
151
188
  # @param pin [Pin::Method]
152
189
  # @param api_map [ApiMap]
153
190
  # @param context [ComplexType]
154
- # @param locals [Enumerable<Pin::Base>]
191
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
155
192
  # @return [Pin::ProxyType]
156
193
  def process_directive pin, api_map, context, locals
157
194
  pin.directives.each do |dir|
@@ -160,17 +197,17 @@ module Solargraph
160
197
  result = inner_process_macro(pin, macro, api_map, context, locals)
161
198
  return result unless result.return_type.undefined?
162
199
  end
163
- Pin::ProxyType.anonymous ComplexType::UNDEFINED
200
+ Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
164
201
  end
165
202
 
166
203
  # @param pin [Pin::Base]
167
204
  # @param macro [YARD::Tags::MacroDirective]
168
205
  # @param api_map [ApiMap]
169
206
  # @param context [ComplexType]
170
- # @param locals [Enumerable<Pin::Base>]
207
+ # @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
171
208
  # @return [Pin::ProxyType]
172
209
  def inner_process_macro pin, macro, api_map, context, locals
173
- vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
210
+ vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
174
211
  txt = macro.tag.text.clone
175
212
  if txt.empty? && macro.tag.name
176
213
  named = api_map.named_macro(macro.tag.name)
@@ -184,9 +221,9 @@ module Solargraph
184
221
  docstring = Solargraph::Source.parse_docstring(txt).to_docstring
185
222
  tag = docstring.tag(:return)
186
223
  unless tag.nil? || tag.types.nil?
187
- return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types))
224
+ return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
188
225
  end
189
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
226
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
190
227
  end
191
228
 
192
229
  # @param docstring [YARD::Docstring]
@@ -41,7 +41,7 @@ module Solargraph
41
41
 
42
42
  private
43
43
 
44
- # @param pin [Pin::Base]
44
+ # @param pin [Pin::Closure]
45
45
  # @return [::Array<String>]
46
46
  def crawl_gates pin
47
47
  clos = pin
@@ -5,18 +5,24 @@ module Solargraph
5
5
  class Chain
6
6
  class Hash < Literal
7
7
  # @param type [String]
8
+ # @param node [Parser::AST::Node]
8
9
  # @param splatted [Boolean]
9
- def initialize type, splatted = false
10
- super(type)
10
+ def initialize type, node, splatted = false
11
+ super(type, node)
11
12
  @splatted = splatted
12
13
  end
13
14
 
15
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
16
+ protected def equality_fields
17
+ super + [@splatted]
18
+ end
19
+
14
20
  def word
15
21
  @word ||= "<#{@type}>"
16
22
  end
17
23
 
18
24
  def resolve api_map, name_pin, locals
19
- [Pin::ProxyType.anonymous(@complex_type)]
25
+ [Pin::ProxyType.anonymous(@complex_type, source: :chain)]
20
26
  end
21
27
 
22
28
  def splatted?
@@ -9,7 +9,7 @@ module Solargraph
9
9
  # @note Chain::Head is only intended to handle `self` and `super`.
10
10
  class Head < Link
11
11
  def resolve api_map, name_pin, locals
12
- return [Pin::ProxyType.anonymous(name_pin.binder)] if word == 'self'
12
+ return [Pin::ProxyType.anonymous(name_pin.binder, source: :chain)] if word == 'self'
13
13
  # return super_pins(api_map, name_pin) if word == 'super'
14
14
  []
15
15
  end
@@ -13,9 +13,14 @@ module Solargraph
13
13
  @links = links
14
14
  end
15
15
 
16
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
17
+ protected def equality_fields
18
+ super + [@links]
19
+ end
20
+
16
21
  def resolve api_map, name_pin, locals
17
22
  types = @links.map { |link| link.infer(api_map, name_pin, locals) }
18
- [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')))]
23
+ [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')), source: :chain)]
19
24
  end
20
25
  end
21
26
  end
@@ -4,6 +4,8 @@ module Solargraph
4
4
  class Source
5
5
  class Chain
6
6
  class Link
7
+ include Equality
8
+
7
9
  # @return [String]
8
10
  attr_reader :word
9
11
 
@@ -15,6 +17,11 @@ module Solargraph
15
17
  @word = word
16
18
  end
17
19
 
20
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
21
+ protected def equality_fields
22
+ [self.class, word]
23
+ end
24
+
18
25
  def undefined?
19
26
  word == '<undefined>'
20
27
  end
@@ -31,18 +38,23 @@ module Solargraph
31
38
  []
32
39
  end
33
40
 
41
+ # debugging description of contents; not for machine use
42
+ def desc
43
+ word
44
+ end
45
+
46
+ def to_s
47
+ desc
48
+ end
49
+
34
50
  def inspect
35
- "#{self.class} #{word}"
51
+ "#<#{self.class} - `#{self.desc}`>"
36
52
  end
37
53
 
38
54
  def head?
39
55
  @head ||= false
40
56
  end
41
57
 
42
- def == other
43
- self.class == other.class and word == other.word
44
- end
45
-
46
58
  # Make a copy of this link marked as the head of a chain
47
59
  #
48
60
  # @return [self]
@@ -70,6 +82,8 @@ module Solargraph
70
82
  "#<#{self.class} - `#{self.desc}`>"
71
83
  end
72
84
 
85
+ include Logging
86
+
73
87
  protected
74
88
 
75
89
  # Mark whether this link is the head of a chain