solargraph 0.54.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +127 -0
  3. data/.github/workflows/plugins.yml +184 -6
  4. data/.github/workflows/rspec.yml +55 -5
  5. data/.github/workflows/typecheck.yml +8 -3
  6. data/.gitignore +7 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +1279 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +171 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/bin/solargraph +8 -5
  16. data/lib/solargraph/api_map/cache.rb +13 -3
  17. data/lib/solargraph/api_map/constants.rb +279 -0
  18. data/lib/solargraph/api_map/index.rb +193 -0
  19. data/lib/solargraph/api_map/source_to_yard.rb +13 -4
  20. data/lib/solargraph/api_map/store.rb +207 -132
  21. data/lib/solargraph/api_map.rb +394 -261
  22. data/lib/solargraph/bench.rb +18 -1
  23. data/lib/solargraph/complex_type/type_methods.rb +29 -12
  24. data/lib/solargraph/complex_type/unique_type.rb +205 -26
  25. data/lib/solargraph/complex_type.rb +126 -26
  26. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  27. data/lib/solargraph/convention/base.rb +20 -3
  28. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  29. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  30. data/lib/solargraph/convention/data_definition.rb +105 -0
  31. data/lib/solargraph/convention/gemspec.rb +3 -2
  32. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  33. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  34. data/lib/solargraph/convention/struct_definition.rb +164 -0
  35. data/lib/solargraph/convention.rb +36 -4
  36. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  38. data/lib/solargraph/doc_map.rb +316 -64
  39. data/lib/solargraph/environ.rb +9 -2
  40. data/lib/solargraph/equality.rb +34 -0
  41. data/lib/solargraph/gem_pins.rb +64 -38
  42. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  43. data/lib/solargraph/language_server/host/message_worker.rb +54 -5
  44. data/lib/solargraph/language_server/host.rb +36 -18
  45. data/lib/solargraph/language_server/message/base.rb +20 -12
  46. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  47. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  48. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  49. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  50. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  51. data/lib/solargraph/language_server/message/text_document/definition.rb +5 -3
  52. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  53. data/lib/solargraph/language_server/message/text_document/formatting.rb +23 -2
  54. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  55. data/lib/solargraph/language_server/message/text_document/type_definition.rb +4 -3
  56. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  57. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  58. data/lib/solargraph/language_server/progress.rb +27 -2
  59. data/lib/solargraph/language_server/request.rb +4 -1
  60. data/lib/solargraph/library.rb +83 -73
  61. data/lib/solargraph/location.rb +45 -1
  62. data/lib/solargraph/logging.rb +12 -2
  63. data/lib/solargraph/page.rb +3 -0
  64. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  65. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  66. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  67. data/lib/solargraph/parser/node_processor.rb +26 -8
  68. data/lib/solargraph/parser/parser_gem/class_methods.rb +10 -18
  69. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  70. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  71. data/lib/solargraph/parser/parser_gem/node_methods.rb +10 -19
  72. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  73. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -0
  74. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +26 -20
  75. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  76. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  77. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  79. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  80. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  81. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  83. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  84. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +14 -2
  85. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  86. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  89. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +16 -6
  90. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +64 -32
  91. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  92. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  93. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  94. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  95. data/lib/solargraph/parser/region.rb +4 -1
  96. data/lib/solargraph/parser/snippet.rb +2 -0
  97. data/lib/solargraph/parser.rb +3 -5
  98. data/lib/solargraph/pin/base.rb +417 -42
  99. data/lib/solargraph/pin/base_variable.rb +21 -12
  100. data/lib/solargraph/pin/block.rb +9 -26
  101. data/lib/solargraph/pin/breakable.rb +9 -0
  102. data/lib/solargraph/pin/callable.rb +231 -0
  103. data/lib/solargraph/pin/closure.rb +30 -10
  104. data/lib/solargraph/pin/common.rb +12 -7
  105. data/lib/solargraph/pin/constant.rb +2 -0
  106. data/lib/solargraph/pin/conversions.rb +3 -2
  107. data/lib/solargraph/pin/delegated_method.rb +20 -1
  108. data/lib/solargraph/pin/documenting.rb +16 -0
  109. data/lib/solargraph/pin/instance_variable.rb +2 -2
  110. data/lib/solargraph/pin/keyword.rb +7 -2
  111. data/lib/solargraph/pin/local_variable.rb +15 -7
  112. data/lib/solargraph/pin/method.rb +241 -70
  113. data/lib/solargraph/pin/method_alias.rb +3 -0
  114. data/lib/solargraph/pin/namespace.rb +21 -13
  115. data/lib/solargraph/pin/parameter.rb +94 -32
  116. data/lib/solargraph/pin/proxy_type.rb +17 -7
  117. data/lib/solargraph/pin/reference/override.rb +24 -6
  118. data/lib/solargraph/pin/reference/require.rb +2 -2
  119. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  120. data/lib/solargraph/pin/reference.rb +17 -0
  121. data/lib/solargraph/pin/search.rb +6 -1
  122. data/lib/solargraph/pin/signature.rb +39 -121
  123. data/lib/solargraph/pin/singleton.rb +1 -1
  124. data/lib/solargraph/pin/symbol.rb +8 -2
  125. data/lib/solargraph/pin/until.rb +18 -0
  126. data/lib/solargraph/pin/while.rb +18 -0
  127. data/lib/solargraph/pin.rb +8 -2
  128. data/lib/solargraph/pin_cache.rb +245 -0
  129. data/lib/solargraph/position.rb +19 -0
  130. data/lib/solargraph/range.rb +23 -4
  131. data/lib/solargraph/rbs_map/conversions.rb +315 -99
  132. data/lib/solargraph/rbs_map/core_fills.rb +50 -16
  133. data/lib/solargraph/rbs_map/core_map.rb +41 -11
  134. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  135. data/lib/solargraph/rbs_map.rb +87 -16
  136. data/lib/solargraph/shell.rb +117 -17
  137. data/lib/solargraph/source/chain/array.rb +13 -8
  138. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  139. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  140. data/lib/solargraph/source/chain/call.rb +135 -66
  141. data/lib/solargraph/source/chain/constant.rb +3 -66
  142. data/lib/solargraph/source/chain/hash.rb +9 -3
  143. data/lib/solargraph/source/chain/head.rb +1 -1
  144. data/lib/solargraph/source/chain/if.rb +7 -2
  145. data/lib/solargraph/source/chain/link.rb +38 -6
  146. data/lib/solargraph/source/chain/literal.rb +27 -2
  147. data/lib/solargraph/source/chain/or.rb +2 -2
  148. data/lib/solargraph/source/chain/z_super.rb +1 -1
  149. data/lib/solargraph/source/chain.rb +140 -63
  150. data/lib/solargraph/source/change.rb +2 -2
  151. data/lib/solargraph/source/cursor.rb +4 -4
  152. data/lib/solargraph/source/source_chainer.rb +3 -3
  153. data/lib/solargraph/source.rb +110 -89
  154. data/lib/solargraph/source_map/clip.rb +22 -28
  155. data/lib/solargraph/source_map/data.rb +34 -0
  156. data/lib/solargraph/source_map/mapper.rb +11 -7
  157. data/lib/solargraph/source_map.rb +50 -43
  158. data/lib/solargraph/type_checker/checks.rb +4 -0
  159. data/lib/solargraph/type_checker/param_def.rb +2 -0
  160. data/lib/solargraph/type_checker/rules.rb +35 -8
  161. data/lib/solargraph/type_checker.rb +331 -189
  162. data/lib/solargraph/version.rb +1 -1
  163. data/lib/solargraph/views/_method.erb +10 -10
  164. data/lib/solargraph/views/_namespace.erb +3 -3
  165. data/lib/solargraph/views/document.erb +10 -10
  166. data/lib/solargraph/views/environment.erb +3 -5
  167. data/lib/solargraph/workspace/config.rb +25 -5
  168. data/lib/solargraph/workspace/require_paths.rb +97 -0
  169. data/lib/solargraph/workspace.rb +53 -72
  170. data/lib/solargraph/yard_map/helpers.rb +29 -1
  171. data/lib/solargraph/yard_map/mapper/to_constant.rb +8 -5
  172. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -19
  173. data/lib/solargraph/yard_map/mapper/to_namespace.rb +11 -7
  174. data/lib/solargraph/yard_map/mapper.rb +5 -3
  175. data/lib/solargraph/yard_map/to_method.rb +6 -3
  176. data/lib/solargraph/yardoc.rb +45 -10
  177. data/lib/solargraph.rb +35 -1
  178. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  179. data/rbs/fills/open3/0/open3.rbs +172 -0
  180. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  181. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  182. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  183. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  184. data/rbs/fills/tuple/tuple.rbs +149 -0
  185. data/rbs_collection.yaml +19 -0
  186. data/sig/shims/ast/0/node.rbs +5 -0
  187. data/sig/shims/ast/2.4/.rbs_meta.yaml +9 -0
  188. data/sig/shims/ast/2.4/ast.rbs +73 -0
  189. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  190. data/sig/shims/parser/3.2.0.1/manifest.yaml +7 -0
  191. data/sig/shims/parser/3.2.0.1/parser.rbs +201 -0
  192. data/sig/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  193. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  194. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  195. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  196. data/solargraph.gemspec +32 -10
  197. metadata +237 -37
  198. data/lib/.rubocop.yml +0 -22
  199. data/lib/solargraph/cache.rb +0 -77
  200. data/lib/solargraph/parser/node_methods.rb +0 -83
