Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

semi.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /**
  2. * @fileoverview Rule to flag missing semicolons.
  3. * @author Nicholas C. Zakas
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const FixTracker = require("../util/fix-tracker");
  10. const astUtils = require("../util/ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Rule Definition
  13. //------------------------------------------------------------------------------
  14. module.exports = {
  15. meta: {
  16. type: "layout",
  17. docs: {
  18. description: "require or disallow semicolons instead of ASI",
  19. category: "Stylistic Issues",
  20. recommended: false,
  21. url: "https://eslint.org/docs/rules/semi"
  22. },
  23. fixable: "code",
  24. schema: {
  25. anyOf: [
  26. {
  27. type: "array",
  28. items: [
  29. {
  30. enum: ["never"]
  31. },
  32. {
  33. type: "object",
  34. properties: {
  35. beforeStatementContinuationChars: {
  36. enum: ["always", "any", "never"]
  37. }
  38. },
  39. additionalProperties: false
  40. }
  41. ],
  42. minItems: 0,
  43. maxItems: 2
  44. },
  45. {
  46. type: "array",
  47. items: [
  48. {
  49. enum: ["always"]
  50. },
  51. {
  52. type: "object",
  53. properties: {
  54. omitLastInOneLineBlock: { type: "boolean" }
  55. },
  56. additionalProperties: false
  57. }
  58. ],
  59. minItems: 0,
  60. maxItems: 2
  61. }
  62. ]
  63. }
  64. },
  65. create(context) {
  66. const OPT_OUT_PATTERN = /^[-[(/+`]/; // One of [(/+-`
  67. const options = context.options[1];
  68. const never = context.options[0] === "never";
  69. const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
  70. const beforeStatementContinuationChars = (options && options.beforeStatementContinuationChars) || "any";
  71. const sourceCode = context.getSourceCode();
  72. //--------------------------------------------------------------------------
  73. // Helpers
  74. //--------------------------------------------------------------------------
  75. /**
  76. * Reports a semicolon error with appropriate location and message.
  77. * @param {ASTNode} node The node with an extra or missing semicolon.
  78. * @param {boolean} missing True if the semicolon is missing.
  79. * @returns {void}
  80. */
  81. function report(node, missing) {
  82. const lastToken = sourceCode.getLastToken(node);
  83. let message,
  84. fix,
  85. loc = lastToken.loc;
  86. if (!missing) {
  87. message = "Missing semicolon.";
  88. loc = loc.end;
  89. fix = function(fixer) {
  90. return fixer.insertTextAfter(lastToken, ";");
  91. };
  92. } else {
  93. message = "Extra semicolon.";
  94. loc = loc.start;
  95. fix = function(fixer) {
  96. /*
  97. * Expand the replacement range to include the surrounding
  98. * tokens to avoid conflicting with no-extra-semi.
  99. * https://github.com/eslint/eslint/issues/7928
  100. */
  101. return new FixTracker(fixer, sourceCode)
  102. .retainSurroundingTokens(lastToken)
  103. .remove(lastToken);
  104. };
  105. }
  106. context.report({
  107. node,
  108. loc,
  109. message,
  110. fix
  111. });
  112. }
  113. /**
  114. * Check whether a given semicolon token is redandant.
  115. * @param {Token} semiToken A semicolon token to check.
  116. * @returns {boolean} `true` if the next token is `;` or `}`.
  117. */
  118. function isRedundantSemi(semiToken) {
  119. const nextToken = sourceCode.getTokenAfter(semiToken);
  120. return (
  121. !nextToken ||
  122. astUtils.isClosingBraceToken(nextToken) ||
  123. astUtils.isSemicolonToken(nextToken)
  124. );
  125. }
  126. /**
  127. * Check whether a given token is the closing brace of an arrow function.
  128. * @param {Token} lastToken A token to check.
  129. * @returns {boolean} `true` if the token is the closing brace of an arrow function.
  130. */
  131. function isEndOfArrowBlock(lastToken) {
  132. if (!astUtils.isClosingBraceToken(lastToken)) {
  133. return false;
  134. }
  135. const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
  136. return (
  137. node.type === "BlockStatement" &&
  138. node.parent.type === "ArrowFunctionExpression"
  139. );
  140. }
  141. /**
  142. * Check whether a given node is on the same line with the next token.
  143. * @param {Node} node A statement node to check.
  144. * @returns {boolean} `true` if the node is on the same line with the next token.
  145. */
  146. function isOnSameLineWithNextToken(node) {
  147. const prevToken = sourceCode.getLastToken(node, 1);
  148. const nextToken = sourceCode.getTokenAfter(node);
  149. return !!nextToken && astUtils.isTokenOnSameLine(prevToken, nextToken);
  150. }
  151. /**
  152. * Check whether a given node can connect the next line if the next line is unreliable.
  153. * @param {Node} node A statement node to check.
  154. * @returns {boolean} `true` if the node can connect the next line.
  155. */
  156. function maybeAsiHazardAfter(node) {
  157. const t = node.type;
  158. if (t === "DoWhileStatement" ||
  159. t === "BreakStatement" ||
  160. t === "ContinueStatement" ||
  161. t === "DebuggerStatement" ||
  162. t === "ImportDeclaration" ||
  163. t === "ExportAllDeclaration"
  164. ) {
  165. return false;
  166. }
  167. if (t === "ReturnStatement") {
  168. return Boolean(node.argument);
  169. }
  170. if (t === "ExportNamedDeclaration") {
  171. return Boolean(node.declaration);
  172. }
  173. if (isEndOfArrowBlock(sourceCode.getLastToken(node, 1))) {
  174. return false;
  175. }
  176. return true;
  177. }
  178. /**
  179. * Check whether a given token can connect the previous statement.
  180. * @param {Token} token A token to check.
  181. * @returns {boolean} `true` if the token is one of `[`, `(`, `/`, `+`, `-`, ```, `++`, and `--`.
  182. */
  183. function maybeAsiHazardBefore(token) {
  184. return (
  185. Boolean(token) &&
  186. OPT_OUT_PATTERN.test(token.value) &&
  187. token.value !== "++" &&
  188. token.value !== "--"
  189. );
  190. }
  191. /**
  192. * Check if the semicolon of a given node is unnecessary, only true if:
  193. * - next token is a valid statement divider (`;` or `}`).
  194. * - next token is on a new line and the node is not connectable to the new line.
  195. * @param {Node} node A statement node to check.
  196. * @returns {boolean} whether the semicolon is unnecessary.
  197. */
  198. function canRemoveSemicolon(node) {
  199. if (isRedundantSemi(sourceCode.getLastToken(node))) {
  200. return true; // `;;` or `;}`
  201. }
  202. if (isOnSameLineWithNextToken(node)) {
  203. return false; // One liner.
  204. }
  205. if (beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) {
  206. return true; // ASI works. This statement doesn't connect to the next.
  207. }
  208. if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
  209. return true; // ASI works. The next token doesn't connect to this statement.
  210. }
  211. return false;
  212. }
  213. /**
  214. * Checks a node to see if it's in a one-liner block statement.
  215. * @param {ASTNode} node The node to check.
  216. * @returns {boolean} whether the node is in a one-liner block statement.
  217. */
  218. function isOneLinerBlock(node) {
  219. const parent = node.parent;
  220. const nextToken = sourceCode.getTokenAfter(node);
  221. if (!nextToken || nextToken.value !== "}") {
  222. return false;
  223. }
  224. return (
  225. !!parent &&
  226. parent.type === "BlockStatement" &&
  227. parent.loc.start.line === parent.loc.end.line
  228. );
  229. }
  230. /**
  231. * Checks a node to see if it's followed by a semicolon.
  232. * @param {ASTNode} node The node to check.
  233. * @returns {void}
  234. */
  235. function checkForSemicolon(node) {
  236. const isSemi = astUtils.isSemicolonToken(sourceCode.getLastToken(node));
  237. if (never) {
  238. if (isSemi && canRemoveSemicolon(node)) {
  239. report(node, true);
  240. } else if (!isSemi && beforeStatementContinuationChars === "always" && maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
  241. report(node);
  242. }
  243. } else {
  244. const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
  245. if (isSemi && oneLinerBlock) {
  246. report(node, true);
  247. } else if (!isSemi && !oneLinerBlock) {
  248. report(node);
  249. }
  250. }
  251. }
  252. /**
  253. * Checks to see if there's a semicolon after a variable declaration.
  254. * @param {ASTNode} node The node to check.
  255. * @returns {void}
  256. */
  257. function checkForSemicolonForVariableDeclaration(node) {
  258. const parent = node.parent;
  259. if ((parent.type !== "ForStatement" || parent.init !== node) &&
  260. (!/^For(?:In|Of)Statement/.test(parent.type) || parent.left !== node)
  261. ) {
  262. checkForSemicolon(node);
  263. }
  264. }
  265. //--------------------------------------------------------------------------
  266. // Public API
  267. //--------------------------------------------------------------------------
  268. return {
  269. VariableDeclaration: checkForSemicolonForVariableDeclaration,
  270. ExpressionStatement: checkForSemicolon,
  271. ReturnStatement: checkForSemicolon,
  272. ThrowStatement: checkForSemicolon,
  273. DoWhileStatement: checkForSemicolon,
  274. DebuggerStatement: checkForSemicolon,
  275. BreakStatement: checkForSemicolon,
  276. ContinueStatement: checkForSemicolon,
  277. ImportDeclaration: checkForSemicolon,
  278. ExportAllDeclaration: checkForSemicolon,
  279. ExportNamedDeclaration(node) {
  280. if (!node.declaration) {
  281. checkForSemicolon(node);
  282. }
  283. },
  284. ExportDefaultDeclaration(node) {
  285. if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
  286. checkForSemicolon(node);
  287. }
  288. }
  289. };
  290. }
  291. };