aboutsummaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/semi.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint/lib/rules/semi.js')
-rw-r--r--tools/eslint/lib/rules/semi.js130
1 files changed, 130 insertions, 0 deletions
diff --git a/tools/eslint/lib/rules/semi.js b/tools/eslint/lib/rules/semi.js
new file mode 100644
index 0000000000..a9f841cd73
--- /dev/null
+++ b/tools/eslint/lib/rules/semi.js
@@ -0,0 +1,130 @@
+/**
+ * @fileoverview Rule to flag missing semicolons.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+module.exports = function(context) {
+
+ var OPT_OUT_PATTERN = /[\[\(\/\+\-]/; // One of [(/+-
+
+ var always = context.options[0] !== "never";
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports a semicolon error with appropriate location and message.
+ * @param {ASTNode} node The node with an extra or missing semicolon.
+ * @returns {void}
+ */
+ function report(node) {
+ var message = always ? "Missing semicolon." : "Extra semicolon.";
+ context.report(node, context.getLastToken(node).loc.end, message);
+ }
+
+ /**
+ * Checks whether a token is a semicolon punctuator.
+ * @param {Token} token The token.
+ * @returns {boolean} True if token is a semicolon punctuator.
+ */
+ function isSemicolon(token) {
+ return (token.type === "Punctuator" && token.value === ";");
+ }
+
+ /**
+ * Check if a semicolon is unnecessary, only true if:
+ * - next token is on a new line and is not one of the opt-out tokens
+ * - next token is a valid statement divider
+ * @param {Token} lastToken last token of current node.
+ * @returns {boolean} whether the semicolon is unnecessary.
+ */
+ function isUnnecessarySemicolon(lastToken) {
+ var isDivider, isOptOutToken, lastTokenLine, nextToken, nextTokenLine;
+
+ if (!isSemicolon(lastToken)) {
+ return false;
+ }
+
+ nextToken = context.getTokenAfter(lastToken);
+
+ if (!nextToken) {
+ return true;
+ }
+
+ lastTokenLine = lastToken.loc.end.line;
+ nextTokenLine = nextToken.loc.start.line;
+ isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value);
+ isDivider = (nextToken.value === "}" || nextToken.value === ";");
+
+ return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
+ }
+
+ /**
+ * Checks a node to see if it's followed by a semicolon.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkForSemicolon(node) {
+ var lastToken = context.getLastToken(node);
+
+ if (always) {
+ if (!isSemicolon(lastToken)) {
+ report(node);
+ }
+ } else {
+ if (isUnnecessarySemicolon(lastToken)) {
+ report(node);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if there's a semicolon after a variable declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {void}
+ */
+ function checkForSemicolonForVariableDeclaration(node) {
+ var ancestors = context.getAncestors(),
+ parentIndex = ancestors.length - 1,
+ parent = ancestors[parentIndex];
+
+ if ((parent.type !== "ForStatement" || parent.init !== node) &&
+ (!/^For(?:In|Of)Statement/.test(parent.type) || parent.left !== node)
+ ) {
+ checkForSemicolon(node);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public API
+ //--------------------------------------------------------------------------
+
+ return {
+
+ "VariableDeclaration": checkForSemicolonForVariableDeclaration,
+ "ExpressionStatement": checkForSemicolon,
+ "ReturnStatement": checkForSemicolon,
+ "ThrowStatement": checkForSemicolon,
+ "DebuggerStatement": checkForSemicolon,
+ "BreakStatement": checkForSemicolon,
+ "ContinueStatement": checkForSemicolon,
+ "ImportDeclaration": checkForSemicolon,
+ "ExportAllDeclaration": checkForSemicolon,
+ "ExportNamedDeclaration": function (node) {
+ if (!node.declaration) {
+ checkForSemicolon(node);
+ }
+ },
+ "ExportDefaultDeclaration": function (node) {
+ if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
+ checkForSemicolon(node);
+ }
+ }
+ };
+
+};