@@ -21,14 +21,24 @@ module Solargraph
21
21
  # @return [ApiMap]
22
22
  attr_reader :api_map
23
23
 
24
- # @param filename [String]
24
+ # @param filename [String, nil]
25
25
  # @param api_map [ApiMap, nil]
26
- # @param level [Symbol]
27
- def initialize filename, api_map: nil, level: :normal
26
+ # @param level [Symbol] Don't complain about anything above this level
27
+ # @param workspace [Workspace, nil] Workspace to use for loading
28
+ # type checker rules modified by user config
29
+ # @param type_checker_rules [Hash{Symbol => Symbol}] Overrides for
30
+ # type checker rules - e.g., :report_undefined => :strong
31
+ # @param rules [Rules] Type checker rules object
32
+ def initialize filename,
33
+ api_map: nil,
34
+ level: :normal,
35
+ workspace: filename ? Workspace.new(File.dirname(filename)) : nil,
36
+ rules: workspace ? workspace.rules(level) : Rules.new(level, {})
28
37
  @filename = filename
29
38
  # @todo Smarter directory resolution
30
39
  @api_map = api_map || Solargraph::ApiMap.load(File.dirname(filename))
31
- @rules = Rules.new(level)
40
+ @rules = rules
41
+ # @type [Array<Range>]
32
42
  @marked_ranges = []
