﻿# Расширение PVS\-Studio для Visual Studio Code: поиск ошибок в Java\-коде

Java\-разработчик и предпочитаешь работать в VS Code? Для тебя есть хорошая новость\! Теперь ты можешь писать ещё более надёжный код вместе с расширением PVS\-Studio, которое помогает находить ошибки в Java\-проектах и не только\. 

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

## Введение

Анализатор PVS\-Studio — это инструмент для автоматического поиска потенциальных ошибок и угроз безопасности в C, C\+\+, C\# и Java коде\. Он состоит из нескольких компонентов:

1. ядра, выполняющего анализ кода;
1. плагинов для различных IDE — интерфейсов, значительно облегчающих взаимодействие пользователя с ядром и обработку результатов анализа\. 

Один из таких плагинов — PVS\-Studio для VS Code\. Недавно он получил крупное обновление, и теперь с помощью расширения можно запускать анализ Java\-проектов\.

В честь этого события я хотел бы продемонстрировать вам принцип работы с ним, как уже делал в [одной из своих предыдущих статей](https://habr.com/ru/companies/pvs-studio/articles/772870/)\. Но теперь наша цель — найти ошибки не в C\#, а в Java\-проекте\. Поехали\!

## Установка анализатора

Для начала нужно установить ядро Java\-анализатора и расширение для VS\. Небольшую [инструкцию](https://pvs-studio.ru/ru/blog/posts/1030/) по установке вы можете найти в одной из моих предыдущих статей\.

Также важно отметить, что для анализа Java\-проектов расширению PVS\-Studio требуется, чтобы в VS Code были установлены стандартные Java\-расширения: [Language Support for Java\(TM\) by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java) и [Project Manager for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-dependency)\.

Обратите внимание, что для автоматического обнаружения ядра расширением его стоит установить в директорию по умолчанию для вашей операционной системы\. Вы также можете установить ядро в произвольную директорию, при этом указав путь к нему в настройках VS Code \(**File \-\> Preferences \-\> Settings \-\> PVS\-Studio: Java Projects Analyzer Path**\)\.

Для работы анализатора требуется JDK 11\-19 версии\. Если вы используете другую, то можете скачать нужный JDK и указать путь к его исполняемому файлу в соответствующей настройке \(**File \-\> Preferences \-\> Settings \-\> PVS\-Studio: Java For Running Analyzer**\)\.

## Запуск анализа проекта

Откройте проект и дождитесь, пока его структура загрузится в разделе инспектора **Java projects**:

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

Если ваш проект включает в себя проекты или папки, расположенные вне открытой директории, выполните команду "Java: Reload Projects"\. После этого должен появиться список со всеми включёнными проектами\. Отметьте те, которые должны участвовать в анализе\.

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

Теперь можно смело запускать анализ\. Это можно сделать разными способами в зависимости от того, хотите ли вы проанализировать весь проект, его отдельные файлы или папки\. Для запуска полного анализа можно кликнуть по соответствующей кнопке во вкладке **PVS\-Studio** нижней панели VS Code\.

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

При первом анализе перед вами появится оповещение о создании файла, в котором можно указать некоторые необязательные параметры анализа, аналогичные [параметрам консольной версии анализатора](https://pvs-studio.ru/ru/docs/manual/6703/)\.

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

Клик по кнопке **Edit** откроет в области редактора этот файл, при этом анализ не начнётся\. При клике по **Continue** он запустится без параметров\.

Не стоит пугаться, если прогресс анализа какое\-то время висит на 0\. Как правило, это происходит по следующим причинам:

1. Подготовка к анализу на стороне расширения\. После открытия директории с проектом расширение в фоновом режиме начинает собирать всю необходимую информацию для анализа\. Если начать его до завершения процесса — запуск отложится\. Стоит отметить, что все последующие запуски анализа будут выполняться быстрее;
1. Подготовка анализа на стороне анализатора\. Этот этап включает, к примеру, эвалюацию проекта, построение или обновление его модели, что тоже может занять некоторое время\.

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

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

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

## Поиск ошибок в коде

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

* **первый уровень** \(красный\) — эти предупреждения не обязательно могут быть самыми критичными, однако они наиболее точные;
* **второй уровень** \(оранжевый\) — часто предупреждения этого уровня указывают на не самые простые и интересные ошибки, однако и обеспечить точность их нахождения сложнее;
* **третий уровень** \(жёлтый\) — предупреждения этого уровня чаще всего бывают спорными или незначительными\. Рекомендуется обращать на них внимание в последнюю очередь, но и полностью игнорировать не стоит\. Среди них также могут оказаться несколько предупреждений, указывающих на ошибки в вашем коде\.

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

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

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

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

Или укажите путь в соответствующей вкладке настроек \(кнопка с шестерёнкой\):

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

Проанализировав проект [Apache Hive](https://github.com/apache/hive) и проверив часть предупреждений из отчёта, мне удалось найти несколько потенциальных ошибок\.

### Ошибка при битовом смещении

```cpp
public void logSargResult(int stripeIx, boolean[] rgsToRead)
{
  ....
  for (int i = 0, valOffset = 0; i < elements; ++i, valOffset += 64) {
    long val = 0;
    for (int j = 0; j < 64; ++j) {
      int ix = valOffset + j;
      if (rgsToRead.length == ix) break;
      if (!rgsToRead[ix]) continue;
      val = val | (1 << j);                // <=
    }
    ....
  }
  ....
}
```

**Предупреждение**: [V6034](https://pvs-studio.ru/ru/docs/warnings/v6034/)\. Shift by the value of 'j' could be inconsistent with the size of type: 'j' \= \[0 \.\. 63\]\. IoTrace\.java: 264\.



Обратите внимание на выражение _1 << j_, результат которого используется при вычислении нового значения переменной _val_\. В этом выражении выполняется побитовое смещение единицы на _j_ битов влево\. Из определения цикла становится понятно, что _j_ может иметь значения в диапазоне от 0 до 64\. Однако числовой литерал по умолчанию имеет тип _int_, который ограничен 32 битами\. 

При смещении этого значения на 32 бита и более вместо расширения этого предела до 64 бит произойдёт зацикливание смещения\. Так, при смещении единицы на 32 бита будет снова получена 1, при смещении на 33 бита — 2 и т\. д\.



### Когда разница между побитовым и логическим ИЛИ имеет значение

```cpp
public static Operator<? extends OperatorDesc> findSourceRS(....) 
{
  ....
  List<Operator<? extends OperatorDesc>> parents = ....;
  if (parents == null | parents.isEmpty()) {
    // reached end e.g. TS operator
    return null;
  }
  ....
}
```

**Предупреждение**: [V6030](https://pvs-studio.ru/ru/docs/warnings/v6030/)\. The method located to the right of the '\|' operator will be called regardless of the value of the left operand\. Perhaps, it is better to use '\|\|'\. OperatorUtils\.java:555\.

Здесь мы имеем потенциальную возможность поймать _NullPointerException_ в выражении _parents\.isEmpty\(\)_\. Дело в том, что в условном выражении был использован побитовый оператор _ИЛИ_ вместо логического\. В результате выражение _parents\.isEmpty\(\)_ будет выполняться независимо от того, имеет ли _parents_ значение _null_ или нет\.

### Небрежный copy\-paste

**Проблема 1**

```cpp
private void generateDateTimeArithmeticIntervalYearMonth(String[] tdesc) 
throws Exception
{
  ....
  String colOrScalar1 = tdesc[4];
  ....
  String colOrScalar2 = tdesc[6];
  ....
  if (colOrScalar1.equals("Col") && 
      colOrScalar1.equals("Column"))    // <=
  {
    ....
  } else if (colOrScalar1.equals("Col") && 
 colOrScalar1.equals("Scalar")) // <=
  {
    ....
  } else if (colOrScalar1.equals("Scalar") && 
             colOrScalar1.equals("Column"))  // <=
  {
    ....
  }
}
```

**Предупреждения**:

* [V6007](https://pvs-studio.ru/ru/docs/warnings/v6007/)\. Expression 'colOrScalar1\.equals\("Column"\)' is always false\. GenVectorCode\.java:3543\.
* V6007\. Expression 'colOrScalar1\.equals\("Scalar"\)' is always false\. GenVectorCode\.java:3550\.
* V6007\. Expression 'colOrScalar1\.equals\("Column"\)' is always false\. GenVectorCode\.java:3561\.

В этом примере в каждом условном выражении переменная _colOrScalar1_ дважды сравнивается с разными значениями через условной оператор _&&\._ В результате этого ни одно из условий никогда не будет истинным\.

**Проблема 2**

```cpp
@Override
public List<LlapServiceInstance> getAllInstancesOrdered(....) {
  ....
  Collections.sort(list, new Comparator<LlapServiceInstance>() {
    @Override
    public int compare(LlapServiceInstance o1, LlapServiceInstance o2) {
      return o2.getWorkerIdentity().compareTo(o2.getWorkerIdentity()); // <=
    }
  });
  ....
}
```

**Предупреждение**: [V6009](https://pvs-studio.ru/ru/docs/warnings/v6009/)\. Function 'compareTo' receives an odd argument\. An object 'o2\.getWorkerIdentity\(\)' is used as an argument to its own method\. LlapFixedRegistryImpl\.java:260\.

А в этом случае метод _compare_ вместо сравнения двух разных объектов сравнивает второй объект с самим собой\.

## Подавление предупреждений

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

Для этого нужно выделить их в таблице \(с помощью комбинации клавиш Ctrl \+ A можно выделить сразу все\), открыть контекстное меню и выбрать опцию **Mark as a False Alarm** или **Add selected messages to suppression file**\. Обратите внимание, применить последнюю функцию сразу ко всем предупреждениям или только к той части, что не была отфильтрована, можно с помощью кнопки в виде молнии в верхнем правом углу окна PVS\-Studio\.

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

Первая функция отличается от второй тем, что подавляет предупреждение путём добавления комментария вида _//\-V\[Код предупреждения\]_ в первую строку кода, на который указывает предупреждение\. Вторая функция, в свою очередь, сохраняет информацию о подавленных предупреждениях в специальном файле\.

Стоит отметить, что для подавления ненужных срабатываний предпочтительно использовать функцию **Mark as a False Alarm**\.

Функция добавления предупреждений в _suppress_\-файл является удобным способом отложить технический долг на потом и сосредоточиться на качестве нового кода\.

В этом случае стандартный сценарий использования функции следующий:

1. Выполняется общий анализ проекта;
1. Полученные предупреждения подавляются, после чего они уже не будут присутствовать в результатах последующих анализов;
1. Позже подавленные предупреждения просматриваются разработчиком\. Чтобы вернуть их, нужно отредактировать или удалить файл подавления \(по умолчанию называемый _suppress\_base\.json_\), который можно найти в папке _\.PVS\-Studio_ в директории решения\.

Подробнее эта тема рассматривается в статье "[Как внедрить статический анализатор кода в legacy проект и не демотивировать команду](https://pvs-studio.ru/ru/blog/posts/0743/)"\.

## Обнаружение потенциальных проблем с совместимостью между разными версиями Java SE

Если в будущем вы планируете миграцию вашего проекта на более свежую версию Java, стоит следить за тем, чтобы уже сейчас не завязываться на API, которое будет удалено в целевом выпуске\. В этом вам поможет специальная диагностика [V6078](https://pvs-studio.ru/ru/docs/warnings/v6078/)\. При работе с расширением VS Code PVS\-Studio эту диагностику можно включить в файле _JavaAnalyzerConfig\.jsonc_ в папке _\.PVS\-Studio_ рабочей директории следующим образом:

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

Здесь указываются следующие аргументы:

1. _compatibility_ — активация диагностики [V6078](https://pvs-studio.ru/ru/docs/warnings/v6078/);
1. _source\-java_ — номер версии JDK, на котором ваш проект работает в данный момент;
1. _target\-java_ — номер версии целевого JDK\.

## Известные проблемы

При тестировании на крупном проекте [Elasticsearch](https://github.com/elastic/elasticsearch) \(более 18000 файлов с кодом\) мы обнаружили, что работа расширения [Project Manager for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-dependency) может привести к сильному перерасходу оперативной памяти, что в конечном счёте приводит к падению окна VS Code\. Если у вас есть идеи, как можно оптимизировать работу этого расширения в подобных случаях, мы будем рады получить советы в комментариях\.

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

На этом наш небольшой обзор подошёл к концу\. Надеюсь, у меня получилось показать вам, что расширение PVS\-Studio для VS Code — это хороший инструмент, который стоит использовать в своей работе\.

Чистого кода и успешных проектов\! До встречи в следующих статьях\!