Skip to content

Commit b9d514a

Browse files
committed
New: no-duplicate-disable rule
1 parent ac19e81 commit b9d514a

File tree

4 files changed

+194
-22
lines changed

4 files changed

+194
-22
lines changed

docs/rules/no-duplicate-disable.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# disallows duplicate `eslint-disable` comments (eslint-comments/no-duplicate-disable)
2+
3+
Duplicate of `eslint-disable` directive-comments implies that there is a mix of wide-range directive-comments and narrow-range directive-comments.
4+
The mix may cause to overlook ESLint warnings in future.
5+
6+
This rule warns duplicate `eslint-disable` directive-comments.
7+
8+
## Rule Details
9+
10+
Examples of :-1: **incorrect** code for this rule:
11+
12+
```js
13+
/*eslint eslint-comments/no-duplicate-disable: error */
14+
15+
/*eslint-disable no-undef */
16+
17+
var foo = bar() //eslint-disable-line no-undef
18+
```
19+
20+
```js
21+
/*eslint eslint-comments/no-duplicate-disable: error */
22+
23+
/*eslint-disable*/
24+
25+
var foo = bar() //eslint-disable-line no-undef
26+
```
27+
28+
Examples of :+1: **correct** code for this rule:
29+
30+
```js
31+
/*eslint eslint-comments/no-duplicate-disable: error */
32+
33+
/*eslint-disable no-undef */
34+
35+
var foo = bar()
36+
```
37+
38+
```js
39+
/*eslint eslint-comments/no-duplicate-disable: error */
40+
41+
var foo = bar() //eslint-disable-line no-undef
42+
```

lib/disabled-area.js

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ function lte(a, b) {
2525
return a.line < b.line || (a.line === b.line && a.column <= b.column)
2626
}
2727