33
43
  end
34
44
 
@@ -37,15 +47,20 @@ module Solargraph
37
47
  @source_map ||= api_map.source_map(filename)
38
48
  end
39
49
 
50
+ # @return [Source]
51
+ def source
52
+ @source_map.source
53
+ end
54
+
40
55
  # @return [Array<Problem>]
41
56
  def problems
42
57
  @problems ||= begin
43
- without_ignored(
44
- method_tag_problems
45
- .concat variable_type_tag_problems
46
- .concat const_problems
47
- .concat call_problems
48
- )
58
+ all = method_tag_problems
59
+ .concat(variable_type_tag_problems)
60
+ .concat(const_problems)
61
+ .concat(call_problems)
62
+ unignored = without_ignored(all)
63
+ unignored.concat(unneeded_sgignore_problems)
49
64
  end
50
65
  end
51
66
 
@@ -90,11 +105,11 @@ module Solargraph
90
105
  def method_return_type_problems_for pin
91
106
  return [] if pin.is_a?(Pin::MethodAlias)
92
107
  result = []
93
- declared = pin.typify(api_map).self_to(pin.full_context.namespace)
108
+ declared = pin.typify(api_map).self_to_type(pin.full_context).qualify(api_map, *pin.gates)
94
109
  if declared.undefined?
95
110
  if pin.return_type.undefined? && rules.require_type_tags?
96
111
  if pin.attribute?
97
- inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
112
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
98
113
  result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined?
99
114
  else
100
115
  result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
@@ -106,14 +121,14 @@ module Solargraph
106
121
  end
107
122
  elsif rules.validate_tags?
108
123
  unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
109
- inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
124
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
110
125
  if inferred.undefined?
111
126
  unless rules.ignore_all_undefined? || external?(pin)
112
127
  result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
113
128
  end
114
129
  else
