UNPKG

3.97 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag use of comma operator
3 * @author Brandon Mills
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("../ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 docs: {
21 description: "disallow comma operators",
22 category: "Best Practices",
23 recommended: false,
24 url: "https://eslint.org/docs/rules/no-sequences"
25 },
26
27 schema: []
28 },
29
30 create(context) {
31 const sourceCode = context.getSourceCode();
32
33 /**
34 * Parts of the grammar that are required to have parens.
35 */
36 const parenthesized = {
37 DoWhileStatement: "test",
38 IfStatement: "test",
39 SwitchStatement: "discriminant",
40 WhileStatement: "test",
41 WithStatement: "object",
42 ArrowFunctionExpression: "body"
43
44 /*
45 * Omitting CallExpression - commas are parsed as argument separators
46 * Omitting NewExpression - commas are parsed as argument separators
47 * Omitting ForInStatement - parts aren't individually parenthesised
48 * Omitting ForStatement - parts aren't individually parenthesised
49 */
50 };
51
52 /**
53 * Determines whether a node is required by the grammar to be wrapped in
54 * parens, e.g. the test of an if statement.
55 * @param {ASTNode} node - The AST node
56 * @returns {boolean} True if parens around node belong to parent node.
57 */
58 function requiresExtraParens(node) {
59 return node.parent && parenthesized[node.parent.type] &&
60 node === node.parent[parenthesized[node.parent.type]];
61 }
62
63 /**
64 * Check if a node is wrapped in parens.
65 * @param {ASTNode} node - The AST node
66 * @returns {boolean} True if the node has a paren on each side.
67 */
68 function isParenthesised(node) {
69 return astUtils.isParenthesised(sourceCode, node);
70 }
71
72 /**
73 * Check if a node is wrapped in two levels of parens.
74 * @param {ASTNode} node - The AST node
75 * @returns {boolean} True if two parens surround the node on each side.
76 */
77 function isParenthesisedTwice(node) {
78 const previousToken = sourceCode.getTokenBefore(node, 1),
79 nextToken = sourceCode.getTokenAfter(node, 1);
80
81 return isParenthesised(node) && previousToken && nextToken &&
82 astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
83 astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
84 }
85
86 return {
87 SequenceExpression(node) {
88
89 // Always allow sequences in for statement update
90 if (node.parent.type === "ForStatement" &&
91 (node === node.parent.init || node === node.parent.update)) {
92 return;
93 }
94
95 // Wrapping a sequence in extra parens indicates intent
96 if (requiresExtraParens(node)) {
97 if (isParenthesisedTwice(node)) {
98 return;
99 }
100 } else {
101 if (isParenthesised(node)) {
102 return;
103 }
104 }
105
106 const child = sourceCode.getTokenAfter(node.expressions[0]);
107
108 context.report({ node, loc: child.loc.start, message: "Unexpected use of comma operator." });
109 }
110 };
111
112 }
113};