solargraph 0.39.17 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -7
  3. data/CHANGELOG.md +984 -0
  4. data/SPONSORS.md +1 -0
  5. data/lib/solargraph.rb +2 -4
  6. data/lib/solargraph/api_map.rb +61 -64
  7. data/lib/solargraph/api_map/cache.rb +2 -2
  8. data/lib/solargraph/api_map/store.rb +3 -7
  9. data/lib/solargraph/{bundle.rb → bench.rb} +6 -2
  10. data/lib/solargraph/compat.rb +14 -0
  11. data/lib/solargraph/convention.rb +13 -4
  12. data/lib/solargraph/convention/base.rb +16 -8
  13. data/lib/solargraph/convention/gemfile.rb +2 -5
  14. data/lib/solargraph/convention/gemspec.rb +3 -6
  15. data/lib/solargraph/convention/rspec.rb +3 -6
  16. data/lib/solargraph/environ.rb +11 -6
  17. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +6 -1
  18. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  19. data/lib/solargraph/library.rb +5 -5
  20. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +1 -1
  21. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +34 -22
  22. data/lib/solargraph/parser/node_processor/base.rb +3 -0
  23. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +1 -1
  24. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +1 -1
  25. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +38 -28
  26. data/lib/solargraph/pin.rb +0 -3
  27. data/lib/solargraph/pin/common.rb +1 -1
  28. data/lib/solargraph/pin/conversions.rb +1 -1
  29. data/lib/solargraph/pin/documenting.rb +3 -9
  30. data/lib/solargraph/pin/method.rb +141 -7
  31. data/lib/solargraph/pin/method_alias.rb +1 -1
  32. data/lib/solargraph/position.rb +2 -14
  33. data/lib/solargraph/source.rb +10 -6
  34. data/lib/solargraph/source/chain.rb +3 -3
  35. data/lib/solargraph/source_map.rb +4 -1
  36. data/lib/solargraph/source_map/clip.rb +3 -2
  37. data/lib/solargraph/source_map/mapper.rb +10 -6
  38. data/lib/solargraph/type_checker.rb +35 -39
  39. data/lib/solargraph/type_checker/param_def.rb +1 -1
  40. data/lib/solargraph/version.rb +1 -1
  41. data/lib/solargraph/yard_map.rb +38 -47
  42. data/lib/solargraph/yard_map/core_fills.rb +185 -0
  43. data/lib/solargraph/yard_map/helpers.rb +16 -0
  44. data/lib/solargraph/yard_map/mapper.rb +11 -5
  45. data/lib/solargraph/{pin/yard_pin/constant.rb → yard_map/mapper/to_constant.rb} +6 -6
  46. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
  47. data/lib/solargraph/{pin/yard_pin/namespace.rb → yard_map/mapper/to_namespace.rb} +6 -6
  48. data/lib/solargraph/yard_map/rdoc_to_yard.rb +1 -1
  49. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
  50. data/lib/solargraph/{pin/yard_pin/method.rb → yard_map/to_method.rb} +29 -30
  51. data/solargraph.gemspec +4 -4
  52. metadata +20 -34
  53. data/lib/solargraph/core_fills.rb +0 -164
  54. data/lib/solargraph/pin/attribute.rb +0 -49
  55. data/lib/solargraph/pin/base_method.rb +0 -149
  56. data/lib/solargraph/pin/yard_pin.rb +0 -12
  57. data/lib/solargraph/pin/yard_pin/yard_mixin.rb +0 -20
  58. data/lib/solargraph/stdlib_fills.rb +0 -40
  59. data/travis-bundler.rb +0 -11
@@ -9,10 +9,8 @@ module Solargraph
9
9
  autoload :Common, 'solargraph/pin/common'
10
10
  autoload :Conversions, 'solargraph/pin/conversions'
11
11
  autoload :Base, 'solargraph/pin/base'
12
- autoload :BaseMethod, 'solargraph/pin/base_method'
13
12
  autoload :Method, 'solargraph/pin/method'