115
- unless (rules.rank > 1 ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
116
- result.push Problem.new(pin.location, "Declared return type #{declared} does not match inferred type #{inferred} for #{pin.path}", pin: pin)
130
+ unless (rules.require_all_return_types_match_inferred? ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
131
+ result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin)
117
132
  end
118
133
  end
119
134
  end
@@ -139,40 +154,39 @@ module Solargraph
139
154
 
140
155
  # @param pin [Pin::Base]
141
156
  def virtual_pin? pin
142
- pin.location && source_map.source.comment_at?(pin.location.range.ending)
157
+ pin.location && source.comment_at?(pin.location.range.ending)
143
158
  end
144
159
 
145
160
  # @param pin [Pin::Method]
146
161
  # @return [Array<Problem>]
147
162
  def method_param_type_problems_for pin
148
163
  stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
149
- params = first_param_hash(stack)
150
164
  result = []
151
- if rules.require_type_tags?
152
- pin.signatures.each do |sig|
153
- sig.parameters.each do |par|
154
- break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
155
- unless params[par.name]
156
- if pin.attribute?
157
- inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
158
- if inferred.undefined?
165
+ pin.signatures.each do |sig|
166
+ params = param_details_from_stack(sig, stack)
167
+ if rules.require_type_tags?
168
+ sig.parameters.each do |par|
169
+ break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
170
+ unless params[par.name]
171
+ if pin.attribute?
172
+ inferred = pin.probe(api_map).self_to_type(pin.full_context)
173
+ if inferred.undefined?
174
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
175
+ end
176
+ else
159
177
  result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
160
178
  end
161
- else
162
- result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
163
179
  end
164
180
  end
165
- end
166
181
  end
167
- end
168
- # @todo Should be able to probe type of name and data here
169
- # @param name [String]
170
- # @param data [Hash{Symbol => BasicObject}]
171
- params.each_pair do |name, data|
172
- # @type [ComplexType]
173
- type = data[:qualified]
174
- if type.undefined?
175
- result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
182
+ # @param name [String]
183
+ # @param data [Hash{Symbol => BasicObject}]
184
+ params.each_pair do |name, data|
185
+ # @type [ComplexType]
186
+ type = data[:qualified]
187
+ if type.undefined?
188
+ result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
189
+ end
176
190
  end
177
191
  end
178
192
  result
@@ -190,7 +204,7 @@ module Solargraph
190
204
  if pin.return_type.defined?
191
205
  declared = pin.typify(api_map)
192
206
  next if declared.duck_type?
193
- if declared.defined?
207
+ if declared.defined? && pin.assignment
194
208
  if rules.validate_tags?
195
209
  inferred = pin.probe(api_map)
196
210
  if inferred.undefined?
@@ -211,7 +225,7 @@ module Solargraph
211
225
  elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
212
226
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
213
227
  end
214
- else
228
+ elsif pin.assignment
215
229
  inferred = pin.probe(api_map)
216
230
  if inferred.undefined? && declared_externally?(pin)
217
231
  ignored_pins.push pin
@@ -230,13 +244,14 @@ module Solargraph
230
244
  def const_problems
231
245
  return [] unless rules.validate_consts?
232
246
  result = []
233
- Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const|
247
+ Solargraph::Parser::NodeMethods.const_nodes_from(source.node).each do |const|
234
248
  rng = Solargraph::Range.from_node(const)
235
249
  chain = Solargraph::Parser.chain(const, filename)
236
- block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
250
+ closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
251
+ closure_pin.rebind(api_map)
237
252
  location = Location.new(filename, rng)
238
253
  locals = source_map.locals_at(location)
239
- pins = chain.define(api_map, block_pin, locals)
254
+ pins = chain.define(api_map, closure_pin, locals)
240
255
  if pins.empty?
241
256
  result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}")
242
257
  @marked_ranges.push location.range
@@ -248,21 +263,29 @@ module Solargraph
248
263
  # @return [Array<Problem>]
249
264
  def call_problems
250
265
  result = []
251
- Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
266
+ Solargraph::Parser::NodeMethods.call_nodes_from(source.node).each do |call|
252
267
  rng = Solargraph::Range.from_node(call)
253
268
  next if @marked_ranges.any? { |d| d.contain?(rng.start) }
254
269
  chain = Solargraph::Parser.chain(call, filename)
255
- block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
270
+ closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
271
+ namespace_pin = closure_pin
272
+ if call.type == :block
273
+ # blocks in the AST include the method call as well, so the
274
+ # node returned by #call_nodes_from needs to be backed out
275
+ # one closure
276
+ closure_pin = closure_pin.closure
277
+ end
278
+ closure_pin.rebind(api_map)
256
279
  location = Location.new(filename, rng)
257
280
  locals = source_map.locals_at(location)
258
- type = chain.infer(api_map, block_pin, locals)
281
+ type = chain.infer(api_map, closure_pin, locals)
259
282
  if type.undefined? && !rules.ignore_all_undefined?
260
283
  base = chain
261
284
  missing = chain
262
285
  found = nil
263
286
  closest = ComplexType::UNDEFINED
264
287
  until base.links.first.undefined?
265
- found = base.define(api_map, block_pin, locals).first
288
+ found = base.define(api_map, closure_pin, locals).first
266
289
  break if found
267
290
  missing = base
268
291
  base = base.base
@@ -271,159 +294,174 @@ module Solargraph
271
294
  # @todo remove the internal_or_core? check at a higher-than-strict level
272
295
  if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
273
296
  unless closest.generic? || ignored_pins.include?(found)
274
- result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
297
+ if closest.defined?
298
+ result.push Problem.new(location, "Unresolved call to #{missing.links.last.word} on #{closest}")
299
+ else
300
+ result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
301
+ end
275
302
  @marked_ranges.push rng
276
303
  end
277
304
  end
278
305
  end
279
- result.concat argument_problems_for(chain, api_map, block_pin, locals, location)
306
+ result.concat argument_problems_for(chain, api_map, closure_pin, locals, location)
280
307
  end
281
308
  result
282
309
  end
283
310
 
284
311
  # @param chain [Solargraph::Source::Chain]
285
312
  # @param api_map [Solargraph::ApiMap]
286
- # @param block_pin [Solargraph::Pin::Base]
313
+ # @param closure_pin [Solargraph::Pin::Closure]
287
314
  # @param locals [Array<Solargraph::Pin::Base>]
288
315
  # @param location [Solargraph::Location]
289
316
  # @return [Array<Problem>]
290
- def argument_problems_for chain, api_map, block_pin, locals, location
317
+ def argument_problems_for chain, api_map, closure_pin, locals, location
291
318
  result = []
