rucoa 0.10.0 → 0.11.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.
@@ -180,16 +180,76 @@ module Rucoa
180
180
 
181
181
  # @param type [String]
182
182
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
183
- # @example includes ancestors' methods
183
+ # @example supports simple instance method
184
+ # source = Rucoa::Source.new(
185
+ # content: <<~RUBY,
186
+ # class A
187
+ # def foo
188
+ # end
189
+ # end
190
+ # RUBY
191
+ # uri: 'file:///path/to/example.rb'
192
+ # )
193
+ # definition_store = Rucoa::DefinitionStore.new
194
+ # definition_store.update_from(source)
195
+ # subject = definition_store.instance_method_definitions_of('A')
196
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
197
+ # @example supports inheritance
198
+ # source = Rucoa::Source.new(
199
+ # content: <<~RUBY,
200
+ # class A
201
+ # def foo
202
+ # end
203
+ # end
204
+ #
205
+ # class B < A
206
+ # end
207
+ # RUBY
208
+ # uri: 'file:///path/to/example.rb'
209
+ # )
210
+ # definition_store = Rucoa::DefinitionStore.new
211
+ # definition_store.update_from(source)
212
+ # subject = definition_store.instance_method_definitions_of('B')
213
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
214
+ # @example supports `include`
215
+ # source = Rucoa::Source.new(
216
+ # content: <<~RUBY,
217
+ # module A
218
+ # def foo
219
+ # end
220
+ # end
221
+ #
222
+ # class B
223
+ # include A
224
+ # end
225
+ # RUBY
226
+ # uri: 'file:///path/to/example.rb'
227
+ # )
184
228
  # definition_store = Rucoa::DefinitionStore.new
185
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
186
- # subject = definition_store.instance_method_definitions_of('File')
187
- # expect(subject.map(&:qualified_name)).to include('IO#raw')
188
- # @example responds to `singleton<File>`
229
+ # definition_store.update_from(source)
230
+ # subject = definition_store.instance_method_definitions_of('B')
231
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
232
+ # @example supports `prepend`
233
+ # source = Rucoa::Source.new(
234
+ # content: <<~RUBY,
235
+ # module A
236
+ # def foo
237
+ # end
238
+ # end
239
+ #
240
+ # class B
241
+ # prepend A
242
+ #
243
+ # def foo
244
+ # end
245
+ # end
246
+ # RUBY
247
+ # uri: 'file:///path/to/example.rb'
248
+ # )
189
249
  # definition_store = Rucoa::DefinitionStore.new
190
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
191
- # subject = definition_store.instance_method_definitions_of('singleton<File>')
192
- # expect(subject.map(&:qualified_name)).to include('IO.write')
250
+ # definition_store.update_from(source)
251
+ # subject = definition_store.instance_method_definitions_of('B')
252
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
193
253
  def instance_method_definitions_of(type)
194
254
  singleton_class_name = singleton_class_name_from(type)
195
255
  return singleton_method_definitions_of(singleton_class_name) if singleton_class_name
@@ -197,31 +257,87 @@ module Rucoa
197
257
  class_or_module_definition = find_definition_by_qualified_name(type)
198
258
  return [] unless class_or_module_definition
199
259
 
200
- method_definitions = instance_method_definitions
260
+ method_definitions = self.method_definitions
201
261
  ancestors_of(class_or_module_definition).flat_map do |ancestor|
202
262
  method_definitions.select do |method_definition|
203
- method_definition.namespace == ancestor.qualified_name
263
+ method_definition.namespace == ancestor.qualified_name &&
264
+ method_definition.instance_method?
204
265
  end
205
- end
266
+ end.uniq(&:method_name)
206
267
  end
207
268
 
208
269
  # @param type [String]
209
270
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
210
- # @example returns singleton method definitions of File
271
+ # @example supports simple singleton method
272
+ # source = Rucoa::Source.new(
273
+ # content: <<~RUBY,
274
+ # class A
275
+ # def self.foo
276
+ # end
277
+ # end
278
+ # RUBY
279
+ # uri: 'file:///path/to/example.rb'
280
+ # )
281
+ # definition_store = Rucoa::DefinitionStore.new
282
+ # definition_store.update_from(source)
283
+ # subject = definition_store.singleton_method_definitions_of('A')
284
+ # expect(subject.map(&:qualified_name)).to include('A.foo')
285
+ # @example supports super class's singleton method
286
+ # source = Rucoa::Source.new(
287
+ # content: <<~RUBY,
288
+ # class A
289
+ # def self.foo
290
+ # end
291
+ # end
292
+ #
293
+ # class B < A
294
+ # end
295
+ # RUBY
296
+ # uri: 'file:///path/to/example.rb'
297
+ # )
298
+ # definition_store = Rucoa::DefinitionStore.new
299
+ # definition_store.update_from(source)
300
+ # subject = definition_store.singleton_method_definitions_of('B')
301
+ # expect(subject.map(&:qualified_name)).to include('A.foo')
302
+ # @example supports extended module's instance method
303
+ # source = Rucoa::Source.new(
304
+ # content: <<~RUBY,
305
+ # module A
306
+ # def foo
307
+ # end
308
+ # end
309
+ #
310
+ # class B
311
+ # def self.foo
312
+ # end
313
+ # end
314
+ #
315
+ # class C < B
316
+ # extend A
317
+ # end
318
+ # RUBY
319
+ # uri: 'file:///path/to/example.rb'
320
+ # )
211
321
  # definition_store = Rucoa::DefinitionStore.new
