lib/parlour/conflict_resolver.rb in parlour-1.0.0 vs lib/parlour/conflict_resolver.rb in parlour-2.0.0
- old
+ new
@@ -86,24 +86,52 @@
# Start by removing all the conflicting items
children.each do |c|
namespace.children.delete(c)
end
- # We can only try to resolve automatically if they're all the same
- # type of object, so check that first
- children_type = single_type_of_array(children)
- unless children_type
- Debugging.debug_puts(self, Debugging::Tree.end("Children are different types; requesting manual resolution"))
+ # Check that the types of the given objects allow them to be merged,
+ # and get the strategy to use
+ strategy = merge_strategy(children)
+ unless strategy
+ Debugging.debug_puts(self, Debugging::Tree.end("Children are unmergeable types; requesting manual resolution"))
# The types aren't the same, so ask the resolver what to do, and
# insert that (if not nil)
choice = resolver.call("Different kinds of definition for the same name", children)
namespace.children << choice if choice
next
end
+ case strategy
+ when :normal
+ first, *rest = children
+ when :differing_namespaces
+ # Let the namespaces be merged normally, but handle the method here
+ namespaces, non_namespaces = children.partition { |x| RbiGenerator::Namespace === x }
+
+ # If there is any non-namespace item in this conflict, it should be
+ # a single method
+ if non_namespaces.length != 0
+ unless non_namespaces.length == 1 && RbiGenerator::Method === non_namespaces.first
+ Debugging.debug_puts(self, Debugging::Tree.end("Non-namespace item in a differing namespace conflict is not a single method; requesting manual resolution"))
+ # The types aren't the same, so ask the resolver what to do, and
+ # insert that (if not nil)
+ choice = resolver.call("Non-namespace item in a differing namespace conflict is not a single method", non_namespaces)
+ non_namespaces = []
+ non_namespaces << choice if choice
+ end
+ end
+
+ non_namespaces.each do |x|
+ namespace.children << x
+ end
+
+ first, *rest = namespaces
+ else
+ raise 'unknown merge strategy; this is a Parlour bug'
+ end
+
# Can the children merge themselves automatically? If so, let them
- first, *rest = children
first, rest = T.must(first), T.must(rest)
if T.must(first).mergeable?(T.must(rest))
Debugging.debug_puts(self, Debugging::Tree.end("Children are all mergeable; resolving automatically"))
first.merge_into_self(rest)
namespace.children << first
@@ -129,18 +157,52 @@
Debugging.debug_puts(self, Debugging::Tree.end("All children done"))
end
private
- sig { params(arr: T::Array[T.untyped]).returns(T.nilable(Class)) }
+ sig { params(arr: T::Array[T.untyped]).returns(T.nilable(Symbol)) }
# Given an array, if all elements in the array are instances of the exact
- # same class, returns that class. If they are not, returns nil.
+ # same class or are otherwise mergeable (for example Namespace and
+ # ClassNamespace), returns the kind of merge which needs to be made. A
+ # return value of nil indicates that the values cannot be merged.
#
+ # The following kinds are available:
+ # - They are all the same. (:normal)
+ # - There are exactly two types, one of which is Namespace and other is a
+ # subclass of it. (:differing_namespaces)
+ # - One of them is Namespace or a subclass (or both, as described above),
+ # and the only other is Method. (also :differing_namespaces)
+ #
# @param arr [Array] The array.
- # @return [Class, nil] Either a class, or nil.
- def single_type_of_array(arr)
+ # @return [Symbol] The merge strategy to use, or nil if they can't be
+ # merged.
+ def merge_strategy(arr)
+ # If they're all the same type, they can be merged easily
array_types = arr.map { |c| c.class }.uniq
- array_types.length == 1 ? array_types.first : nil
+ return :normal if array_types.length == 1
+
+ # Find all the namespaces and non-namespaces
+ namespace_types, non_namespace_types = array_types.partition { |x| x <= RbiGenerator::Namespace }
+
+ # If there are two namespace types, one should be Namespace and the other
+ # should be a subclass of it
+ if namespace_types.length == 2
+ exactly_namespace, exactly_one_subclass = namespace_types.partition { |x| x == RbiGenerator::Namespace }
+
+ return nil unless exactly_namespace.length == 1 \
+ && exactly_one_subclass.length == 1 \
+ && exactly_one_subclass.first < RbiGenerator::Namespace
+ elsif namespace_types.length != 1
+ # The only other valid number of namespaces is 1, where we don't need to
+ # check anything
+ return nil
+ end
+
+ # It's OK, albeit cursed, for there to be a method with the same name as
+ # a namespace (Rainbow does this)
+ return nil if non_namespace_types.length != 0 && non_namespace_types != [RbiGenerator::Method]
+
+ :differing_namespaces
end
sig { params(arr: T::Array[T.untyped]).returns(T::Boolean) }
# Given an array, returns true if all elements in the array are equal by
# +==+. (Assumes a transitive definition of +==+.)