Let’s look at an example. It makes no sense to use a strict equality operator like === or !== on two operands which don’t have the same type: in such cases, === always returns false and !== always returns true. We have a rule to check that and this rule found the following issue in JQuery:
In this case, we know that “type” is either a string or undefined when it is compared to the boolean value false with a strict equality operator. This condition is therefore useless, and such a comparison is certainly a bug.
Of course, we can go further. SonarJS embeds some knowledge about built-in objects and their properties and methods. We added a new rule “Non-existent properties shouldn't be accessed for reading” which is based on that knowledge. It detects issues which could be due to a typo in the name of the property or to a mistake about the type of the variable, such as the following issue which was found in the OpenCart project:
This piece of code confuses two of its variables: “number” and “s”. The first one is a number and the second is a string representation of the first. The “length” property therefore exists on “s”, but is undefined on “number”. As a result, this function does not return what it’s supposed to.
We know that “methodArgs” may be an array: when it is, comparing it to a number doesn’t make sense and that’s what the rule detects. The author of this code probably intended to use methodArgs.length in the comparison.
How can SonarJS catch such mistakes? Briefly, we rely on path-sensitive dataflow analysis: as we explained a few months ago, our analyzer can explore the various execution paths of a function and the possible constraints on the variables. In the last few versions, we improved our engine so that it tracks the types of the variables. We derive type information based on indicators in the code such as:
- Literals, e.g. 42 is a number,  is an array.
- Operators, e.g. the result of a + can be either a number (addition) or a string (concatenation).
- typeof expressions.
- Calls to built-in functions, e.g. we know that a call to Number.isNaN returns a boolean value.
That not only allowed us to implement the rules I just described, it also improved existing rules not directly related to types. The rule which checks for conditions which are always true or false is now able to find new issues such as the following one in the YUI project:
This piece of code tests whether “config” is a function twice. However, it’s re-assigned to null if the first test returns true. We therefore know for sure that the second test will return false. This rule doesn’t specifically check the types of the variables, but it is based on all the constraints we’ve derived on the variables and type is one of them.