eslint 6.1.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +8 -4
  3. package/conf/config-schema.js +2 -0
  4. package/conf/default-cli-options.js +1 -1
  5. package/conf/environments.js +72 -15
  6. package/lib/cli-engine/config-array/config-array.js +13 -0
  7. package/lib/cli-engine/config-array/extracted-config.js +22 -1
  8. package/lib/cli-engine/config-array-factory.js +4 -0
  9. package/lib/cli-engine/formatters/stylish.js +2 -1
  10. package/lib/init/config-initializer.js +29 -0
  11. package/lib/init/npm-utils.js +10 -10
  12. package/lib/linter/apply-disable-directives.js +17 -9
  13. package/lib/linter/code-path-analysis/code-path-analyzer.js +1 -0
  14. package/lib/linter/linter.js +70 -17
  15. package/lib/options.js +1 -1
  16. package/lib/rules/accessor-pairs.js +195 -35
  17. package/lib/rules/class-methods-use-this.js +10 -3
  18. package/lib/rules/dot-notation.js +6 -2
  19. package/lib/rules/func-call-spacing.js +30 -20
  20. package/lib/rules/func-name-matching.js +1 -0
  21. package/lib/rules/func-names.js +4 -0
  22. package/lib/rules/function-call-argument-newline.js +120 -0
  23. package/lib/rules/function-paren-newline.js +36 -24
  24. package/lib/rules/indent.js +13 -2
  25. package/lib/rules/index.js +1 -0
  26. package/lib/rules/new-cap.js +2 -1
  27. package/lib/rules/no-dupe-keys.js +1 -1
  28. package/lib/rules/no-duplicate-case.js +10 -8
  29. package/lib/rules/no-extra-bind.js +1 -0
  30. package/lib/rules/no-extra-boolean-cast.js +54 -5
  31. package/lib/rules/no-extra-parens.js +62 -23
  32. package/lib/rules/no-mixed-operators.js +48 -13
  33. package/lib/rules/no-restricted-syntax.js +2 -2
  34. package/lib/rules/no-self-assign.js +11 -1
  35. package/lib/rules/no-unused-vars.js +1 -1
  36. package/lib/rules/prefer-template.js +1 -10
  37. package/lib/rules/sort-keys.js +11 -3
  38. package/lib/rules/utils/ast-utils.js +19 -2
  39. package/lib/rules/yoda.js +12 -3
  40. package/lib/shared/types.js +4 -0
  41. package/package.json +5 -5
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @fileoverview Rule to enforce line breaks between arguments of a function call
3
+ * @author Alexey Gonchar <https://github.com/finico>
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Rule Definition
10
+ //------------------------------------------------------------------------------
11
+
12
+ module.exports = {
13
+ meta: {
14
+ type: "layout",
15
+
16
+ docs: {
17
+ description: "enforce line breaks between arguments of a function call",
18
+ category: "Stylistic Issues",
19
+ recommended: false,
20
+ url: "https://eslint.org/docs/rules/function-call-argument-newline"
21
+ },
22
+
23
+ fixable: "whitespace",
24
+
25
+ schema: [
26
+ {
27
+ enum: ["always", "never", "consistent"]
28
+ }
29
+ ],
30
+
31
+ messages: {
32
+ unexpectedLineBreak: "There should be no line break here.",
33
+ missingLineBreak: "There should be a line break after this argument."
34
+ }
35
+ },
36
+
37
+ create(context) {
38
+ const sourceCode = context.getSourceCode();
39
+
40
+ const checkers = {
41
+ unexpected: {
42
+ messageId: "unexpectedLineBreak",
43
+ check: (prevToken, currentToken) => prevToken.loc.start.line !== currentToken.loc.start.line,
44
+ createFix: (token, tokenBefore) => fixer =>
45
+ fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
46
+ },
47
+ missing: {
48
+ messageId: "missingLineBreak",
49
+ check: (prevToken, currentToken) => prevToken.loc.start.line === currentToken.loc.start.line,
50
+ createFix: (token, tokenBefore) => fixer =>
51
+ fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
52
+ }
53
+ };
54
+
55
+ /**
56
+ * Check all arguments for line breaks in the CallExpression
57
+ * @param {CallExpression} node node to evaluate
58
+ * @param {{ messageId: string, check: Function }} checker selected checker
59
+ * @returns {void}
60
+ * @private
61
+ */
62
+ function checkArguments(node, checker) {
63
+ for (let i = 1; i < node.arguments.length; i++) {
64
+ const prevArgToken = sourceCode.getFirstToken(node.arguments[i - 1]);
65
+ const currentArgToken = sourceCode.getFirstToken(node.arguments[i]);
66
+
67
+ if (checker.check(prevArgToken, currentArgToken)) {
68
+ const tokenBefore = sourceCode.getTokenBefore(
69
+ currentArgToken,
70
+ { includeComments: true }
71
+ );
72
+
73
+ context.report({
74
+ node,
75
+ loc: {
76
+ start: tokenBefore.loc.end,
77
+ end: currentArgToken.loc.start
78
+ },
79
+ messageId: checker.messageId,
80
+ fix: checker.createFix(currentArgToken, tokenBefore)
81
+ });
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Check if open space is present in a function name
88
+ * @param {CallExpression} node node to evaluate
89
+ * @returns {void}
90
+ * @private
91
+ */
92
+ function check(node) {
93
+ if (node.arguments.length < 2) {
94
+ return;
95
+ }
96
+
97
+ const option = context.options[0] || "always";
98
+
99
+ if (option === "never") {
100
+ checkArguments(node, checkers.unexpected);
101
+ } else if (option === "always") {
102
+ checkArguments(node, checkers.missing);
103
+ } else if (option === "consistent") {
104
+ const firstArgToken = sourceCode.getFirstToken(node.arguments[0]);
105
+ const secondArgToken = sourceCode.getFirstToken(node.arguments[1]);
106
+
107
+ if (firstArgToken.loc.start.line === secondArgToken.loc.start.line) {
108
+ checkArguments(node, checkers.unexpected);
109
+ } else {
110
+ checkArguments(node, checkers.missing);
111
+ }
112
+ }
113
+ }
114
+
115
+ return {
116
+ CallExpression: check,
117
+ NewExpression: check
118
+ };
119
+ }
120
+ };
@@ -51,8 +51,8 @@ module.exports = {
51
51
  expectedBefore: "Expected newline before ')'.",
52
52
  expectedAfter: "Expected newline after '('.",
53
53
  expectedBetween: "Expected newline between arguments/params.",
54
- unexpectedBefore: "Unexpected newline before '('.",
55
- unexpectedAfter: "Unexpected newline after ')'."
54
+ unexpectedBefore: "Unexpected newline before ')'.",
55
+ unexpectedAfter: "Unexpected newline after '('."
56
56
  }
57
57
  },
58
58
 
@@ -232,25 +232,15 @@ module.exports = {
232
232
  };
233
233
  }
234
234
 
235
- default:
236
- throw new TypeError(`unexpected node with type ${node.type}`);
237
- }
238
- }
239
-
240
- /**
241
- * Validates the parentheses for a node
242
- * @param {ASTNode} node The node with parens
243
- * @returns {void}
244
- */
245
- function validateNode(node) {
246
- const parens = getParenTokens(node);
247
-
248
- if (parens) {
249
- validateParens(parens, astUtils.isFunction(node) ? node.params : node.arguments);
235
+ case "ImportExpression": {
236
+ const leftParen = sourceCode.getFirstToken(node, 1);
237
+ const rightParen = sourceCode.getLastToken(node);
250
238
 
251
- if (multilineArgumentsOption) {
252
- validateArguments(parens, astUtils.isFunction(node) ? node.params : node.arguments);
239
+ return { leftParen, rightParen };
253
240
  }
241
+
242
+ default:
243
+ throw new TypeError(`unexpected node with type ${node.type}`);
254
244
  }
255
245
  }
256
246
 
@@ -259,11 +249,33 @@ module.exports = {
259
249
  //----------------------------------------------------------------------
260
250
 
261
251
  return {
262
- ArrowFunctionExpression: validateNode,
263
- CallExpression: validateNode,
264
- FunctionDeclaration: validateNode,
265
- FunctionExpression: validateNode,
266
- NewExpression: validateNode
252
+ [[
253
+ "ArrowFunctionExpression",
254
+ "CallExpression",
255
+ "FunctionDeclaration",
256
+ "FunctionExpression",
257
+ "ImportExpression",
258
+ "NewExpression"
259
+ ]](node) {
260
+ const parens = getParenTokens(node);
261
+ let params;
262
+
263
+ if (node.type === "ImportExpression") {
264
+ params = [node.source];
265
+ } else if (astUtils.isFunction(node)) {
266
+ params = node.params;
267
+ } else {
268
+ params = node.arguments;
269
+ }
270
+
271
+ if (parens) {
272
+ validateParens(parens, params);
273
+
274
+ if (multilineArgumentsOption) {
275
+ validateArguments(parens, params);
276
+ }
277
+ }
278
+ }
267
279
  };
268
280
  }
269
281
  };
