lib/ruby_indexer/lib/ruby_indexer/index.rb in ruby-lsp-0.17.16 vs lib/ruby_indexer/lib/ruby_indexer/index.rb in ruby-lsp-0.17.17
- old
+ new
@@ -230,11 +230,11 @@
# last part of the conditional checks if the current entry was found earlier in the hierarchy chain, in which
# case we must update the existing entry to avoid showing the wrong method declaration for overridden methods
next unless ancestor_index && (!existing_entry || ancestor_index < existing_entry_index)
if entry.is_a?(Entry::UnresolvedMethodAlias)
- resolved_alias = resolve_method_alias(entry, receiver_name)
+ resolved_alias = resolve_method_alias(entry, receiver_name, [])
hash[entry_name] = [resolved_alias, ancestor_index] if resolved_alias.is_a?(Entry::MethodAlias)
else
hash[entry_name] = [entry, ancestor_index]
end
end
@@ -392,20 +392,22 @@
end
real_parts.join("::")
end
- # Attempts to find methods for a resolved fully qualified receiver name.
+ # Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
+ # as it is used only internally to prevent infinite loops when resolving circular aliases
# Returns `nil` if the method does not exist on that receiver
sig do
params(
method_name: String,
receiver_name: String,
+ seen_names: T::Array[String],
inherited_only: T::Boolean,
).returns(T.nilable(T::Array[T.any(Entry::Member, Entry::MethodAlias)]))
end
- def resolve_method(method_name, receiver_name, inherited_only: false)
+ def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
method_entries = self[method_name]
return unless method_entries
ancestors = linearized_ancestors_of(receiver_name.delete_prefix("::"))
ancestors.each do |ancestor|
@@ -416,11 +418,11 @@
when Entry::Member, Entry::MethodAlias
entry if entry.owner&.name == ancestor
when Entry::UnresolvedMethodAlias
# Resolve aliases lazily as we find them
if entry.owner&.name == ancestor
- resolved_alias = resolve_method_alias(entry, receiver_name)
+ resolved_alias = resolve_method_alias(entry, receiver_name, seen_names)
resolved_alias if resolved_alias.is_a?(Entry::MethodAlias)
end
end
end
@@ -612,10 +614,21 @@
end
singleton
end
+ sig do
+ type_parameters(:T).params(
+ path: String,
+ type: T::Class[T.all(T.type_parameter(:T), Entry)],
+ ).returns(T.nilable(T::Array[T.type_parameter(:T)]))
+ end
+ def entries_for(path, type)
+ entries = @files_to_entries[path]
+ entries&.grep(type)
+ end
+
private
# Runs the registered included hooks
sig { params(fully_qualified_name: String, nesting: T::Array[String]).void }
def run_included_hooks(fully_qualified_name, nesting)
@@ -917,19 +930,24 @@
# identify the target or the same unresolved alias entry if we couldn't
sig do
params(
entry: Entry::UnresolvedMethodAlias,
receiver_name: String,
+ seen_names: T::Array[String],
).returns(T.any(Entry::MethodAlias, Entry::UnresolvedMethodAlias))
end
- def resolve_method_alias(entry, receiver_name)
- return entry if entry.new_name == entry.old_name
+ def resolve_method_alias(entry, receiver_name, seen_names)
+ new_name = entry.new_name
+ return entry if new_name == entry.old_name
+ return entry if seen_names.include?(new_name)
- target_method_entries = resolve_method(entry.old_name, receiver_name)
+ seen_names << new_name
+
+ target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
return entry unless target_method_entries
resolved_alias = Entry::MethodAlias.new(T.must(target_method_entries.first), entry)
- original_entries = T.must(@entries[entry.new_name])
+ original_entries = T.must(@entries[new_name])
original_entries.delete(entry)
original_entries << resolved_alias
resolved_alias
end
end