Repository: eslint/eslint
Pull request #11983 (2019-07-14 00:32:54)

Update: support bigint and dynamic import (refs #11803)
**What is the purpose of this pull request? (put an "X" next to item)**

[X] Add something to the core (#11803).

**What changes did you make? (Give an overview)**

This PR adds supports of bigint and dynamic import.

- Upgrade `espree` and `eslint-visitor-keys`.
- Fix `CodePathAnalyzer` to handle `ImportExpression` node as throwable.
- Fix `new-cap` rule's false positive.
- Fix `astUtils.getPrecedence` function to fix `no-extra-paren` false positive.
- Fix `astUtils.isNullLiteral` function to fix `eqeqeq` false positive.
- Update `func-call-spacing` rule to recognize `ImportExpression` node.
- Update `function-paren-newline` rule to recognize `ImportExpression` node.
- Update `indent` rule to recognize `ImportExpression` node.
- Update `no-extra-parens` rule to recognize `ImportExpression` node.
- Add `env.es2020` to declare new global variables: `BigInt`, `BigInt64Array`, and `BigUint64Array`.
- In passing, add `env.es2017` to declare new global variables: `Atomics` and `SharedBufferArray`. I'm not sure why we have not had it.

(platinumazure: Consumed espree/eslint-visitor-keys, fixed merge conflicts, did some minor chores to get tests to pass-- see commit list.)

**Is there anything you'd like reviewers to focus on?**

There are a ton of remaining stuff (see #11803), but those are "enhancements which increase errors." As following our semver policy, we need a major update to add those... :(

Or maybe can we add those with a minor?

mysticatea: > There are a ton of remaining stuff (see #11803), but those are "enhancements which increase errors." > As following our semver policy, we need a major update to add those... :(
>
> Or maybe can we add those with a minor?

We can update `ImportExpression`-related change safely because nobody has used `ImportExpression` node at all. (previously, it had been represented with `CallExpression` node with `Import` node in experimental. `babel-eslint` and `@typescript-eslint/parser` are using the old form.
mercmobily: What's the status of this PR? #11803 depends on it.
mysticatea: Please track https://github.com/eslint/eslint/issues/11803. This PR is waiting on new `acorn` release and the updates of `espree` and `eslint-visitor-keys`.
mdjermanovic: A small fix for `dot-notation` in PR #12095.

`astUtils.getStaticPropertyName` should be also aware of the `Literal.bigint` property, there is a pending PR #12056 which fixes the same for regex, feel free to copy everything to this PR if that would be more convenient for the project.

There might be an issue with `no-implicit-coercion` which auto-fixes `+foo` to `Number(foo)` as these two are no longer equivalent (`+` throws if `foo` is a bigint).



 



Patch:
diff --git a/.eslintignore b/.eslintignore
index 49737ab8f..ae833e748 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -7,6 +7,7 @@
 /tests/fixtures/**
 /tests/performance/**
 /tmp/**
+/tools/internal-rules/node_modules/**
 /lib/rules/utils/unicode/is-combining-character.js
 test.js
 !.eslintrc.js
diff --git a/.gitignore b/.gitignore
index b550439d3..0bff39a12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ versions.json
 .eslintcache
 .cache
 /packages/**/node_modules
+/tools/internal-rules/node_modules
 /.vscode
 .sublimelinterrc
 .eslint-release-info.json
diff --git a/conf/environments.js b/conf/environments.js
index f77340cea..10e6fdaa7 100644
--- a/conf/environments.js
+++ b/conf/environments.js
@@ -10,15 +10,76 @@
 
 const globals = require("globals");
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Get the object that has differentce.
+ * @param {Record<string,boolean>} current The newer object.
+ * @param {Record<string,boolean>} prev The older object.
+ * @returns {Record<string,boolean>} The difference object.
+ */
+function getDiff(current, prev) {
+    const retv = {};
+
+    for (const [key, value] of Object.entries(current)) {
+        if (!Object.hasOwnProperty.call(prev, key)) {
+            retv[key] = value;
+        }
+    }
+
+    return retv;
+}
+
+const newGlobals2015 = getDiff(globals.es2015, globals.es5); // 19 variables such as Promise, Map, ...
+const newGlobals2017 = {
+    Atomics: false,
+    SharedArrayBuffer: false
+};
+const newGlobals2020 = {
+    BigInt: false,
+    BigInt64Array: false,
+    BigUint64Array: false
+};
+
 //------------------------------------------------------------------------------
 // Public Interface
 //------------------------------------------------------------------------------
 
 /** @type {Map<string, import("../lib/shared/types").Environment>} */
 module.exports = new Map(Object.entries({
+
+    // Language
     builtin: {
         globals: globals.es5
     },
+    es6: {
+        globals: newGlobals2015,
+        parserOptions: {
+            ecmaVersion: 6
+        }
+    },
+    es2015: {
+        globals: newGlobals2015,
+        parserOptions: {
+            ecmaVersion: 6
+        }
+    },
+    es2017: {
+        globals: { ...newGlobals2015, ...newGlobals2017 },
+        parserOptions: {
+            ecmaVersion: 8
+        }
+    },
+    es2020: {
+        globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020 },
+        parserOptions: {
+            ecmaVersion: 11
+        }
+    },
+
+    // Platforms
     browser: {
         globals: globals.browser
     },
@@ -30,6 +91,17 @@ module.exports = new Map(Object.entries({
             }
         }
     },
+    "shared-node-browser": {
+        globals: globals["shared-node-browser"]
+    },
+    worker: {
+        globals: globals.worker
+    },
+    serviceworker: {
+        globals: globals.serviceworker
+    },
+
+    // Frameworks
     commonjs: {
         globals: globals.commonjs,
         parserOptions: {
@@ -38,12 +110,6 @@ module.exports = new Map(Object.entries({
             }
         }
     },
-    "shared-node-browser": {
-        globals: globals["shared-node-browser"]
-    },
-    worker: {
-        globals: globals.worker
-    },
     amd: {
         globals: globals.amd
     },
@@ -86,9 +152,6 @@ module.exports = new Map(Object.entries({
     nashorn: {
         globals: globals.nashorn
     },
-    serviceworker: {
-        globals: globals.serviceworker
-    },
     atomtest: {
         globals: globals.atomtest
     },
@@ -98,12 +161,6 @@ module.exports = new Map(Object.entries({
     webextensions: {
         globals: globals.webextensions
     },
-    es6: {
-        globals: globals.es2015,
-        parserOptions: {
-            ecmaVersion: 6
-        }
-    },
     greasemonkey: {
         globals: globals.greasemonkey
     }
diff --git a/lib/linter/code-path-analysis/code-path-analyzer.js b/lib/linter/code-path-analysis/code-path-analyzer.js
index c1f4a6004..821477aef 100644
--- a/lib/linter/code-path-analysis/code-path-analyzer.js
+++ b/lib/linter/code-path-analysis/code-path-analyzer.js
@@ -526,6 +526,7 @@ function processCodePathToExit(analyzer, node) {
             break;
 
         case "CallExpression":
+        case "ImportExpression":
         case "MemberExpression":
         case "NewExpression":
             state.makeFirstThrowablePathInTryBlock();
diff --git a/lib/rules/func-call-spacing.js b/lib/rules/func-call-spacing.js
index f9c8e7805..e2edd4282 100644
--- a/lib/rules/func-call-spacing.js
+++ b/lib/rules/func-call-spacing.js
@@ -78,21 +78,13 @@ module.exports = {
         /**
          * Check if open space is present in a function name
          * @param {ASTNode} node node to evaluate
+         * @param {Token} leftToken The last token of the callee. This may be the closing parenthesis that encloses the callee.
+         * @param {Token} rightToken Tha first token of the arguments. this is the opening parenthesis that encloses the arguments.
          * @returns {void}
          * @private
          */
-        function checkSpacing(node) {
-            const lastToken = sourceCode.getLastToken(node);
-            const lastCalleeToken = sourceCode.getLastToken(node.callee);
-            const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
-            const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
-
-            // Parens in NewExpression are optional
-            if (!(parenToken && parenToken.range[1] < node.range[1])) {
-                return;
-            }
-
-            const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//gu, "");
+        function checkSpacing(node, leftToken, rightToken) {
+            const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, "");
             const hasWhitespace = /\s/u.test(textBetweenTokens);
             const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
 
@@ -123,7 +115,7 @@ module.exports = {
             if (never && hasWhitespace) {
                 context.report({
                     node,
-                    loc: lastCalleeToken.loc.start,
+                    loc: leftToken.loc.start,
                     messageId: "unexpected",
                     fix(fixer) {
 
@@ -132,7 +124,7 @@ module.exports = {
                          * https://github.com/eslint/eslint/issues/7787
                          */
                         if (!hasNewline) {
-                            return fixer.removeRange([prevToken.range[1], parenToken.range[0]]);
+                            return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
                         }
 
                         return null;
@@ -141,27 +133,45 @@ module.exports = {
             } else if (!never && !hasWhitespace) {
                 context.report({
                     node,
-                    loc: lastCalleeToken.loc.start,
+                    loc: leftToken.loc.start,
                     messageId: "missing",
                     fix(fixer) {
-                        return fixer.insertTextBefore(parenToken, " ");
+                        return fixer.insertTextBefore(rightToken, " ");
                     }
                 });
             } else if (!never && !allowNewlines && hasNewline) {
                 context.report({
                     node,
-                    loc: lastCalleeToken.loc.start,
+                    loc: leftToken.loc.start,
                     messageId: "unexpected",
                     fix(fixer) {
-                        return fixer.replaceTextRange([prevToken.range[1], parenToken.range[0]], " ");
+                        return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
                     }
                 });
             }
         }
 
         return {
-            CallExpression: checkSpacing,
-            NewExpression: checkSpacing
+            "CallExpression, NewExpression"(node) {
+                const lastToken = sourceCode.getLastToken(node);
+                const lastCalleeToken = sourceCode.getLastToken(node.callee);
+                const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
+                const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
+
+                // Parens in NewExpression are optional
+                if (!(parenToken && parenToken.range[1] < node.range[1])) {
+                    return;
+                }
+
+                checkSpacing(node, prevToken, parenToken);
+            },
+
+            ImportExpression(node) {
+                const leftToken = sourceCode.getFirstToken(node);
+                const rightToken = sourceCode.getTokenAfter(leftToken);
+
+                checkSpacing(node, leftToken, rightToken);
+            }
         };
 
     }
diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js
index 0a0b57a37..c9f09fdef 100644
--- a/lib/rules/function-paren-newline.js
+++ b/lib/rules/function-paren-newline.js
@@ -232,25 +232,15 @@ module.exports = {
                     };
                 }
 
-                default:
-                    throw new TypeError(`unexpected node with type ${node.type}`);
-            }
-        }
-
-        /**
-         * Validates the parentheses for a node
-         * @param {ASTNode} node The node with parens
-         * @returns {void}
-         */
-        function validateNode(node) {
-            const parens = getParenTokens(node);
-
-            if (parens) {
-                validateParens(parens, astUtils.isFunction(node) ? node.params : node.arguments);
+                case "ImportExpression": {
+                    const leftParen = sourceCode.getFirstToken(node, 1);
+                    const rightParen = sourceCode.getLastToken(node);
 
-                if (multilineArgumentsOption) {
-                    validateArguments(parens, astUtils.isFunction(node) ? node.params : node.arguments);
+                    return { leftParen, rightParen };
                 }
+
+                default:
+                    throw new TypeError(`unexpected node with type ${node.type}`);
             }
         }
 
@@ -259,11 +249,33 @@ module.exports = {
         //----------------------------------------------------------------------
 
         return {
-            ArrowFunctionExpression: validateNode,
-            CallExpression: validateNode,
-            FunctionDeclaration: validateNode,
-            FunctionExpression: validateNode,
-            NewExpression: validateNode
+            [[
+                "ArrowFunctionExpression",
+                "CallExpression",
+                "FunctionDeclaration",
+                "FunctionExpression",
+                "ImportExpression",
+                "NewExpression"
+            ]](node) {
+                const parens = getParenTokens(node);
+                let params;
+
+                if (node.type === "ImportExpression") {
+                    params = [node.source];
+                } else if (astUtils.isFunction(node)) {
+                    params = node.params;
+                } else {
+                    params = node.arguments;
+                }
+
+                if (parens) {
+                    validateParens(parens, params);
+
+                    if (multilineArgumentsOption) {
+                        validateArguments(parens, params);
+                    }
+                }
+            }
         };
     }
 };
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 345c69e81..79b106313 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -99,7 +99,8 @@ const KNOWN_NODES = new Set([
     "ImportDeclaration",
     "ImportSpecifier",
     "ImportDefaultSpecifier",
-    "ImportNamespaceSpecifier"
+    "ImportNamespaceSpecifier",
+    "ImportExpression"
 ]);
 
 /*
@@ -1109,7 +1110,6 @@ module.exports = {
 
             CallExpression: addFunctionCallIndent,
 
-
             "ClassDeclaration[superClass], ClassExpression[superClass]"(node) {
                 const classToken = sourceCode.getFirstToken(node);
                 const extendsToken = sourceCode.getTokenBefore(node.superClass, astUtils.isNotOpeningParenToken);
@@ -1236,6 +1236,17 @@ module.exports = {
                 }
             },
 
+            ImportExpression(node) {
+                const openingParen = sourceCode.getFirstToken(node, 1);
+                const closingParen = sourceCode.getLastToken(node);
+
+                parameterParens.add(openingParen);
+                parameterParens.add(closingParen);
+                offsets.setDesiredOffset(openingParen, sourceCode.getTokenBefore(openingParen), 0);
+
+                addElementListIndent([node.source], openingParen, closingParen, options.CallExpression.arguments);
+            },
+
             "MemberExpression, JSXMemberExpression, MetaProperty"(node) {
                 const object = node.type === "MetaProperty" ? node.meta : node.object;
                 const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken);
diff --git a/lib/rules/new-cap.js b/lib/rules/new-cap.js
index dcae238d9..cee979310 100644
--- a/lib/rules/new-cap.js
+++ b/lib/rules/new-cap.js
@@ -23,7 +23,8 @@ const CAPS_ALLOWED = [
     "Object",
     "RegExp",
     "String",
-    "Symbol"
+    "Symbol",
+    "BigInt"
 ];
 
 /**
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 5e41d7468..aa455c6a2 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -8,6 +8,7 @@
 // Rule Definition
 //------------------------------------------------------------------------------
 
+const { isParenthesized: isParenthesizedRaw } = require("eslint-utils");
 const astUtils = require("./utils/ast-utils.js");
 
 module.exports = {
@@ -68,7 +69,6 @@ module.exports = {
         const sourceCode = context.getSourceCode();
 
         const tokensToIgnore = new WeakSet();
-        const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode);
         const precedence = astUtils.getPrecedence;
         const ALL_NODES = context.options[0] !== "functions";
         const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
@@ -118,6 +118,16 @@ module.exports = {
             return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
         }
 
+        /**
+         * Determines if a node is surrounded by parentheses.
+         * @param {ASTNode} node - The node to be checked.
+         * @returns {boolean} True if the node is parenthesised.
+         * @private
+         */
+        function isParenthesised(node) {
+            return isParenthesizedRaw(1, node, sourceCode);
+        }
+
         /**
          * Determines if a node is surrounded by parentheses twice.
          * @param {ASTNode} node - The node to be checked.
@@ -125,12 +135,7 @@ module.exports = {
          * @private
          */
         function isParenthesisedTwice(node) {
-            const previousToken = sourceCode.getTokenBefore(node, 1),
-                nextToken = sourceCode.getTokenAfter(node, 1);
-
-            return isParenthesised(node) && previousToken && nextToken &&
-                astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
-                astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
+            return isParenthesizedRaw(2, node, sourceCode);
         }
 
         /**
@@ -406,15 +411,9 @@ module.exports = {
                     report(node.callee);
                 }
             }
-            if (node.arguments.length === 1) {
-                if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
-                    report(node.arguments[0]);
-                }
-            } else {
-                node.arguments
-                    .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
-                    .forEach(report);
-            }
+            node.arguments
+                .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+                .forEach(report);
         }
 
         /**
@@ -712,7 +711,7 @@ module.exports = {
             },
 
             DoWhileStatement(node) {
-                if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+                if (hasExcessParens(node.test) && !isCondAssignException(node)) {
                     report(node.test);
                 }
             },
@@ -837,11 +836,23 @@ module.exports = {
             },
 
             IfStatement(node) {
-                if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+                if (hasExcessParens(node.test) && !isCondAssignException(node)) {
                     report(node.test);
                 }
             },
 
+            ImportExpression(node) {
+                const { source } = node;
+
+                if (source.type === "SequenceExpression") {
+                    if (hasDoubleExcessParens(source)) {
+                        report(source);
+                    }
+                } else if (hasExcessParens(source)) {
+                    report(source);
+                }
+            },
+
             LogicalExpression: checkBinaryLogical,
 
             MemberExpression(node) {
@@ -924,7 +935,7 @@ module.exports = {
             },
 
             SwitchStatement(node) {
-                if (hasDoubleExcessParens(node.discriminant)) {
+                if (hasExcessParens(node.discriminant)) {
                     report(node.discriminant);
                 }
             },
@@ -952,13 +963,13 @@ module.exports = {
             },
 
             WhileStatement(node) {
-                if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
+                if (hasExcessParens(node.test) && !isCondAssignException(node)) {
                     report(node.test);
                 }
             },
 
             WithStatement(node) {
-                if (hasDoubleExcessParens(node.object)) {
+                if (hasExcessParens(node.object)) {
                     report(node.object);
                 }
             },
diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js
index 7c17fc2ec..f0b926e32 100644
--- a/lib/rules/utils/ast-utils.js
+++ b/lib/rules/utils/ast-utils.js
@@ -848,6 +848,7 @@ module.exports = {
                 return 17;
 
             case "CallExpression":
+            case "ImportExpression":
                 return 18;
 
             case "NewExpression":
@@ -1302,7 +1303,7 @@ module.exports = {
          * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
          * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
          */
-        return node.type === "Literal" && node.value === null && !node.regex;
+        return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
     },
 
     /**
diff --git a/package.json b/package.json
index 097466ca9..3bd9f7b12 100644
--- a/package.json
+++ b/package.json
@@ -50,9 +50,9 @@
     "debug": "^4.0.1",
     "doctrine": "^3.0.0",
     "eslint-scope": "^5.0.0",
-    "eslint-utils": "^1.3.1",
-    "eslint-visitor-keys": "^1.0.0",
-    "espree": "^6.0.0",
+    "eslint-utils": "^1.4.0",
+    "eslint-visitor-keys": "^1.1.0",
+    "espree": "^6.1.0",
     "esquery": "^1.0.1",
     "esutils": "^2.0.2",
     "file-entry-cache": "^5.0.1",
@@ -84,7 +84,7 @@
   "devDependencies": {
     "@babel/core": "^7.4.3",
     "@babel/preset-env": "^7.4.3",
-    "acorn": "^6.1.1",
+    "acorn": "^7.0.0",
     "babel-loader": "^8.0.5",
     "chai": "^4.0.1",
     "cheerio": "^0.22.0",


Test patch:
diff --git a/tests/lib/rules/eqeqeq.js b/tests/lib/rules/eqeqeq.js
index e82fae048..b74e9bc2c 100644
--- a/tests/lib/rules/eqeqeq.js
+++ b/tests/lib/rules/eqeqeq.js
@@ -50,7 +50,10 @@ ruleTester.run("eqeqeq", rule, {
         { code: "null != null", options: ["always", { null: "never" }] },
 
         // https://github.com/eslint/eslint/issues/8020
-        { code: "foo === /abc/u", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 6 } }
+        { code: "foo === /abc/u", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 2015 } },
+
+        // bigint
+        { code: "foo === 1n", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 2020 } }
     ],
     invalid: [
         { code: "a == b", errors: [{ messageId: "unexpected", data: wantedEqEqEq, type: "BinaryExpression" }] },
diff --git a/tests/lib/rules/func-call-spacing.js b/tests/lib/rules/func-call-spacing.js
index 460a6022d..d276ae9fc 100644
--- a/tests/lib/rules/func-call-spacing.js
+++ b/tests/lib/rules/func-call-spacing.js
@@ -38,6 +38,10 @@ ruleTester.run("func-call-spacing", rule, {
         "f(0, (1))",
         "describe/**/('foo', function () {});",
         "new (foo())",
+        {
+            code: "import(source)",
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // "never"
         {
@@ -104,6 +108,11 @@ ruleTester.run("func-call-spacing", rule, {
             code: "new (foo())",
             options: ["never"]
         },
+        {
+            code: "import(source)",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // "always"
         {
@@ -154,6 +163,11 @@ ruleTester.run("func-call-spacing", rule, {
             code: "f ();\n t   ();",
             options: ["always"]
         },
+        {
+            code: "import (source)",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // "always", "allowNewlines": true
         {
@@ -199,6 +213,11 @@ ruleTester.run("func-call-spacing", rule, {
         {
             code: "f\r\n();",
             options: ["always", { allowNewlines: true }]
+        },
+        {
+            code: "import\n(source)",
+            options: ["always", { allowNewlines: true }],
+            parserOptions: { ecmaVersion: 2020 }
         }
     ],
     invalid: [
@@ -262,6 +281,12 @@ ruleTester.run("func-call-spacing", rule, {
                 { messageId: "unexpected", type: "CallExpression" }
             ]
         },
+        {
+            code: "import (source);",
+            output: "import(source);",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "unexpected", type: "ImportExpression" }]
+        },
 
         // https://github.com/eslint/eslint/issues/7787
         {
@@ -289,6 +314,12 @@ ruleTester.run("func-call-spacing", rule, {
             output: null, // no change
             errors: [{ messageId: "unexpected", type: "CallExpression" }]
         },
+        {
+            code: "import\n(source);",
+            output: null,
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "unexpected", type: "ImportExpression" }]
+        },
 
         // "never"
         {
@@ -360,6 +391,13 @@ ruleTester.run("func-call-spacing", rule, {
                 { messageId: "unexpected", type: "CallExpression" }
             ]
         },
+        {
+            code: "import (source);",
+            output: "import(source);",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "unexpected", type: "ImportExpression" }]
+        },
 
         // https://github.com/eslint/eslint/issues/7787
         {
@@ -567,6 +605,13 @@ ruleTester.run("func-call-spacing", rule, {
             options: ["always"],
             errors: [{ messageId: "missing", type: "CallExpression" }]
         },
+        {
+            code: "import(source);",
+            output: "import (source);",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "missing", type: "ImportExpression" }]
+        },
         {
             code: "f();\n t();",
             output: "f ();\n t ();",
diff --git a/tests/lib/rules/function-paren-newline.js b/tests/lib/rules/function-paren-newline.js
index 854df7a8c..f918a38ba 100644
--- a/tests/lib/rules/function-paren-newline.js
+++ b/tests/lib/rules/function-paren-newline.js
@@ -87,6 +87,14 @@ ruleTester.run("function-paren-newline", rule, {
             code: "function baz(foo, bar) {}",
             options: ["multiline"]
         },
+        {
+            code: "import(source)",
+            parserOptions: { ecmaVersion: 2020 }
+        },
+        {
+            code: "import(source\n  + ext)",
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // multiline-arguments
         {
@@ -241,6 +249,16 @@ ruleTester.run("function-paren-newline", rule, {
             code: "new (Foo)",
             options: ["multiline-arguments"]
         },
+        {
+            code: "import(source)",
+            options: ["multiline-arguments"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
+        {
+            code: "import(source\n  + ext)",
+            options: ["multiline-arguments"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         {
             code: `
@@ -311,6 +329,11 @@ ruleTester.run("function-paren-newline", rule, {
             `,
             options: ["always"]
         },
+        {
+            code: "import(\n  source\n)",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // never option
         {
@@ -337,6 +360,11 @@ ruleTester.run("function-paren-newline", rule, {
             code: "function baz() {}",
             options: ["never"]
         },
+        {
+            code: "import(source)",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
 
         // minItems option
         {
@@ -363,6 +391,18 @@ ruleTester.run("function-paren-newline", rule, {
             code: "baz(foo, bar);",
             options: [{ minItems: 3 }]
         },
+        {
+            code: "import(source)",
+            options: [{ minItems: 3 }],
+            parserOptions: { ecmaVersion: 2020 }
+        },
+        {
+            code: "import(\n  source\n)",
+            options: [{ minItems: 1 }],
+            parserOptions: { ecmaVersion: 2020 }
+        },
+
+        // consistent option
         {
             code: "foo(bar, baz)",
             options: ["consistent"]
@@ -390,6 +430,16 @@ ruleTester.run("function-paren-newline", rule, {
                 )
             `,
             options: ["consistent"]
+        },
+        {
+            code: "import(source)",
+            options: ["consistent"],
+            parserOptions: { ecmaVersion: 2020 }
+        },
+        {
+            code: "import(\n  source\n)",
+            options: ["consistent"],
+            parserOptions: { ecmaVersion: 2020 }
         }
     ],
 
@@ -514,6 +564,12 @@ ruleTester.run("function-paren-newline", rule, {
             output: null,
             errors: [RIGHT_UNEXPECTED_ERROR]
         },
+        {
+            code: "import(\n  source\n)",
+            output: "import(source)",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
+        },
 
         // multiline-arguments
         {
@@ -701,6 +757,20 @@ ruleTester.run("function-paren-newline", rule, {
             options: ["multiline-arguments"],
             errors: [RIGHT_UNEXPECTED_ERROR]
         },
+        {
+            code: "import(source\n)",
+            output: "import(source)",
+            options: ["multiline-arguments"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: "import(\n  source)",
+            output: "import(\n  source\n)",
+            options: ["multiline-arguments"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [RIGHT_MISSING_ERROR]
+        },
 
         // always option
         {
@@ -779,6 +849,13 @@ ruleTester.run("function-paren-newline", rule, {
             options: ["always"],
             errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR]
         },
+        {
+            code: "import(source)",
+            output: "import(\nsource\n)",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR]
+        },
 
         // never option
         {
@@ -888,6 +965,13 @@ ruleTester.run("function-paren-newline", rule, {
             options: ["never"],
             errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
         },
+        {
+            code: "import(\n  source\n)",
+            output: "import(source)",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
+        },
 
         // minItems option
         {
@@ -928,6 +1012,22 @@ ruleTester.run("function-paren-newline", rule, {
             options: [{ minItems: 3 }],
             errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
         },
+        {
+            code: "import(\n  source\n)",
+            output: "import(source)",
+            options: [{ minItems: 3 }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: "import(source)",
+            output: "import(\nsource\n)",
+            options: [{ minItems: 1 }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR]
+        },
+
+        // consistent option
         {
             code: `
                 foo(
@@ -954,6 +1054,20 @@ ruleTester.run("function-paren-newline", rule, {
             `,
             options: ["consistent"],
             errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: "import(source\n)",
+            output: "import(source)",
+            options: ["consistent"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: "import(\n  source)",
+            output: "import(\n  source\n)",
+            options: ["consistent"],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [RIGHT_MISSING_ERROR]
         }
     ]
 });
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index 48d6e4eb1..b38c97e96 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -4949,7 +4949,19 @@ ruleTester.run("indent", rule, {
             { // comment
                 bar
             }];
-        `
+        `,
+
+        // import expressions
+        {
+            code: unIndent`
+                import(
+                    // before
+                    source
+                    // after
+                )
+            `,
+            parserOptions: { ecmaVersion: 2020 }
+        }
     ],
 
     invalid: [
@@ -9593,6 +9605,25 @@ ruleTester.run("indent", rule, {
                 }];
             `,
             errors: expectedErrors([5, 0, 4, "Line"])
+        },
+
+        // import expressions
+        {
+            code: unIndent`
+                import(
+                source
+                    )
+            `,
+            output: unIndent`
+                import(
+                    source
+                )
+            `,
+            parserOptions: { ecmaVersion: 2020 },
+            errors: expectedErrors([
+                [2, 4, 0, "Identifier"],
+                [3, 0, 4, "Punctuator"]
+            ])
         }
     ]
 });
diff --git a/tests/lib/rules/new-cap.js b/tests/lib/rules/new-cap.js
index ba321ec31..706bca198 100644
--- a/tests/lib/rules/new-cap.js
+++ b/tests/lib/rules/new-cap.js
@@ -43,6 +43,7 @@ ruleTester.run("new-cap", rule, {
         "var x = RegExp(42)",
         "var x = String(42)",
         "var x = Symbol('symbol')",
+        "var x = BigInt('1n')",
         "var x = _();",
         "var x = $();",
         { code: "var x = Foo(42)", options: [{ capIsNew: false }] },
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 86b5ae4d6..2b20f20f6 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -44,7 +44,7 @@ function invalid(code, output, type, line, config) {
 
 const ruleTester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2018,
+        ecmaVersion: 2020,
         ecmaFeatures: {
             jsx: true
         }
@@ -97,6 +97,8 @@ ruleTester.run("no-extra-parens", rule, {
         "(new (Foo || Bar))()",
         "(2 + 3) ** 4",
         "2 ** (2 + 3)",
+        "new (import(source))",
+        "import((s,t))",
 
         // same precedence
         "a, b, c",
@@ -1820,6 +1822,29 @@ ruleTester.run("no-extra-parens", rule, {
                     messageId: "unexpected"
                 }
             )
-        }
+        },
+
+        // import expressions
+        invalid(
+            "import((source))",
+            "import(source)",
+            "Identifier",
+            1,
+            { parserOptions: { ecmaVersion: 2020 } }
+        ),
+        invalid(
+            "import((source = 'foo.js'))",
+            "import(source = 'foo.js')",
+            "AssignmentExpression",
+            1,
+            { parserOptions: { ecmaVersion: 2020 } }
+        ),
+        invalid(
+            "import(((s,t)))",
+            "import((s,t))",
+            "SequenceExpression",
+            1,
+            { parserOptions: { ecmaVersion: 2020 } }
+        )
     ]
 });