diff --git a/lib/ruby_indexer/lib/ruby_indexer/index.rb b/lib/ruby_indexer/lib/ruby_indexer/index.rb index fdf91ea80..5da20c2ea 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/index.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/index.rb @@ -980,29 +980,23 @@ def inherited_constant_completion_candidates(name, nesting) # nesting sig { params(name: String, nesting: T::Array[String]).returns(String) } def build_non_redundant_full_name(name, nesting) + # If there's no nesting, then we can just return the name as is return name if nesting.empty? - namespace = nesting.join("::") - # If the name is not qualified, we can just concatenate the nesting and the name - return "#{namespace}::#{name}" unless name.include?("::") + return "#{nesting.join("::")}::#{name}" unless name.include?("::") name_parts = name.split("::") + first_redundant_part = nesting.index(name_parts[0]) - # Find the first part of the name that is not in the nesting - index = name_parts.index { |part| !nesting.include?(part) } + # If there are no redundant parts between the name and the nesting, then the full name is both combined + return "#{nesting.join("::")}::#{name}" unless first_redundant_part - if index.nil? - # All parts of the nesting are redundant because they are already present in the name. We can return the name - # directly - name - elsif index == 0 - # No parts of the nesting are in the name, we can concatenate the namespace and the name - "#{namespace}::#{name}" - else - # The name includes some parts of the nesting. We need to remove the redundant parts - "#{namespace}::#{T.must(name_parts[index..-1]).join("::")}" - end + # Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we + # have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but + # we still need to include the `Namespace` part + T.unsafe(name_parts).unshift(*nesting[0...first_redundant_part]) + name_parts.join("::") end sig do diff --git a/lib/ruby_indexer/test/index_test.rb b/lib/ruby_indexer/test/index_test.rb index 90cd40424..d750c2abc 100644 --- a/lib/ruby_indexer/test/index_test.rb +++ b/lib/ruby_indexer/test/index_test.rb @@ -1934,5 +1934,94 @@ module Namespace real_namespace = @index.follow_aliased_namespace("Namespace::Second") assert_equal("First::Second", real_namespace) end + + def test_resolving_alias_to_non_existing_namespace + index(<<~RUBY) + module Namespace + class Foo + module InnerNamespace + Constants = Namespace::Foo::Constants + end + end + end + RUBY + + entry = @index.resolve("Constants", ["Namespace", "Foo", "InnerNamespace"])&.first + assert_instance_of(Entry::UnresolvedConstantAlias, entry) + + entry = @index.resolve("Namespace::Foo::Constants", ["Namespace", "Foo", "InnerNamespace"])&.first + assert_nil(entry) + end + + def test_resolving_alias_to_existing_constant_from_inner_namespace + index(<<~RUBY) + module Parent + CONST = 123 + end + + module First + module Namespace + class Foo + include Parent + + module InnerNamespace + Constants = Namespace::Foo::CONST + end + end + end + end + RUBY + + entry = @index.resolve("Namespace::Foo::CONST", ["First", "Namespace", "Foo", "InnerNamespace"])&.first + assert_equal("Parent::CONST", entry.name) + assert_instance_of(Entry::Constant, entry) + end + + def test_build_non_redundant_name + assert_equal( + "Namespace::Foo::Constants", + @index.send( + :build_non_redundant_full_name, + "Namespace::Foo::Constants", + ["Namespace", "Foo", "InnerNamespace"], + ), + ) + + assert_equal( + "Namespace::Foo::Constants", + @index.send( + :build_non_redundant_full_name, + "Namespace::Foo::Constants", + ["Namespace", "Foo"], + ), + ) + + assert_equal( + "Namespace::Foo::Constants", + @index.send( + :build_non_redundant_full_name, + "Foo::Constants", + ["Namespace", "Foo"], + ), + ) + + assert_equal( + "Bar::Namespace::Foo::Constants", + @index.send( + :build_non_redundant_full_name, + "Namespace::Foo::Constants", + ["Bar"], + ), + ) + + assert_equal( + "First::Namespace::Foo::Constants", + @index.send( + :build_non_redundant_full_name, + "Namespace::Foo::Constants", + ["First", "Namespace", "Foo", "InnerNamespace"], + ), + ) + end end end