Skip to content

Commit

Permalink
feat: treat unknown nodes as having the lowest precedence (#17302)
Browse files Browse the repository at this point in the history
* feat: treat unknown nodes as having the lowest precedence

* fix test typo

* more tests and handle prefer-exponentiation case

* review fixes and more cases

* Apply suggestions from code review

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

---------

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
  • Loading branch information
bradzacher and mdjermanovic committed Jun 28, 2023
1 parent 9718a97 commit ef6e24e
Show file tree
Hide file tree
Showing 19 changed files with 4,180 additions and 10 deletions.
7 changes: 5 additions & 2 deletions lib/rules/logical-assignment-operators.js
Expand Up @@ -370,8 +370,11 @@ module.exports = {
return;
}

const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" &&
(astUtils.getPrecedence({ type: "AssignmentExpression" }) < astUtils.getPrecedence(logical.parent));
const parentPrecedence = astUtils.getPrecedence(logical.parent);
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
parentPrecedence === -1 ||
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
);

if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
yield ruleFixer.insertTextBefore(logical, "(");
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/prefer-exponentiation-operator.js
Expand Up @@ -55,11 +55,12 @@ function doesExponentNeedParens(exponent) {
function doesExponentiationExpressionNeedParens(node, sourceCode) {
const parent = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;

const parentPrecedence = astUtils.getPrecedence(parent);
const needsParens = (
parent.type === "ClassDeclaration" ||
(
parent.type.endsWith("Expression") &&
astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR &&
(parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) &&
!(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) &&
!((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&
!(parent.type === "MemberExpression" && parent.computed && parent.property === node) &&
Expand Down
12 changes: 11 additions & 1 deletion lib/rules/utils/ast-utils.js
Expand Up @@ -9,6 +9,7 @@
// Requirements
//------------------------------------------------------------------------------

const { KEYS: eslintVisitorKeys } = require("eslint-visitor-keys");
const esutils = require("esutils");
const espree = require("espree");
const escapeRegExp = require("escape-string-regexp");
Expand Down Expand Up @@ -1461,7 +1462,16 @@ module.exports = {
return 19;

default:
return 20;
if (node.type in eslintVisitorKeys) {
return 20;
}

/*
* if the node is not a standard node that we know about, then assume it has the lowest precedence
* this will mean that rules will wrap unknown nodes in parentheses where applicable instead of
* unwrapping them and potentially changing the meaning of the code or introducing a syntax error.
*/
return -1;
}
},

Expand Down
@@ -0,0 +1,333 @@
"use strict";

/**
* Parser: @typescript-eslint/parser 5.59.11 (TS 5.1.3)
* Source code:
* if (!Boolean(a as any)) { }
*/

exports.parse = () => ({
type: "Program",
body: [
{
type: "IfStatement",
test: {
type: "UnaryExpression",
operator: "!",
prefix: true,
argument: {
type: "CallExpression",
callee: {
type: "Identifier",
decorators: [],
name: "Boolean",
optional: false,
range: [5, 12],
loc: {
start: {
line: 1,
column: 5,
},
end: {
line: 1,
column: 12,
},
},
},
arguments: [
{
type: "TSAsExpression",
expression: {
type: "Identifier",
decorators: [],
name: "a",
optional: false,
range: [13, 14],
loc: {
start: {
line: 1,
column: 13,
},
end: {
line: 1,
column: 14,
},
},
},
typeAnnotation: {
type: "TSAnyKeyword",
range: [18, 21],
loc: {
start: {
line: 1,
column: 18,
},
end: {
line: 1,
column: 21,
},
},
},
range: [13, 21],
loc: {
start: {
line: 1,
column: 13,
},
end: {
line: 1,
column: 21,
},
},
},
],
optional: false,
range: [5, 22],
loc: {
start: {
line: 1,
column: 5,
},
end: {
line: 1,
column: 22,
},
},
},
range: [4, 22],
loc: {
start: {
line: 1,
column: 4,
},
end: {
line: 1,
column: 22,
},
},
},
consequent: {
type: "BlockStatement",
body: [],
range: [24, 27],
loc: {
start: {
line: 1,
column: 24,
},
end: {
line: 1,
column: 27,
},
},
},
alternate: null,
range: [0, 27],
loc: {
start: {
line: 1,
column: 0,
},
end: {
line: 1,
column: 27,
},
},
},
],
comments: [],
range: [0, 27],
sourceType: "script",
tokens: [
{
type: "Keyword",
value: "if",
range: [0, 2],
loc: {
start: {
line: 1,
column: 0,
},
end: {
line: 1,
column: 2,
},
},
},
{
type: "Punctuator",
value: "(",
range: [3, 4],
loc: {
start: {
line: 1,
column: 3,
},
end: {
line: 1,
column: 4,
},
},
},
{
type: "Punctuator",
value: "!",
range: [4, 5],
loc: {
start: {
line: 1,
column: 4,
},
end: {
line: 1,
column: 5,
},
},
},
{
type: "Identifier",
value: "Boolean",
range: [5, 12],
loc: {
start: {
line: 1,
column: 5,
},
end: {
line: 1,
column: 12,
},
},
},
{
type: "Punctuator",
value: "(",
range: [12, 13],
loc: {
start: {
line: 1,
column: 12,
},
end: {
line: 1,
column: 13,
},
},
},
{
type: "Identifier",
value: "a",
range: [13, 14],
loc: {
start: {
line: 1,
column: 13,
},
end: {
line: 1,
column: 14,
},
},
},
{
type: "Identifier",
value: "as",
range: [15, 17],
loc: {
start: {
line: 1,
column: 15,
},
end: {
line: 1,
column: 17,
},
},
},
{
type: "Identifier",
value: "any",
range: [18, 21],
loc: {
start: {
line: 1,
column: 18,
},
end: {
line: 1,
column: 21,
},
},
},
{
type: "Punctuator",
value: ")",
range: [21, 22],
loc: {
start: {
line: 1,
column: 21,
},
end: {
line: 1,
column: 22,
},
},
},
{
type: "Punctuator",
value: ")",
range: [22, 23],
loc: {
start: {
line: 1,
column: 22,
},
end: {
line: 1,
column: 23,
},
},
},
{
type: "Punctuator",
value: "{",
range: [24, 25],
loc: {
start: {
line: 1,
column: 24,
},
end: {
line: 1,
column: 25,
},
},
},
{
type: "Punctuator",
value: "}",
range: [26, 27],
loc: {
start: {
line: 1,
column: 26,
},
end: {
line: 1,
column: 27,
},
},
},
],
loc: {
start: {
line: 1,
column: 0,
},
end: {
line: 1,
column: 27,
},
},
});

0 comments on commit ef6e24e

Please sign in to comment.