Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit 375988b

Browse files
no-duplicated-branches: consider exceptions (#109)
1 parent 4b492ad commit 375988b

File tree

3 files changed

+96
-22
lines changed

3 files changed

+96
-22
lines changed

ruling/snapshots/no-duplicated-branches

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
src/angular.js/src/ng/parse.js: 760,1327
2+
src/angular.js/src/ngAnimate/animateQueue.js: 194
23
src/brackets/src/document/TextRange.js: 133
34
src/brackets/src/extensions/default/JavaScriptQuickEdit/unittests.js: 151
45
src/brackets/src/extensions/default/MDNDocs/InlineDocsViewer.js: 103
56
src/brackets/src/language/CSSUtils.js: 1236
7+
src/brackets/src/utils/DragAndDrop.js: 54
8+
src/Chart.js/src/scales/scale.linear.js: 116,122
9+
src/Chart.js/src/scales/scale.logarithmic.js: 155,161
610
src/freeCodeCamp/server/utils/user-stats.js: 30
711
src/Ghost/core/server/models/post.js: 198
12+
src/jquery/external/sinon/sinon.js: 506
813
src/react-native/Libraries/Renderer/ReactFabric-dev.js: 10298,10386
914
src/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js: 10668,10756
1015
src/react-native/Libraries/Renderer/ReactNativeRenderer-prod.js: 4202,4310
16+
src/react-native/local-cli/util/Config.js: 36
1117
src/react/packages/react-reconciler/src/ReactFiberCommitWork.js: 669,758
18+
src/reveal.js/js/reveal.js: 1133
1219
src/spectrum/src/reducers/dashboardFeed.js: 46
1320
src/spectrum/src/views/notifications/components/sortAndGroupNotificationMessages.js: 72
1421
src/three.js/editor/js/History.js: 61
22+
src/three.js/src/renderers/WebGLRenderer.js: 1636,1640,1646
1523
src/vue/packages/vue-server-renderer/basic.js: 4378
1624
src/vue/packages/vue-server-renderer/build.js: 4121
1725
src/vue/packages/vue-template-compiler/browser.js: 3493

src/rules/no-duplicated-branches.ts

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { Rule } from "eslint";
2323
import * as estree from "estree";
2424
import { getParent, isIfStatement, isBlockStatement } from "../utils/nodes";
2525
import { areEquivalent } from "../utils/equivalence";
26-
import { collectIfBranches, takeWithoutBreak } from "../utils/conditions";
26+
import { collectIfBranches, takeWithoutBreak, collectSwitchBranches } from "../utils/conditions";
2727

2828
const MESSAGE = "This {{type}}'s code block is the same as the block for the {{type}} on line {{line}}.";
2929

@@ -41,7 +41,12 @@ const rule: Rule.RuleModule = {
4141
function visitIfStatement(ifStmt: estree.IfStatement) {
4242
const parent = getParent(context);
4343
if (!isIfStatement(parent)) {
44-
const { branches } = collectIfBranches(ifStmt);
44+
const { branches, endsWithElse } = collectIfBranches(ifStmt);
45+
46+
if (allEquivalentWithoutDefault(branches, endsWithElse)) {
47+
branches.slice(1).forEach((branch, i) => reportIssue(branch, branches[i], "branch"));
48+
return;
49+
}
4550

4651
for (let i = 1; i < branches.length; i++) {
4752
if (hasRequiredSize([branches[i]])) {
@@ -55,23 +60,27 @@ const rule: Rule.RuleModule = {
5560
}
5661
}
5762

58-
function visitSwitchStatement({ cases }: estree.SwitchStatement) {
59-
for (let i = 1; i < cases.length; i++) {
60-
const firstClauseWithoutBreak = takeWithoutBreak(expandSingleBlockStatement(cases[i].consequent));
63+
function visitSwitchStatement(switchStmt: estree.SwitchStatement) {
64+
const { cases } = switchStmt;
65+
const { endsWithDefault } = collectSwitchBranches(switchStmt);
66+
const nonEmptyCases = cases
67+
.map(c => takeWithoutBreak(expandSingleBlockStatement(c.consequent)))
68+
.filter(c => c.length > 0);
69+
70+
if (allEquivalentWithoutDefault(nonEmptyCases, endsWithDefault)) {
71+
cases.slice(1).forEach((caseStmt, i) => reportIssue(caseStmt, cases[i], "case"));
72+
return;
73+
}
74+
75+
for (let i = 1; i < nonEmptyCases.length; i++) {
76+
const firstClauseWithoutBreak = nonEmptyCases[i];
6177

6278
if (hasRequiredSize(firstClauseWithoutBreak)) {
6379
for (let j = 0; j < i; j++) {
64-
const secondClauseWithoutBreak = takeWithoutBreak(expandSingleBlockStatement(cases[j].consequent));
80+
const secondClauseWithoutBreak = nonEmptyCases[j];
6581

6682
if (areEquivalent(firstClauseWithoutBreak, secondClauseWithoutBreak, context.getSourceCode())) {
67-
context.report({
68-
message: MESSAGE,
69-
data: {
70-
type: "case",
71-
line: String(cases[j].loc!.start.line),
72-
},
73-
node: cases[i],
74-
});
83+
reportIssue(cases[i], cases[j], "case");
7584
break;
7685
}
7786
}
@@ -93,14 +102,7 @@ const rule: Rule.RuleModule = {
93102
function compareIfBranches(a: estree.Statement, b: estree.Statement) {
94103
const equivalent = areEquivalent(a, b, context.getSourceCode());
95104
if (equivalent && b.loc) {
96-
context.report({
97-
message: MESSAGE,
98-
data: {
99-
type: "branch",
100-
line: String(b.loc.start.line),
101-
},
102-
node: a,
103-
});
105+
reportIssue(a, b, "branch");
104106
}
105107
return equivalent;
106108
}
@@ -114,6 +116,25 @@ const rule: Rule.RuleModule = {
114116
}
115117
return nodes;
116118
}
119+
120+
function allEquivalentWithoutDefault(branches: Array<estree.Node | estree.Node[]>, endsWithDefault: boolean) {
121+
return (
122+
!endsWithDefault &&
123+
branches.length > 1 &&
124+
branches.slice(1).every((branch, index) => areEquivalent(branch, branches[index], context.getSourceCode()))
125+
);
126+
}
127+
128+
function reportIssue(node: estree.Node, equivalentNode: estree.Node, type: string) {
129+
context.report({
130+
message: MESSAGE,
131+
data: {
132+
type,
133+
line: String(equivalentNode.loc!.start.line),
134+
},
135+
node,
136+
});
137+
}
117138
},
118139
};
119140

tests/rules/no-duplicated-branches.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ ruleTester.run("no-duplicated-branches if", rule, {
8282
first();
8383
}`,
8484
},
85+
{
86+
code: `
87+
if(a == 1) {
88+
doSomething(); //no issue, usually this is done on purpose to increase the readability
89+
} else if (a == 2) {
90+
doSomethingElse();
91+
} else {
92+
doSomething();
93+
}`,
94+
},
8595
],
8696
invalid: [
8797
{
@@ -128,6 +138,29 @@ ruleTester.run("no-duplicated-branches if", rule, {
128138
}`,
129139
errors: [{ line: 8 }],
130140
},
141+
{
142+
code: `
143+
if(a == 1) {
144+
doSomething();
145+
} else if (a == 2) {
146+
doSomething();
147+
}`,
148+
errors: [{ line: 4, message: "This branch's code block is the same as the block for the branch on line 2." }],
149+
},
150+
{
151+
code: `
152+
if(a == 1) {
153+
doSomething();
154+
} else if (a == 2) {
155+
doSomething();
156+
} else if (a == 3) {
157+
doSomething();
158+
}`,
159+
errors: [
160+
{ line: 4, message: "This branch's code block is the same as the block for the branch on line 2." },
161+
{ line: 6, message: "This branch's code block is the same as the block for the branch on line 4." },
162+
],
163+
},
131164
],
132165
});
133166

@@ -274,5 +307,17 @@ ruleTester.run("no-duplicated-branches switch", rule, {
274307
}`,
275308
errors: [{ line: 7 }, { line: 11 }, { line: 15 }],
276309
},
310+
{
311+
code: `
312+
switch(a) {
313+
case 1:
314+
doSomething();
315+
break;
316+
case 2:
317+
doSomething();
318+
break;
319+
}`,
320+
errors: [{ line: 6, message: "This case's code block is the same as the block for the case on line 3." }],
321+
},
277322
],
278323
});

0 commit comments

Comments
 (0)