212
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
213
- # subject = definition_store.singleton_method_definitions_of('File')
214
- # expect(subject.map(&:qualified_name)).to include('IO.write')
322
+ # definition_store.update_from(source)
323
+ # subject = definition_store.singleton_method_definitions_of('C')
324
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
215
325
  def singleton_method_definitions_of(type)
216
326
  class_or_module_definition = find_definition_by_qualified_name(type)
217
327
  return [] unless class_or_module_definition
218
328
 
219
- method_definitions = singleton_method_definitions
329
+ method_definitions = self.method_definitions
220
330
  ancestors_of(class_or_module_definition).flat_map do |ancestor|
221
331
  method_definitions.select do |method_definition|
222
- method_definition.namespace == ancestor.qualified_name
332
+ method_definition.namespace == ancestor.qualified_name &&
333
+ method_definition.singleton_method?
334
+ end + ancestor.extended_module_qualified_names.flat_map do |extended_module_qualified_name|
335
+ method_definitions.select do |method_definition|
336
+ method_definition.namespace == extended_module_qualified_name &&
337
+ method_definition.instance_method?
338
+ end
223
339
  end
224
- end
340
+ end.uniq(&:method_name)
225
341
  end
226
342
 
227
343
  # @param namespace [String]
@@ -232,6 +348,7 @@ module Rucoa
232
348
  end
233
349
  end
234
350
 
351
+ # @todo Search ancestors.
235
352
  # @param unqualified_name [Rucoa::UnqualifiedName]
236
353
  # @return [String]
237
354
  def resolve_constant(unqualified_name)
@@ -258,7 +375,7 @@ module Rucoa
258
375
  # @param type [String]
259
376
  # @return [String, nil]
260
377
  def singleton_class_name_from(type)
261
- type[/singleton<(\w+)>/, 1]
378
+ type[/singleton<([\w:]+)>/, 1]
262
379
  end
263
380
 
264
381
  # @param definition [Rucoa::Definitions::Class, Rucoa::Definitions::Module]
@@ -311,16 +428,6 @@ module Rucoa
311
428
  definitions.grep(Definitions::MethodDefinition)
312
429
  end
313
430
 
314
- # @return [Array<Rucoa::Definition::MethodDefinition>]
315
- def instance_method_definitions
316
- method_definitions.select(&:instance_method?)
317
- end
318
-
319
- # @return [Array<Rucoa::Definition::MethodDefinition>]
320
- def singleton_method_definitions
321
- method_definitions.select(&:singleton_method?)
322
- end
323
-
324
431
  # @return [Array<Rucoa::Definition::ConstantDefinition>]
325
432
  def constant_definitions
326
433
  definitions.grep(Definitions::ConstantDefinition)
@@ -348,6 +455,11 @@ module Rucoa
348
455
  definition.super_class_qualified_name = resolve_constant(definition.super_class_unqualified_name)
349
456
  definition.super_class_unqualified_name = nil
350
457
 
458
+ definition.extended_module_qualified_names = definition.extended_module_unqualified_names.map do |unqualified_name|
459
+ resolve_constant(unqualified_name)
460
+ end
461
+ definition.extended_module_unqualified_names = []
462
+
351
463
  definition.included_module_qualified_names = definition.included_module_unqualified_names.map do |unqualified_name|
352
464
  resolve_constant(unqualified_name)
353
465
  end
@@ -3,6 +3,12 @@
3
3
  module Rucoa
4
4
  module Definitions
5
5
  class ModuleDefinition < ConstantDefinition
6
+ # @return [Array<String>]
7
+ attr_accessor :extended_module_qualified_names
8
+
9
+ # @return [Array<Rucoa::UnqualifiedName>]
10
+ attr_accessor :extended_module_unqualified_names
11
+
6
12
  # @return [Array<String>]
7
13
  attr_accessor :included_module_qualified_names
8
14
 
@@ -15,11 +21,15 @@ module Rucoa
15
21
  # @return [Array<Rucoa::UnqualifiedName>]
16
22
  attr_accessor :prepended_module_unqualified_names
17
23
 
