Skip to content
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
47 changes: 27 additions & 20 deletions lib/rbs/definition_builder/method_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,40 +46,47 @@ def validate!
self
end

def each
if block_given?
Sorter.new(methods).each_strongly_connected_component do |scc|
if scc.size > 1
raise RecursiveAliasDefinitionError.new(type: type, defs: scc)
def each(&block)
if block
# Yields the original method of an alias before the alias, like the
# topological sort did, and detects recursive alias definitions on the way
if methods.each_value.any? {|defn| defn.original.is_a?(AST::Members::Alias) }
done = {} #: Hash[Definition, bool]
done.compare_by_identity
methods.each_value do |defn|
each_alias_first(defn, done, [], &block)
end

yield scc[0]
else
methods.each_value(&block)
end
else
enum_for :each
end
end

class Sorter
include TSort
private

attr_reader :methods
def each_alias_first(defn, done, visiting, &block)
return if done[defn]

def initialize(methods)
@methods = methods
if visiting.any? {|other| other.equal?(defn) }
index = visiting.index {|other| other.equal?(defn) } or raise
raise RecursiveAliasDefinitionError.new(type: type, defs: visiting[index..] || raise)
end

def tsort_each_node(&block)
methods.each_value(&block)
end

def tsort_each_child(defn)
if (member = defn.original).is_a?(AST::Members::Alias)
if old = methods[member.old_name]
yield old
if (member = defn.original).is_a?(AST::Members::Alias)
if old = methods.fetch(member.old_name, nil)
# A self alias forms a size-1 SCC that the topological sort yielded as is
unless old.equal?(defn)
visiting.push(defn)
each_alias_first(old, done, visiting, &block)
visiting.pop
end
end
end

done[defn] = true
yield defn
end
end

Expand Down
17 changes: 7 additions & 10 deletions sig/method_builder.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,14 @@ module RBS
def each: () { (Definition) -> void } -> void
| () -> Enumerator[Definition, void]

class Sorter
include TSort[Definition]
private

attr_reader methods: Hash[Symbol, Definition]

def initialize: (Hash[Symbol, Definition]) -> void

def tsort_each_node: { (Definition) -> void } -> void

def tsort_each_child: (Definition) { (Definition) -> void } -> void
end
# Yields the definition, recursively yielding the original of an alias first
#
# Raises `RecursiveAliasDefinitionError` when the aliases form a cycle of two or
# more methods.
#
def each_alias_first: (Definition, Hash[Definition, bool] done, Array[Definition] visiting) { (Definition) -> void } -> void
end

attr_reader env: Environment
Expand Down
Loading