1 | /**
|
2 | * @fileoverview Rule to flag use of unnecessary semicolons
|
3 | * @author Nicholas C. Zakas
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Requirements
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | const FixTracker = require("./utils/fix-tracker");
|
13 | const astUtils = require("./utils/ast-utils");
|
14 |
|
15 | //------------------------------------------------------------------------------
|
16 | // Rule Definition
|
17 | //------------------------------------------------------------------------------
|
18 |
|
19 | module.exports = {
|
20 | meta: {
|
21 | type: "suggestion",
|
22 |
|
23 | docs: {
|
24 | description: "disallow unnecessary semicolons",
|
25 | category: "Possible Errors",
|
26 | recommended: true,
|
27 | url: "https://eslint.org/docs/rules/no-extra-semi"
|
28 | },
|
29 |
|
30 | fixable: "code",
|
31 | schema: [],
|
32 |
|
33 | messages: {
|
34 | unexpected: "Unnecessary semicolon."
|
35 | }
|
36 | },
|
37 |
|
38 | create(context) {
|
39 | const sourceCode = context.getSourceCode();
|
40 |
|
41 | /**
|
42 | * Reports an unnecessary semicolon error.
|
43 | * @param {Node|Token} nodeOrToken A node or a token to be reported.
|
44 | * @returns {void}
|
45 | */
|
46 | function report(nodeOrToken) {
|
47 | context.report({
|
48 | node: nodeOrToken,
|
49 | messageId: "unexpected",
|
50 | fix(fixer) {
|
51 |
|
52 | /*
|
53 | * Expand the replacement range to include the surrounding
|
54 | * tokens to avoid conflicting with semi.
|
55 | * https://github.com/eslint/eslint/issues/7928
|
56 | */
|
57 | return new FixTracker(fixer, context.getSourceCode())
|
58 | .retainSurroundingTokens(nodeOrToken)
|
59 | .remove(nodeOrToken);
|
60 | }
|
61 | });
|
62 | }
|
63 |
|
64 | /**
|
65 | * Checks for a part of a class body.
|
66 | * This checks tokens from a specified token to a next MethodDefinition or the end of class body.
|
67 | * @param {Token} firstToken The first token to check.
|
68 | * @returns {void}
|
69 | */
|
70 | function checkForPartOfClassBody(firstToken) {
|
71 | for (let token = firstToken;
|
72 | token.type === "Punctuator" && !astUtils.isClosingBraceToken(token);
|
73 | token = sourceCode.getTokenAfter(token)
|
74 | ) {
|
75 | if (astUtils.isSemicolonToken(token)) {
|
76 | report(token);
|
77 | }
|
78 | }
|
79 | }
|
80 |
|
81 | return {
|
82 |
|
83 | /**
|
84 | * Reports this empty statement, except if the parent node is a loop.
|
85 | * @param {Node} node A EmptyStatement node to be reported.
|
86 | * @returns {void}
|
87 | */
|
88 | EmptyStatement(node) {
|
89 | const parent = node.parent,
|
90 | allowedParentTypes = [
|
91 | "ForStatement",
|
92 | "ForInStatement",
|
93 | "ForOfStatement",
|
94 | "WhileStatement",
|
95 | "DoWhileStatement",
|
96 | "IfStatement",
|
97 | "LabeledStatement",
|
98 | "WithStatement"
|
99 | ];
|
100 |
|
101 | if (allowedParentTypes.indexOf(parent.type) === -1) {
|
102 | report(node);
|
103 | }
|
104 | },
|
105 |
|
106 | /**
|
107 | * Checks tokens from the head of this class body to the first MethodDefinition or the end of this class body.
|
108 | * @param {Node} node A ClassBody node to check.
|
109 | * @returns {void}
|
110 | */
|
111 | ClassBody(node) {
|
112 | checkForPartOfClassBody(sourceCode.getFirstToken(node, 1)); // 0 is `{`.
|
113 | },
|
114 |
|
115 | /**
|
116 | * Checks tokens from this MethodDefinition to the next MethodDefinition or the end of this class body.
|
117 | * @param {Node} node A MethodDefinition node of the start point.
|
118 | * @returns {void}
|
119 | */
|
120 | MethodDefinition(node) {
|
121 | checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
122 | }
|
123 | };
|
124 |
|
125 | }
|
126 | };
|