ruby-lsp 0.16.7 → 0.17.3

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/VERSION +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +195 -18
  5. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +129 -12
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +333 -44
  7. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +99 -0
  8. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  9. data/lib/ruby_indexer/test/classes_and_modules_test.rb +58 -11
  10. data/lib/ruby_indexer/test/configuration_test.rb +5 -6
  11. data/lib/ruby_indexer/test/constant_test.rb +9 -9
  12. data/lib/ruby_indexer/test/index_test.rb +867 -7
  13. data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
  14. data/lib/ruby_indexer/test/method_test.rb +57 -0
  15. data/lib/ruby_indexer/test/rbs_indexer_test.rb +42 -0
  16. data/lib/ruby_indexer/test/test_case.rb +10 -1
  17. data/lib/ruby_lsp/addon.rb +8 -8
  18. data/lib/ruby_lsp/document.rb +26 -3
  19. data/lib/ruby_lsp/global_state.rb +37 -16
  20. data/lib/ruby_lsp/internal.rb +2 -0
  21. data/lib/ruby_lsp/listeners/code_lens.rb +2 -2
  22. data/lib/ruby_lsp/listeners/completion.rb +74 -17
  23. data/lib/ruby_lsp/listeners/definition.rb +82 -24
  24. data/lib/ruby_lsp/listeners/hover.rb +62 -6
  25. data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
  26. data/lib/ruby_lsp/node_context.rb +39 -0
  27. data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
  28. data/lib/ruby_lsp/requests/code_actions.rb +16 -15
  29. data/lib/ruby_lsp/requests/completion.rb +21 -13
  30. data/lib/ruby_lsp/requests/completion_resolve.rb +9 -0
  31. data/lib/ruby_lsp/requests/definition.rb +25 -5
  32. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  33. data/lib/ruby_lsp/requests/hover.rb +5 -6
  34. data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
  35. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  36. data/lib/ruby_lsp/requests/support/common.rb +4 -3
  37. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +23 -6
  38. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +5 -1
  39. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
  40. data/lib/ruby_lsp/requests/workspace_symbol.rb +7 -4
  41. data/lib/ruby_lsp/server.rb +22 -5
  42. data/lib/ruby_lsp/test_helper.rb +1 -0
  43. metadata +29 -5
@@ -107,16 +107,16 @@ module RubyIndexer
107
107
  RUBY
108
108
 
109
109
  result = @index.fuzzy_search("Bar")
110
- assert_equal(1, result.length)
111
- assert_equal(@index["Bar"].first, result.first)
110
+ assert_equal(3, result.length)
111
+ assert_equal(["Bar", "Backtrace", "Base"], result.map(&:name))
112
112
 
113
113
  result = @index.fuzzy_search("foobarsomeking")
114
- assert_equal(5, result.length)
115
- assert_equal(["Foo::Baz::Something", "Foo::Bar", "Foo::Baz", "Foo", "Bar"], result.map(&:name))
114
+ assert_equal(6, result.length)
115
+ assert_equal(["Foo::Baz::Something", "Foo::Bar", "Foo::Baz", "Foo", "Base", "Bar"], result.map(&:name))
116
116
 
117
117
  result = @index.fuzzy_search("FooBaz")
118
- assert_equal(4, result.length)
119
- assert_equal(["Foo::Baz", "Foo::Bar", "Foo", "Foo::Baz::Something"], result.map(&:name))
118
+ assert_equal(5, result.length)
119
+ assert_equal(["Foo::Baz", "Foo::Bar", "Foo", "Foo::Baz::Something", "Float"], result.map(&:name))
120
120
  end
121
121
 
122
122
  def test_index_single_ignores_directories
@@ -140,6 +140,8 @@ module RubyIndexer
140
140
  end
141
141
 
142
142
  def test_searching_for_entries_based_on_prefix
143
+ # For this test, it's easier if we don't include core classes and modules
144
+ @index = Index.new
143
145
  @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
144
146
  class Foo::Bar
145
147
  end
@@ -181,6 +183,9 @@ module RubyIndexer
181
183
 
