Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Check division operator in astUtils.canTokensBeAdjacent #12879

Merged
merged 3 commits into from Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/rules/no-extra-parens.js
Expand Up @@ -307,13 +307,13 @@ module.exports = {
*/
function requiresLeadingSpace(node) {
const leftParenToken = sourceCode.getTokenBefore(node);
const tokenBeforeLeftParen = sourceCode.getTokenBefore(node, 1);
const firstToken = sourceCode.getFirstToken(node);
const tokenBeforeLeftParen = sourceCode.getTokenBefore(leftParenToken, { includeComments: true });
const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParenToken, { includeComments: true });

return tokenBeforeLeftParen &&
tokenBeforeLeftParen.range[1] === leftParenToken.range[0] &&
leftParenToken.range[1] === firstToken.range[0] &&
!astUtils.canTokensBeAdjacent(tokenBeforeLeftParen, firstToken);
leftParenToken.range[1] === tokenAfterLeftParen.range[0] &&
!astUtils.canTokensBeAdjacent(tokenBeforeLeftParen, tokenAfterLeftParen);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/operator-assignment.js
Expand Up @@ -214,12 +214,12 @@ module.exports = {
) {
rightText = `${sourceCode.text.slice(operatorToken.range[1], node.right.range[0])}(${sourceCode.getText(node.right)})`;
} else {
const firstRightToken = sourceCode.getFirstToken(node.right);
const tokenAfterOperator = sourceCode.getTokenAfter(operatorToken, { includeComments: true });
let rightTextPrefix = "";

if (
operatorToken.range[1] === firstRightToken.range[0] &&
!astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, firstRightToken)
operatorToken.range[1] === tokenAfterOperator.range[0] &&
!astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, tokenAfterOperator)
) {
rightTextPrefix = " "; // foo+=+bar -> foo= foo+ +bar
}
Expand Down
61 changes: 58 additions & 3 deletions lib/rules/utils/ast-utils.js
Expand Up @@ -1357,17 +1357,65 @@ module.exports = {
* next to each other, behavior is undefined (although it should return `true` in most cases).
*/
canTokensBeAdjacent(leftValue, rightValue) {
const espreeOptions = {
ecmaVersion: espree.latestEcmaVersion,
comment: true,
range: true
};

let leftToken;

if (typeof leftValue === "string") {
const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 });
let tokens;

try {
tokens = espree.tokenize(leftValue, espreeOptions);
} catch (e) {
return false;
}

const comments = tokens.comments;

leftToken = tokens[tokens.length - 1];
if (comments.length) {
const lastComment = comments[comments.length - 1];

leftToken = leftTokens[leftTokens.length - 1];
if (lastComment.range[0] > leftToken.range[0]) {
leftToken = lastComment;
}
}
} else {
leftToken = leftValue;
}

const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue;
if (leftToken.type === "Shebang") {
return false;
}

let rightToken;

if (typeof rightValue === "string") {
let tokens;

try {
tokens = espree.tokenize(rightValue, espreeOptions);
} catch (e) {
return false;
}

const comments = tokens.comments;

rightToken = tokens[0];
if (comments.length) {
const firstComment = comments[0];

if (firstComment.range[0] < rightToken.range[0]) {
rightToken = firstComment;
}
}
} else {
rightToken = rightValue;
}

if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
Expand All @@ -1379,6 +1427,9 @@ module.exports = {
MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
);
}
if (leftToken.type === "Punctuator" && leftToken.value === "/") {
return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
}
return true;
}

Expand All @@ -1393,6 +1444,10 @@ module.exports = {
return true;
}

if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
return true;
}

return false;
},

Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -55,7 +55,7 @@
"eslint-scope": "^5.0.0",
"eslint-utils": "^1.4.3",
"eslint-visitor-keys": "^1.1.0",
"espree": "^6.1.2",
"espree": "^6.2.0",
"esquery": "^1.0.1",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
Expand Down
16 changes: 15 additions & 1 deletion tests/lib/rules/no-extra-parens.js
Expand Up @@ -2192,6 +2192,20 @@ ruleTester.run("no-extra-parens", rule, {
code: "var foo = { [((bar1, bar2))]: baz };",
output: "var foo = { [(bar1, bar2)]: baz };",
errors: [{ messageId: "unexpected" }]
}
},