14
13
  autoload :MethodAlias, 'solargraph/pin/method_alias'
15
- autoload :Attribute, 'solargraph/pin/attribute'
16
14
  autoload :BaseVariable, 'solargraph/pin/base_variable'
17
15
  autoload :InstanceVariable, 'solargraph/pin/instance_variable'
18
16
  autoload :ClassVariable, 'solargraph/pin/class_variable'
@@ -30,7 +28,6 @@ module Solargraph
30
28
  autoload :Localized, 'solargraph/pin/localized'
31
29
  autoload :ProxyType, 'solargraph/pin/proxy_type'
32
30
  autoload :DuckMethod, 'solargraph/pin/duck_method'
33
- autoload :YardPin, 'solargraph/pin/yard_pin'
34
31
  autoload :Singleton, 'solargraph/pin/singleton'
35
32
  autoload :KeywordParam, 'solargraph/pin/keyword_param'
36
33
 
@@ -54,7 +54,7 @@ module Solargraph
54
54
  until here.nil?
55
55
  if here.is_a?(Pin::Namespace)
56
56
  return here.return_type
57
- elsif here.is_a?(Pin::BaseMethod)
57
+ elsif here.is_a?(Pin::Method)
58
58
  if here.scope == :instance
59
59
  return ComplexType.try_parse(here.context.namespace)
60
60
  else
@@ -47,7 +47,7 @@ module Solargraph
47
47
  # This property is not cached in an instance variable because it can
48
48
  # change when pins get proxied.
49
49
  detail = String.new
50
- detail += "(#{parameters.map(&:full).join(', ')}) " unless !is_a?(Pin::BaseMethod) || parameters.empty?
50
+ detail += "(#{parameters.map(&:full).join(', ')}) " unless !is_a?(Pin::Method) || parameters.empty?
51
51
  detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined?
52
52
  detail.strip!
53
53
  return nil if detail.empty?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'maruku'
3
+ require 'kramdown'
4
+ require 'kramdown-parser-gfm'
4
5
  require 'yard'
5
6
  require 'reverse_markdown'
6
7
  require 'solargraph/converters/dl'
@@ -8,9 +9,6 @@ require 'solargraph/converters/dt'
8
9
  require 'solargraph/converters/dd'
9
10
  require 'solargraph/converters/misc'
10
11
 
11
- # HACK: Setting :html_parser through `Maruku.new` does not work
12
- MaRuKu::Globals[:html_parser] = 'nokogiri'
13
-
14
12
  module Solargraph
15
13
  module Pin
16
14
  # A module to add the Pin::Base#documentation method.
@@ -41,11 +39,7 @@ module Solargraph
41
39
 
42
40
  def to_s
43
41
  return "\n```ruby\n#{@plaintext}#{@plaintext.end_with?("\n") ? '' : "\n"}```\n\n" if code?
44
- ReverseMarkdown.convert unescape_brackets(Maruku.new(escape_brackets(@plaintext), on_error: :raise).to_html)
45
- rescue MaRuKu::Exception
46
- # Maruku exceptions usually indicate that the documentation is in
47
- # RDoc syntax.
48
- ReverseMarkdown.convert YARD::Templates::Helpers::Markup::RDocMarkup.new(@plaintext).to_html
42
+ ReverseMarkdown.convert unescape_brackets(Kramdown::Document.new(escape_brackets(@plaintext), input: 'GFM').to_html)
49
43
  end
50
44
 
51
45
  private
@@ -2,18 +2,29 @@
2
2
 
3
3
  module Solargraph
4
4
  module Pin
5
- class Method < BaseMethod
5
+ # The base class for method and attribute pins.
6
+ #
7
+ class Method < Closure
6
8
  include Solargraph::Parser::NodeMethods
7
9
 
8
10
  # @return [Array<Pin::Parameter>]
9
11
  attr_reader :parameters
10
12
 
