-
Notifications
You must be signed in to change notification settings - Fork 885
Conversation
Thanks for your interest in palantir/tslint, @subash-a! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request. |
67c6d3a
to
6743e25
Compare
Looks great to me 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good overall. Good tests. Needs some minor changes
@@ -0,0 +1,116 @@ | |||
import * as ts from "typescript"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
license, metadata
|
||
class StrictBooleanExpressionsRule extends Lint.ProgramAwareRuleWalker { | ||
public visitBinaryExpression(node: ts.BinaryExpression): void { | ||
let checker = this.getTypeChecker(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
call this once. move to constructor
public visitBinaryExpression(node: ts.BinaryExpression): void { | ||
let checker = this.getTypeChecker(); | ||
// check all Boolean and BooleanLiteral expressions for validity | ||
let nodeType = checker.getTypeAtLocation(node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this and as much logic as possible inside of if
statement for perf
} | ||
|
||
class StrictBooleanExpressionsRule extends Lint.ProgramAwareRuleWalker { | ||
public visitBinaryExpression(node: ts.BinaryExpression): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we typically don't explicitly write the void
return type
} | ||
|
||
private isBooleanType(btype: ts.Type): boolean { | ||
let boolType = /* tslint:disable:no-bitwise */(btype.flags & ts.TypeFlags.BooleanLike)/* tslint:enable:no-bitwise */; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use utils.isNodeFlagSet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nchen63 this would not work because I am checking the type flags and not the node flags, there are two options available
- I can leave this line as is
- I can introduce a new function
isTypeFlagsSet
intolanguage/utils.ts
which can be used to check the type flags here
which option do you suggest I go with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add this to utils
|
||
private isBooleanType(btype: ts.Type): boolean { | ||
let boolType = /* tslint:disable:no-bitwise */(btype.flags & ts.TypeFlags.BooleanLike)/* tslint:enable:no-bitwise */; | ||
if (boolType === ts.TypeFlags.Boolean || boolType === ts.TypeFlags.BooleanLiteral || boolType === ts.TypeFlags.BooleanLike) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't need if
statement, just return the condition
@@ -0,0 +1,184 @@ | |||
/* tslint:disable:no-unused-parameters */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't need these disable
comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also this is not a tslint rule
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.BINARY_EXPRESSION_ERROR)); | ||
} | ||
} | ||
super.visitBinaryExpression(node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't call super.visitBinaryExpression
if a failure is detected at this node. Seems excessive to flag true && 1 && 1
twice since errors overlap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with other super
calls below
export class Rule extends Lint.Rules.TypedRule { | ||
public static BINARY_EXPRESSION_ERROR = "Invalid Expression: Operands for the && or || operator should be of type boolean"; | ||
public static UNARY_EXPRESSION_ERROR = "Invalid Expression: Operand for the ! operator should be of type boolean"; | ||
public static IF_STATEMENT_ERROR = "Invalid Expression: If statement condition needs to be a boolean expression or literal"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine lines 7 to 11 into Statement is not a boolean expression
. After that, combine some of the checker logic below to reduce code duplication.
import * as Lint from "../index"; | ||
|
||
export class Rule extends Lint.Rules.TypedRule { | ||
public static BINARY_EXPRESSION_ERROR = "Invalid Expression: Operands for the && or || operator should be of type boolean"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove Invalid Expression:
6743e25
to
b1a04bb
Compare
@nchen63 I have made all the changes suggested, but one of the comments had a problem with the implementation for which I have left you a note, will make changes based on your answer there. Thanks for the quick and detailed review |
b1a04bb
to
47072f1
Compare
public visitIfStatement(node: ts.IfStatement) { | ||
let hasError = this.checkStatement(node); | ||
if (!hasError) { | ||
super.visitIfStatement(node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this skip the block/body of the if
statement?
if (!this.isBooleanType(expType)) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.IfStatement: | ||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `If ${Rule.STATEMENT_ERROR}`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't necessarily need different error message for each type of conditional, but this works
let rhsExpression = node.right; | ||
let rhsType = this.checker.getTypeAtLocation(rhsExpression); | ||
if (!this.isBooleanType(lhsType) || !this.isBooleanType(rhsType)) { | ||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.BINARY_EXPRESSION_ERROR)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failure should underline the offending operand, not the entire statement
6c73667
to
815912a
Compare
@nchen63 changed the code to point out only the offending expression and also changed the logic of checking statements even after error has been flagged to find further errors in the statement block. |
@nchen63 However I had a question about the |
we have rules like this that don't call super. See |
Rules are evaluated individually in isolation, so omitting a |
/*** If Statement ***/ | ||
/*** Invalid ***/ | ||
if (numType) { /* statements */ } | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [If statement condition needs to be a boolean expression or literal] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
underline the condition, not the entire if
statement. Same with others
bwrapType || boolType; | ||
~~~~~~~~~ [Operands for the && or || operator should be of type boolean] | ||
objType || boolType || enumType; | ||
~~~~~~~~~~~~~~~~~~~ [Operands for the && or || operator should be of type boolean] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes sense to underline objType || boolType
, objType
should only be underlined. If we can avoid flagging this, we can go back to the way it was before using errorFlagged
and call super
since that address the issue of overlapping errors
5a83f90
to
8cfc9d0
Compare
This rule helps in enforcing usage of boolean expressions in if statements and other constructs where boolean expressions are used also restricts the && and || operators to work with boolean types so that the operator behaves consistently without the need for type coercion.
8cfc9d0
to
a984107
Compare
Thank you @nchen63 and @adidahiya for that explanation, things are much clearer to me now, so based on the feedback left in the earlier comments I have made changes to eliminate the duplication of error messages and also narrow down the error message to point to the specific expression or identifier, please take a look and let me know if the changes are fine, this should work as good first cut for this rule and probably improved upon as we go. Please let me know what you guys think. |
@nchen63 and @adidahiya Thanks for the review, how do we get the documentation updated, i.e add the description about the new rule at https://palantir.github.io/tslint/rules/ |
We don't update that site until a release is made. You can run |
PR checklist
Introduced a rule to type check boolean expressions using type aware rules. This rule checks if the given binary expression, if statement, while statement or for statement comprises of correctly formed boolean expressions or literals. This is a first cut of the implementation and would like to get feedback on how to improve this and finally make this rule a part of the tslint rule set.
@myitcv tagging you here since we both discussed this rule offline, @adidahiya any feedback is welcome on how we can improve this.