solargraph 0.54.4 → 0.56.2

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 +62 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/index.rb +24 -16
  8. data/lib/solargraph/api_map/store.rb +48 -23
  9. data/lib/solargraph/api_map.rb +175 -77
  10. data/lib/solargraph/bench.rb +17 -1
  11. data/lib/solargraph/complex_type/type_methods.rb +6 -1
  12. data/lib/solargraph/complex_type/unique_type.rb +98 -9
  13. data/lib/solargraph/complex_type.rb +35 -6
  14. data/lib/solargraph/convention/base.rb +3 -3
  15. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
  16. data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
  17. data/lib/solargraph/convention/data_definition.rb +104 -0
  18. data/lib/solargraph/convention/gemspec.rb +2 -1
  19. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  20. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  21. data/lib/solargraph/convention/struct_definition.rb +141 -0
  22. data/lib/solargraph/convention.rb +5 -3
  23. data/lib/solargraph/doc_map.rb +277 -57
  24. data/lib/solargraph/gem_pins.rb +53 -37
  25. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  26. data/lib/solargraph/language_server/host.rb +12 -2
  27. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  28. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  29. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  30. data/lib/solargraph/library.rb +45 -17
  31. data/lib/solargraph/location.rb +21 -0
  32. data/lib/solargraph/logging.rb +1 -0
  33. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  34. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  35. data/lib/solargraph/parser/node_methods.rb +14 -0
  36. data/lib/solargraph/parser/node_processor/base.rb +9 -4
  37. data/lib/solargraph/parser/node_processor.rb +21 -8
  38. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  39. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  40. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  41. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  44. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  46. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  47. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  48. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  49. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  51. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  52. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  53. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  57. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  58. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  59. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  60. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  61. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  63. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  64. data/lib/solargraph/parser/region.rb +1 -1
  65. data/lib/solargraph/parser.rb +1 -0
  66. data/lib/solargraph/pin/base.rb +316 -28
  67. data/lib/solargraph/pin/base_variable.rb +16 -9
  68. data/lib/solargraph/pin/block.rb +2 -0
  69. data/lib/solargraph/pin/breakable.rb +9 -0
  70. data/lib/solargraph/pin/callable.rb +74 -3
  71. data/lib/solargraph/pin/closure.rb +18 -1
  72. data/lib/solargraph/pin/common.rb +5 -0
  73. data/lib/solargraph/pin/delegated_method.rb +20 -1
  74. data/lib/solargraph/pin/documenting.rb +16 -0
  75. data/lib/solargraph/pin/keyword.rb +7 -2
  76. data/lib/solargraph/pin/local_variable.rb +15 -6
  77. data/lib/solargraph/pin/method.rb +169 -43
  78. data/lib/solargraph/pin/namespace.rb +17 -9
  79. data/lib/solargraph/pin/parameter.rb +60 -11
  80. data/lib/solargraph/pin/proxy_type.rb +12 -6
  81. data/lib/solargraph/pin/reference/override.rb +10 -6
  82. data/lib/solargraph/pin/reference/require.rb +2 -2
  83. data/lib/solargraph/pin/signature.rb +42 -0
  84. data/lib/solargraph/pin/singleton.rb +1 -1
  85. data/lib/solargraph/pin/symbol.rb +3 -2
  86. data/lib/solargraph/pin/until.rb +18 -0
  87. data/lib/solargraph/pin/while.rb +18 -0
  88. data/lib/solargraph/pin.rb +4 -1
  89. data/lib/solargraph/pin_cache.rb +185 -0
  90. data/lib/solargraph/position.rb +9 -0
  91. data/lib/solargraph/range.rb +9 -0
  92. data/lib/solargraph/rbs_map/conversions.rb +221 -67
  93. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  94. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  95. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  96. data/lib/solargraph/rbs_map.rb +74 -17
  97. data/lib/solargraph/shell.rb +17 -18
  98. data/lib/solargraph/source/chain/array.rb +11 -7
  99. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  100. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  101. data/lib/solargraph/source/chain/call.rb +53 -23
  102. data/lib/solargraph/source/chain/constant.rb +1 -1
  103. data/lib/solargraph/source/chain/hash.rb +4 -3
  104. data/lib/solargraph/source/chain/head.rb +1 -1
  105. data/lib/solargraph/source/chain/if.rb +1 -1
  106. data/lib/solargraph/source/chain/link.rb +2 -0
  107. data/lib/solargraph/source/chain/literal.rb +22 -2
  108. data/lib/solargraph/source/chain/or.rb +1 -1
  109. data/lib/solargraph/source/chain/z_super.rb +1 -1
  110. data/lib/solargraph/source/chain.rb +78 -48
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source_map/clip.rb +3 -1
  113. data/lib/solargraph/source_map/mapper.rb +9 -5
  114. data/lib/solargraph/source_map.rb +0 -17
  115. data/lib/solargraph/type_checker/checks.rb +4 -0
  116. data/lib/solargraph/type_checker.rb +35 -8
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/views/_method.erb +10 -10
  119. data/lib/solargraph/views/_namespace.erb +3 -3
  120. data/lib/solargraph/views/document.erb +10 -10
  121. data/lib/solargraph/workspace/config.rb +1 -1
  122. data/lib/solargraph/workspace.rb +23 -5
  123. data/lib/solargraph/yard_map/helpers.rb +29 -1
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  127. data/lib/solargraph/yard_map/mapper.rb +4 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +7 -8
  130. data/lib/solargraph.rb +32 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +2 -1
  134. metadata +37 -9
  135. data/lib/solargraph/cache.rb +0 -77
