"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
const util = __importStar(require("../util"));
exports.default = util.createRule({
    name: 'typedef',
    meta: {
        docs: {
            description: 'Requires type annotations to exist',
            category: 'Stylistic Issues',
            recommended: false,
        },
        messages: {
            expectedTypedef: 'expected a type annotation',
            expectedTypedefNamed: 'expected {{name}} to have a type annotation',
        },
        schema: [
            {
                type: 'object',
                properties: {
                    ["arrayDestructuring" /* ArrayDestructuring */]: { type: 'boolean' },
                    ["arrowParameter" /* ArrowParameter */]: { type: 'boolean' },
                    ["memberVariableDeclaration" /* MemberVariableDeclaration */]: { type: 'boolean' },
                    ["objectDestructuring" /* ObjectDestructuring */]: { type: 'boolean' },
                    ["parameter" /* Parameter */]: { type: 'boolean' },
                    ["propertyDeclaration" /* PropertyDeclaration */]: { type: 'boolean' },
                    ["variableDeclaration" /* VariableDeclaration */]: { type: 'boolean' },
                },
            },
        ],
        type: 'suggestion',
    },
    defaultOptions: [
        {
            ["arrowParameter" /* ArrowParameter */]: true,
            ["memberVariableDeclaration" /* MemberVariableDeclaration */]: true,
            ["parameter" /* Parameter */]: true,
            ["propertyDeclaration" /* PropertyDeclaration */]: true,
        },
    ],
    create(context, [options]) {
        function report(location, name) {
            context.report({
                node: location,
                messageId: name ? 'expectedTypedefNamed' : 'expectedTypedef',
                data: { name },
            });
        }
        function getNodeName(node) {
            return node.type === experimental_utils_1.AST_NODE_TYPES.Identifier ? node.name : undefined;
        }
        function checkParameters(params) {
            for (const param of params) {
                let annotationNode;
                switch (param.type) {
                    case experimental_utils_1.AST_NODE_TYPES.AssignmentPattern:
                        annotationNode = param.left;
                        break;
                    case experimental_utils_1.AST_NODE_TYPES.TSParameterProperty:
                        annotationNode = param.parameter;
                        // Check TS parameter property with default value like `constructor(private param: string = 'something') {}`
                        if (annotationNode &&
                            annotationNode.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern) {
                            annotationNode = annotationNode.left;
                        }
                        break;
                    default:
                        annotationNode = param;
                        break;
                }
                if (annotationNode !== undefined && !annotationNode.typeAnnotation) {
                    report(param, getNodeName(param));
                }
            }
        }
        return {
            ArrayPattern(node) {
                if (options["arrayDestructuring" /* ArrayDestructuring */] && !node.typeAnnotation) {
                    report(node);
                }
            },
            ArrowFunctionExpression(node) {
                if (options["arrowParameter" /* ArrowParameter */]) {
                    checkParameters(node.params);
                }
            },
            ClassProperty(node) {
                if (options["memberVariableDeclaration" /* MemberVariableDeclaration */] &&
                    !node.typeAnnotation) {
                    report(node, node.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier
                        ? node.key.name
                        : undefined);
                }
            },
            'FunctionDeclaration, FunctionExpression'(node) {
                if (options["parameter" /* Parameter */]) {
                    checkParameters(node.params);
                }
            },
            ObjectPattern(node) {
                if (options["objectDestructuring" /* ObjectDestructuring */] && !node.typeAnnotation) {
                    report(node);
                }
            },
            'TSIndexSignature, TSPropertySignature'(node) {
                if (options["propertyDeclaration" /* PropertyDeclaration */] && !node.typeAnnotation) {
                    report(node, node.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature
                        ? getNodeName(node.key)
                        : undefined);
                }
            },
            VariableDeclarator(node) {
                if (!options["variableDeclaration" /* VariableDeclaration */] ||
                    node.id.typeAnnotation ||
                    (node.id.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern &&
                        !options["arrayDestructuring" /* ArrayDestructuring */]) ||
                    (node.id.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern &&
                        !options["objectDestructuring" /* ObjectDestructuring */])) {
                    return;
                }
                let current = node.parent;
                while (current) {
                    switch (current.type) {
                        case experimental_utils_1.AST_NODE_TYPES.VariableDeclaration:
                            // Keep looking upwards
                            current = current.parent;
                            break;
                        case experimental_utils_1.AST_NODE_TYPES.ForOfStatement:
                        case experimental_utils_1.AST_NODE_TYPES.ForInStatement:
                            // Stop traversing and don't report an error
                            return;
                        default:
                            // Stop traversing
                            current = undefined;
                            break;
                    }
                }
                report(node, getNodeName(node.id));
            },
        };
    },
});
//# sourceMappingURL=typedef.js.map