﻿# Проверяем проект PascalABC\.NET с помощью плагинов для SonarQube: SonarC\# и PVS\-Studio

В ноябре прошлого года в нашем блоге была опубликована статья о разработке и использовании плагина PVS\-Studio для SonarQube\. Мы получили много откликов от клиентов и просто заинтересованных пользователей с просьбами провести тестирование плагина на реальном проекте\. Так как интерес к этому вопросу не ослабевает, было решено провести тестирование на C\# проекте PascalABC\.NET\. Также не будем забывать, что SonarQube содержит собственный статический анализатор C\# кода \- SonarC\#\. Для полноты картины проведем исследование и SonarC\#\. Целью данной работы является не сравнение анализаторов, а показ основных особенностей их взаимодействия с сервисом SonarQube\. Прямое сравнение анализаторов было бы не вполне корректным по той причине, что PVS\-Studio является специализированным инструментом поиска ошибок и потенциальных уязвимостей, в то время как SonarQube \- это сервис оценки качества кода по большому числу параметров: дублирование кода, соблюдение стандартов кодирования, покрытие кода модульными тестами, возможные ошибки в коде, плотность комментариев в коде, технический долг и т\.д\.

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

## Введение

Предварительно рекомендую ознакомиться с материалами [статьи](https://pvs-studio.ru/ru/blog/posts/0452/), где мы рассказываем о платформе SonarQube и интеграции с ней анализатора PVS\-Studio\.

Теперь немного об исследуемом проекте\. PascalABC\.NET – это реализация языка программирования Pascal нового поколения, содержащая собственную среду разработки, а также Web\-среду для создания программ на языках PascalABC\.NET, C\#, Visual Basic\.NET, F\#, IronPython\. Проект разработан на языке C\# и распространяется под свободной лицензией LGPLv3\. [Сайт проекта](http://pascalabc.net/)\. Исходный код можно загрузить из [репозитория](https://github.com/pascalabcnet/pascalabcnet) на GitHub\.

Решение PascalABC\.NET включает 2628 файлов с расширением '\.cs', которые содержат около 752 тысяч строк кода \(метрики получены с помощью утилиты SourceMonitor\)\. Таким образом, проект имеет вполне подходящий размер для наших исследовательских целей\.

## SonarC\#

Как уже говорилось ранее, сервис [SonarQube](https://www.sonarqube.org/) имеет в своем составе, в том числе, статический анализатор C\# кода\. Чтобы, как в нашем случае, добавить открытый проект на сайт, а также произвести его анализ, достаточно нескольких несложных действий\.

Для регистрации на сайте SonarQube я использовал учетную запись GitHub\. Далее воспользовался [инструкцией](https://docs.sonarqube.org/latest/setup/get-started-2-minutes/) по быстрому старту\. Весь процесс настройки, включая привязку проекта PascalABC\.NET к аккаунту, получение уникального ключа организации и настройку на локальном компьютере, занял у меня около 15 минут\. Еще 10 минут ушло на анализ проекта\. После этого [результат](https://www.sonarqube.org/) был загружен на сайт SonarQube, с ним может ознакомиться любой желающий\.

SonarQube выдал 3636 предупреждений о возможных ошибках в коде PascalABC\.NET:

![0492_PascalABCNET_ru/image2.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image2.png)

Из них: 8 блокирующих \(требуют немедленного устранения\), 64 критичных, 1742 важных и 1822 не критичных\. Информационных сообщений выдано не было\. Давайте попробуем ознакомиться с полученными предупреждениями, найти интересные ошибки и понять, каков процент ложных срабатываний\. Для этого воспользуемся удобными средствами фильтрации в различных измерениях, предоставляемыми сервисом SonarQube\. Начнем с блокирующих предупреждений\.

### Blocker

![0492_PascalABCNET_ru/image3.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image3.png)

Как видим, блокирующие предупреждения выданы для двух правил: бесконечная рекурсия и очистка IDisposable\-ресурсов\. Вот пример одного из blocker\-предупреждений:

![0492_PascalABCNET_ru/image4.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image4.png)

В get\-секции свойства _Instance_ ошибочно возвращается _Instance_, вместо _instance_, что порождает бесконечную рекурсию\.

Все остальные предупреждения на уровне Blocker также являются ошибками\.

### Critical

![0492_PascalABCNET_ru/image5.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image5.png)

На уровне Critical было выдано 64 предупреждения для правила о недопустимом приведении типов\. Рассмотрим одно из таких предупреждений:

![0492_PascalABCNET_ru/image6.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image6.png)

Изучив код и список имплементаций, я соглашусь с анализатором: в данный момент действительно нет ни одного типа, реализующего сразу оба интерфейса _IBaseScope_ и _IComparable_, вследствие чего результатом проверки _boxItem\.Item is IComparable_ всегда будет _false_\. Однако, я бы не стал говорить об ошибке в данном случае, так как, во\-первых, само наличие такой проверки исключает последующее возникновение исключения при попытке приведения типа _\(IComparable\)boxItem\.Item\._ Во\-вторых, в любой момент к решению может быть подключена, например, некая _dll_, в которой будет объявлен тип, реализующий оба интерфейса _IBaseScope_ и _IComparable_\. Возможно, на это и рассчитывал разработчик, реализуя приведение типа только после проверки\. Рассмотренное предупреждение, на мой взгляд, следует отнести к разряду сообщений для секции Minor, а не критичных для выполнения, и наличие его на уровне Critical считать ложным срабатыванием\.

Оставшиеся 63 предупреждения аналогичны рассмотренному\.

### Major

На данном уровне было выдано достаточно много предупреждений \- 1742, для пятнадцати типов диагностик:

![0492_PascalABCNET_ru/image7.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image7.png)

Давайте пройдем по списку предупреждений с целью поиска реальных ошибок и оценки возможностей анализатора\.

**General exceptions should never be thrown**

Правило сообщает о выбросе исключения общего типа при помощи _throw_\. В коде проекта PascalABC\.NET было найдено 634 подобные конструкции\. Подавляющее большинство имеет следующий вид:

![0492_PascalABCNET_ru/image8.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image8.png)

Также встречается очень много \(более 600\) конструкций, похожих на "заглушки" в коде, умышленно оставленные разработчиками:

![0492_PascalABCNET_ru/image9.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image9.png)

Конечно, выброс исключений общего типа является "дурным тоном"\. Тем не менее, как мне кажется, это вовсе не ошибки\. Тем более маловероятно, чтобы авторы кода умышленно наплодили их в таком количестве\. Да, обработка исключений в проекте PascalABC\.NET, по всей видимости, не на высоте\. Тем не менее, место всем этим 634 однотипным предупреждениям в секции Minor или вовсе в ложных срабатываниях анализатора\.

Кстати, это хороший пример, в чем разница между SonarC\# и анализатором PVS\-Studio\. SonarC\# указывает на "запахи" в коде и совершенно прав, выдав эти предупреждения\. Они позволяют судить о качестве проекта\. С точки же нас, разработчиков анализатора PVS\-Studio, это ложные срабатывания, так как мы ориентированы на поиск ошибок и дефектов безопасности\.

**Dead stores should be removed**

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

![0492_PascalABCNET_ru/image10.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image10.png)

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

Есть и несколько абсолютно однозначных false\-positive предупреждений вида:

![0492_PascalABCNET_ru/image11.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image11.png)

Если в данном случае последовать рекомендациям анализатора, то логика работы программы будет нарушена\. 

Таким образом, мне так и не удалось найти среди 618 предупреждений из рассмотренной группы ни одной реальной ошибки\.

**Floating point numbers should not be tested for equality**

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

В коде PascalABC\.NET мне встретился, преимущественно, следующий паттерн сравнения с вещественной переменной:

![0492_PascalABCNET_ru/image12.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image12.png)

Обратите внимание, что сравнение производится как двух вещественных переменных, так и вещественной переменной с переменной целого типа\. Конечно, такой код не вполне безопасен, так как неизвестно, каким образом были получены сравниваемые значения\. Стоит ли здесь говорить о явной ошибке? Сложно дать однозначный ответ\. Но код, вероятно, требует доработки\.

Кстати, анализатор PVS\-Studio также предупреждает о таких подозрительных сравнениях, но эти диагностики относятся к уровню достоверности Low и не рекомендуются нами к изучению\.

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

![0492_PascalABCNET_ru/image13.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image13.png)

В данном случае производится сравнение двух переменных типа _byte_\. Переменные _left_ и _right_ имеют тип _byte\_const\_node_:

```cpp
public class byte_const_node : concrete_constant<byte>,
                               SemanticTree.IByteConstantNode
{
  public byte_const_node(byte value, location loc)
      : base(value, loc)
  {
  }
  ....
}

public abstract class concrete_constant<ConstantType> : constant_node
{
  private ConstantType _constant_value;
  public concrete_constant(ConstantType value, location loc) :
    base(compiled_type_node.get_type_node(typeof(ConstantType)), loc)
  {
    _constant_value = value;
  }
  ....
  public ConstantType constant_value
  {
    get
    {
      return _constant_value;
    }
    ....
  }
  ....
  }
  ....
}
```

Думаю, данная группа предупреждений обоснованно находится в секции Major\. Однако, я бы не стал считать все найденные предупреждения ошибками\. Решение должен принимать автор кода в каждом конкретном случае\.

**Multiline blocks should be enclosed in curly braces**

Группа из 108 предупреждений, включающая потенциальные ошибки форматирования, влияющие на логику выполнения программы\. Здесь я обнаружил довольно подозрительные конструкции\. Пример:

![0492_PascalABCNET_ru/image14.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image14.png)

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

Ещё одно подобное предупреждение:

![0492_PascalABCNET_ru/image15.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image15.png)

Ошибки нет, но код выглядит неаккуратно\. Необходим рефакторинг\.

В принципе, все предупреждения из данной группы выданы по делу, но настоящих ошибок они не выявили\.

**Null pointers should not be dereferenced**

75 предупреждений о возможном доступе по нулевой ссылке\. В данном блоке я обнаружил интересные ошибки:

![0492_PascalABCNET_ru/image16.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image16.png)

Действительно, ранее в коде переменная _returned\_scope_ всегда проверяется на равенство _null_ перед использованием, но в данном случае про это забыли:

```cpp
public override void visit(....)
{
  ....
  if (returned_scope != null && ....)
  {
    ....
  }
  else if (returned_scope != null)
  {
    ....
  }
  returned_scope.declaringUnit = entry_scope;  // <=
  ....
}
```

Ещё одна похожая ошибка:

![0492_PascalABCNET_ru/image17.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image17.png)

В первом случае переменную _pi_ проверяют на равенство _null_ перед использованием, но далее, при следующем обращении к _pi\.CompilationUnit,_ это сделать забывают\.

Блок предупреждений содержит некоторое количество не столь очевидных ошибок, а также ложных срабатываний\. Я бы оценил процент нахождения реальных ошибок здесь равным 85%\. Очень хороший результат\.

**Conditions should not unconditionally evaluate to "true" or to "false"**

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

![0492_PascalABCNET_ru/image18.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image18.png)

Странный код, требующий доработки автором\. Возможно, допущена серьезная ошибка\.

В целом, группа содержит порядка 70% подобных ошибок\.

**Exceptions should not be thrown from property getters**

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

![0492_PascalABCNET_ru/image19.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image19.png)

Есть и не вполне корректные конструкции, требующие рефакторинга:

![0492_PascalABCNET_ru/image20.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image20.png)

Тем не менее, я не считаю данные предупреждения ошибками\. Думаю, рациональней их было\-бы отнести к секции Minor\.

**Static fields should not be updated in constructors**

Диагностика об опасности обновления статических полей в конструкторах: это может привести к несогласованному поведению программы, так как поле будет заново инициализировано для всех экземпляров класса\. Всего для проекта PascalABC\.NET анализатор выдал 26 подобных предупреждений\. Я не нашёл среди них реальных ошибок\. Вот пара примеров обнаруженных фрагментов кода:

![0492_PascalABCNET_ru/image21.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image21.png)

В статическую переменную _\_instance_ каждый раз записывают ссылку на новый экземпляр класса\. Судя по имени переменной \- так и задумывалось\.

![0492_PascalABCNET_ru/image22.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image22.png)

Флаг _parsers\_loaded_ сигнализирует о том, что хотя бы один экземпляр класса уже был создан\. Ничего криминального\.

**"\=\+" should not be used instead of "\+\="**

Интересная диагностика о том, что вместо оператора "\-\=", например, ошибочно использовали "\=\-"\. Анализатор выдал 9 подобных предупреждений\. К сожалению, все они являются ложными срабатываниями\. 6 предупреждений выдано для конструкций, являющихся объявлением переменных, где в принципе невозможно использование оператора "\-\=" или "\+\=":

![0492_PascalABCNET_ru/image23.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image23.png)

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

![0492_PascalABCNET_ru/image24.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image24.png)

**Related "if/else if" statements should not have the same condition**

5 предупреждений было выдано для фрагментов кода с одинаковым условием в блоках _if_ и _else if_\. Часто такой код либо уже является ошибочным, либо содержит потенциальную возможность ошибки\. В нашем случае 4 из 5 предупреждений содержали простое дублирование условия, а также блока выполнения, что, конечно, подозрительно, но не является грубой ошибкой\. Одно предупреждение более интересно:

![0492_PascalABCNET_ru/image25.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image25.png)

До того, как часть условия в первом блоке _if_ закомментировали, оно отличалось от условия в следующем за ним блоке _else if_\. Также обратите внимание на блок выполнения этого второго _else if_: он пуст\. Там присутствует только один оператор: ";"\. Очень странный и подозрительный код\.

**Short\-circuit logic should be used in boolean contexts**

Диагностика предупреждает, например, о возможном ошибочном использовании оператора _&_ вместо _&&_ для выражений типа _bool_\. Всего было найдено 5 подозрительных конструкций\. Все они так или иначе требуют к себе внимания, хотя, возможно, и не содержат ошибок\. Вот пример одной и них:

![0492_PascalABCNET_ru/image26.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image26.png)

В данном случае нельзя точно утверждать, что использование оператора "\|" ошибочно, так как в правой его части проверяется свойство со сложной логикой внутри\. Возможно, разработчик добивался именно того, чтобы всегда проверялись оба условия\.

**Exceptions should not be explicitly rethrown**

Диагностика про потерю стека исключения\. Анализатор выдал 4 однотипных предупреждения, вида:

![0492_PascalABCNET_ru/image27.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image27.png)

Конечно, так делать не следует\. Дальнейшая отладка приложения будет затруднена\. Но все эти предупреждения не настолько критичны\. По моему мнению, их место в секции Minor\.

**Variables should not be self\-assigned**

3 предупреждения о присваивании значения переменной самой себе\. Вот пример одного из найденных фрагментов кода:

![0492_PascalABCNET_ru/image28.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image28.png)

Странный и явно ошибочный код\. Объявление _visitNode_ имеет вид:

```cpp
protected bool visitNode = true;
```

Всего в данной группе предупреждений содержится две ошибки\.

**Identical expressions should not be used on both sides of a binary operator**

Диагностика производит поиск условий, в которых есть одинаковые подвыражения\. Было найдено 2 подозрительные конструкции\. Явной ошибки нет ни в одной из них, но, возможно, код должен был выглядеть и работать иначе\. Пример одного из предупреждений:

![0492_PascalABCNET_ru/image29.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image29.png)

Странный код\. Возможно, забыли заменить вторую проверку\.

** "ToString\(\)" method should not return null**

Последняя группа предупреждений в секции Major\. Перегрузка метода _ToString\(\)_ реализована некорректно\. Выдано 2 предупреждения, и оба являются ошибками\. Пример одной из них:

![0492_PascalABCNET_ru/image30.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image30.png)

Некорректно возвращать _null_ из перегруженного метода _ToString\(\)\._ Необходимо использовать _string\.Empty_\.

### Minor

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

### Результаты проверки анализатором SonarC\#

Подводя итог могу сказать, что в целом анализатор нашёл реальные ошибки на уровнях Blocker, Critical и Major \(я насчитал 268 ошибочных или крайне подозрительных конструкций на 1814 предупреждений\), некоторые из которых действительно интересны\. Тем не менее, значение процента ложных срабатываний довольно велико и составляет более 85%\. Это значительно затрудняет анализ результатов работы\.

## Плагин PVS\-Studio для SonarQube

Вопросам интеграции результатов работы анализатора PVS\-Studio в SonarQube посвящен [раздел документации](https://pvs-studio.ru/ru/docs/manual/0037/) на нашем сайте\. Для настройки интеграции "с нуля" мне понадобилось около 15 минут\. Еще столько же заняла проверка проекта и загрузка результатов на локальный сервер SonarQube\.

PVS\-Studio выдал 1039 предупреждений в ходе проверки кода PascalABC\.NET\. Из них: 156 предупреждений уровня Critical, 541 \- уровня Major, 342 \- уровня Minor\.

![0492_PascalABCNET_ru/image31.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image31.png)

Предупреждения уровня Minor рассматривать не будем, так как среди них обычно высок процент ложных срабатываний\. 

Распределение предупреждений по диагностикам на уровне Critical:

![0492_PascalABCNET_ru/image32.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image32.png)

Распределение предупреждений по диагностикам на уровне Major:



![0492_PascalABCNET_ru/image33.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image33.png)

Проанализировав 697 предупреждений на уровнях Critical и Major, я выяснил, что 204 из них можно отнести к ложным срабатываниям\. Это составляет около 29% от общего числа предупреждений на первом и втором уровне важности\. Таким образом, процент выявления реальных ошибок и подозрительных конструкций для проекта PascalABC\.NET равен 71%\. В пересчете на количество строк кода \(KLOC\) это 0\.66 ошибки на KLOC\. Давайте взглянем на наиболее интересные из обнаруженных ошибок\. Для удобства я буду приводить ошибки по возрастанию номера диагностического правила\.

**Copy\-Paste**

[V3001](https://pvs-studio.ru/ru/docs/warnings/v3001/) There are identical sub\-expressions 'token\.Kind \=\= openBracketToken' to the left and to the right of the '\|\|' operator\. ICSharpCode\.SharpDevelop NRefactoryInsightWindowHandler\.cs 66

```cpp
readonly int eofToken,
             commaToken, 
             openParensToken, 
             closeParensToken,
             openBracketToken, 
             closeBracketToken,
             openBracesToken,
             closeBracesToken,
             statementEndToken;

public void InitializeOpenedInsightWindow(....)
{
  ....
  if (token.Kind == openParensToken || 
      token.Kind == openBracketToken ||
      token.Kind == openBracketToken) {  // <=
    bracketCount++;
  }
  ....
}
```

В условии блока _if_ дважды проверяется равенство _token\.Kind \=\= openBracketToken_\. Среди полей, объявленных в классе, можно найти поле с очень похожим именем _openBracesToken_\. Вероятно, именно это поле было пропущено в условии\. В таком случае, исправленный вариант кода мог бы иметь вид:

```cpp
public void InitializeOpenedInsightWindow(....)
{
  ....
  if (token.Kind == openParensToken || 
      token.Kind == openBracketToken ||
      token.Kind == openBracesToken) {
    bracketCount++;
  }
  ....
}
```

Подобные ошибки в коде:

* [V3001](https://pvs-studio.ru/ru/docs/warnings/v3001/) There are identical sub\-expressions 'File\.Exists\(pdbFileName\)' to the left and to the right of the '&&' operator\. VisualPascalABCNET RunnerManagerHandlers\.cs 165
* [V3001](https://pvs-studio.ru/ru/docs/warnings/v3001/) There are identical sub\-expressions '\_pascal\_set\_constant\.values \!\= null' to the left and to the right of the '&&' operator\. TreeConverter syntax\_tree\_visitor\.cs 4553

**Невнимательность**

[V3003](https://pvs-studio.ru/ru/docs/warnings/v3003/) The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: 597, 631\. ParserTools SyntaxTreeComparer\.cs 597

```cpp
public void CompareInternal(....)
{
  ....
  if (left is ident)
    CompareInternal(left as ident, right as ident);
  ....
  else if (left is int64_const)
    CompareInternal(left as int64_const, right as int64_const);
  ....
  else if (left is int64_const)
    CompareInternal(left as int64_const, right as int64_const);  
  ....
}
```

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

Аналогичные ошибки:

* [V3003](https://pvs-studio.ru/ru/docs/warnings/v3003/) The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: 1599, 1611\. ParserTools SyntaxTreeComparer\.cs 1599
* [V3003](https://pvs-studio.ru/ru/docs/warnings/v3003/) The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: 1611, 1615\. ParserTools SyntaxTreeComparer\.cs 1611
* [V3003](https://pvs-studio.ru/ru/docs/warnings/v3003/) The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: 103, 209\. SyntaxVisitors SimplePrettyPrinterVisitor\.cs 103

**Copy\-Paste v2\.0**

[V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. VisualPascalABCNET CodeCompletionWindow\.cs 204

```cpp
public void HandleMouseWheel(....)
{
  ....
  if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines
      > 0) {
    newValue = this.vScrollBar.Value -
      (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) *
      multiplier;
  } else {
    newValue = this.vScrollBar.Value -
      (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) *
      multiplier;
  }
  ....
}
```

Обе ветви блока _if_ содержат идентичные подвыражения\. В данном случае сложно сделать вывод о правильном варианте данного фрагмента, но в приведенном виде код будет работать не так, как ожидается\.

Подобные ошибки в коде:

* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. NETGenerator NETGenerator\.cs 439
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. NETGenerator NETGenerator\.cs 2338
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. NETGenerator NETGenerator\.cs 4062
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. NETGenerator NETGenerator\.cs 5971
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. NETGenerator NETGenerator\.cs 6069
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. CodeCompletion CodeFormatter\.cs 1254
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. CodeCompletion DomConverter\.cs 428
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. TreeConverter type\_table\.cs 380
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. TreeConverter type\_table\.cs 401
* [V3004](https://pvs-studio.ru/ru/docs/warnings/v3004/) The 'then' statement is equivalent to the 'else' statement\. TreeConverter type\_table\.cs 424

Я привел только первые 10 подобных ошибок из 20\.

**Переменная присваивается сама себе**

[V3005](https://pvs-studio.ru/ru/docs/warnings/v3005/) The 'miGenerateRealization\.Visible' variable is assigned to itself\. VisualPascalABCNET OptionsManager\.cs 342

```cpp
public void UpdateUserOptions()
{
  ....
  tsViewIntellisensePanel.Visible = tssmIntellisence.Visible = 
  tsGotoDefinition.Visible = tsGotoRealization.Visible =
  tsFindAllReferences.Visible = miGenerateRealization.Visible =
  miGenerateRealization.Visible = cmGenerateRealization.Visible =
  cmsCodeCompletion.Visible = cmFindAllReferences.Visible = 
  cmGotoDefinition.Visible = cmGotoRealization.Visible = 
  UserOptions.AllowCodeCompletion;
}
```

Переменная _miGenerateRealization\.Visible_ получает одинаковое значение дважды в ходе присваивания\. Вероятно, лишнее присваивание добавлено случайно\. Однако, вместо одной из переменных _miGenerateRealization\.Visible_ могла бы находиться какая\-то другая переменная, которая теперь не инициализируется\.

Найдена ещё одна аналогичная ошибка:

[V3005](https://pvs-studio.ru/ru/docs/warnings/v3005/) The 'visitNode' variable is assigned to itself\. SyntaxVisitors SimplePrettyPrinterVisitor\.cs 106

**Повторное присваивание**

[V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'codeCompileUnit' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 126, 124\. VisualPascalABCNET CodeDomHostLoader\.cs 126

```cpp
CodeCompileUnit codeCompileUnit = null;
private DesignSurface Designer;
....
protected override CodeCompileUnit Parse()
{
  ....
  CodeCompileUnit ccu = null;
  DesignSurface ds = new DesignSurface();
  ....
  ccu = cg.GetCodeCompileUnit(idh);
  ....
  codeCompileUnit = ccu;
  Designer = ds;
  codeCompileUnit = ccu;  // <=
  ....
}
```

Из кода видно, что нет совершенно никакого логического объяснения повторному присваиванию переменной _codeCompileUnit_ того же самого значения\.

Подобные ошибки в коде:

* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'mSTEPToolStripMenuItem\_Enabled' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 541, 532\. VisualPascalABCNET VisibilityService\.cs 541
* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 62, 60\. NETGenerator Helpers\.cs 62
* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'loc' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 2123, 2122\. TreeConverter compilation\_context\.cs 2123
* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'cnfn\.function\_code' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 260, 259\. TreeConverter functions\_calls\.cs 260
* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'namespace\_func\.function\_code' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 267, 266\. TreeConverter functions\_calls\.cs 267
* [V3008](https://pvs-studio.ru/ru/docs/warnings/v3008/) The 'ti\.init\_meth' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 1739, 1735\. NETGenerator NETGenerator\.cs 1739

**Результат работы метода всегда одинаков**

[V3009](https://pvs-studio.ru/ru/docs/warnings/v3009/) It's odd that this method always returns one and the same value of 'false'\. NETGenerator NETGenerator\.cs 5434

```cpp
private bool BeginOnForNode(IStatementNode value)
{
    //if (value is IForNode) return true;
    IStatementsListNode stats = value as IStatementsListNode;
    if (stats == null) return false;
    if (stats.statements.Length == 0) return false;
    //if (stats.statements[0] is IForNode) return true;
    return false;
}
```

Вероятно, здесь мы имеем дело с невнимательностью при рефакторинге\. Ранее в коде были блоки кода, возвращающие _true_\. Однако теперь они закомментированы, а метод, независимо от результата своей работы, вернет _false_\.

Подобные ошибки в коде:

* [V3009](https://pvs-studio.ru/ru/docs/warnings/v3009/) It's odd that this method always returns one and the same value of '0'\. PABCNETC CommandConsoleCompiler\.cs 297
* [V3009](https://pvs-studio.ru/ru/docs/warnings/v3009/) It's odd that this method always returns one and the same value of '0'\. PABCNETCclear CommandConsoleCompiler\.cs 266

**Невнимательность**

[V3010](https://pvs-studio.ru/ru/docs/warnings/v3010/) The return value of function 'OrderBy' is required to be utilized\. ICSharpCode\.SharpDevelop RefactoringService\.cs 86

```cpp
static IEnumerable<ITreeNode<IClass>> FindDerivedClassesTree(....)
{
  ....
  var result = new List<TreeNode<IClass>>();
  ....
  result.OrderBy(node => node.Content.FullyQualifiedName);  // <=
  return result;
}
```

Результат сортировки списка _result_ никуда не сохраняется\. Исправленный вариант приведенного фрагмента:

```cpp
static IEnumerable<ITreeNode<IClass>> FindDerivedClassesTree(....)
{
  ....
  var result = new List<TreeNode<IClass>>();
  ....
  return result.OrderBy(node => node.Content.FullyQualifiedName);
}
```

И ещё одна такая ошибка:

[V3010](https://pvs-studio.ru/ru/docs/warnings/v3010/) The return value of function 'ToString' is required to be utilized\. CodeCompletion SymTable\.cs 2145

**Проблема с логикой**

[V3018](https://pvs-studio.ru/ru/docs/warnings/v3018/) Consider inspecting the application's logic\. It's possible that 'else' keyword is missing\. VisualPascalABCNET InsightWindow\.cs 145

```cpp
public void HandleMouseWheel(MouseEventArgs e)
{
  ....
  if (e.Delta > 0) {
    if (control.TextEditorProperties.MouseWheelScrollDown) {
      CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount;
    } else {
      CurrentData = (CurrentData + DataProvider.InsightDataCount - 1)
        % DataProvider.InsightDataCount;
    }
  } if (e.Delta < 0) {  // <=
      if (control.TextEditorProperties.MouseWheelScrollDown) {
        CurrentData = (CurrentData + DataProvider.InsightDataCount
          - 1) % DataProvider.InsightDataCount;
      } else {
        CurrentData = (CurrentData + 1) %
          DataProvider.InsightDataCount;
      }
  }
  ....
}
```

Обратите внимание на условие _if \(e\.Delta < 0\)_\. Исходя из того, как отформатирован код, а также из логики программы, следует вывод: возможно, пропущено ключевое слово _else_\. Тем не менее, только автор может дать точный ответ об особенностях данной конструкции\.

**Классическая ошибка при работе с оператором "as"**

[V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'baseScope', 'this\.baseScope'\. CodeCompletion SymTable\.cs 3497

```cpp
public TypeScope(...., SymScope baseScope)
{
  ....
  this.baseScope = baseScope as TypeScope;
  ....
  if (baseScope == null)
  {
    ....
  }
  ....
}
```

После приведения аргумента _baseScope_ к типу _TypeScope_ по ошибке на равенство _null_ проверяется не поле _this\.baseScope_, а аргумент _baseScope_\. Исправленный вариант кода:

```cpp
public TypeScope(...., SymScope baseScope)
{
  ....
  this.baseScope = baseScope as TypeScope;
  ....
  if (this.baseScope == null)
  {
    ....
  }
  ....
}
```

Подобные ошибки в коде:

* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'returned\_scope', 'ts'\. CodeCompletion ExpressionVisitor\.cs 1595
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'returned\_scope', 'tmp\_scope'\. CodeCompletion DomSyntaxTreeVisitor\.cs 1553
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'returned\_scope', 'ts\.elementType'\. CodeCompletion DomSyntaxTreeVisitor\.cs 2815
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'returned\_scope', 'es\.elementType'\. CodeCompletion DomSyntaxTreeVisitor\.cs 2828
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'node', 'solutionFolderNode'\. ICSharpCode\.SharpDevelop SolutionNodeCommands\.cs 21
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'node', 'solutionFolderNode'\. ICSharpCode\.SharpDevelop SolutionNodeCommands\.cs 91
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'node', 'solutionFolderNode'\. ICSharpCode\.SharpDevelop SolutionNodeCommands\.cs 115
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'node', 'solutionFolderNode'\. ICSharpCode\.SharpDevelop SolutionNodeCommands\.cs 138
* [V3019](https://pvs-studio.ru/ru/docs/warnings/v3019/) Possibly an incorrect variable is compared to null after type conversion using 'as' keyword\. Check variables 'rr', 'mrr'\. ICSharpCode\.SharpDevelop RefactoringService\.cs 330

**Неаккуратный код**

[V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 't \=\= null' is always true\. VisualPascalABCNET Debugger\.cs 141

```cpp
public static Type GetTypeForStatic(string name)
{
  Type t = stand_types[name] as Type;
  if (t != null) return t;
  if (t == null)  //  <=
    foreach (string s in ns_ht.Keys)
    {
      ....
    }
  t = PascalABCCompiler.NetHelper.NetHelper.FindType(name);
  ....
}
```

Ошибки нет, но программа выглядит неаккуратно\. 

Подобные конструкции в коде:

* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'CodeCompletion\.CodeCompletionController\.CurrentParser \=\= null' is always false\. VisualPascalABCNET CodeCompletionKeyHandler\.cs 91
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'CodeCompletion\.CodeCompletionController\.CurrentParser \=\= null' is always false\. VisualPascalABCNET CodeCompletionKeyHandler\.cs 114
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'CodeCompletion\.CodeCompletionController\.CurrentParser \=\= null' is always false\. VisualPascalABCNET CodeCompletionKeyHandler\.cs 136
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'CodeCompletion\.CodeCompletionController\.CurrentParser \=\= null' is always false\. VisualPascalABCNET CodeCompletionKeyHandler\.cs 183
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'defaultCompletionElement \=\= null && data \!\= null' is always false\. VisualPascalABCNET CodeCompletionProvider\.cs 507
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'inRecalculateNeedsRedraw' is always false\. VisualPascalABCNET DynamicTreeView\.cs 1103
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'expressionResult \!\= null && expressionResult \!\= ""' is always false\. VisualPascalABCNET CodeCompletionActions\.cs 225
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression 'SaveCanceled' is always false\. VisualPascalABCNET FileOperations\.cs 442
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression '\!SaveCanceled' is always true\. VisualPascalABCNET FileOperations\.cs 450
* [V3022](https://pvs-studio.ru/ru/docs/warnings/v3022/) Expression '\_format\_expr\.format2 \!\= null' is always true\. VisualPascalABCNET ExpressionEvaluation\.cs 7028

Я привёл только первые 10 подобных предупреждений из более чем 45\.

**Избыточная проверка или ошибка?**

[V3030](https://pvs-studio.ru/ru/docs/warnings/v3030/) Recurring check\. The 'upperScopeWhereVarsAreCaptured \!\= scope' condition was already verified in line 383\. TreeConverter CapturedVariablesSubstitutionClassGenerator\.cs 391

```cpp
private void VisitCapturedVar(....)
{
  ....
  if (upperScopeWhereVarsAreCaptured != scope)
  {
  ....
    if (upperScopeWhereVarsAreCaptured != scope)
    {
      ....
    }
    ....
  }
  ....    
}
```

Обычно такие конструкции не являются ошибкой, но существует вероятность, что одна из проверок должна была содержать другое условие\.

Подобные ошибки в коде:

* [V3030](https://pvs-studio.ru/ru/docs/warnings/v3030/) Recurring check\. The 'kav\.Count \=\= 0' condition was already verified in line 2515\. ParserTools DefaultLanguageInformation\.cs 2518
* [V3030](https://pvs-studio.ru/ru/docs/warnings/v3030/) Recurring check\. The 'ret\_tn \!\= null' condition was already verified in line 289\. CodeCompletion FindReferences\.cs 291
* [V3030](https://pvs-studio.ru/ru/docs/warnings/v3030/) Recurring check\. The 'kav\.Count \=\= 0' condition was already verified in line 885\. VBNETParser LanguageInformation\.cs 888

**Странное форматирование**

[V3033](https://pvs-studio.ru/ru/docs/warnings/v3033/) It is possible that this 'else' branch must apply to the previous 'if' statement\. TreeConverter syntax\_tree\_visitor\.cs 14894

```cpp
public override void visit(....)
{
  ....
  if (_var_def_statement.inital_value != null)
    if (is_event) AddError(....);
  else
  {
    ....
  }
  ....
}
```

По логике работы программы, ключевое слово _else_ относится к блоку условия _if \(is\_event\)_\. Однако, код отформатирован таким образом, что создается совсем другое впечатление\. Вероятно, использование скобок _\{ \}_ решило бы проблему\.

**Опечатка**

[V3038](https://pvs-studio.ru/ru/docs/warnings/v3038/) The 'enum\_consts\[i\]' argument was passed to 'Compare' method several times\. It is possible that other argument should be passed instead\. CodeCompletion SymTable\.cs 2206

```cpp
private List<string> enum_consts = new List<string>();
public override bool IsEqual(SymScope ts)
{
  EnumScope es = ts as EnumScope;
  if (es == null) return false;
  if (enum_consts.Count != es.enum_consts.Count) return false;
  for (int i = 0; i < es.enum_consts.Count; i++)
    if (string.Compare(enum_consts[i],
                       this.enum_consts[i], true) != 0)  // <=
      return false;
  return true;
}
```

К сожалению, метод _IsEqual_ не содержит объявления локальной переменной _enum\_consts_\. Поэтому внутри цикла _for_ элементы списка _enum\_consts_ многократно сравниваются сами с собой\. По виду метода _IsEqual_ можно сделать предположение о правильном варианте кода:

```cpp
public override bool IsEqual(SymScope ts)
{
  ....
  for (int i = 0; i < es.enum_consts.Count; i++)
    if (string.Compare(enum_consts[i],
                       es.enum_consts[i], true) != 0)
    ....
}
```

**Проблема с логикой v2\.0**

[V3043](https://pvs-studio.ru/ru/docs/warnings/v3043/) The code's operational logic does not correspond with its formatting\. The statement is indented to the right, but it is always executed\. It is possible that curly brackets are missing\. VBNETParser LanguageInformation\.cs 1002

```cpp
public override string FindExpression(....)
{
  ....
  switch (ch)
  {
    ....
    case '(':
      if (kav.Count == 0)
      {
        ....
      }
      else sb.Insert(0, ch); punkt_sym = true;
      break;
  }
  ....
}
```

Присваивание _punkt\_sym \= true_ будет выполнено независимо от результата проверки _kav\.Count \=\= 0_\. Тем не менее, код отформатирован таким образом, что создается впечатление, что это будет сделано только при условии _kav\.Count \!\= 0_\.

Ещё одна подобная ошибка:

[V3043](https://pvs-studio.ru/ru/docs/warnings/v3043/) The code's operational logic does not correspond with its formatting\. The statement is indented to the right, but it is always executed\. It is possible that curly brackets are missing\. ICSharpCode\.SharpDevelop AbstractConsolePad\.cs 159

**Потеря стека исключения**

[V3052](https://pvs-studio.ru/ru/docs/warnings/v3052/) The original exception object 'e' was swallowed\. Stack of original exception could be lost\. NETGenerator NETGenerator\.cs 925

```cpp
public void ConvertFromTree(....)
{
  ....
  try
  {
    ....
  }
  catch (System.Runtime.InteropServices.COMException e)
  {
    throw new TreeConverter.SaveAssemblyError(e.Message);
  }
  ....
}
```

Из объекта выброшенного исключения типа _COMException_ разработчик использует только текст сообщения\. По всей видимости, это осмысленное решение, так как далее выбрасывается исключение типа _SaveAssemblyError_, конструктор которого ничего кроме текста сообщения не требует:

```cpp
public class SaveAssemblyError : CompilationError
{
  ....
  public SaveAssemblyError(string text)
  {
    _text = text;
  }
  ....
}
```

Конечно, такой вариант реализации \- это право автора\. Однако, на мой взгляд, обработка исключения в данном случае не выглядит полноценной\.

Подобные ошибки в коде:

* [V3052](https://pvs-studio.ru/ru/docs/warnings/v3052/) The original exception object 'e' was swallowed\. Stack of original exception could be lost\. NETGenerator NETGenerator\.cs 929
* [V3052](https://pvs-studio.ru/ru/docs/warnings/v3052/) The original exception object 'ex' was swallowed\. Stack of original exception could be lost\. ICSharpCode\.SharpDevelop ReferenceFolderNodeCommands\.cs 92
* [V3052](https://pvs-studio.ru/ru/docs/warnings/v3052/) The original exception object 'ex' was swallowed\. Stack of original exception could be lost\. TreeConverter syntax\_tree\_visitor\.cs 16324

**Ошибка работы с подстроками**

[V3053](https://pvs-studio.ru/ru/docs/warnings/v3053/) An excessive expression\. Examine the substrings 'reduction' and 'reduction\('\. TreeConverter OpenMP\.cs 267

```cpp
private void ProcessClauses(string Text, ....)
{
  ....
  if (....)
  {
    ....
  }
  else if (AllowReduction && 
           (Text.StartsWith("reduction") ||
            Text.StartsWith("reduction(")))
  {
    ....
  }
  ....
}
```

В данном случае поиск подстроки _"reduction\("_ лишен смысла, так как ранее всегда будет найдена подстрока _"reduction"_\.

**Ошибочный порядок инициализации**

[V3070](https://pvs-studio.ru/ru/docs/warnings/v3070/) Uninitialized variable 'event\_add\_method\_prefix' is used when initializing the 'event\_add\_method\_nameformat' variable\. TreeConverter compiler\_string\_consts\.cs 313

```cpp
public static class compiler_string_consts
{
  ....
  public static string event_add_method_nameformat =
         event_add_method_prefix + "{0}";
  ....
  public static string event_add_method_prefix = "add_";
  ....
}
```

В результате выполнения приведенного фрагмента кода, строка _event\_add\_method\_nameformat_ получит значение _"\{0\}"_, вместо ожидаемого _"add\_\{0\}"_\. Для исправления ошибки следует поменять местами строки инициализации полей:

```cpp
public static class compiler_string_consts
{
  ....
  public static string event_add_method_prefix = "add_";
  ....
  public static string event_add_method_nameformat =
         event_add_method_prefix + "{0}";
  ....
}
```

Ещё одна такая же ошибка:

[V3070](https://pvs-studio.ru/ru/docs/warnings/v3070/) Uninitialized variable 'event\_remove\_method\_prefix' is used when initializing the 'event\_remove\_method\_nameformat' variable\. TreeConverter compiler\_string\_consts\.cs 314

**Доступ по нулевой ссылке: небрежный рефакторинг**

[V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'tc'\. CodeCompletion CodeCompletionPCUReader\.cs 736

```cpp
private TypeScope GetTemplateInstance()
{
  TypeScope tc = null;//GetTemplateClassReference();
  int params_count = br.ReadInt32();
  for (int i = 0; i < params_count; i++)
  {
    tc.AddGenericInstanciation(GetTypeReference());  // <=
  }
  return tc;
}
```

Как видим, ранее переменная _tc_ инициализировалась значением _GetTemplateClassReference\(\)_\. Однако, теперь \- это значение _null_\. В результате, на первой же итерации цикла _for_ будет возникать ошибка доступа по нулевой ссылке\. Возможно, ошибка пока никак не проявила себя, так как вызовы метода _GetTemplateInstance\(\)_ в коде отсутствуют\. Но нет никакой гарантии того, что так будет и в дальнейшем\.

Подобные ошибки в коде:

* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7334
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7336
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7338
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7340
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7409
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7411
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7413
* [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference\. Consider inspecting 'bfc'\. TreeConverter syntax\_tree\_visitor\.cs 7415

**Доступ по нулевой ссылке: невнимательность**

[V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'VisualEnvironmentCompiler\.RemoteCompiler' object was used before it was verified against null\. Check lines: 52, 54\. CompilerController CompilerControllerPlugin\.cs 52

```cpp
public CompilerController_VisualPascalABCPlugin(....)
{
  ....
  VisualEnvironmentCompiler.RemoteCompiler.InternalDebug.RunOnMono = 
    CompilerInformation.cbRunMono.Checked;
  ....
  if (VisualEnvironmentCompiler.RemoteCompiler != null)
    ....
}
```

Проверку на возможное равенство переменной _null_ делают уже после её использования\. Исправленный вариант кода:

```cpp
public CompilerController_VisualPascalABCPlugin(....)
{
  ....
  if (VisualEnvironmentCompiler.RemoteCompiler != null)
  {
    VisualEnvironmentCompiler.RemoteCompiler.
    InternalDebug.RunOnMono = 
      CompilerInformation.cbRunMono.Checked;
    ....
  }
}
```

Подобные ошибки в коде:

* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'cun' object was used before it was verified against null\. Check lines: 400, 401\. Compiler PCUReader\.cs 400
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'cnfn\.ConnectedToType\.element\_type' object was used before it was verified against null\. Check lines: 2918, 2930\. Compiler PCUReader\.cs 2918
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The '\_currentTreeNode' object was used before it was verified against null\. Check lines: 590, 593\. TreeConverter CapturedVariablesTreeBuilder\.cs 590
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'Units' object was used before it was verified against null\. Check lines: 3031, 3073\. Compiler Compiler\.cs 3031
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'frm' object was used before it was verified against null\. Check lines: 2358, 2364\. NETGenerator NETGenerator\.cs 2358
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'InitalValue' object was used before it was verified against null\. Check lines: 2915, 2918\. NETGenerator NETGenerator\.cs 2915
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'InitalValue' object was used before it was verified against null\. Check lines: 2952, 2956\. NETGenerator NETGenerator\.cs 2952
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'InitalValue' object was used before it was verified against null\. Check lines: 3005, 3009\. NETGenerator NETGenerator\.cs 3005
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'InitalValue' object was used before it was verified against null\. Check lines: 3041, 3045\. NETGenerator NETGenerator\.cs 3041
* [V3095](https://pvs-studio.ru/ru/docs/warnings/v3095/) The 'InitalValue' object was used before it was verified against null\. Check lines: 3103, 3107\. NETGenerator NETGenerator\.cs 3103

Я привел только первые 10 подобных ошибок из более чем 40\.

**Бесконечная рекурсия: x2**

[V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'SetRange' method\. TreeConverter SymbolInfoArrayList\.cs 439

[V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'SetRange' method\. TreeConverter SymbolInfoArrayList\.cs 444

```cpp
public void SetRange(int index,SymbolInfo[] tnarr)
{
  SetRange(index,tnarr);
}

public void SetRange(int index,SymbolInfoArrayList tnarl)
{
  SetRange(index,tnarl);
}
```

Сразу два метода, реализующих бесконечную рекурсию\. Оба метода похожи и отличаются только типом второго аргумента\. Нигде в коде не используются\. По крайней мере пока не используются\.

Подобные ошибки в коде:

* [V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'node\_kind' property\. TreeConverter functions\.cs 2528
* [V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'node\_location\_kind' property\. TreeConverter functions\.cs 2590
* [V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'node\_kind' property\. TreeConverter functions\.cs 2693
* [V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'node\_location\_kind' property\. TreeConverter functions\.cs 2704
* [V3110](https://pvs-studio.ru/ru/docs/warnings/v3110/) Possible infinite recursion inside 'Instance' property\. ParserTools LanguageInformation\.cs 549

**Небрежная реализация метода Equals**

[V3115](https://pvs-studio.ru/ru/docs/warnings/v3115/) Passing 'null' to 'Equals' method should not result in 'NullReferenceException'\. ICSharpCode\.SharpDevelop ServiceReferenceMapFile\.cs 31

```cpp
public override bool Equals(object obj)
{
  var rhs = obj as ServiceReferenceMapFile;
  return FileName == rhs.FileName;  // <=
}
```

Автор данного фрагмента кода довольно халатно отнёсся к вопросам безопасности его работы\. Не хватает как минимум одной проверки на равенство _null_ переменной _rhs_ после её инициализации\. А чтобы вообще не делать лишней работы, необходима предварительная проверка на _null_ переменной _obj_:

```cpp
public override bool Equals(object obj)
{
  if (obj == null || !(obj is ServiceReferenceMapFile))
    return false;
  var rhs = obj as ServiceReferenceMapFile;
  return FileName == rhs.FileName;
}
```

**Недостаточно проверок**

[V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'resources' object was used after it was verified against null\. Check lines: 215, 211\. VisualPascalABCNET DesignerResourceService\.cs 215

```cpp
public System.Resources.IResourceReader
  GetResourceReader(System.Globalization.CultureInfo info)
{
  ....
  if (resources != null && resources.ContainsKey(info.Name)) {
    resourceStorage = resources[info.Name];
  } else {
    resourceStorage = new ResourceStorage();
    resources[info.Name] = resourceStorage;  // <=
  }
  ....
}
```

В коде присутствует проверка на равенство _null_ переменной _resources_, но этого недостаточно, так как блок _else_ такой проверки не содержит\. При определённом стечении обстоятельств это неминуемо приведет к доступу по нулевой ссылке\. Код необходимо откорректировать:

```cpp
public System.Resources.IResourceReader
  GetResourceReader(System.Globalization.CultureInfo info)
{
  ....
  if (resources != null) {
    if (resources.ContainsKey(info.Name)) {
      resourceStorage = resources[info.Name];
    } else {
      resourceStorage = new ResourceStorage();
      resources[info.Name] = resourceStorage;
    }
  }
  ....
}
```

Подобные ошибки в коде:

* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'this\.\_grid' object was used after it was verified against null\. Check lines: 751, 746\. VisualPascalABCNET TreeGridNode\.cs 751
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'this\.\_grid' object was used after it was verified against null\. Check lines: 774, 770\. VisualPascalABCNET TreeGridNode\.cs 774
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'node\.Parent' object was used after it was verified against null\. Check lines: 369, 350\. VisualPascalABCNET TreeGridView\.cs 369
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'CurrentCodeFileDocument' object was used after it was verified against null\. Check lines: 395, 384\. VisualPascalABCNET WindowOperations\.cs 395
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'value\.main\_function' object was used after it was verified against null\. Check lines: 948, 942\. LanguageConverter Visitor\.cs 948
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'left\.prim\_val' object was used after it was verified against null\. Check lines: 4711, 4699\. VisualPascalABCNET ExpressionEvaluation\.cs 4711
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'left\.obj\_val' object was used after it was verified against null\. Check lines: 4849, 4822\. VisualPascalABCNET ExpressionEvaluation\.cs 4849
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'to' object was used after it was verified against null\. Check lines: 335, 327\. TreeConverter CapturedVariablesTreeBuilder\.cs 335
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 'dii\_left' object was used after it was verified against null\. Check lines: 256, 254\. TreeConverter LambdaHelper\.cs 256
* [V3125](https://pvs-studio.ru/ru/docs/warnings/v3125/) The 't' object was used after it was verified against null\. Check lines: 23, 20\. TreeConverter semantic\_checks\_for\_sugar\.cs 23

Я привел только первые 10 подобных ошибок из более чем 80 \(восьмидесяти\!\)\.

**Ошибочный порядок инициализации**

[V3128](https://pvs-studio.ru/ru/docs/warnings/v3128/) The 'dockPanel' field is used before it is initialized in constructor\. ICSharpCode\.SharpDevelop SearchResultsPad\.cs 49

```cpp
....
DockPanel dockPanel;
....
public SearchResultsPad()
{
  ....
  defaultToolbarItems = ToolBarService.
    CreateToolBarItems(dockPanel, ....);  // <=
  foreach (object toolBarItem in defaultToolbarItems) {
    toolBar.Items.Add(toolBarItem);
  }
  ....
  dockPanel = new DockPanel {
    Children = { toolBar, contentPlaceholder }
  };
  ....
}
```

Поле _dockPanel_ сначала используют в конструкторе _SearchResultsPad_, а затем инициализируют\. При этом, даже если в методе _CreateToolBarItems_ или вложенных методах предусмотрено равенство _null _первого аргумента, метод, вероятно, вернет _null\. _Это, в свою очередь, приведет к дальнейшим ошибкам при использовании переменной _defaultToolbarItems_\.

## Статистика

Я вижу общую картину следующим образом\. Анализаторы SonarC\# и PVS\-Studio решают разные задачи\. SonarC\# предназначен для оценки и контроля качества кода\. Поэтому он сообщает как об ошибках, так и о "запахах" кода\. PVS\-Studio ориентирован на поиск ошибок или мест в коде, которые могут впоследствии привести к ошибкам\. Конечно, выдаваемые сообщения этих анализаторов частично пересекаются, но рассчитаны для разных потребностей:

* SonarC\# \- регулярный многофакторный анализ метрик и предупреждений, с целью контроля качества кода;
* PVS\-Studio \- позволяет в любой момент начать искать ошибки и тем самым улучшать качество кода\.

Приведу сводную таблицу по результатам проверки проекта PascalABC\.NET \(взяты уровни предупреждений Blocker, Critical и Major\):

![0492_PascalABCNET_ru/image34.png](https://import.viva64.com/docx/blog/0492_PascalABCNET_ru/image34.png)

Ещё раз хочу отметить, что анализаторы нельзя напрямую сравнивать по количеству найденных ошибок и количеству ложных срабатываний\. SonarC\# старается выдать предупреждение на код, который хотя и плох, но не содержит ошибки\. Это как раз и позволяет оценивать качество кода\. В свою очередь, анализатор PVS\-Studio в таком случае предпочитает промолчать или выдать предупреждение с минимальным уровнем достоверности\. При этом он старается выявить как можно больше ошибок и обучен выявлять большее количество дефектов, приводящих к сбоям в работе программы\.

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

Итак, как и ожидалось, при работе с плагинами PVS\-Studio и SonarC\# для SonarQube у меня не возникло никаких проблем\. Все функции и особенности инструментов задокументированы\. После загрузки результатов на сервер SonarQube вы получаете доступ к множеству функциональных возможностей для оценки качества своего программного продукта\. Что касается собственно поиска ошибок анализаторами исходного кода, то оба инструмента показали достойный результат\.

Для загрузки и анализа проекта онлайн на сайте [SonarQube](https://www.sonarqube.org/) вам потребуется минимум усилий и времени\. 

Использование плагина PVS\-Studio для интеграции результатов его работы в SonarQube также не вызывает трудностей\. Единственное ограничение \- вам понадобится [Enterprise версия](https://pvs-studio.ru/ru/order/license/) анализатора\. Если же у вас нет необходимости интеграции с SonarQube, вы можете использовать PVS\-Studio как независимый инструмент\.

[Скачать и попробовать PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/try-free/)\.

По вопросам приобретения коммерческой лицензии PVS\-Studio просим Вас [связаться](https://pvs-studio.ru/ru/about-feedback/) с нами в почте\. Вы также можете написать нам, чтобы получить временную лицензию для всестороннего изучения PVS\-Studio, если хотите снять [ограничения](https://pvs-studio.ru/ru/docs/manual/0009/) демонстрационной версии\.