diff --git a/docs/rules/no-private-in.md b/docs/rules/no-private-in.md
new file mode 100644
index 00000000..a25adb9d
--- /dev/null
+++ b/docs/rules/no-private-in.md
@@ -0,0 +1,24 @@
+# es/no-private-in
+> disallow `#x in obj`
+
+- ✅ The following configurations enable this rule: `plugin:es/no-new-in-esnext`
+
+This rule reports ES2022 private in (`#x in obj`) as errors.
+
+## Examples
+
+⛔ Examples of **incorrect** code for this rule:
+
+
+
+## 📚 References
+
+- [Rule source](https://github.com/mysticatea/eslint-plugin-es/blob/v4.1.0/lib/rules/no-private-in.js)
+- [Test source](https://github.com/mysticatea/eslint-plugin-es/blob/v4.1.0/tests/lib/rules/no-private-in.js)
diff --git a/lib/rules/no-private-in.js b/lib/rules/no-private-in.js
new file mode 100644
index 00000000..051d43b5
--- /dev/null
+++ b/lib/rules/no-private-in.js
@@ -0,0 +1,41 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+"use strict"
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow `#x in obj`.",
+ category: "ES2022",
+ recommended: false,
+ url:
+ "http://mysticatea.github.io/eslint-plugin-es/rules/no-private-in.html",
+ },
+ fixable: null,
+ messages: {
+ forbidden:
+ "ES2022 private in (`#{{private}} in {{object}}`) is forbidden.",
+ },
+ schema: [],
+ type: "problem",
+ },
+ create(context) {
+ return {
+ "BinaryExpression[operator='in'] > PrivateIdentifier.left"(node) {
+ context.report({
+ node,
+ messageId: "forbidden",
+ data: {
+ private: node.name,
+ object:
+ node.parent.right.type === "Identifier"
+ ? node.parent.right.name
+ : "object",
+ },
+ })
+ },
+ }
+ },
+}
diff --git a/package.json b/package.json
index 5b10b719..e05812cd 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,11 @@
"@mysticatea/eslint-plugin": "^13.0.0",
"@typescript-eslint/parser": "^4.8.2",
"@vuepress/plugin-pwa": "^1.2.0",
- "acorn": "^7.1.0",
+ "acorn": "^8.5.0",
"codecov": "^3.8.1",
- "eslint": "^7.10.0",
+ "eslint": "^8.0.0-0",
"eslint4b": "^7.10.0",
- "espree": "^7.0.0",
+ "espree": "^9.0.0",
"globals": "^12.0.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.5",
diff --git a/tests/lib/rules/no-private-in.js b/tests/lib/rules/no-private-in.js
new file mode 100644
index 00000000..ef6ea090
--- /dev/null
+++ b/tests/lib/rules/no-private-in.js
@@ -0,0 +1,36 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+"use strict"
+
+const RuleTester = require("../../tester")
+const rule = require("../../../lib/rules/no-private-in.js")
+
+if (!RuleTester.isSupported(2022)) {
+ //eslint-disable-next-line no-console
+ console.log("Skip the tests of no-private-in.")
+ return
+}
+
+new RuleTester().run("no-private-in", rule, {
+ valid: [
+ "class A { f(obj) { return '#x' in obj } }",
+ "class A { f(obj) { return x in obj } }",
+ "class A { #x; f(obj) { return foo in obj.#x } }",
+ ],
+ invalid: [
+ {
+ code: "class A { #x; f(obj) { return #x in obj } }",
+ errors: ["ES2022 private in (`#x in obj`) is forbidden."],
+ },
+ {
+ code: "class A { #x; f(obj) { return #x in obj.foo } }",
+ errors: ["ES2022 private in (`#x in object`) is forbidden."],
+ },
+ {
+ code: "class A { #x; f(obj) { return #x in obj.#x } }",
+ errors: ["ES2022 private in (`#x in object`) is forbidden."],
+ },
+ ],
+})