﻿# Проверка кода XMage и почему недоступны специальные редкие карточки для коллекции Dragon's Maze

XMage \- клиент\-серверное приложение для игры в Magic: The Gathering \(MTG\)\. XMage начал развиваться еще в начале 2010 года\. За это время было выпущено 182 релиза, набралась целая армия контрибьюторов, и проект до сих пор активно развивается\. Отличный повод поучаствовать и нам в его развитии\! Поэтому сегодня единорог из PVS\-Studio проверит кодовую базу XMage, и кто знает, может и схлестнется с кем\-нибудь в бою\.

![0758_XMage_ru/image1.png](https://import.viva64.com/docx/blog/0758_XMage_ru/image1.png)

## Вкратце о проекте

[XMage](https://github.com/magefree/mage) \- активно развивающееся приложение на протяжении уже 10 лет\. Его цель \- сделать бесплатную, с открытым исходным кодом, онлайновую версию оригинальной карточной игры [Magic: the Gathering](https://magic.wizards.com/en)\.

Возможности приложения:

* доступ к \~19 000 уникальных карт, выпущенных за 20\-летнюю историю MTG;
* автоматический контроль и применение всех существующих правил игры;
* многопользовательский режим с поиском игроков на общем сервере;
* одиночный режим с игрой против компьютера \(AI\);
* десятки форматов и режимов игры \(Standard, Modern, Vintage, Commander и многое другое\);
* возможность проведения как одиночных матчей, так и турниров\.

## Небольшое отступление

Случайно наткнулся на [работу](https://delftswa.gitbooks.io/desosa2018/content/xmage/chapter.html) студентов из Делфтского технического университета 2018 года \(магистерский курс [Software Architecture](https://se.ewi.tudelft.nl/delftswa/)\)\. Она заключалась в том, что ребята принимали активное участие в open\-source проектах, которые должны были быть достаточно сложными и активно развиваться\. В течение восьминедельного периода студенты изучали курс и open\-source проекты, чтобы понять и описать архитектуру выбранного программного обеспечения\.

Так вот\. В этой работе ребята анализировали проект XMage, и одним из аспектов их работы было получение различных метрик при помощи SonarQube \(количество строк кода, цикломатическая сложность, дублирование кода, запахи кода, ошибки, уязвимости и т\.д\.\)\.

Моё внимание привлекло то, что на момент 2018 года сканирование SonarQube'ом показало 700 дефектов \(bugs, vulnerabilities\) на 1 000 000 строк кода\.

Покопавшись в истории ребят\-контрибьюторов, я выявил, что из полученного отчета с предупреждениями они сделали pull\-request на исправление примерно 30 дефектов из категории "Blocker" или "Critical"\. Что с остальными предупреждениями \- неизвестно, но надеюсь на то, что их не пропустили мимо глаз\.

С тех пор прошло уже 2 года и кодовая база подросла примерно на 250 000 строк кода \- неплохой повод посмотреть, как там дела\.

## Об анализе

Для анализа я взял релиз XMage \-  [1\.4\.44V0](https://github.com/magefree/mage/tree/xmage_1.4.44V0)\.

С проектом очень повезло\. Собрать XMage при помощи Maven получилось очень просто \(как и было написано в документации\):

```cpp
mvn clean install -DskipTests
```

Больше от меня ничего не потребовалось\. Круто же?

С интеграцией плагина PVS\-Studio в Maven тоже не возникло проблем \-все как в [документации](https://pvs-studio.ru/ru/docs/manual/6703/)\. 

После анализа было получено 911 предупреждений, из которых 674 приходится на предупреждения 1 и 2 уровня достоверности\. В рамках данной статьи я не рассматривал предупреждения 3 уровня достоверности, так как там обычно велик процент ложных срабатываний\. Хочу обратить ваше внимание на то, что при использовании статического анализатора в реальном бою игнорировать такие предупреждения нельзя, так как они могут также указывать на значимые дефекты в коде\.

Кроме того, я не рассматривал предупреждения некоторых правил из соображения, что их лучше рассматривать тем, кто хорошо знаком с проектом, нежели мне:

* V6022, которое ищет неиспользуемые параметры в методах/конструкторах\. На их долю пришлось аж 336 срабатываний\.
* V6014, которое предупреждает о том, что все ветки выхода из метода возвращают одно и то же значение\. 73 срабатывания\.
* V6021, которое сигнализирует о том, что в переменную записывается некий результат и про эту переменную забывают\. 36 срабатываний\.
* V6048, которое предупреждает о том, что выражение можно упростить\. 17 срабатываний\.

Плюс к этому несколько диагностических правил выдали примерно 20 явных однотипных ложных срабатываний\. Записали в todo\! 

В итоге, если все вычесть, то к рассмотрению ко мне попало примерно 190 срабатываний\.

При просмотре срабатываний было выявлено много однотипных незначительных дефектов, которые были связаны либо с отладкой, либо с бессмысленной проверкой или операцией\. Также много срабатываний было связано с очень странным фрагментом кода, который так и напрашивался на рефакторинг\.

В итоге, для данной статьи я выделил 11 диагностических правил и разобрал по одному самому интересному срабатыванию\.

Давайте глянем, что вышло\.

**Предупреждение N1**

[V6003](https://pvs-studio.ru/ru/docs/warnings/v6003/) The use of 'if \(card \!\= null\) \{\.\.\.\} else if \(card \!\= null\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. TorrentialGearhulk\.java\(90\), TorrentialGearhulk\.java\(102\)

```cpp
@Override
public boolean apply(Game game, Ability source) {
  ....
  Card card = game.getCard(....);
  if (card != null) {
      ....
  } else if (card != null) {
      ....
  }
  ....
}
```

Тут все просто: тело второго условного оператора _if \(card \!\= null\)_ в конструкции _if\-else\-if_ никогда не выполнится, так как выполнение программы либо не дойдет до этого места, либо _card \!\= null_ будет всегда _false_\.

**Предупреждение N2**

[V6004](https://pvs-studio.ru/ru/docs/warnings/v6004/) The 'then' statement is equivalent to the 'else' statement\. AsThoughEffectImpl\.java\(35\), AsThoughEffectImpl\.java\(37\)

```cpp
@Override
public boolean applies(....) {
  // affectedControllerId = player to check
  if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
    return applies(objectId, source, playerId, game);
  } else {
    return applies(objectId, source, playerId, game);
  }
}
```

Банальная ошибка, которая частенько встречалась на моей практике проверок open\-source проектов\. Copy\-paste? Или я что\-то не понимаю? Предположу, что в ветке _else_ всё\-таки нужно возвращать _false_\.

P\.S\. Если что, тут нет рекурсивного вызова _applies\(\.\.\.\.\)_, так как это разные методы\.

Аналогичное срабатывание:

* V6004 The 'then' statement is equivalent to the 'else' statement\. GuiDisplayUtil\.java\(194\), GuiDisplayUtil\.java\(198\)

**Предупреждение N3**

[V6007](https://pvs-studio.ru/ru/docs/warnings/v6007/) Expression 'filter\.getMessage\(\)\.toLowerCase\(Locale\.ENGLISH\)\.startsWith\("Each "\)' is always false\. SetPowerToughnessAllEffect\.java\(107\)

```cpp
@Override
public String getText(Mode mode) {
  StringBuilder sb = new StringBuilder();
  ....
  if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("Each ")) {
    sb.append(" has base power and toughness ");
  } else {
    sb.append(" have base power and toughness ");
  }
  ....
  return sb.toString();
}
```

Срабатывания диагностического правила [V6007](https://pvs-studio.ru/ru/docs/warnings/v6007/) достаточно популярны для каждого проверяемого проекта\. XMage не исключение \(79 штук\)\. Срабатывания правила, в принципе, все по делу, но много случаев приходится то на debug, то на перестраховывание, то еще на что\. В общем, такие срабатывания лучше смотреть автору кода, нежели мне\. 

Данное срабатывание, однако, точно является ошибкой\. В зависимости от начала строки _filter\.getMessage\(\)_ к _sb_ добавляется текст " has \.\.\.", либо " have \.\.\."\. Но ошибочка в том, что разработчики проверяют, чтобы строка начиналась с заглавной буквы, преобразовав перед этим эту самую строку в нижний регистр\. Упс\. В результате добавленной строкой всегда будет " have \.\.\."\. Результат дефекта не критический, но тоже неприятный: где\-то будет фигурировать неграмотно составленный текст\.

Срабатывания, которые мне показались наиболее интересными:

* V6007 Expression 't\.startsWith\("\-"\)' is always false\. BoostSourceEffect\.java\(103\)
* V6007 Expression 'setNames\.isEmpty\(\)' is always false\. DownloadPicturesService\.java\(300\)
* V6007 Expression 'existingBucketName \=\= null' is always false\. S3Uploader\.java\(23\)
* V6007 Expression '\!lastRule\.endsWith\("\."\)' is always true\. Effects\.java\(76\)
* V6007 Expression 'subtypesToIgnore::contains' is always false\. VerifyCardDataTest\.java\(893\)
* V6007 Expression 'notStartedTables \=\= 1' is always false\. MageServerImpl\.java\(1330\)

**Предупреждение N4**

[V6008](https://pvs-studio.ru/ru/docs/warnings/v6008/) Null dereference of 'savedSpecialRares'\. DragonsMaze\.java\(230\)

```cpp
public final class DragonsMaze extends ExpansionSet {
  ....
  private List<CardInfo> savedSpecialRares = new ArrayList<>();
  ....
  @Override
  public List<CardInfo> getSpecialRare() {
    if (savedSpecialRares == null) {                    // <=
      CardCriteria criteria = new CardCriteria();
      criteria.setCodes("GTC").name("Breeding Pool");
      savedSpecialRares.addAll(....);                   // <=
      criteria = new CardCriteria();
      criteria.setCodes("GTC").name("Godless Shrine");
      savedSpecialRares.addAll(....);
      ....
    }
    return new ArrayList<>(savedSpecialRares);
  }
}
```

Анализатор ругается на разыменование нулевой ссылки _savedSpecialRares_, когда выполнение дойдет до первого заполнения коллекции\. 

Первое, что приходит на ум: просто перепутали _savedSpecialRares \=\= null_ с _savedSpecialRares \!\= null\. _Но в таком случае NPE может произойти в конструкторе _ArrayList_ при возвращении коллекции из метода, так как _savedSpecialRares \=\= null_ по\-прежнему не исключено\. Исправлять код первым пришедшим в голову решением не очень хороший вариант\. Немного разобравшись с кодом, выяснил, что s_avedSpecialRares_ сразу определяется пустой коллекцией при объявлении и при этом больше нигде не переприсваивается\. Это говорит нам о том, что _savedSpecialRares _никогда_ _не будет _null,_ и разыменование нулевой ссылки, о котором предупреждает анализатор, так и не произойдет, так как до заполнения коллекции дело так и не дойдет\. Как итог, метод всегда будет возвращать пустую коллекцию\.

P\.S\. Для исправления нужно заменить _savedSpecialRares \=\= null_ на _savedSpecialRares\.isEmpty\(\)_\.

P\.P\.S\. Увы, но пока что, играя в XMage, не получится получить специальные редкие карточки для коллекции [Dragon's Maze](https://mtg.gamepedia.com/Dragon%27s_Maze)\.

Еще один случай разыменования нулевой ссылки:

* V6008 Null dereference of 'match'\. TableController\.java\(973\)

**Предупреждение N5**

[V6012](https://pvs-studio.ru/ru/docs/warnings/v6012/) The '?:' operator, regardless of its conditional expression, always returns one and the same value 'table\.getCreateTime\(\)'\. TableManager\.java\(418\), TableManager\.java\(418\)

```cpp
private void checkTableHealthState() {
  ....
  logger.debug(.... + formatter.format(table.getStartTime() == null
                                        ? table.getCreateTime()
                                        : table.getCreateTime()) + ....);
  ....
}
```

Здесь тернарный оператор _?:_ возвращает одно и тоже значение вне зависимости от условия _table\.getStartTime\(\) \=\= null_\. Полагаю, что автодополнение кода сыграло злую шутку с разработчиком\. Вариант исправления:

```cpp
private void checkTableHealthState() {
  ....
  logger.debug(.... + formatter.format(table.getStartTime() == null
                                        ? table.getCreateTime()
                                        : table.getStartTime()) + ....);
  ....
}
```

**Предупреждение N6**

[V6026](https://pvs-studio.ru/ru/docs/warnings/v6026/) This value is already assigned to the 'this\.loseOther' variable\. BecomesCreatureTypeTargetEffect\.java\(54\)

```cpp
public
BecomesCreatureTypeTargetEffect(final BecomesCreatureTypeTargetEffect effect) {
  super(effect);
  this.subtypes.addAll(effect.subtypes);
  this.loseOther = effect.loseOther;
  this.loseOther = effect.loseOther;
}
```

Дублирующая строка присваивания\. Выглядит так, словно разработчик немного увлёкся использованием горячих клавиш и не заметил этого\. Но с учетом того, что _effect_ имеет большое количество полей, фрагмент заслуживает того, чтобы на нем заострили внимание\.  

**Предупреждение N7**

[V6036](https://pvs-studio.ru/ru/docs/warnings/v6036/) The value from the uninitialized 'selectUser' optional is used\. Session\.java\(227\)

```cpp
public String connectUserHandling(String userName, String password)
{
  ....
  if (!selectUser.isPresent()) {  // user already exists
      selectUser = UserManager.instance.getUserByName(userName);
      if (selectUser.isPresent()) {
          User user = selectUser.get();
            ....
      }
  }
  User user = selectUser.get(); // <=
  ....
}
```

По предупреждению анализатора можно сделать вывод, что _selectUser\.get\(\)_ может бросить исключение _NoSuchElementException\._

Давайте рассмотрим более детально что тут происходит\.

Если верить комментарию, что _user_ уже существует, то исключения не возникнет:

```cpp
....
if (!selectUser.isPresent()) {  // user already exists
  ....
}
User user = selectUser.get()
....
```

В таком случае выполнение программы не будет заходить в тело условного оператора\. И все будет в порядке\. Но тогда возникает вопрос: зачем нужен условный оператор с какой\-то сложной логикой, если он так и не выполнится?

А что, если комментарий ничего из себя не представляет? 

```cpp
....
if (!selectUser.isPresent()) {  // user already exists
    selectUser = UserManager.instance.getUserByName(userName);
    if (selectUser.isPresent()) {
      ....
    }
}
User user = selectUser.get(); // <=
....
```

Тогда выполнение заходит в тело условного оператора и заново получает пользователя через _getUserByName\(\)\._ Пользователя снова проверяют на валидность, и это наталкивает на мысль, что _selectUser_ может быть неинициализированным\. Ветки _else_ на этот случай нет, что далее и приведет к _NoSuchElementException_ на рассматриваемой строке кода\. 

**Предупреждение N8**

[V6042](https://pvs-studio.ru/ru/docs/warnings/v6042/) The expression is checked for compatibility with type 'A' but is cast to type 'B'\. CheckBoxList\.java\(586\)

```cpp
/**
 * sets the model - must be an instance of CheckBoxListModel
 * 
 * @param model the model to use
 * @throws IllegalArgumentException if the model is not an instance of
 *           CheckBoxListModel
 * @see CheckBoxListModel
 */
@Override
public void setModel(ListModel model) {
  if (!(model instanceof CheckBoxListModel)) {
    if (model instanceof javax.swing.DefaultListModel) {
       super.setModel((CheckBoxListModel)model);         // <=
    }
    else {
      throw new IllegalArgumentException(
          "Model must be an instance of CheckBoxListModel!");
    }
  }
  else {
    super.setModel(model);
  }
}
```

Автор кода что\-то здесь запутался: сначала убеждается в том, что _model_ не является _CheckBoxListModel_, а потом в итоге явно приводит объект к этому типу\. Из\-за этого метод _setModel_ сразу же выбросит _ClassCastException_, добравшись до этого места\.

Файл _CheckBoxList\.java_ был добавлен 2 года назад, и эта ошибка живет в коде до сих пор\. Тестов на некорректные параметры, видимо, нет, реального использования этого метода c объектами неподходящих типов тоже нет, поэтому и живет\. 

Если вдруг кто\-нибудь завяжется на этот метод и прочтет Javadoc, то будет ожидать _IllegalArgumentException_, а не _ClassCastException_\. Не думаю, что кто\-то намеренно будет нарываться на это исключение, но кто знает\.

Учитывая документацию, код, скорее всего, должен выглядеть следующим образом:

```cpp
public void setModel(ListModel model) {
  if (!(model instanceof CheckBoxListModel)) {
     throw new IllegalArgumentException(
        "Model must be an instance of CheckBoxListModel!");  
  }
  else {
    super.setModel(model);
  }
}
```

**Предупреждение N9**

[V6060](https://pvs-studio.ru/ru/docs/warnings/v6060/) The 'player' reference was utilized before it was verified against null\. VigeanIntuition\.java\(79\), VigeanIntuition\.java\(78\)

```cpp
@Override
public boolean apply(Game game, Ability source) {
    MageObject sourceObject = game.getObject(source.getSourceId());
    Player player = game.getPlayer(source.getControllerId());
    Library library = player.getLibrary();                           // <=
    if (player != null && sourceObject != null && library != null) { // <=
        ....
    }
}
```

[V6060](https://pvs-studio.ru/ru/docs/warnings/v6060/) предупреждает разработчика о том, что происходит обращение к объекту до того, как производится его проверка на _null_\. Срабатывания этого правила частенько встречаются в статьях о проверках open\-source проектов: обычно причиной этого становится неудачный рефакторинг или смена контрактов у методов\. Если обратить внимание на объявление метода _getPlayer\(\)_, то всё сразу станет на свои места:

```cpp
// Result must be checked for null.
// Possible errors search pattern: (\S*) = game.getPlayer.+\n(?!.+\1 != null)
Player getPlayer(UUID playerId);
```

**Предупреждение N10**

[V6072](https://pvs-studio.ru/ru/docs/warnings/v6072/) Two similar code fragments were found\. Perhaps, this is a typo and 'playerB' variable should be used instead of 'playerA'\. SubTypeChangingEffectsTest\.java\(162\), SubTypeChangingEffectsTest\.java\(158\), SubTypeChangingEffectsTest\.java\(156\), SubTypeChangingEffectsTest\.java\(160\)

```cpp
@Test
public void testArcaneAdaptationGiveType() {
    addCard(Zone.HAND, playerA, "Arcane Adaptation", 1); // Enchantment {2}{U}
    addCard(Zone.BATTLEFIELD, playerA, "Island", 3);

    addCard(Zone.HAND, playerA, "Silvercoat Lion");
    addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
    addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

    addCard(Zone.HAND, playerB, "Silvercoat Lion");
    addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
    addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

    ....

    for (Card card : playerB.getGraveyard().getCards(currentGame)) {
        if (card.isCreature()) {
            Assert.assertEquals(card.getName() + " should not have ORC type",
                    false, card.getSubtype(currentGame).contains(SubType.ORC));
            Assert.assertEquals(card.getName() + " should have CAT type",
                    true, card.getSubtype(currentGame).contains(SubType.CAT));
        }
    }
}
```

Посмотрев, что эта ошибка в тестах, вы можете сразу же обесценить найденный дефект, подумав: "Нууу, это же теесты"\. Если это так, то я с вами не согласен\. Ведь тесты играют достаточно важную роль в разработке \(хотя и не настолько заметную, как программирование\), и при проявлении дефекта в релизе сразу же начинают тыкать пальцами на тесты/тестировщиков\. Так вот, дефектные тесты несостоятельны\. Зачем тогда такие тесты нужны? Зачем тратить ресурсы на них?

Метод _testArcaneAdaptationGiveType\(\)_ тестирует карточку "Arcane Adaptation"\. Каждому игроку раздаются карты в определенную игровую зону\. И благодаря copy\-paste в игровую зону "Кладбище" игроку _playerА_ попали 2 одинаковые карточки "Silvercoat Lion", а игроку _playerB_ так ничего и не досталось\. Далее какая\-то магия и само тестирование\.

Когда доходит тестирование до "кладбища" игрока _playerB_ в текущем розыгрыше, то в цикл выполнение теста так и не заходит, ведь в "кладбище" ничего и не было\. Это я выяснил старым добрым _System\.out\.println\(\)_ при запуске теста_\._

Исправленный вариант copy\-paste:

```cpp
....
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

addCard(Zone.HAND, playerB, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion");   // <=
....
```

После того как я скорректировал код, при запуске теста проверка существ в кладбище игрока _playerB_ начала работать\. Аве, _System\.out\.println\(\)_\!

Тест при этом зеленый и до, и после исправления, что является большим везением\. Но в случае каких\-либо правок, изменяющих логику выполнения программы, такой тест окажет вам медвежью услугу, уведомляя об успешном прохождении даже при наличии ошибок\.  

Такой же copy\-paste в других местах:

* V6072 Two similar code fragments were found\. Perhaps, this is a typo and 'playerB' variable should be used instead of 'playerA'\. PaintersServantTest\.java\(33\), PaintersServantTest\.java\(29\), PaintersServantTest\.java\(27\), PaintersServantTest\.java\(31\)
* V6072 Two similar code fragments were found\. Perhaps, this is a typo and 'playerB' variable should be used instead of 'playerA'\. SubTypeChangingEffectsTest\.java\(32\), SubTypeChangingEffectsTest\.java\(28\), SubTypeChangingEffectsTest\.java\(26\), SubTypeChangingEffectsTest\.java\(30\)

**Предупреждение N11**

[V6086](https://pvs-studio.ru/ru/docs/warnings/v6086/) Suspicious code formatting\. 'else' keyword is probably missing\. DeckImporter\.java\(23\)

```cpp
public static DeckImporter getDeckImporter(String file) {
  if (file == null) {
    return null;
  } if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {   // <=
    return new DecDeckImporter();
  } else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) {
    return new MWSDeckImporter();
  } else if (file.toLowerCase(Locale.ENGLISH).endsWith("txt")) {
    return new TxtDeckImporter(haveSideboardSection(file));
  }
  ....
  else {
    return null;
  }
}
```

Диагностическое правило [V6086](https://pvs-studio.ru/ru/docs/warnings/v6086/) диагностирует некорректное форматирование _if\-else\-if_, подразумевающее пропуск _else_\.

Данный фрагмент кода это и демонстрирует\. В данном случае из\-за выражения _return_ _null_ неаккуратность в форматировании ни к чему не приводит, но все же находить такие случаи круто, так как раз на раз не приходится\. 

Давайте рассмотрим случай, когда пропуск _else_ может привести к неожиданному поведению:

```cpp
public SomeType smtMethod(SomeType obj) {
  ....
  if (obj == null) {
    obj = getNewObject();
  } if (obj.isSomeObject()) {
    // some logic
  } else if (obj.isOtherSomething()) {
    obj = calulateNewObject(obj);
    // some logic
  } 
  ....
  else {
    // some logic
  }
  return obj;
}
```

Теперь, в случае _obj \=\= null_, рассматриваемому объекту присвоится какое\-то значение, и отсутствующий _else_ приведет к тому, что вновь присвоенный объект начнет проверяться по цепочке _if\-else\-if_, в то время как предполагалось, что объект сразу же вернется из метода\. 

## Заключение

Проверка XMage – очередная статья, которая раскрывает возможности современных статических анализаторов\. В современной разработке потребность в них только растет, так как сложность ПО увеличивается\. И сколько бы у вас не было релизов, тестов, обратной связи от пользователей: баг всегда найдет лазеечку, чтобы пробраться в вашу кодовую базу\. Так почему не обзавестись еще одним барьером для своей защиты?

Как вы поняли, анализаторам свойственны ложные срабатывания \(в том числе и PVS\-Studio Java\)\. Это может быть результатом как явной недоработки, так и слишком запутанного кода \(увы, анализатор не разобрался\)\. Нужно к ним относиться с пониманием и без стеснения сразу же [отписываться](https://pvs-studio.ru/ru/about-feedback/), а пока ложные срабатывания ждут своего исправления, можно воспользоваться  одним из [способов](https://pvs-studio.ru/ru/docs/manual/6703/) подавления предупреждений\. 

В заключение предлагаю лично "пощупать" анализатор, [скачав](https://pvs-studio.ru/ru/pvs-studio/download/) его с нашего сайта\.