Анализ Scala

Мы добавили в Svace поддержку для языка Scala.

Svace уже поддерживал статический анализ для двух языков, использующих байт-код JVM в качестве промежуточного представления: Java и Kotlin. Программы на языке Scala, как и программы на Java и Kotlin, могут компилироваться в байт-код JVM. Таким образом, поддержка данного языка в Svace значительно упрощается.

В отличие от Java и Kotlin язык Scala является менее популярным. Согласно индексу TIOBE в настоящее время по популярности язык Scala находится на 36м месте. Поэтому мы не добавляли поддержку языка Scala в той же мере, как мы сделали для Java и Kotlin. Ключевое отличие заключается в том, что мы не осуществляли модификацию компилятора Scala для улучшения анализа и не добавляли компиляторы Scala в дистрибутив Svace.

Запуск

Как и для других языков, для запуска анализа проекта на языке Scala необходимо инициализировать Svace, запустить сборку и анализ.

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

svace init
svace build sbt clean compile
svace analyze

Так как Svace анализирует проект только на основе перехваченных компиляций, важно, чтобы сборка происходила с нуля. Для этого можно предварительно воспользоваться командами “sbt clean”, “mvn clean” или удалить все скомпилированные файлы вручную.

Поддерживаемые версии

Мы добавили поддержку для систем сборки SBT и Maven, а также одиночных компиляций. Поддерживаемые версии языка:

Внутреннее устройство

По аналогии с Java и Kotlin, для перехвата сборки программ на Scala мы используем Java-agent, который подключается к системе сборки и/или компилятору и инструментирует их код необходимым для сбора информации о компиляции образом. Но в отличие от Java и Kotlin, для которых в Svace присутствуют модифицированные компиляторы, для языка Scala мы используем байт-код, полученный оригинальным компилятором.

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

Такой подход сильно упрощает поддержку данного языка, так как не приходится дополнительно поддерживать кодовую базу для компиляторов этого языка. Для Scala это наиболее критично, так как для этого языка существуют сразу два невзаимозаменяемых компилятора: scalac для Scala 2 и dotty для Scala 3.

Таким образом Svace перехватывает вызовы компилятора Scala, сохраняет байт-код, сгенерированный компилятором и использует его для поиска ошибок. При этом запускается тот же самый анализатор, что и для языков Java и Kotlin.

Ограничения

Клиент-серверный режим сборки SBT

Система сборки SBT имеет функционал для работы в клиент-серверном режиме (как при помощи опций самого SBT, так и с помощью нативного клиента). В данном режиме клиентский процесс отправляет серверному процессу команды, а сервер их исполняет. Таким образом, процесс компиляции инициируется серверным процессом. При этом клиентский и серверный процесс могут быть неродственными.

Так как Svace перехватывает только те процессы компиляции, которые будут запущены исходной командой сборки и её дочерними процессами, то перехват сборки для клиент-серверного режима не поддерживается. Таким образом, если вы используете “/bin/sbtn”, опции “--client”, “--java-client”, “-Dsbt.client=true” или переменную окружения “SBT_NATIVE_CLIENT=true”, то все компиляции никак не будут перехватываться Svace и, следовательно, проект не будет проанализирован. Поэтому рекомендуется использовать команду сборки вида

SBT_NATIVE_CLIENT=false sbt --no-server clean compile

чтобы SBT гарантированно не использовал данный режим.

Межъязыковой анализ

На данный момент, межязыковый анализ (т.е анализ для ситуаций, когда код на одном языке вызывает код на другом языке) для Scala+Java и Scala+Kotlin не поддерживается. В случае интереса к анализатору Scala мы добавим этот режим в следующих обновлениях.

UAST

Мы не модифицировали компилятор языка и не создавали дополнительных плагинов к компилятору, поэтому представление для UAST-анализатора не генерируется и этот анализ не запускается.

Отсутствие подсветки синтаксиса

Ещё одним минусом будет отсутствие подсветки синтаксиса в сервере истории.

Заключение

Несмотря на вышеописанные ограничения, большая часть функциональности анализатора доступна для языка Scala. Для языка доступно более 50 детекторов.

Ниже приведён пример срабатывания детектора HANDLE_LEAK для проекта apache/incubator-streampark в streampark-common/src/main/scala/org/apache/streampark/common/util/FileUtils.scala:

  def equals(file1: File, file2: File): Boolean = {
    (file1, file2) match {
      case (a, b) if a == null || b == null => false
      case (a, b) if !a.exists() || !b.exists() => false
      case (a, b) if a.getAbsolutePath == b.getAbsolutePath => true
      case (a, b) =>
        // Input stream `first` is created
        val first = new BufferedInputStream(new FileInputStream(a))
        // Input stream `second` is created
        val second = new BufferedInputStream(new FileInputStream(b))
        if (first.available() != second.available()) false; // Streams `first` and `second` are not closed
        else {
          while (true) {
            val firRead = first.read()
            val secRead = second.read()
            if (firRead != secRead) {
              Utils.close(first, second)
              return false
            }
            if (firRead == -1) {
              Utils.close(first, second)
              return true;
            }
          }
          true
        }
    }
  }

Включение языка Scala является экспериментом по интеграции языков без модификации компилятора. В связи с этим возможно худшее качество анализа.

Для анализа Scala мы используем тот же движок, что и для анализа Java и Kotlin. Поэтому при улучшении анализа Java и Kotlin, изменения будут также доступны для Scala.


Виталий Афанасьев