docrb-parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +75 -0
  4. data/Rakefile +12 -0
  5. data/docrb-parser.gemspec +38 -0
  6. data/lib/docrb/core_extensions.rb +60 -0
  7. data/lib/docrb/parser/attribute.rb +25 -0
  8. data/lib/docrb/parser/call.rb +27 -0
  9. data/lib/docrb/parser/class.rb +94 -0
  10. data/lib/docrb/parser/comment.rb +40 -0
  11. data/lib/docrb/parser/comment_parser.rb +290 -0
  12. data/lib/docrb/parser/computations.rb +471 -0
  13. data/lib/docrb/parser/constant.rb +19 -0
  14. data/lib/docrb/parser/container.rb +305 -0
  15. data/lib/docrb/parser/deferred_singleton_class.rb +17 -0
  16. data/lib/docrb/parser/location.rb +43 -0
  17. data/lib/docrb/parser/method.rb +62 -0
  18. data/lib/docrb/parser/method_parameters.rb +85 -0
  19. data/lib/docrb/parser/module.rb +50 -0
  20. data/lib/docrb/parser/node_array.rb +24 -0
  21. data/lib/docrb/parser/reference.rb +25 -0
  22. data/lib/docrb/parser/reloader.rb +19 -0
  23. data/lib/docrb/parser/resolved_reference.rb +26 -0
  24. data/lib/docrb/parser/version.rb +7 -0
  25. data/lib/docrb/parser/virtual_container.rb +21 -0
  26. data/lib/docrb/parser/virtual_location.rb +9 -0
  27. data/lib/docrb/parser/virtual_method.rb +19 -0
  28. data/lib/docrb/parser.rb +139 -0
  29. data/lib/docrb-parser.rb +3 -0
  30. data/sig/docrb/core_extensions.rbs +24 -0
  31. data/sig/docrb/parser/attribute.rbs +18 -0
  32. data/sig/docrb/parser/call.rbs +17 -0
  33. data/sig/docrb/parser/class.rbs +34 -0
  34. data/sig/docrb/parser/comment.rbs +14 -0
  35. data/sig/docrb/parser/comment_parser.rbs +79 -0
  36. data/sig/docrb/parser/constant.rbs +15 -0
  37. data/sig/docrb/parser/container.rbs +91 -0
  38. data/sig/docrb/parser/deferred_singleton_class.rbs +12 -0
  39. data/sig/docrb/parser/location.rbs +24 -0
  40. data/sig/docrb/parser/method.rbs +34 -0
  41. data/sig/docrb/parser/method_parameters.rbs +34 -0
  42. data/sig/docrb/parser/module.rbs +14 -0
  43. data/sig/docrb/parser/node_array.rbs +12 -0
  44. data/sig/docrb/parser/reference.rbs +19 -0
  45. data/sig/docrb/parser/reloader.rbs +7 -0
  46. data/sig/docrb/parser/resolved_reference.rbs +22 -0
  47. data/sig/docrb/parser/virtual_method.rbs +17 -0
  48. data/sig/docrb/parser.rbs +5 -0
  49. metadata +109 -0