182
184
  def test_resolving_aliases_to_non_existing_constants_with_conflicting_names
183
185
  @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
186
+ class Float
187
+ end
188
+
184
189
  module Foo
185
190
  class Float < self
186
191
  INFINITY = ::Float::INFINITY
@@ -299,6 +304,10 @@ module RubyIndexer
299
304
  end
300
305
 
301
306
  def test_indexing_prism_fixtures_succeeds
307
+ unless Dir.exist?("test/fixtures/prism/test/prism/fixtures")
308
+ raise "Prism fixtures not found. Run `git submodule update --init` to fetch them."
309
+ end
310
+
302
311
  fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
303
312
 
304
313
  fixtures.each do |fixture|
@@ -311,7 +320,858 @@ module RubyIndexer
311
320
 
312
321
  def test_index_single_does_not_fail_for_non_existing_file
313
322
  @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"))
314
- assert_empty(@index.instance_variable_get(:@entries))
323
+ entries_after_indexing = @index.instance_variable_get(:@entries).keys
324
+ assert_equal(@default_indexed_entries.keys, entries_after_indexing)
325
+ end
326
+
327
+ def test_linearized_ancestors_basic_ordering
328
+ index(<<~RUBY)
329
+ module A; end
330
+ module B; end
331
+
332
+ class Foo
333
+ prepend A
334
+ prepend B
335
+ end
336
+
337
+ class Bar
338
+ include A
339
+ include B
340
+ end
341
+ RUBY
342
+
343
+ assert_equal(
344
+ [
345
+ "B",
346
+ "A",
347
+ "Foo",
348
+ "Object",
349
+ "Kernel",
350
+ "BasicObject",
351
+ ],
352
+ @index.linearized_ancestors_of("Foo"),
353
+ )
354
+
355
+ assert_equal(
356
+ [
357
+ "Bar",
358
+ "B",
359
+ "A",
360
+ "Object",
361
+ "Kernel",
362
+ "BasicObject",
363
+ ],
364
+ @index.linearized_ancestors_of("Bar"),
365
+ )
366
+ end
367
+
368
+ def test_linearized_ancestors
369
+ index(<<~RUBY)
370
+ module A; end
371
+ module B; end
372
+ module C; end
373
+
374
+ module D
375
+ include A
376
+ end
377
+
378
+ module E
379
+ prepend B
380
+ end
381
+
382
+ module F
383
+ include C
384
+ include A
385
+ end
386
+
387
+ class Bar
388
+ prepend F
389
+ end
390
+
391
+ class Foo < Bar
392
+ include E
393
+ prepend D
394
+ end
395
+ RUBY
396
+
397
+ # Object, Kernel and BasicObject are intentionally commented out for now until we develop a strategy for indexing
398
+ # declarations made in C code
399
+ assert_equal(
400
+ [
401
+ "D",
402
+ "A",
403
+ "Foo",
404
+ "B",
405
+ "E",
406
+ "F",
407
+ "A",
408
+ "C",
409
+ "Bar",
410
+ "Object",
411
+ "Kernel",
412
+ "BasicObject",
413
+ ],
414
+ @index.linearized_ancestors_of("Foo"),
415
+ )
416
+ end
417
+
418
+ def test_linearized_ancestors_duplicates
419
+ index(<<~RUBY)
420
+ module A; end
421
+ module B
422
+ include A
423
+ end
424
+
425
+ class Foo
426
+ include B
427
+ include A
428
+ end
429
+
430
+ class Bar
431
+ prepend B
432
+ prepend A
433
+ end
434
+ RUBY
435
+
436
+ assert_equal(
437
+ [
438
+ "Foo",
439
+ "B",
440
+ "A",
441
+ "Object",
442
+ "Kernel",
443
+ "BasicObject",
444
+ ],
445
+ @index.linearized_ancestors_of("Foo"),
446
+ )
447
+
448
+ assert_equal(
449
+ [
450
+ "B",
451
+ "A",
452
+ "Bar",
453
+ "Object",
454
+ "Kernel",
455
+ "BasicObject",
456
+ ],
457
+ @index.linearized_ancestors_of("Bar"),
458
+ )
459
+ end
460
+
461
+ def test_linearizing_ancestors_is_cached
462
+ index(<<~RUBY)
463
+ module C; end
464
+ module A; end
465
+ module B
466
+ include A
467
+ end
468
+
469
+ class Foo
470
+ include B
471
+ include A
472
+ end
473
+ RUBY
474
+
475
+ @index.linearized_ancestors_of("Foo")
476
+ ancestors = @index.instance_variable_get(:@ancestors)
477
+ assert(ancestors.key?("Foo"))
478
+ assert(ancestors.key?("A"))
479
+ assert(ancestors.key?("B"))
480
+ refute(ancestors.key?("C"))
481
+ end
482
+
483
+ def test_duplicate_prepend_include
484
+ index(<<~RUBY)
485
+ module A; end
486
+
487
+ class Foo
488
+ prepend A
489
+ include A
490
+ end
491
+
492
+ class Bar
493
+ include A
494
+ prepend A
495
+ end
496
+ RUBY
497
+
498
+ assert_equal(
499
+ [
500
+ "A",
501
+ "Foo",
502
+ "Object",
503
+ "Kernel",
504
+ "BasicObject",
505
+ ],
506
+ @index.linearized_ancestors_of("Foo"),
507
+ )
508
+
509
+ assert_equal(
510
+ [
511
+ "A",
512
+ "Bar",
513
+ "A",
514
+ "Object",
515
+ "Kernel",
516
+ "BasicObject",
517
+ ],
518
+ @index.linearized_ancestors_of("Bar"),
519
+ )
520
+ end
521
+
522
+ def test_linearizing_ancestors_handles_circular_parent_class
523
+ index(<<~RUBY)
524
+ class Foo < Foo
525
+ end
526
+ RUBY
527
+
528
+ assert_equal(["Foo"], @index.linearized_ancestors_of("Foo"))
529
+ end
530
+
531
+ def test_ancestors_linearization_complex_prepend_duplication
532
+ index(<<~RUBY)
533
+ module A; end
534
+ module B
535
+ prepend A
536
+ end
537
+ module C
538
+ prepend B
539
+ end
540
+
541
+ class Foo
542
+ prepend A
543
+ prepend C
544
+ end
545
+ RUBY
546
+
547
+ assert_equal(
548
+ [
549
+ "A",
550
+ "B",
551
+ "C",
552
+ "Foo",
553
+ "Object",
554
+ "Kernel",
555
+ "BasicObject",
556
+ ],
557
+ @index.linearized_ancestors_of("Foo"),
558
+ )
559
+ end
560
+
561
+ def test_ancestors_linearization_complex_include_duplication
562
+ index(<<~RUBY)
563
+ module A; end
564
+ module B
565
+ include A
566
+ end
567
+ module C
568
+ include B
569
+ end
570
+
571
+ class Foo
572
+ include A
573
+ include C
574
+ end
575
+ RUBY
576
+
577
+ assert_equal(
578
+ [
579
+ "Foo",
580
+ "C",
581
+ "B",
582
+ "A",
583
+ "Object",
584
+ "Kernel",
585
+ "BasicObject",
586
+ ],
587
+ @index.linearized_ancestors_of("Foo"),
588
+ )
589
+ end
590
+
591
+ def test_linearizing_ancestors_that_need_to_be_resolved
592
+ index(<<~RUBY)
593
+ module Foo
594
+ module Baz
595
+ end
596
+ module Qux
597
+ end
598
+
599
+ class Something; end
600
+
601
+ class Bar < Something
602
+ include Baz
603
+ prepend Qux
604
+ end
605
+ end
606
+ RUBY
607
+
608
+ assert_equal(
609
+ [
610
+ "Foo::Qux",
611
+ "Foo::Bar",
612
+ "Foo::Baz",
613
+ "Foo::Something",
614
+ "Object",
615
+ "Kernel",
616
+ "BasicObject",
617
+ ],
618
+ @index.linearized_ancestors_of("Foo::Bar"),
619
+ )
620
+ end
621
+
622
+ def test_linearizing_ancestors_for_non_existing_namespaces
623
+ index(<<~RUBY)
624
+ def Bar(a); end
625
+ RUBY
626
+
627
+ assert_raises(Index::NonExistingNamespaceError) do
628
+ @index.linearized_ancestors_of("Foo")
629
+ end
630
+
631
+ assert_raises(Index::NonExistingNamespaceError) do
632
+ @index.linearized_ancestors_of("Bar")
633
+ end
634
+ end
635
+
636
+ def test_linearizing_circular_ancestors
637
+ index(<<~RUBY)
638
+ module M1
639
+ include M2
640
+ end
641
+
642
+ module M2
643
+ include M1
644
+ end
645
+
646
+ module A1
647
+ include A2
648
+ end
649
+
650
+ module A2
651
+ include A3
652
+ end
653
+
654
+ module A3
655
+ include A1
656
+ end
657
+
658
+ class Foo < Foo
659
+ include Foo
660
+ end
661
+
662
+ module Bar
663
+ include Bar
664
+ end
665
+ RUBY
666
+
667
+ assert_equal(["M2", "M1"], @index.linearized_ancestors_of("M2"))
668
+ assert_equal(["A3", "A1", "A2"], @index.linearized_ancestors_of("A3"))
669
+ assert_equal(["Foo"], @index.linearized_ancestors_of("Foo"))
670
+ assert_equal(["Bar"], @index.linearized_ancestors_of("Bar"))
671
+ end
672
+
673
+ def test_linearizing_circular_aliased_dependency
674
+ index(<<~RUBY)
675
+ module A
676
+ end
677
+
678
+ ALIAS = A
679
+
680
+ module A
681
+ include ALIAS
682
+ end
683
+ RUBY
684
+
685
+ assert_equal(["A", "ALIAS"], @index.linearized_ancestors_of("A"))
686
+ end
687
+
688
+ def test_resolving_an_inherited_method
689
+ index(<<~RUBY)
690
+ module Foo
691
+ def baz; end
692
+ end
693
+
694
+ class Bar
695
+ def qux; end
696
+ end
697
+
698
+ class Wow < Bar
699
+ include Foo
700
+ end
701
+ RUBY
702
+
703
+ entry = T.must(@index.resolve_method("baz", "Wow")&.first)
704
+ assert_equal("baz", entry.name)
705
+ assert_equal("Foo", T.must(entry.owner).name)
706
+
707
+ entry = T.must(@index.resolve_method("qux", "Wow")&.first)
708
+ assert_equal("qux", entry.name)
709
+ assert_equal("Bar", T.must(entry.owner).name)
710
+ end
711
+
712
+ def test_resolving_an_inherited_method_lands_on_first_match
713
+ index(<<~RUBY)
714
+ module Foo
715
+ def qux; end
716
+ end
717
+
718
+ class Bar
719
+ def qux; end
720
+ end
721
+
722
+ class Wow < Bar
723
+ prepend Foo
724
+
725
+ def qux; end
726
+ end
727
+ RUBY
728
+
729
+ entries = T.must(@index.resolve_method("qux", "Wow"))
730
+ assert_equal(1, entries.length)
731
+
732
+ entry = T.must(entries.first)
733
+ assert_equal("qux", entry.name)
734
+ assert_equal("Foo", T.must(entry.owner).name)
735
+ end
736
+
737
+ def test_handle_change_clears_ancestor_cache_if_tree_changed
738
+ Dir.mktmpdir do |dir|
739
+ Dir.chdir(dir) do
740
+ # Write the original file
741
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
742
+ module Foo
743
+ end
744
+
745
+ class Bar
746
+ include Foo
747
+ end
748
+ RUBY
749
+
750
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
751
+ @index.index_single(indexable_path)
752
+
753
+ assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
754
+
755
+ # Remove include to invalidate the ancestor tree
756
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
757
+ module Foo
758
+ end
759
+
760
+ class Bar
761
+ end
762
+ RUBY
763
+
764
+ @index.handle_change(indexable_path)
765
+ assert_empty(@index.instance_variable_get(:@ancestors))
766
+ assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
767
+ end
768
+ end
769
+ end
770
+
771
+ def test_handle_change_does_not_clear_ancestor_cache_if_tree_not_changed
772
+ Dir.mktmpdir do |dir|
773
+ Dir.chdir(dir) do
774
+ # Write the original file
775
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
776
+ module Foo
777
+ end
778
+
779
+ class Bar
780
+ include Foo
781
+ end
782
+ RUBY
783
+
784
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
785
+ @index.index_single(indexable_path)
786
+
787
+ assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
788
+
789
+ # Remove include to invalidate the ancestor tree
790
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
791
+ module Foo
792
+ end
793
+
794
+ class Bar
795
+ include Foo
796
+
797
+ def baz; end
798
+ end
799
+ RUBY
800
+
801
+ @index.handle_change(indexable_path)
802
+ refute_empty(@index.instance_variable_get(:@ancestors))
803
+ assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
804
+ end
805
+ end
806
+ end
807
+
808
+ def test_handle_change_clears_ancestor_cache_if_parent_class_changed
809
+ Dir.mktmpdir do |dir|
810
+ Dir.chdir(dir) do
811
+ # Write the original file
812
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
813
+ class Foo
814
+ end
815
+
816
+ class Bar < Foo
817
+ end
818
+ RUBY
819
+
820
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
821
+ @index.index_single(indexable_path)
822
+
823
+ assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
824
+
825
+ # Remove include to invalidate the ancestor tree
826
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
827
+ class Foo
828
+ end
829
+
830
+ class Bar
831
+ end
832
+ RUBY
833
+
834
+ @index.handle_change(indexable_path)
835
+ assert_empty(@index.instance_variable_get(:@ancestors))
836
+ assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
837
+ end
838
+ end
839
+ end
840
+
841
+ def test_resolving_inherited_constants
842
+ index(<<~RUBY)
843
+ module Foo
844
+ CONST = 1
845
+ end
846
+
847
+ module Baz
848
+ CONST = 2
849
+ end
850
+
851
+ module Qux
852
+ include Foo
853
+ end
854
+
855
+ module Namespace
856
+ CONST = 3
857
+
858
+ include Baz
859
+
860
+ class Bar
861
+ include Qux
862
+ end
863
+ end
864
+
865
+ CONST = 4
866
+ RUBY
867
+
868
+ entry = T.must(@index.resolve("CONST", ["Namespace", "Bar"])&.first)
869
+ assert_equal(14, entry.location.start_line)
870
+ end
871
+
872
+ def test_resolving_inherited_alised_namespace
873
+ index(<<~RUBY)
874
+ module Bar
875
+ TARGET = 123
876
+ end
877
+
878
+ module Foo
879
+ CONST = Bar
880
+ end
881
+
882
+ module Namespace
883
+ class Bar
884
+ include Foo
885
+ end
886
+ end
887
+ RUBY
888
+
889
+ entry = T.must(@index.resolve("Foo::CONST::TARGET", [])&.first)
890
+ assert_equal(2, entry.location.start_line)
891
+
892
+ entry = T.must(@index.resolve("Namespace::Bar::CONST::TARGET", [])&.first)
893
+ assert_equal(2, entry.location.start_line)
894
+ end
895
+
896
+ def test_resolving_same_constant_from_different_scopes
897
+ index(<<~RUBY)
898
+ module Namespace
899
+ CONST = 123
900
+
901
+ class Parent
902
+ CONST = 321
903
+ end
904
+
905
+ class Child < Parent
906
+ end
907
+ end
908
+ RUBY
909
+
910
+ entry = T.must(@index.resolve("CONST", ["Namespace", "Child"])&.first)
911
+ assert_equal(2, entry.location.start_line)
912
+
913
+ entry = T.must(@index.resolve("Namespace::Child::CONST", [])&.first)
914
+ assert_equal(5, entry.location.start_line)
915
+ end
916
+
917
+ def test_resolving_prepended_constants
918
+ index(<<~RUBY)
919
+ module Included
920
+ CONST = 123
921
+ end
922
+
923
+ module Prepended
924
+ CONST = 321
925
+ end
926
+
927
+ class Foo
928
+ include Included
929
+ prepend Prepended
930
+ end
931
+
932
+ class Bar
933
+ CONST = 456
934
+ include Included
935
+ prepend Prepended
936
+ end
937
+ RUBY
938
+
939
+ entry = T.must(@index.resolve("CONST", ["Foo"])&.first)
940
+ assert_equal(6, entry.location.start_line)
941
+
942
+ entry = T.must(@index.resolve("Foo::CONST", [])&.first)
943
+ assert_equal(6, entry.location.start_line)
944
+
945
+ entry = T.must(@index.resolve("Bar::CONST", [])&.first)
946
+ assert_equal(15, entry.location.start_line)
947
+ end
948
+
949
+ def test_resolving_constants_favors_ancestors_over_top_level
950
+ index(<<~RUBY)
951
+ module Value1
952
+ CONST = 1
953
+ end
954
+
955
+ module Value2
956
+ CONST = 2
957
+ end
958
+
959
+ CONST = 3
960
+ module First
961
+ include Value1
962
+
963
+ module Second
964
+ include Value2
965
+ end
966
+ end
967
+ RUBY
968
+
969
+ entry = T.must(@index.resolve("CONST", ["First", "Second"])&.first)
970
+ assert_equal(6, entry.location.start_line)
971
+ end
972
+
973
+ def test_resolving_circular_alias
974
+ index(<<~RUBY)
975
+ module Namespace
976
+ FOO = BAR
977
+ BAR = FOO
978
+ end
979
+ RUBY
980
+
981
+ foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
982
+ assert_equal(2, foo_entry.location.start_line)
983
+ assert_instance_of(Entry::Alias, foo_entry)
984
+
985
+ bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
986
+ assert_equal(3, bar_entry.location.start_line)
987
+ assert_instance_of(Entry::Alias, bar_entry)
988
+ end
989
+
990
+ def test_resolving_circular_alias_three_levels
991
+ index(<<~RUBY)
992
+ module Namespace
993
+ FOO = BAR
994
+ BAR = BAZ
995
+ BAZ = FOO
996
+ end
997
+ RUBY
998
+
999
+ foo_entry = T.must(@index.resolve("FOO", ["Namespace"])&.first)
1000
+ assert_equal(2, foo_entry.location.start_line)
1001
+ assert_instance_of(Entry::Alias, foo_entry)
1002
+
1003
+ bar_entry = T.must(@index.resolve("BAR", ["Namespace"])&.first)
1004
+ assert_equal(3, bar_entry.location.start_line)
1005
+ assert_instance_of(Entry::Alias, bar_entry)
1006
+
1007
+ baz_entry = T.must(@index.resolve("BAZ", ["Namespace"])&.first)
1008
+ assert_equal(4, baz_entry.location.start_line)
1009
+ assert_instance_of(Entry::Alias, baz_entry)
1010
+ end
1011
+
1012
+ def test_resolving_top_level_compact_reference
1013
+ index(<<~RUBY)
1014
+ class Foo::Bar
1015
+ end
1016
+ RUBY
1017
+
1018
+ foo_entry = T.must(@index.resolve("Foo::Bar", [])&.first)
1019
+ assert_equal(1, foo_entry.location.start_line)
1020
+ assert_instance_of(Entry::Class, foo_entry)
1021
+ end
1022
+
1023
+ def test_resolving_references_with_redundant_namespaces
1024
+ index(<<~RUBY)
1025
+ module Bar
1026
+ CONST = 1
1027
+ end
1028
+
1029
+ module A
1030
+ CONST = 2
1031
+
1032
+ module B
1033
+ CONST = 3
1034
+
1035
+ class Foo
1036
+ include Bar
1037
+ end
1038
+
1039
+ A::B::Foo::CONST
1040
+ end
1041
+ end
1042
+ RUBY
1043
+
1044
+ foo_entry = T.must(@index.resolve("A::B::Foo::CONST", ["A", "B"])&.first)
1045
+ assert_equal(2, foo_entry.location.start_line)
1046
+ end
1047
+
1048
+ def test_resolving_qualified_references
1049
+ index(<<~RUBY)
1050
+ module Namespace
1051
+ class Entry
1052
+ CONST = 1
1053
+ end
1054
+ end
1055
+
1056
+ module Namespace
1057
+ class Index
1058
+ end
1059
+ end
1060
+ RUBY
1061
+
1062
+ foo_entry = T.must(@index.resolve("Entry::CONST", ["Namespace", "Index"])&.first)
1063
+ assert_equal(3, foo_entry.location.start_line)
1064
+ end
1065
+
1066
+ def test_resolving_unqualified_references
1067
+ index(<<~RUBY)
1068
+ module Foo
1069
+ CONST = 1
1070
+ end
1071
+
1072
+ module Namespace
1073
+ CONST = 2
1074
+
1075
+ class Index
1076
+ include Foo
1077
+ end
1078
+ end
1079
+ RUBY
1080
+
1081
+ foo_entry = T.must(@index.resolve("CONST", ["Namespace", "Index"])&.first)
1082
+ assert_equal(6, foo_entry.location.start_line)
1083
+ end
1084
+
1085
+ def test_resolving_references_with_only_top_level_declaration
1086
+ index(<<~RUBY)
1087
+ CONST = 1
1088
+
1089
+ module Foo; end
1090
+
1091
+ module Namespace
1092
+ class Index
1093
+ include Foo
1094
+ end
1095
+ end
1096
+ RUBY
1097
+
1098
+ foo_entry = T.must(@index.resolve("CONST", ["Namespace", "Index"])&.first)
1099
+ assert_equal(1, foo_entry.location.start_line)
1100
+ end
1101
+
1102
+ def test_instance_variables_completions_from_different_owners_with_conflicting_names
1103
+ index(<<~RUBY)
1104
+ class Foo
1105
+ def initialize
1106
+ @bar = 1
1107
+ end
1108
+ end
1109
+
1110
+ class Bar
1111
+ def initialize
1112
+ @bar = 2
1113
+ end
1114
+ end
1115
+ RUBY
1116
+
1117
+ entry = T.must(@index.instance_variable_completion_candidates("@", "Bar")&.first)
1118
+ assert_equal("@bar", entry.name)
1119
+ assert_equal("Bar", T.must(entry.owner).name)
1120
+ end
1121
+
1122
+ def test_resolving_a_qualified_reference
1123
+ index(<<~RUBY)
1124
+ class Base
1125
+ module Third
1126
+ CONST = 1
1127
+ end
1128
+ end
1129
+
1130
+ class Foo
1131
+ module Third
1132
+ CONST = 2
1133
+ end
1134
+
1135
+ class Second < Base
1136
+ end
1137
+ end
1138
+ RUBY
1139
+
1140
+ foo_entry = T.must(@index.resolve("Third::CONST", ["Foo"])&.first)
1141
+ assert_equal(9, foo_entry.location.start_line)
1142
+ end
1143
+
1144
+ def test_resolving_unindexed_constant_with_no_nesting
1145
+ assert_nil(@index.resolve("RSpec", []))
1146
+ end
1147
+
1148
+ def test_object_superclass_resolution
1149
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1150
+ module Foo
1151
+ class Object; end
1152
+
1153
+ class Bar; end
1154
+ class Baz < Object; end
1155
+ end
1156
+ RUBY
1157
+
1158
+ assert_equal(["Foo::Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Foo::Bar"))
1159
+ assert_equal(
1160
+ ["Foo::Baz", "Foo::Object", "Object", "Kernel", "BasicObject"],
1161
+ @index.linearized_ancestors_of("Foo::Baz"),
1162
+ )
1163
+ end
1164
+
1165
+ def test_top_level_object_superclass_resolution
1166
+ @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1167
+ module Foo
1168
+ class Object; end
1169
+
1170
+ class Bar < ::Object; end
1171
+ end
1172
+ RUBY
1173
+
1174
+ assert_equal(["Foo::Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Foo::Bar"))
315
1175
  end
316
1176
  end
317
1177
  end