DeepScan vs ESLint

Semantic analysis at your hand

JavaScript has become the most popular language, but static analysis for it has not been around because of the dynamic features of JavaScript.

We have researched JavaScript code analysis for years, and now released "DeepScan". "Deep" means our aim for "Beyond lint".

Beyond Lint

DeepScan comes to semantic analysis that other lint tools can't.

analyzer

Linter tools like ESLint provide some useful checks by pattern-matching on AST (Abstract Syntax Tree). But, because it does not know the flow of program, it can detect some stylistic issues or only stereotyped errors.

DeepScan uses CFG (Control Flow Graph), and it can follow the execution and data flow of program. So it can detect more useful errors than linter tools do.

Let's see some examples:

What value the variable has

DeepScan tracks possible values of variable at each program point while ESLint can't.

Below code is for destroying an object, but it does not work recursively because type is "undefined" by uninitialized variable i.

var destroyObject = function (object) {
    var type = Object.prototype.toString.call(object[i]); // Use of uninitialized variable
    for (var i in object) {
        if (type === '[object Object]' || type === '[object Array]') {
            destroyObject(object[i]);
        }
        object[i] = null;
        delete object[i];
    }
}
DeepScan detects object[i] as a defect: Variable i is uninitialized. This is possible because DeepScan tracks the status and value of variable i in its scope.

More compact example?

function isStringType(obj) {
    var type = 'stringg'; // Mis-typed string
    if (typeof obj === type) {
        return true;
    }
    return false;
}
ESLint supports valid-typeof check, but it can't detect above case because it does not know the value of type.

Control flow

By return statement, the loop in the below code is stopped interruptedly at the first iteration.

_checkCacheExpire : function(sKey) {
    if (sKey) {
        for (var i = 0, n = this._waKeyList.length(); i < n; i++) {
            return this._checkCacheExpire(this._waKeyList.get(i));
        }
    }
}
ESLint supports no-unreachable check, but it can't detect above case because it does not know the control flow (for in the above) of the program. It detects only the stereotyped case like:
for (var i = 0, n = this._waKeyList.length(); i < n; i++) {
    return this._checkCacheExpire(this._waKeyList.get(i));
    console.log('Something to do'); // statement after 'return'
}

Program execution

DeepScan understands program execution while ESLint can't.

In the below, fromArgs variable is checked in the first if statement. But it is checked again in the body of else if statement, which must be false. So contrast to programmer's intent, code guarded by the second check is never executed.

function foo(fromArgs) {
    // ...

    if (fromArgs) {
        // setting date by clicking
        this.setValue();
        this.element.change();
    } else if (this.dates.length) {
        // setting date by typing
        // DeepScan: Condition 'fromArgs' is always false because it is redundant.
        // ESLint: (no detection because it has no knowledge about 'fromArgs' at this point)
        if (String(oldDates) !== String(this.dates) && fromArgs) {
            // DeepScan: (understands that this code is never executed)
            this._trigger('changeDate');
            this.element.change();
        }
    }
}
ESLint supports no-constant-condition, but it detects only the stereotyped case like:
if (false) {
    // do something
}