src/Validator/Rules/OverlappingFieldsCanBeMerged.php
private function findConflict(
        ValidationContext $context,
        $parentFieldsAreMutuallyExclusive,
        $responseName,
        array $field1,
        array $field2
    ) {
        [$parentType1, $ast1, $def1] = $field1;
        [$parentType2, $ast2, $def2] = $field2;

        // If it is known that two fields could not possibly apply at the same
        // time, due to the parent types, then it is safe to permit them to diverge
        // in aliased field or arguments used as they will not present any ambiguity
        // by differing.
        // It is known that two parent types could never overlap if they are
        // different Object types. Interface or Union types might overlap - if not
        // in the current state of the schema, then perhaps in some future version,
        // thus may not safely diverge.
        $areMutuallyExclusive =
            $parentFieldsAreMutuallyExclusive ||
            (
                $parentType1 !== $parentType2 &&
                $parentType1 instanceof ObjectType &&
                $parentType2 instanceof ObjectType
            );

        // The return type for each field.
        $type1 = $def1 === null ? null : $def1->getType();
        $type2 = $def2 === null ? null : $def2->getType();

        if (! $areMutuallyExclusive) {
            // Two aliases must refer to the same field.
            $name1 = $ast1->name->value;
            $name2 = $ast2->name->value;
            if ($name1 !== $name2) {
                return [
                    [$responseName, sprintf('%s and %s are different fields', $name1, $name2)],
                    [$ast1],
                    [$ast2],
                ];
            }

            if (! $this->sameArguments($ast1->arguments ?: [], $ast2->arguments ?: [])) {
                return [
                    [$responseName, 'they have differing arguments'],
                    [$ast1],
                    [$ast2],
                ];
            }
        }

        if ($type1 && $type2 && $this->doTypesConflict($type1, $type2)) {
            return [
                [$responseName, sprintf('they return conflicting types %s and %s', $type1, $type2)],
                [$ast1],
                [$ast2],
            ];
        }

        // Collect and compare sub-fields. Use the same "visited fragment names" list
        // for both collections so fields in a fragment reference are never
        // compared to themselves.
        $selectionSet1 = $ast1->selectionSet;
        $selectionSet2 = $ast2->selectionSet;
        if ($selectionSet1 && $selectionSet2) {
            $conflicts = $this->findConflictsBetweenSubSelectionSets(
                $context,
                $areMutuallyExclusive,
                Type::getNamedType($type1),
                $selectionSet1,
                Type::getNamedType($type2),
                $selectionSet2
            );

            return $this->subfieldConflicts(
                $conflicts,
                $responseName,
                $ast1,
                $ast2
            );
        }

        return null;
    }
private function findConflict(
        ValidationContext $context,
        $parentFieldsAreMutuallyExclusive,
        $responseName,
        array $field1,
        array $field2
    ) {
        [$parentType1, $ast1, $def1] = $field1;
        [$parentType2, $ast2, $def2] = $field2;

        // If it is known that two fields could not possibly apply at the same
        // time, due to the parent types, then it is safe to permit them to diverge
        // in aliased field or arguments used as they will not present any ambiguity
        // by differing.
        // It is known that two parent types could never overlap if they are
        // different Object types. Interface or Union types might overlap - if not
        // in the current state of the schema, then perhaps in some future version,
        // thus may not safely diverge.
        $areMutuallyExclusive =
            $parentFieldsAreMutuallyExclusive ||
            (
                $parentType1 !== $parentType2 &&
                $parentType1 instanceof ObjectType &&
                $parentType2 instanceof ObjectType
            );

        // The return type for each field.
        $type1 = $def1 === null ? null : $def1->getType();
        $type2 = $def2 === null ? null : $def2->getType();

        if (! $areMutuallyExclusive) {
            // Two aliases must refer to the same field.
            $name1 = $ast1->name->value;
            $name2 = $ast2->name->value;
            if ($name1 !== $name2) {
                return [
                    [$responseName, sprintf('%s and %s are different fields', $name1, $name2)],
                    [$ast1],
                    [$ast2],
                ];
            }

            if (! $this->sameArguments($ast1->arguments ?: [], $ast2->arguments ?: [])) {
                return [
                    [$responseName, 'they have differing arguments'],
                    [$ast1],
                    [$ast2],
                ];
            }
        }

        if ($type1 && $type2 && $this->doTypesConflict($type1, $type2)) {
            return [
                [$responseName, sprintf('they return conflicting types %s and %s', $type1, $type2)],
                [$ast1],
                [$ast2],
            ];
        }

        // Collect and compare sub-fields. Use the same "visited fragment names" list
        // for both collections so fields in a fragment reference are never
        // compared to themselves.
        $selectionSet1 = $ast1->selectionSet;
        $selectionSet2 = $ast2->selectionSet;
        if ($selectionSet1 && $selectionSet2) {
            $conflicts = $this->findConflictsBetweenSubSelectionSets(
                $context,
                $areMutuallyExclusive,
                Type::getNamedType($type1),
                $selectionSet1,
                Type::getNamedType($type2),
                $selectionSet2
            );

            return $this->subfieldConflicts(
                $conflicts,
                $responseName,
                $ast1,
                $ast2
            );
        }

        return null;
    }
Determines if there is a conflict between two particular fields, including
comparing their sub-fields.

@param bool    $parentFieldsAreMutuallyExclusive
@param string  $responseName
@param mixed[] $field1
@param mixed[] $field2

@return mixed[]|null