rucoa 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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