steep 0.44.1 → 0.45.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/ruby.yml +3 -2
  4. data/.gitignore +0 -1
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile +0 -2
  7. data/Gemfile.lock +77 -0
  8. data/lib/steep.rb +3 -1
  9. data/lib/steep/ast/builtin.rb +7 -1
  10. data/lib/steep/ast/types/factory.rb +19 -25
  11. data/lib/steep/diagnostic/ruby.rb +137 -60
  12. data/lib/steep/diagnostic/signature.rb +34 -0
  13. data/lib/steep/equatable.rb +21 -0
  14. data/lib/steep/interface/function.rb +798 -579
  15. data/lib/steep/server/interaction_worker.rb +238 -19
  16. data/lib/steep/services/file_loader.rb +26 -19
  17. data/lib/steep/services/hover_content.rb +131 -79
  18. data/lib/steep/source.rb +7 -10
  19. data/lib/steep/type_construction.rb +435 -502
  20. data/lib/steep/type_inference/block_params.rb +2 -5
  21. data/lib/steep/type_inference/method_params.rb +483 -0
  22. data/lib/steep/type_inference/send_args.rb +610 -128
  23. data/lib/steep/typing.rb +46 -21
  24. data/lib/steep/version.rb +1 -1
  25. data/sig/steep/type_inference/send_args.rbs +42 -0
  26. data/smoke/array/test_expectations.yml +3 -3
  27. data/smoke/block/c.rb +0 -1
  28. data/smoke/class/test_expectations.yml +12 -15
  29. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  30. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  31. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  32. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  33. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  34. data/smoke/diagnostics/test_expectations.yml +108 -31
  35. data/smoke/ensure/test_expectations.yml +3 -3
  36. data/smoke/enumerator/test_expectations.yml +1 -1
  37. data/smoke/literal/test_expectations.yml +2 -2
  38. data/smoke/method/test_expectations.yml +11 -10
  39. data/smoke/rescue/test_expectations.yml +3 -3
  40. data/smoke/toplevel/test_expectations.yml +3 -3
  41. data/smoke/tsort/test_expectations.yml +2 -2
  42. data/steep.gemspec +1 -1
  43. metadata +13 -5
@@ -7,6 +7,8 @@ module Steep
7
7
  HoverJob = Struct.new(:id, :path, :line, :column, keyword_init: true)
8
8
  CompletionJob = Struct.new(:id, :path, :line, :column, :trigger, keyword_init: true)
9
9
 
10
+ LSP = LanguageServer::Protocol
11
+
10
12
  attr_reader :service
11
13
 
12
14
  def initialize(project:, reader:, writer:, queue: Queue.new)
@@ -77,11 +79,12 @@ module Steep
77
79
  Steep.logger.info { "path=#{job.path}, line=#{job.line}, column=#{job.column}" }
78
80
 
79
81
  hover = Services::HoverContent.new(service: service)
80
- content = hover.content_for(path: job.path, line: job.line, column: job.column+1)
82
+ content = hover.content_for(path: job.path, line: job.line, column: job.column)
81
83
  if content
82
84
  range = content.location.yield_self do |location|
83
- start_position = { line: location.line - 1, character: location.column }
84
- end_position = { line: location.last_line - 1, character: location.last_column }
85
+ lsp_range = location.as_lsp_range
86
+ start_position = { line: lsp_range[:start][:line], character: lsp_range[:start][:character] }
87
+ end_position = { line: lsp_range[:end][:line], character: lsp_range[:end][:character] }
85
88
  { start: start_position, end: end_position }
86
89
  end
87
90
 
@@ -99,6 +102,36 @@ module Steep
99
102
 
100
103
  def format_hover(content)
101
104
  case content
105
+ when Services::HoverContent::TypeAliasContent
106
+ comment = content.decl.comment&.string || ''
107
+
108
+ <<-MD
109
+ #{comment}
110
+
111
+ ```rbs
112
+ #{retrieve_decl_information(content.decl)}
113
+ ```
114
+ MD
115
+ when Services::HoverContent::InterfaceContent
116
+ comment = content.decl.comment&.string || ''
117
+
118
+ <<-MD
119
+ #{comment}
120
+
121
+ ```rbs
122
+ #{retrieve_decl_information(content.decl)}
123
+ ```
124
+ MD
125
+ when Services::HoverContent::ClassContent
126
+ comment = content.decl.comment&.string || ''
127
+
128
+ <<-MD
129
+ #{comment}
130
+
131
+ ```rbs
132
+ #{retrieve_decl_information(content.decl)}
133
+ ```
134
+ MD
102
135
  when Services::HoverContent::VariableContent
