summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules
diff options
context:
space:
mode:
authorcjihrig <cjihrig@gmail.com>2019-07-21 00:15:32 -0400
committerMichaƫl Zasso <targos@protonmail.com>2019-07-23 08:15:42 +0200
commited43880d6ba1ab6e499ef027c1e9e11a2376ba50 (patch)
treebd9551f12adfd0dc9f333adf306fc92f2417b2ad /tools/node_modules/eslint/lib/rules
parent302865e8b9756812b45d9ff60403c2a231c01152 (diff)
downloadandroid-node-v8-ed43880d6ba1ab6e499ef027c1e9e11a2376ba50.tar.gz
android-node-v8-ed43880d6ba1ab6e499ef027c1e9e11a2376ba50.tar.bz2
android-node-v8-ed43880d6ba1ab6e499ef027c1e9e11a2376ba50.zip
tools: update ESLint to 6.1.0
Update ESLint to 6.1.0 PR-URL: https://github.com/nodejs/node/pull/28793 Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Diffstat (limited to 'tools/node_modules/eslint/lib/rules')
-rw-r--r--tools/node_modules/eslint/lib/rules/arrow-body-style.js4
-rw-r--r--tools/node_modules/eslint/lib/rules/dot-location.js38
-rw-r--r--tools/node_modules/eslint/lib/rules/no-extra-parens.js258
-rw-r--r--tools/node_modules/eslint/lib/rules/no-param-reassign.js13
-rw-r--r--tools/node_modules/eslint/lib/rules/prefer-const.js12
-rw-r--r--tools/node_modules/eslint/lib/rules/utils/ast-utils.js27
6 files changed, 311 insertions, 41 deletions
diff --git a/tools/node_modules/eslint/lib/rules/arrow-body-style.js b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
index 6d84c7adfb..8d3b400037 100644
--- a/tools/node_modules/eslint/lib/rules/arrow-body-style.js
+++ b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
@@ -175,10 +175,10 @@ module.exports = {
}
/*
- * If the first token of the reutrn value is `{`,
+ * If the first token of the reutrn value is `{` or the return value is a sequence expression,
* enclose the return value by parentheses to avoid syntax error.
*/
- if (astUtils.isOpeningBraceToken(firstValueToken)) {
+ if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
fixes.push(
fixer.insertTextBefore(firstValueToken, "("),
fixer.insertTextAfter(lastValueToken, ")")
diff --git a/tools/node_modules/eslint/lib/rules/dot-location.js b/tools/node_modules/eslint/lib/rules/dot-location.js
index b9857ae239..c2e734a1a0 100644
--- a/tools/node_modules/eslint/lib/rules/dot-location.js
+++ b/tools/node_modules/eslint/lib/rules/dot-location.js
@@ -54,29 +54,31 @@ module.exports = {
*/
function checkDotLocation(obj, prop, node) {
const dot = sourceCode.getTokenBefore(prop);
- const textBeforeDot = sourceCode.getText().slice(obj.range[1], dot.range[0]);
+
+ // `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
+ const tokenBeforeDot = sourceCode.getTokenBefore(dot);
+
+ const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
- if (dot.type === "Punctuator" && dot.value === ".") {
- if (onObject) {
- if (!astUtils.isTokenOnSameLine(obj, dot)) {
- const neededTextAfterObj = astUtils.isDecimalInteger(obj) ? " " : "";
-
- context.report({
- node,
- loc: dot.loc.start,
- messageId: "expectedDotAfterObject",
- fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
- });
- }
- } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
+ if (onObject) {
+ if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
+ const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
+
context.report({
node,
loc: dot.loc.start,
- messageId: "expectedDotBeforeProperty",
- fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
+ messageId: "expectedDotAfterObject",
+ fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
});
}
+ } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
+ context.report({
+ node,
+ loc: dot.loc.start,
+ messageId: "expectedDotBeforeProperty",
+ fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
+ });
}
}
@@ -86,7 +88,9 @@ module.exports = {
* @returns {void}
*/
function checkNode(node) {
- checkDotLocation(node.object, node.property, node);
+ if (!node.computed) {
+ checkDotLocation(node.object, node.property, node);
+ }
}
return {
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-parens.js b/tools/node_modules/eslint/lib/rules/no-extra-parens.js
index 98fc49654c..6c3198b5f0 100644
--- a/tools/node_modules/eslint/lib/rules/no-extra-parens.js
+++ b/tools/node_modules/eslint/lib/rules/no-extra-parens.js
@@ -81,6 +81,8 @@ module.exports = {
const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
+ let reportsBuffer;
+
/**
* Determines if this rule should be enforced for a node given the current configuration.
* @param {ASTNode} node - The node to be checked.
@@ -316,19 +318,33 @@ module.exports = {
}
}
- context.report({
- node,
- loc: leftParenToken.loc.start,
- messageId: "unexpected",
- fix(fixer) {
- const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
+ /**
+ * Finishes reporting
+ * @returns {void}
+ * @private
+ */
+ function finishReport() {
+ context.report({
+ node,
+ loc: leftParenToken.loc.start,
+ messageId: "unexpected",
+ fix(fixer) {
+ const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
+
+ return fixer.replaceTextRange([
+ leftParenToken.range[0],
+ rightParenToken.range[1]
+ ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
+ }
+ });
+ }
- return fixer.replaceTextRange([
- leftParenToken.range[0],
- rightParenToken.range[1]
- ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
- }
- });
+ if (reportsBuffer) {
+ reportsBuffer.reports.push({ node, finishReport });
+ return;
+ }
+
+ finishReport();
}
/**
@@ -498,6 +514,126 @@ module.exports = {
}
}
+ /**
+ * Finds the path from the given node to the specified ancestor.
+ * @param {ASTNode} node First node in the path.
+ * @param {ASTNode} ancestor Last node in the path.
+ * @returns {ASTNode[]} Path, including both nodes.
+ * @throws {Error} If the given node does not have the specified ancestor.
+ */
+ function pathToAncestor(node, ancestor) {
+ const path = [node];
+ let currentNode = node;
+
+ while (currentNode !== ancestor) {
+
+ currentNode = currentNode.parent;
+
+ /* istanbul ignore if */
+ if (currentNode === null) {
+ throw new Error("Nodes are not in the ancestor-descendant relationship.");
+ }
+
+ path.push(currentNode);
+ }
+
+ return path;
+ }
+
+ /**
+ * Finds the path from the given node to the specified descendant.
+ * @param {ASTNode} node First node in the path.
+ * @param {ASTNode} descendant Last node in the path.
+ * @returns {ASTNode[]} Path, including both nodes.
+ * @throws {Error} If the given node does not have the specified descendant.
+ */
+ function pathToDescendant(node, descendant) {
+ return pathToAncestor(descendant, node).reverse();
+ }
+
+ /**
+ * Checks whether the syntax of the given ancestor of an 'in' expression inside a for-loop initializer
+ * is preventing the 'in' keyword from being interpreted as a part of an ill-formed for-in loop.
+ *
+ * @param {ASTNode} node Ancestor of an 'in' expression.
+ * @param {ASTNode} child Child of the node, ancestor of the same 'in' expression or the 'in' expression itself.
+ * @returns {boolean} True if the keyword 'in' would be interpreted as the 'in' operator, without any parenthesis.
+ */
+ function isSafelyEnclosingInExpression(node, child) {
+ switch (node.type) {
+ case "ArrayExpression":
+ case "ArrayPattern":
+ case "BlockStatement":
+ case "ObjectExpression":
+ case "ObjectPattern":
+ case "TemplateLiteral":
+ return true;
+ case "ArrowFunctionExpression":
+ case "FunctionExpression":
+ return node.params.includes(child);
+ case "CallExpression":
+ case "NewExpression":
+ return node.arguments.includes(child);
+ case "MemberExpression":
+ return node.computed && node.property === child;
+ case "ConditionalExpression":
+ return node.consequent === child;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Starts a new reports buffering. Warnings will be stored in a buffer instead of being reported immediately.
+ * An additional logic that requires multiple nodes (e.g. a whole subtree) may dismiss some of the stored warnings.
+ *
+ * @returns {void}
+ */
+ function startNewReportsBuffering() {
+ reportsBuffer = {
+ upper: reportsBuffer,
+ inExpressionNodes: [],
+ reports: []
+ };
+ }
+
+ /**
+ * Ends the current reports buffering.
+ * @returns {void}
+ */
+ function endCurrentReportsBuffering() {
+ const { upper, inExpressionNodes, reports } = reportsBuffer;
+
+ if (upper) {
+ upper.inExpressionNodes.push(...inExpressionNodes);
+ upper.reports.push(...reports);
+ } else {
+
+ // flush remaining reports
+ reports.forEach(({ finishReport }) => finishReport());
+ }
+
+ reportsBuffer = upper;
+ }
+
+ /**
+ * Checks whether the given node is in the current reports buffer.
+ * @param {ASTNode} node Node to check.
+ * @returns {boolean} True if the node is in the current buffer, false otherwise.
+ */
+ function isInCurrentReportsBuffer(node) {
+ return reportsBuffer.reports.some(r => r.node === node);
+ }
+
+ /**
+ * Removes the given node from the current reports buffer.
+ * @param {ASTNode} node Node to remove.
+ * @returns {void}
+ */
+ function removeFromCurrentReportsBuffer(node) {
+ reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
+ }
+
return {
ArrayExpression(node) {
node.elements
@@ -540,7 +676,14 @@ module.exports = {
}
},
- BinaryExpression: checkBinaryLogical,
+ BinaryExpression(node) {
+ if (reportsBuffer && node.operator === "in") {
+ reportsBuffer.inExpressionNodes.push(node);
+ }
+
+ checkBinaryLogical(node);
+ },
+
CallExpression: checkCallNew,
ConditionalExpression(node) {
@@ -602,10 +745,6 @@ module.exports = {
},
ForStatement(node) {
- if (node.init && hasExcessParens(node.init)) {
- report(node.init);
- }
-
if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
report(node.test);
}
@@ -613,6 +752,81 @@ module.exports = {
if (node.update && hasExcessParens(node.update)) {
report(node.update);
}
+
+ if (node.init) {
+ startNewReportsBuffering();
+
+ if (hasExcessParens(node.init)) {
+ report(node.init);
+ }
+ }
+ },
+
+ "ForStatement > *.init:exit"(node) {
+
+ /*
+ * Removing parentheses around `in` expressions might change semantics and cause errors.
+ *
+ * For example, this valid for loop:
+ * for (let a = (b in c); ;);
+ * after removing parentheses would be treated as an invalid for-in loop:
+ * for (let a = b in c; ;);
+ */
+
+ if (reportsBuffer.reports.length) {
+ reportsBuffer.inExpressionNodes.forEach(inExpressionNode => {
+ const path = pathToDescendant(node, inExpressionNode);
+ let nodeToExclude;
+
+ for (let i = 0; i < path.length; i++) {
+ const pathNode = path[i];
+
+ if (i < path.length - 1) {
+ const nextPathNode = path[i + 1];
+
+ if (isSafelyEnclosingInExpression(pathNode, nextPathNode)) {
+
+ // The 'in' expression in safely enclosed by the syntax of its ancestor nodes (e.g. by '{}' or '[]').
+ return;
+ }
+ }
+
+ if (isParenthesised(pathNode)) {
+ if (isInCurrentReportsBuffer(pathNode)) {
+
+ // This node was supposed to be reported, but parentheses might be necessary.
+
+ if (isParenthesisedTwice(pathNode)) {
+
+ /*
+ * This node is parenthesised twice, it certainly has at least one pair of `extra` parentheses.
+ * If the --fix option is on, the current fixing iteration will remove only one pair of parentheses.
+ * The remaining pair is safely enclosing the 'in' expression.
+ */
+ return;
+ }
+
+ // Exclude the outermost node only.
+ if (!nodeToExclude) {
+ nodeToExclude = pathNode;
+ }
+
+ // Don't break the loop here, there might be some safe nodes or parentheses that will stay inside.
+
+ } else {
+
+ // This node will stay parenthesised, the 'in' expression in safely enclosed by '()'.
+ return;
+ }
+ }
+ }
+
+ // Exclude the node from the list (i.e. treat parentheses as necessary)
+ removeFromCurrentReportsBuffer(nodeToExclude);
+ });
+ }
+
+ endCurrentReportsBuffering();
},
IfStatement(node) {
@@ -664,6 +878,16 @@ module.exports = {
}).forEach(property => report(property.value));
},
+ Property(node) {
+ if (node.computed) {
+ const { key } = node;
+
+ if (key && hasExcessParens(key) && precedence(key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+ report(key);
+ }
+ }
+ },
+
ReturnStatement(node) {
const returnToken = sourceCode.getFirstToken(node);
diff --git a/tools/node_modules/eslint/lib/rules/no-param-reassign.js b/tools/node_modules/eslint/lib/rules/no-param-reassign.js
index 880ff93cb5..9b8c828d2c 100644
--- a/tools/node_modules/eslint/lib/rules/no-param-reassign.js
+++ b/tools/node_modules/eslint/lib/rules/no-param-reassign.js
@@ -67,7 +67,8 @@ module.exports = {
let node = reference.identifier;
let parent = node.parent;
- while (parent && !stopNodePattern.test(parent.type)) {
+ while (parent && (!stopNodePattern.test(parent.type) ||
+ parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
switch (parent.type) {
// e.g. foo.a = 0;
@@ -85,6 +86,16 @@ module.exports = {
}
break;
+ // e.g. for (foo.a in b) {}
+ case "ForInStatement":
+ case "ForOfStatement":
+ if (parent.left === node) {
+ return true;
+ }
+
+ // this is a stop node for parent.right and parent.body
+ return false;
+
// EXCLUDES: e.g. cache.get(foo.a).b = 0;
case "CallExpression":
if (parent.callee !== node) {
diff --git a/tools/node_modules/eslint/lib/rules/prefer-const.js b/tools/node_modules/eslint/lib/rules/prefer-const.js
index 68c07da4ed..854da310e4 100644
--- a/tools/node_modules/eslint/lib/rules/prefer-const.js
+++ b/tools/node_modules/eslint/lib/rules/prefer-const.js
@@ -420,8 +420,9 @@ module.exports = {
let shouldFix = varDeclParent &&
- // Don't do a fix unless the variable is initialized (or it's in a for-in or for-of loop)
- (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" || varDeclParent.declarations[0].init) &&
+ // Don't do a fix unless all variables in the declarations are initialized (or it's in a for-in or for-of loop)
+ (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" ||
+ varDeclParent.declarations.every(declaration => declaration.init)) &&
/*
* If options.destructuring is "all", then this warning will not occur unless
@@ -450,7 +451,12 @@ module.exports = {
node,
messageId: "useConst",
data: node,
- fix: shouldFix ? fixer => fixer.replaceText(sourceCode.getFirstToken(varDeclParent), "const") : null
+ fix: shouldFix
+ ? fixer => fixer.replaceText(
+ sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind),
+ "const"
+ )
+ : null
});
});
}
diff --git a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js
index 1aef4c162d..78ae7bc015 100644
--- a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js
+++ b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js
@@ -37,6 +37,8 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
// A set of node types that can contain a list of statements
const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
+const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
+
/**
* Checks reference if is non initializer and writable.
* @param {Reference} reference - A reference to check.
@@ -284,6 +286,16 @@ function isCommaToken(token) {
}
/**
+ * Checks if the given token is a dot token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a dot token.
+ */
+function isDotToken(token) {
+ return token.value === "." && token.type === "Punctuator";
+}
+
+/**
* Checks if the given token is a semicolon token or not.
*
* @param {Token} token - The token to check.
@@ -462,12 +474,14 @@ module.exports = {
isColonToken,
isCommaToken,
isCommentToken,
+ isDotToken,
isKeywordToken,
isNotClosingBraceToken: negate(isClosingBraceToken),
isNotClosingBracketToken: negate(isClosingBracketToken),
isNotClosingParenToken: negate(isClosingParenToken),
isNotColonToken: negate(isColonToken),
isNotCommaToken: negate(isCommaToken),
+ isNotDotToken: negate(isDotToken),
isNotOpeningBraceToken: negate(isOpeningBraceToken),
isNotOpeningBracketToken: negate(isOpeningBracketToken),
isNotOpeningParenToken: negate(isOpeningParenToken),
@@ -988,7 +1002,18 @@ module.exports = {
* '5' // false
*/
isDecimalInteger(node) {
- return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/u.test(node.raw);
+ return node.type === "Literal" && typeof node.value === "number" &&
+ DECIMAL_INTEGER_PATTERN.test(node.raw);
+ },
+
+ /**
+ * Determines whether this token is a decimal integer numeric token.
+ * This is similar to isDecimalInteger(), but for tokens.
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if this token is a decimal integer.
+ */
+ isDecimalIntegerNumericToken(token) {
+ return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
},
/**