24
+ # @param extended_module_qualified_names [Array<String>]
25
+ # @param extended_module_unqualified_names [Array<String>]
18
26
  # @param included_module_qualified_names [Array<String>]
19
27
  # @param included_module_unqualified_names [Array<String>]
20
28
  # @param prepended_module_qualified_names [Array<String>]
21
29
  # @param prepended_module_unqualified_names [Array<String>]
22
30
  def initialize(
31
+ extended_module_qualified_names: [],
32
+ extended_module_unqualified_names: [],
23
33
  included_module_qualified_names: [],
24
34
  included_module_unqualified_names: [],
25
35
  prepended_module_qualified_names: [],
@@ -27,6 +37,8 @@ module Rucoa
27
37
  **keyword_arguments
28
38
  )
29
39
  super(**keyword_arguments)
40
+ @extended_module_qualified_names = extended_module_qualified_names
41
+ @extended_module_unqualified_names = extended_module_unqualified_names
30
42
  @included_module_qualified_names = included_module_qualified_names
31
43
  @included_module_unqualified_names = included_module_unqualified_names
32
44
  @prepended_module_qualified_names = prepended_module_qualified_names
@@ -36,6 +48,8 @@ module Rucoa
36
48
  # @param other [Rucoa::Definitions::ModuleDefinition]
37
49
  # @return [Rucoa::Definitions::ModuleDefinition]
38
50
  def merge!(other)
51
+ self.extended_module_qualified_names |= other.extended_module_qualified_names
52
+ self.extended_module_unqualified_names |= other.extended_module_unqualified_names
39
53
  self.included_module_qualified_names |= other.included_module_qualified_names
40
54
  self.included_module_unqualified_names |= other.included_module_unqualified_names
41
55
  self.prepended_module_qualified_names |= other.prepended_module_qualified_names
@@ -51,7 +51,7 @@ module Rucoa
51
51
  def return_types
52
52
  case @node.type
53
53
  when :const
54
- ["singleton<#{@node.name}>"]
54
+ return_types_for_const
55
55
  when :lvar
56
56
  return_types_for_lvar
57
57
  when :send
@@ -113,6 +113,17 @@ module Rucoa
113
113
  @node.each_ancestor(:def).first&.qualified_name
114
114
  end
115
115
 
116
+ # @return [Array<String>]
117
+ def return_types_for_const
118
+ qualified_name = @definition_store.resolve_constant(
119
+ UnqualifiedName.new(
120
+ chained_name: @node.chained_name,
121
+ module_nesting: @node.module_nesting
122
+ )
123
+ )
124
+ ["singleton<#{qualified_name}>"]
125
+ end
126
+
116
127
  # @return [Array<String>]
117
128
  def return_types_for_lvar
118
129
  qualified_name = nearest_def_qualified_name
@@ -12,6 +12,7 @@ module Rucoa
12
12
  def call