@@ -99,7 +99,8 @@ const KNOWN_NODES = new Set([
99
99
  "ImportDeclaration",
100
100
  "ImportSpecifier",
101
101
  "ImportDefaultSpecifier",
102
- "ImportNamespaceSpecifier"
102
+ "ImportNamespaceSpecifier",
103
+ "ImportExpression"
103
104
  ]);
104
105
 
105
106
  /*
@@ -1109,7 +1110,6 @@ module.exports = {
1109
1110
 
1110
1111
  CallExpression: addFunctionCallIndent,
1111
1112
 
1112
-
1113
1113
  "ClassDeclaration[superClass], ClassExpression[superClass]"(node) {
1114
1114
  const classToken = sourceCode.getFirstToken(node);
1115
1115
  const extendsToken = sourceCode.getTokenBefore(node.superClass, astUtils.isNotOpeningParenToken);
@@ -1236,6 +1236,17 @@ module.exports = {
1236
1236
  }
1237
1237
  },
1238
1238
 
1239
+ ImportExpression(node) {
1240
+ const openingParen = sourceCode.getFirstToken(node, 1);
1241
+ const closingParen = sourceCode.getLastToken(node);
1242
+
1243
+ parameterParens.add(openingParen);
1244
+ parameterParens.add(closingParen);
1245
+ offsets.setDesiredOffset(openingParen, sourceCode.getTokenBefore(openingParen), 0);
1246
+
1247
+ addElementListIndent([node.source], openingParen, closingParen, options.CallExpression.arguments);
1248
+ },
1249
+
1239
1250
  "MemberExpression, JSXMemberExpression, MetaProperty"(node) {
1240
1251
  const object = node.type === "MetaProperty" ? node.meta : node.object;
1241
1252
  const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken);
@@ -46,6 +46,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
46
46
  "func-name-matching": () => require("./func-name-matching"),
47
47
  "func-names": () => require("./func-names"),
48
48
  "func-style": () => require("./func-style"),
49
+ "function-call-argument-newline": () => require("./function-call-argument-newline"),
49
50
  "function-paren-newline": () => require("./function-paren-newline"),
50
51
  "generator-star-spacing": () => require("./generator-star-spacing"),
51
52
  "getter-return": () => require("./getter-return"),
@@ -23,7 +23,8 @@ const CAPS_ALLOWED = [
23
23
  "Object",
24
24
  "RegExp",
25
25
  "String",
26
- "Symbol"
26
+ "Symbol",
27
+ "BigInt"
27
28
  ];
28
29
 
29
30
  /**
@@ -120,7 +120,7 @@ module.exports = {
120
120
  }
121
121
 
122
122
  // Skip if the name is not static.
123
- if (!name) {
123
+ if (name === null) {
124
124
  return;
125
125
  }
126
126
 
@@ -33,17 +33,19 @@ module.exports = {
33
33
 
34
34
  return {
35
35
  SwitchStatement(node) {
36
- const mapping = {};
36
+ const previousKeys = new Set();
37
37
 
38
- node.cases.forEach(switchCase => {
39
- const key = sourceCode.getText(switchCase.test);
38
+ for (const switchCase of node.cases) {
39
+ if (switchCase.test) {
40
+ const key = sourceCode.getText(switchCase.test);
40
41
 
41
- if (mapping[key]) {
42
- context.report({ node: switchCase, messageId: "unexpected" });
43
- } else {
44
- mapping[key] = switchCase;
42
+ if (previousKeys.has(key)) {
43
+ context.report({ node: switchCase, messageId: "unexpected" });
44
+ } else {
45
+ previousKeys.add(key);
46
+ }
45
47
  }
46
- });
48
+ }
47
49
  }
48
50
  };
49
51
  }
@@ -98,6 +98,7 @@ module.exports = {
98
98
  grandparent.type === "CallExpression" &&
99
99
  grandparent.callee === parent &&
100
100
  grandparent.arguments.length === 1 &&
101
+ grandparent.arguments[0].type !== "SpreadElement" &&
101
102
  parent.type === "MemberExpression" &&
102
103
  parent.object === node &&
103
104
  astUtils.getStaticPropertyName(parent) === "bind"
@@ -50,8 +50,8 @@ module.exports = {
50
50
  /**
51
51
  * Check if a node is in a context where its value would be coerced to a boolean at runtime.
52
52
  *
53
- * @param {Object} node The node
54
- * @param {Object} parent Its parent
53
+ * @param {ASTNode} node The node
54
+ * @param {ASTNode} parent Its parent
55
55
  * @returns {boolean} If it is in a boolean context
56
56
  */