292
319
  base = chain
293
- until base.links.length == 1 && base.undefined?
294
- last_base_link = base.links.last
295
- break unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
296
-
297
- arguments = last_base_link.arguments
298
-
299
- pins = base.define(api_map, block_pin, locals)
300
-
301
- first_pin = pins.first
302
- if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
303
- # Do nothing, as we can't find the actual method implementation
304
- elsif first_pin.is_a?(Pin::Method)
305
- # @type [Pin::Method]
306
- pin = first_pin
307
- ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
308
- arity_problems_for(pin, fake_args_for(block_pin), location)
309
- elsif pin.path == 'Class#new'
310
- fqns = if base.links.one?
311
- block_pin.namespace
320
+ # @type last_base_link [Solargraph::Source::Chain::Call]
321
+ last_base_link = base.links.last
322
+ return [] unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
323
+
324
+ arguments = last_base_link.arguments
325
+
326
+ pins = base.define(api_map, closure_pin, locals)
327
+
328
+ first_pin = pins.first
329
+ if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
330
+ # Do nothing, as we can't find the actual method implementation
331
+ elsif first_pin.is_a?(Pin::Method)
332
+ # @type [Pin::Method]
333
+ pin = first_pin
334
+ ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
335
+ arity_problems_for(pin, fake_args_for(closure_pin), location)
336
+ elsif pin.path == 'Class#new'
337
+ fqns = if base.links.one?
338
+ closure_pin.namespace
339
+ else
340
+ base.base.infer(api_map, closure_pin, locals).namespace
341
+ end
342
+ init = api_map.get_method_stack(fqns, 'initialize').first
343
+ init ? arity_problems_for(init, arguments, location) : []
344
+ else
345
+ arity_problems_for(pin, arguments, location)
346
+ end
347
+ return ap unless ap.empty?
348
+ return [] if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
349
+
350
+ all_errors = []
351
+ pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
352
+ params = param_details_from_stack(sig, pins)
353
+
354
+ signature_errors = signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
355
+
356
+ if signature_errors.empty?
357
+ # we found a signature that works - meaning errors from
358
+ # other signatures don't matter.
359
+ return []
360
+ end
361
+ all_errors.concat signature_errors
362
+ end
363
+ result.concat all_errors
364
+ end
365
+ result
366
+ end
367
+
368
+ # @param location [Location]
369
+ # @param locals [Array<Pin::LocalVariable>]
370
+ # @param closure_pin [Pin::Closure]
371
+ # @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}]
372
+ # @param arguments [Array<Source::Chain>]
373
+ # @param sig [Pin::Signature]
374
+ # @param pin [Pin::Method]
375
+ # @param pins [Array<Pin::Method>]
376
+ #
377
+ # @return [Array<Problem>]
378
+ def signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
379
+ errors = []
380
+ # @todo add logic mapping up restarg parameters with
381
+ # arguments (including restarg arguments). Use tuples
382
+ # when possible, and when not, ensure provably
383
+ # incorrect situations are detected.
384
+ sig.parameters.each_with_index do |par, idx|
385
+ return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing
386
+ argchain = arguments[idx]
387
+ if argchain.nil?
388
+ if par.decl == :arg
389
+ final_arg = arguments.last
390
+ if final_arg && final_arg.node.type == :splat
391
+ argchain = final_arg
392
+ return errors
312
393
  else
313
- base.base.infer(api_map, block_pin, locals).namespace
394
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
314
395
  end
315
- init = api_map.get_method_stack(fqns, 'initialize').first
316
- init ? arity_problems_for(init, arguments, location) : []
317
396
  else
318
- arity_problems_for(pin, arguments, location)
319
- end
320
- unless ap.empty?
321
- result.concat ap
322
- break
397
+ final_arg = arguments.last
398
+ argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
323
399
  end
