Accord.Net: шукаємо помилку в коді, з-за якої машини поработят людство


Статті про перевірку проектів з відкритим вихідним кодом — річ корисна. Хтось, в тому числі і розробники, дізнається про помилки, що містяться у проекті, хто дізнається про методологію статичного аналізу і почне застосовувати її для підвищення якості свого коду. Для нас же це прекрасний спосіб популяризації аналізатора PVS-Studio, а заразом можливість його додаткового тестування. На цей раз я перевірив платформу Accord.Net і знайшов у коді багато цікавих фрагментів.


Про проект і аналізаторі
Accord.Net — платформа машинного навчання на базі .Net, написана на мові C#. Платформа складається з декількох бібліотек, що охоплюють широкий діапазон завдань, таких як статична обробка даних, машинне навчання, розпізнавання образів тощо Вихідний код проекту доступний репозиторії на GitHub.



Для перевірки проекту використовувався статичний аналізатор коду PVS-Studio, доступний для завантаження посилання. Крім того, ви можете ознайомитися з іншими статтями про перевірці відкритих проектів або з "базою помилок", в якій ми збираємо знайдені нами баги.

Трохи про попередження
Аналізатор видав 91 попередження першого рівня і 141 попередження другого рівня достовірності. 109 з цих попереджень були описані або згадані в статті. Побіжно переглянувши інші попередження, ще 23 я б відніс до помилок, але вони не наведені в статті, так як здалися недостатньо цікавими або були схожі з уже описаними. Про решту попередження аналізатора судити вже складніше — потрібно ретельніше розбирати і аналізувати код. У підсумку, мінімум 132 232 попереджень я б відніс до помилок. Це говорить про те, що число помилкових спрацьовувань аналізатора становить приблизно 46%. А, ні, стривайте… Це говорить нам про те, що кожне друге попередження аналізатора — помилка в коді! По-моєму, це досить вагомий аргумент необхідності використання інструментів статичного аналізу. Про те, як правильно і неправильно використовувати статичні аналізатори, написано в кінці статті. Поки пропоную подивитися, що ж цікавого вдалося виявити у вихідному коді.

Знайдені помилки
Однакові подвираженія

Допустити помилку, отлавливаемую діагностичним повідомленням V3001 досить легко, особливо якщо використовувати copy-paste, або якщо назви змінних, використовуваних у виразі, схожі. Ці помилки — одні з найпоширеніших, і в цьому проекті без них не обійшлося. Спробуйте знайти помилку у фрагменті нижче, не підглядаючи в попередження аналізатора.
public Blob[] GetObjects(UnmanagedImage image, 
bool extractInOriginalSize)
{
....
if ((image.PixelFormat != PixelFormat.Format24bppRgb) &&
(image.PixelFormat != PixelFormat.Format8bppIndexed) &&
(image.PixelFormat != PixelFormat.Format32bppRgb) &&
(image.PixelFormat != PixelFormat.Format32bppArgb) &&
(image.PixelFormat != PixelFormat.Format32bppRgb) &&
(image.PixelFormat != PixelFormat.Format32bppPArgb)
)
....
}

Попередження PVS-Studio: V3001 There are identical sub-expressions 'image.PixelFormat != PixelFormat.Format32bppRgb' to the left and to the right of the '&&' operator. Accord.Imaging BlobCounterBase.cs 670

Тут два рази повторюється підвираз image.PixelFormat != PixelFormat.Format32bppRgb. З такими назвами елементів перерахування помилитися було досить просто, що і сталося. Варто відзначити, що, переглядаючи такий код, дуже легко пропустити зайве підвираз. А от є одне з порівнянь зайвим, чи малося на увазі інше значення перерахування — питання цікаве. В одному випадку буде просто надлишковий код, а в іншому — логічна помилка.

Цей код зустрівся ще раз в цьому ж файлі у рядку 833. Copy-paste… Copy-paste ніколи не змінюється.

Помилкове умова виходу з циклу

Всі ми звикли називати лічильники циклів іменами на зразок i, j, k і т. п. Як правило, це зручно і є повсякденною практикою, але іноді може встати боком. Як в прикладі нижче.
public static void Convert(float[][] from, short[][] to)
{
for (int i = 0; i < from.Length; i++)
for (int j = 0; i < from[0].Length; j++)
to[i][j] = (short)(from[i][j] * (32767f));
}