57
57
  function isInBooleanContext(node, parent) {
@@ -65,6 +65,15 @@ module.exports = {
65
65
  );
66
66
  }
67
67
 
68
+ /**
69
+ * Check if a node has comments inside.
70
+ *
71
+ * @param {ASTNode} node The node to check.
72
+ * @returns {boolean} `true` if it has comments inside.
73
+ */
74
+ function hasCommentsInside(node) {
75
+ return Boolean(sourceCode.getCommentsInside(node).length);
76
+ }
68
77
 
69
78
  return {
70
79
  UnaryExpression(node) {
@@ -89,7 +98,22 @@ module.exports = {
89
98
  context.report({
90
99
  node,
91
100
  messageId: "unexpectedNegation",
92
- fix: fixer => fixer.replaceText(parent, sourceCode.getText(node.argument))
101
+ fix: fixer => {
102
+ if (hasCommentsInside(parent)) {
103
+ return null;
104
+ }
105
+
106
+ let prefix = "";
107
+ const tokenBefore = sourceCode.getTokenBefore(parent);
108
+ const firstReplacementToken = sourceCode.getFirstToken(node.argument);
109
+
110
+ if (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
111
+ !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)) {
112
+ prefix = " ";
113
+ }
114
+
115
+ return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
116
+ }
93
117
  });
94
118
  }
95
119
  },
