Repository: sindresorhus/eslint-plugin-unicorn (JavaScript)

Issue:
Results of weekly scheduled smoke test
Detected 1 ESLint reports and/or crashes. 
Scanned 3570 repositories.

Rules:
-   `unicorn/prefer-string-replace-all`

<details>
    <summary>Click to expand</summary>

## Rule: unicorn/prefer-string-replace-all

-   Message: `Expected atom at position 35     定义 SVG 图标)+([^]*?)((               ^ Occurred while linting /github/workspace/node_modules/eslint-remote-tester/.cache-eslint-remote-tester/JeromeLin/zarm-web/site/components/Markdown/index.jsx:73 Rule: "unicorn/prefer-string-replace-all"`
-   Path: `JeromeLin/zarm-web/site/components/Markdown/index.jsx`
-   [Link](https://github.com/JeromeLin/zarm-web/blob/HEAD/site/components/Markdown/index.jsx#L73)

```jsx
  71 |     components.clear();
  72 |     html = marked(
> 73 |       content.replace(
  74 |         /##\s+(?!自定义 Iconfont 图标|自定义 SVG 图标)+([^]*?)((?=##))/g,
  75 |         (match, p1) => {
  76 |           const id = parseInt(Math.random() * 1e9, 10).toString(36);
```

```
SyntaxError: Expected atom at position 35
    定义 SVG 图标)+([^]*?)((
              ^
Occurred while linting /github/workspace/node_modules/eslint-remote-tester/.cache-eslint-remote-tester/JeromeLin/zarm-web/site/components/Markdown/index.jsx:73
Rule: "unicorn/prefer-string-replace-all"
    at bail (/github/workspace/node_modules/regjsparser/parser.js:1571:13)
    at parseTerm (/github/workspace/node_modules/regjsparser/parser.js:584:11)
    at parseAlternative (/github/workspace/node_modules/regjsparser/parser.js:548:21)
    at parseDisjunction (/github/workspace/node_modules/regjsparser/parser.js:528:16)
    at parse (/github/workspace/node_modules/regjsparser/parser.js:1597:18)
    at getPatternReplacement (/github/workspace/rules/prefer-string-replace-all.js:30:15)
    at [type="CallExpression"][optional!=true][arguments.length=2][arguments.0.type!="SpreadElement"][arguments.1.type!="SpreadElement"][callee.type="MemberExpression"][callee.computed!=true][callee.property.type="Identifier"][callee.optional!=true]:matches([callee.property.name="replace"], [callee.property.name="replaceAll"]) (/github/workspace/rules/prefer-string-replace-all.js:88:30)
    at /github/workspace/rules/utils/rule.js:39:18
    at ruleErrorHandler (/github/workspace/node_modules/eslint/lib/linter/linter.js:1115:28)
    at /github/workspace/node_modules/eslint/lib/linter/safe-emitter.js:45:58
```

</details>



Discussion:

Patch:
diff --git a/rules/better-regex.js b/rules/better-regex.js
index dcb6184277..d12081fc66 100644
--- a/rules/better-regex.js
+++ b/rules/better-regex.js
@@ -6,8 +6,10 @@ const {newExpressionSelector} = require('./selectors/index.js');
 const {isStringLiteral} = require('./ast/index.js');
 
 const MESSAGE_ID = 'better-regex';
+const MESSAGE_ID_PARSE_ERROR = 'better-regex/parse-error';
 const messages = {
 	[MESSAGE_ID]: '{{original}} can be optimized to {{optimized}}.',
+	[MESSAGE_ID_PARSE_ERROR]: 'Problem parsing {{original}}: {{error}}',
 };
 
 const newRegExp = newExpressionSelector({name: 'RegExp', minimumArguments: 1});
@@ -39,11 +41,11 @@ const create = context => {
 			} catch (error) {
 				return {
 					node,
+					messageId: MESSAGE_ID_PARSE_ERROR,
 					data: {
 						original,
 						error: error.message,
 					},
-					message: 'Problem parsing {{original}}: {{error}}',
 				};
 			}
 
diff --git a/rules/prefer-string-replace-all.js b/rules/prefer-string-replace-all.js
index 8e925bed2a..fd69042d07 100644
--- a/rules/prefer-string-replace-all.js
+++ b/rules/prefer-string-replace-all.js
@@ -27,11 +27,17 @@ function getPatternReplacement(node) {
 		return;
 	}
 
-	const tree = parseRegExp(pattern, flags, {
-		unicodePropertyEscape: true,
-		namedGroups: true,
-		lookbehind: true,
-	});
+	let tree;
+
+	try {
+		tree = parseRegExp(pattern, flags, {
+			unicodePropertyEscape: true,
+			namedGroups: true,
+			lookbehind: true,
+		});
+	} catch {
+		return;
+	}
 
 	const parts = tree.type === 'alternative' ? tree.body : [tree];
 	if (parts.some(part => part.type !== 'value')) {


Test patch:
diff --git a/test/better-regex.mjs b/test/better-regex.mjs
index de3ec56bc0..1c6cefc18c 100644
--- a/test/better-regex.mjs
+++ b/test/better-regex.mjs
@@ -323,3 +323,11 @@ test({
 		},
 	],
 });