11
- # @param args [Array<String>]
12
- # @param node [Parser::AST::Node, nil]
13
- def initialize parameters: [], node: nil, **splat
13
+ # @return [::Symbol] :public, :private, or :protected
14
+ attr_reader :visibility
15
+
16
+ # @return [Parser::AST::Node]
17
+ attr_reader :node
18
+
19
+ # @param visibility [::Symbol] :public, :protected, or :private
20
+ # @param explicit [Boolean]
21
+ def initialize visibility: :public, explicit: true, parameters: [], node: nil, attribute: false, **splat
14
22
  super(**splat)
23
+ @visibility = visibility
24
+ @explicit = explicit
15
25
  @parameters = parameters
16
26
  @node = node
27
+ @attribute = attribute
17
28
  end
18
29
 
19
30
  # @return [Array<String>]
@@ -22,11 +33,70 @@ module Solargraph
22
33
  end
23
34
 
24
35
  def completion_item_kind
25
- Solargraph::LanguageServer::CompletionItemKinds::METHOD
36
+ attribute? ? Solargraph::LanguageServer::CompletionItemKinds::PROPERTY : Solargraph::LanguageServer::CompletionItemKinds::METHOD
26
37
  end
27
38
 
28
39
  def symbol_kind
29
- LanguageServer::SymbolKinds::METHOD
40
+ attribute? ? Solargraph::LanguageServer::SymbolKinds::PROPERTY : LanguageServer::SymbolKinds::METHOD
41
+ end
42
+
43
+ def return_type
44
+ @return_type ||= generate_complex_type
45
+ end
46
+
47
+ def path
48
+ @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}"
49
+ end
50
+
51
+ def typify api_map
52
+ decl = super
53
+ return decl unless decl.undefined?
54
+ type = see_reference(api_map) || typify_from_super(api_map)
55
+ return type.qualify(api_map, namespace) unless type.nil?
56
+ name.end_with?('?') ? ComplexType::BOOLEAN : ComplexType::UNDEFINED
57
+ end
58
+
59
+ def documentation
60
+ if @documentation.nil?
61
+ @documentation ||= super || ''
62
+ param_tags = docstring.tags(:param)
63
+ unless param_tags.nil? or param_tags.empty?
64
+ @documentation += "\n\n" unless @documentation.empty?
65
+ @documentation += "Params:\n"
66
+ lines = []
67
+ param_tags.each do |p|
68
+ l = "* #{p.name}"
69
+ l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty?
70
+ l += " #{p.text}"
71
+ lines.push l
72
+ end
73
+ @documentation += lines.join("\n")
74
+ end
75
+ return_tags = docstring.tags(:return)
76
+ unless return_tags.empty?
77
+ @documentation += "\n\n" unless @documentation.empty?
78
+ @documentation += "Returns:\n"
79
+ lines = []
80
+ return_tags.each do |r|
81
+ l = "*"
82
+ l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty?
83
+ l += " #{r.text}"
84
+ lines.push l
85
+ end
86
+ @documentation += lines.join("\n")
87
+ end
88
+ @documentation += "\n\n" unless @documentation.empty?
89
+ @documentation += "Visibility: #{visibility}"
90
+ end
91
+ @documentation.to_s
92
+ end
93
+
94
+ def explicit?
95
+ @explicit
96
+ end
97
+
98
+ def attribute?
99
+ @attribute
30
100
  end
31
101
 
32
102
  def nearly? other
@@ -37,7 +107,7 @@ module Solargraph
37
107
  end
38
108
 
39
109
  def probe api_map
40
- infer_from_return_nodes(api_map)
110
+ attribute? ? infer_from_iv(api_map) : infer_from_return_nodes(api_map)
41
111
  end
42
112
 
43
113
  def try_merge! pin
@@ -70,6 +140,57 @@ module Solargraph
70
140
 
71
141
  private
72
142
 
