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