February 2018 (version 1.11.0-beta)

1.11.0 Update

Hello!

We've released a 1.11.0 DeepScan service.

Please kindly go ahead with the highlights for this release.

Release Summary

This version includes a number of updates that we hope you’ve found some of them helpful. The key highlights are:

New Rules

To date, we have been hard at work detecting more code issues and providing much more rules.

Improved Rules

INSUFFICIENT_NULL_CHECK
  • Detect alarms on property accesses at in operator and object destructuring
  • Detect alarms on global variables
SAME_OPERAND_VALUE
  • Filter alarms on the opposite condition checking at else branch for code explanation purpose
UNUSED_DECL
  • Filter alarms on object destructuring with rest property because the intention is to exclude some properties

Analysis Improvements

Enhanced support for the async function analysis

The return value of an async function is guaranteed to be a Promise object. We took this into account and it enabled us to detect the following alarm in an open source project:

class AtomBackend {
  async start (rootPath, eventCallback, errorCallback) {
    const isOpenInEditor = async eventPath => {
      const openPaths = await Promise.all(
        atom.workspace.getTextEditors().map(editor => getRealPath(editor.getPath()))
      )
      return openPaths.includes(eventPath)
    }

    this.subs.add(treeView.onEntryDeleted(async event => {
      const realPath = await getRealPath(event.path)
      if (!realPath || isOpenInEditor(realPath)) return // Condition 'isOpenInEditor(realPath)' is always true because it evaluates to a 'Promise' object. The object is originated from the return value of the function defined at line 3.

      eventCallback([{action: 'deleted', path: realPath}])
    }))
  }
}

The async function isOpenInEditor returns a boolean value wrapped in a Promise object. Checking the Promise object itself always succeeds and it would not be the programmer's intention.

It is highly likely that await was missing when calling the function isOpenInEditor.

Improved precision for the string value analysis

In JavaScript, a string value is treated as a truthy value when it is not an empty string. We have reflected this fact as precise as possible and the following alarm is detected as a result:

export default class IconElement extends BaseElement {
  _cleanClassAttribute(lastIcon, lastModifier) {
    const { className, prefix } = this._prefixIcon(this._parseAttr(lastIcon, lastModifier));
    const customPrefixRE = className !== prefix ? `|${prefix}$|${prefix}-` : `|${className}$` || ''; // Condition '`|${className}$`' is always true because it evaluates to a non-empty string.
    const re = new RegExp(`^(fa$|fa-|ion-|zmdi$|zmdi-|ons-icon--${customPrefixRE})`);

    util.arrayFrom(this.classList)
      .filter(className => re.test(className))
      .forEach(className => this.classList.remove(className));
  }
}

In the above code, the template literal `|${className}$` is non-empty because it starts with |. It seems that the intention was checking className variable instead of the template literal itself.

The consequence is that when className is empty, the whole regular expression re will match an empty string.

Deeper data-flow on outer-scope variable

In addition to applying data-flow analysis technique for analyzing local variables, we have applied the technique for outer-scope variables. With this improvement, you can check a defect like the following:

(function() {
    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
    var isIE = !!document.documentMode;

    var isPluginRTC = isSafari || isIE;

    function getSTUNObj(stunStr) {
        var urlsParam = 'urls';
        if (typeof isPluginRTC !== 'undefined') { // Condition 'typeof isPluginRTC !== 'undefined'' is always true at this point. The value of variable 'isPluginRTC' is originated from the assignment 'isPluginRTC = isSafari || isIE' at line 5.
            urlsParam = 'url';
        }

        var obj = {};
        obj[urlsParam] = stunStr;
        return obj;
    }
})();

When analyzing getSTUNObj function, the analyzer knows that the outer isPluginRTC variable has a boolean value at line 5, so ultimately the return value of typeof cannot be 'undefined'.

Here, what was intended would be checking the boolean value isPluginRTC itself.

New Badge Token

Since the last release on January, we prohibited anonymous users to see the badge for private repositories. By this change, you could not see the badge added in your GitHub private repo when you are not logged-in as our service.

Now we introduced some tokens in the badge URL to resolve this. Please do the following when you have a problem with the badge:

  1. Go to the Project Dashboard for your private repository
  2. In Overview, copy a badge Markdown or HTML on the right
  3. Use it or replace it with an old one

Note: Users with some tokens can see the badge. So be attentive to the share of the URL with anyone who you don’t want to see the status of your private project.

Miscellaneous

  • Visual Studio Code extension supports an option 'deepscan.fileSuffixes' to set the file suffixes to analyze. For example, you can use this to analyze .mjs files. (See #2)

Bug Fixes

  • Unicode characters or emojis are broken in the file viewer
  • Cause location is wrong for some INSUFFICIENT_NULL_CHECK alarms

Thank You

Thank you to the following folks who help to make DeepScan better:

  • Ishant Solanki: REACT_USELESS_PROP_TYPES has a false alarm for the object destructuring pattern (See #4)
  • Josh Pike: Visual Studio Code extension does not work for .mjs files (See #2)
  • Ole Turvoll: Inspiration for the new badge token
  • shaklev: A false alarm for UNUSED_IMPORT can occur for extends keyword in class declarations e.g., class A extends B<FooType>