Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove non-existing algorithm path for constant resolution #2158

Merged
merged 1 commit into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 2 additions & 34 deletions lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ def resolve(name, nesting, seen_names = [])
return entries if entries
end

return lookup_qualified_reference(name, nesting, seen_names) if name.include?("::")

# Non qualified reference path
full_name = nesting.any? ? "#{nesting.join("::")}::#{name}" : name

Expand All @@ -171,9 +169,6 @@ def resolve(name, nesting, seen_names = [])
entries = direct_or_aliased_constant(full_name, seen_names)
return entries if entries

# If the nesting is empty and we did not find results at this point, that means we haven't indexed this constant
return if nesting.empty?

# If the constant is not found yet, then Ruby will try to find the constant in the enclosing lexical scopes,
# unwrapping each level one by one. Important note: the top level is not included because that's the fallback of
# the algorithm after every other possibility has been exhausted
Expand Down Expand Up @@ -480,35 +475,6 @@ def resolve_alias(entry, seen_names)
resolved_alias
end

sig do
params(
name: String,
nesting: T::Array[String],
seen_names: T::Array[String],
).returns(T.nilable(T::Array[Entry]))
end
def lookup_qualified_reference(name, nesting, seen_names)
# If the name is qualified (has any namespace parts), then Ruby will search for the constant first in the exact
# namespace where the reference was found across the entire ancestor chain
#
# First, check if the constant is in the exact namespace where the reference was found
full_name = nesting.any? ? "#{nesting.join("::")}::#{name}" : name
results = direct_or_aliased_constant(full_name, seen_names)
return results if results

# Then search ancestors for the current namespace
entries = lookup_ancestor_chain(name, nesting, seen_names)
return entries if entries

# If the qualified constant is not found in the ancestors of the specific namespace where the reference was
# found, then Ruby searches for it in the enclosing lexical scopes
entries = lookup_enclosing_scopes(name, nesting, seen_names)
return entries if entries

# Finally, as a fallback, Ruby will search for the constant in the top level namespace
search_top_level(name, seen_names)
end

sig do
params(
name: String,
Expand Down Expand Up @@ -543,6 +509,8 @@ def lookup_enclosing_scopes(name, nesting, seen_names)
end
def lookup_ancestor_chain(name, nesting, seen_names)
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
return if T.must(nesting_parts).empty?

namespace_entries = resolve(T.must(nesting_parts).join("::"), [], seen_names)
return unless namespace_entries

Expand Down
26 changes: 26 additions & 0 deletions lib/ruby_indexer/test/index_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1125,5 +1125,31 @@ def initialize
assert_equal("@bar", entry.name)
assert_equal("Bar", T.must(entry.owner).name)
end

def test_resolving_a_qualified_reference
index(<<~RUBY)
class Base
module Third
CONST = 1
end
end

class Foo
module Third
CONST = 2
end

class Second < Base
end
end
RUBY

foo_entry = T.must(@index.resolve("Third::CONST", ["Foo"])&.first)
assert_equal(9, foo_entry.location.start_line)
end

def test_resolving_unindexed_constant_with_no_nesting
assert_nil(@index.resolve("RSpec", []))
end
end
end
Loading