Skip to content

Commit 2d5f9fc

Browse files
committed
Handle superclasses
1 parent ce9de31 commit 2d5f9fc

File tree

2 files changed

+72
-14
lines changed

2 files changed

+72
-14
lines changed

lib/syntax_tree/index.rb

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ def initialize(line, column)
2020

2121
# This entry represents a class definition using the class keyword.
2222
class ClassDefinition
23-
attr_reader :nesting, :name, :location, :comments
23+
attr_reader :nesting, :name, :superclass, :location, :comments
2424

25-
def initialize(nesting, name, location, comments)
25+
def initialize(nesting, name, superclass, location, comments)
2626
@nesting = nesting
2727
@name = name
28+
@superclass = superclass
2829
@location = location
2930
@comments = comments
3031
end
@@ -182,7 +183,7 @@ def find_constant_path(insns, index)
182183
if insn.is_a?(Array) && insn[0] == :opt_getconstant_path
183184
# In this case we're on Ruby 3.2+ and we have an opt_getconstant_path
184185
# instruction, so we already know all of the symbols in the nesting.
185-
insn[1]
186+
[index - 1, insn[1]]
186187
elsif insn.is_a?(Symbol) && insn.match?(/\Alabel_\d+/)
187188
# Otherwise, if we have a label then this is very likely the
188189
# destination of an opt_getinlinecache instruction, in which case
@@ -195,7 +196,9 @@ def find_constant_path(insns, index)
195196
index -= 1
196197
end
197198

198-
names
199+
[index - 1, names]
200+
else
201+
[index, []]
199202
end
200203
end
201204

@@ -213,7 +216,24 @@ def index_iseq(iseq, file_comments)
213216
_, name, class_iseq, flags = insn
214217
next_nesting = current_nesting.dup
215218

216-
if (nesting = find_constant_path(insns, index - 2))
219+
# This is the index we're going to search for the nested constant
220+
# path within the declaration name.
221+
constant_index = index - 2
222+
223+
# This is the superclass of the class being defined.
224+
superclass = []
225+
226+
# If there is a superclass, then we're going to find it here and
227+
# then update the constant_index as necessary.
228+
if flags & VM_DEFINECLASS_FLAG_HAS_SUPERCLASS > 0
229+
constant_index, superclass = find_constant_path(insns, index - 1)
230+
231+
if superclass.empty?
232+
raise NotImplementedError, "superclass with non constant path"
233+
end
234+
end
235+
236+
if (_, nesting = find_constant_path(insns, constant_index))
217237
# If there is a constant path in the class name, then we need to
218238
# handle that by updating the nesting.
219239
next_nesting << (nesting << name)
@@ -243,6 +263,7 @@ def index_iseq(iseq, file_comments)
243263
results << ClassDefinition.new(
244264
next_nesting,
245265
name,
266+
superclass,
246267
location,
247268
EntryComments.new(file_comments, location)
248269
)
@@ -299,9 +320,23 @@ def visit_class(node)
299320
location =
300321
Location.new(node.location.start_line, node.location.start_column)
301322

323+
superclass =
324+
if node.superclass
325+
visited = visit(node.superclass)
326+
327+
if visited == [[]]
328+
raise NotImplementedError, "superclass with non constant path"
329+
end
330+
331+
visited
332+
else
333+
[]
334+
end
335+
302336
results << ClassDefinition.new(
303337
nesting.dup,
304338
names.last,
339+
superclass,
305340
location,
306341
comments_for(node)
307342
)
@@ -315,14 +350,7 @@ def visit_const_ref(node)
315350
end
316351

317352
def visit_const_path_ref(node)
318-
names =
319-
if node.parent.is_a?(ConstPathRef)
320-
visit(node.parent)
321-
else
322-
[visit(node.parent)]
323-
end
324-
325-
names << node.constant.value.to_sym
353+
visit(node.parent) << node.constant.value.to_sym
326354
end
327355

328356
def visit_def(node)
@@ -376,7 +404,7 @@ def visit_statements(node)
376404
end
377405

378406
def visit_var_ref(node)
379-
node.value.value.to_sym
407+
[node.value.value.to_sym]
380408
end
381409
end
382410

test/index_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,36 @@ def test_class_paths_nested
6060
end
6161
end
6262

63+
def test_class_superclass
64+
index_each("class Foo < Bar; end") do |entry|
65+
assert_equal :Foo, entry.name
66+
assert_equal [[:Foo]], entry.nesting
67+
assert_equal [:Bar], entry.superclass
68+
end
69+
end
70+
71+
def test_class_path_superclass
72+
index_each("class Foo::Bar < Baz::Qux; end") do |entry|
73+
assert_equal :Bar, entry.name
74+
assert_equal [[:Foo, :Bar]], entry.nesting
75+
assert_equal [:Baz, :Qux], entry.superclass
76+
end
77+
end
78+
79+
def test_class_path_superclass_unknown
80+
source = "class Foo < bar; end"
81+
82+
assert_raises NotImplementedError do
83+
Index.index(source, backend: Index::ParserBackend.new)
84+
end
85+
86+
if defined?(RubyVM::InstructionSequence)
87+
assert_raises NotImplementedError do
88+
Index.index(source, backend: Index::ISeqBackend.new)
89+
end
90+
end
91+
end
92+
6393
def test_class_comments
6494
index_each("# comment1\n# comment2\nclass Foo; end") do |entry|
6595
assert_equal :Foo, entry.name

0 commit comments

Comments
 (0)