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 - New rules for common pitfalls.
- Analysis improvements - Enhance the analysis for the async functions, string values, and outer-scope variables.
- New badge token - Token support for the badge of private projects.
New Rules
To date, we have been hard at work detecting more code issues and providing much more rules.
- CALL_REQUIRE_AS_CONSTRUCTOR - Do not call
require()
as a constructor - GETTER_SETTER_RECURSION - Avoid an infinite call in getter or setter functions
- MULTIPLE_SUPER_CALL - Do not call
super()
multiple times - SETTER_RETURN_VALUE - Do not return a value in the setter functions
- USELESS_ARROW_FUNC_BIND - Do not bind the arrow function explicitly
Improved Rules
INSUFFICIENT_NULL_CHECK- Detect alarms on property accesses at
in
operator and object destructuring - Detect alarms on global variables
- Filter alarms on the opposite condition checking at else branch for code explanation purpose
- 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:
- Go to the Project Dashboard for your private repository
- In Overview, copy a badge Markdown or HTML on the right
- 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>