28+
/**
29+
* Checks `a` is less than `b`.
30+
*
31+
* @param {{line: number, column: number}} a - A location to compare.
32+
* @param {{line: number, column: number}} b - Another location to compare.
33+
* @returns {boolean} `true` if `a` is less than `b`.
34+
* @private
35+
*/
36+
function lt(a, b) {
37+
return a.line < b.line || (a.line === b.line && a.column < b.column)
38+
}
39+
2840
//------------------------------------------------------------------------------
2941
// Exports
3042
//------------------------------------------------------------------------------
@@ -54,7 +66,7 @@ module.exports = class DisabledArea {
5466
constructor() {
5567
this.areas = []
5668
this.duplicateDisableDirectives = []
57-
this.uselessEnableDirectives = []
69+
this.unusedEnableDirectives = []
5870
}
5971

6072
/**
@@ -70,7 +82,7 @@ module.exports = class DisabledArea {
7082
_disable(comment, location, ruleIds, kind) {
7183
if (ruleIds) {
7284
for (const ruleId of ruleIds) {
73-
if (this._isDisabled(ruleId, location)) {
85+
if (this._getArea(ruleId, location) != null) {
7486
this.duplicateDisableDirectives.push({
7587
comment,
7688
ruleId,
@@ -89,7 +101,7 @@ module.exports = class DisabledArea {
89101
}
90102
}
91103
else {
92-
if (this._isDisabled(null, location)) {
104+
if (this._getArea(null, location) != null) {
93105
this.duplicateDisableDirectives.push({
94106
comment,
95107
ruleId: null,
@@ -133,7 +145,7 @@ module.exports = class DisabledArea {
133145
}
134146

135147
if (!used) {
136-
this.uselessEnableDirectives.push({comment, ruleId})
148+
this.unusedEnableDirectives.push({comment, ruleId})
137149
}
138150
}
139151
}
@@ -153,7 +165,7 @@ module.exports = class DisabledArea {
153165
}
154166

155167
if (start === null) {
156-
this.uselessEnableDirectives.push({comment, ruleId: null})
168+
this.unusedEnableDirectives.push({comment, ruleId: null})
157169
}
158170
}
159171
}
@@ -163,35 +175,25 @@ module.exports = class DisabledArea {
163175
*
164176
* @param {string|null} ruleId - The ruleId name to get.
165177
* @param {object} location - The location to get.
166-
* @returns {Generator<object>} The area of the given ruleId and location.
178+
* @returns {object|null} The area of the given ruleId and location.
167179
* @private
168180
*/
169-
* _getAreas(ruleId, location) {
181+
_getArea(ruleId, location) {
170182
for (let i = this.areas.length - 1; i >= 0; --i) {
171183
const area = this.areas[i]
172184

173-
if (lte(location, area.start)) {
185+
if (lt(location, area.start)) {
174186
break
175187
}
176188
if ((area.ruleId === null || area.ruleId === ruleId) &&
177189
lte(area.start, location) &&
178190
(area.end === null || lte(location, area.end))
179191
) {
180-
yield area
192+
return area
181193
}
182194
}
183-
}
184195

185-
/**
186-
* Checks whether the given ruleId and location pair is disabled.
187-
*
188-
* @param {string|null} ruleId - The ruleId name to check.
189-
* @param {object} location - The location to check.
190-
* @returns {boolean} `true` if it's disabled.
191-
* @private
192-
*/
193-
_isDisabled(ruleId, location) {
194-
return !this._getAreas(ruleId, location).next().done
196+
return null
195197
}
196198

197199
/**
@@ -249,9 +251,9 @@ module.exports = class DisabledArea {
249251
* @returns {void}
250252
*/
251253
report(ruleId, location) {
252-
for (const area of this._getAreas(ruleId, location)) {
254+
const area = this._getArea(ruleId, location)
255+
if (area != null) {
253256
area.reported = true
254-
return
255257
}
256258
}
257259
}

lib/rules/no-duplicate-disable.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @author Toru Nagashima
3+
* @copyright 2016 Toru Nagashima. All rights reserved.
4+
* See LICENSE file in root directory for full license.
5+
*/
6+
"use strict"
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const DisabledArea = require("../disabled-area")
13+
const utils = require("../utils")
14+
15+
//------------------------------------------------------------------------------
16+
// Rule Definition
17+
//------------------------------------------------------------------------------
18+
19+
module.exports = {
20+
meta: {
21+
docs: {
22+
description: "disallows duplicate `eslint-disable` comments",
23+
category: "Best Practices",
24+
recommended: false,
25+
},
26+
fixable: false,
27+
schema: [],
28+
},
29+
30+
create(context) {
31+
const sourceCode = context.getSourceCode()
32+
const disabledArea = DisabledArea.get(sourceCode)
33+
34+
return {
35+
Program() {
36+
for (const item of disabledArea.duplicateDisableDirectives) {
37+
context.report({
38+
loc: utils.toRuleIdLocation(item.comment, item.ruleId),
39+
message: (item.ruleId) ?
40+
"'{{ruleId}}' rule has been disabled already." :
41+
"ESLint rules have been disabled already.",
42+
data: item,
43+
})
44+
}
45+
},
46+
}
47+
},
48+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @author Toru Nagashima
3+
* @copyright 2016 Toru Nagashima. All rights reserved.
4+
* See LICENSE file in root directory for full license.
5+
*/
6+
"use strict"
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const RuleTester = require("eslint").RuleTester
13+
const rule = require("../../../lib/rules/no-duplicate-disable")
14+
15+
//------------------------------------------------------------------------------
16+
// Tests
17+
//------------------------------------------------------------------------------
18+
19+
const tester = new RuleTester()
20+
21+
tester.run("no-duplicate-disable", rule, {
22+
valid: [
23+
`
24+
//eslint-disable-line
25+
`,
26+
`
27+
/*eslint-disable no-undef*/
28+
//eslint-disable-line no-unused-vars
29+
//eslint-disable-next-line semi
30+
/*eslint-disable eqeqeq*/
31+
`,
32+
],
33+
invalid: [
34+
{
35+
code: `
36+
/*eslint-disable no-undef*/
37+
//eslint-disable-line no-undef
38+
`,
39+
errors: [
40+
{
41+
message: "'no-undef' rule has been disabled already.",
42+
line: 3,
43+
column: 23,
44+
endLine: 3,
45+
endColumn: 31,
46+
},
47+
],
48+
},
49+
{
50+
code: `
51+
/*eslint-disable no-undef*/
52+
//eslint-disable-next-line no-undef
53+
`,
54+
errors: [
55+
{
56+
message: "'no-undef' rule has been disabled already.",
57+
line: 3,
58+
column: 28,
59+
endLine: 3,
60+
endColumn: 36,
61+
},
62+
],
63+
},
64+
{
65+
code: `
66+
//eslint-disable-next-line no-undef
67+
//eslint-disable-line no-undef
68+
`,
69+
errors: [
70+
{
71+
message: "'no-undef' rule has been disabled already.",
72+
line: 3,
73+
column: 23,
74+
endLine: 3,
75+
endColumn: 31,
76+
},
77+
],
78+
},
79+
],
80+
})

0 commit comments

Comments
 (0)