Blame view
node_modules/eslint/lib/rules/no-cond-assign.js
4.7 KB
f7563de62
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
/** * @fileoverview Rule to flag assignment in a conditional statement's test expression * @author Stephen Murray <spmurrayzzz> */ "use strict"; const astUtils = require("../ast-utils"); const NODE_DESCRIPTIONS = { DoWhileStatement: "a 'do...while' statement", ForStatement: "a 'for' statement", IfStatement: "an 'if' statement", WhileStatement: "a 'while' statement" }; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ module.exports = { meta: { docs: { description: "disallow assignment operators in conditional expressions", category: "Possible Errors", recommended: true }, schema: [ { enum: ["except-parens", "always"] } ] }, create(context) { const prohibitAssign = (context.options[0] || "except-parens"); const sourceCode = context.getSourceCode(); /** * Check whether an AST node is the test expression for a conditional statement. * @param {!Object} node The node to test. * @returns {boolean} `true` if the node is the text expression for a conditional statement; otherwise, `false`. */ function isConditionalTestExpression(node) { return node.parent && node.parent.test && node === node.parent.test; } /** * Given an AST node, perform a bottom-up search for the first ancestor that represents a conditional statement. * @param {!Object} node The node to use at the start of the search. * @returns {?Object} The closest ancestor node that represents a conditional statement. */ function findConditionalAncestor(node) { let currentAncestor = node; do { if (isConditionalTestExpression(currentAncestor)) { return currentAncestor.parent; } } while ((currentAncestor = currentAncestor.parent) && !astUtils.isFunction(currentAncestor)); return null; } /** * Check whether the code represented by an AST node is enclosed in two sets of parentheses. * @param {!Object} node The node to test. * @returns {boolean} `true` if the code is enclosed in two sets of parentheses; otherwise, `false`. */ function isParenthesisedTwice(node) { const previousToken = sourceCode.getTokenBefore(node, 1), nextToken = sourceCode.getTokenAfter(node, 1); return astUtils.isParenthesised(sourceCode, node) && astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] && astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1]; } /** * Check a conditional statement's test expression for top-level assignments that are not enclosed in parentheses. * @param {!Object} node The node for the conditional statement. * @returns {void} */ function testForAssign(node) { if (node.test && (node.test.type === "AssignmentExpression") && (node.type === "ForStatement" ? !astUtils.isParenthesised(sourceCode, node.test) : !isParenthesisedTwice(node.test) ) ) { // must match JSHint's error message context.report({ node, loc: node.test.loc.start, message: "Expected a conditional expression and instead saw an assignment." }); } } /** * Check whether an assignment expression is descended from a conditional statement's test expression. * @param {!Object} node The node for the assignment expression. * @returns {void} */ function testForConditionalAncestor(node) { const ancestor = findConditionalAncestor(node); if (ancestor) { context.report({ node: ancestor, message: "Unexpected assignment within {{type}}.", data: { type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type } }); } } if (prohibitAssign === "always") { return { AssignmentExpression: testForConditionalAncestor }; } return { DoWhileStatement: testForAssign, ForStatement: testForAssign, IfStatement: testForAssign, WhileStatement: testForAssign }; } }; |