ErrorProne.NET. Część 1
ErrorProne.NET. Część 1
Dawno już miałem zamiar zrobić analizator, który pomógłby w przechwytywaniu błędów, w różnym stopniu specyficznych dla platformy .NET. Wiele takich błędów świetnie przechwytuje R#, ale przecież zawsze chce się czegoś swojego, prawda? Poza tym, analizatory Roslyn ulegają bezszwowej integracji do procesu kompilacji, mogą być używane w nocy (*) i mogą zawierać reguły specyficzne dla Twojego produktu.
12 sty 2017
2187
Other articles
Testowanie aplikacji za pomocą JUnit5 i JMock. Część 2
Jak przygotować się do certyfikacji IIBA. Wyzwania i hacki
Testowanie aplikacji za pomocą JUnit5 i Mockito. Część 2
Testowanie aplikacji za pomocą JUnit5 i Mockito. Część 1
Testowanie aplikacji za pomocą JUnit5 i EasyMock. Część 2
Testowanie aplikacji za pomocą JUnit5 i EasyMock. Część 1
Test Driven Development z użyciem JUnit 5. Część 6
Test Driven Development z użyciem JUnit 5. Część 5
Test Driven Development z użyciem JUnit 5. Część 4
Test Driven Development z uzyciem JUnit 5. Czesc 3
Dawno już miałem zamiar zrobić analizator, który pomógłby w przechwytywaniu błędów, w różnym stopniu specyficznych dla platformy .NET. Wiele takich błędów świetnie przechwytuje R#, ale przecież zawsze chce się czegoś swojego, prawda? Poza tym, analizatory Roslyn ulegają bezszwowej integracji do procesu kompilacji, mogą być używane w nocy (*) i mogą zawierać reguły specyficzne dla Twojego produktu.
Tak więc, bazując na ideach z R# i z podobnej biblioteki dla Java od Google'a pod nazwą Error Prone, zabrałem się do roboty. Poniżej przedstawiam pierwszą część rezultatów mojej pracy.
Wywołanie metod pure
Brak <obserwacji> wyników wywołania metody pure jest jednym z najczęstszych błędów zachodzących w trakcie testów lokalnych. Problem polega na tym, że po prostu czytając kod, trudno z wyprzedzeniem powiedzieć czy wywołanie someCollection.Union(anotherCollection) jest <pure> i zwraca nową kolekcję czy zmienia oryginalną.
Oto przykłady działania tej reguły:

Ta reguła uwzględnia szereg znanych typów z BCL, które są gwarantownie niemodyfikowalne i zawierają wyłącznie reguły <pure>. Uwzględnia także atrybut PureAttribute, którym możesz zaznaczyć dowolną metodę. Dodałem też parę heurystyk: wszystkie typy o prefiksie Immutable uważa się za niemodyfikowalne, wszystkie metody o prefiksie With, który zwraca typ pierwszego argumentu oraz wszystkie metody rozszerzające typy niemodyfikowalne są pure. Z tą regułą zdarza się błąd false-positive, ale nie jest ich wiele, a korzyści są wystarczające.
Niestety, na razie nie wiadomo, w jaki sposób wykorzystać istniejące adnotacje "czystości" z biblioteki Code Contracts i trzeba się zastanowić, jak zrobić rozwiązanie bardziej rozszerzalnym. Chociaż nawet teraz reguła wynalazła kilkanaście mniejszych błędów w kodzie mojego projektu (wszystkie były w testach i kodzie wtórnym, a jednak).
Tworzenie obiektów bez zapisywania wartości
Szczególnym przypadkiem poprzedniej reguły jest reguła, która szuka wywołań konstruktorów obiektów, nie używając wyniku typu new SomeObject();
Niestety, nie można wydawać ostrzeżeń dla każdego niezależnego wywołania new, ponieważ ludzie często robią straszne rzeczy w postaci jakichś operacji z efektami ubocznymi w konstruktorach. Chociaż w niektórych przypadkach można z pewnością powiedzieć, że konstruktor nie ma żadnych efektów ubocznych. Dotyczy to wywołań konstruktorów typów wartości domyślnych, kolekcji, obiektów prymitywnych oraz typów niemodyfikowalnych.

Istnieje szczególny przypadek tej reguły, który generuje błąd podczas tworzenia obiektu wyjątku:

Formatowanie łańcuchów
Jeszcze jeden popularny rodzaj błędów stanowią niewłaściwe argumenty podczas wywołania string.Format i podobnych metod. Tak, częstotliwość użycia string.Format znacznie spadła po ukazaniu się string interpolation w C# 6.0, ale jest sporo przestarzałego kodu, a łańcuchy formatu są często używane w innych miejscach.
ErrorProne.NET zawiera trzy reguły:
(Owszem, drugi problem nie występuje podczas wykonania i nie powoduje generacji wyjątku FormatException, jednak, IMHO, to może tylko ukryć błędy i może występować w praktyce nawet częściej niż pozostałe opcje. Właśnie ta reguła pomogła w znalezieniu jawnego buga w kodzie Roslyn. Oto ticket dla zainteresowanych).
Argumenty nieznane:

Argumnety zbędne:

Nieprawidłowy łańcuch formatu:

Istnieje także odrębna reguła weryfikująca wzorzec wyrażenia regularnego:

Zwróć uwagę, że reguła uwzględnia atrybut JetBrains.Annotations.StringFormatMethodAttribute, który możesz otrzymać poprzez NuGet albo po prostu stworzyć taki atrybut w swoim własnym kodzie (używany jest taki sobie duck typing).
-----------------
(*) Tak, wiem, że istnieje ReSharper Command Line Tools.
Wnioski
Zdaję sobie sprawę, że na rynku jest sporo podobnych narzędzi; trudno konkurować z R# czy analizatorami od PVS-Studio i nie miałem takiego celu. Po prostu zadanie jest bardzo ciekawe i chciałem zebrać w jednym miejscu cenne analizatory, które będą przydatne tu i teraz w moich własnych projektach oraz w projektach moich kolegów.
Tutaj znalazła się tylko część reguł wspieranych przez ErrorProne.NET, a więc ciąg dalszy nastąpi w drugiej części;)
Linki
Sergey Teplyakov
Specjalista w dziedzinach .Net, C++ i Architektury aplikacji
Tak więc, bazując na ideach z R# i z podobnej biblioteki dla Java od Google'a pod nazwą Error Prone, zabrałem się do roboty. Poniżej przedstawiam pierwszą część rezultatów mojej pracy.
Wywołanie metod pure
Brak <obserwacji> wyników wywołania metody pure jest jednym z najczęstszych błędów zachodzących w trakcie testów lokalnych. Problem polega na tym, że po prostu czytając kod, trudno z wyprzedzeniem powiedzieć czy wywołanie someCollection.Union(anotherCollection) jest <pure> i zwraca nową kolekcję czy zmienia oryginalną.
Oto przykłady działania tej reguły:

Ta reguła uwzględnia szereg znanych typów z BCL, które są gwarantownie niemodyfikowalne i zawierają wyłącznie reguły <pure>. Uwzględnia także atrybut PureAttribute, którym możesz zaznaczyć dowolną metodę. Dodałem też parę heurystyk: wszystkie typy o prefiksie Immutable uważa się za niemodyfikowalne, wszystkie metody o prefiksie With, który zwraca typ pierwszego argumentu oraz wszystkie metody rozszerzające typy niemodyfikowalne są pure. Z tą regułą zdarza się błąd false-positive, ale nie jest ich wiele, a korzyści są wystarczające.
Niestety, na razie nie wiadomo, w jaki sposób wykorzystać istniejące adnotacje "czystości" z biblioteki Code Contracts i trzeba się zastanowić, jak zrobić rozwiązanie bardziej rozszerzalnym. Chociaż nawet teraz reguła wynalazła kilkanaście mniejszych błędów w kodzie mojego projektu (wszystkie były w testach i kodzie wtórnym, a jednak).
Tworzenie obiektów bez zapisywania wartości
Szczególnym przypadkiem poprzedniej reguły jest reguła, która szuka wywołań konstruktorów obiektów, nie używając wyniku typu new SomeObject();
Niestety, nie można wydawać ostrzeżeń dla każdego niezależnego wywołania new, ponieważ ludzie często robią straszne rzeczy w postaci jakichś operacji z efektami ubocznymi w konstruktorach. Chociaż w niektórych przypadkach można z pewnością powiedzieć, że konstruktor nie ma żadnych efektów ubocznych. Dotyczy to wywołań konstruktorów typów wartości domyślnych, kolekcji, obiektów prymitywnych oraz typów niemodyfikowalnych.

Istnieje szczególny przypadek tej reguły, który generuje błąd podczas tworzenia obiektu wyjątku:

Formatowanie łańcuchów
Jeszcze jeden popularny rodzaj błędów stanowią niewłaściwe argumenty podczas wywołania string.Format i podobnych metod. Tak, częstotliwość użycia string.Format znacznie spadła po ukazaniu się string interpolation w C# 6.0, ale jest sporo przestarzałego kodu, a łańcuchy formatu są często używane w innych miejscach.
ErrorProne.NET zawiera trzy reguły:
- Argumenty nieznane w łańcuchu formatu
- Argumenty zbędne, których nie używa się w łańcuchu formatu
- Nieprawidłowy łańcuch formatu
(Owszem, drugi problem nie występuje podczas wykonania i nie powoduje generacji wyjątku FormatException, jednak, IMHO, to może tylko ukryć błędy i może występować w praktyce nawet częściej niż pozostałe opcje. Właśnie ta reguła pomogła w znalezieniu jawnego buga w kodzie Roslyn. Oto ticket dla zainteresowanych).
Argumenty nieznane:

Argumnety zbędne:

Nieprawidłowy łańcuch formatu:

Istnieje także odrębna reguła weryfikująca wzorzec wyrażenia regularnego:

Zwróć uwagę, że reguła uwzględnia atrybut JetBrains.Annotations.StringFormatMethodAttribute, który możesz otrzymać poprzez NuGet albo po prostu stworzyć taki atrybut w swoim własnym kodzie (używany jest taki sobie duck typing).
-----------------
(*) Tak, wiem, że istnieje ReSharper Command Line Tools.
Wnioski
Zdaję sobie sprawę, że na rynku jest sporo podobnych narzędzi; trudno konkurować z R# czy analizatorami od PVS-Studio i nie miałem takiego celu. Po prostu zadanie jest bardzo ciekawe i chciałem zebrać w jednym miejscu cenne analizatory, które będą przydatne tu i teraz w moich własnych projektach oraz w projektach moich kolegów.
Tutaj znalazła się tylko część reguł wspieranych przez ErrorProne.NET, a więc ciąg dalszy nastąpi w drugiej części;)
Linki
- ErrorProne.NET on GitHub
- SolutionDiagnosticRunner on GitHub - używam go do uruchamiania analizatorów z wiersza poleceń do analizy różnych projektów, w tym projektu Roslyn
- ErrorProne.NET on Visual Studio Gallery
- ErrorProne.NET on NuGet.org
Sergey Teplyakov
Specjalista w dziedzinach .Net, C++ i Architektury aplikacji