143
+ # @return [ComplexType]
144
+ def generate_complex_type
145
+ tags = docstring.tags(:return).map(&:types).flatten.reject(&:nil?)
146
+ return ComplexType::UNDEFINED if tags.empty?
147
+ ComplexType.try_parse *tags
148
+ end
149
+
150
+ # @param api_map [ApiMap]
151
+ # @return [ComplexType, nil]
152
+ def see_reference api_map
153
+ docstring.ref_tags.each do |ref|
154
+ next unless ref.tag_name == 'return' && ref.owner
155
+ result = resolve_reference(ref.owner.to_s, api_map)
156
+ return result unless result.nil?
157
+ end
158
+ match = comments.match(/^[ \t]*\(see (.*)\)/m)
159
+ return nil if match.nil?
160
+ resolve_reference match[1], api_map
161
+ end
162
+
163
+ # @param api_map [ApiMap]
164
+ # @return [ComplexType, nil]
165
+ def typify_from_super api_map
166
+ stack = api_map.get_method_stack(namespace, name, scope: scope).reject { |pin| pin.path == path }
167
+ return nil if stack.empty?
168
+ stack.each do |pin|
169
+ return pin.return_type unless pin.return_type.undefined?
170
+ end
171
+ nil
172
+ end
173
+
174
+ # @param ref [String]
175
+ # @param api_map [ApiMap]
176
+ # @return [ComplexType]
177
+ def resolve_reference ref, api_map
178
+ parts = ref.split(/[\.#]/)
179
+ if parts.first.empty? || parts.one?
180
+ path = "#{namespace}#{ref}"
181
+ else
182
+ fqns = api_map.qualify(parts.first, namespace)
183
+ return ComplexType::UNDEFINED if fqns.nil?
184
+ path = fqns + ref[parts.first.length] + parts.last
185
+ end
186
+ pins = api_map.get_path_pins(path)
187
+ pins.each do |pin|
188
+ type = pin.typify(api_map)
189
+ return type unless type.undefined?
190
+ end
191
+ nil
192
+ end
193
+
73
194
  # @return [Parser::AST::Node, nil]
74
195
  def method_body_node
75
196
  return nil if node.nil?
@@ -106,6 +227,19 @@ module Solargraph
106
227
  return ComplexType::UNDEFINED if result.empty?
107
228
  ComplexType.try_parse(*result.map(&:tag).uniq)
108
229
  end
230
+
231
+ def infer_from_iv api_map
232
+ types = []
233
+ varname = "@#{name.gsub(/=$/, '')}"
234
+ pins = api_map.get_instance_variable_pins(binder.namespace, binder.scope).select { |iv| iv.name == varname }
235
+ pins.each do |pin|
236
+ type = pin.typify(api_map)
237
+ type = pin.probe(api_map) if type.undefined?
238
+ types.push type if type.defined?
239
+ end
240
+ return ComplexType::UNDEFINED if types.empty?
241
+ ComplexType.try_parse(*types.map(&:tag).uniq)
242
+ end
109
243
  end
110
244
  end
111
245
  end
@@ -6,7 +6,7 @@ module Solargraph
6
6
  # examples that defer mapping are aliases for superclass methods or
7
7
  # methods from included modules.
8
8
  #
9
- class MethodAlias < BaseMethod
9
+ class MethodAlias < Method
10
10
  # @return [::Symbol]
11
11
  attr_reader :scope
12
12
 
@@ -40,20 +40,8 @@ module Solargraph
40
40
  # @param position [Position]
41
41
  # @return [Integer]
42
42
  def self.to_offset text, position
43
- result = 0
44
- feed = 0
45
- line = position.line
46
- column = position.character
47
- text.lines.each do |l|
48
- line_length = l.length
49
- if feed == line
50
- result += column
51
- break
52
- end
53
- result += line_length
54
- feed += 1
55
- end
56
- result
43
+ return 0 if text.empty?
44
+ text.lines[0...position.line].sum(&:length) + position.character
57
45
  end
58
46
 
59
47
  # Get a numeric offset for the specified text and a position identified
@@ -284,7 +284,7 @@ module Solargraph
284
284
 
285
285
  FOLDING_NODE_TYPES = if Parser.rubyvm?
286
286
  %i[
287
- CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR
287
+ CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
288
288
  ].freeze
289
289
  else
290
290
  %i[
@@ -345,18 +345,22 @@ module Solargraph
345
345
 
346
346
  # @param top [Parser::AST::Node]
347
347
  # @param result [Array<Range>]
348
+ # @param parent [Symbol]
348
349
  # @return [void]
349
- def inner_folding_ranges top, result = []
350
+ def inner_folding_ranges top, result = [], parent = nil
350
351
  # return unless top.is_a?(::Parser::AST::Node)
351
352
  return unless Parser.is_ast_node?(top)
352
353
  if FOLDING_NODE_TYPES.include?(top.type)
353
- range = Range.from_node(top)
354
- if result.empty? || range.start.line > result.last.start.line
355
- result.push range unless range.ending.line - range.start.line < 2
354
+ # @todo Smelly exception for hash's first-level array in RubyVM
355
+ unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
356
+ range = Range.from_node(top)
357
+ if result.empty? || range.start.line > result.last.start.line
358
+ result.push range unless range.ending.line - range.start.line < 2
359
+ end
356
360
  end
357
361
  end
358
362
  top.children.each do |child|
359
- inner_folding_ranges(child, result)
363
+ inner_folding_ranges(child, result, top.type)
360
364
  end
361
365
  end
362
366
 
@@ -119,12 +119,12 @@ module Solargraph
119
119
  @@inference_stack.pop
120
120
  if type.defined?
121
121
  possibles.push type
122
- break if pin.is_a?(Pin::BaseMethod)
122
+ break if pin.is_a?(Pin::Method)
123
123
  end
124
124
  end
125
125
  if possibles.empty?
126
126
  # Limit method inference recursion
127
- return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::BaseMethod)
127
+ return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
128
128
  @@inference_depth += 1
129
129
  pins.each do |pin|
130
130
  # Avoid infinite recursion
@@ -134,7 +134,7 @@ module Solargraph
134
134
  @@inference_stack.pop
135
135
  if type.defined?
136
136
  possibles.push type
137
- break if pin.is_a?(Pin::BaseMethod)
137
+ break if pin.is_a?(Pin::Method)
138
138
  end
139
139
  end
140
140
  @@inference_depth -= 1
@@ -30,7 +30,7 @@ module Solargraph
30
30
  @source = source.dup
31
31
  @pins = pins
32
32
  @locals = locals
33
- environ.merge Convention.for(source)
33
+ environ.merge Convention.for_local(self) unless filename.nil?
34
34
  @pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
35
35
  @pin_select_cache = {}
36
36
  end
@@ -168,6 +168,9 @@ module Solargraph
168
168
  position = Position.new(line, character)
169
169
  found = nil
170
170
  pins.each do |pin|
171
+ # @todo Attribute pins should not be treated like closures, but
172
+ # there's probably a better way to handle it
173
+ next if pin.is_a?(Pin::Method) && pin.attribute?
171
174
  found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position)
172
175
  break if pin.location.range.start.line > line
173
176
  end
@@ -149,7 +149,7 @@ module Solargraph
149
149
  frag_start = cursor.start_of_word.to_s.downcase
150
150
  filtered = result.uniq(&:name).select { |s|
151
151
  s.name.downcase.start_with?(frag_start) &&
152
- (!s.is_a?(Pin::BaseMethod) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
152
+ (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
153
153
  }
154
154
  Completion.new(filtered, cursor.range)
155
155
  end
@@ -212,7 +212,8 @@ module Solargraph
212
212
  result.concat api_map.get_constants(context_pin.context.namespace, *gates)
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
- result.concat ApiMap.keywords
215
+ # result.concat ApiMap.keywords
216
+ result.concat api_map.keyword_pins
216
217
  result.concat yielded_self_pins
217
218
  end
218
219
  end
@@ -126,27 +126,31 @@ module Solargraph
126
126
  namespace = closure_at(source_position)
127
127
  t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
128
128
  if t.nil? || t.include?('r')
129
- pins.push Solargraph::Pin::Attribute.new(
129
+ pins.push Solargraph::Pin::Method.new(
130
130
  location: location,
131
131
  closure: namespace,
132
132
  name: directive.tag.name,
133
133
  comments: docstring.all.to_s,
134
- access: :reader,
135
134
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
136
135
  visibility: :public,
137
- explicit: false
136
+ explicit: false,
137
+ attribute: true
138
138
  )
139
139
  end
140
140
  if t.nil? || t.include?('w')
141
- pins.push Solargraph::Pin::Attribute.new(
141
+ pins.push Solargraph::Pin::Method.new(
142
142
  location: location,
143
143
  closure: namespace,
144
144
  name: "#{directive.tag.name}=",
145
145
  comments: docstring.all.to_s,
146
- access: :writer,
147
146
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
148
- visibility: :public
147
+ visibility: :public,
148
+ attribute: true
149
149
  )
150
+ pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
151
+ if pins.last.return_type.defined?
152
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
153
+ end
150
154
  end
151
155
  when 'parse'
152
156
  begin
@@ -73,15 +73,15 @@ module Solargraph
73
73
  # @return [Array<Problem>]
74
74
  def method_tag_problems
75
75
  result = []
76
- # @param pin [Pin::BaseMethod]
77
- source_map.pins_by_class(Pin::BaseMethod).each do |pin|
76
+ # @param pin [Pin::Method]
77
+ source_map.pins_by_class(Pin::Method).each do |pin|
78
78
  result.concat method_return_type_problems_for(pin)
79
79
  result.concat method_param_type_problems_for(pin)
80
80
  end
81
81
  result
82
82
  end
83
83
 
84
- # @param pin [Pin::BaseMethod]
84
+ # @param pin [Pin::Method]
85
85
  # @return [Array<Problem>]
86
86
  def method_return_type_problems_for pin
87
87
  result = []
@@ -115,7 +115,7 @@ module Solargraph
115
115
  pin.location && source_map.source.comment_at?(pin.location.range.ending)
116
116
  end
117
117
 
118
- # @param pin [Pin::BaseMethod]
118
+ # @param pin [Pin::Method]
119
119
  # @return [Array<Problem>]
120
120
  def method_param_type_problems_for pin
121
121
  stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
@@ -147,14 +147,12 @@ module Solargraph
147
147
  result = []
148
148
  all_variables.each do |pin|
149
149
  if pin.return_type.defined?
150
- # @todo Somwhere in here we still need to determine if the variable is defined by an external call
151
150
  declared = pin.typify(api_map)
152
151
  if declared.defined?
153
152
  if rules.validate_tags?
154
153
  inferred = pin.probe(api_map)
155
154
  if inferred.undefined?
156
155
  next if rules.ignore_all_undefined?
157
- # next unless internal?(pin) # @todo This might be redundant for variables
158
156
  if declared_externally?(pin)
159
157
  ignored_pins.push pin
160
158
  else
@@ -172,7 +170,6 @@ module Solargraph
172
170
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
173
171
  end
174
172
  else
175
- # @todo Check if the variable is defined by an external call
176
173
  inferred = pin.probe(api_map)
177
174
  if inferred.undefined? && declared_externally?(pin)
178
175
  ignored_pins.push pin
@@ -227,7 +224,7 @@ module Solargraph
227
224
  base = base.base
228
225
  end
229
226
  closest = found.typify(api_map) if found
230
- if !found || closest.defined? || internal?(found)
227
+ if !found || (closest.defined? && internal_or_core?(found))
231
228
  unless ignored_pins.include?(found)
232
229
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
233
230
  @marked_ranges.push rng
@@ -244,8 +241,8 @@ module Solargraph
244
241
  base = chain
245
242
  until base.links.length == 1 && base.undefined?
246
243
  pins = base.define(api_map, block_pin, locals)
247
- if pins.first.is_a?(Pin::BaseMethod)
248
- # @type [Pin::BaseMethod]
244
+ if pins.first.is_a?(Pin::Method)
245
+ # @type [Pin::Method]
249
246
  pin = pins.first
250
247
  ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
251
248
  arity_problems_for(pin, fake_args_for(block_pin), location)
@@ -313,7 +310,6 @@ module Solargraph
313
310
  end
314
311
  else
315
312
  if par.decl == :kwarg
316
- # @todo Problem: missing required keyword argument
317
313
  result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
318
314
  end
319
315
  end
@@ -360,9 +356,14 @@ module Solargraph
360
356
 
361
357
  # @param pin [Pin::Base]
362
358
  def internal? pin
359
+ return false if pin.nil?
363
360
  pin.location && api_map.bundled?(pin.location.filename)
364
361
  end
365
362
 
363
+ def internal_or_core? pin
364
+ internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
365
+ end
366
+
366
367
  # @param pin [Pin::Base]
367
368
  def external? pin
368
369
  !internal? pin
@@ -395,7 +396,7 @@ module Solargraph
395
396
  true
396
397
  end
397
398
 
398
- # @param pin [Pin::BaseMethod]
399
+ # @param pin [Pin::Method]
399
400
  def arity_problems_for(pin, arguments, location)
400
401
  return [] unless pin.explicit?
401
402
  return [] if pin.parameters.empty? && arguments.empty?
@@ -410,29 +411,29 @@ module Solargraph
410
411
  if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
411
412
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
412
413
  end
414
+ settled_kwargs = 0
413
415
  unless unchecked.empty?
414
416
  kwargs = convert_hash(unchecked.last.node)
415
- # unless kwargs.empty?
416
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
417
- if kwargs.empty?
418
- add_params += 1
419
- else
420
- unchecked.pop
421
- pin.parameters.each do |param|
422
- next unless param.keyword?
423
- if kwargs.key?(param.name.to_sym)
424
- kwargs.delete param.name.to_sym
425
- elsif param.decl == :kwarg
426
- return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
427
- end
428
- end
429
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
430
- unless kwargs.empty?
431
- return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
417
+ if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
418
+ if kwargs.empty?
419
+ add_params += 1
420
+ else
421
+ unchecked.pop
422
+ pin.parameters.each do |param|
423
+ next unless param.keyword?
424
+ if kwargs.key?(param.name.to_sym)
425
+ kwargs.delete param.name.to_sym
426
+ settled_kwargs += 1
427
+ elsif param.decl == :kwarg
428
+ return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
432
429
  end
433
430
  end
431
+ kwargs.clear if pin.parameters.any?(&:kwrestarg?)
432
+ unless kwargs.empty?
433
+ return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
434
+ end
434
435
  end
435
- # end
436
+ end
436
437
  end
437
438
  req = required_param_count(pin)
438
439
  if req + add_params < unchecked.length
@@ -443,23 +444,18 @@ module Solargraph
443
444
  return []
444
445
  end
445
446
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
446
- elsif unchecked.length < req && (arguments.empty? || !arguments.last.splat?)
447
+ elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
447
448
  return [Problem.new(location, "Not enough arguments to #{pin.path}")]
448
449
  end
449
450
  []
450
451
  end
451
452
 
452
- # @param pin [Pin::BaseMethod]
453
+ # @param pin [Pin::Method]
453
454
  def required_param_count(pin)
454
- count = 0
455
- pin.parameters.each do |param|
456
- break unless param.decl == :arg
457
- count += 1
458
- end
459
- count
455
+ pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
460
456
  end
461
457
 
462
- # @param pin [Pin::BaseMethod]
458
+ # @param pin [Pin::Method]
463
459
  def optional_param_count(pin)
464
460
  count = 0
465
461
  pin.parameters.each do |param|