Попередження PVS-Studio: V3015 It is likely that a wrong variable is being compared inside the 'for' operator. Consider reviewing 'i' Accord.Audio SampleConverter.cs 611

В умові виходу з другого циклу використовується змінна i, хоча лічильником цього циклу виступає j. При цьому змінна i всередині тіла вкладеного циклу не змінюється. У підсумку j буде инкрементироваться до тих пір, поки не вийде за межі масиву, і не буде згенеровано виняток.

Різна логіка для однакових умов

Зустрівся код, коли для двох однакових операторів if була передбачена різна логіка.
public void Fit(double[][] observations, 
double[] weights, 
MultivariateEmpiricalOptions options)
{
if (weights != null)
throw new ArgumentException("This distribution does not support 
weighted samples.", "weights");
....
if (weights != null)
weights = оперативне ? weights : (double[])weights.Clone();
....
}

Попередження PVS-Studio: V3021 There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless Accord.Statistics MultivariateEmpiricalDistribution.cs 653

Дивний код, правда? Особливо якщо врахувати той факт, що змінна weights є параметром методу і ніяк не використовується між цими умовами. Таким чином, другий оператор if ніколи не буде виконано, так як якщо вираз weights != null істинно, буде згенеровано виняток типу ArgumentException.

Цей код зустрівся ще раз в цьому ж файлі у рядку 687.

Умови, значення яких завжди помилкові

Діагностичне правило V3022 дуже покращало з першого релізу і не перестає мене дивувати. Давайте подивимося, чи зможе воно здивувати і вас. Для початку пропоную знайти помилку в коді, не вдаючись до допомоги діагностичного повідомлення аналізатора. При цьому звертаю увагу на те, що це вже спрощений варіант, з вирізаними фрагментами коду.
private static void dscal(int n, double da, double[] dx, 
int _dx_offset, int incx)
{
....
if (((n < = 0) || (incx <= 0)))
{
return;
}
....
int _i_inc = incx;
for (i = 1; (_i_inc < 0) ? i >= nincx : i <= nincx; i += _i_inc)
....
}

Попередження PVS-Studio: V3022 Expression '(_i_inc < 0)' is always false. Accord.Math BoundedBroydenFletcherGoldfarbShanno.FORTRAN.cs 5222

Безумовно, зараз легше знайти помилку, так як методу спеціально були вирізані фрагменти, не цікаві нам. І все ж одразу важко сказати, де ж у цьому коді проблемне місце. А справа в тому (як ви могли здогадатися, прочитавши повідомлення аналізатора), що вираз (_i_inc < 0) завжди помилково. Тут слід звернути увагу на те, що мінлива _i_inc ініціалізується значенням змінної incx. У той же час значення змінної incx на момент ініціалізації _i_inc позитивно, так як інакше був би здійснений вихід з тіла методу вище за кодом. Отже, _i_inc може мати позитивне значення, результат порівняння _i_inc < 0 завжди має значення false, і умовою виходу з циклу завжди буде вираз i <= nincx.

Такий поглиблений аналіз став доступний завдяки механізму віртуальних значень, який сильно покращив деякі діагностичні правила аналізатора.
private void hqr2()
{
....
int low = 0;
....
for (int i = 0; i < nn; i++)
{
if (i < low | i > high)
....
}
....
}

Попередження PVS-Studio: V3063 A part of conditional expression is always false: i < low. Accord.Math JaggedEigenvalueDecompositionF.cs 571

Підвираз i < low завжди буде мати значення false, так як мінімальне значення, що приймається змінної i 0, а значення low на момент порівняння також завжди буде дорівнює 0. Отже, підвираз i < low обчислюватиметься «вхолосту».

Таких місць зустрілося багато. Всі попередження приводити не буду, але кілька перерахую:
  • V3063 A part of conditional expression is always false: i < low. Accord.Math JaggedEigenvalueDecompositionF.cs 972
  • V3063 A part of conditional expression is always false: i < low. Accord.Math JaggedEigenvalueDecomposition.cs 571
  • V3063 A part of conditional expression is always false: i < low. Accord.Math JaggedEigenvalueDecomposition.cs 972
  • V3063 A part of conditional expression is always false: i < low. Accord.Math EigenvalueDecomposition.cs 567
Цілочисельне ділення з приведенням до речового типу

Аналізатор знайшов у коді підозрілі математичні обчислення. Досить просто забути про те, що при поділі цілих виконується цілочисельне ділення. Якщо малося на увазі речовий поділ, може виникнути неприємна і помітна помилка. У деяких випадках сторонньому розробнику буває складно сказати, чи міститься в такому вираженні помилка, але перевірити подібні операції варто. Пропоную розглянути декілька подібних випадків.
public static double GetSpectralResolution(int samplingRate, 
int samples)
{
return samplingRate / samples;
}

Попередження PVS-Studio: V3041 The expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. Accord.Audio Tools.cs 158

Даний метод виконує ділення двох цілочисельних змінних, однак результат цього поділу неявно приводиться до типу double. Виглядає підозріло.



А ось наступний код виглядає ще більш дивно:
public static int GreatestCommonDivisor(int a, int b)
{
int x = a - b * (int)Math.Floor((double)(a / b));
while (x != 0)
{
a = b;
b = x;
x = a - b * (int)Math.Floor((double)(a / b));
}
return b; 
}

Попередження PVS-Studio:
  • V3041 The expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. Accord.Math Tools.cs 137
  • V3041 The expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. Accord.Math Tools.cs 142
Аналізатору здалося підозрілим вираз (double)(a / b). Метод Floor повертає найбільше ціле число, менше або дорівнює заданому числу подвійної точності із плаваючою комою (MSDN. Math.Floor). Але змінні a b мають тип int, отже — буде виконано цілочисельне ділення, результатом якого буде цілочисельне значення. Виходить, що немає ніякого сенсу в явному приведення цього значення до типу double і викликом методу Floor.

Для коректного виконання операції було потрібно привести до типу double один з операндів. Тоді було б виконано десяткове ділення, і виклик методу Floor був би виправданий:
Math.Floor((double)a / b)

Параметр методу, значення якого завжди перезаписується

Продовжимо. Помилки такого типу зустрічаються не часто, але все ж трапляються.
private static double WeightedMode(double[] observations, 
double[] weights, 
double mode, 
int imax, 
int imin)
{
....
var bestValue = currentValue;
....
mode = bestValue;
return mode;
}

Попередження PVS-Studio: V3061 Parameter 'mode' is always rewritten method in body before being used. Accord.Statistics TriangularDistribution.cs 646

Один з параметрів методу — mode — перезаписується і повертається. При цьому в тілі методу mode ніяк не використовується (не рахуючи перезапису). Не візьмуся стверджувати, що це помилка (серед підозрілих місць, знайдених цим діагностичним правилом в інших проектах, явно було видно помилкові), але виглядає цей код дивно.

Взагалі є цікава особливість: як правило, помилки в цьому проекті не зустрічаються поодинці. Точно такий же код можна знайти в декількох місцях. Дійсно, copy-paste не змінюється...
  • V3061 Parameter 'mode' is always rewritten method in body before being used. Accord.Statistics TriangularDistribution.cs 678
  • V3061 Parameter 'mode' is always rewritten method in body before being used. Accord.Statistics TriangularDistribution.cs 706
  • V3061 Parameter 'mode' is always rewritten method in body before being used. Accord.Statistics TriangularDistribution.cs 735
Разыменовывание нульовою посилання
public override string ToString(format string, 
IFormatProvider formatProvider)
{
....
var fmt = components[i] as IFormattable;
if (fmt != null)
sb.AppendFormat(fmt.ToString(format, formatProvider));
else
sb.AppendFormat(fmt.ToString());
....
}

Попередження PVS-Studio: V3080 Possible null dereference. Consider inspecting 'fmt'. Accord.Statistics MultivariateMixture'1.cs 697

Зазвичай помилки, що призводять до виникнення винятків, відловлюються в процесі розробки, якщо код тестується досить ретельно. Але не завжди, і приклад вище — тому доказ. У випадку, якщо вираз fmt != null помилково, об'єкт fmt викликається экземплярный метод ToString. Результат? Виняток типу NullReferenceException.

І, як ви вже могли здогадатися, така ж помилка трапилася ще раз: MultivariateMixture'1.cs 697

Взаємне присвоєння посилань
public class MetropolisHasting<T> : IRandomNumberGenerator<T[]>
{
.... 
T[] current;
T[] next;
....
public bool TryGenerate()
{
....
var aux = current;
current = next;
next = current;
....
}
....
}

Попередження PVS-Studio: V3037 An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 290, 289. Accord.Statistics MetropolisHasting.cs 290

Очевидно, що у наведеному фрагменті методу TryGenerate хотіли поміняти місцями посилання на масиви next current (змінна aux більше ніде не використовується). Але з-за помилки вийшло, що і current, next тепер зберігають посилання на один і той же масив — той, посилання на який містилася у змінній next.

Правильний код повинен виглядати так:
var aux = current;
current = next;
next = aux;

Потенційне ділення на 0

При аналізі виявилося декілька помилок потенційного ділення на 0. Мигцем поглянемо на них:
public BlackmanWindow(double alpha, int length) 
: base(length)
{
double a0 = (1.0 - alpha) / 2.0;
double a1 = 0.5;
double a2 = alpha / 2.0;

for (int i = 0; i < length; i++)
this[i] = (float)(a0 - 
a1 * Math.Cos((2.0 * System.Math.PI * i) / (length - 1)) +
a2 * Math.Cos((4.0 * System.Math.PI * i) / (length - 1)));
}

Попередження PVS-Studio:
  • V3064 Potential division by zero. Consider inspecting denominator '(length — 1)'. Accord.Audio BlackmanWindow.cs 64
  • V3064 Potential division by zero. Consider inspecting denominator '(length — 1)'. Accord.Audio BlackmanWindow.cs 65
Аналізатор визнав підозрілим наступний код:
(2.0 * System.Math.PI * i) / (length - 1)

Можливо, що в реальності помилки тут і не буде (length — довжина якогось вікна, і для виникнення помилки вона повинна мати значення 1), але як знати? Краще перестрахуватися, інакше великий шанс отримати дуже неприємну помилку, яку до того ж буде важко виявити.

Зустрівся ще один цікавий фрагмент коду з потенційним діленням на 0.
public static double[,] Centering(int size)
{
if (size < 0)
{
throw new ArgumentOutOfRangeException("size", size,
"The size of the centering matrix must 
be a positive integer.");
}

double[,] C = Matrix.Square(size, -1.0 / size);

....
}

Попередження PVS-Studio: V3064 Potential division by zero. Consider inspecting denominator 'size'. Accord.Matrix Math.Construction.cs 794

Як на мене — дуже цікава і забавна помилка. Аналізатор зазначив, що у виразі -1.0 / size можливе ділення на 0. А тепер зверніть увагу на перевірку вище. Якщо size < 0, буде згенеровано виняток, однак якщо size == 0, винятку не буде, а ось ділення на 0 — буде. При цьому в литерале, що передається конструктору винятку, написано, що розмір матриці повинен бути позитивним числом, але перевірка при цьому здійснюється на невід'ємні значення. А позитивне невід'ємне — все-таки різні поняття. Вважаю, для виправлення помилки досить просто підправити перевірку:
if (size <= 0)

Використання бітового оператора замість логічного

Деколи доводиться стикатися з тим, що не всі програмісти знають різницю між растровими і логічними операторами ('|' і '||', '&' і '&&'). Це може призводити до різних наслідків — від зайвих обчислень до падінь. В даному проекті зустрілося кілька підозрілих місць з використанням бітових операцій:
public JaggedSingularValueDecompositionf(
Single[][] value,
bool computeLeftSingularVectors, 
bool computeRightSingularVectors, 
bool autoTranspose, 
bool оперативне)
{
....
if ((k < nct) & (s[k] != 0.0))
....
}

Попередження PVS-Studio: V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecompositionf.cs 461

Тіло оператора if буде виконано, якщо обидва подвираженія (k < nct s[k] != 0.0) мають значення true. Але при цьому, навіть якщо значення першого подвираженія (k < nct) має значення false, друге він все одно буде обчислюватися, чого не було б при використанні оператора &&. Отже, якщо програміст перевіряв значення k, щоб не вийти за кордон масиву, то у нього нічого з цього не вийшло.

Схожі попередження:
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecompositionf.cs 510
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecompositionf.cs 595
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecomposition.cs 461
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecomposition.cs 510
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math JaggedSingularValueDecomposition.cs 595
  • V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. Accord.Math Gamma.cs 296
Перевірка в циклі одного і того ж елемента

Зустрілася помилка, знайдена діагностичним правилом з останнього релізу аналізатора.
public override int[] Compute(double[][] data, double[] weights)
{
....
int cols = data[0].Length;
for (int i = 0; i < data.Length; i++)
if (data[0].Length != cols)
throw new DimensionMismatchException("data", 
"The points matrix should be rectangular. 
The vector at position {} has a different
length than previous ones.");
....
}

Попередження PVS-Studio: V3102 Suspicious access to element of 'data' object by a constant index inside a loop. Accord.MachineLearning BinarySplit.cs 121

Цікава помилка. Програміст хотів перевірити, що зубчастий масив data насправді є двовимірним (тобто є матрицею). Але помилився у вираженні data[0].Length != cols, використавши в якості індексу не лічильник циклу i, а цілочисельний літерал — 0. У результаті вираз data[0].Length != cols завжди буде помилково, так як воно еквівалентно виразу data[0].Length != data[0].Length. Якби параметр data двовимірним масивом (Double[,]), цієї помилки, як і всієї перевірки, можна було б уникнути. Але можливо, що використання зубчастого масиву обумовлено якимись особливостями архітектури програми.

Передача методу в якості аргументу викликає об'єкта

Наступний фрагмент коду також виглядає підозріло.
public static double WeightedMean(this double[] values, 
double[] weights)
{
....
}

public override void Fit(double[] observations, 
double[] weights, 
IFittingOptions options)
{
....
mean = observations.WeightedMean(observations);
....
}

Попередження PVS-Studio: V3062 An object 'observations' is used as an argument to its own method. Consider checking the first actual argument of the 'WeightedMean' method. Accord.Statistics InverseGaussianDistribution.cs 325

Аналізатор визнав підозрілим факт, що метод WeightedMean в якості аргументу приймає той же об'єкт, у якого він викликається. Це стає подвійно дивним, якщо врахувати, що WeightedMean — метод-розширення. Я провів додаткове дослідження, подивившись, як використовується цей метод в інших місцях. У всіх місцях його використання в якості другого аргументу виступав масив weights (зверніть увагу, що такий масив є і в розглянутому методі Fit), так що скоріш за все це помилка і правильний код повинен виглядати так:
mean = observations.WeightedMean(weights);

Потенційна помилка серіалізації

Виявилася потенційна проблема серіалізації одного з класів.
public class DenavitHartenbergNodeCollection : 
Collection<DenavitHartenbergNode>
{ .... }

[Serializable]
public class DenavitHartenbergNode
{
....
public DenavitHartenbergNodeCollection Children 
{ 
get; 
private set; 
}
....
}

Попередження PVS-Studio: V3097 Possible exception: the 'DenavitHartenbergNode' type marked by [Serializable] contains non-serializable members not marked by [NonSerialized]. Accord.Math DenavitHartenbergNode.cs 77

При серіалізації екземпляра класу DenavitHartenbergNode можливо виникнення виключення типу SerializationException. Все залежить від вибраного типу сериализатора. Якщо в якості сериализатора виступає, наприклад, екземпляр типу BinaryFormatter, буде згенеровано виняток, так як потрібно, щоб всі сериализуемые члени (а це властивість теж сериализуемо) були декоровані атрибутом [Serializable].

Деякі способи вирішення:
  • реалізувати це властивість через поле, декороване атрибутом [NonSerialized]. Тоді полі (а отже — і асоційоване з ним властивість) не будуть серіалізовать;
  • реалізувати інтерфейс ISerializable, і в методі GetObjecData проігнорувати серіалізацію цього властивості;
  • декорувати тип DenavitHartenbergNodeCollection атрибутом [Serializable].
Судячи по навколишньому кодом (всі інші властивості мають сериализуемые типи), необхідно реалізувати останній випадок.

Якщо ж екземпляри даного типу сериализуются з використанням сериализаторов, не вимагають, щоб всі сериализуемые члени були декоровані атрибутом [Serializable], можна не хвилюватися на рахунок цього коду.

Аналізатором було виявлено дуже багато небезпечних фрагментів викликів подій. Наскільки багато? 75 попереджень V3083! Пропоную розглянути один фрагмент коду, так як інші, в принципі, схожі з ним.
private void timeUp_Elapsed(object sender, ElapsedEventArgs e)
{
....
if (TempoDetected != null)
TempoDetected(this, EventArgs.Empty);
}

Попередження PVS-Studio: V3083 Unsafe invocation of event 'TempoDetected', NullReferenceException is possible. Consider assigning event to a local variable before invoking it. Accord.Audio Metronome.cs 223

У даному фрагменті коду перевіряється, чи є передплатники у події TempoDetected, і, якщо вони є, викликається подія. Передбачалося, що, якщо на момент перевірки у TempoDetected не буде передплатників, це дозволить уникнути виникнення виключення. Однак є ймовірність, що в момент між перевіркою TempoDetected на нерівність null і викликом події, у нього не залишиться передплатників (наприклад, в інших потоках відбудеться відписка від цієї події). У результаті, якщо на момент виклику події у TempoDetected не буде передплатників, буде згенеровано виняток типу NullReferenceException. Уникнути подібних проблем можна, наприклад, використовую null-умовний оператор '?.', доданий в C# 6.0. Більш докладно прочитати про проблеми і інших способах її рішення можна в документації діагностичного правилом.

Як потрібно і як не потрібно використовувати статичний аналізатор
Перед тим, як закінчити, я б хотів трохи розповісти про те, як же все-таки необхідно використовувати інструменти статичного аналізу. Часто доводиться стикатися з таким підходом: «Ми перевірили перед релізом наш проект. Що-то нічого особливо цікавого не знайшлося.» Ні, ні, ні! Це самий неправильний спосіб використання. В якості паралелі можна навести такий приклад — відмовтеся від розробки програм в IDE, пишіть все в простому блокноті. І перед самим релізом починайте працювати в IDE. Дивно? Ще б! Толку від цієї IDE, якщо більшу частину часу, коли від неї був би толк, вона припадала пилом у вас на SSD/HDD? Така ж ситуація і зі статичними аналізаторами. Застосовувати їх потрібно регулярно, а не одноразово.



Якщо ви перевіряєте аналізатором коду програми тільки перед релізом, очевидно, що багато помилок будуть вже виправлені. Але якою ціною? Ціною нервів і часу розробників, які писали код, ціною численних тестів, покликаних виявляти ці самі помилки. В результаті вартість таких помилок стає, м'яко кажучи, чималою.

В той же час всього цього можна було б уникнути, якщо правильно впровадити аналізатор в процес розробки. Встановивши аналізатор локально на машину кожному розробнику, можна домогтися того, щоб більшість помилок, що фіксуються аналізатором, виявлялися і виправлялися ще до моменту потрапляння в репозиторій. Більш того, виявлена і виправлена помилка, яка ще не встигла обрости різними залежностями, буде коштувати набагато дешевше. Ще більше посприяти виявленню помилок допоможе режим інкрементального аналізу, використання якого дозволяє виявити помилку відразу після її появи.

Корисним кроком буде впровадження статичного аналізу і процес нічних збірок. Це також дозволить досить швидко виявляти помилки, а також дозволить дізнатися, хто допустив їх потрапляння у сховище. Для розробників це також буде додатковим стимулом до більш акуратного написання коду.

Таким чином, саме регулярне використання інструментів статичного аналізу дозволить отримати від них найбільшу користь.

Підсумки
В черговий раз вдалося перевірити цікавий проект, знайти не менш цікаві помилки і розповісти про них. Можливо, хтось візьме на замітку, дізнається щось нове або просто буде акуратніше при написанні коду. Тим не менше, всі ми люди, а людині властиво помилятися. Допомогти виправити помилки, допущені в програмному коді, допоможе PVS-Studio. Регулярне застосування статичного аналізатора дозволить зменшити головний біль, пов'язану з пошуком помилок і їх виправлення.



Якщо хочете поділитися цією статтею з англомовної аудиторією, то прошу використовувати посилання на переклад: Sergey Vasiliev. Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind.

Прочитали статтю і є питання?Часто до наших статей задають одні і ті ж питання. Відповіді на них ми зібрали тут: Відповіді на питання читачів статей про PVS-Studio, версія 2015. Будь ласка, ознайомтеся зі списком.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.