@@ -5,23 +5,46 @@ module Solargraph
5
5
  # Ruby core pins
6
6
  #
7
7
  class CoreMap
8
- include Conversions
9
8
 
10
- def initialize
11
- cache = Cache.load('core.ser')
9
+ def resolved?
10
+ true
11
+ end
12
+
13
+ FILLS_DIRECTORY = File.join(File.dirname(__FILE__), '..', '..', '..', 'rbs', 'fills')
14
+
15
+ def initialize; end
16
+
17
+ def pins
18
+ return @pins if @pins
19
+
20
+ @pins = []
21
+ cache = PinCache.deserialize_core
12
22
  if cache
13
- pins.replace cache
23
+ @pins.replace cache
14
24
  else
15
- loader = RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false))
16
- RBS::Environment.from_loader(loader).resolve_type_names
17
- load_environment_to_pins(loader)
18
- pins.concat RbsMap::CoreFills::ALL
25
+ loader.add(path: Pathname(FILLS_DIRECTORY))
26
+ @pins = conversions.pins
27
+ @pins.concat RbsMap::CoreFills::ALL
19
28
  processed = ApiMap::Store.new(pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) }
20
- processed.each { |pin| pin.source = :rbs }
21
- pins.replace processed
29
+ @pins.replace processed
22
30
 
23
- Cache.save('core.ser', pins)
31
+ PinCache.serialize_core @pins
24
32
  end
33
+ @pins
34
+ end
35
+
36
+ def loader
37
+ @loader ||= RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false))
38
+ end
39
+
40
+ private
41
+
42
+ def loader
43
+ @loader ||= RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false))
44
+ end
45
+
46
+ def conversions
47
+ @conversions ||= Conversions.new(loader: loader)
25
48
  end
26
49
  end
27
50
  end
@@ -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,12 +118,15 @@ module Solargraph
64
118
  @@rbs_maps_hash[library] ||= RbsMap.new(library)
65
119
  end
66
120
 
67
- # @param gemspec [Gem::Specification]
68
- def self.from_gemspec(gemspec)
69
- RbsMap.new(gemspec.name, gemspec.version)
121
+ private
122
+
123
+ def loader
124
+ @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
70
125
  end
71
126
 
72
- private
127
+ def conversions
128
+ @conversions ||= Conversions.new(loader: loader)
129
+ end
73
130
 
74
131
  # @param loader [RBS::EnvironmentLoader]
75
132
  # @param library [String]
@@ -77,10 +134,10 @@ module Solargraph
77
134
  def add_library loader, library, version
78
135
  @resolved = if loader.has_library?(library: library, version: version)
79
136
  loader.add library: library, version: version
80
- Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
137
+ logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" }
81
138
  true
82
139
  else
83
- Solargraph.logger.info "#{short_name} failed to load library #{library}"
140
+ logger.info { "#{short_name} did not find data for library #{library}:#{version}" }
84
141
  false
85
142
  end
86
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"
@@ -117,18 +118,17 @@ module Solargraph
117
118
  raise ArgumentError, 'No gems specified.' if gems.empty?
118
119
  gems.each do |gem|
119
120
  if gem == 'core'
120
- Cache.uncache("core.ser")
121
+ PinCache.uncache_core
121
122
  next
122
123
  end
123
124
 
124
125
  if gem == 'stdlib'
125
- Cache.uncache("stdlib")
126
+ PinCache.uncache_stdlib
126
127
  next
127
128
  end
128
129
 
129
130
  spec = Gem::Specification.find_by_name(gem)
