2 * @fileoverview Ensure handling of errors when we know they exist.
3 * @author Jamund Ferguson
4 * @copyright 2014 Jamund Ferguson. All rights reserved.
9 //------------------------------------------------------------------------------
11 //------------------------------------------------------------------------------
13 module.exports = function(context) {
15 var errorArgument = context.options[0] || "err";
20 * Checks if the given argument should be interpreted as a regexp pattern.
21 * @param {string} stringToCheck The string which should be checked.
22 * @returns {boolean} Whether or not the string should be interpreted as a pattern.
24 function isPattern(stringToCheck) {
25 var firstChar = stringToCheck[0];
26 return firstChar === "^";
30 * Checks if the given name matches the configured error argument.
31 * @param {string} name The name which should be compared.
32 * @returns {boolean} Whether or not the given name matches the configured error variable name.
34 function matchesConfiguredErrorName(name) {
35 if (isPattern(errorArgument)) {
36 var regexp = new RegExp(errorArgument);
37 return regexp.test(name);
39 return name === errorArgument;
43 * Check the arguments to see if we need to start tracking the error object.
44 * @param {ASTNode} node The AST node to check.
47 function startFunction(node) {
49 // keep track of nested scopes
52 // check if the first argument matches our argument name
53 var firstArg = node.params && node.params[0];
54 if (firstArg && matchesConfiguredErrorName(firstArg.name)) {
55 callbacks.push({handled: false, depth: scopes, errorVariableName: firstArg.name});
60 * At the end of a function check to see if the error was handled.
61 * @param {ASTNode} node The AST node to check.
64 function endFunction(node) {
66 var callback = callbacks[callbacks.length - 1] || {};
68 // check if a callback is ending, if so pop it off the stack
69 if (callback.depth === scopes) {
72 // check if there were no handled errors since the last callback
73 if (!callback.handled) {
74 context.report(node, "Expected error to be handled.");
78 // less nested functions
84 * Check to see if we're handling the error object properly.
85 * @param {ASTNode} node The AST node to check.
88 function checkForError(node) {
89 if (callbacks.length > 0) {
90 var callback = callbacks[callbacks.length - 1] || {};
92 // make sure the node's name matches our error argument name
93 var isAboutError = node.name === callback.errorVariableName;
95 // we don't consider these use cases as "handling" the error
96 var doNotCount = ["FunctionDeclaration", "ArrowFunctionExpression", "FunctionExpression", "CatchClause"];
98 // make sure this identifier isn't used as part of one of them
99 var isHandled = doNotCount.indexOf(node.parent.type) === -1;
101 if (isAboutError && isHandled) {
102 // record that this callback handled its error
103 callback.handled = true;
109 "FunctionDeclaration": startFunction,
110 "FunctionExpression": startFunction,
111 "ArrowFunctionExpression": startFunction,
112 "Identifier": checkForError,
113 "FunctionDeclaration:exit": endFunction,
114 "FunctionExpression:exit": endFunction,
115 "ArrowFunctionExpression:exit": endFunction