В ближайшее время в инструмент Svace будет добавлена первая версия поддержки языка JavaScript.
В данной версии Svace реализует анализ JavaScript-программ на основе абстрактного синтаксического дерева, выполненном в компоненте UAST
.
По аналогии с языком Python, анализ которого поддерживается в Svace, для программ на JavaScript компиляция не требуется.
Поэтому на этапе сборки Svace сканирует директорию с исходным кодом, ищет файлы с расширением *.js
и строит для них промежуточное представление.
Svace поддерживает анализ JavaScript-программ, реализованных в соответствии с ES6. Также возможен частичный анализ файлов, использующих JSX. Анализ TypeScript и прочих расширений JavaScript не поддерживается.
Как и для других языков, для запуска анализа JavaScript необходимо инициализировать Svace, запустить сборку проекта и анализ.
В команде сборки нужно указать ключевое слово --javascript
и путь до файла или директории, содержащей файлы с исходным кодом:
svace init
svace build --javascript ${FILE_OR_DIRECTORY}
svace analyze
В настоящий момент для языка JavaScript поддерживаются следующие 18 детекторов:
INVARIANT_RESULT
— выражение всегда принимает константное значение вне зависимости от значений операндов (например, x === x
).SIMILAR_BRANCHES
— ветви условного или тернарного оператора являются одинаковыми.SIMILAR_BRANCHES.SWITCH
— ветви оператора switch
являются одинаковыми.DEFAULT_CASE_MISSING
— в операторе switch
отсутствует ветвь default
.CATCH.NO_BODY
— в catch
-блоке отсутствуют операторы.CATCH.EXCEPTION_ASSIGN
— присваивание значения в параметр-исключение, передаваемый в catch
-блок, что приводит к игнорированию исключения.BAD_COPY_PASTE
— частичный клон кода, в котором некоторые части кода могли быть случайно оставлены не изменёнными (например, не все использования одной переменной были заменены на другую).WRONG_ARGUMENTS_ORDER
— вызов функции с неправильным порядком аргументов.RETURN_IN_FINALLY
— использование оператора return
в finally
-блоке, что может привести к игнорированию исключения.INAPPROPRIATE_FOR_IN
— использование цикла for ... in
в тех случаях, когда уместнее было бы использовать for ... of
.INCORRECT_NAN_COMPARE
— проверка на значение NaN
через сравнение вида x == NaN
, а не вызов встроенной функции isNaN
.INCORRECT_TYPEOF_COMPARE
— сравнение результата typeof
с именем несуществующего типа.GETTER_WITHOUT_RETURN
— отсутствие оператора return
в геттере.SETTER_WITH_RETURN
— наличие оператора return
в сеттере.CONSTRUCTOR_WITH_RETURN
— наличие оператора return
в конструкторе.EMPTY_DESTRUCTURING_PATTERN
— пустой шаблон при деструктуризации.CALLBACK_WITHOUT_RETURN
— отсутствие возвращаемого значения в callback-функции, передаваемой в функцию высшего порядка, которая ожидает возвращаемое значение (например, передача void
-функции в Array.filter
).CALLBACK_WITH_RETURN
— наличие возвращаемого значения у callback-функции, передаваемой в функцию высшего порядка forEach
.На данном этапе отсутствует подсветка синтаксиса в сервере истории.
В коде проекта ESLint v9.0.0 в lib/rules/key-spacing.js
был обнаружен дефект BAD_COPY_PASTE
(см. код ниже).
Использование alignmentOptions.mode
внутри else
должно быть заменено на multiLineOptions.mode
.
В ином случае, при достижении данной строки возникнет TypeError
, т.к. alignmentOptions
будет иметь значение null
.
function verifyGroupAlignment(properties) {
const length = properties.length,
widths = properties.map(getKeyWidth),
align = alignmentOptions.on;
let targetWidth = Math.max(...widths),
beforeColon, afterColon, mode;
if (alignmentOptions && length > 1) {
beforeColon = alignmentOptions.beforeColon;
afterColon = alignmentOptions.afterColon;
mode = alignmentOptions.mode;
} else {
beforeColon = multiLineOptions.beforeColon;
afterColon = multiLineOptions.afterColon;
mode = alignmentOptions.mode; // BAD_COPY_PASTE: 'alignmentOptions' might be 'multiLineOptions'
}
...
}
В коде проекта ESLint v9.0.0 в lib/eslint/legacy-eslint.js
был обнаружен дефект INAPPROPRIATE_FOR_IN
(см. код ниже).
Использование цикла for ... in
в данном случае бессмысленно, т.к. используются только значения свойств объекта, но не ключи. Стоит заменить данный цикл на for ... of
.
function hasDefinedProperty(obj) {
if (typeof obj === "object" && obj !== null) {
for (const key in obj) { // INAPPROPRIATE_FOR_IN: 'for ... in' can be replaced with 'for ... of'
if (typeof obj[key] !== "undefined") {
return true;
}
}
}
return false;
}
В коде проекта Ghost v5.81.0 в ghost/members-api/lib/repositories/ProductRepository.js
был обнаружен дефект BAD_COPY_PASTE
(см. код ниже).
Судя по логике кода, использование data.monthly_price
во втором условии должно быть заменено data.yearly_price
.
async create(data, options = {}) {
...
if (data.monthly_price) {
validatePrice(data.monthly_price);
}
if (data.yearly_price) {
validatePrice(data.monthly_price); // BAD_COPY_PASTE: 'monthly_price' might be 'yearly_price'
}
...
}
В коде проекта video.js v8.11.8 в docs/legacy-docs/js/guides.js
был обнаружен дефект INCORRECT_NAN_COMPARE
(см. код ниже).
В соответствии cо стандартом IEEE 754, проверка x === NaN
всегда будет возвращать false
, даже если x
принимает значение NaN
.
Необходимо заменить данную проверку на вызов встроенной функции isNaN(x)
.
function isDefined (x){
if ( x === "" || x === null || x === undefined || x === NaN) { // INCORRECT_NAN_COMPARE
return false;
}
return true;
}
В коде проекта TheAlgorithms/JavaScript в Timing-Functions/IntervalTimer.js
был обнаружен дефект CONSTRUCTOR_WITH_RETURN
(см. код ниже).
В соответствии с комментарием, данный класс должен быть singleton’ом.
Но использование оператора return
в конструкторе в данном случае никак не влияет на то, что будет из него возвращено - вызов new IntervalTimer()
всегда будет возвращать новый объект.
Обсуждение данного бага в реализации можно найти в комментарии на GitHub.
/**
* @description Singleton class that handles the timing of tests and specs.
*/
class IntervalTimer {
/**
* @description Constructor for Timer.
* @param interval Sets the interval for running the timer.
* @param callBack The callback function to be executed.
* @return {IntervalTimer} If exists, the existing object.
*/
constructor(interval = 10, callBack = () => {}) {
this.prevInterval = 0
if (this.instance == null) {
this.interval = interval
this.callBack = callBack
this.instance = this
} else {
return this.instance // CONSTRUCTOR_WITH_RETURN
}
}
}