Skip to content

Commit 42831a6

Browse files
authored
Add regexp/no-useless-range rule (#34)
1 parent 97c1bea commit 42831a6

File tree

6 files changed

+218
-0
lines changed

6 files changed

+218
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ The rules with the following star :star: are included in the `plugin:regexp/reco
9191
| [regexp/no-useless-backreference](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-backreference.html) | disallow useless backreferences in regular expressions | |
9292
| [regexp/no-useless-character-class](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-character-class.html) | disallow character class with one character | :wrench: |
9393
| [regexp/no-useless-exactly-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-exactly-quantifier.html) | disallow unnecessary exactly quantifier | :star: |
94+
| [regexp/no-useless-range](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-range.html) | disallow unnecessary range of characters by using a hyphen | :wrench: |
9495
| [regexp/no-useless-non-greedy](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-non-greedy.html) | disallow unnecessary quantifier non-greedy (`?`) | :wrench: |
9596
| [regexp/no-useless-two-nums-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-two-nums-quantifier.html) | disallow unnecessary `{n,m}` quantifier | :star: |
9697
| [regexp/prefer-d](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-d.html) | enforce using `\d` | :star::wrench: |

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The rules with the following star :star: are included in the `plugin:regexp/reco
2323
| [regexp/no-useless-backreference](./no-useless-backreference.md) | disallow useless backreferences in regular expressions | |
2424
| [regexp/no-useless-character-class](./no-useless-character-class.md) | disallow character class with one character | :wrench: |
2525
| [regexp/no-useless-exactly-quantifier](./no-useless-exactly-quantifier.md) | disallow unnecessary exactly quantifier | :star: |
26+
| [regexp/no-useless-range](./no-useless-range.md) | disallow unnecessary range of characters by using a hyphen | :wrench: |
2627
| [regexp/no-useless-non-greedy](./no-useless-non-greedy.md) | disallow unnecessary quantifier non-greedy (`?`) | :wrench: |
2728
| [regexp/no-useless-two-nums-quantifier](./no-useless-two-nums-quantifier.md) | disallow unnecessary `{n,m}` quantifier | :star: |
2829
| [regexp/prefer-d](./prefer-d.md) | enforce using `\d` | :star::wrench: |

docs/rules/no-useless-range.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "regexp/no-useless-range"
5+
description: "disallow unnecessary range of characters by using a hyphen"
6+
---
7+
# regexp/no-useless-range
8+
9+
> disallow unnecessary range of characters by using a hyphen
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
13+
14+
## :book: Rule Details
15+
16+
This rule reports unnecessary range of characters by using a hyphen. e.g. `[a-a]`
17+
18+
<eslint-code-block fix>
19+
20+
```js
21+
/* eslint regexp/no-useless-range: "error" */
22+
23+
/* ✓ GOOD */
24+
var foo = /[a]/
25+
var foo = /[ab]/
26+
27+
/* ✗ BAD */
28+
var foo = /[a-a]/
29+
var foo = /[a-b]/
30+
```
31+
32+
</eslint-code-block>
33+
34+
## :wrench: Options
35+
36+
Nothing.
37+
38+
## :mag: Implementation
39+
40+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/lib/rules/no-useless-range.ts)
41+
- [Test source](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/tests/lib/rules/no-useless-range.ts)

lib/rules/no-useless-range.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import type { Expression } from "estree"
2+
import type { RegExpVisitor } from "regexpp/visitor"
3+
import {
4+
createRule,
5+
defineRegexpVisitor,
6+
fixerApplyEscape,
7+
getRegexpLocation,
8+
getRegexpRange,
9+
} from "../utils"
10+
11+
export default createRule("no-useless-range", {
12+
meta: {
13+
docs: {
14+
description:
15+
"disallow unnecessary range of characters by using a hyphen",
16+
// TODO In the major version, it will be changed to "recommended".
17+
recommended: false,
18+
},
19+
fixable: "code",
20+
schema: [],
21+
messages: {
22+
unexpected:
23+
"Unexpected unnecessary range of characters by using a hyphen.",
24+
},
25+
type: "suggestion", // "problem",
26+
},
27+
create(context) {
28+
const sourceCode = context.getSourceCode()
29+
30+
/**
31+
* Create visitor
32+
* @param node
33+
*/
34+
function createVisitor(node: Expression): RegExpVisitor.Handlers {
35+
return {
36+
onCharacterClassRangeEnter(ccrNode) {
37+
if (
38+
ccrNode.min.value !== ccrNode.max.value &&
39+
ccrNode.min.value + 1 !== ccrNode.max.value
40+
) {
41+
return
42+
}
43+
context.report({
44+
node,
45+
loc: getRegexpLocation(sourceCode, node, ccrNode),
46+
messageId: "unexpected",
47+
fix(fixer) {
48+
const range = getRegexpRange(
49+
sourceCode,
50+
node,
51+
ccrNode,
52+
)
53+
if (range == null) {
54+
return null
55+
}
56+
let text =
57+
ccrNode.min.value < ccrNode.max.value
58+
? ccrNode.min.raw + ccrNode.max.raw
59+
: ccrNode.min.raw
60+
61+
const parent = ccrNode.parent
62+
const next =
63+
parent.elements[
64+
parent.elements.indexOf(ccrNode) + 1
65+
]
66+
if (
67+
next &&
68+
next.type === "Character" &&
69+
next.raw === "-"
70+
) {
71+
text += fixerApplyEscape("\\", node)
72+
}
73+
return fixer.replaceTextRange(range, text)
74+
},
75+
})
76+
},
77+
}
78+
}
79+
80+
return defineRegexpVisitor(context, {
81+
createVisitor,
82+
})
83+
},
84+
})

