/**
 * @fileoverview Checks for unreachable code due to return, throws, break, and continue.
 * @author Joel Feenstra
 * @copyright 2015 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a given variable declarator has the initializer.
 * @param {ASTNode} node - A VariableDeclarator node to check.
 * @returns {boolean} `true` if the node has the initializer.
 */
function isInitialized(node) {
    return Boolean(node.init);
}

/**
 * Checks whether or not a given code path segment is unreachable.
 * @param {CodePathSegment} segment - A CodePathSegment to check.
 * @returns {boolean} `true` if the segment is unreachable.
 */
function isUnreachable(segment) {
    return !segment.reachable;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
    var currentCodePath = null;

    /**
     * Reports a given node if it's unreachable.
     * @param {ASTNode} node - A statement node to report.
     * @returns {void}
     */
    function reportIfUnreachable(node) {
        if (currentCodePath.currentSegments.every(isUnreachable)) {
            context.report({message: "Unreachable code.", node: node});
        }
    }

    return {
        // Manages the current code path.
        "onCodePathStart": function(codePath) {
            currentCodePath = codePath;
        },
        "onCodePathEnd": function() {
            currentCodePath = currentCodePath.upper;
        },

        // Registers for all statement nodes (excludes FunctionDeclaration).
        BlockStatement: reportIfUnreachable,
        BreakStatement: reportIfUnreachable,
        ClassDeclaration: reportIfUnreachable,
        ContinueStatement: reportIfUnreachable,
        DebuggerStatement: reportIfUnreachable,
        DoWhileStatement: reportIfUnreachable,
        EmptyStatement: reportIfUnreachable,
        ExpressionStatement: reportIfUnreachable,
        ForInStatement: reportIfUnreachable,
        ForOfStatement: reportIfUnreachable,
        ForStatement: reportIfUnreachable,
        IfStatement: reportIfUnreachable,
        ImportDeclaration: reportIfUnreachable,
        LabeledStatement: reportIfUnreachable,
        ReturnStatement: reportIfUnreachable,
        SwitchStatement: reportIfUnreachable,
        ThrowStatement: reportIfUnreachable,
        TryStatement: reportIfUnreachable,
        VariableDeclaration: function(node) {
            if (node.kind !== "var" || node.declarations.some(isInitialized)) {
                reportIfUnreachable(node);
            }
        },
        WhileStatement: reportIfUnreachable,
        WithStatement: reportIfUnreachable,
        ExportNamedDeclaration: reportIfUnreachable,
        ExportDefaultDeclaration: reportIfUnreachable,
        ExportAllDeclaration: reportIfUnreachable
    };
};

module.exports.schema = [];