324
- break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
325
-
326
- params = first_param_hash(pins)
327
-
328
- all_errors = []
329
- pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
330
- errors = []
331
- sig.parameters.each_with_index do |par, idx|
332
- # @todo add logic mapping up restarg parameters with
333
- # arguments (including restarg arguments). Use tuples
334
- # when possible, and when not, ensure provably
335
- # incorrect situations are detected.
336
- break if par.decl == :restarg # bail out pending better arg processing
337
- argchain = arguments[idx]
338
- if argchain.nil?
339
- if par.decl == :arg
340
- final_arg = arguments.last
341
- if final_arg && final_arg.node.type == :splat
342
- argchain = final_arg
343
- next # don't try to apply the type of the splat - unlikely to be specific enough
344
- else
345
- errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
346
- next
347
- end
348
- else
349
- final_arg = arguments.last
350
- argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
351
- end
352
- end
353
- if argchain
354
- if par.decl != :arg
355
- errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
356
- next
357
- else
358
- if argchain.node.type == :splat && argchain == arguments.last
359
- final_arg = argchain
360
- end
361
- if (final_arg && final_arg.node.type == :splat)
362
- # The final argument given has been seen and was a
363
- # splat, which doesn't give us useful types or
364
- # arities against positional parameters, so let's
365
- # continue on in case there are any required
366
- # kwargs we should warn about
367
- next
368
- end
369
-
370
- if argchain.node.type == :splat && par != sig.parameters.last
371
- # we have been given a splat and there are more
372
- # arguments to come.
373
-
374
- # @todo Improve this so that we can skip past the
375
- # rest of the positional parameters here but still
376
- # process the kwargs
377
- break
378
- end
379
- ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
380
- ptype = ptype.self_to(par.context.namespace)
381
- if ptype.nil?
382
- # @todo Some level (strong, I guess) should require the param here
383
- else
384
- argtype = argchain.infer(api_map, block_pin, locals)
385
- if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
386
- errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
387
- next
388
- end
389
- end
390
- end
391
- elsif par.decl == :kwarg
392
- errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
393
- next
394
- end
400
+ end
401
+ if argchain
402
+ if par.decl != :arg
403
+ errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx
404
+ next
405
+ else
406
+ if argchain.node.type == :splat && argchain == arguments.last
407
+ final_arg = argchain
408
+ end
409
+ if (final_arg && final_arg.node.type == :splat)
410
+ # The final argument given has been seen and was a
411
+ # splat, which doesn't give us useful types or
412
+ # arities against positional parameters, so let's
413
+ # continue on in case there are any required
414
+ # kwargs we should warn about
415
+ next
395
416
  end
396
- if errors.empty?
397
- all_errors.clear
398
- break
417
+ if argchain.node.type == :splat && par != sig.parameters.last
418
+ # we have been given a splat and there are more
419
+ # arguments to come.
420
+
421
+ # @todo Improve this so that we can skip past the
422
+ # rest of the positional parameters here but still
423
+ # process the kwargs
424
+ return errors
425
+ end
426
+ ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
427
+ ptype = ptype.self_to_type(par.context)
428
+ if ptype.nil?
429
+ # @todo Some level (strong, I guess) should require the param here
430
+ else
431
+ argtype = argchain.infer(api_map, closure_pin, locals)
432
+ argtype = argtype.self_to_type(closure_pin.context)
433
+ if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
434
+ errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
435
+ return errors
436
+ end
399
437
  end
400
- all_errors.concat errors
401
438
  end
402
- result.concat all_errors
439
+ elsif par.decl == :kwarg
440
+ errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
441
+ next
403
442
  end
404
- base = base.base
405
443
  end
406
- result
444
+ errors
407
445
  end
408
446
 
409
447
  # @param sig [Pin::Signature]
410
448
  # @param argchain [Source::Chain]
411
449
  # @param api_map [ApiMap]
412
- # @param block_pin [Pin::Block]
450
+ # @param closure_pin [Pin::Closure]
413
451
  # @param locals [Array<Pin::LocalVariable>]
414
452
  # @param location [Location]
415
453
  # @param pin [Pin::Method]
416
- # @param params [Hash{String => [nil, Hash]}]
454
+ # @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}]
417
455
  # @param idx [Integer]
418
456
  #
419
457
  # @return [Array<Problem>]
420
- def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
458
+ def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx
421
459
  result = []
422
460
  kwargs = convert_hash(argchain.node)
423
461
  par = sig.parameters[idx]
424
462
  argchain = kwargs[par.name.to_sym]
425
463
  if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
426
- result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
464
+ result.concat kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs)
427
465
  else
428
466
  if argchain
429
467
  data = params[par.name]
@@ -431,8 +469,11 @@ module Solargraph
431
469
  # @todo Some level (strong, I guess) should require the param here
432
470
  else
433
471
  ptype = data[:qualified]
472
+ ptype = ptype.self_to_type(pin.context)
434
473
  unless ptype.undefined?
435
- argtype = argchain.infer(api_map, block_pin, locals)
474
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1127
475
+ argtype = argchain.infer(api_map, closure_pin, locals).self_to_type(closure_pin.context)
476
+ # @sg-ignore Unresolved call to defined?
436
477
  if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
437
478
  result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
438
479
  end
@@ -446,19 +487,21 @@ module Solargraph
446
487
  end
447
488
 
448
489
  # @param api_map [ApiMap]
449
- # @param block_pin [Pin::Block]
490
+ # @param closure_pin [Pin::Closure]
450
491
  # @param locals [Array<Pin::LocalVariable>]
451
492
  # @param location [Location]
452
493
  # @param pin [Pin::Method]
453
494
  # @param params [Hash{String => [nil, Hash]}]
454
495
  # @param kwargs [Hash{Symbol => Source::Chain}]
455
496
  # @return [Array<Problem>]
456
- def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
497
+ def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs)
457
498
  result = []
458
499
  kwargs.each_pair do |pname, argchain|
