UNPKG

3.87 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 },
25
26 schema: []
27 },
28
29 create(context) {
30 const sourceCode = context.getSourceCode();
31
32 /**
33 * Parts of the grammar that are required to have parens.
34 */
35 const parenthesized = {
36 DoWhileStatement: "test",
37 IfStatement: "test",
38 SwitchStatement: "discriminant",
39 WhileStatement: "test",
40 WithStatement: "object",
41 ArrowFunctionExpression: "body"
42
43 // Omitting CallExpression - commas are parsed as argument separators
44 // Omitting NewExpression - commas are parsed as argument separators
45 // Omitting ForInStatement - parts aren't individually parenthesised
46 // Omitting ForStatement - parts aren't individually parenthesised
47 };
48
49 /**
50 * Determines whether a node is required by the grammar to be wrapped in
51 * parens, e.g. the test of an if statement.
52 * @param {ASTNode} node - The AST node
53 * @returns {boolean} True if parens around node belong to parent node.
54 */
55 function requiresExtraParens(node) {
56 return node.parent && parenthesized[node.parent.type] &&
57 node === node.parent[parenthesized[node.parent.type]];
58 }
59
60 /**
61 * Check if a node is wrapped in parens.
62 * @param {ASTNode} node - The AST node
63 * @returns {boolean} True if the node has a paren on each side.
64 */
65 function isParenthesised(node) {
66 return astUtils.isParenthesised(sourceCode, node);
67 }
68
69 /**
70 * Check if a node is wrapped in two levels of parens.
71 * @param {ASTNode} node - The AST node
72 * @returns {boolean} True if two parens surround the node on each side.
73 */
74 function isParenthesisedTwice(node) {
75 const previousToken = sourceCode.getTokenBefore(node, 1),
76 nextToken = sourceCode.getTokenAfter(node, 1);
77
78 return isParenthesised(node) && previousToken && nextToken &&
79 astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
80 astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
81 }
82
83 return {
84 SequenceExpression(node) {
85
86 // Always allow sequences in for statement update
87 if (node.parent.type === "ForStatement" &&
88 (node === node.parent.init || node === node.parent.update)) {
89 return;
90 }
91
92 // Wrapping a sequence in extra parens indicates intent
93 if (requiresExtraParens(node)) {
94 if (isParenthesisedTwice(node)) {
95 return;
96 }
97 } else {
98 if (isParenthesised(node)) {
99 return;
100 }
101 }
102
103 const child = sourceCode.getTokenAfter(node.expressions[0]);
104
105 context.report({ node, loc: child.loc.start, message: "Unexpected use of comma operator." });
106 }
107 };
108
109 }
110};