f57e4d1752dc17dab3c3c89909e37d34f3876c74
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Ensure handling of errors when we know they exist.
3  * @author Jamund Ferguson
4  * @copyright 2014 Jamund Ferguson. All rights reserved.
5  */
6
7 "use strict";
8
9 //------------------------------------------------------------------------------
10 // Rule Definition
11 //------------------------------------------------------------------------------
12
13 module.exports = function(context) {
14
15     var errorArgument = context.options[0] || "err";
16     var callbacks = [];
17     var scopes = 0;
18
19     /**
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.
23      */
24     function isPattern(stringToCheck) {
25         var firstChar = stringToCheck[0];
26         return firstChar === "^";
27     }
28
29     /**
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.
33      */
34     function matchesConfiguredErrorName(name) {
35         if (isPattern(errorArgument)) {
36             var regexp = new RegExp(errorArgument);
37             return regexp.test(name);
38         }
39         return name === errorArgument;
40     }
41
42     /**
43      * Check the arguments to see if we need to start tracking the error object.
44      * @param {ASTNode} node The AST node to check.
45      * @returns {void}
46      */
47     function startFunction(node) {
48
49         // keep track of nested scopes
50         scopes++;
51
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});
56         }
57     }
58
59     /**
60      * At the end of a function check to see if the error was handled.
61      * @param {ASTNode} node The AST node to check.
62      * @returns {void}
63      */
64     function endFunction(node) {
65
66         var callback = callbacks[callbacks.length - 1] || {};
67
68         // check if a callback is ending, if so pop it off the stack
69         if (callback.depth === scopes) {
70             callbacks.pop();
71
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.");
75             }
76         }
77
78         // less nested functions
79         scopes--;
80
81     }
82
83     /**
84      * Check to see if we're handling the error object properly.
85      * @param {ASTNode} node The AST node to check.
86      * @returns {void}
87      */
88     function checkForError(node) {
89         if (callbacks.length > 0) {
90             var callback = callbacks[callbacks.length - 1] || {};
91
92             // make sure the node's name matches our error argument name
93             var isAboutError = node.name === callback.errorVariableName;
94
95             // we don't consider these use cases as "handling" the error
96             var doNotCount = ["FunctionDeclaration", "ArrowFunctionExpression", "FunctionExpression", "CatchClause"];
97
98             // make sure this identifier isn't used as part of one of them
99             var isHandled = doNotCount.indexOf(node.parent.type) === -1;
100
101             if (isAboutError && isHandled) {
102                 // record that this callback handled its error
103                 callback.handled = true;
104             }
105         }
106     }
107
108     return {
109         "FunctionDeclaration": startFunction,
110         "FunctionExpression": startFunction,
111         "ArrowFunctionExpression": startFunction,
112         "Identifier": checkForError,
113         "FunctionDeclaration:exit": endFunction,
114         "FunctionExpression:exit": endFunction,
115         "ArrowFunctionExpression:exit": endFunction
116     };
117
118 };