103
136
  "`#{content.name}`: `#{content.type.to_s}`"
104
137
  when Services::HoverContent::MethodCallContent
@@ -151,32 +184,207 @@ HOVER
151
184
  Steep.logger.tagged("#response_to_completion") do
152
185
  Steep.measure "Generating response" do
153
186
  Steep.logger.info "path: #{job.path}, line: #{job.line}, column: #{job.column}, trigger: #{job.trigger}"
187
+ case
188
+ when target = project.target_for_source_path(job.path)
189
+ file = service.source_files[job.path] or return
190
+ subtyping = service.signature_services[target.name].current_subtyping or return
191
+
192
+ provider = Services::CompletionProvider.new(source_text: file.content, path: job.path, subtyping: subtyping)
193
+ items = begin
194
+ provider.run(line: job.line, column: job.column)
195
+ rescue Parser::SyntaxError
196
+ []
197
+ end
198
+
199
+ completion_items = items.map do |item|
200
+ format_completion_item(item)
201
+ end
202
+
203
+ Steep.logger.debug "items = #{completion_items.inspect}"
204
+
205
+ LSP::Interface::CompletionList.new(
206
+ is_incomplete: false,
207
+ items: completion_items
208
+ )
209
+ when (_, targets = project.targets_for_path(job.path))
210
+ target = targets[0] or return
211
+ sig_service = service.signature_services[target.name]
212
+ relative_path = job.path
213
+ buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files[relative_path].content)
214
+ pos = buffer.loc_to_pos([job.line, job.column])
215
+ prefix = buffer.content[0...pos].reverse[/\A[\w\d]*/].reverse
216
+
217
+ case sig_service.status
218
+ when Steep::Services::SignatureService::SyntaxErrorStatus, Steep::Services::SignatureService::AncestorErrorStatus
219
+ return
220
+ end
221
+
222
+ decls = sig_service.files[relative_path].decls
223
+ locator = RBS::Locator.new(decls: decls)
224
+
225
+ hd, tail = locator.find2(line: job.line, column: job.column)
226
+
227
+ namespace = []
228
+ tail.each do |t|
229
+ case t
230
+ when RBS::AST::Declarations::Module, RBS::AST::Declarations::Class
231
+ namespace << t.name.to_namespace
232
+ end
233
+ end
234
+ context = []
154
235
 
155
- target = project.target_for_source_path(job.path) or return
156
- file = service.source_files[job.path] or return
157
- subtyping = service.signature_services[target.name].current_subtyping or return
236
+ namespace.each do |ns|
237
+ context.map! { |n| ns + n }
238
+ context << ns
239
+ end
240
+
241
+ context.map!(&:absolute!)
242
+
243
+ class_items = sig_service.latest_env.class_decls.keys.map { |type_name|
244
+ format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
245
+ }.compact
246
+
247
+ alias_items = sig_service.latest_env.alias_decls.keys.map { |type_name|
248
+ format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
249
+ }.compact
250
+
251
+ interface_items = sig_service.latest_env.interface_decls.keys.map {|type_name|
252
+ format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
253
+ }.compact
158
254
 
159
- provider = Services::CompletionProvider.new(source_text: file.content, path: job.path, subtyping: subtyping)
160
- items = begin
161
- provider.run(line: job.line, column: job.column)
162
- rescue Parser::SyntaxError
163
- []
164
- end
255
+ completion_items = class_items + alias_items + interface_items
165
256
 
166
- completion_items = items.map do |item|
167
- format_completion_item(item)
257
+ LSP::Interface::CompletionList.new(
258
+ is_incomplete: false,
259
+ items: completion_items
260
+ )
168
261
  end
262
+ end
263
+ end
264
+ end
265
+
266
+ def format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
267
+ range = LanguageServer::Protocol::Interface::Range.new(
268
+ start: LanguageServer::Protocol::Interface::Position.new(
269
+ line: job.line - 1,
270
+ character: job.column - prefix.size
271
+ ),
272
+ end: LanguageServer::Protocol::Interface::Position.new(
273
+ line: job.line - 1,
274
+ character: job.column - prefix.size
275
+ )
276
+ )
169
277
 
170
- Steep.logger.debug "items = #{completion_items.inspect}"
278
+ name = relative_name_in_context(type_name, context).to_s
171
279
 