// adjacent tokens tests for division operator, comments and regular expressions
invalid("a+/**/(/**/b)", "a+/**//**/b", "Identifier"),
invalid("a+/**/(//\nb)", "a+/**///\nb", "Identifier"),
invalid("a in(/**/b)", "a in/**/b", "Identifier"),
invalid("a in(//\nb)", "a in//\nb", "Identifier"),
invalid("a+(/**/b)", "a+/**/b", "Identifier"),
invalid("a+/**/(b)", "a+/**/b", "Identifier"),
invalid("a+(//\nb)", "a+//\nb", "Identifier"),
invalid("a+//\n(b)", "a+//\nb", "Identifier"),
invalid("a+(/^b$/)", "a+/^b$/", "Literal"),
invalid("a/(/**/b)", "a/ /**/b", "Identifier"),
invalid("a/(//\nb)", "a/ //\nb", "Identifier"),
invalid("a/(/^b$/)", "a/ /^b$/", "Literal")
]
});
10 changes: 10 additions & 0 deletions tests/lib/rules/no-implicit-coercion.js
Expand Up @@ -345,6 +345,16 @@ ruleTester.run("no-implicit-coercion", rule, {
data: { recommendation: "Number(foo)" },
type: "UnaryExpression"
}]
},
{
code: "let x ='' + 1n;",
output: "let x =String(1n);",
parserOptions: { ecmaVersion: 2020 },
errors: [{
messageId: "useRecommendation",
data: { recommendation: "String(1n)" },
type: "BinaryExpression"
}]
}
]
});
15 changes: 15 additions & 0 deletions tests/lib/rules/operator-assignment.js
Expand Up @@ -363,6 +363,21 @@ ruleTester.run("operator-assignment", rule, {
output: "foo= foo/bar", // tokens can be adjacent
options: ["never"],
errors: UNEXPECTED_OPERATOR_ASSIGNMENT
}, {
code: "foo/=/**/bar",
output: "foo= foo/ /**/bar", // // tokens cannot be adjacent, insert a space between
options: ["never"],
errors: UNEXPECTED_OPERATOR_ASSIGNMENT
}, {
code: "foo/=//\nbar",
output: "foo= foo/ //\nbar", // // tokens cannot be adjacent, insert a space between
options: ["never"],
errors: UNEXPECTED_OPERATOR_ASSIGNMENT
}, {
code: "foo/=/^bar$/",
output: "foo= foo/ /^bar$/", // // tokens cannot be adjacent, insert a space between
options: ["never"],
errors: UNEXPECTED_OPERATOR_ASSIGNMENT
}, {
code: "foo+=+bar",
output: "foo= foo+ +bar", // tokens cannot be adjacent, insert a space between
Expand Down
32 changes: 31 additions & 1 deletion tests/lib/rules/utils/ast-utils.js
Expand Up @@ -1342,14 +1342,44 @@ describe("ast-utils", () => {
[["++", "+"], false],
[["--", "-"], false],
[["+", "++"], false],
[["-", "--"], false]
[["-", "--"], false],
[["a/", "b"], true],
[["a/", "+b"], true],
[["a+", "/^regex$/"], true],
[["a/", "/^regex$/"], false],
[["a+", "/**/b"], true],
[["a/", "/**/b"], false],
[["a+", "//\nb"], true],
[["a/", "//\nb"], false],
[["a/**/", "b"], true],
[["/**/a", "b"], false],
[["a", "/**/b"], true],
[["a", "b/**/"], false],
[["a", "//\nb"], true],
[["a", "b//"], false],
[["#!/usr/bin/env node", "("], false],
[["123invalidtoken", "("], false],
[["(", "123invalidtoken"], false],
[["(", "1n"], true],
[["1n", "+"], true],
[["1n", "in"], false]
]);

CASES.forEach((expectedResult, tokenStrings) => {
it(tokenStrings.join(", "), () => {
assert.strictEqual(astUtils.canTokensBeAdjacent(tokenStrings[0], tokenStrings[1]), expectedResult);
});
});

it("#!/usr/bin/env node, (", () => {
assert.strictEqual(
astUtils.canTokensBeAdjacent(
{ type: "Shebang", value: "#!/usr/bin/env node" },
{ type: "Punctuator", value: "(" }
),
false
);
});
});

describe("equalTokens", () => {
Expand Down