@@ -0,0 +1,305 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Container
6
+ visible_attr_accessor :classes, :modules, :defined_by, :instance_methods,
7
+ :class_methods, :extends, :includes, :name, :path_segments,
8
+ :instance_attributes, :class_attributes, :current_visibility_modifier,
9
+ :constants, :location
10
+ attr_accessor :parent, :parser, :doc
11
+
12
+ SINGLETON_CLASS_TYPES = %i[
13
+ singleton_class_node
14
+ deferred_singleton_class_node
15
+ ].freeze
16
+
17
+ def initialize(parser, parent, node)
18
+ @object_id = parser.make_id(self)
19
+ @node = node
20
+ @parser = parser
21
+ @explicit_instance_visibility = {}
22
+ @explicit_class_visibility = {}
23
+ @constants = NodeArray.new
24
+ @classes = NodeArray.new
25
+ @modules = NodeArray.new
26
+ @includes = NodeArray.new
27
+ @extends = NodeArray.new
28
+ @defined_by = []
29
+ @instance_methods = NodeArray.new
30
+ @class_methods = NodeArray.new
31
+ @class_attributes = NodeArray.new
32
+ @instance_attributes = NodeArray.new
33
+ @inside_module_function = false
34
+ @location = parser.location(node.location)
35
+ @parent = parent
36
+
37
+ @name = if SINGLETON_CLASS_TYPES.include? node&.type
38
+ nil
39
+ elsif node
40
+ name = parser.unfurl_constant_path(node.constant_path)
41
+ @path_segments = name
42
+ name.pop
43
+ end
44
+
45
+ node&.body&.body&.each { handle_node(_1) }
46
+ end
47
+
48
+ def full_path(relative_to: nil)
49
+ path = [self] + (parent&.full_path || [])
50
+ return path unless relative_to
51
+
52
+ rel_path = relative_to.full_path
53
+ while rel_path.last == path.last
54
+ rel_path.pop
55
+ path.pop
56
+ end
57
+
58
+ path
59
+ end
60
+
61
+ def extract_references(from, attr)
62
+ NodeArray.new(from
63
+ .filter(&:fulfilled?)
64
+ .map { _1.resolved.id }
65
+ .map { parser.object_by_id(_1) }
66
+ .map { _1.send(attr) }
67
+ .flatten)
68
+ end
69
+
70
+ def unowned_classes = extract_references(@includes, :all_classes)
71
+
72
+ def all_classes = NodeArray.new(@classes).tap { _1.merge_unowned(*unowned_classes) }
73
+
74
+ def unowned_modules = extract_references(@includes, :all_modules)
75
+
76
+ def all_modules = NodeArray.new(@modules).tap { _1.merge_unowned(*unowned_modules) }
77
+
78
+ def unowned_instance_methods = extract_references(@includes, :all_instance_methods)
79
+
80
+ def all_instance_methods = NodeArray.new(@instance_methods).tap { _1.merge_unowned(*unowned_instance_methods) }
81
+
82
+ def unowned_class_methods = extract_references(@extends, :all_instance_methods)
83
+
84
+ def all_class_methods = NodeArray.new(@class_methods).tap { _1.merge_unowned(*unowned_class_methods) }
85
+
86
+ def unowned_class_attributes = extract_references(@extends, :all_instance_attributes)
87
+
88
+ def all_class_attributes = NodeArray.new(@class_attributes).tap { _1.merge_unowned(*unowned_class_attributes) }
89
+
90
+ def unowned_instance_attributes = extract_references(@includes, :all_instance_attributes)
91
+
92
+ def all_instance_attributes
93
+ NodeArray.new(@instance_attributes)
94
+ .tap { _1.merge_unowned(*unowned_instance_attributes) }
95
+ end
96
+
97
+ def all_objects
98
+ NodeArray.new(
99
+ all_classes + all_modules + all_instance_methods + all_classes +
100
+ all_class_attributes + all_instance_attributes
101
+ )
102
+ end
103
+
104
+ def id = @object_id
105
+
106
+ def source_of(obj)
107
+ parent = obj.parent.id
108
+ case
109
+ when parent == id
110
+ :self
111
+ when @includes.filter(&:fulfilled?).any? { _1.resolved.id == id }
112
+ :included
113
+ when @extends.filter(&:fulfilled?).any? { _1.resolved.id == id }
114
+ :extended
115
+ else
116
+ :unknown
117
+ end
118
+ end
119
+
120
+ def handle_parsed_node(_parser, _node) = nil
121
+
122
+ def instance_method_added(_parser, _node, _method) = nil
123
+
124
+ def class_method_added(_parser, _node, _method) = nil
125
+
126
+ def class_added(_parser, _node, _method) = nil
127
+
128
+ def module_added(_parser, _node, _method) = nil
129
+
130
+ def handle_call(call)
131
+ case call.name
132
+ when :extend
133
+ extends.append(*call.arguments.map { reference(_1) })
134
+ true
135
+ when :include
136
+ includes.append(*call.arguments.map { reference(_1) })
137
+ true
138
+ when :attr, :attr_reader
139
+ add_attribute(parser, call, :reader)
140
+ true
141
+ when :attr_accessor
142
+ add_attribute(parser, call, :accessor)
143
+ true
144
+ when :attr_writer
145
+ add_attribute(parser, call, :writer)
146
+ true
147
+ when :private, :public, :protected
148
+ handle_visibility_modifier(parser, call)
149
+ true
150
+ when :private_class_method, :public_class_method
151
+ handle_singleton_visibility_modifier(parser, call)
152
+ true
153
+ else
154
+ false
155
+ end
156
+ end
157
+
158
+ def handle_node(node)
159
+ case v = parse_node(node)
160
+ when nil
161
+ # Pass
162
+ when Method
163
+ if v.instance?
164
+ @instance_methods << v
165
+ instance_method_added(parser, node, v)
166
+ else
167
+ @class_methods << v
168
+ class_method_added(parser, node, v)
169
+ end
170
+ when Class
171
+ if v.singleton? && kind == :class
172
+ merge_singleton_class(v)
173
+ else
174
+ @classes << v
175
+ class_added(parser, node, v)
176
+ end
177
+ when Module
178
+ @modules << v
179
+ module_added(parser, node, v)
180
+ when Call
181
+ handle_parsed_node(parser, v) unless handle_call(v)
182
+ when Constant
183
+ @constants << v
184
+ else
185
+ handle_parsed_node(parser, v)
186
+ end
187
+ end
188
+
189
+ def parse_node(node) = parser.handle_node(node, self)
190
+
191
+ def reference(path) = parser.reference(self, path)
192
+
193
+ def add_attribute(parser, node, type)
194
+ node.arguments.each do |arg|
195
+ next unless arg.try(:type) == :symbol_node # TODO: Support extra types?
196
+
197
+ @instance_attributes << Attribute.new(parser, self, node, arg.value.to_sym, type)
198
+ end
199
+ end
200
+
201
+ def adjust_split_attributes!(scope)
202
+ attrs = instance_variable_get("@#{scope}_attributes")
203
+ methods = instance_variable_get("@#{scope}_methods")
204
+ exp = instance_variable_get("@explicit_#{scope}_visibility")
205
+
206
+ loop do
207
+ changed = false
208
+ attrs.each do |attr|
209
+ setter = "#{attr.name}=".to_sym
210
+
211
+ # Do we have a writer defined explicitly?
212
+ if (method = methods.named(setter).first)
213
+ changed = true
214
+ methods.delete(method)
215
+ attr.writer_visibility = exp.fetch(setter, method.visibility)
216
+
217
+ # Promote attribute to accessor in case it was only a reader.
218
+ attr.type = :accessor if attr.type == :reader
219
+ end
220
+
221
+ next unless (method = methods.named(attr.name).first)
222
+
223
+ changed = true
224
+ methods.delete(method)
225
+ attr.reader_visibility = exp.fetch(setter, method.visibility)
226
+
227
+ # Promote attribute to accessor in case it was only a writer.
228
+ attr.type = :accessor if attr.type == :writer
229
+ end
230
+
231
+ # Finally, move all methods ending in `=` that are left to write-only
232
+ # attributes, and re-run this loop if required.
233
+ methods.each do |met|
234
+ name = met.name.to_s
235
+ next unless name.end_with? "="
236
+
237
+ changed = true
238
+ attr_name = name.gsub("=", "").to_sym
239
+ attrs << Attribute.new(@parser, self, met.node, attr_name, :writer).tap do |attr|
240
+ attr.writer_visibility = met.visibility
241
+ end
242
+ methods.delete(met)
243
+ end
244
+
245
+ break unless changed
246
+ end
247
+ end
248
+
249
+ def handle_visibility_modifier(_parser, node)
250
+ @inside_module_function = false
251
+ old_visibility = @current_visibility_modifier
252
+ @current_visibility_modifier = node.name
253
+ node.arguments&.each do |arg|
254
+ case arg.type
255
+ when :symbol_node
256
+ @explicit_instance_visibility[arg.value.to_sym] = @current_visibility_modifier
257
+ if (method = @instance_methods.named(arg.value.to_sym).first)
258
+ method.visibility = @current_visibility_modifier
259
+ next
260
+ end
261
+
262
+ if (attr = @instance_attributes.named(arg.value.gsub("=", "").to_sym).first)
263
+ if arg.value.end_with? "="
264
+ attr.writer_visibility = @current_visibility_modifier
265
+ else
266
+ attr.reader_visibility = @current_visibility_modifier
267
+ end
268
+ end
269
+
270
+ when :def_node, :call_node
271
+ @explicit_instance_visibility[arg.name.to_sym] = @current_visibility_modifier
272
+ known_methods = @instance_methods.map(&:name)
273
+ handle_node(arg)
274
+ (@instance_methods.map(&:name) - known_methods).each do |n|
275
+ @explicit_instance_visibility[n] = @current_visibility_modifier
276
+ end
277
+ else
278
+ parser.unhandled_node! node
279
+ end
280
+ end
281
+ ensure
282
+ @current_visibility_modifier = old_visibility unless node.arguments && node.arguments.empty?
283
+ end
284
+
285
+ def handle_singleton_visibility_modifier(_parser, node)
286
+ visibility = node.name.to_s.split("_", 2).first.to_sym
287
+ node.arguments.each do |arg|
288
+ if arg.value == "new"
289
+ @default_constructor_visibility = visibility
290
+ next
291
+ end
292
+ @class_methods.named(arg.value.to_sym).first&.visibility = visibility
293
+ end
294
+ end
295
+
296
+ def update_constructor_visibility!
297
+ return if @default_constructor_visibility == :public
298
+
299
+ # Create a new "new" singleton
300
+ handle_node(VirtualMethod.new(:new, :self_node))
301
+ @class_methods.named(:new).first!.visibility = @default_constructor_visibility
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class DeferredSingletonClass < Class
6
+ visible_attr_reader :target
7
+
8
+ def kind = :deferred_singleton_class
9
+
10
+ def initialize(parser, parent, node)
11
+ super
12
+ @target = parser.unfurl_constant_path(node.expression)
13
+ singleton!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Location
6
+ visible_attr_reader :line_start, :line_end, :offset_start, :offset_end
7
+ attr_reader :parser, :file_path, :ast, :source
8
+
9
+ def initialize(parser, ast, loc)
10
+ @parser = parser
11
+ @ast = ast
12
+ if loc.try(:virtual?)
13
+ @virtual = true
14
+ else
15
+ @line_start = loc.start_line
16
+ @line_end = loc.end_line
17
+ @offset_start = loc.start_offset
18
+ @offset_end = loc.end_offset
19
+ @file_path = parser.current_file
20
+ end
21
+
22
+ @source = nil
23
+ end
24
+
25
+ def virtual? = @virtual || false
26
+
27
+ def load_source!
28
+ return if @virtual
29
+
30
+ @source = @ast.source.source[@offset_start...@offset_end]
31
+ .then do |src|
32
+ lines = src.lines
33
+ next src unless lines.length > 1
34
+
35
+ match = /^(\s+)/.match(lines.last)
36
+ next src unless match
37
+
38
+ src.gsub(/^\s{#{match[1].length}}/, "")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Method
6
+ visible_attr_accessor :name, :parameters, :visibility, :overriding,
7
+ :overridden_by, :type, :external_receiver, :location
8
+ attr_accessor :parent, :node, :doc
9
+
10
+ def kind = :method
11
+
12
+ def initialize(parser, parent, node)
13
+ @object_id = parser.make_id(self)
14
+ @parser = parser
15
+ @parent = parent
16
+ @node = node
17
+ @name = node.name
18
+ @visibility = parent.try(:current_visibility_modifier) || :public
19
+ @location = parser.location(node.location) unless node.is_a? VirtualMethod
20
+ @parameters = MethodParameters.new(parser, self, node.parameters)
21
+ @overridden_by = []
22
+ case node.receiver&.type
23
+ when nil
24
+ @type = :instance
25
+ when :self_node
26
+ @type = :class
27
+ else
28
+ @type = :class
29
+ @external_receiver = parser.reference(self, parser.unfurl_constant_path(node.receiver))
30
+ end
31
+ end
32
+
33
+ def id = @object_id
34
+
35
+ def class? = @type == :class
36
+
37
+ def instance? = @type == :instance
38
+
39
+ def module_function? = @visibility == :module_function
40
+
41
+ def external? = !@external_receiver.nil?
42
+
43
+ def override_target = (@parser.object_by_id(@overriding) if @overriding)
44
+ def overriders = @overridden_by.map { @parser.object_by_id(_1) }.map(&:parent).compact
45
+
46
+ def full_path(relative_to: nil)
47
+ path = (parent&.full_path || []).reverse + [self]
48
+ return path unless relative_to
49
+
50
+ full_rel = relative_to.full_path.reverse
51
+ while full_rel.first == path.first
52
+ full_rel.shift
53
+ path.shift
54
+ end
55
+
56
+ path
57
+ end
58
+
59
+ docrb_inspect { "name=#{name}" }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class MethodParameters < Array
6
+ def initialize(parser, parent, node)
7
+ @object_id = parser.make_id(self)
8
+ super()
9
+ @parent = parent
10
+ @node = node
11
+ node&.compact_child_nodes&.sort_by { _1.location.start_offset }&.each do |n|
12
+ kind = case n
13
+ when Prism::RequiredParameterNode then :arg
14
+ when Prism::OptionalParameterNode then :optarg
15
+ when Prism::RestParameterNode then :rest
16
+ when Prism::KeywordParameterNode then n.value ? :optkw : :kw
17
+ when Prism::KeywordRestParameterNode then :kwrest
18
+ when Prism::BlockParameterNode then :block
19
+ else raise NotImplementedError, "Unsupported parameter kind #{n.class}"
20
+ end
21
+ append Parameter.new(parser, kind, n.name, n.try(:value))
22
+ end
23
+ end
24
+
25
+ def id = @object_id
26
+
27
+ docrb_inspect { to_a }
28
+
29
+ class Parameter
30
+ visible_attr_reader :kind, :name, :value, :value_type
31
+ attr_reader :parser
32
+
33
+ def initialize(parser, kind, name, value = nil)
34
+ @object_id = parser.make_id(self)
35
+ @kind = kind
36
+ @name = name || name_by_type
37
+ @value_type = type_for_value(value)
38
+ @value = value.then! { parser.location(_1.location) }
39
+ end
40
+
41
+ def id = @object_id
42
+
43
+ def has_value? = !value.nil?
44
+
45
+ def optional? = kind == :optarg || kind == :optkw
46
+
47
+ def positional? = kind == :optarg || kind == :arg || kind == :rest
48
+
49
+ def keyword? = kind == :kw || kind == :optkw || kind == :kwrest
50
+
51
+ def block? = kind == :block
52
+
53
+ def rest? = kind == :kwrest || kind == :rest
54
+
55
+ private
56
+
57
+ def name_by_type
58
+ case kind
59
+ when :rest then :*
60
+ when :kwrest then :**
61
+ when :block then :&
62
+ else
63
+ raise "Invalid call to name_by_type for a non-anonymous parameter"
64
+ end
65
+ end
66
+
67
+ def type_for_value(value)
68
+ return unless value
69
+
70
+ case value
71
+ when Prism::SymbolNode then :symbol
72
+ when Prism::NilNode then :nil
73
+ when Prism::FalseNode, Prism::TrueNode then :bool
74
+ when Prism::CallNode then :call
75
+ when Prism::StringNode then :string
76
+ when Prism::IntegerNode, Prism::FloatNode then :number
77
+ when Prism::ConstantReadNode, Prism::ConstantPathNode then :const
78
+ else
79
+ puts "Unhandled parameter value type #{value.class.name}"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Module < Container
6
+ def kind = :module
7
+
8
+ def initialize(parser, parent, node)
9
+ super
10
+ adjust_split_attributes! :class
11
+ adjust_split_attributes! :instance
12
+ end
13
+
14
+ def instance_method_added(_parser, _node, method)
15
+ return unless @inside_module_function
16
+ return unless method.instance?
17
+
18
+ method.type = :module_function
19
+ @class_methods.append(method)
20
+ @instance_methods.delete(method)
21
+ end
22
+
23
+ def handle_parsed_node(parser, node)
24
+ return unless node.is_a? Call
25
+ return unless node.name == :module_function
26
+
27
+ old_module_function = @inside_module_function
28
+ @inside_module_function = true
29
+
30
+ node.arguments&.each do |arg|
31
+ case arg.type
32
+ when :symbol_node
33
+ if (method = @instance_methods.named(arg.value.to_sym).first)
34
+ method.type = :module_function
35
+ @class_methods.append(method)
36
+ @instance_methods.delete(method)
37
+ end
38
+
39
+ when :def_node, :call_node
40
+ handle_node(arg)
41
+ else
42
+ parser.unhandled_node! node
43
+ end
44
+ ensure
45
+ @inside_module_function = old_module_function unless node.arguments && node.arguments.empty?
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class NodeArray < Array
6
+ def by_kind(*kind) = filter { _1.respond_to?(:kind) && kind.include?(_1.kind) }
7
+
8
+ def named(name)
9
+ name = name.to_s
10
+ NodeArray.new(filter { _1.respond_to?(:name) && _1.name.to_s == name })
11
+ end
12
+
13
+ def named!(name) = named(name).first!
14
+
15
+ def typed(*classes) = filter { |node| classes.any? { node.is_a? _1 } }
16
+
17
+ def merge_unowned(*other)
18
+ other
19
+ .reject { |v| find { _1.name == v.name } }
20
+ .then { append(*_1) unless _1.empty? }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Reference
6
+ visible_attr_accessor :path, :resolved
7
+ attr_accessor :parent
8
+
9
+ def initialize(parser, parent, path)
10
+ @parent = parent
11
+ @path = path
12
+ @parser = parser
13
+ end
14
+
15
+ def resolved? = !resolved.nil?
16
+ def fulfilled? = resolved? && resolved.valid?
17
+
18
+ def dereference!
19
+ raise "Dereference of unfulfilled reference" unless fulfilled?
20
+
21
+ resolved.dereference!
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class Reloader
6
+ def self.reload!
7
+ Object.send(:remove_const, :Docrb) if defined? Docrb
8
+
9
+ root_dir = File.expand_path("../..", __dir__)
10
+ $LOADED_FEATURES
11
+ .select { _1.start_with? root_dir }
12
+ .each { $LOADED_FEATURES.delete _1 }
13
+
14
+ require "docrb/parser"
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class ResolvedReference
6
+ visible_attr_accessor :status
7
+
8
+ def initialize(parser, status, id)
9
+ @status = status
10
+ @object_id = id
11
+ @parser = parser
12
+ end
13
+
14
+ def id = @object_id
15
+
16
+ def broken? = status == :broken
17
+ def valid? = status == :valid
18
+
19
+ def dereference!
20
+ raise "Cannot dereference broken reference" if broken?
21
+
22
+ @parser.object_by_id(id)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class VirtualContainer
6
+ Body = Struct.new(:body)
7
+ ConstantName = Struct.new(:name) do
8
+ def type = :constant_read_node
9
+ end
10
+
11
+ attr_accessor :location, :body, :constant_path, :type, :superclass
12
+
13
+ def initialize(type, name)
14
+ @type = type
15
+ @constant_path = ConstantName.new(name:)
16
+ @location = VirtualLocation.new
17
+ @body = Body.new(body: [])
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docrb
4
+ class Parser
5
+ class VirtualLocation
6
+ def virtual? = true
7
+ end
8
+ end
9
+ end