172
- LSP::Interface::CompletionList.new(
173
- is_incomplete: false,
174
- items: completion_items
175
- )
280
+ return unless name.start_with?(prefix)
281
+
282
+ case type_name.kind
283
+ when :class
284
+ class_decl = sig_service.latest_env.class_decls[type_name]&.decls[0]&.decl or raise
285
+
286
+ LanguageServer::Protocol::Interface::CompletionItem.new(
287
+ label: "#{name}",
288
+ documentation: format_comment(class_decl.comment),
289
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
290
+ range: range,
291
+ new_text: name
292
+ ),
293
+ kind: LSP::Constant::CompletionItemKind::CLASS,
294
+ insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
295
+
296
+ )
297
+ when :alias
298
+ alias_decl = sig_service.latest_env.alias_decls[type_name]&.decl or raise
299
+ LanguageServer::Protocol::Interface::CompletionItem.new(
300
+ label: "#{name}",
301
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
302
+ range: range,
303
+ new_text: name
304
+ ),
305
+ documentation: format_comment(alias_decl.comment),
306
+ # https://github.com/microsoft/vscode-languageserver-node/blob/6d78fc4d25719b231aba64a721a606f58b9e0a5f/client/src/common/client.ts#L624-L650
307
+ kind: LSP::Constant::CompletionItemKind::FIELD,
308
+ insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
309
+ )
310
+ when :interface
311
+ interface_decl = sig_service.latest_env.interface_decls[type_name]&.decl or raise
312
+
313
+ LanguageServer::Protocol::Interface::CompletionItem.new(
314
+ label: "#{name}",
315
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
316
+ range: range,
317
+ new_text: name
318
+ ),
319
+ documentation: format_comment(interface_decl.comment),
320
+ kind: LanguageServer::Protocol::Constant::CompletionItemKind::INTERFACE,
321
+ insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
322
+ )
323
+ end
324
+ end
325
+
326
+ def format_comment(comment)
327
+ if comment
328
+ LSP::Interface::MarkupContent.new(
329
+ kind: LSP::Constant::MarkupKind::MARKDOWN,
330
+ value: comment.string
331
+ )
332
+ end
333
+ end
334
+
335
+ def name_and_params(name, params)
336
+ if params.empty?
337
+ "#{name}"
338
+ else
339
+ ps = params.each.map do |param|
340
+ s = ""
341
+ if param.skip_validation
342
+ s << "unchecked "
343
+ end
344
+ case param.variance
345
+ when :invariant
346
+ # nop
347
+ when :covariant
348
+ s << "out "
349
+ when :contravariant
350
+ s << "in "
351
+ end
352
+ s + param.name.to_s
353
+ end
354
+
355
+ "#{name}[#{ps.join(", ")}]"
356
+ end
357
+ end
358
+
359
+ def name_and_args(name, args)
360
+ if name && args
361
+ if args.empty?
362
+ "#{name}"
363
+ else
364
+ "#{name}[#{args.join(", ")}]"
176
365
  end
177
366
  end
178
367
  end
179
368
 
369
+ def retrieve_decl_information(decl)
370
+ case decl
371
+ when RBS::AST::Declarations::Class
372
+ super_class = if super_class = decl.super_class
373
+ " < #{name_and_args(super_class.name, super_class.args)}"
374
+ end
375
+ "class #{name_and_params(decl.name, decl.type_params)}#{super_class}"
376
+ when RBS::AST::Declarations::Module
377
+ self_type = unless decl.self_types.empty?
378
+ " : #{decl.self_types.join(", ")}"
379
+ end
380
+ "module #{name_and_params(decl.name, decl.type_params)}#{self_type}"
381
+ when RBS::AST::Declarations::Alias
382
+ "type #{decl.name} = #{decl.type}"
383
+ when RBS::AST::Declarations::Interface
384
+ "interface #{name_and_params(decl.name, decl.type_params)}"
385
+ end
386
+ end
387
+
180
388
  def format_completion_item(item)