@@ -106,10 +130,35 @@ module.exports = {
106
130
  messageId: "unexpectedCall",
107
131
  fix: fixer => {
108
132
  if (!node.arguments.length) {
109
- return fixer.replaceText(parent, "true");
133
+ if (parent.type === "UnaryExpression" && parent.operator === "!") {
134
+
135
+ // !Boolean() -> true
136
+
137
+ if (hasCommentsInside(parent)) {
138
+ return null;
139
+ }
140
+
141
+ const replacement = "true";
142
+ let prefix = "";
143
+ const tokenBefore = sourceCode.getTokenBefore(parent);
144
+
145
+ if (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
146
+ !astUtils.canTokensBeAdjacent(tokenBefore, replacement)) {
147
+ prefix = " ";
148
+ }
149
+
150
+ return fixer.replaceText(parent, prefix + replacement);
151
+ }
152
+
153
+ // Boolean() -> false
154
+ if (hasCommentsInside(node)) {
155
+ return null;
156
+ }
157
+ return fixer.replaceText(node, "false");
110
158
  }
111
159
 
112
- if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement") {
160
+ if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement" ||
161
+ hasCommentsInside(node)) {
113
162
  return null;
114
163
  }
115
164
 
@@ -8,6 +8,7 @@
8
8
  // Rule Definition
9
9
  //------------------------------------------------------------------------------
10
10
 
11
+ const { isParenthesized: isParenthesizedRaw } = require("eslint-utils");
11
12
  const astUtils = require("./utils/ast-utils.js");
12
13
 
13
14
  module.exports = {
@@ -48,7 +49,8 @@ module.exports = {
48
49
  nestedBinaryExpressions: { type: "boolean" },
49
50
  returnAssign: { type: "boolean" },
50
51
  ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
51
- enforceForArrowConditionals: { type: "boolean" }
52
+ enforceForArrowConditionals: { type: "boolean" },
53
+ enforceForSequenceExpressions: { type: "boolean" }
52
54
  },
53
55
  additionalProperties: false
54
56
  }
@@ -68,7 +70,6 @@ module.exports = {
68
70
  const sourceCode = context.getSourceCode();
69
71
 
70
72
  const tokensToIgnore = new WeakSet();
71
- const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode);
72
73
  const precedence = astUtils.getPrecedence;
73
74
  const ALL_NODES = context.options[0] !== "functions";
74
75
  const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
@@ -77,6 +78,8 @@ module.exports = {
77
78
  const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
78
79
  const IGNORE_ARROW_CONDITIONALS = ALL_NODES && context.options[1] &&
79
80
  context.options[1].enforceForArrowConditionals === false;
81
+ const IGNORE_SEQUENCE_EXPRESSIONS = ALL_NODES && context.options[1] &&
82
+ context.options[1].enforceForSequenceExpressions === false;
80
83
 
81
84
  const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
82
85
  const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
@@ -115,9 +118,23 @@ module.exports = {
115
118
  }
116
119
  }
117
120
 
121
+ if (node.type === "SequenceExpression" && IGNORE_SEQUENCE_EXPRESSIONS) {
122
+ return false;
123
+ }
124
+
118
125
  return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
119
126
  }
120
127
 
