text: /**
   * @fileoverview Ensure handling of errors when we know they exist.
   * @author Jamund Ferguson
   */
  
  //------------------------------------------------------------------------------
  // Rule Definition
  //------------------------------------------------------------------------------
  
  module.exports = function(context) {
  
      "use strict";
  
      var errorArgument = context.options[0] || "err";
      var callbacks = [];
      var scopes = 0;
  
      /**
       * Checks if the given argument should be interpreted as a regexp pattern.
       * @param {string} stringToCheck The string which should be checked.
       * @returns {boolean} Whether or not the string should be interpreted as a pattern.
       */
      function isPattern(stringToCheck) {
          var firstChar = stringToCheck[0];
          return firstChar === "^";
      }
  
      /**
       * Checks if the given name matches the configured error argument.
       * @param {string} name The name which should be compared.
       * @returns {boolean} Whether or not the given name matches the configured error variable name.
       */
      function matchesConfiguredErrorName(name) {
          if (isPattern(errorArgument)) {
              var regexp = new RegExp(errorArgument);
              return regexp.test(name);
          }
          return name === errorArgument;
      }
  
      /**
       * Check the arguments to see if we need to start tracking the error object.
       * @param {ASTNode} node The AST node to check.
       * @returns {void}
       */
      function startFunction(node) {
  
          // keep track of nested scopes
          scopes++;
  
          // check if the first argument matches our argument name
          var firstArg = node.params && node.params[0];
          if (firstArg && matchesConfiguredErrorName(firstArg.name)) {
              callbacks.push({handled: false, depth: scopes, errorVariableName: firstArg.name});
          }
      }
  
      /**
       * At the end of a function check to see if the error was handled.
       * @param {ASTNode} node The AST node to check.
       * @returns {void}
       */
      function endFunction(node) {
  
          var callback = callbacks[callbacks.length - 1] || {};
  
          // check if a callback is ending, if so pop it off the stack
          if (callback.depth === scopes) {
              callbacks.pop();
  
              // check if there were no handled errors since the last callback
              if (!callback.handled) {
                  context.report(node, "Expected error to be handled.");
              }
          }
  
          // less nested functions
          scopes--;
  
      }
  
      /**
       * Check to see if we're handling the error object properly.
       * @param {ASTNode} node The AST node to check.
       * @returns {void}
       */
      function checkForError(node) {
          if (callbacks.length > 0) {
              var callback = callbacks[callbacks.length - 1] || {};
  
              // make sure the node's name matches our error argument name
              var isAboutError = node.name === callback.errorVariableName;
  
              // we don't consider these use cases as "handling" the error
              var doNotCount = ["FunctionDeclaration", "FunctionExpression", "CatchClause"];
  
              // make sure this identifier isn't used as part of one of them
              var isHandled = doNotCount.indexOf(node.parent.type) === -1;
  
              if (isAboutError && isHandled) {
                  // record that this callback handled its error
                  callback.handled = true;
              }
          }
      }
  
      return {
          "FunctionDeclaration": startFunction,
          "FunctionExpression": startFunction,
          "Identifier": checkForError,
          "FunctionDeclaration:exit": endFunction,
          "FunctionExpression:exit": endFunction
      };
  
  };
  