181
389
  range = LanguageServer::Protocol::Interface::Range.new(
182
390
  start: LanguageServer::Protocol::Interface::Position.new(
@@ -297,6 +505,17 @@ HOVER
297
505
 
298
506
  params.join(", ")
299
507
  end
508
+
509
+ def relative_name_in_context(type_name, context)
510
+ context.each do |namespace|
511
+ if (type_name.to_s == namespace.to_type_name.to_s || type_name.namespace.to_s == "::")
512
+ return RBS::TypeName.new(namespace: RBS::Namespace.empty, name: type_name.name)
513
+ elsif type_name.to_s.start_with?(namespace.to_s)
514
+ return TypeName(type_name.to_s.sub(namespace.to_type_name.to_s, '')).relative!
515
+ end
516
+ end
517
+ type_name
518
+ end
300
519
  end
301
520
  end
302
521
  end
@@ -8,29 +8,36 @@ module Steep
8
8
  end
9
9
 
10
10
  def each_path_in_patterns(pattern, commandline_patterns = [])
11
- pats = commandline_patterns.empty? ? pattern.patterns : commandline_patterns
12
-
13
- pats.each do |path|
14
- absolute_path = base_dir + path
15
-
16
- if absolute_path.file?
17
- yield absolute_path.relative_path_from(base_dir)
18
- else
19
- files = if absolute_path.directory?
20
- Pathname.glob("#{absolute_path}/**/*#{pattern.ext}")
21
- else
22
- Pathname.glob(absolute_path)
23
- end
24
-
25
- files.sort.each do |source_path|
26
- if source_path.file?
27
- relative_path = source_path.relative_path_from(base_dir)
28
- unless pattern.ignore?(relative_path)
29
- yield relative_path
11
+ if block_given?
12
+ pats = commandline_patterns.empty? ? pattern.patterns : commandline_patterns
13
+
14
+ pats.each do |path|
15
+ absolute_path = base_dir + path
16
+
17
+ if absolute_path.file?
18
+ if pattern =~ path
19
+ yield absolute_path.relative_path_from(base_dir)
20
+ end
21
+ else
22
+ files = if absolute_path.directory?
23
+ Pathname.glob("#{absolute_path}/**/*#{pattern.ext}")
24
+ else
25
+ Pathname.glob(absolute_path)
26
+ end
27
+
28
+ files.sort.each do |source_path|
29
+ if source_path.file?
30
+ relative_path = source_path.relative_path_from(base_dir)
31
+ unless pattern.ignore?(relative_path)
32
+ yield relative_path
33
+ end
30
34
  end
31
35
  end
32
36
  end
37
+
33
38
  end
39
+ else
40
+ enum_for :each_path_in_patterns, pattern, commandline_patterns
34
41
  end
35
42
  end
36
43
 
@@ -5,6 +5,10 @@ module Steep
5
5
  VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true)
6
6
  MethodCallContent = Struct.new(:node, :method_name, :type, :definition, :location, keyword_init: true)
7
7
  DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true) do
8
+ TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
9
+ ClassContent = Struct.new(:location, :decl, keyword_init: true)
10
+ InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
11
+
8
12
  def comment_string
9
13
  if comments = definition&.comments
10
14
  comments.map {|c| c.string.chomp }.uniq.join("\n----\n")
@@ -50,99 +54,147 @@ module Steep
50
54
  end
51
55
 
52
56
  def content_for(path:, line:, column:)
53
- target = project.target_for_source_path(path)
57
+ target_for_code, targets_for_sigs = project.targets_for_path(path)
58
+
59
+ case
60
+ when target = target_for_code
61
+ Steep.logger.info "target #{target}"
62
+
63
+ hover_for_source(column, line, path, target)
64
+
65
+ when target = targets_for_sigs[0]
66
+ service = self.service.signature_services[target.name]
67
+
68
+ _buffer, decls = service.latest_env.buffers_decls.find do |buffer, _|
69
+ Pathname(buffer.name) == path
70
+ end
71
+
72
+ return if decls.nil?
73
+
74
+ locator = RBS::Locator.new(decls: decls)
75
+ hd, tail = locator.find2(line: line, column: column)
76
+
77
+ case type = tail[0]
78
+ when RBS::Types::Alias
79
+ alias_decl = service.latest_env.alias_decls[type.name]&.decl or raise
80
+
81
+ location = tail[0].location
82
+ TypeAliasContent.new(
83
+ location: location,
84
+ decl: alias_decl
85
+ )
86
+ when RBS::Types::ClassInstance, RBS::Types::ClassSingleton
87
+ if hd == :name
88
+ env = service.latest_env
89
+ class_decl = env.class_decls[type.name]&.decls[0]&.decl or raise
90
+ location = tail[0].location[:name]
91
+ ClassContent.new(
92
+ location: location,
93
+ decl: class_decl
94
+ )
95
+ end
96
+ when RBS::Types::Interface
97
+ env = service.latest_env
98
+ interface_decl = env.interface_decls[type.name]&.decl or raise
99
+ location = type.location[:name]
100
+
101
+ InterfaceContent.new(
102
+ location: location,
103
+ decl: interface_decl
104
+ )
105
+ end
106
+ end
107
+ end
54
108
 
55
- if target
56
- file = service.source_files[path]
57
- typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
109
+ def hover_for_source(column, line, path, target)
110
+ file = service.source_files[path]
111
+ typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
112
+ node, *parents = typing.source.find_nodes(line: line, column: column)
58
113
 
