solargraph 0.42.1 → 0.43.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/SPONSORS.md +0 -1
  4. data/lib/solargraph/api_map.rb +1 -1
  5. data/lib/solargraph/api_map/bundler_methods.rb +1 -6
  6. data/lib/solargraph/api_map/store.rb +17 -17
  7. data/lib/solargraph/bench.rb +1 -1
  8. data/lib/solargraph/complex_type.rb +4 -0
  9. data/lib/solargraph/complex_type/type_methods.rb +6 -3
  10. data/lib/solargraph/language_server/host.rb +2 -1
  11. data/lib/solargraph/language_server/host/dispatch.rb +2 -3
  12. data/lib/solargraph/language_server/transport/adapter.rb +1 -3
  13. data/lib/solargraph/library.rb +12 -5
  14. data/lib/solargraph/parser/legacy/node_chainer.rb +14 -1
  15. data/lib/solargraph/parser/legacy/node_methods.rb +9 -2
  16. data/lib/solargraph/parser/node_processor/base.rb +0 -3
  17. data/lib/solargraph/parser/rubyvm/node_chainer.rb +22 -1
  18. data/lib/solargraph/parser/rubyvm/node_methods.rb +5 -0
  19. data/lib/solargraph/pin/base.rb +1 -1
  20. data/lib/solargraph/pin/local_variable.rb +47 -1
  21. data/lib/solargraph/shell.rb +2 -2
  22. data/lib/solargraph/source.rb +0 -12
  23. data/lib/solargraph/source/chain.rb +1 -4
  24. data/lib/solargraph/source/chain/call.rb +3 -2
  25. data/lib/solargraph/source/chain/hash.rb +28 -0
  26. data/lib/solargraph/source_map.rb +2 -1
  27. data/lib/solargraph/source_map/clip.rb +1 -1
  28. data/lib/solargraph/type_checker.rb +11 -4
  29. data/lib/solargraph/version.rb +1 -1
  30. data/lib/solargraph/workspace.rb +1 -1
  31. data/lib/solargraph/yard_map.rb +16 -14
  32. data/lib/solargraph/yard_map/core_fills.rb +18 -1
  33. data/lib/yard-solargraph.rb +3 -0
  34. data/solargraph.gemspec +1 -1
  35. metadata +9 -9
  36. data/lib/solargraph/pin/localized.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1099281670bd5cacd480624f66b36d7ce8fb6b69b4486f080d0c0d6436c0f36c
4
- data.tar.gz: 4b1e028bc357204b953c31916845a7f12caefc07d733dc90cfb5bc142401f2d4
3
+ metadata.gz: 89a1a9d76094688ff7ebe1af3dec4452c379819408ffc458eddcd75f5b66d2eb
4
+ data.tar.gz: 9b03143ba896ccd2d3bd0a44c9a1e90f06e3ed757a47a08e6b8aaf2a9f56e62e
5
5
  SHA512:
6
- metadata.gz: 771c40ddff0a11757d1e9c4487802afc9f5b992840383f525643957d92516e735dc7328a8d23ed1ff4f0e55ceb09e3792eb24818758e4b217927bf4ba9708b5a
7
- data.tar.gz: 948101863fc7f3f167dee00139f07ccdf5711afd12125aa2a18b708ed4ba6edaffbc7ba0c1a7a7dd09ad51460a0827546841cf2dbcd223198247fd86d8b0841b
6
+ metadata.gz: a6d4e99416af96a8a11ca569d096f4ba1491155f3fde1efc734e59c73d1794007d0b7b1e936c1dfb248472df1fa679c7701866176de99119e8f6a0b8eef721d4
7
+ data.tar.gz: edd612d341e6c9bb421e4bbb8db04badc855e924eca469c47943c374813025810151aca1abdd0da13100597803799a50c7355a362b8a0ae65187c0760464e840
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.43.0 - July 25, 2021
2
+ - Correct arity checks when restarg precedes arg (#418)
3
+ - Improve the performance of catalog by 4 times (#457)
4
+ - Type checker validates duck type variables and params (#453)
5
+ - Kernel#raise exception type checker
6
+ - Pin::Base#inspect includes path
7
+ - Fix arity with combined restargs and kwrestargs (#396)
8
+
9
+ ## 0.42.4 - July 11, 2021
10
+ - Yardoc cache handling
11
+ - Fix required_paths when gemspec is used (#451)
12
+ - fix: yard stdout may break language client (#454)
13
+
14
+ ## 0.42.3 - June 14, 2021
15
+ - Require 'pathname' for Library
16
+
17
+ ## 0.42.2 - June 14, 2021
18
+ - Improve download-core command output
19
+ - Ignore missing requests to client responses
20
+ - Add automatically required gems to YardMap
21
+ - Use closures to identify local variables
22
+
1
23
  ## 0.42.1 - June 11, 2021
2
24
  - YardMap#change sets new directory (#445)
3
25
 
data/SPONSORS.md CHANGED
@@ -13,4 +13,3 @@ The following people and organizations provide funding or other resources. [Beco
13
13
  - Emily Strickland
14
14
  - Tom de Grunt
15
15
  - Akira Yamada
16
- - Jared White
@@ -138,7 +138,7 @@ module Solargraph
138
138
 
139
139
  # An array of pins based on Ruby keywords (`if`, `end`, etc.).
140
140
  #
141
- # @return [Array<Solargraph::Pin::Keyword>]
141
+ # @return [Enumerable<Solargraph::Pin::Keyword>]
142
142
  def keyword_pins
143
143
  store.pins_by_class(Pin::Keyword)
144
144
  end
@@ -9,7 +9,7 @@ module Solargraph
9
9
  # @param directory [String]
10
10
  # @return [Hash]
11
11
  def require_from_bundle directory
12
- @require_from_bundle ||= begin
12
+ begin
13
13
  Solargraph.logger.info "Loading gems for bundler/require"
14
14
  Documentor.specs_from_bundle(directory)
15
15
  rescue BundleNotFoundError => e
@@ -17,11 +17,6 @@ module Solargraph
17
17
  {}
18
18
  end
19
19
  end
20
-
21
- # @return [void]
22
- def reset_require_from_bundle
23
- @require_from_bundle = nil
24
- end
25
20
  end
26
21
  end
27
22
  end
@@ -5,10 +5,10 @@ require 'set'
5
5
  module Solargraph
6
6
  class ApiMap
7
7
  class Store
8
- # @return [Array<Solargraph::Pin::Base>]
8
+ # @return [Enumerable<Solargraph::Pin::Base>]
9
9
  attr_reader :pins
10
10
 
11
- # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param pins [Enumerable<Solargraph::Pin::Base>]
12
12
  def initialize pins = []
13
13
  @pins = pins
14
14
  index
@@ -16,7 +16,7 @@ module Solargraph
16
16
 
17
17
  # @param fqns [String]
18
18
  # @param visibility [Array<Symbol>]
19
- # @return [Array<Solargraph::Pin::Base>]
19
+ # @return [Enumerable<Solargraph::Pin::Base>]
20
20
  def get_constants fqns, visibility = [:public]
21
21
  namespace_children(fqns).select { |pin|
22
22
  !pin.name.empty? && (pin.is_a?(Pin::Namespace) || pin.is_a?(Pin::Constant)) && visibility.include?(pin.visibility)
@@ -26,7 +26,7 @@ module Solargraph
26
26
  # @param fqns [String]
27
27
  # @param scope [Symbol]
28
28
  # @param visibility [Array<Symbol>]
29
- # @return [Array<Solargraph::Pin::Base>]
29
+ # @return [Enumerable<Solargraph::Pin::Base>]
30
30
  def get_methods fqns, scope: :instance, visibility: [:public]
31
31
  namespace_children(fqns).select do |pin|
32
32
  pin.is_a?(Pin::Method) && pin.scope == scope && visibility.include?(pin.visibility)
@@ -61,14 +61,14 @@ module Solargraph
61
61
  end
62
62
 
63
63
  # @param path [String]
64
- # @return [Array<Solargraph::Pin::Base>]
64
+ # @return [Enumerable<Solargraph::Pin::Base>]
65
65
  def get_path_pins path
66
66
  path_pin_hash[path] || []
67
67
  end
68
68
 
69
69
  # @param fqns [String]
70
70
  # @param scope [Symbol] :class or :instance
71
- # @return [Array<Solargraph::Pin::Base>]
71
+ # @return [Enumerable<Solargraph::Pin::Base>]
72
72
  def get_instance_variables(fqns, scope = :instance)
73
73
  all_instance_variables.select { |pin|
74
74
  pin.binder.namespace == fqns && pin.binder.scope == scope
@@ -76,12 +76,12 @@ module Solargraph
76
76
  end
77
77
 
78
78
  # @param fqns [String]
79
- # @return [Array<Solargraph::Pin::Base>]
79
+ # @return [Enumerable<Solargraph::Pin::Base>]
80
80
  def get_class_variables(fqns)
81
81
  namespace_children(fqns).select{|pin| pin.is_a?(Pin::ClassVariable)}
82
82
  end
83
83
 
84
- # @return [Array<Solargraph::Pin::Base>]
84
+ # @return [Enumerable<Solargraph::Pin::Base>]
85
85
  def get_symbols
86
86
  symbols.uniq(&:name)
87
87
  end
@@ -97,12 +97,12 @@ module Solargraph
97
97
  @namespaces ||= Set.new
98
98
  end
99
99
 
100
- # @return [Array<Solargraph::Pin::Base>]
100
+ # @return [Enumerable<Solargraph::Pin::Base>]
101
101
  def namespace_pins
102
102
  pins_by_class(Solargraph::Pin::Namespace)
103
103
  end
104
104
 
105
- # @return [Array<Solargraph::Pin::Method>]
105
+ # @return [Enumerable<Solargraph::Pin::Method>]
106
106
  def method_pins
107
107
  pins_by_class(Solargraph::Pin::Method)
108
108
  end
@@ -131,7 +131,7 @@ module Solargraph
131
131
  end
132
132
  end
133
133
 
134
- # @return [Array<Pin::Block>]
134
+ # @return [Enumerable<Pin::Block>]
135
135
  def block_pins
136
136
  pins_by_class(Pin::Block)
137
137
  end
@@ -142,9 +142,9 @@ module Solargraph
142
142
  end
143
143
 
144
144
  # @param klass [Class]
145
- # @return [Array<Solargraph::Pin::Base>]
145
+ # @return [Enumerable<Solargraph::Pin::Base>]
146
146
  def pins_by_class klass
147
- @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
147
+ @pin_select_cache[klass] ||= @pin_class_hash.each_with_object(Set.new) { |(key, o), n| n.merge(o) if key <= klass }
148
148
  end
149
149
 
150
150
  private
@@ -171,7 +171,7 @@ module Solargraph
171
171
  end
172
172
  end
173
173
 
174
- # @return [Array<Solargraph::Pin::Symbol>]
174
+ # @return [Enumerable<Solargraph::Pin::Symbol>]
175
175
  def symbols
176
176
  pins_by_class(Pin::Symbol)
177
177
  end
@@ -193,7 +193,7 @@ module Solargraph
193
193
  end
194
194
 
195
195
  # @param name [String]
196
- # @return [Array<Solargraph::Pin::Base>]
196
+ # @return [Enumerable<Solargraph::Pin::Base>]
197
197
  def namespace_children name
198
198
  namespace_map[name] || []
199
199
  end
@@ -216,8 +216,8 @@ module Solargraph
216
216
  set = pins.to_set
217
217
  @pin_class_hash = set.classify(&:class).transform_values(&:to_a)
218
218
  @pin_select_cache = {}
219
- @namespace_map = set.classify(&:namespace).transform_values(&:to_a)
220
- @path_pin_hash = set.classify(&:path).transform_values(&:to_a)
219
+ @namespace_map = set.classify(&:namespace)
220
+ @path_pin_hash = set.classify(&:path)
221
221
  @namespaces = @path_pin_hash.keys.compact.to_set
222
222
  pins_by_class(Pin::Reference::Include).each do |pin|
223
223
  include_references[pin.namespace] ||= []
@@ -3,7 +3,7 @@
3
3
  require 'set'
4
4
 
5
5
  module Solargraph
6
- # A container of source maps and gem specs to be cataloged in an ApiMap.
6
+ # A container of source maps and workspace data to be cataloged in an ApiMap.
7
7
  #
8
8
  class Bench
9
9
  # @return [Set<SourceMap>]
@@ -51,6 +51,10 @@ module Solargraph
51
51
  def select &block
52
52
  @items.select &block
53
53
  end
54
+ def namespace
55
+ # cache this attr for high frequency call
56
+ @namespace ||= method_missing(:namespace).to_s
57
+ end
54
58
 
55
59
  def method_missing name, *args, &block
56
60
  return if @items.first.nil?
@@ -72,9 +72,12 @@ module Solargraph
72
72
 
73
73
  # @return [String]
74
74
  def namespace
75
- @namespace ||= 'Object' if duck_type?
76
- @namespace ||= 'NilClass' if nil_type?
77
- @namespace ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name
75
+ # if priority higher than ||=, old implements cause unnecessary check
76
+ @namespace ||= lambda do
77
+ return 'Object' if duck_type?
78
+ return 'NilClass' if nil_type?
79
+ return (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name
80
+ end.call
78
81
  end
79
82
 
80
83
  # @return [Symbol] :class or :instance
@@ -31,7 +31,7 @@ module Solargraph
31
31
  @cancel = []
32
32
  @buffer = String.new
33
33
  @stopped = true
34
- @next_request_id = 0
34
+ @next_request_id = 1
35
35
  @dynamic_capabilities = Set.new
36
36
  @registered_capabilities = Set.new
37
37
  end
@@ -113,6 +113,7 @@ module Solargraph
113
113
  requests.delete request['id']
114
114
  else
115
115
  logger.warn "Discarding client response to unrecognized message #{request['id']}"
116
+ nil
116
117
  end
117
118
  else
118
119
  logger.warn "Invalid message received."
@@ -41,7 +41,8 @@ module Solargraph
41
41
  result = explicit_library_for(uri) ||
42
42
  implicit_library_for(uri) ||
43
43
  generic_library_for(uri)
44
- result.attach sources.find(uri) if sources.include?(uri)
44
+ # previous library for already call attach. avoid call twice
45
+ # result.attach sources.find(uri) if sources.include?(uri)
45
46
  result
46
47
  end
47
48
 
@@ -82,7 +83,6 @@ module Solargraph
82
83
  libraries.each do |lib|
83
84
  if filename.start_with?(lib.workspace.directory)
84
85
  lib.attach sources.find(uri)
85
- lib.catalog
86
86
  return lib
87
87
  end
88
88
  end
@@ -98,7 +98,6 @@ module Solargraph
98
98
  # @return [Library]
99
99
  def generic_library_for uri
100
100
  generic_library.attach sources.find(uri)
101
- generic_library.catalog
102
101
  generic_library
103
102
  end
104
103
 
@@ -42,9 +42,7 @@ module Solargraph
42
42
  # @return [void]
43
43
  def process request
44
44
  message = @host.receive(request)
45
- message.send_response
46
- # tmp = @host.flush
47
- # write tmp unless tmp.empty?
45
+ message && message.send_response
48
46
  end
49
47
 
50
48
  def shutdown
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module Solargraph
4
6
  # A Library handles coordination between a Workspace and an ApiMap.
5
7
  #
@@ -46,7 +48,7 @@ module Solargraph
46
48
  # @return [void]
47
49
  def attach source
48
50
  mutex.synchronize do
49
- if @current && @current.filename != source.filename && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
51
+ if @current && (!source || @current.filename != source.filename) && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
50
52
  source_map_hash.delete @current.filename
51
53
  source_map_external_require_hash.delete @current.filename
52
54
  @external_requires = nil
@@ -54,7 +56,7 @@ module Solargraph
54
56
  end
55
57
  @current = source
56
58
  maybe_map @current
57
- api_map.catalog bench unless synchronized?
59
+ catalog_inlock
58
60
  end
59
61
  end
60
62
 
@@ -349,7 +351,7 @@ module Solargraph
349
351
  else
350
352
  args = line.split(':').map(&:strip)
351
353
  name = args.shift
352
- reporter = Diagnostics.reporter(name)
354
+ reporter = Diagnostics.reporter(name)
353
355
  raise DiagnosticsError, "Diagnostics reporter #{name} does not exist" if reporter.nil?
354
356
  repargs[reporter] ||= []
355
357
  repargs[reporter].concat args
@@ -366,12 +368,16 @@ module Solargraph
366
368
  # @return [void]
367
369
  def catalog
368
370
  mutex.synchronize do
369
- break if synchronized?
371
+ catalog_inlock
372
+ end
373
+ end
374
+
375
+ private def catalog_inlock
376
+ return if synchronized?
370
377
  logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
371
378
  api_map.catalog bench
372
379
  @synchronized = true
373
380
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
374
- end
375
381
  end
376
382
 
377
383
  def bench
@@ -511,6 +517,7 @@ module Solargraph
511
517
  end
512
518
 
513
519
  def maybe_map source
520
+ return unless source
514
521
  if source_map_hash.key?(source.filename)
515
522
  return if source_map_hash[source.filename].code == source.code &&
516
523
  source_map_hash[source.filename].source.synchronized? &&
@@ -102,13 +102,26 @@ module Solargraph
102
102
  result.concat generate_links(n.children[0])
103
103
  elsif n.type == :block_pass
104
104
  result.push Chain::BlockVariable.new("&#{n.children[0].children[0].to_s}")
105
+ elsif n.type == :hash
106
+ result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
105
107
  else
106
108
  lit = infer_literal_node_type(n)
107
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
109
+ # if lit == '::Hash'
110
+ # result.push Chain::Hash.new(lit, hash_is_splatted?(n))
111
+ # else
112
+ result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
113
+ # end
108
114
  end
109
115
  result
110
116
  end
111
117
 
118
+ def hash_is_splatted? node
119
+ return false unless Parser.is_ast_node?(node) && node.type == :hash
120
+ return false unless Parser.is_ast_node?(node.children.last) && node.children.last.type == :kwsplat
121
+ return false if Parser.is_ast_node?(node.children.last.children[0]) && node.children.last.children[0].type == :hash
122
+ true
123
+ end
124
+
112
125
  def block_passed? node
113
126
  node.children.last.is_a?(::Parser::AST::Node) && node.children.last.type == :block_pass
114
127
  end
@@ -97,8 +97,10 @@ module Solargraph
97
97
  end
98
98
 
99
99
  def convert_hash node
100
- return {} unless Parser.is_ast_node?(node) && node.type == :hash
101
- return convert_hash(node.children[0].children[0]) if splatted_hash?(node)
100
+ return {} unless Parser.is_ast_node?(node)
101
+ return convert_hash(node.children[0]) if node.type == :kwsplat
102
+ return convert_hash(node.children[0]) if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
103
+ return {} unless node.type == :hash
102
104
  result = {}
103
105
  node.children.each do |pair|
104
106
  result[pair.children[0].children[0]] = Solargraph::Parser.chain(pair.children[1])
@@ -124,9 +126,14 @@ module Solargraph
124
126
  end
125
127
 
126
128
  def splatted_call? node
129
+ return false unless Parser.is_ast_node?(node)
127
130
  Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat && node.children[0].children[0].type != :hash
128
131
  end
129
132
 
133
+ def any_splatted_call?(nodes)
134
+ nodes.any? { |n| splatted_call?(n) }
135
+ end
136
+
130
137
  # @todo Temporarily here for testing. Move to Solargraph::Parser.
131
138
  def call_nodes_from node
132
139
  return [] unless node.is_a?(::Parser::AST::Node)
@@ -4,9 +4,6 @@ module Solargraph
4
4
  module Parser
5
5
  module NodeProcessor
6
6
  class Base
7
- # @todo The base node processor should not include legacy node methods
8
- # include Legacy::NodeMethods
9
-
10
7
  # @return [Parser::AST::Node]
11
8
  attr_reader :node
12
9
 
@@ -97,13 +97,31 @@ module Solargraph
97
97
  result.concat generate_links(n.children[0])
98
98
  elsif n.type == :BLOCK_PASS
99
99
  result.push Chain::BlockVariable.new("&#{n.children[1].children[0].to_s}")
100
+ elsif n.type == :HASH
101
+ result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
100
102
  else
101
103
  lit = infer_literal_node_type(n)
102
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
104
+ if lit
105
+ if lit == '::Hash'
106
+ result.push Chain::Hash.new(lit, hash_is_splatted?(n))
107
+ else
108
+ result.push Chain::Literal.new(lit)
109
+ end
110
+ else
111
+ result.push Chain::Link.new
112
+ end
113
+ # result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
103
114
  end
104
115
  result
105
116
  end
106
117
 
118
+ def hash_is_splatted? node
119
+ return false unless Parser.is_ast_node?(node.children[0]) && node.children[0].type == :LIST
120
+ list = node.children[0].children
121
+ eol = list.rindex(&:nil?)
122
+ eol && Parser.is_ast_node?(list[eol + 1])
123
+ end
124
+
107
125
  def block_passed? node
108
126
  node.children.last.is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children.last.type == :BLOCK_PASS
109
127
  end
@@ -114,6 +132,9 @@ module Solargraph
114
132
  node.children[0..-2].map { |c| NodeChainer.chain(c) }
115
133
  elsif node.type == :SPLAT
116
134
  [NodeChainer.chain(node)]
135
+ elsif node.type == :ARGSPUSH
136
+ result = node_to_argchains(node.children[0])
137
+ result.push NodeChainer.chain(node.children[1]) if Parser.is_ast_node?(node.children[1])
117
138
  elsif node.type == :ARGSCAT
118
139
  result = node.children[0].children[0..-2].map { |c| NodeChainer.chain(c) }
119
140
  result.push NodeChainer.chain(node.children[1])
@@ -117,9 +117,14 @@ module Solargraph
117
117
  end
118
118
 
119
119
  def splatted_call? node
120
+ return false unless Parser.is_ast_node?(node)
120
121
  splatted_node?(node) && node.children[0].children[1].type != :HASH
121
122
  end
122
123
 
124
+ def any_splatted_call?(nodes)
125
+ nodes.any? { |n| splatted_call?(n) }
126
+ end
127
+
123
128
  def node? node
124
129
  node.is_a?(RubyVM::AbstractSyntaxTree::Node)
125
130
  end
@@ -218,7 +218,7 @@ module Solargraph
218
218
  end
219
219
 
220
220
  def inspect
221
- "#<#{self.class} at #{self.location.inspect}>"
221
+ "#<#{self.class} `#{self.path}` at #{self.location.inspect}>"
222
222
  end
223
223
 
224
224
  protected
@@ -3,7 +3,8 @@
3
3
  module Solargraph
4
4
  module Pin
5
5
  class LocalVariable < BaseVariable
6
- include Localized
6
+ # @return [Range]
7
+ attr_reader :presence
7
8
 
8
9
  def initialize assignment: nil, presence: nil, **splat
9
10
  super(**splat)
@@ -16,6 +17,51 @@ module Solargraph
16
17
  @presence = pin.presence
17
18
  true
18
19
  end
20
+
21
+ # @param other [Pin::Base] The caller's block
22
+ # @param position [Position, Array(Integer, Integer)] The caller's position
23
+ # @return [Boolean]
24
+ def visible_from?(other, position)
25
+ position = Position.normalize(position)
26
+ other.filename == filename &&
27
+ match_tags(other.full_context.tag, full_context.tag) &&
28
+ (other == closure ||
29
+ (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
30
+ ) &&
31
+ presence.contain?(position)
32
+ end
33
+
34
+ # @param other_loc [Location]
35
+ def visible_at?(other_closure, other_loc)
36
+ return true if location.filename == other_loc.filename &&
37
+ presence.include?(other_loc.range.start) &&
38
+ match_named_closure(other_closure, closure)
39
+ end
40
+
41
+ private
42
+
43
+ # @param tag1 [String]
44
+ # @param tag2 [String]
45
+ # @return [Boolean]
46
+ def match_tags tag1, tag2
47
+ # @todo This is an unfortunate hack made necessary by a discrepancy in
48
+ # how tags indicate the root namespace. The long-term solution is to
49
+ # standardize it, whether it's `Class<>`, an empty string, or
50
+ # something else.
51
+ tag1 == tag2 ||
52
+ (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
53
+ end
54
+
55
+ def match_named_closure needle, haystack
56
+ return true if needle == haystack
57
+ cursor = haystack
58
+ until cursor.nil?
59
+ return true if needle.path == cursor.path
60
+ return false if cursor.path && !cursor.path.empty?
61
+ cursor = cursor.closure
62
+ end
63
+ false
64
+ end
19
65
  end
20
66
  end
21
67
  end
@@ -76,12 +76,12 @@ module Solargraph
76
76
  ver = version || Solargraph::YardMap::CoreDocs.best_download
77
77
  if RUBY_VERSION != ver
78
78
  puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
79
- else
80
- puts "Downloading docs for #{ver}..."
81
79
  end
80
+ puts "Downloading docs for #{ver}..."
82
81
  Solargraph::YardMap::CoreDocs.download ver
83
82
  # Clear cached documentation if it exists
84
83
  FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
84
+ puts "Download complete."
85
85
  rescue ArgumentError => e
86
86
  STDERR.puts "ERROR: #{e.message}"
87
87
  STDERR.puts "Run `solargraph available-cores` for a list."
@@ -6,19 +6,15 @@ module Solargraph
6
6
  # A Ruby file that has been parsed into an AST.
7
7
  #
8
8
  class Source
9
- # autoload :FlawedBuilder, 'solargraph/source/flawed_builder'
10
9
  autoload :Updater, 'solargraph/source/updater'
11
10
  autoload :Change, 'solargraph/source/change'
12
11
  autoload :Mapper, 'solargraph/source/mapper'
13
- # autoload :NodeMethods, 'solargraph/source/node_methods'
14
12
  autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
15
13
  autoload :Cursor, 'solargraph/source/cursor'
16
14
  autoload :Chain, 'solargraph/source/chain'
17
15
  autoload :SourceChainer, 'solargraph/source/source_chainer'
18
- # autoload :NodeChainer, 'solargraph/source/node_chainer'
19
16
 
20
17
  include EncodingFixes
21
- # include NodeMethods
22
18
 
23
19
  # @return [String]
24
20
  attr_reader :filename
@@ -50,17 +46,9 @@ module Solargraph
50
46
  @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
51
47
  @parsed = true
52
48
  rescue Parser::SyntaxError, EncodingError => e
53
- # @todo 100% whitespace results in a nil node, so there's no reason to parse it.
54
- # We still need to determine whether the resulting node should be nil or a dummy
55
- # node with a location that encompasses the range.
56
- # @node, @comments = Source.parse_with_comments(@code.gsub(/[^\s]/, ' '), filename)
57
49
  @node = nil
58
50
  @comments = {}
59
51
  @parsed = false
60
- # rescue Exception => e
61
- # Solargraph.logger.warn "[#{e.class}] #{e.message}"
62
- # Solargraph.logger.warn e.backtrace.join("\n")
63
- # raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
64
52
  ensure
65
53
  @code.freeze
66
54
  end
@@ -21,6 +21,7 @@ module Solargraph
21
21
  autoload :Or, 'solargraph/source/chain/or'
22
22
  autoload :BlockVariable, 'solargraph/source/chain/block_variable'
23
23
  autoload :ZSuper, 'solargraph/source/chain/z_super'
24
+ autoload :Hash, 'solargraph/source/chain/hash'
24
25
 
25
26
  @@inference_stack = []
26
27
  @@inference_depth = 0
@@ -61,10 +62,6 @@ module Solargraph
61
62
  working_pin = name_pin
62
63
  links[0..-2].each do |link|
63
64
  pins = link.resolve(api_map, working_pin, locals)
64
- # Locals are only used when resolving the first link
65
- # @todo There's a problem here. Call links need to resolve arguments
66
- # that might refer to local variables.
67
- # locals = []
68
65
  type = infer_first_defined(pins, working_pin, api_map)
69
66
  return [] if type.undefined?
70
67
  working_pin = Pin::ProxyType.anonymous(type)
@@ -77,6 +77,7 @@ module Solargraph
77
77
  end
78
78
  if match
79
79
  type = extra_return_type(ol, context)
80
+ break if type
80
81
  type = ComplexType.try_parse(*ol.tag(:return).types).self_to(context.to_s).qualify(api_map, context.namespace) if ol.has_tag?(:return) && ol.tag(:return).types && !ol.tag(:return).types.empty? && (type.nil? || type.undefined?)
81
82
  type ||= ComplexType::UNDEFINED
82
83
  end
@@ -170,8 +171,8 @@ module Solargraph
170
171
  # @param context [ComplexType]
171
172
  # @return [ComplexType]
172
173
  def extra_return_type docstring, context
173
- if docstring.has_tag?(:return_single_parameter) && context.subtypes.one?
174
- return context.subtypes.first
174
+ if docstring.has_tag?(:return_single_parameter) #&& context.subtypes.one?
175
+ return context.subtypes.first || ComplexType::UNDEFINED
175
176
  elsif docstring.has_tag?(:return_value_parameter) && context.value_types.one?
176
177
  return context.value_types.first
177
178
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ class Hash < Literal
7
+ # @param type [String]
8
+ # @param splatted [Boolean]
9
+ def initialize type, splatted = false
10
+ super(type)
11
+ @splatted = splatted
12
+ end
13
+
14
+ def word
15
+ @word ||= "<#{@type}>"
16
+ end
17
+
18
+ def resolve api_map, name_pin, locals
19
+ [Pin::ProxyType.anonymous(@complex_type)]
20
+ end
21
+
22
+ def splatted?
23
+ @splatted
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -131,7 +131,8 @@ module Solargraph
131
131
  # @return [Array<Pin::LocalVariable>]
132
132
  def locals_at(location)
133
133
  return [] if location.filename != filename
134
- locals.select { |pin| pin.visible_at?(location) }
134
+ closure = locate_named_path_pin(location.range.start.line, location.range.start.character)
135
+ locals.select { |pin| pin.visible_at?(closure, location) }
135
136
  end
136
137
 
137
138
  class << self
@@ -213,7 +213,7 @@ module Solargraph
213
213
  result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
214
214
  result.concat api_map.get_methods('Kernel')
215
215
  # result.concat ApiMap.keywords
216
- result.concat api_map.keyword_pins
216
+ result.concat api_map.keyword_pins.to_a
217
217
  result.concat yielded_self_pins
218
218
  end
219
219
  end
@@ -148,6 +148,7 @@ module Solargraph
148
148
  all_variables.each do |pin|
149
149
  if pin.return_type.defined?
150
150
  declared = pin.typify(api_map)
151
+ next if declared.duck_type?
151
152
  if declared.defined?
152
153
  if rules.validate_tags?
153
154
  inferred = pin.probe(api_map)
@@ -416,7 +417,7 @@ module Solargraph
416
417
  end
417
418
  settled_kwargs = 0
418
419
  unless unchecked.empty?
419
- if Parser.is_ast_node?(unchecked.last.node) && splatted_call?(unchecked.last.node)
420
+ if any_splatted_call?(unchecked.map(&:node))
420
421
  settled_kwargs = pin.parameters.count(&:keyword?)
421
422
  else
422
423
  kwargs = convert_hash(unchecked.last.node)
@@ -431,6 +432,7 @@ module Solargraph
431
432
  kwargs.delete param.name.to_sym
432
433
  settled_kwargs += 1
433
434
  elsif param.decl == :kwarg
435
+ return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
434
436
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
435
437
  end
436
438
  end
@@ -450,12 +452,17 @@ module Solargraph
450
452
  if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
451
453
  return []
452
454
  end
453
- if req + add_params + 1 == unchecked.length && splatted_call?(unchecked.last.node) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
455
+ if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
454
456
  return []
455
457
  end
458
+ return [] if arguments.length - req == pin.parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
456
459
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
457
- elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
458
- return [Problem.new(location, "Not enough arguments to #{pin.path}")]
460
+ elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
461
+ # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
462
+ # See https://github.com/castwide/solargraph/issues/418
463
+ unless arguments.empty? && pin.path == 'Kernel#raise'
464
+ return [Problem.new(location, "Not enough arguments to #{pin.path}")]
465
+ end
459
466
  end
460
467
  []
461
468
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.42.1'
4
+ VERSION = '0.43.0'
5
5
  end
@@ -183,7 +183,7 @@ module Solargraph
183
183
  end
184
184
  end
185
185
  end
186
- result.concat config.require_paths
186
+ result.concat(config.require_paths.map { |p| File.join(directory, p) })
187
187
  result.push File.join(directory, 'lib') if result.empty?
188
188
  result
189
189
  end
@@ -156,12 +156,12 @@ module Solargraph
156
156
  @base_required ||= Set.new
157
157
  end
158
158
 
159
- private
160
-
161
159
  def directory
162
160
  @directory ||= ''
163
161
  end
164
162
 
163
+ private
164
+
165
165
  # @return [YardMap::Cache]
166
166
  def cache
167
167
  @cache ||= YardMap::Cache.new
@@ -190,11 +190,8 @@ module Solargraph
190
190
 
191
191
  # @return [void]
192
192
  def process_requires
193
- @gemset = if required.include?('bundler/require')
194
- require_from_bundle(directory)
195
- else
196
- {}
197
- end
193
+ @gemset = process_gemsets
194
+ required.merge @gemset.keys if required.include?('bundler/require')
198
195
  pins.replace core_pins
199
196
  unresolved_requires.clear
200
197
  stdlib_pins.clear
@@ -254,6 +251,11 @@ module Solargraph
254
251
  pins.concat environ.pins
255
252
  end
256
253
 
254
+ def process_gemsets
255
+ return {} if directory.empty? || !File.file?(File.join(directory, 'Gemfile'))
256
+ require_from_bundle(directory)
257
+ end
258
+
257
259
  # @param spec [Gem::Specification]
258
260
  # @return [void]
259
261
  def add_gem_dependencies spec
@@ -286,9 +288,6 @@ module Solargraph
286
288
  # @return [Array<Pin::Base>]
287
289
  def process_yardoc y, spec = nil
288
290
  return [] if y.nil?
289
- size = Dir.glob(File.join(y, '**', '*'))
290
- .map{ |f| File.size(f) }
291
- .inject(:+)
292
291
  if spec
293
292
  ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
294
293
  if File.file?(ser)
@@ -297,19 +296,24 @@ module Solargraph
297
296
  dump = file.read
298
297
  file.close
299
298
  begin
300
- return Marshal.load(dump)
299
+ result = Marshal.load(dump)
300
+ return result unless result.nil? || result.empty?
301
+ Solargraph.logger.warn "Empty cache for #{spec.name} #{spec.version}. Reloading"
301
302
  rescue StandardError => e
302
303
  Solargraph.logger.warn "Error loading pin cache: [#{e.class}] #{e.message}"
303
304
  File.unlink ser
304
305
  end
305
306
  end
306
307
  end
308
+ size = Dir.glob(File.join(y, '**', '*'))
309
+ .map{ |f| File.size(f) }
310
+ .inject(:+)
307
311
  if !size.nil? && size > 20_000_000
308
312
  Solargraph::Logging.logger.warn "Yardoc at #{y} is too large to process (#{size} bytes)"
309
313
  return []
310
314
  end
315
+ Solargraph.logger.info "Loading #{spec.name} #{spec.version} from #{y}"
311
316
  load_yardoc y
312
- Solargraph.logger.info "Loading #{spec.name} #{spec.version} from yardoc"
313
317
  result = Mapper.new(YARD::Registry.all, spec).map
314
318
  if spec
315
319
  ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
@@ -433,5 +437,3 @@ module Solargraph
433
437
  end
434
438
 
435
439
  Solargraph::YardMap::CoreDocs.require_minimum
436
- # Change YARD log IO to avoid sending unexpected messages to STDOUT
437
- YARD::Logger.instance.io = File.new(File::NULL, 'w')
@@ -82,6 +82,18 @@ module Solargraph
82
82
  Override.method_return('Class#allocate', 'self'),
83
83
  Override.method_return('Class.allocate', 'Class<Object>'),
84
84
 
85
+ Override.from_comment('Enumerable#detect', %(
86
+ @overload detect(&block)
87
+ @return_single_parameter
88
+ @overload detect()
89
+ @return [Enumerator]
90
+ )),
91
+ Override.from_comment('Enumerable#find', %(
92
+ @overload find(&block)
93
+ @return_single_parameter
94
+ @overload find()
95
+ @return [Enumerator]
96
+ )),
85
97
  Override.method_return('Enumerable#select', 'self'),
86
98
 
87
99
  Override.method_return('File.absolute_path', 'String'),
@@ -111,7 +123,12 @@ module Solargraph
111
123
  @param y [Numeric]
112
124
  @return [Numeric]
113
125
  )),
114
- Override.method_return('Integer#times', 'Enumerator', delete: [:overload]),
126
+ Override.from_comment('Integer#times', %(
127
+ @overload times(&block)
128
+ @return [Integer]
129
+ @overload times()
130
+ @return [Enumerator]
131
+ )),
115
132
 
116
133
  Override.method_return('Kernel#puts', 'nil'),
117
134
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  require 'yard'
4
4
 
5
+ # Change YARD log IO to avoid sending unexpected messages to STDOUT
6
+ YARD::Logger.instance.io = File.new(File::NULL, 'w')
7
+
5
8
  module Solargraph
6
9
  # A placeholder for the @!domain directive. It doesn't need to do anything
7
10
  # for yardocs. It's only used for Solargraph API maps.
data/solargraph.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.required_ruby_version = '>= 2.4'
21
21
 
22
- s.add_runtime_dependency 'backport', '~> 1.1'
22
+ s.add_runtime_dependency 'backport', '~> 1.2'
23
23
  s.add_runtime_dependency 'benchmark'
24
24
  s.add_runtime_dependency 'bundler', '>= 1.17.2'
25
25
  s.add_runtime_dependency 'diff-lcs', '~> 1.4'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.42.1
4
+ version: 0.43.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-11 00:00:00.000000000 Z
11
+ date: 2021-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: benchmark
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -477,7 +477,6 @@ files:
477
477
  - lib/solargraph/pin/keyword.rb
478
478
  - lib/solargraph/pin/keyword_param.rb
479
479
  - lib/solargraph/pin/local_variable.rb
480
- - lib/solargraph/pin/localized.rb
481
480
  - lib/solargraph/pin/method.rb
482
481
  - lib/solargraph/pin/method_alias.rb
483
482
  - lib/solargraph/pin/namespace.rb
@@ -503,6 +502,7 @@ files:
503
502
  - lib/solargraph/source/chain/class_variable.rb
504
503
  - lib/solargraph/source/chain/constant.rb
505
504
  - lib/solargraph/source/chain/global_variable.rb
505
+ - lib/solargraph/source/chain/hash.rb
506
506
  - lib/solargraph/source/chain/head.rb
507
507
  - lib/solargraph/source/chain/instance_variable.rb
508
508
  - lib/solargraph/source/chain/link.rb
@@ -554,7 +554,7 @@ homepage: http://solargraph.org
554
554
  licenses:
555
555
  - MIT
556
556
  metadata: {}
557
- post_install_message:
557
+ post_install_message:
558
558
  rdoc_options: []
559
559
  require_paths:
560
560
  - lib
@@ -569,8 +569,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
569
569
  - !ruby/object:Gem::Version
570
570
  version: '0'
571
571
  requirements: []
572
- rubygems_version: 3.1.2
573
- signing_key:
572
+ rubygems_version: 3.1.6
573
+ signing_key:
574
574
  specification_version: 4
575
575
  summary: A Ruby language server
576
576
  test_files: []
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Pin
5
- module Localized
6
- # @return [Range]
7
- attr_reader :presence
8
-
9
- # @param other [Pin::Base] The caller's block
10
- # @param position [Position, Array(Integer, Integer)] The caller's position
11
- # @return [Boolean]
12
- def visible_from?(other, position)
13
- position = Position.normalize(position)
14
- other.filename == filename &&
15
- match_tags(other.full_context.tag, full_context.tag) &&
16
- (other == closure ||
17
- (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
18
- ) &&
19
- presence.contain?(position)
20
- end
21
-
22
- # @param other_loc [Location]
23
- def visible_at?(other_loc)
24
- return false if location.filename != other_loc.filename
25
- presence.include?(other_loc.range.start)
26
- end
27
-
28
- private
29
-
30
- # @param tag1 [String]
31
- # @param tag2 [String]
32
- # @return [Boolean]
33
- def match_tags tag1, tag2
34
- # @todo This is an unfortunate hack made necessary by a discrepancy in
35
- # how tags indicate the root namespace. The long-term solution is to
36
- # standardize it, whether it's `Class<>`, an empty string, or
37
- # something else.
38
- tag1 == tag2 ||
39
- (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
40
- end
41
- end
42
- end
43
- end