Реальный ответ здесь: Вы никогда не можете знать наверняка.
По крайней мере, для нетривиальных случаев вы не можете быть уверены, что получили все это. Рассмотрим следующее из статьи Википедии о недоступном коде :
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 , или для любой функции с фиксированным вводом, чью область он может выяснить. Это становится очень, очень сложным, и сложность в основном безгранична. Вы можете продолжать добавлять уровни сложности к своему компилятору, но всегда будет способ проникнуть в некоторый код, который будет недоступен для любого заданного набора входных данных.
И есть наборы ввода, которые просто никогда не вводятся. Ввод, который не имеет смысла в реальной жизни или блокируется логикой проверки в другом месте. Компилятор не сможет узнать о них.
Конечным результатом этого является то, что, хотя программные инструменты, о которых упоминали другие, чрезвычайно полезны, вы никогда не узнаете наверняка, что все поймали, если потом не пройдете код вручную. Даже тогда вы никогда не будете уверены, что ничего не пропустили.
Единственное реальное решение, ИМХО, - это быть как можно бдительнее, использовать автоматизацию в вашем распоряжении, проводить рефакторинг, где вы можете, и постоянно искать способы улучшить свой код. Конечно, в любом случае это хорошая идея.