+
+test.snapshot({
+	valid: [],
+	invalid: [
+		// Invalid RegExp
+		'/(?!a)+/g',
+	],
+});
diff --git a/test/prefer-string-replace-all.mjs b/test/prefer-string-replace-all.mjs
index d8226d698d..e742f84c3b 100644
--- a/test/prefer-string-replace-all.mjs
+++ b/test/prefer-string-replace-all.mjs
@@ -103,5 +103,8 @@ test.snapshot({
 		'foo.replaceAll(/a]/g, _)',
 		'foo.replaceAll(/\\r\\n\\u{1f600}/gu, _)',
 		`foo.replaceAll(/a${' very'.repeat(30)} long string/g, _)`,
+
+		// Invalid RegExp #2010
+		'foo.replace(/(?!a)+/g, "")',
 	],
 });
diff --git a/test/snapshots/better-regex.mjs.md b/test/snapshots/better-regex.mjs.md
new file mode 100644
index 0000000000..ad358abe96
--- /dev/null
+++ b/test/snapshots/better-regex.mjs.md
@@ -0,0 +1,19 @@
+# Snapshot report for `test/better-regex.mjs`
+
+The actual snapshot is saved in `better-regex.mjs.snap`.
+
+Generated by [AVA](https://avajs.dev).
+
+## Invalid #1
+      1 | /(?!a)+/g
+
+> Error 1/1
+
+    `␊
+    > 1 | /(?!a)+/g␊
+        | ^^^^^^^^^ Problem parsing /(?!a)+/g: ␊
+    ␊
+    /(?!a)+/g␊
+          ^␊
+    Unexpected token: "+" at 1:6.␊
+    `
diff --git a/test/snapshots/better-regex.mjs.snap b/test/snapshots/better-regex.mjs.snap
new file mode 100644
index 0000000000..344e9979f7
Binary files /dev/null and b/test/snapshots/better-regex.mjs.snap differ
diff --git a/test/snapshots/prefer-string-replace-all.mjs.md b/test/snapshots/prefer-string-replace-all.mjs.md
index 56aa976f28..ead98c4bb3 100644
--- a/test/snapshots/prefer-string-replace-all.mjs.md
+++ b/test/snapshots/prefer-string-replace-all.mjs.md
@@ -613,3 +613,19 @@ Generated by [AVA](https://avajs.dev).
     > 1 | foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _)␊
         |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This pattern can be replaced with a string literal.␊
     `
+
+## Invalid #38
+      1 | foo.replace(/(?!a)+/g, "")
+
+> Output
+
+    `␊
+      1 | foo.replaceAll(/(?!a)+/g, "")␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | foo.replace(/(?!a)+/g, "")␊
+        |     ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊
+    `
diff --git a/test/snapshots/prefer-string-replace-all.mjs.snap b/test/snapshots/prefer-string-replace-all.mjs.snap
index aa0b930b23..8e01a4e09c 100644
Binary files a/test/snapshots/prefer-string-replace-all.mjs.snap and b/test/snapshots/prefer-string-replace-all.mjs.snap differ