ruby-lsp 0.17.4 → 0.17.13

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +26 -1
  5. data/exe/ruby-lsp-check +1 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +74 -43
  7. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +26 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +147 -29
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +383 -79
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +195 -61
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -8
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +71 -3
  13. data/lib/ruby_indexer/test/configuration_test.rb +1 -1
  14. data/lib/ruby_indexer/test/constant_test.rb +17 -17
  15. data/lib/ruby_indexer/test/enhancements_test.rb +197 -0
  16. data/lib/ruby_indexer/test/index_test.rb +367 -17
  17. data/lib/ruby_indexer/test/method_test.rb +58 -25
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +297 -0
  19. data/lib/ruby_indexer/test/test_case.rb +1 -5
  20. data/lib/ruby_lsp/addon.rb +22 -5
  21. data/lib/ruby_lsp/base_server.rb +8 -3
  22. data/lib/ruby_lsp/document.rb +27 -46
  23. data/lib/ruby_lsp/erb_document.rb +125 -0
  24. data/lib/ruby_lsp/global_state.rb +47 -19
  25. data/lib/ruby_lsp/internal.rb +2 -0
  26. data/lib/ruby_lsp/listeners/completion.rb +161 -57
  27. data/lib/ruby_lsp/listeners/definition.rb +91 -27
  28. data/lib/ruby_lsp/listeners/document_highlight.rb +5 -1
  29. data/lib/ruby_lsp/listeners/hover.rb +61 -19
  30. data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
  31. data/lib/ruby_lsp/node_context.rb +65 -5
  32. data/lib/ruby_lsp/requests/code_action_resolve.rb +107 -9
  33. data/lib/ruby_lsp/requests/code_actions.rb +11 -2
  34. data/lib/ruby_lsp/requests/completion.rb +4 -4
  35. data/lib/ruby_lsp/requests/completion_resolve.rb +14 -9
  36. data/lib/ruby_lsp/requests/definition.rb +18 -8
  37. data/lib/ruby_lsp/requests/diagnostics.rb +6 -5
  38. data/lib/ruby_lsp/requests/document_symbol.rb +2 -7
  39. data/lib/ruby_lsp/requests/folding_ranges.rb +6 -2
  40. data/lib/ruby_lsp/requests/formatting.rb +15 -0
  41. data/lib/ruby_lsp/requests/hover.rb +5 -5
  42. data/lib/ruby_lsp/requests/on_type_formatting.rb +6 -4
  43. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  44. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
  45. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  46. data/lib/ruby_lsp/requests/support/common.rb +11 -2
  47. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +2 -6
  48. data/lib/ruby_lsp/ruby_document.rb +74 -0
  49. data/lib/ruby_lsp/server.rb +129 -54
  50. data/lib/ruby_lsp/store.rb +33 -9
  51. data/lib/ruby_lsp/test_helper.rb +3 -1
  52. data/lib/ruby_lsp/type_inferrer.rb +61 -25
  53. data/lib/ruby_lsp/utils.rb +13 -0
  54. metadata +9 -8
  55. data/exe/ruby-lsp-doctor +0 -23