130
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.ser")
131
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.yardoc")
131
+ PinCache.uncache_gem(spec, out: $stdout)
132
132
  end
133
133
  end
134
134
 
@@ -137,15 +137,18 @@ module Solargraph
137
137
  # @param names [Array<String>]
138
138
  # @return [void]
139
139
  def gems *names
140
+ api_map = ApiMap.load('.')
140
141
  if names.empty?
141
- 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."
142
144
  else
143
145
  names.each do |name|
144
146
  spec = Gem::Specification.find_by_name(*name.split('='))
145
- do_cache spec
147
+ do_cache spec, api_map
146
148
  rescue Gem::MissingSpecError
147
149
  warn "Gem '#{name}' not found"
148
150
  end
151
+ STDERR.puts "Documentation cached for #{names.count} gems."
149
152
  end
150
153
  end
151
154
 
@@ -206,6 +209,7 @@ module Solargraph
206
209
  # @return [void]
207
210
  def scan
208
211
  directory = File.realpath(options[:directory])
212
+ # @type [Solargraph::ApiMap, nil]
209
213
  api_map = nil
210
214
  time = Benchmark.measure {
211
215
  api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
@@ -255,15 +259,10 @@ module Solargraph
255
259
 
256
260
  # @param gemspec [Gem::Specification]
257
261
  # @return [void]
258
- def do_cache gemspec
259
- cached = Yardoc.cached?(gemspec)
260
- if cached && !options.rebuild
261
- puts "Cache already exists for #{gemspec.name} #{gemspec.version}"
262
- else
263
- puts "#{cached ? 'Rebuilding' : 'Caching'} gem documentation for #{gemspec.name} #{gemspec.version}"
264
- pins = GemPins.build(gemspec)
265
- Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
266
- 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)
267
266
  end
268
267
  end
269
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
 
@@ -17,15 +18,18 @@ module Solargraph
17
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,10 +24,12 @@ 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
@@ -35,20 +45,21 @@ module Solargraph
35
45
  end
36
46
 
37
47
  # @param api_map [ApiMap]
38
- # @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
39
49
  # @param locals [::Array<Pin::LocalVariable>]
40
50
  def resolve api_map, name_pin, locals
41
51
  return super_pins(api_map, name_pin) if word == 'super'
42
52
  return yield_pins(api_map, name_pin) if word == 'yield'
43
53
  found = if head?
44
- locals.select { |p| p.name == word }
54
+ api_map.visible_pins(locals, word, name_pin, location)
45
55
  else
46
56
  []
47
57
  end
48
58
  return inferred_pins(found, api_map, name_pin, locals) unless found.empty?
49
59
  pins = name_pin.binder.each_unique_type.flat_map do |context|
50
- ns = context.namespace == '' ? '' : context.namespace_type.tag
51
- api_map.get_method_stack(ns, 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
52
63
  end
53
64
  return [] if pins.empty?
54
65
  inferred_pins(pins, api_map, name_pin, locals)
@@ -72,7 +83,9 @@ module Solargraph
72
83
  # use it. If we didn't pass a block, the logic below will
73
84
  # reject it regardless
74
85
 
75
- 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]
76
89
  new_signature_pin = nil
77
90
  sorted_overloads.each do |ol|
78
91
  next unless ol.arity_matches?(arguments, with_block?)
@@ -85,13 +98,8 @@ module Solargraph
85
98
  match = ol.parameters.any?(&:restarg?)
86
99
  break
87
100
  end
88
- atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context), locals)
89
- # make sure we get types from up the method
90
- # inheritance chain if we don't have them on this pin
91
- ptype = param.typify api_map
92
- # @todo Weak type comparison
93
- # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
94
- unless ptype.undefined? || atype.name == ptype.name || ptype.any? { |current_ptype| api_map.super_and_sub?(current_ptype.name, atype.name) } || ptype.generic? || param.restarg?
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?
95
103
  match = false
96
104
  break
97
105
  end
@@ -106,9 +114,30 @@ module Solargraph
106
114
  blocktype = block_call_type(api_map, name_pin, locals)
107
115
  end
108
116
  end
117
+ # @type new_signature_pin [Pin::Signature]
109
118
  new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
110
119
  new_return_type = new_signature_pin.return_type
111
- 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?
112
141
  type ||= ComplexType::UNDEFINED
113
142
  end
114
143
  break if type.defined?
@@ -124,13 +153,14 @@ module Solargraph
124
153
  end
125
154
  p
126
155
  end
127
- result.map do |pin|
128
- if pin.path == 'Class#new' && name_pin.context.tag != 'Class'
129
- 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
130
160
  pin.proxy(reduced_context)
