diff options
Diffstat (limited to 'tools/eslint/lib/rules/semi.js')
-rw-r--r-- | tools/eslint/lib/rules/semi.js | 130 |
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); + } + } + }; + +}; |