Skip to content

Commit f221d81

Browse files
Add regexp/no-empty-character-class rule (#329)
* Add `regexp/no-empty-character-class` rule * Add comment * update no-empty-character-class * Update lib/rules/no-empty-character-class.ts Co-authored-by: Michael Schmidt <[email protected]> * Update lib/rules/no-empty-character-class.ts Co-authored-by: Michael Schmidt <[email protected]> * Update docs/rules/no-empty-character-class.md Co-authored-by: Michael Schmidt <[email protected]> * Update lib/rules/no-empty-character-class.ts Co-authored-by: Michael Schmidt <[email protected]> * Update docs/rules/no-empty-character-class.md Co-authored-by: Michael Schmidt <[email protected]> * update no-empty-character-class Co-authored-by: Michael Schmidt <[email protected]>
1 parent 437b8ab commit f221d81

File tree

8 files changed

+190
-0
lines changed

8 files changed

+190
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ The rules with the following star :star: are included in the `plugin:regexp/reco
108108
| [regexp/no-dupe-disjunctions](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-dupe-disjunctions.html) | disallow duplicate disjunctions | :star: |
109109
| [regexp/no-empty-alternative](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-alternative.html) | disallow alternatives without elements | :star: |
110110
| [regexp/no-empty-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-capturing-group.html) | disallow capturing group that captures empty. | :star: |
111+
| [regexp/no-empty-character-class](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-character-class.html) | disallow character classes that match no characters | |
111112
| [regexp/no-empty-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-group.html) | disallow empty group | :star: |
112113
| [regexp/no-empty-lookarounds-assertion](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-lookarounds-assertion.html) | disallow empty lookahead assertion or empty lookbehind assertion | :star: |
113114
| [regexp/no-escape-backspace](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-escape-backspace.html) | disallow escape backspace (`[\b]`) | :star: |

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The rules with the following star :star: are included in the `plugin:regexp/reco
1717
| [regexp/no-dupe-disjunctions](./no-dupe-disjunctions.md) | disallow duplicate disjunctions | :star: |
1818
| [regexp/no-empty-alternative](./no-empty-alternative.md) | disallow alternatives without elements | :star: |
1919
| [regexp/no-empty-capturing-group](./no-empty-capturing-group.md) | disallow capturing group that captures empty. | :star: |
20+
| [regexp/no-empty-character-class](./no-empty-character-class.md) | disallow character classes that match no characters | |
2021
| [regexp/no-empty-group](./no-empty-group.md) | disallow empty group | :star: |
2122
| [regexp/no-empty-lookarounds-assertion](./no-empty-lookarounds-assertion.md) | disallow empty lookahead assertion or empty lookbehind assertion | :star: |
2223
| [regexp/no-escape-backspace](./no-escape-backspace.md) | disallow escape backspace (`[\b]`) | :star: |
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "regexp/no-empty-character-class"
5+
description: "disallow character classes that match no characters"
6+
---
7+
# regexp/no-empty-character-class
8+
9+
> disallow character classes that match no characters
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+
13+
## :book: Rule Details
14+
15+
This rule reports character classes that cannot match any characters.
16+
17+
Character classes that cannot match any characters are either empty or negated character classes of elements that contain all characters.
18+
19+
The reports for this rule include reports for the ESLint core [no-empty-character-class] rule. That is, if you use this rule, you can turn off the ESLint core [no-empty-character-class] rule.
20+
21+
<eslint-code-block>
22+
23+
```js
24+
/* eslint regexp/no-empty-character-class: "error" */
25+
26+
/* ✓ GOOD */
27+
var foo = /abc[d]/;
28+
var foo = /abc[a-z]/;
29+
var foo = /[^]/;
30+
var foo = /[\s\S]/;
31+
32+
/* ✗ BAD */
33+
var foo = /abc[]/;
34+
var foo = /[^\s\S]/;
35+
```
36+
37+
</eslint-code-block>
38+
39+
## :wrench: Options
40+
41+
Nothing.
42+
43+
## :books: Further reading
44+
45+
- [no-empty-character-class]
46+
47+
[no-empty-character-class]: https://eslint.org/docs/rules/no-empty-character-class
48+
49+
## :mag: Implementation
50+
51+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/lib/rules/no-empty-character-class.ts)
52+
- [Test source](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/tests/lib/rules/no-empty-character-class.ts)

