Dead Code Finder: Trudna rzeczywistość analizy statycznej
Zbudowałem narzędzie na hackathon, aby znajdować martwy kod. Cel był prosty: znaleźć kod, którego nikt nie wywołuje.
Nie chciałem wiedzieć, co się popsuje po usunięciu kodu. Chciałem wiedzieć, czy jakakolwiek część kodu w ogóle wywołuje dany fragment.
Nazwałem je Dead Code Finder. Narzędzie wykorzystuje graf wiedzy do wyszukiwania wywołań i importów. Każde znalezisko sortuje do trzech kategorii:
• Pewne (Confident): Zero krawędzi przychodzących i nie jest to punkt wejścia. • Niepewne (Uncertain): Przypadki takie jak dziedziczenie, gdzie analiza statyczna jest niewystarczająca. • Pominięte (Skipped): Elementy takie jak dekoratory czy frameworki testowe, których narzędzie nie potrafi rozstrzygnąć.
Trzymałem się jednej surowej zasady: nigdy nie twierdzić, że kod można bezpiecznie usunąć. Raport informuje jedynie, że w grafie nie znaleziono żadnego odniesienia.
Projekt okazał się trudniejszy, niż się spodziewałem. Napotkałem dwa główne problemy z platformą:
- Brakujące narzędzia: Narzędzia grafowe nie były dostępne podczas działania programu, mimo że znajdowały się w konfiguracji.
- Niestabilna iniekcja: System czasami nie dostarczał pełnej logiki dla agenta.
Rozwiązałem to, budując tryb fallback. Jeśli narzędzia grafowe są niedostępne, narzędzie czyta rzeczywiste pliki w repozytorium. Wykorzystuje przeszukiwanie plików, aby znaleźć odniesienia. Jeśli używa tej metody, oznacza znaleziska jako wywnioskowane (inferred).
Musiałem również naprawić błędy logiczne w konkretnych przypadkach:
- Metody dunder: Metody takie jak
__init__często wykazują zero krawędzi przychodzących, ponieważ graf łączy wywołanie z klasą, a nie z metodą. Naprawiłem to, sprawdzając klasę otaczającą. - Dekoratory: Funkcje wywoływane poprzez wyszukiwanie ciągów znaków w słowniku wydają się martwe dla statycznego grafu. Przeniosłem je do kategorii Skipped.
- Testy: Frameworki testowe znajdują metody poprzez refleksję. One również trafiają do kategorii Skipped.
Wyniki były wiarygodne. Mój tryb fallback poprawnie identyfikował martwy kod i zgadzał się z rzeczywistymi danymi z grafu. Poprawnie oznaczał również przypadki niepewne, takie jak dziedziczenie.
Wyciągnięte wnioski:
- Potwierdź dostępność narzędzi przed pisaniem logiki, która od nich zależy.
- Raport, który mówi „nie wiem”, jest lepszy niż raport, który jest pewny siebie, ale błędny.
- Oznaczanie niepewności sprawia, że Twoje pewne znaleziska są warte podjęcia działań.
Opcjonalna społeczność edukacyjna: https://t.me/GyaanSetuAi
