Реальный ответ здесь: вы никогда не можете знать наверняка.
По крайней мере, для нетривиальных случаев вы не можете быть уверены, что получили все это. Рассмотрим следующее из статьи Википедии о недоступном коде :
double x = sqrt(2);
if (x > 5)
{
doStuff();
}
Как правильно отмечает Википедия, умный компилятор может поймать что-то подобное. Но рассмотрим модификацию:
int y;
cin >> y;
double x = sqrt((double)y);
if (x != 0 && x < 1)
{
doStuff();
}
Компилятор поймает это? Может быть. Но для этого нужно будет сделать больше, чем просто запустить sqrt
скалярное значение. Придется выяснить, что (double)y
всегда будет целым числом (легко), а затем понять математический диапазон sqrt
для набора целых чисел (трудно). Очень сложный компилятор может сделать это для sqrt
функции, или для каждой функции в math.h , или для любой функции с фиксированным вводом, чью область он может выяснить. Это становится очень, очень сложным, и сложность в основном безгранична. Вы можете продолжать добавлять уровни сложности к своему компилятору, но всегда будет способ проникнуть в некоторый код, который будет недоступен для любого данного набора входных данных.
И затем есть наборы ввода, которые просто никогда не вводятся. Ввод, который не имеет смысла в реальной жизни или блокируется логикой проверки в другом месте. У компилятора нет возможности узнать о них.
Конечным результатом этого является то, что хотя программные инструменты, о которых упоминали другие, чрезвычайно полезны, вы никогда не узнаете наверняка, что все поймали, если потом не пройдете код вручную. Даже тогда вы никогда не будете уверены, что ничего не пропустили.
ИМХО, единственное реальное решение - это быть как можно бдительнее, использовать автоматизацию в вашем распоряжении, проводить рефакторинг, где вы можете, и постоянно искать способы улучшить свой код. Конечно, в любом случае это хорошая идея.