459
500
  next unless params.key?(pname.to_s)
460
501
  ptype = params[pname.to_s][:qualified]
461
- argtype = argchain.infer(api_map, block_pin, locals)
502
+ ptype = ptype.self_to_type(pin.context)
503
+ argtype = argchain.infer(api_map, closure_pin, locals)
504
+ argtype = argtype.self_to_type(closure_pin.context)
462
505
  if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
463
506
  result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
464
507
  end
@@ -466,32 +509,94 @@ module Solargraph
466
509
  result
467
510
  end
468
511
 
469
- # @param pin [Pin::Method]
470
- # @return [Hash{String => Hash{Symbol => BaseObject}}]
471
- def param_hash(pin)
512
+ # @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
513
+ # @param pin [Pin::Method, Pin::Signature]
514
+ # @param relevant_pin [Pin::Method, Pin::Signature] the pin which is under inspection
515
+ # @return [void]
516
+ def add_restkwarg_param_tag_details(param_details, pin, relevant_pin)
517
+ # see if we have additional tags to pay attention to from YARD -
518
+ # e.g., kwargs in a **restkwargs splat
472
519
  tags = pin.docstring.tags(:param)
473
- return {} if tags.empty?
520
+ tags.each do |tag|
521
+ next if param_details.key? tag.name.to_s
522
+ next if tag.types.nil?
523
+ details = {
524
+ tagged: tag.types.join(', '),
525
+ qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
526
+ }
527
+ # don't complain about a param that didn't come from the pin we're looking at anyway
528
+ if details[:qualified].defined? ||
529
+ relevant_pin.parameter_names.include?(tag.name.to_s)
530
+ param_details[tag.name.to_s] = details
531
+ end
532
+ end
533
+ end
534
+
535
+ # @param pin [Pin::Signature]
536
+ # @return [Hash{String => Hash{Symbol => String, ComplexType}}]
537
+ def signature_param_details(pin)
538
+ # @type [Hash{String => Hash{Symbol => String, ComplexType}}]
474
539
  result = {}
540
+ pin.parameters.each do |param|
541
+ type = param.typify(api_map)
542
+ next if type.nil? || type.undefined?
543
+ result[param.name.to_s] = {
544
+ tagged: type.tags,
545
+ qualified: type
546
+ }
547
+ end
548
+ # see if we have additional tags to pay attention to from YARD -
549
+ # e.g., kwargs in a **restkwargs splat
550
+ tags = pin.docstring.tags(:param)
475
551
  tags.each do |tag|
476
- next if tag.types.nil? || tag.types.empty?
552
+ next if result.key? tag.name.to_s
553
+ next if tag.types.nil?
477
554
  result[tag.name.to_s] = {
478
555
  tagged: tag.types.join(', '),
479
- qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
556
+ qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, *pin.closure.gates)
480
557
  }
481
558
  end
482
559
  result
483
560
  end
484
561
 
485
- # @param pins [Array<Pin::Method>]
486
- # @return [Hash{String => Hash{Symbol => BasicObject}}]
487
- def first_param_hash(pins)
488
- pins.each do |pin|
489
- # @todo this assignment from parametric use of Hash should not lose its generic
490
- # @type [Hash{String => Hash{Symbol => BasicObject}}]
491
- result = param_hash(pin)
492
- return result unless result.empty?
562
+ # The original signature defines the parameters, but other
563
+ # signatures and method pins can help by adding type information
564
+ #
565
+ # @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
566
+ # @param param_names [Array<String>]
567
+ # @param new_param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
568
+ #
569
+ # @return [void]
570
+ def add_to_param_details(param_details, param_names, new_param_details)
571
+ new_param_details.each do |param_name, details|
572
+ next unless param_names.include?(param_name)
573
+
574
+ param_details[param_name] ||= {}
575
+ param_details[param_name][:tagged] ||= details[:tagged]
576
+ param_details[param_name][:qualified] ||= details[:qualified]
577
+ end
578
+ end
579
+
580
+ # @param signature [Pin::Signature]
581
+ # @param method_pin_stack [Array<Pin::Method>]
582
+ # @return [Hash{String => Hash{Symbol => String, ComplexType}}]
583
+ def param_details_from_stack(signature, method_pin_stack)
584
+ signature_type = signature.typify(api_map)
585
+ signature = signature.proxy signature_type
586
+ param_details = signature_param_details(signature)
587
+ param_names = signature.parameter_names
588
+
589
+ method_pin_stack.each do |method_pin|
590
+ add_restkwarg_param_tag_details(param_details, method_pin, signature)
591
+
592
+ # documentation of types in superclasses should fail back to
593
+ # subclasses if the subclass hasn't documented something
594
+ method_pin.signatures.each do |sig|
595
+ add_restkwarg_param_tag_details(param_details, sig, signature)
596
+ add_to_param_details param_details, param_names, signature_param_details(sig)
597
+ end
493
598
  end