@@ -0,0 +1,197 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class EnhancementTest < TestCase
8
+ def test_enhancing_indexing_included_hook
9
+ enhancement_class = Class.new do
10
+ include Enhancement
11
+
12
+ def on_call_node(index, owner, node, file_path)
13
+ return unless owner
14
+ return unless node.name == :extend
15
+
16
+ arguments = node.arguments&.arguments
17
+ return unless arguments
18
+
19
+ location = node.location
20
+
21
+ arguments.each do |node|
22
+ next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
23
+
24
+ module_name = node.full_name
25
+ next unless module_name == "ActiveSupport::Concern"
26
+
27
+ index.register_included_hook(owner.name) do |index, base|
28
+ class_methods_name = "#{owner.name}::ClassMethods"
29
+
30
+ if index.indexed?(class_methods_name)
31
+ singleton = index.existing_or_new_singleton_class(base.name)
32
+ singleton.mixin_operations << Entry::Include.new(class_methods_name)
33
+ end
34
+ end
35
+
36
+ index.add(Entry::Method.new(
37
+ "new_method",
38
+ file_path,
39
+ location,
40
+ location,
41
+ [],
42
+ [Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
43
+ Entry::Visibility::PUBLIC,
44
+ owner,
45
+ ))
46
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
47
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
48
+ # Do nothing
49
+ end
50
+ end
51
+ end
52
+
53
+ @index.register_enhancement(enhancement_class.new)
54
+ index(<<~RUBY)
55
+ module ActiveSupport
56
+ module Concern
57
+ def self.extended(base)
58
+ base.class_eval("def new_method(a); end")
59
+ end
60
+ end
61
+ end
62
+
63
+ module ActiveRecord
64
+ module Associations
65
+ extend ActiveSupport::Concern
66
+
67
+ module ClassMethods
68
+ def belongs_to(something); end
69
+ end
70
+ end
71
+
72
+ class Base
73
+ include Associations
74
+ end
75
+ end
76
+
77
+ class User < ActiveRecord::Base
78
+ end
79
+ RUBY
80
+
81
+ assert_equal(
82
+ [
83
+ "User::<Class:User>",
84
+ "ActiveRecord::Base::<Class:Base>",
85
+ "ActiveRecord::Associations::ClassMethods",
86
+ "Object::<Class:Object>",
87
+ "BasicObject::<Class:BasicObject>",
88
+ "Class",
89
+ "Module",
90
+ "Object",
91
+ "Kernel",
92
+ "BasicObject",
93
+ ],
94
+ @index.linearized_ancestors_of("User::<Class:User>"),
95
+ )
96
+
97
+ assert_entry("new_method", Entry::Method, "/fake/path/foo.rb:10-4:10-33")
98
+ end
99
+
100
+ def test_enhancing_indexing_configuration_dsl
101
+ enhancement_class = Class.new do
102
+ include Enhancement
103
+
104
+ def on_call_node(index, owner, node, file_path)
105
+ return unless owner
106
+
107
+ name = node.name
108
+ return unless name == :has_many
109
+
110
+ arguments = node.arguments&.arguments
111
+ return unless arguments
112
+
113
+ association_name = arguments.first
114
+ return unless association_name.is_a?(Prism::SymbolNode)
115
+
116
+ location = association_name.location
117
+
118
+ index.add(Entry::Method.new(
119
+ T.must(association_name.value),
120
+ file_path,
121
+ location,
122
+ location,
123
+ [],
124
+ [],
125
+ Entry::Visibility::PUBLIC,
126
+ owner,
127
+ ))
128
+ end
129
+ end
130
+
131
+ @index.register_enhancement(enhancement_class.new)
132
+ index(<<~RUBY)
133
+ module ActiveSupport
134
+ module Concern
135
+ def self.extended(base)
136
+ base.class_eval("def new_method(a); end")
137
+ end
138
+ end
139
+ end
140
+
141
+ module ActiveRecord
142
+ module Associations
143
+ extend ActiveSupport::Concern
144
+
145
+ module ClassMethods
146
+ def belongs_to(something); end
147
+ end
148
+ end
149
+
150
+ class Base
151
+ include Associations
152
+ end
153
+ end
154
+
155
+ class User < ActiveRecord::Base
156
+ has_many :posts
157
+ end
158
+ RUBY
159
+
160
+ assert_entry("posts", Entry::Method, "/fake/path/foo.rb:23-11:23-17")
161
+ end
162
+
163
+ def test_error_handling_in_enhancement
164
+ enhancement_class = Class.new do
165
+ include Enhancement
166
+
167
+ def on_call_node(index, owner, node, file_path)
168
+ raise "Error"
169
+ end
170
+
171
+ class << self
172
+ def name
173
+ "TestEnhancement"
174
+ end
175
+ end
176
+ end
177
+
178
+ @index.register_enhancement(enhancement_class.new)
179
+
180
+ _stdout, stderr = capture_io do
181
+ index(<<~RUBY)
182
+ module ActiveSupport
183
+ module Concern
184
+ def self.extended(base)
185
+ base.class_eval("def new_method(a); end")
186
+ end
187
+ end
188
+ end
189
+ RUBY
190
+ end
191
+
192
+ assert_match(%r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' enhancement}, stderr)
193
+ # The module should still be indexed
194
+ assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
195
+ end
196
+ end
197
+ end
@@ -181,20 +181,20 @@ module RubyIndexer
181
181
 
182
182
  def test_resolving_aliases_to_non_existing_constants_with_conflicting_names
183
183
  @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
184
- class Float
184
+ class Bar
185
185
  end
186
186
 
187
187
  module Foo
188
- class Float < self
189
- INFINITY = ::Float::INFINITY
188
+ class Bar < self
189
+ BAZ = ::Bar::BAZ
190
190
  end
191
191
  end
192
192
  RUBY
193
193
 
194
- entry = @index.resolve("INFINITY", ["Foo", "Float"]).first
194
+ entry = @index.resolve("BAZ", ["Foo", "Bar"]).first
195
195
  refute_nil(entry)
196
196
 
197
- assert_instance_of(Entry::UnresolvedAlias, entry)
197
+ assert_instance_of(Entry::UnresolvedConstantAlias, entry)
198
198
  end
199
199
 
200
200
  def test_visitor_does_not_visit_unnecessary_nodes
@@ -285,6 +285,43 @@ module RubyIndexer
285
285
  assert_includes(second_entry.comments, "Hello from second `bar`")
286
286
  end
287
287
 
288
+ def test_resolve_method_inherited_only
289
+ index(<<~RUBY)
290
+ class Bar
291
+ def baz; end
292
+ end
293
+
294
+ class Foo < Bar
295
+ def baz; end
296
+ end
297
+ RUBY
298
+
299
+ entry = T.must(@index.resolve_method("baz", "Foo", inherited_only: true).first)
300
+
301
+ assert_equal("Bar", T.must(entry.owner).name)
302
+ end
303
+
304
+ def test_resolve_method_inherited_only_for_prepended_module
305
+ index(<<~RUBY)
306
+ module Bar
307
+ def baz
308
+ super
309
+ end
310
+ end
311
+
312
+ class Foo
313
+ prepend Bar
314
+
315
+ def baz; end
316
+ end
317
+ RUBY
318
+
319
+ # This test is just to document the fact that we don't yet support resolving inherited methods for modules that
320
+ # are prepended. The only way to support this is to find all namespaces that have the module a subtype, so that we
321
+ # can show the results for everywhere the module has been prepended.
322
+ assert_nil(@index.resolve_method("baz", "Bar", inherited_only: true))
323
+ end
324
+
288
325
  def test_prefix_search_for_methods
289
326
  index(<<~RUBY)
290
327
  module Foo
@@ -313,12 +350,12 @@ module RubyIndexer
313
350
  @index.index_single(indexable_path)
314
351
  end
315
352
 
316
- refute_empty(@index.instance_variable_get(:@entries))
353
+ refute_empty(@index)
317
354
  end
318
355
 
319
356
  def test_index_single_does_not_fail_for_non_existing_file
320
357
  @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"))
321
- entries_after_indexing = @index.instance_variable_get(:@entries).keys
358
+ entries_after_indexing = @index.names
322
359
  assert_equal(@default_indexed_entries.keys, entries_after_indexing)
323
360
  end
324
361
 
@@ -978,11 +1015,11 @@ module RubyIndexer
978
1015
 
979
1016
  foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
980
1017
  assert_equal(2, foo_entry.location.start_line)
981
- assert_instance_of(Entry::Alias, foo_entry)
1018
+ assert_instance_of(Entry::ConstantAlias, foo_entry)
982
1019
 
983
1020
  bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
984
1021
  assert_equal(3, bar_entry.location.start_line)
985
- assert_instance_of(Entry::Alias, bar_entry)
1022
+ assert_instance_of(Entry::ConstantAlias, bar_entry)
986
1023
  end
987
1024
 
988
1025
  def test_resolving_circular_alias_three_levels
@@ -996,15 +1033,52 @@ module RubyIndexer
996
1033
 
997
1034
  foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
998
1035
  assert_equal(2, foo_entry.location.start_line)
999
- assert_instance_of(Entry::Alias, foo_entry)
1036
+ assert_instance_of(Entry::ConstantAlias, foo_entry)
1000
1037
 
1001
1038
  bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
1002
1039
  assert_equal(3, bar_entry.location.start_line)
1003
- assert_instance_of(Entry::Alias, bar_entry)
1040
+ assert_instance_of(Entry::ConstantAlias, bar_entry)
1004
1041
 
1005
1042
  baz_entry = T.must(@index.resolve("BAZ", ["Namespace"])&.first)
1006
1043
  assert_equal(4, baz_entry.location.start_line)
1007
- assert_instance_of(Entry::Alias, baz_entry)
1044
+ assert_instance_of(Entry::ConstantAlias, baz_entry)
1045
+ end
1046
+
1047
+ def test_resolving_constants_in_aliased_namespace
1048
+ index(<<~RUBY)
1049
+ module Original
1050
+ module Something
1051
+ CONST = 123
1052
+ end
1053
+ end
1054
+
1055
+ module Other
1056
+ ALIAS = Original::Something
1057
+ end
1058
+
1059
+ module Third
1060
+ Other::ALIAS::CONST
1061
+ end
1062
+ RUBY
1063
+
1064
+ entry = T.must(@index.resolve("Other::ALIAS::CONST", ["Third"])&.first)
1065
+ assert_kind_of(Entry::Constant, entry)
1066
+ assert_equal("Original::Something::CONST", entry.name)
1067
+ end
1068
+
1069
+ def test_resolving_top_level_aliases
1070
+ index(<<~RUBY)
1071
+ class Foo
1072
+ CONST = 123
1073
+ end
1074
+
1075
+ FOO = Foo
1076
+ FOO::CONST
1077
+ RUBY
1078
+
1079
+ entry = T.must(@index.resolve("FOO::CONST", [])&.first)
1080
+ assert_kind_of(Entry::Constant, entry)
1081
+ assert_equal("Foo::CONST", entry.name)
1008
1082
  end
1009
1083
 
1010
1084
  def test_resolving_top_level_compact_reference
@@ -1321,11 +1395,6 @@ module RubyIndexer
1321
1395
  entries = @index.instance_variable_completion_candidates("@", "Foo::Bar::<Class:Bar>").map(&:name)
1322
1396
  assert_includes(entries, "@a")
1323
1397
  assert_includes(entries, "@b")
1324
-
1325
- assert_includes(
1326
- @index.instance_variable_completion_candidates("@", "Foo::Bar::<Class:Bar>::<Class:<Class:Bar>>").map(&:name),
1327
- "@c",
1328
- )
1329
1398
  end
1330
1399
 
1331
1400
  def test_singletons_are_excluded_from_prefix_search
@@ -1472,5 +1541,286 @@ module RubyIndexer
1472
1541
 
1473
1542
  assert_empty(@index.method_completion_candidates("bar", "Foo"))
1474
1543
  end
1544
+
1545
+ def test_first_unqualified_const
1546
+ index(<<~RUBY)
1547
+ module Foo
1548
+ class Bar; end
1549
+ end
1550
+
1551
+ module Baz
1552
+ class Bar; end
1553
+ end
1554
+ RUBY
1555
+
1556
+ entry = T.must(@index.first_unqualified_const("Bar")&.first)
1557
+ assert_equal("Foo::Bar", entry.name)
1558
+ end
1559
+
1560
+ def test_completion_does_not_duplicate_overridden_methods
1561
+ index(<<~RUBY)
1562
+ class Foo
1563
+ def bar; end
1564
+ end
1565
+
1566
+ class Baz < Foo
1567
+ def bar; end
1568
+ end
1569
+ RUBY
1570
+
1571
+ entries = @index.method_completion_candidates("bar", "Baz")
1572
+ assert_equal(["bar"], entries.map(&:name))
1573
+ assert_equal("Baz", T.must(entries.first.owner).name)
1574
+ end
1575
+
1576
+ def test_completion_does_not_duplicate_methods_overridden_by_aliases
1577
+ index(<<~RUBY)
1578
+ class Foo
1579
+ def bar; end
1580
+ end
1581
+
1582
+ class Baz < Foo
1583
+ alias bar to_s
1584
+ end
1585
+ RUBY
1586
+
1587
+ entries = @index.method_completion_candidates("bar", "Baz")
1588
+ assert_equal(["bar"], entries.map(&:name))
1589
+ assert_equal("Baz", T.must(entries.first.owner).name)
1590
+ end
1591
+
1592
+ def test_decorated_parameters
1593
+ index(<<~RUBY)
1594
+ class Foo
1595
+ def bar(a, b = 1, c: 2)
1596
+ end
1597
+ end
1598
+ RUBY
1599
+
1600
+ methods = @index.resolve_method("bar", "Foo")
1601
+ refute_nil(methods)
1602
+
1603
+ entry = T.must(methods.first)
1604
+
1605
+ assert_equal("(a, b = <default>, c: <default>)", entry.decorated_parameters)
1606
+ end
1607
+
1608
+ def test_decorated_parameters_when_method_has_no_parameters
1609
+ index(<<~RUBY)
1610
+ class Foo
1611
+ def bar
1612
+ end
1613
+ end
1614
+ RUBY
1615
+
1616
+ methods = @index.resolve_method("bar", "Foo")
1617
+ refute_nil(methods)
1618
+
1619
+ entry = T.must(methods.first)
1620
+
1621
+ assert_equal("()", entry.decorated_parameters)
1622
+ end
1623
+
1624
+ def test_linearizing_singleton_ancestors_of_singleton_when_class_has_parent
1625
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1626
+ class Foo; end
1627
+
1628
+ class Bar < Foo
1629
+ end
1630
+
1631
+ class Baz < Bar
1632
+ class << self
1633
+ class << self
1634
+ end
1635
+ end
1636
+ end
1637
+ RUBY
1638
+
1639
+ assert_equal(
1640
+ [
1641
+ "Baz::<Class:Baz>::<Class:<Class:Baz>>",
1642
+ "Bar::<Class:Bar>::<Class:<Class:Bar>>",
1643
+ "Foo::<Class:Foo>::<Class:<Class:Foo>>",
1644
+ "Object::<Class:Object>::<Class:<Class:Object>>",
1645
+ "BasicObject::<Class:BasicObject>::<Class:<Class:BasicObject>>",
1646
+ "Class::<Class:Class>",
1647
+ "Module::<Class:Module>",
1648
+ "Object::<Class:Object>",
1649
+ "BasicObject::<Class:BasicObject>",
1650
+ "Class",
1651
+ "Module",
1652
+ "Object",
1653
+ "Kernel",
1654
+ "BasicObject",
1655
+ ],
1656
+ @index.linearized_ancestors_of("Baz::<Class:Baz>::<Class:<Class:Baz>>"),
1657
+ )
1658
+ end
1659
+
1660
+ def test_linearizing_singleton_object
1661
+ assert_equal(
1662
+ [
1663
+ "Object::<Class:Object>",
1664
+ "BasicObject::<Class:BasicObject>",
1665
+ "Class",
1666
+ "Module",
1667
+ "Object",
1668
+ "Kernel",
1669
+ "BasicObject",
1670
+ ],
1671
+ @index.linearized_ancestors_of("Object::<Class:Object>"),
1672
+ )
1673
+ end
1674
+
1675
+ def test_linearizing_singleton_ancestors
1676
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1677
+ module First
1678
+ end
1679
+
1680
+ module Second
1681
+ include First
1682
+ end
1683
+
1684
+ module Foo
1685
+ class Bar
1686
+ class << self
1687
+ class Baz
1688
+ extend Second
1689
+
1690
+ class << self
1691
+ include First
1692
+ end
1693
+ end
1694
+ end
1695
+ end
1696
+ end
1697
+ RUBY
1698
+
1699
+ assert_equal(
1700
+ [
1701
+ "Foo::Bar::<Class:Bar>::Baz::<Class:Baz>",
1702
+ "Second",
1703
+ "First",
1704
+ "Object::<Class:Object>",
1705
+ "BasicObject::<Class:BasicObject>",
1706
+ "Class",
1707
+ "Module",
1708
+ "Object",
1709
+ "Kernel",
1710
+ "BasicObject",
1711
+ ],
1712
+ @index.linearized_ancestors_of("Foo::Bar::<Class:Bar>::Baz::<Class:Baz>"),
1713
+ )
1714
+ end
1715
+
1716
+ def test_linearizing_singleton_ancestors_when_class_has_parent
1717
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1718
+ class Foo; end
1719
+
1720
+ class Bar < Foo
1721
+ end
1722
+
1723
+ class Baz < Bar
1724
+ class << self
1725
+ end
1726
+ end
1727
+ RUBY
1728
+
1729
+ assert_equal(
1730
+ [
1731
+ "Baz::<Class:Baz>",
1732
+ "Bar::<Class:Bar>",
1733
+ "Foo::<Class:Foo>",
1734
+ "Object::<Class:Object>",
1735
+ "BasicObject::<Class:BasicObject>",
1736
+ "Class",
1737
+ "Module",
1738
+ "Object",
1739
+ "Kernel",
1740
+ "BasicObject",
1741
+ ],
1742
+ @index.linearized_ancestors_of("Baz::<Class:Baz>"),
1743
+ )
1744
+ end
1745
+
1746
+ def test_linearizing_a_module_singleton_class
1747
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1748
+ module A; end
1749
+ RUBY
1750
+
1751
+ assert_equal(
1752
+ [
1753
+ "A::<Class:A>",
1754
+ "Module",
1755
+ "Object",
1756
+ "Kernel",
1757
+ "BasicObject",
1758
+ ],
1759
+ @index.linearized_ancestors_of("A::<Class:A>"),
1760
+ )
1761
+ end
1762
+
1763
+ def test_linearizing_a_singleton_class_with_no_attached
1764
+ assert_raises(Index::NonExistingNamespaceError) do
1765
+ @index.linearized_ancestors_of("A::<Class:A>")
1766
+ end
1767
+ end
1768
+
1769
+ def test_linearizing_singleton_parent_class_with_namespace
1770
+ index(<<~RUBY)
1771
+ class ActiveRecord::Base; end
1772
+
1773
+ class User < ActiveRecord::Base
1774
+ end
1775
+ RUBY
1776
+
1777
+ assert_equal(
1778
+ [
1779
+ "User::<Class:User>",
1780
+ "ActiveRecord::Base::<Class:Base>",
1781
+ "Object::<Class:Object>",
1782
+ "BasicObject::<Class:BasicObject>",
1783
+ "Class",
1784
+ "Module",
1785
+ "Object",
1786
+ "Kernel",
1787
+ "BasicObject",
1788
+ ],
1789
+ @index.linearized_ancestors_of("User::<Class:User>"),
1790
+ )
1791
+ end
1792
+
1793
+ def test_singleton_nesting_is_correctly_split_during_linearization
1794
+ index(<<~RUBY)
1795
+ module Bar; end
1796
+
1797
+ module Foo
1798
+ class Namespace::Parent
1799
+ extend Bar
1800
+ end
1801
+ end
1802
+
1803
+ module Foo
1804
+ class Child < Namespace::Parent
1805
+ end
1806
+ end
1807
+ RUBY
1808
+
1809
+ assert_equal(
1810
+ [
1811
+ "Foo::Child::<Class:Child>",
1812
+ "Foo::Namespace::Parent::<Class:Parent>",
1813
+ "Bar",
1814
+ "Object::<Class:Object>",
1815
+ "BasicObject::<Class:BasicObject>",
1816
+ "Class",
1817
+ "Module",
1818
+ "Object",
1819
+ "Kernel",
1820
+ "BasicObject",
1821
+ ],
1822
+ @index.linearized_ancestors_of("Foo::Child::<Class:Child>"),
1823
+ )
1824
+ end
1475
1825
  end
1476
1826
  end