131
161
  else
132
162
  next pin if pin.return_type.undefined?
133
- selfy = pin.return_type.self_to_type(name_pin.context)
163
+ selfy = pin.return_type.self_to_type(name_pin.binder)
134
164
  selfy == pin.return_type ? pin : pin.proxy(selfy)
135
165
  end
136
166
  end
@@ -152,7 +182,7 @@ module Solargraph
152
182
  result = inner_process_macro(pin, macro, api_map, context, locals)
153
183
  return result unless result.return_type.undefined?
154
184
  end
155
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
185
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
156
186
  end
157
187
 
158
188
  # @param pin [Pin::Method]
@@ -167,7 +197,7 @@ module Solargraph
167
197
  result = inner_process_macro(pin, macro, api_map, context, locals)
168
198
  return result unless result.return_type.undefined?
169
199
  end
170
- Pin::ProxyType.anonymous ComplexType::UNDEFINED
200
+ Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
171
201
  end
172
202
 
173
203
  # @param pin [Pin::Base]
@@ -177,7 +207,7 @@ module Solargraph
177
207
  # @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
178
208
  # @return [Pin::ProxyType]
179
209
  def inner_process_macro pin, macro, api_map, context, locals
180
- 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) }
181
211
  txt = macro.tag.text.clone
182
212
  if txt.empty? && macro.tag.name
183
213
  named = api_map.named_macro(macro.tag.name)
@@ -191,9 +221,9 @@ module Solargraph
191
221
  docstring = Solargraph::Source.parse_docstring(txt).to_docstring
192
222
  tag = docstring.tag(:return)
193
223
  unless tag.nil? || tag.types.nil?
194
- return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types))
224
+ return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
195
225
  end
196
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
226
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
197
227
  end
198
228
 
199
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,9 +5,10 @@ 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
 
@@ -21,7 +22,7 @@ module Solargraph
21
22
  end
22
23
 
23
24
  def resolve api_map, name_pin, locals
24
- [Pin::ProxyType.anonymous(@complex_type)]
25
+ [Pin::ProxyType.anonymous(@complex_type, source: :chain)]
25
26
  end
26
27
 
27
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
@@ -20,7 +20,7 @@ module Solargraph
20
20
 
21
21
  def resolve api_map, name_pin, locals
22
22
  types = @links.map { |link| link.infer(api_map, name_pin, locals) }
23
- [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)]
24
24
  end
25
25
  end
26
26
  end
@@ -82,6 +82,8 @@ module Solargraph
82
82
  "#<#{self.class} - `#{self.desc}`>"
83
83
  end
84
84
 
85
+ include Logging
86
+
85
87
  protected
86
88
 
87
89
  # Mark whether this link is the head of a chain
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'parser'
4
+
3
5
  module Solargraph
4
6
  class Source
5
7
  class Chain
@@ -8,9 +10,22 @@ module Solargraph
8
10
  @word ||= "<#{@type}>"
9
11
  end
10
12
 
13
+ attr_reader :value
14
+
11
15
  # @param type [String]
12
- def initialize type
16
+ # @param node [Parser::AST::Node, Object]
17
+ def initialize type, node
18
+ if node.is_a?(::Parser::AST::Node)
19
+ if node.type == :true
20
+ @value = true
21
+ elsif node.type == :false
22
+ @value = false
23
+ elsif [:int, :sym].include?(node.type)
24
+ @value = node.children.first
25
+ end
26
+ end
13
27
  @type = type
28
+ @literal_type = ComplexType.try_parse(@value.inspect)
14
29
  @complex_type = ComplexType.try_parse(type)
15
30
  end
16
31
 
@@ -20,7 +35,12 @@ module Solargraph
20
35
  end
21
36
 
22
37
  def resolve api_map, name_pin, locals
23
- [Pin::ProxyType.anonymous(@complex_type)]
38
+ if api_map.super_and_sub?(@complex_type.name, @literal_type.name)
39
+ [Pin::ProxyType.anonymous(@literal_type, source: :chain)]
40
+ else
41
+ # we don't support this value as a literal type
42
+ [Pin::ProxyType.anonymous(@complex_type, source: :chain)]
43
+ end
24
44
  end
25
45
  end
26
46
  end
@@ -15,7 +15,7 @@ module Solargraph
15
15
 
16
16
  def resolve api_map, name_pin, locals
17
17
  types = @links.map { |link| link.infer(api_map, name_pin, locals) }
18
- [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq))]
18
+ [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq), source: :chain)]
19
19
  end
20
20
  end
21
21
  end