Skip to content
This repository was archived by the owner on Mar 25, 2021. It is now read-only.

Commit 72c7bd5

Browse files
Josh Goldbergadidahiya
authored andcommitted
Fixed false positive in unnecessary-else after non-jumping statement (#4603)
1 parent 6b28417 commit 72c7bd5

File tree

2 files changed

+137
-11
lines changed

2 files changed

+137
-11
lines changed

src/rules/unnecessaryElseRule.ts

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,74 @@ export class Rule extends Lint.Rules.AbstractRule {
5050
}
5151
}
5252

53+
interface IJumpAndIfStatement {
54+
jumpStatement: string | undefined;
55+
node: ts.IfStatement;
56+
}
57+
5358
function walk(ctx: Lint.WalkContext): void {
54-
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
55-
if (utils.isIfStatement(node)) {
56-
const jumpStatement = utils.isBlock(node.thenStatement)
57-
? getJumpStatement(getLastStatement(node.thenStatement))
58-
: getJumpStatement(node.thenStatement);
59+
const ifStatementStack: IJumpAndIfStatement[] = [];
60+
61+
function visitIfStatement(node: ts.IfStatement) {
62+
const jumpStatement = utils.isBlock(node.thenStatement)
63+
? getJumpStatement(getLastStatement(node.thenStatement))
64+
: getJumpStatement(node.thenStatement);
65+
66+
ifStatementStack.push({ node, jumpStatement });
67+
68+
if (
69+
jumpStatement !== undefined &&
70+
node.elseStatement !== undefined &&
71+
!recentStackParentMissingJumpStatement()
72+
) {
73+
const elseKeyword = getPositionOfElseKeyword(node, ts.SyntaxKind.ElseKeyword);
74+
ctx.addFailureAtNode(elseKeyword, Rule.FAILURE_STRING(jumpStatement));
75+
}
76+
77+
ts.forEachChild(node, visitNode);
78+
ifStatementStack.pop();
79+
}
80+
81+
function recentStackParentMissingJumpStatement() {
82+
if (ifStatementStack.length <= 1) {
83+
return false;
84+
}
85+
86+
for (let i = ifStatementStack.length - 2; i >= 0; i -= 1) {
87+
const { jumpStatement, node } = ifStatementStack[i];
5988

60-
if (jumpStatement !== undefined && node.elseStatement !== undefined) {
61-
const elseKeyword = getPositionOfElseKeyword(node, ts.SyntaxKind.ElseKeyword);
62-
ctx.addFailureAtNode(elseKeyword, Rule.FAILURE_STRING(jumpStatement));
89+
if (node.elseStatement !== ifStatementStack[i + 1].node) {
90+
return false;
91+
}
92+
93+
if (jumpStatement === undefined) {
94+
return true;
6395
}
6496
}
65-
return ts.forEachChild(node, cb);
66-
});
97+
98+
return false;
99+
}
100+
101+
function visitNode(node: ts.Node): void {
102+
if (utils.isIfStatement(node)) {
103+
visitIfStatement(node);
104+
} else {
105+
ts.forEachChild(node, visitNode);
106+
}
107+
}
108+
109+
ts.forEachChild(ctx.sourceFile, visitNode);
67110
}
68111

69112
function getPositionOfElseKeyword(node: ts.Node, kind: ts.SyntaxKind) {
70113
return node.getChildren().filter(child => child.kind === kind)[0];
71114
}
72115

73-
function getJumpStatement(node: ts.Statement): string | undefined {
116+
function getJumpStatement(node: ts.Statement | undefined): string | undefined {
117+
if (node === undefined) {
118+
return undefined;
119+
}
120+
74121
switch (node.kind) {
75122
case ts.SyntaxKind.BreakStatement:
76123
return "break";

test/rules/unnecessary-else/test.ts.lint

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,85 @@ const testNoJump = (a) => {
252252
}
253253
}
254254

255+
const testConsoleLogIf = (a) => {
256+
if (a) {
257+
// ...
258+
} else if (!a) {
259+
// ...
260+
}
261+
}
262+
263+
const testNonJumpingElse = (a) => {
264+
if (a === 1) {
265+
console.log("a");
266+
} else if (a === 2) {
267+
return;
268+
} else {
269+
console.log("b");
270+
}
271+
272+
console.log("c");
273+
}
274+
275+
const testNonJumpingIf = (a) => {
276+
if (a === 1) {
277+
console.log("a");
278+
} else if (a === 2) {
279+
return;
280+
} else if (a % 2 === 1) {
281+
console.log("b");
282+
}
283+
284+
console.log("c");
285+
}
286+
287+
const testNonJumpingIfNestedJumping = (a) => {
288+
if (a === 1) {
289+
if (a) {
290+
return;
291+
}
292+
else { }
293+
~~~~ [return]
294+
} else if (a === 2) {
295+
return;
296+
} else if (a % 2 === 1) {
297+
console.log("b");
298+
}
299+
300+
console.log("c");
301+
}
302+
303+
const testNonJumpingIfNestedNonJumping = (a) => {
304+
if (a === 1) {
305+
if (a) {}
306+
else if (a) {
307+
return;
308+
}
309+
else { }
310+
} else if (a === 2) {
311+
return;
312+
} else if (a % 2 === 1) {
313+
console.log("b");
314+
}
315+
316+
console.log("c");
317+
}
318+
319+
const testNonJumpingIfAndUnrelated = (a) => {
320+
if (a === 1) {
321+
console.log("a");
322+
if (a) {}
323+
else if (a) { }
324+
else { }
325+
} else if (a === 2) {
326+
return;
327+
} else if (a % 2 === 1) {
328+
console.log("b");
329+
}
330+
331+
console.log("c");
332+
}
333+
255334
[return]: The preceding `if` block ends with a `return` statement. This `else` is unnecessary.
256335
[throw]: The preceding `if` block ends with a `throw` statement. This `else` is unnecessary.
257336
[break]: The preceding `if` block ends with a `break` statement. This `else` is unnecessary.

0 commit comments

Comments
 (0)