128
+ /**
129
+ * Determines if a node is surrounded by parentheses.
130
+ * @param {ASTNode} node - The node to be checked.
131
+ * @returns {boolean} True if the node is parenthesised.
132
+ * @private
133
+ */
134
+ function isParenthesised(node) {
135
+ return isParenthesizedRaw(1, node, sourceCode);
136
+ }
137
+
121
138
  /**
122
139
  * Determines if a node is surrounded by parentheses twice.
123
140
  * @param {ASTNode} node - The node to be checked.
@@ -125,12 +142,7 @@ module.exports = {
125
142
  * @private
126
143
  */
127
144
  function isParenthesisedTwice(node) {
128
- const previousToken = sourceCode.getTokenBefore(node, 1),
129
- nextToken = sourceCode.getTokenAfter(node, 1);
130
-
131
- return isParenthesised(node) && previousToken && nextToken &&
132
- astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
133
- astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
145
+ return isParenthesizedRaw(2, node, sourceCode);
134
146
  }
135
147
 
136
148
  /**
@@ -406,15 +418,9 @@ module.exports = {
406
418
  report(node.callee);
407
419
  }
408
420
  }
409
- if (node.arguments.length === 1) {
410
- if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
411
- report(node.arguments[0]);
412
- }
413
- } else {
414
- node.arguments
415
- .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
416
- .forEach(report);
417
- }
421
+ node.arguments
422
+ .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
423
+ .forEach(report);
418
424
  }
419
425
 
420
426
  /**
@@ -686,6 +692,13 @@ module.exports = {
686
692
 
687
693
  CallExpression: checkCallNew,
688
694
 
695
+ ClassBody(node) {
696
+ node.body
697
+ .filter(member => member.type === "MethodDefinition" && member.computed &&
698
+ member.key && hasExcessParens(member.key) && precedence(member.key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
699
+ .forEach(member => report(member.key));
700
+ },
701
+
689
702
  ConditionalExpression(node) {
690
703
  if (isReturnAssignException(node)) {
691
704
  return;
@@ -705,7 +718,7 @@ module.exports = {
705
718
  },
706
719
 
707
720
  DoWhileStatement(node) {
708
- if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
721
+ if (hasExcessParens(node.test) && !isCondAssignException(node)) {
709
722
  report(node.test);
710
723
  }
711
724
  },
@@ -830,11 +843,23 @@ module.exports = {
830
843
  },
831
844
 
832
845
  IfStatement(node) {
833
- if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
846
+ if (hasExcessParens(node.test) && !isCondAssignException(node)) {
834
847
  report(node.test);
835
848
  }
836
849
  },
837
850
 
851
+ ImportExpression(node) {
852
+ const { source } = node;
853
+
854
+ if (source.type === "SequenceExpression") {
855
+ if (hasDoubleExcessParens(source)) {
856
+ report(source);
857
+ }
858
+ } else if (hasExcessParens(source)) {
859
+ report(source);
860
+ }
861
+ },
862
+
838
863
  LogicalExpression: checkBinaryLogical,
839
864
 
840
865
  MemberExpression(node) {
@@ -917,7 +942,7 @@ module.exports = {
917
942
  },
918
943
 
919
944
  SwitchStatement(node) {
920
- if (hasDoubleExcessParens(node.discriminant)) {
945
+ if (hasExcessParens(node.discriminant)) {
921
946
  report(node.discriminant);
922
947
  }
923
948
  },
@@ -945,13 +970,13 @@ module.exports = {
945
970
  },
946
971
 
947
972
  WhileStatement(node) {
948
- if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
973
+ if (hasExcessParens(node.test) && !isCondAssignException(node)) {
949
974
  report(node.test);
950
975
  }
951
976
  },
952
977
 
953
978
  WithStatement(node) {
954
- if (hasDoubleExcessParens(node.object)) {
979
+ if (hasExcessParens(node.object)) {
955
980
  report(node.object);
956
981
  }
957
982
  },
@@ -973,7 +998,21 @@ module.exports = {
973
998
 
974
999
  SpreadElement: checkSpreadOperator,
975
1000
  SpreadProperty: checkSpreadOperator,
976
- ExperimentalSpreadProperty: checkSpreadOperator
1001
+ ExperimentalSpreadProperty: checkSpreadOperator,
1002
+
1003
+ TemplateLiteral(node) {
1004
+ node.expressions
1005
+ .filter(e => e && hasExcessParens(e))
1006
+ .forEach(report);
1007
+ },
1008
+
1009
+ AssignmentPattern(node) {
1010
+ const { right } = node;
1011
+
1012
+ if (right && hasExcessParens(right) && precedence(right) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
1013
+ report(right);
1014
+ }
1015
+ }
977
1016
  };
978
1017
 
979
1018
  }