59
- node, *parents = typing.source.find_nodes(line: line, column: column)
114
+ if node
115
+ case node.type
116
+ when :lvar
117
+ var_name = node.children[0]
118
+ context = typing.context_at(line: line, column: column)
119
+ var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
60
120
 
61
- if node
62
- case node.type
63
- when :lvar
64
- var_name = node.children[0]
65
- context = typing.context_at(line: line, column: column)
66
- var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
121
+ VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
122
+ when :lvasgn
123
+ var_name, rhs = node.children
124
+ context = typing.context_at(line: line, column: column)
125
+ type = context.lvar_env[var_name] || typing.type_of(node: rhs)
67
126
 
68
- VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
69
- when :lvasgn
70
- var_name, rhs = node.children
71
- context = typing.context_at(line: line, column: column)
72
- type = context.lvar_env[var_name] || typing.type_of(node: rhs)
127
+ VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
128
+ when :send
129
+ receiver, method_name, *_ = node.children
73
130
 
74
- VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
75
- when :send
76
- receiver, method_name, *_ = node.children
77
131
 
132
+ result_node = if parents[0]&.type == :block
133
+ parents[0]
134
+ else
135
+ node
136
+ end
78
137
 
79
- result_node = if parents[0]&.type == :block
80
- parents[0]
138
+ context = typing.context_at(line: line, column: column)
139
+
140
+ receiver_type = if receiver
141
+ typing.type_of(node: receiver)
81
142
  else
82
- node
143
+ context.self_type
83
144
  end
84
145
 
85
- context = typing.context_at(line: line, column: column)
86
-
87
- receiver_type = if receiver
88
- typing.type_of(node: receiver)
89
- else
90
- context.self_type
91
- end
92
-
93
- factory = context.type_env.subtyping.factory
94
- method_name, definition = case receiver_type
95
- when AST::Types::Name::Instance
96
- method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
97
- if method_definition&.defined_in
98
- owner_name = method_definition.defined_in
99
- [
100
- InstanceMethodName.new(owner_name, method_name),
101
- method_definition
102
- ]
103
- end
104
- when AST::Types::Name::Singleton
105
- method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
106
- if method_definition&.defined_in
107
- owner_name = method_definition.defined_in
108
- [
109
- SingletonMethodName.new(owner_name, method_name),
110
- method_definition
111
- ]
112
- end
113
- else
114
- nil
146
+ factory = context.type_env.subtyping.factory
147
+ method_name, definition = case receiver_type
148
+ when AST::Types::Name::Instance
149
+ method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
150
+ if method_definition&.defined_in
151
+ owner_name = method_definition.defined_in
152
+ [
153
+ InstanceMethodName.new(owner_name, method_name),
154
+ method_definition
155
+ ]
115
156
  end
116
-
117
- MethodCallContent.new(
118
- node: node,
119
- method_name: method_name,
120
- type: typing.type_of(node: result_node),
121
- definition: definition,
122
- location: result_node.location.expression
123
- )
124
- when :def, :defs
125
- context = typing.context_at(line: line, column: column)
126
- method_context = context.method_context
127
-
128
- if method_context && method_context.method
129
- DefinitionContent.new(
130
- node: node,
131
- method_name: method_context.name,
132
- method_type: method_context.method_type,
133
- definition: method_context.method,
134
- location: node.loc.expression
135
- )
136
- end
137
- else
138
- type = typing.type_of(node: node)
139
-
140
- TypeContent.new(
157
+ when AST::Types::Name::Singleton
158
+ method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
159
+ if method_definition&.defined_in
160
+ owner_name = method_definition.defined_in
161
+ [
162
+ SingletonMethodName.new(owner_name, method_name),
163
+ method_definition
164
+ ]
165
+ end
166
+ else
167
+ nil
168
+ end
169
+
170
+ MethodCallContent.new(
171
+ node: node,
172
+ method_name: method_name,
173
+ type: typing.type_of(node: result_node),
174
+ definition: definition,
175
+ location: result_node.location.expression
176
+ )
177
+ when :def, :defs
178
+ context = typing.context_at(line: line, column: column)
179
+ method_context = context.method_context
180
+
181
+ if method_context && method_context.method
182
+ DefinitionContent.new(
141
183
  node: node,
142
- type: type,
143
- location: node.location.expression
184
+ method_name: method_context.name,
185
+ method_type: method_context.method_type,
186
+ definition: method_context.method,
187
+ location: node.loc.expression
144
188
  )
145
189
  end
190
+ else
191
+ type = typing.type_of(node: node)
192
+
193
+ TypeContent.new(
194
+ node: node,
195
+ type: type,
196
+ location: node.location.expression
197
+ )
146
198
  end
147
199
  end
148
200
  end