494
- {}
599
+ param_details
495
600
  end
496
601
 
497
602
  # @param pin [Pin::Base]
@@ -512,22 +617,23 @@ module Solargraph
512
617
  !internal? pin
513
618
  end
514
619
 
515
- # @param pin [Pin::Base]
620
+ # @param pin [Pin::BaseVariable]
516
621
  def declared_externally? pin
517
- return true if pin.assignment.nil?
622
+ raise "No assignment found" if pin.assignment.nil?
623
+
518
624
  chain = Solargraph::Parser.chain(pin.assignment, filename)
519
625
  rng = Solargraph::Range.from_node(pin.assignment)
520
- block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
626
+ closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
521
627
  location = Location.new(filename, Range.from_node(pin.assignment))
522
628
  locals = source_map.locals_at(location)
523
- type = chain.infer(api_map, block_pin, locals)
629
+ type = chain.infer(api_map, closure_pin, locals)
524
630
  if type.undefined? && !rules.ignore_all_undefined?
525
631
  base = chain
526
632
  missing = chain
527
633
  found = nil
528
634
  closest = ComplexType::UNDEFINED
529
635
  until base.links.first.undefined?
530
- found = base.define(api_map, block_pin, locals).first
636
+ found = base.define(api_map, closure_pin, locals).first
531
637
  break if found
532
638
  missing = base
533
639
  base = base.base
@@ -562,7 +668,7 @@ module Solargraph
562
668
  return [] unless pin.explicit?
563
669
  return [] if parameters.empty? && arguments.empty?
564
670
  return [] if pin.anon_splat?
565
- unchecked = arguments.clone
671
+ unchecked = arguments.dup # creates copy of and unthaws array
566
672
  add_params = 0
567
673
  if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
568
674
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
@@ -584,7 +690,8 @@ module Solargraph
584
690
  kwargs.delete param.name.to_sym
585
691
  settled_kwargs += 1
586
692
  elsif param.decl == :kwarg
587
- return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
693
+ last_arg_last_link = arguments.last.links.last
694
+ return [] if last_arg_last_link.is_a?(Solargraph::Source::Chain::Hash) && last_arg_last_link.splatted?
588
695
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
589
696
  end
590
697
  end
@@ -619,7 +726,6 @@ module Solargraph
619
726
  # @param parameters [Enumerable<Pin::Parameter>]
620
727
  # @todo need to use generic types in method to choose correct
621
728
  # signature and generate Integer as return type
622
- # @sg-ignore
623
729
  # @return [Integer]
624
730
  def required_param_count(parameters)
625
731
  parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
@@ -638,7 +744,7 @@ module Solargraph
638
744
  (pin.closure && pin.closure.docstring.has_tag?('abstract'))
639
745
  end
640
746
 
641
- # @param pin [Pin::Base]
747
+ # @param pin [Pin::Method]
642
748
  # @return [Array<Source::Chain>]
643
749
  def fake_args_for(pin)
644
750
  args = []
@@ -660,12 +766,48 @@ module Solargraph
660
766
  args
661
767
  end
662
768
 
769
+ # @return [Set<Integer>]
770
+ def sg_ignore_lines_processed
771
+ @sg_ignore_lines_processed ||= Set.new
772
+ end
773
+
774
+ # @return [Set<Integer>]
775
+ def all_sg_ignore_lines
776
+ source.associated_comments.select do |_line, text|
777
+ text.include?('@sg-ignore')
778
+ end.keys.to_set
779
+ end
780
+
781
+ # @return [Array<Integer>]
782
+ def unprocessed_sg_ignore_lines
783
+ (all_sg_ignore_lines - sg_ignore_lines_processed).to_a.sort
784
+ end
785
+
786
+ # @return [Array<Problem>]
787
+ def unneeded_sgignore_problems
788
+ return [] unless rules.validate_sg_ignores?
789
+
790
+ unprocessed_sg_ignore_lines.map do |line|
791
+ Problem.new(
792
+ Location.new(filename, Range.from_to(line, 0, line, 0)),
793
+ 'Unneeded @sg-ignore comment'
794
+ )
795
+ end
796
+ end
797
+
663
798
  # @param problems [Array<Problem>]
664
799
  # @return [Array<Problem>]
665
800
  def without_ignored problems
666
801
  problems.reject do |problem|
667
- node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
668
- node && source_map.source.comments_for(node)&.include?('@sg-ignore')
802
+ node = source.node_at(problem.location.range.start.line, problem.location.range.start.column)
803
+ ignored = node && source.comments_for(node)&.include?('@sg-ignore')
804
+ unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line)
805
+ # :nocov:
806
+ Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" }
807
+ # :nocov:
808
+ end
809
+ sg_ignore_lines_processed.add problem.location.range.start.line if ignored
810
+ ignored
669
811
  end
670
812
  end
671
813
  end