Вы всегда можете написать свой собственный код. Это подпадает под надлежащее модульное тестирование.
ЕСЛИ ваши аналогичные функции изменяют некоторый экземпляр / класс состояния, ТО затем используйте отражение, чтобы проверить, равны ли значения свойств результирующих классов.
Если ваши аналогичные функции изменяют базу данных, ТОГДА сделайте копию базы данных, запустите каждую функцию для копии и сравните.
Но, вероятно, все начинается с правильных юнит-тестов. Если вам известны все возможные «варианты использования», то, когда вы обнаружите, что выходные данные двух (или более) функций одинаковы для всех этих «вариантов использования», вы можете безопасно оставить одну функцию и выбросить повторы за ненадобностью.
Другой вариант - получить фактические требования к тому, что делают код / функции. Изучение того, что ваша система на самом деле пытается выполнить, значительно упрощает рефакторинг старого или повторяющегося кода.
Инструменты, которые проверяют дублирование логики, приведут вас к тому, насколько вы готовы работать. Если вы говорите, что текущие инструменты не учитывают вложенные функции или функции, которые вызывают другие функции, то почему бы не изменить код, чтобы встроить эти вызываемые функции, чтобы ваш инструмент работал? Вы просто ищете панацею, если вы даже не хотите рефакторизовать метод A (который вызывает методы B, C, D) в метод AA, который включает код из B, C, D.
КОРОТКО с некоторой «работой» вы можете заставить текущие инструменты работать на вас. Возможно, вы захотите внести свой вклад в инструменты с открытым исходным кодом, чтобы компенсировать упомянутые вами ошибки.