13
13
  Definitions::ClassDefinition.new(
14
14
  description: description,
15
+ extended_module_qualified_names: extended_module_qualified_names,
15
16
  included_module_qualified_names: included_module_qualified_names,
16
17
  location: location,
17
18
  prepended_module_qualified_names: prepended_module_qualified_names,
@@ -20,6 +20,7 @@ module Rucoa
20
20
  def call
21
21
  Definitions::ModuleDefinition.new(
22
22
  description: description,
23
+ extended_module_qualified_names: extended_module_qualified_names,
23
24
  included_module_qualified_names: included_module_qualified_names,
24
25
  location: location,
25
26
  prepended_module_qualified_names: prepended_module_qualified_names,
@@ -44,21 +45,25 @@ module Rucoa
44
45
  Location.from_rbs_location(@declaration.location)
45
46
  end
46
47
 
48
+ # @return [Array<String>]
49
+ def extended_module_qualified_names
50
+ module_qualified_names_for(::RBS::AST::Members::Extend)
51
+ end
52
+
47
53
  # @return [Array<String>]
48
54
  def included_module_qualified_names
49
- @declaration.members.filter_map do |member|
50
- case member
51
- when ::RBS::AST::Members::Include
52
- member.name.to_s.delete_prefix('::')
53
- end
54
- end
55
+ module_qualified_names_for(::RBS::AST::Members::Include)
55
56
  end
56
57
 
57
58
  # @return [Array<String>]
58
59
  def prepended_module_qualified_names
60
+ module_qualified_names_for(::RBS::AST::Members::Prepend)
61
+ end
62
+
63
+ def module_qualified_names_for(member_class)
59
64
  @declaration.members.filter_map do |member|
60
65
  case member
61
- when ::RBS::AST::Members::Prepend
66
+ when member_class
62
67
  member.name.to_s.delete_prefix('::')
63
68
  end
64
69
  end
data/lib/rucoa/server.rb CHANGED
@@ -43,8 +43,8 @@ module Rucoa
43
43
  # @param io_in [IO]
44
44
  # @param io_out [IO]
45
45
  def initialize(
46
- io_log: ::StringIO.new,
47
46
  io_in: ::StringIO.new,
47
+ io_log: ::StringIO.new,
48
48
  io_out: ::StringIO.new
49
49
  )
50
50
  @logger = ::Logger.new(io_log)
data/lib/rucoa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rucoa
4
- VERSION = '0.10.0'
4
+ VERSION = '0.11.0'
5
5
  end
@@ -35,10 +35,26 @@ module Rucoa
35
35
  # Foo#bar=
36
36
  # ]
37
37
  # )
38
+ # @example ignores unrecognizable attributes
39
+ # definitions = Rucoa::Source.new(
40
+ # content: <<~RUBY,
41
+ # class Foo
42
+ # attr_reader foo
43
+ # end
44
+ # RUBY
45
+ # uri: '/path/to/foo.rb'
46
+ # ).definitions
47
+ # expect(definitions.map(&:qualified_name)).to eq(
48
+ # %w[
49
+ # Foo
50
+ # ]
51
+ # )
38
52
  def call
39
53
  return [] unless @node.is_a?(Nodes::SendNode) && READER_METHOD_NAMES.include?(@node.name)
40
54
 
41
- @node.arguments.map do |argument|
55
+ @node.arguments.filter_map do |argument|
56
+ next unless argument.respond_to?(:value)
57
+
42
58
  Definitions::MethodDefinition.new(
43
59
  description: description,
44
60
  kind: :instance,
@@ -35,10 +35,26 @@ module Rucoa
35
35
  # Foo#bar=
36
36
  # ]
37
37
  # )
38
+ # @example ignores unrecognizable attributes
39
+ # definitions = Rucoa::Source.new(
40
+ # content: <<~RUBY,
41
+ # class Foo
42
+ # attr_writer foo
43
+ # end
44
+ # RUBY
45
+ # uri: '/path/to/foo.rb'
46
+ # ).definitions
47
+ # expect(definitions.map(&:qualified_name)).to eq(
48
+ # %w[
49
+ # Foo
50
+ # ]
51
+ # )
38
52
  def call
39
53
  return [] unless @node.is_a?(Nodes::SendNode) && WRITER_METHOD_NAMES.include?(@node.name)
40
54
 
41
- @node.arguments.map do |argument|
55
+ @node.arguments.filter_map do |argument|
56
+ next unless argument.respond_to?(:value)
57
+
42
58
  Definitions::MethodDefinition.new(
43
59
  description: description,
44
60
  kind: :instance,
@@ -49,6 +49,7 @@ module Rucoa
49
49
  [
50
50
  Definitions::ClassDefinition.new(
51
51
  description: description,
52
+ extended_module_unqualified_names: extended_module_unqualified_names,
52
53
  included_module_unqualified_names: included_module_unqualified_names,
53
54
  location: location,
54
55
  prepended_module_unqualified_names: prepended_module_unqualified_names,
@@ -22,6 +22,19 @@ module Rucoa
22
22
  # uri: '/path/to/foo.rb'
23
23
  # ).definitions
24
24
  # expect(definitions[0]).to be_a(Rucoa::Definitions::MethodDefinition)
25
+ # @example returns method definition for another style of singleton def node
26
+ # definitions = Rucoa::Source.new(
27
+ # content: <<~RUBY,
28
+ # class Foo
29
+ # class << self
30
+ # def bar
31
+ # end
32
+ # end
33
+ # end
34
+ # RUBY
35
+ # uri: '/path/to/foo.rb'
36
+ # ).definitions
37
+ # expect(definitions[1].qualified_name).to eq('Foo.bar')
25
38
  def call
26
39
  return [] unless @node.is_a?(Nodes::DefNode) || @node.is_a?(Nodes::DefsNode)
27
40
 
@@ -19,6 +19,7 @@ module Rucoa
19
19
  [
20
20
  Definitions::ModuleDefinition.new(
21
21
  description: description,
22
+ extended_module_unqualified_names: extended_module_unqualified_names,
22
23
  included_module_unqualified_names: included_module_unqualified_names,
23
24
  location: location,
24
25
  prepended_module_unqualified_names: prepended_module_unqualified_names,
@@ -29,6 +30,11 @@ module Rucoa
29
30
 
30
31
  private
31
32
 
33
+ # @return [Array<Rucoa::UnqualifiedName>]
34
+ def extended_module_unqualified_names
35
+ unqualified_names_for('extend')
36
+ end
37
+
32
38
  # @return [Array<Rucoa::UnqualifiedName>]
33
39
  def included_module_unqualified_names
34
40
  unqualified_names_for('include')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rucoa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-24 00:00:00.000000000 Z
11
+ date: 2022-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser