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