lib/configs/recommended.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export = {
1010
// The ESLint rule will report fewer cases than our rule
1111
"no-invalid-regexp": "off",
1212
"no-useless-backreference": "off",
13+
// TODO Switch in the major version.
14+
// "no-empty-character-class": "off",
1315

1416
// eslint-plugin-regexp rules
1517
"regexp/confusing-quantifier": "warn",

lib/rules/no-empty-character-class.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { matchesNoCharacters } from "regexp-ast-analysis"
2+
import type { RegExpVisitor } from "regexpp/visitor"
3+
import type { RegExpContext } from "../utils"
4+
import { createRule, defineRegexpVisitor } from "../utils"
5+
6+
export default createRule("no-empty-character-class", {
7+
meta: {
8+
docs: {
9+
description: "disallow character classes that match no characters",
10+
category: "Possible Errors",
11+
// TODO Switch to recommended in the major version.
12+
// recommended: true,
13+
recommended: false,
14+
},
15+
schema: [],
16+
messages: {
17+
empty:
18+
"This character class matches no characters because it is empty.",
19+
cannotMatchAny: "This character class cannot match any characters.",
20+
},
21+
type: "suggestion", // "problem",
22+
},
23+
create(context) {
24+
/**
25+
* Create visitor
26+
*/
27+
function createVisitor(
28+
regexpContext: RegExpContext,
29+
): RegExpVisitor.Handlers {
30+
const { node, getRegexpLocation, flags } = regexpContext
31+
32+
return {
33+
onCharacterClassEnter(ccNode) {
34+
if (matchesNoCharacters(ccNode, flags)) {
35+
context.report({
36+
node,
37+
loc: getRegexpLocation(ccNode),
38+
messageId: ccNode.elements.length
39+
? "cannotMatchAny"
40+
: "empty",
41+
})
42+
}
43+
},
44+
}
45+
}
46+
47+
return defineRegexpVisitor(context, {
48+
createVisitor,
49+
})
50+
},
51+
})

lib/utils/rules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import noDupeCharactersCharacterClass from "../rules/no-dupe-characters-characte
1111
import noDupeDisjunctions from "../rules/no-dupe-disjunctions"
1212
import noEmptyAlternative from "../rules/no-empty-alternative"
1313
import noEmptyCapturingGroup from "../rules/no-empty-capturing-group"
14+
import noEmptyCharacterClass from "../rules/no-empty-character-class"
1415
import noEmptyGroup from "../rules/no-empty-group"
1516
import noEmptyLookaroundsAssertion from "../rules/no-empty-lookarounds-assertion"
1617
import noEscapeBackspace from "../rules/no-escape-backspace"
@@ -81,6 +82,7 @@ export const rules = [
8182
noDupeDisjunctions,
8283
noEmptyAlternative,
8384
noEmptyCapturingGroup,
85+
noEmptyCharacterClass,
8486
noEmptyGroup,
8587
noEmptyLookaroundsAssertion,
8688
noEscapeBackspace,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { RuleTester } from "eslint"
2+
import rule from "../../../lib/rules/no-empty-character-class"
3+
4+
const tester = new RuleTester({
5+
parserOptions: {
6+
ecmaVersion: 2020,
7+
sourceType: "module",
8+
},
9+
})
10+
11+
tester.run("no-empty-character-class", rule as any, {
12+
valid: [
13+
`/[a]/`,
14+
`/[a-z]/`,
15+
`/[a]?/`,
16+
`/[a]*/`,
17+
`/[[]/`,
18+
String.raw`/\[]/`,
19+
`/[^]/`,
20+
`/[()]/`,
21+
`/[ ]/`,
22+
String.raw`/[\s\S]/`,
23+
String.raw`/[\da-zA-Z_\W]/`,
24+
],
25+
invalid: [
26+
{
27+
code: `/[]/`,
28+
errors: [
29+
{
30+
message:
31+
"This character class matches no characters because it is empty.",
32+
line: 1,
33+
column: 2,
34+
},
35+
],
36+
},
37+
{
38+
code: `/abc[]/`,
39+
errors: [
40+
{
41+
message:
42+
"This character class matches no characters because it is empty.",
43+
line: 1,
44+
column: 5,
45+
},
46+
],
47+
},
48+
{
49+
code: `/([])/`,
50+
errors: [
51+
{
52+
message:
53+
"This character class matches no characters because it is empty.",
54+
line: 1,
55+
column: 3,
56+
},
57+
],
58+
},
59+
{
60+
code: `new RegExp("[]");`,
61+
errors: [
62+
{
63+
message:
64+
"This character class matches no characters because it is empty.",
65+
line: 1,
66+
column: 13,
67+
},
68+
],
69+
},
70+
{
71+
code: String.raw`/[^\s\S]/`,
72+
errors: ["This character class cannot match any characters."],
73+
},
74+
{
75+
code: String.raw`/[^\da-zA-Z_\W]/`,
76+
errors: ["This character class cannot match any characters."],
77+
},
78+
],
79+
})

tools/update-rulesets.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export = {
2424
// The ESLint rule will report fewer cases than our rule
2525
"no-invalid-regexp": "off",
2626
"no-useless-backreference": "off",
27+
// TODO Switch in the major version.
28+
// "no-empty-character-class": "off",
2729
2830
// eslint-plugin-regexp rules
2931
${rules

0 commit comments

Comments
 (0)