Repository: eslint/eslint
Pull request #12771 (2020-01-10 18:07:45)

Fix: Display pipe character correctly in test output
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[X] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

#12031 introduced an invalid regex, which caused the `|` character to be displayed as `\u007c` in test output.
This ofc ruins any output with an OR (`||` / `|`). I finally got annoyed enough to look into a fix.

![image](https://user-images.githubusercontent.com/7462525/72175521-2f983480-3391-11ea-9bb8-da7182244c47.png)

This just fixes the output and adds a test to make sure it doesn't happen again.



Patch:
diff --git a/README.md b/README.md
index 303b920d3..65c572719 100644
--- a/README.md
+++ b/README.md
@@ -264,7 +264,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <h3>Gold Sponsors</h3>
 <p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.facebook.com"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://medium.com/@niksundin/best-web-design-companies-1872e445775f"><img src="https://images.opencollective.com/top-web-design-agencies/d92d747/logo.png" alt="Top Web Design Agencies" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://www.crosswordsolver.com"><img src="https://images.opencollective.com/crosswordsolver/d4481d6/logo.png" alt="Crosswordsolver" height="32"></a> <a href="https://www.codacy.com/?utm_source=eslint&utm_medium=cpm&utm_campaign=eslint-sponsorship"><img src="https://images.opencollective.com/codacy/ed22716/logo.png" alt="Codacy" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
+<p><a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://www.bonus.com.de/freispiele"><img src="https://images.opencollective.com/bonusfinder-deutschland/646169e/logo.png" alt="BonusFinder Deutschland" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://www.crosswordsolver.com"><img src="https://images.opencollective.com/crosswordsolver/d4481d6/logo.png" alt="Crosswordsolver" height="32"></a> <a href="https://www.codacy.com/?utm_source=eslint&utm_medium=cpm&utm_campaign=eslint-sponsorship"><img src="https://images.opencollective.com/codacy/ed22716/logo.png" alt="Codacy" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://medium.com/@niksundin/best-web-design-companies-1872e445775f"><img src="https://images.opencollective.com/top-web-design-agencies/d92d747/logo.png" alt="Top Web Design Agencies" height="32"></a> <a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/docs/rules/sort-keys.md b/docs/rules/sort-keys.md
index 199d7bbc8..937a7da81 100644
--- a/docs/rules/sort-keys.md
+++ b/docs/rules/sort-keys.md
@@ -1,6 +1,6 @@
 # require object keys to be sorted (sort-keys)
 
-When declaring multiple properties, some developers prefer to sort property names alphabetically to be able to find necessary property easier at the later time. Others feel that it adds complexity and becomes burden to maintain.
+When declaring multiple properties, some developers prefer to sort property names alphabetically to more easily find and/or diff necessary properties at a later time. Others feel that it adds complexity and becomes burden to maintain.
 
 ## Rule Details
 
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 265ca4fea..e0ab32b4e 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -169,6 +169,28 @@ module.exports = {
             return ruleApplies(node) && isParenthesisedTwice(node);
         }
 
+        /**
+         * Determines if a node that is expected to be parenthesised is surrounded by
+         * (potentially) invalid extra parentheses with considering precedence level of the node.
+         * If the preference level of the node is not higher or equal to precedence lower limit, it also checks
+         * whether the node is surrounded by parentheses twice or not.
+         * @param {ASTNode} node The node to be checked.
+         * @param {number} precedenceLowerLimit The lower limit of precedence.
+         * @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
+         * @private
+         */
+        function hasExcessParensWithPrecedence(node, precedenceLowerLimit) {
+            if (ruleApplies(node) && isParenthesised(node)) {
+                if (
+                    precedence(node) >= precedenceLowerLimit ||
+                    isParenthesisedTwice(node)
+                ) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         /**
          * Determines if a node test expression is allowed to have a parenthesised assignment
          * @param {ASTNode} node The node to be checked.
@@ -370,17 +392,13 @@ module.exports = {
         }
 
         /**
-         * Evaluate Unary update
+         * Evaluate a argument of the node.
          * @param {ASTNode} node node to evaluate
          * @returns {void}
          * @private
          */
-        function checkUnaryUpdate(node) {
-            if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
-                return;
-            }
-
-            if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) {
+        function checkArgumentWithPrecedence(node) {
+            if (hasExcessParensWithPrecedence(node.argument, precedence(node))) {
                 report(node.argument);
             }
         }
@@ -411,7 +429,7 @@ module.exports = {
         function checkCallNew(node) {
             const callee = node.callee;
 
-            if (hasExcessParens(callee) && precedence(callee) >= precedence(node)) {
+            if (hasExcessParensWithPrecedence(callee, precedence(node))) {
                 const hasNewParensException = callee.type === "NewExpression" && !isNewExpressionWithParens(callee);
 
                 if (
@@ -428,7 +446,7 @@ module.exports = {
                 }
             }
             node.arguments
-                .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+                .filter(arg => hasExcessParensWithPrecedence(arg, PRECEDENCE_OF_ASSIGNMENT_EXPR))
                 .forEach(report);
         }
 
@@ -443,15 +461,26 @@ module.exports = {
             const leftPrecedence = precedence(node.left);
             const rightPrecedence = precedence(node.right);
             const isExponentiation = node.operator === "**";
-            const shouldSkipLeft = (NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression")) ||
-              node.left.type === "UnaryExpression" && isExponentiation;
+            const shouldSkipLeft = NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression");
             const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression");
 
-            if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) {
-                report(node.left);
+            if (!shouldSkipLeft && hasExcessParens(node.left)) {
+                if (
+                    !(node.left.type === "UnaryExpression" && isExponentiation) &&
+                    (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) ||
+                    isParenthesisedTwice(node.left)
+                ) {
+                    report(node.left);
+                }
             }
-            if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) {
-                report(node.right);
+
+            if (!shouldSkipRight && hasExcessParens(node.right)) {
+                if (
+                    (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation)) ||
+                    isParenthesisedTwice(node.right)
+                ) {
+                    report(node.right);
+                }
             }
         }
 
@@ -484,11 +513,7 @@ module.exports = {
          * @returns {void}
          */
         function checkSpreadOperator(node) {
-            const hasExtraParens = precedence(node.argument) >= PRECEDENCE_OF_ASSIGNMENT_EXPR
-                ? hasExcessParens(node.argument)
-                : hasDoubleExcessParens(node.argument);
-
-            if (hasExtraParens) {
+            if (hasExcessParensWithPrecedence(node.argument, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                 report(node.argument);
             }
         }
@@ -650,7 +675,7 @@ module.exports = {
         return {
             ArrayExpression(node) {
                 node.elements
-                    .filter(e => e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+                    .filter(e => e && hasExcessParensWithPrecedence(e, PRECEDENCE_OF_ASSIGNMENT_EXPR))
                     .forEach(report);
             },
 
@@ -673,18 +698,14 @@ module.exports = {
                     if (astUtils.isOpeningParenToken(tokenBeforeFirst) && astUtils.isOpeningBraceToken(firstBodyToken)) {
                         tokensToIgnore.add(firstBodyToken);
                     }
-                    if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                    if (hasExcessParensWithPrecedence(node.body, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                         report(node.body);
                     }
                 }
             },
 
             AssignmentExpression(node) {
-                if (isReturnAssignException(node)) {
-                    return;
-                }
-
-                if (hasExcessParens(node.right) && precedence(node.right) >= precedence(node)) {
+                if (!isReturnAssignException(node) && hasExcessParensWithPrecedence(node.right, precedence(node))) {
                     report(node.right);
                 }
             },
@@ -701,8 +722,8 @@ module.exports = {
 
             ClassBody(node) {
                 node.body
-                    .filter(member => member.type === "MethodDefinition" && member.computed &&
-                        member.key && hasExcessParens(member.key) && precedence(member.key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
+                    .filter(member => member.type === "MethodDefinition" && member.computed && member.key)
+                    .filter(member => hasExcessParensWithPrecedence(member.key, PRECEDENCE_OF_ASSIGNMENT_EXPR))
                     .forEach(member => report(member.key));
             },
 
@@ -710,16 +731,18 @@ module.exports = {
                 if (isReturnAssignException(node)) {
                     return;
                 }
-
-                if (hasExcessParens(node.test) && precedence(node.test) >= precedence({ type: "LogicalExpression", operator: "||" })) {
+                if (
+                    !isCondAssignException(node) &&
+                    hasExcessParensWithPrecedence(node.test, precedence({ type: "LogicalExpression", operator: "||" }))
+                ) {
                     report(node.test);
                 }
 
-                if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                if (hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                     report(node.consequent);
                 }
 
-                if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                if (hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                     report(node.alternate);
                 }
             },
@@ -756,9 +779,19 @@ module.exports = {
                         tokensToIgnore.add(firstLeftToken);
                     }
                 }
-                if (!(node.type === "ForOfStatement" && node.right.type === "SequenceExpression") && hasExcessParens(node.right)) {
+
+                if (node.type === "ForOfStatement") {
+                    const hasExtraParens = node.right.type === "SequenceExpression"
+                        ? hasDoubleExcessParens(node.right)
+                        : hasExcessParens(node.right);
+
+                    if (hasExtraParens) {
+                        report(node.right);
+                    }
+                } else if (hasExcessParens(node.right)) {
                     report(node.right);
                 }
+
                 if (hasExcessParens(node.left)) {
                     report(node.left);
                 }
@@ -910,18 +943,15 @@ module.exports = {
 
             ObjectExpression(node) {
                 node.properties
-                    .filter(property => {
-                        const value = property.value;
-
-                        return value && hasExcessParens(value) && precedence(value) >= PRECEDENCE_OF_ASSIGNMENT_EXPR;
-                    }).forEach(property => report(property.value));
+                    .filter(property => property.value && hasExcessParensWithPrecedence(property.value, PRECEDENCE_OF_ASSIGNMENT_EXPR))
+                    .forEach(property => report(property.value));
             },
 
             Property(node) {
                 if (node.computed) {
                     const { key } = node;
 
-                    if (key && hasExcessParens(key) && precedence(key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                    if (key && hasExcessParensWithPrecedence(key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                         report(key);
                     }
                 }
@@ -944,8 +974,10 @@ module.exports = {
             },
 
             SequenceExpression(node) {
+                const precedenceOfNode = precedence(node);
+
                 node.expressions
-                    .filter(e => hasExcessParens(e) && precedence(e) >= precedence(node))
+                    .filter(e => hasExcessParensWithPrecedence(e, precedenceOfNode))
                     .forEach(report);
             },
 
@@ -969,16 +1001,17 @@ module.exports = {
                 }
             },
 
-            UnaryExpression: checkUnaryUpdate,
-            UpdateExpression: checkUnaryUpdate,
-            AwaitExpression: checkUnaryUpdate,
+            UnaryExpression: checkArgumentWithPrecedence,
+            UpdateExpression: checkArgumentWithPrecedence,
+            AwaitExpression: checkArgumentWithPrecedence,
 
             VariableDeclarator(node) {
-                if (node.init && hasExcessParens(node.init) &&
-                        precedence(node.init) >= PRECEDENCE_OF_ASSIGNMENT_EXPR &&
+                if (
+                    node.init && hasExcessParensWithPrecedence(node.init, PRECEDENCE_OF_ASSIGNMENT_EXPR) &&
 
-                        // RegExp literal is allowed to have parens (#1589)
-                        !(node.init.type === "Literal" && node.init.regex)) {
+                    // RegExp literal is allowed to have parens (#1589)
+                    !(node.init.type === "Literal" && node.init.regex)
+                ) {
                     report(node.init);
                 }
             },
@@ -1023,7 +1056,7 @@ module.exports = {
             AssignmentPattern(node) {
                 const { right } = node;
 
-                if (right && hasExcessParens(right) && precedence(right) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                if (right && hasExcessParensWithPrecedence(right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
                     report(right);
                 }
             }
diff --git a/lib/rules/no-restricted-imports.js b/lib/rules/no-restricted-imports.js
index ec0696f99..5514b979c 100644
--- a/lib/rules/no-restricted-imports.js
+++ b/lib/rules/no-restricted-imports.js
@@ -64,7 +64,11 @@ module.exports = {
 
             everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
             // eslint-disable-next-line eslint-plugin/report-message-format
-            everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}"
+            everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
+
+            importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
+            // eslint-disable-next-line eslint-plugin/report-message-format
+            importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
         },
 
         schema: {
@@ -95,6 +99,11 @@ module.exports = {
         const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
         const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
 
+        // if no imports are restricted we don"t need to check
+        if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
+            return {};
+        }
+
         const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
             if (typeof importSource === "string") {
                 memo[importSource] = { message: null };
@@ -107,40 +116,68 @@ module.exports = {
             return memo;
         }, {});
 
-        // if no imports are restricted we don"t need to check
-        if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
-            return {};
-        }
-
         const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
 
-        /**
-         * Checks to see if "*" is being used to import everything.
-         * @param {Set.<string>} importNames Set of import names that are being imported
-         * @returns {boolean} whether everything is imported or not
-         */
-        function isEverythingImported(importNames) {
-            return importNames.has("*");
-        }
-
         /**
          * Report a restricted path.
+         * @param {string} importSource path of the import
+         * @param {Map<string,Object[]>} importNames Map of import names that are being imported
          * @param {node} node representing the restricted path reference
          * @returns {void}
          * @private
          */
-        function reportPath(node) {
-            const importSource = node.source.value.trim();
-            const customMessage = restrictedPathMessages[importSource] && restrictedPathMessages[importSource].message;
+        function checkRestrictedPathAndReport(importSource, importNames, node) {
+            if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
+                return;
+            }
 
-            context.report({
-                node,
-                messageId: customMessage ? "pathWithCustomMessage" : "path",
-                data: {
-                    importSource,
-                    customMessage
+            const customMessage = restrictedPathMessages[importSource].message;
+            const restrictedImportNames = restrictedPathMessages[importSource].importNames;
+
+            if (restrictedImportNames) {
+                if (importNames.has("*")) {
+                    const specifierData = importNames.get("*")[0];
+
+                    context.report({
+                        node,
+                        messageId: customMessage ? "everythingWithCustomMessage" : "everything",
+                        loc: specifierData.loc,
+                        data: {
+                            importSource,
+                            importNames: restrictedImportNames,
+                            customMessage
+                        }
+                    });
                 }
-            });
+
+                restrictedImportNames.forEach(importName => {
+                    if (importNames.has(importName)) {
+                        const specifiers = importNames.get(importName);
+
+                        specifiers.forEach(specifier => {
+                            context.report({
+                                node,
+                                messageId: customMessage ? "importNameWithCustomMessage" : "importName",
+                                loc: specifier.loc,
+                                data: {
+                                    importSource,
+                                    customMessage,
+                                    importName
+                                }
+                            });
+                        });
+                    }
+                });
+            } else {
+                context.report({
+                    node,
+                    messageId: customMessage ? "pathWithCustomMessage" : "path",
+                    data: {
+                        importSource,
+                        customMessage
+                    }
+                });
+            }
         }
 
         /**
@@ -161,75 +198,6 @@ module.exports = {
             });
         }
 
-        /**
-         * Report a restricted path specifically when using the '*' import.
-         * @param {string} importSource path of the import
-         * @param {node} node representing the restricted path reference
-         * @returns {void}
-         * @private
-         */
-        function reportPathForEverythingImported(importSource, node) {
-            const importNames = restrictedPathMessages[importSource].importNames;
-            const customMessage = restrictedPathMessages[importSource] && restrictedPathMessages[importSource].message;
-
-            context.report({
-                node,
-                messageId: customMessage ? "everythingWithCustomMessage" : "everything",
-                data: {
-                    importSource,
-                    importNames,
-                    customMessage
-                }
-            });
-        }
-
-        /**
-         * Check if the given importSource is restricted because '*' is being imported.
-         * @param {string} importSource path of the import
-         * @param {Set.<string>} importNames Set of import names that are being imported
-         * @returns {boolean} whether the path is restricted
-         * @private
-         */
-        function isRestrictedForEverythingImported(importSource, importNames) {
-            return Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource) &&
-                restrictedPathMessages[importSource].importNames &&
-                isEverythingImported(importNames);
-        }
-
-        /**
-         * Check if the given importNames are restricted given a list of restrictedImportNames.
-         * @param {Set.<string>} importNames Set of import names that are being imported
-         * @param {string[]} restrictedImportNames array of import names that are restricted for this import
-         * @returns {boolean} whether the objectName is restricted
-         * @private
-         */
-        function isRestrictedObject(importNames, restrictedImportNames) {
-            return restrictedImportNames.some(restrictedObjectName => (
-                importNames.has(restrictedObjectName)
-            ));
-        }
-
-        /**
-         * Check if the given importSource is a restricted path.
-         * @param {string} importSource path of the import
-         * @param {Set.<string>} importNames Set of import names that are being imported
-         * @returns {boolean} whether the variable is a restricted path or not
-         * @private
-         */
-        function isRestrictedPath(importSource, importNames) {
-            let isRestricted = false;
-
-            if (Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
-                if (restrictedPathMessages[importSource].importNames) {
-                    isRestricted = isRestrictedObject(importNames, restrictedPathMessages[importSource].importNames);
-                } else {
-                    isRestricted = true;
-                }
-            }
-
-            return isRestricted;
-        }
-
         /**
          * Check if the given importSource is restricted by a pattern.
          * @param {string} importSource path of the import
@@ -248,26 +216,33 @@ module.exports = {
          */
         function checkNode(node) {
             const importSource = node.source.value.trim();
-            const importNames = node.specifiers ? node.specifiers.reduce((set, specifier) => {
+            const importNames = node.specifiers ? node.specifiers.reduce((map, specifier) => {
+                let name;
+                const specifierData = { loc: specifier.loc };
+
                 if (specifier.type === "ImportDefaultSpecifier") {
-                    set.add("default");
+                    name = "default";
                 } else if (specifier.type === "ImportNamespaceSpecifier") {
-                    set.add("*");
+                    name = "*";
                 } else if (specifier.imported) {
-                    set.add(specifier.imported.name);
+                    name = specifier.imported.name;
                 } else if (specifier.local) {
-                    set.add(specifier.local.name);
+                    name = specifier.local.name;
                 }
-                return set;
-            }, new Set()) : new Set();
 
-            if (isRestrictedForEverythingImported(importSource, importNames)) {
-                reportPathForEverythingImported(importSource, node);
-            }
+                if (name) {
+                    if (map.has(name)) {
+                        map.get(name).push(specifierData);
+                    } else {
+                        map.set(name, [specifierData]);
+                    }
+                }
+
+                return map;
+            }, new Map()) : new Map();
+
+            checkRestrictedPathAndReport(importSource, importNames, node);
 
-            if (isRestrictedPath(importSource, importNames)) {
-                reportPath(node);
-            }
             if (isRestrictedPattern(importSource)) {
                 reportPathForPatterns(node);
             }


Test patch:
diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js
index 4b8c38692..2ac26534f 100644
--- a/lib/rule-tester/rule-tester.js
+++ b/lib/rule-tester/rule-tester.js
@@ -174,7 +174,7 @@ function freezeDeeply(x) {
  */
 function sanitize(text) {
     return text.replace(
-        /[\u0000-\u0009|\u000b-\u001a]/gu, // eslint-disable-line no-control-regex
+        /[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex
         c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
     );
 }
diff --git a/tests/lib/rule-tester/rule-tester.js b/tests/lib/rule-tester/rule-tester.js
index 4abb053ab..7f2b6de67 100644
--- a/tests/lib/rule-tester/rule-tester.js
+++ b/tests/lib/rule-tester/rule-tester.js
@@ -1281,6 +1281,20 @@ describe("RuleTester", () => {
             });
             sinon.assert.calledWith(spyRuleTesterIt, "\\u0000");
         });
+        it("should present the pipe character correctly", () => {
+            const code = "var foo = bar || baz;";
+
+            ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                valid: [],
+                invalid: [
+                    {
+                        code,
+                        errors: [/^Bad var/u]
+                    }
+                ]
+            });
+            sinon.assert.calledWith(spyRuleTesterIt, code);
+        });
 
     });
 });
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 707992f80..26f682bae 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -199,6 +199,7 @@ ruleTester.run("no-extra-parens", rule, {
         "var regex = (/^a$/);",
         "function a(){ return (/^a$/); }",
         "function a(){ return (/^a$/).test('a'); }",
+        "var isA = ((/^a$/)).test('a');",
 
         // IIFE is allowed to have parens in any position (#655)
         "var foo = (function() { return bar(); }())",
@@ -250,17 +251,22 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "var a = (b = c)", options: ["functions"] },
         { code: "_ => (a = 0)", options: ["functions"] },
 
-        // ["all", {conditionalAssign: false}] enables extra parens around conditional assignments
+        // ["all", { conditionalAssign: false }] enables extra parens around conditional assignments
         { code: "while ((foo = bar())) {}", options: ["all", { conditionalAssign: false }] },
         { code: "if ((foo = bar())) {}", options: ["all", { conditionalAssign: false }] },
         { code: "do; while ((foo = bar()))", options: ["all", { conditionalAssign: false }] },
         { code: "for (;(a = b););", options: ["all", { conditionalAssign: false }] },
+        { code: "var a = ((b = c)) ? foo : bar;", options: ["all", { conditionalAssign: false }] },
 
         // ["all", { nestedBinaryExpressions: false }] enables extra parens around conditional assignments
         { code: "a + (b * c)", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "(a * b) + c", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "(a * b) / c", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "a || (b && c)", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "a + ((b * c))", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "((a * b)) + c", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "((a * b)) / c", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "a || ((b && c))", options: ["all", { nestedBinaryExpressions: false }] },
 
         // ["all", { returnAssign: false }] enables extra parens around expressions returned by return statements
         { code: "function a(b) { return b || c; }", options: ["all", { returnAssign: false }] },
@@ -276,6 +282,8 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "b => { return (b = 1) };", options: ["all", { returnAssign: false }] },
         { code: "b => { return (b = c) || (b = d) };", options: ["all", { returnAssign: false }] },
         { code: "b => { return c ? (d = b) : (e = b) };", options: ["all", { returnAssign: false }] },
+        { code: "function a(b) { return ((b = 1)); }", options: ["all", { returnAssign: false }] },
+        { code: "b => ((b = 1));", options: ["all", { returnAssign: false }] },
 
         // https://github.com/eslint/eslint/issues/3653
         "(function(){}).foo(), 1, 2;",
@@ -352,6 +360,8 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "foo in (bar in baz)", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "foo + (bar + baz)", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "foo && (bar && baz)", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "((foo instanceof bar)) instanceof baz", options: ["all", { nestedBinaryExpressions: false }] },
+        { code: "((foo in bar)) in baz", options: ["all", { nestedBinaryExpressions: false }] },
 
         // https://github.com/eslint/eslint/issues/9019
         "(async function() {});",
@@ -452,7 +462,9 @@ ruleTester.run("no-extra-parens", rule, {
 
         // ["all", { enforceForSequenceExpressions: false }]
         { code: "(a, b)", options: ["all", { enforceForSequenceExpressions: false }] },
+        { code: "((a, b))", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "(foo(), bar());", options: ["all", { enforceForSequenceExpressions: false }] },
+        { code: "((foo(), bar()));", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "if((a, b)){}", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "while ((val = foo(), val < 10));", options: ["all", { enforceForSequenceExpressions: false }] },
 
@@ -463,6 +475,7 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "(new foo(bar)).baz", options: ["all", { enforceForNewInMemberExpressions: false }] },
         { code: "(new foo.bar()).baz", options: ["all", { enforceForNewInMemberExpressions: false }] },
         { code: "(new foo.bar()).baz()", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "((new foo.bar())).baz()", options: ["all", { enforceForNewInMemberExpressions: false }] },
 
         "let a = [ ...b ]",
         "let a = { ...b }",
@@ -481,6 +494,7 @@ ruleTester.run("no-extra-parens", rule, {
         "const A = class extends B {}",
         "class A extends (B=C) {}",
         "const A = class extends (B=C) {}",
+        "class A extends (++foo) {}",
         "() => ({ foo: 1 })",
         "() => ({ foo: 1 }).foo",
         "() => ({ foo: 1 }.foo().bar).baz.qux()",
@@ -631,11 +645,15 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("-(-foo)", "- -foo", "UnaryExpression"),
         invalid("+(-foo)", "+-foo", "UnaryExpression"),
         invalid("-(+foo)", "-+foo", "UnaryExpression"),
+        invalid("-((bar+foo))", "-(bar+foo)", "BinaryExpression"),
+        invalid("+((bar-foo))", "+(bar-foo)", "BinaryExpression"),
         invalid("++(foo)", "++foo", "Identifier"),
         invalid("--(foo)", "--foo", "Identifier"),
         invalid("(a || b) ? c : d", "a || b ? c : d", "LogicalExpression"),
         invalid("a ? (b = c) : d", "a ? b = c : d", "AssignmentExpression"),
         invalid("a ? b : (c = d)", "a ? b : c = d", "AssignmentExpression"),
+        invalid("(c = d) ? (b) : c", "(c = d) ? b : c", "Identifier", null, { options: ["all", { conditionalAssign: false }] }),
+        invalid("(c = d) ? b : (c)", "(c = d) ? b : c", "Identifier", null, { options: ["all", { conditionalAssign: false }] }),
         invalid("f((a = b))", "f(a = b)", "AssignmentExpression"),
         invalid("a, (b = c)", "a, b = c", "AssignmentExpression"),
         invalid("a = (b * c)", "a = b * c", "BinaryExpression"),
@@ -647,6 +665,9 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("(2 ** 3)", "2 ** 3", "BinaryExpression", null),
         invalid("(2 ** 3) + 1", "2 ** 3 + 1", "BinaryExpression", null),
         invalid("1 - (2 ** 3)", "1 - 2 ** 3", "BinaryExpression", null),
+        invalid("-((2 ** 3))", "-(2 ** 3)", "BinaryExpression", null),
+        invalid("typeof ((a ** b));", "typeof (a ** b);", "BinaryExpression", null),
+        invalid("((-2)) ** 3", "(-2) ** 3", "UnaryExpression", null),
 
         invalid("a = (b * c)", "a = b * c", "BinaryExpression", null, { options: ["all", { nestedBinaryExpressions: false }] }),
         invalid("(b * c)", "b * c", "BinaryExpression", null, { options: ["all", { nestedBinaryExpressions: false }] }),
@@ -735,6 +756,7 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("bar((class{}).foo(), 0);", "bar(class{}.foo(), 0);", "ClassExpression", null),
         invalid("bar[(class{}).foo()];", "bar[class{}.foo()];", "ClassExpression", null),
         invalid("var bar = (class{}).foo();", "var bar = class{}.foo();", "ClassExpression", null),
+        invalid("var foo = ((bar, baz));", "var foo = (bar, baz);", "SequenceExpression", null),
 
         // https://github.com/eslint/eslint/issues/4608
         invalid("function *a() { yield (b); }", "function *a() { yield b; }", "Identifier", null),
@@ -1023,6 +1045,7 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("async function a() { await (a()); }", "async function a() { await a(); }", "CallExpression", null),
         invalid("async function a() { await (+a); }", "async function a() { await +a; }", "UnaryExpression", null),
         invalid("async function a() { +(await a); }", "async function a() { +await a; }", "AwaitExpression", null),
+        invalid("async function a() { await ((a,b)); }", "async function a() { await (a,b); }", "SequenceExpression", null),
         invalid("(foo) instanceof bar", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
         invalid("(foo) in bar", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
         invalid("(foo) + bar", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
@@ -1300,6 +1323,12 @@ ruleTester.run("no-extra-parens", rule, {
             "AssignmentExpression",
             1
         ),
+        invalid(
+            "class A extends ((++foo)) {}",
+            "class A extends (++foo) {}",
+            "UpdateExpression",
+            1
+        ),
         invalid(
             "for (foo of(bar));",
             "for (foo of bar);",
@@ -1312,6 +1341,12 @@ ruleTester.run("no-extra-parens", rule, {
             "Identifier",
             1
         ),
+        invalid(
+            "for (foo of ((bar, baz)));",
+            "for (foo of (bar, baz));",
+            "SequenceExpression",
+            1
+        ),
         invalid(
             "for ((foo)in bar);",
             "for (foo in bar);",
@@ -2041,6 +2076,98 @@ ruleTester.run("no-extra-parens", rule, {
             "SequenceExpression",
             1,
             { parserOptions: { ecmaVersion: 2020 } }
-        )
+        ),
+
+        // https://github.com/eslint/eslint/issues/12127
+        {
+            code: "[1, ((2, 3))];",
+            output: "[1, (2, 3)];",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "const foo = () => ((bar, baz));",
+            output: "const foo = () => (bar, baz);",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "foo = ((bar, baz));",
+            output: "foo = (bar, baz);",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "foo + ((bar + baz));",
+            output: "foo + (bar + baz);",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "((foo + bar)) + baz;",
+            output: "(foo + bar) + baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "foo * ((bar + baz));",
+            output: "foo * (bar + baz);",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "((foo + bar)) * baz;",
+            output: "(foo + bar) * baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "new A(((foo, bar)))",
+            output: "new A((foo, bar))",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "class A{ [((foo, bar))]() {} }",
+            output: "class A{ [(foo, bar)]() {} }",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "new ((A, B))()",
+            output: "new (A, B)()",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "((foo, bar)) ? bar : baz;",
+            output: "(foo, bar) ? bar : baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "((f ? o : o)) ? bar : baz;",
+            output: "(f ? o : o) ? bar : baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "((f = oo)) ? bar : baz;",
+            output: "(f = oo) ? bar : baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "foo ? ((bar, baz)) : baz;",
+            output: "foo ? (bar, baz) : baz;",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "foo ? bar : ((bar, baz));",
+            output: "foo ? bar : (bar, baz);",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "function foo(bar = ((baz1, baz2))) {}",
+            output: "function foo(bar = (baz1, baz2)) {}",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "var foo = { bar: ((baz1, baz2)) };",
+            output: "var foo = { bar: (baz1, baz2) };",
+            errors: [{ messageId: "unexpected" }]
+        },
+        {
+            code: "var foo = { [((bar1, bar2))]: baz };",
+            output: "var foo = { [(bar1, bar2)]: baz };",
+            errors: [{ messageId: "unexpected" }]
+        }
     ]
 });
diff --git a/tests/lib/rules/no-restricted-imports.js b/tests/lib/rules/no-restricted-imports.js
index 15ee089b2..958395f45 100644
--- a/tests/lib/rules/no-restricted-imports.js
+++ b/tests/lib/rules/no-restricted-imports.js
@@ -164,44 +164,102 @@ ruleTester.run("no-restricted-imports", rule, {
                     message: "Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead."
                 }]
             }]
+        },
+        {
+            code: "import {\nAllowedObject,\nDisallowedObject, // eslint-disable-line\n} from \"foo\";",
+            options: [{ paths: [{ name: "foo", importNames: ["DisallowedObject"] }] }]
         }
     ],
     invalid: [{
         code: "import \"fs\"",
         options: ["fs"],
-        errors: [{ message: "'fs' import is restricted from being used.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'fs' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 12
+        }]
     }, {
         code: "import os from \"os \";",
         options: ["fs", "crypto ", "stream", "os"],
-        errors: [{ message: "'os' import is restricted from being used.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'os' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 22
+        }]
     }, {
         code: "import \"foo/bar\";",
         options: ["foo/bar"],
-        errors: [{ message: "'foo/bar' import is restricted from being used.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'foo/bar' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 18
+        }]
     }, {
         code: "import withPaths from \"foo/bar\";",
         options: [{ paths: ["foo/bar"] }],
-        errors: [{ message: "'foo/bar' import is restricted from being used.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'foo/bar' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 33
+        }]
     }, {
         code: "import withPatterns from \"foo/bar\";",
         options: [{ patterns: ["foo"] }],
-        errors: [{ message: "'foo/bar' import is restricted from being used by a pattern.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'foo/bar' import is restricted from being used by a pattern.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 36
+        }]
     }, {
         code: "import withPatterns from \"foo/bar\";",
         options: [{ patterns: ["bar"] }],
-        errors: [{ message: "'foo/bar' import is restricted from being used by a pattern.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'foo/bar' import is restricted from being used by a pattern.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 36
+        }]
     }, {
         code: "import withGitignores from \"foo/bar\";",
         options: [{ patterns: ["foo/*", "!foo/baz"] }],
-        errors: [{ message: "'foo/bar' import is restricted from being used by a pattern.", type: "ImportDeclaration" }]
+        errors: [{
+            message: "'foo/bar' import is restricted from being used by a pattern.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 38
+        }]
     }, {
         code: "export * from \"fs\";",
         options: ["fs"],
-        errors: [{ message: "'fs' import is restricted from being used.", type: "ExportAllDeclaration" }]
+        errors: [{
+            message: "'fs' import is restricted from being used.",
+            type: "ExportAllDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 20
+        }]
     }, {
         code: "export {a} from \"fs\";",
         options: ["fs"],
-        errors: [{ message: "'fs' import is restricted from being used.", type: "ExportNamedDeclaration" }]
+        errors: [{
+            message: "'fs' import is restricted from being used.",
+            type: "ExportNamedDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 22
+        }]
     }, {
         code: "export {foo as b} from \"fs\";",
         options: [{
@@ -211,7 +269,13 @@ ruleTester.run("no-restricted-imports", rule, {
                 message: "Don't import 'foo'."
             }]
         }],
-        errors: [{ message: "'fs' import is restricted from being used. Don't import 'foo'.", type: "ExportNamedDeclaration" }]
+        errors: [{
+            message: "'foo' import from 'fs' is restricted. Don't import 'foo'.",
+            type: "ExportNamedDeclaration",
+            line: 1,
+            column: 9,
+            endColumn: 17
+        }]
     }, {
         code: "import withGitignores from \"foo\";",
         options: [{
@@ -220,7 +284,10 @@ ruleTester.run("no-restricted-imports", rule, {
         }],
         errors: [{
             message: "'foo' import is restricted from being used. Please import from 'bar' instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 34
         }]
     }, {
         code: "import withGitignores from \"bar\";",
@@ -234,7 +301,10 @@ ruleTester.run("no-restricted-imports", rule, {
         ],
         errors: [{
             message: "'bar' import is restricted from being used. Please import from 'baz' instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 34
         }]
     }, {
         code: "import withGitignores from \"foo\";",
@@ -246,7 +316,10 @@ ruleTester.run("no-restricted-imports", rule, {
         }],
         errors: [{
             message: "'foo' import is restricted from being used. Please import from 'bar' instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 34
         }]
     },
     {
@@ -259,8 +332,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import the default import of 'foo' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'default' import from 'foo' is restricted. Please import the default import of 'foo' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 24
         }]
     },
     {
@@ -274,7 +350,10 @@ ruleTester.run("no-restricted-imports", rule, {
         }],
         errors: [{
             message: "* import is invalid because 'DisallowedObject' from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 16
         }]
     },
     {
@@ -287,8 +366,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 26
         }]
     },
     {
@@ -301,8 +383,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 43
         }]
     },
     {
@@ -315,8 +400,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 41
         }]
     },
     {
@@ -329,8 +417,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 61
         }]
     },
     {
@@ -343,8 +434,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 61
         }]
     },
     {
@@ -357,8 +451,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 61
         }]
     },
     {
@@ -371,8 +468,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import the default import of 'foo' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'default' import from 'foo' is restricted. Please import the default import of 'foo' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 24
         }]
     },
     {
@@ -385,8 +485,11 @@ ruleTester.run("no-restricted-imports", rule, {
             }]
         }],
         errors: [{
-            message: "'foo' import is restricted from being used. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            message: "'DisallowedObject' import from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 61
         }]
     },
     {
@@ -400,7 +503,10 @@ ruleTester.run("no-restricted-imports", rule, {
         }],
         errors: [{
             message: "* import is invalid because 'DisallowedObject' from 'foo' is restricted. Please import 'DisallowedObject' from /bar/ instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 23,
+            endColumn: 44
         }]
     },
     {
@@ -414,7 +520,198 @@ ruleTester.run("no-restricted-imports", rule, {
         }],
         errors: [{
             message: "* import is invalid because 'DisallowedObject,DisallowedObjectTwo' from 'foo' is restricted. Please import 'DisallowedObject' and 'DisallowedObjectTwo' from /bar/ instead.",
-            type: "ImportDeclaration"
+            type: "ImportDeclaration",
+            line: 1,
+            column: 23,
+            endColumn: 44
+        }]
+    },
+    {
+        code: "import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from \"foo\";",
+        options: [{
+            paths: [{
+                name: "foo",
+                importNames: ["DisallowedObjectOne", "DisallowedObjectTwo"]
+            }]
+        }],
+        errors: [{
+            message: "'DisallowedObjectOne' import from 'foo' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 29
+        }, {
+            message: "'DisallowedObjectTwo' import from 'foo' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 31,
+            endColumn: 50
+        }]
+    },
+    {
+        code: "import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from \"foo\";",
+        options: [{
+            paths: [{
+                name: "foo",
+                importNames: ["DisallowedObjectOne", "DisallowedObjectTwo"],
+                message: "Please import this module from /bar/ instead."
+            }]
+        }],
+        errors: [{
+            message: "'DisallowedObjectOne' import from 'foo' is restricted. Please import this module from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 29
+        }, {
+            message: "'DisallowedObjectTwo' import from 'foo' is restricted. Please import this module from /bar/ instead.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 31,
+            endColumn: 50
+        }]
+    },
+    {
+        code: "import { AllowedObject, DisallowedObject as Bar } from \"foo\";",
+        options: [{
+            paths: [{
+                name: "foo",
+                importNames: ["DisallowedObject"]
+            }]
+        }],
+        errors: [{
+            message: "'DisallowedObject' import from 'foo' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 25,
+            endColumn: 48
+        }]
+    },
+    {
+        code: "import foo, { bar } from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["bar"]
+            }]
+        }],
+        errors: [{
+            message: "'bar' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 15,
+            endColumn: 18
+        }]
+    },
+    {
+        code: "import foo, { bar } from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["default"]
+            }]
+        }],
+        errors: [{
+            message: "'default' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 11
+        }]
+    },
+    {
+        code: "import foo, * as bar from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["default"]
+            }]
+        }],
+        errors: [{
+            message: "'default' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 11
+        }, {
+            message: "* import is invalid because 'default' from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 13,
+            endColumn: 21
+        }]
+    }, {
+        code: "import * as bar from 'foo';",
+        options: ["foo"],
+        errors: [{
+            message: "'foo' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 28
+        }]
+    }, {
+        code: "import { a, a as b } from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["a"]
+            }]
+        }],
+        errors: [{
+            message: "'a' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 11
+        }, {
+            message: "'a' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 13,
+            endColumn: 19
+        }]
+    }, {
+        code: "export { x as y, x as z } from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["x"]
+            }]
+        }],
+        errors: [{
+            message: "'x' import from 'mod' is restricted.",
+            type: "ExportNamedDeclaration",
+            line: 1,
+            column: 10,
+            endColumn: 16
+        }, {
+            message: "'x' import from 'mod' is restricted.",
+            type: "ExportNamedDeclaration",
+            line: 1,
+            column: 18,
+            endColumn: 24
+        }]
+    }, {
+        code: "import foo, { default as bar } from 'mod';",
+        options: [{
+            paths: [{
+                name: "mod",
+                importNames: ["default"]
+            }]
+        }],
+        errors: [{
+            message: "'default' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 8,
+            endColumn: 11
+        }, {
+            message: "'default' import from 'mod' is restricted.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 15,
+            endColumn: 29
         }]
     }
     ]