lib/utils/rules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import noOctal from "../rules/no-octal"
1111
import noUselessBackreference from "../rules/no-useless-backreference"
1212
import noUselessCharacterClass from "../rules/no-useless-character-class"
1313
import noUselessExactlyQuantifier from "../rules/no-useless-exactly-quantifier"
14+
import noUselessRange from "../rules/no-useless-range"
1415
import noUselessNonGreedy from "../rules/no-useless-non-greedy"
1516
import noUselessTwoNumsQuantifier from "../rules/no-useless-two-nums-quantifier"
1617
import preferD from "../rules/prefer-d"
@@ -37,6 +38,7 @@ export const rules = [
3738
noUselessBackreference,
3839
noUselessCharacterClass,
3940
noUselessExactlyQuantifier,
41+
noUselessRange,
4042
noUselessNonGreedy,
4143
noUselessTwoNumsQuantifier,
4244
preferD,

tests/lib/rules/no-useless-range.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { RuleTester } from "eslint"
2+
import rule from "../../../lib/rules/no-useless-range"
3+
4+
const tester = new RuleTester({
5+
parserOptions: {
6+
ecmaVersion: 2020,
7+
sourceType: "module",
8+
},
9+
})
10+
11+
tester.run("no-useless-range", rule as any, {
12+
valid: [`/[a]/`, `/[ab]/`, `/[a-c]/`],
13+
invalid: [
14+
{
15+
code: `/[a-a]/`,
16+
output: `/[a]/`,
17+
errors: [
18+
{
19+
message:
20+
"Unexpected unnecessary range of characters by using a hyphen.",
21+
line: 1,
22+
column: 3,
23+
},
24+
],
25+
},
26+
{
27+
code: `/[a-b]/`,
28+
output: `/[ab]/`,
29+
errors: [
30+
{
31+
message:
32+
"Unexpected unnecessary range of characters by using a hyphen.",
33+
line: 1,
34+
column: 3,
35+
},
36+
],
37+
},
38+
{
39+
code: `/[a-a-c-c]/`,
40+
output: `/[a\\-c]/`,
41+
errors: [
42+
{
43+
message:
44+
"Unexpected unnecessary range of characters by using a hyphen.",
45+
line: 1,
46+
column: 3,
47+
},
48+
{
49+
message:
50+
"Unexpected unnecessary range of characters by using a hyphen.",
51+
line: 1,
52+
column: 7,
53+
},
54+
],
55+
},
56+
{
57+
code: `/[a-abc]/`,
58+
output: `/[abc]/`,
59+
errors: [
60+
{
61+
message:
62+
"Unexpected unnecessary range of characters by using a hyphen.",
63+
line: 1,
64+
column: 3,
65+
},
66+
],
67+
},
68+
{
69+
code: `
70+
const s = "[a-a-c]"
71+
new RegExp(s)`,
72+
output: `
73+
const s = "[a\\\\-c]"
74+
new RegExp(s)`,
75+
errors: [
76+
"Unexpected unnecessary range of characters by using a hyphen.",
77+
],
78+
},
79+
{
80+
code: `
81+
const s = "[a-"+"a]"
82+
new RegExp(s)`,
83+
output: null,
84+
errors: [
85+
"Unexpected unnecessary range of characters by using a hyphen.",
86+
],
87+
},
88+
],
89+
})

0 commit comments

Comments
 (0)