"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Validator = exports.KEYWORDS = void 0;
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const dockerfile_ast_1 = require("dockerfile-ast");
const main_1 = require("./main");
exports.KEYWORDS = [
    "ADD",
    "ARG",
    "CMD",
    "COPY",
    "ENTRYPOINT",
    "ENV",
    "EXPOSE",
    "FROM",
    "HEALTHCHECK",
    "LABEL",
    "MAINTAINER",
    "ONBUILD",
    "RUN",
    "SHELL",
    "STOPSIGNAL",
    "USER",
    "VOLUME",
    "WORKDIR"
];
/**
 * A list of variables that are prepopulated when using the BuildKit
 * backend.
 */
const PREDEFINED_VARIABLES = [
    "TARGETPLATFORM",
    "TARGETOS",
    "TARGETARCH",
    "TARGETVARIANT",
    "BUILDPLATFORM",
    "BUILDOS",
    "BUILDARCH",
    "BUILDVARIAN"
];
class Validator {
    constructor(settings) {
        this.settings = {
            deprecatedMaintainer: main_1.ValidationSeverity.WARNING,
            directiveCasing: main_1.ValidationSeverity.WARNING,
            emptyContinuationLine: main_1.ValidationSeverity.WARNING,
            instructionCasing: main_1.ValidationSeverity.WARNING,
            instructionCmdMultiple: main_1.ValidationSeverity.WARNING,
            instructionEntrypointMultiple: main_1.ValidationSeverity.WARNING,
            instructionHealthcheckMultiple: main_1.ValidationSeverity.WARNING,
            instructionJSONInSingleQuotes: main_1.ValidationSeverity.WARNING,
            instructionWorkdirRelative: main_1.ValidationSeverity.WARNING
        };
        if (settings) {
            this.settings = settings;
        }
    }
    checkDirectives(dockerfile, problems) {
        const duplicatedEscapes = [];
        for (const directive of dockerfile.getDirectives()) {
            if (directive.getDirective() === dockerfile_ast_1.Directive.escape) {
                duplicatedEscapes.push(directive);
            }
        }
        if (duplicatedEscapes.length > 1) {
            // multiple escape parser directives have been found
            for (let i = 1; i < duplicatedEscapes.length; i++) {
                problems.push(Validator.createDuplicatedEscapeDirective(duplicatedEscapes[i].getNameRange().start, duplicatedEscapes[i].getValueRange().end));
            }
            return;
        }
        for (const directive of dockerfile.getDirectives()) {
            const directiveName = directive.getDirective();
            if (directiveName === dockerfile_ast_1.Directive.escape) {
                const value = directive.getValue();
                if (value !== '\\' && value !== '`' && value !== "") {
                    // if the directive's value is invalid or isn't the empty string, flag it
                    const range = directive.getValueRange();
                    problems.push(Validator.createInvalidEscapeDirective(range.start, range.end, value));
                }
                if (directive.getName() !== dockerfile_ast_1.Directive.escape) {
                    const range = directive.getNameRange();
                    const diagnostic = this.createLowercaseDirective(range.start, range.end);
                    if (diagnostic) {
                        problems.push(diagnostic);
                    }
                }
            }
        }
    }
    /**
     * Checks the arguments of the given instruction.
     *
     * @param instruction the instruction to validate
     * @param problems an array of identified problems in the document
     * @param expectedArgCount an array of expected number of arguments
     *                         for the instruction, if its length is 1
     *                         and its value is -1, any number of
     *                         arguments greater than zero is valid
     * @param validate the function to use to validate an argument
     * @param createIncompleteDiagnostic the function to use to create a diagnostic
     *                                   if the number of arguments is incorrect
     */
    checkArguments(instruction, problems, expectedArgCount, validate, createIncompleteDiagnostic) {
        let args = instruction instanceof dockerfile_ast_1.PropertyInstruction ? instruction.getPropertyArguments() : instruction.getArguments();
        if (args.length === 0) {
            if (instruction.getKeyword() !== dockerfile_ast_1.Keyword.RUN) {
                // all instructions are expected to have at least one argument
                const range = instruction.getInstructionRange();
                problems.push(Validator.createMissingArgument(range.start.line, range.start, range.end));
            }
        }
        else if (expectedArgCount[0] === -1) {
            for (let i = 0; i < args.length; i++) {
                let createInvalidDiagnostic = validate(i, args[i].getValue(), args[i].getRange());
                if (createInvalidDiagnostic) {
                    let range = args[i].getRange();
                    problems.push(createInvalidDiagnostic(instruction.getInstructionRange().start.line, range.start, range.end, args[i].getValue()));
                }
            }
        }
        else {
            for (let i = 0; i < expectedArgCount.length; i++) {
                if (expectedArgCount[i] === args.length) {
                    for (let j = 0; j < args.length; j++) {
                        let range = args[j].getRange();
                        let createInvalidDiagnostic = validate(j, args[j].getValue(), range);
                        if (createInvalidDiagnostic instanceof Function) {
                            problems.push(createInvalidDiagnostic(instruction.getInstructionRange().start.line, range.start, range.end, args[j].getValue()));
                        }
                        else if (createInvalidDiagnostic !== null) {
                            problems.push(createInvalidDiagnostic);
                        }
                    }
                    return;
                }
            }
            let range = args[args.length - 1].getRange();
            if (createIncompleteDiagnostic) {
                problems.push(createIncompleteDiagnostic(instruction.getInstructionRange().start.line, range.start, range.end));
            }
            else {
                problems.push(Validator.createExtraArgument(instruction.getInstructionRange().start.line, range.start, range.end));
            }
        }
    }
    checkVariables(instruction, problems) {
        for (let variable of instruction.getVariables()) {
            let modifier = variable.getModifier();
            if (modifier !== null) {
                switch (instruction.getKeyword()) {
                    case dockerfile_ast_1.Keyword.CMD:
                    case dockerfile_ast_1.Keyword.ENTRYPOINT:
                    case dockerfile_ast_1.Keyword.RUN:
                        // allow shell expansions to go through for RUN instructions
                        break;
                    default:
                        if (modifier === "") {
                            problems.push(Validator.createVariableUnsupportedModifier(instruction.getRange().start.line, variable.getRange(), variable.toString(), modifier));
                        }
                        else if (modifier !== '+' && modifier !== '-' && modifier !== '?') {
                            problems.push(Validator.createVariableUnsupportedModifier(instruction.getRange().start.line, variable.getModifierRange(), variable.toString(), modifier));
                        }
                        break;
                }
            }
        }
    }
    checkProperty(document, escapeChar, keyword, instructionLine, property, firstProperty, optionalValue, problems) {
        let name = property.getName();
        if (name === "") {
            let range = property.getRange();
            problems.push(Validator.createSyntaxMissingNames(instructionLine, range.start, range.end, keyword));
        }
        else if (name.indexOf('=') !== -1) {
            let nameRange = property.getNameRange();
            let unescapedName = document.getText(nameRange);
            let index = unescapedName.indexOf('=');
            if (unescapedName.charAt(0) === '\'') {
                problems.push(Validator.createSyntaxMissingSingleQuote(instructionLine, nameRange.start, document.positionAt(document.offsetAt(nameRange.start) + index), unescapedName.substring(0, unescapedName.indexOf('='))));
            }
            else if (unescapedName.charAt(0) === '"') {
                problems.push(Validator.createSyntaxMissingDoubleQuote(instructionLine, nameRange.start, document.positionAt(document.offsetAt(nameRange.start) + index), unescapedName.substring(0, unescapedName.indexOf('='))));
            }
            return;
        }
        let value = property.getValue();
        if (value === null) {
            if (!optionalValue) {
                let range = property.getNameRange();
                if (firstProperty) {
                    problems.push(Validator.createENVRequiresTwoArguments(instructionLine, range.start, range.end));
                }
                else {
                    problems.push(Validator.createSyntaxMissingEquals(instructionLine, range.start, range.end, name));
                }
            }
        }
        else if (value.charAt(0) === '"') {
            let found = false;
            for (let i = 1; i < value.length; i++) {
                switch (value.charAt(i)) {
                    case escapeChar:
                        i++;
                        break;
                    case '"':
                        if (i === value.length - 1) {
                            found = true;
                        }
                        break;
                }
            }
            if (!found) {
                let range = property.getValueRange();
                problems.push(Validator.createSyntaxMissingDoubleQuote(instructionLine, range.start, range.end, property.getUnescapedValue()));
            }
        }
        else if (value.charAt(0) === '\'' && value.charAt(value.length - 1) !== '\'') {
            let range = property.getValueRange();
            problems.push(Validator.createSyntaxMissingSingleQuote(instructionLine, range.start, range.end, value));
        }
    }
    validate(document) {
        this.document = document;
        let problems = [];
        let dockerfile = dockerfile_ast_1.DockerfileParser.parse(document.getText());
        this.checkDirectives(dockerfile, problems);
        let instructions = dockerfile.getInstructions();
        if (instructions.length === 0 || dockerfile.getARGs().length === instructions.length) {
            // no instructions in this file, or only ARGs
            problems.push(Validator.createNoSourceImage(document.positionAt(0), document.positionAt(0)));
        }
        let cmds = [];
        let entrypoints = [];
        let healthchecks = [];
        for (let instruction of instructions) {
            if (instruction instanceof dockerfile_ast_1.Cmd) {
                cmds.push(instruction);
            }
            else if (instruction instanceof dockerfile_ast_1.Entrypoint) {
                entrypoints.push(instruction);
            }
            else if (instruction instanceof dockerfile_ast_1.Healthcheck) {
                healthchecks.push(instruction);
            }
            else if (instruction instanceof dockerfile_ast_1.From) {
                this.createDuplicatesDiagnostics(problems, this.settings.instructionCmdMultiple, "CMD", cmds);
                this.createDuplicatesDiagnostics(problems, this.settings.instructionEntrypointMultiple, "ENTRYPOINT", entrypoints);
                this.createDuplicatesDiagnostics(problems, this.settings.instructionHealthcheckMultiple, "HEALTHCHECK", healthchecks);
                cmds = [];
                entrypoints = [];
                healthchecks = [];
            }
        }
        this.createDuplicatesDiagnostics(problems, this.settings.instructionCmdMultiple, "CMD", cmds);
        this.createDuplicatesDiagnostics(problems, this.settings.instructionEntrypointMultiple, "ENTRYPOINT", entrypoints);
        this.createDuplicatesDiagnostics(problems, this.settings.instructionHealthcheckMultiple, "HEALTHCHECK", healthchecks);
        this.createDuplicateBuildStageNameDiagnostics(problems, dockerfile.getFROMs());
        let escapeChar = dockerfile.getEscapeCharacter();
        let hasFrom = false;
        for (let instruction of dockerfile.getInstructions()) {
            let keyword = instruction.getKeyword();
            if (keyword === "FROM") {
                hasFrom = true;
            }
            else if (!hasFrom && keyword !== "ARG") {
                // first non-ARG instruction is not a FROM
                let range = instruction.getInstructionRange();
                problems.push(Validator.createNoSourceImage(range.start, range.end));
                hasFrom = true;
            }
            this.validateInstruction(document, escapeChar, instruction, keyword, false, problems);
            this.checkVariables(instruction, problems);
        }
        for (let instruction of dockerfile.getOnbuildTriggers()) {
            this.validateInstruction(document, escapeChar, instruction, instruction.getKeyword(), true, problems);
        }
        const ignoredLines = [];
        for (const comment of dockerfile.getComments()) {
            if (comment.getContent() === "dockerfile-utils: ignore") {
                ignoredLines.push(comment.getRange().start.line);
            }
        }
        problemsCheck: for (let i = 0; i < problems.length; i++) {
            if (problems[i].instructionLine !== null) {
                for (const ignoredLine of ignoredLines) {
                    if (ignoredLine + 1 === problems[i].instructionLine) {
                        problems.splice(i, 1);
                        i--;
                        continue problemsCheck;
                    }
                }
            }
        }
        return problems;
    }
    /**
     * Retrieves the line numbers that corresponds to the content of
     * here-documents in the given instruction. The line numbers are
     * zero-based.
     *
     * @param instruction the instruction to check
     * @returns an array of line numbers where content of
     *          here-documents are defined
     */
    getHeredocLines(instruction) {
        if (instruction instanceof dockerfile_ast_1.Copy || instruction instanceof dockerfile_ast_1.Run) {
            const lines = [];
            for (const heredoc of instruction.getHeredocs()) {
                const range = heredoc.getContentRange();
                if (range !== null) {
                    for (let i = range.start.line; i <= range.end.line; i++) {
                        lines.push(i);
                    }
                }
            }
            return lines;
        }
        return [];
    }
    validateInstruction(document, escapeChar, instruction, keyword, isTrigger, problems) {
        if (exports.KEYWORDS.indexOf(keyword) === -1) {
            let range = instruction.getInstructionRange();
            // invalid instruction found
            problems.push(Validator.createUnknownInstruction(range.start.line, range.start, range.end, keyword));
        }
        else {
            if (keyword !== instruction.getInstruction()) {
                let range = instruction.getInstructionRange();
                // warn about uppercase convention if the keyword doesn't match the actual instruction
                let diagnostic = this.createUppercaseInstruction(range.start.line, range.start, range.end);
                if (diagnostic) {
                    problems.push(diagnostic);
                }
            }
            if (keyword === "MAINTAINER") {
                let range = instruction.getInstructionRange();
                let diagnostic = this.createMaintainerDeprecated(range.start.line, range.start, range.end);
                if (diagnostic) {
                    problems.push(diagnostic);
                }
            }
            const fullRange = instruction.getRange();
            if (fullRange.start.line !== fullRange.end.line && !isTrigger) {
                // if the instruction spans multiple lines, check for empty newlines
                const content = document.getText();
                const endingLine = fullRange.end.line;
                const skippedLines = this.getHeredocLines(instruction);
                let skipIndex = 0;
                let start = -1;
                for (let i = fullRange.start.line; i <= endingLine; i++) {
                    if (i === skippedLines[skipIndex]) {
                        skipIndex++;
                        continue;
                    }
                    const lineContent = content.substring(document.offsetAt(vscode_languageserver_types_1.Position.create(i, 0)), document.offsetAt(vscode_languageserver_types_1.Position.create(i + 1, 0)));
                    if (lineContent.trim().length === 0) {
                        if (start === -1) {
                            start = i;
                            continue;
                        }
                    }
                    else if (start !== -1) {
                        const diagnostic = Validator.createEmptyContinuationLine(vscode_languageserver_types_1.Position.create(start, 0), vscode_languageserver_types_1.Position.create(i, 0), this.settings.emptyContinuationLine);
                        if (diagnostic) {
                            problems.push(diagnostic);
                        }
                        start = -1;
                    }
                }
                if (start !== -1) {
                    const diagnostic = Validator.createEmptyContinuationLine(vscode_languageserver_types_1.Position.create(start, 0), vscode_languageserver_types_1.Position.create(endingLine + 1, 0), this.settings.emptyContinuationLine);
                    if (diagnostic) {
                        problems.push(diagnostic);
                    }
                    start = -1;
                }
            }
            switch (keyword) {
                case "CMD":
                    this.checkJSONQuotes(instruction, problems);
                    break;
                case "ENTRYPOINT":
                case "RUN":
                case "VOLUME":
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    this.checkJSONQuotes(instruction, problems);
                    break;
                case "ARG":
                    this.checkArguments(instruction, problems, [-1], () => null);
                    let arg = instruction;
                    let argProperty = arg.getProperty();
                    if (argProperty) {
                        this.checkProperty(document, escapeChar, keyword, instruction.getRange().start.line, argProperty, true, true, problems);
                    }
                    break;
                case "ENV":
                case "LABEL":
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    let properties = instruction.getProperties();
                    if (properties.length === 1) {
                        this.checkProperty(document, escapeChar, keyword, instruction.getRange().start.line, properties[0], true, false, problems);
                    }
                    else if (properties.length !== 0) {
                        for (let property of properties) {
                            this.checkProperty(document, escapeChar, keyword, instruction.getRange().start.line, property, false, false, problems);
                        }
                    }
                    break;
                case "FROM":
                    const fromInstructionRange = instruction.getInstructionRange();
                    const fromFlags = instruction.getFlags();
                    for (const flag of fromFlags) {
                        const flagName = flag.getName();
                        if (flagName !== "platform") {
                            const range = flag.getRange();
                            problems.push(Validator.createUnknownFromFlag(fromInstructionRange.start.line, range.start, flagName === "" ? range.end : flag.getNameRange().end, flag.getName()));
                        }
                    }
                    this.checkFlagValue(fromInstructionRange.start.line, fromFlags, ["platform"], problems);
                    this.checkArguments(instruction, problems, [1, 3], function (index, argument, range) {
                        switch (index) {
                            case 0:
                                let variables = instruction.getVariables();
                                if (variables.length > 0) {
                                    let variableRange = variables[0].getRange();
                                    if (variableRange.start.line === range.start.line
                                        && variableRange.start.character === range.start.character
                                        && variableRange.end.line === range.end.line
                                        && variableRange.end.character === range.end.character) {
                                        if (!variables[0].isDefined()) {
                                            if (PREDEFINED_VARIABLES.indexOf(variables[0].getName()) !== -1) {
                                                return null;
                                            }
                                            // the '-' sign suggests a default value so even if the variable is not defined it's okay
                                            if (variables[0].getModifier() === '-') {
                                                const parameter = variables[0].getSubstitutionParameter();
                                                if (parameter !== "" && parameter !== null) {
                                                    return null;
                                                }
                                            }
                                            return Validator.createBaseNameEmpty(fromInstructionRange.start.line, variableRange, variables[0].toString());
                                        }
                                    }
                                    return null;
                                }
                                let from = instruction;
                                let digestRange = from.getImageDigestRange();
                                const tagRange = from.getImageTagRange();
                                if (digestRange === null) {
                                    if (tagRange === null) {
                                        return null;
                                    }
                                    let tag = document.getText(tagRange);
                                    if (tag === "") {
                                        // no tag specified, just highlight the whole argument
                                        return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, range);
                                    }
                                    let tagRegexp = new RegExp(/^[\w][\w.-]{0,127}$/);
                                    if (tagRegexp.test(tag)) {
                                        return null;
                                    }
                                    return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, from.getImageTagRange());
                                }
                                if (tagRange !== null) {
                                    // specified an optional tag with the digest
                                    if (tagRange.start.line === tagRange.end.line && tagRange.start.character === tagRange.end.character) {
                                        // tag is empty, flag the whole argument as an error
                                        return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, range);
                                    }
                                }
                                let digest = document.getText(digestRange);
                                let algorithmIndex = digest.indexOf(':');
                                if (algorithmIndex === -1) {
                                    if (digest === "") {
                                        // no digest specified, just highlight the whole argument
                                        return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, range);
                                    }
                                    return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, from.getImageDigestRange());
                                }
                                let algorithmRegexp = new RegExp(/[A-Fa-f0-9_+.-]+/);
                                let algorithm = digest.substring(0, algorithmIndex);
                                if (!algorithmRegexp.test(algorithm)) {
                                    return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, from.getImageDigestRange());
                                }
                                let hex = digest.substring(algorithmIndex + 1);
                                let hexRegexp = new RegExp(/[A-Fa-f0-9]+/);
                                if (hexRegexp.test(hex)) {
                                    return null;
                                }
                                return Validator.createInvalidReferenceFormat(fromInstructionRange.start.line, from.getImageDigestRange());
                            case 1:
                                return argument.toUpperCase() === "AS" ? null : Validator.createInvalidAs;
                            case 2:
                                argument = argument.toLowerCase();
                                let regexp = new RegExp(/^[a-z]([a-z0-9_\-.]*)*$/);
                                if (regexp.test(argument)) {
                                    return null;
                                }
                                return Validator.createInvalidBuildStageName(fromInstructionRange.start.line, range, argument);
                                ;
                            default:
                                return null;
                        }
                    }, Validator.createRequiresOneOrThreeArguments);
                    break;
                case "HEALTHCHECK":
                    let args = instruction.getArguments();
                    const healthcheckInstructionRange = instruction.getInstructionRange();
                    const healthcheckFlags = instruction.getFlags();
                    if (args.length === 0) {
                        // all instructions are expected to have at least one argument
                        problems.push(Validator.createHEALTHCHECKRequiresAtLeastOneArgument(healthcheckInstructionRange.start.line, healthcheckInstructionRange));
                    }
                    else {
                        const value = args[0].getValue();
                        const uppercase = value.toUpperCase();
                        if (uppercase === "NONE") {
                            // check that NONE doesn't have any arguments after it
                            if (args.length > 1) {
                                // get the next argument
                                const start = args[1].getRange().start;
                                // get the last argument
                                const end = args[args.length - 1].getRange().end;
                                // highlight everything after the NONE and warn the user
                                problems.push(Validator.createHealthcheckNoneUnnecessaryArgument(healthcheckInstructionRange.start.line, start, end));
                            }
                            // don't need to validate flags of a NONE
                            break;
                        }
                        else if (uppercase === "CMD") {
                            if (args.length === 1) {
                                // this HEALTHCHECK has a CMD with no arguments
                                const range = args[0].getRange();
                                problems.push(Validator.createHealthcheckCmdArgumentMissing(healthcheckInstructionRange.start.line, range.start, range.end));
                            }
                        }
                        else {
                            // unknown HEALTHCHECK type
                            problems.push(Validator.createHealthcheckTypeUnknown(healthcheckInstructionRange.start.line, args[0].getRange(), uppercase));
                        }
                    }
                    const validFlags = ["interval", "retries", "start-period", "timeout", "start-interval"];
                    for (const flag of healthcheckFlags) {
                        const flagName = flag.getName();
                        if (validFlags.indexOf(flagName) === -1) {
                            const range = flag.getRange();
                            problems.push(Validator.createUnknownHealthcheckFlag(healthcheckInstructionRange.start.line, range.start, flagName === "" ? range.end : flag.getNameRange().end, flag.getName()));
                        }
                        else if (flagName === "retries") {
                            const value = flag.getValue();
                            if (value) {
                                const valueRange = flag.getValueRange();
                                const integer = parseInt(value);
                                // test for NaN or numbers with decimals
                                if (isNaN(integer) || value.indexOf('.') !== -1) {
                                    problems.push(Validator.createInvalidSyntax(healthcheckInstructionRange.start.line, valueRange.start, valueRange.end, value));
                                }
                                else if (integer < 1) {
                                    problems.push(Validator.createFlagAtLeastOne(healthcheckInstructionRange.start.line, valueRange.start, valueRange.end, "--retries", integer.toString()));
                                }
                            }
                        }
                    }
                    this.checkFlagValue(healthcheckInstructionRange.start.line, healthcheckFlags, validFlags, problems);
                    this.checkFlagDuration(healthcheckInstructionRange.start.line, healthcheckFlags, ["interval", "start-period", "timeout", "start-interval"], problems);
                    this.checkDuplicateFlags(healthcheckInstructionRange.start.line, healthcheckFlags, validFlags, problems);
                    break;
                case "ONBUILD":
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    let onbuild = instruction;
                    let trigger = onbuild.getTrigger();
                    switch (trigger) {
                        case "FROM":
                        case "MAINTAINER":
                            problems.push(Validator.createOnbuildTriggerDisallowed(onbuild.getInstructionRange().start.line, onbuild.getTriggerRange(), trigger));
                            break;
                        case "ONBUILD":
                            problems.push(Validator.createOnbuildChainingDisallowed(onbuild.getInstructionRange().start.line, onbuild.getTriggerRange()));
                            break;
                    }
                    break;
                case "SHELL":
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    this.checkJSON(document, instruction, problems);
                    break;
                case "STOPSIGNAL":
                    this.checkArguments(instruction, problems, [1], function (_index, argument) {
                        if (argument.indexOf("SIG") === 0 || argument.indexOf('$') != -1) {
                            return null;
                        }
                        for (var i = 0; i < argument.length; i++) {
                            if ('0' > argument.charAt(i) || '9' < argument.charAt(i)) {
                                return Validator.createInvalidStopSignal;
                            }
                        }
                        return null;
                    });
                    let stopsignalArgs = instruction.getExpandedArguments();
                    if (stopsignalArgs.length === 1) {
                        let value = stopsignalArgs[0].getValue();
                        let variables = instruction.getVariables();
                        if (variables.length === 0) {
                            if (value.indexOf('$') !== -1) {
                                const instructionRange = instruction.getInstructionRange();
                                let range = stopsignalArgs[0].getRange();
                                problems.push(Validator.createInvalidStopSignal(instructionRange.start.line, range.start, range.end, value));
                            }
                        }
                        else {
                            for (let variable of variables) {
                                let variableRange = variable.getRange();
                                let variableDefinition = this.document.getText().substring(this.document.offsetAt(variableRange.start), this.document.offsetAt(variableRange.end));
                                // an un-expanded variable is here
                                if (value.includes(variableDefinition) && !variable.isBuildVariable() && !variable.isDefined()) {
                                    const instructionRange = instruction.getInstructionRange();
                                    let range = stopsignalArgs[0].getRange();
                                    problems.push(Validator.createInvalidStopSignal(instructionRange.start.line, range.start, range.end, ""));
                                    break;
                                }
                            }
                        }
                    }
                    break;
                case "EXPOSE":
                    let exposeArgs = instruction.getArguments();
                    let exposeExpandedArgs = instruction.getExpandedArguments();
                    if (exposeExpandedArgs.length === 0) {
                        let range = instruction.getInstructionRange();
                        problems.push(Validator.createMissingArgument(range.start.line, range.start, range.end));
                    }
                    else {
                        const regex = /^([0-9])+(-[0-9]+)?(:([0-9])+(-[0-9]*)?)?(\/(\w*))?(\/\w*)*$/;
                        argCheck: for (let i = 0; i < exposeExpandedArgs.length; i++) {
                            let value = exposeExpandedArgs[i].getValue();
                            if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
                                value = value.substring(1, value.length - 1);
                            }
                            const match = regex.exec(value);
                            if (match) {
                                if (match[7]) {
                                    const protocol = match[7].toLowerCase();
                                    if (protocol !== "" && protocol !== "tcp" && protocol !== "udp" && protocol !== "sctp") {
                                        const range = exposeExpandedArgs[i].getRange();
                                        const rangeStart = this.document.offsetAt(range.start);
                                        const rawArg = this.document.getText().substring(rangeStart, this.document.offsetAt(range.end));
                                        const start = rangeStart + rawArg.indexOf(match[7].substring(0, 1));
                                        const end = protocol.length === 1 ? rangeStart + start + 1 : rangeStart + rawArg.length;
                                        problems.push(Validator.createInvalidProto(instruction.getInstructionRange().start.line, this.document.positionAt(start), this.document.positionAt(end), match[7]));
                                    }
                                }
                            }
                            else {
                                // see if we're referencing a variable here
                                if (value.charAt(0) === '$') {
                                    continue argCheck;
                                }
                                problems.push(Validator.createInvalidPort(instruction.getInstructionRange().start.line, exposeExpandedArgs[i].getRange(), value));
                            }
                        }
                    }
                    break;
                case "ADD":
                    const add = instruction;
                    const addFlags = add.getFlags();
                    const addInstructionRange = instruction.getInstructionRange();
                    for (let flag of addFlags) {
                        const name = flag.getName();
                        const flagRange = flag.getRange();
                        if (name === "") {
                            problems.push(Validator.createUnknownAddFlag(addInstructionRange.start.line, flagRange.start, flagRange.end, name));
                        }
                        else if (name === "link" || name === "keep-git-dir") {
                            const problem = this.checkFlagBoolean(addInstructionRange.start.line, flag);
                            if (problem !== null) {
                                problems.push(problem);
                            }
                        }
                        else if (name !== "chmod" && name !== "chown" && name !== "checksum" && name !== "exclude") {
                            let range = flag.getNameRange();
                            problems.push(Validator.createUnknownAddFlag(addInstructionRange.start.line, flagRange.start, range.end, name));
                        }
                    }
                    const addDestinationDiagnostic = this.checkDestinationIsDirectory(add, Validator.createADDRequiresAtLeastTwoArguments, Validator.createADDDestinationNotDirectory);
                    if (addDestinationDiagnostic !== null) {
                        problems.push(addDestinationDiagnostic);
                    }
                    this.checkFlagValue(addInstructionRange.start.line, addFlags, ["chmod", "chown", "checksum", "exclude"], problems);
                    this.checkDuplicateFlags(addInstructionRange.start.line, addFlags, ["chmod", "chown", "checksum", "keep-git-dir", "link"], problems);
                    this.checkJSONQuotes(instruction, problems);
                    break;
                case "COPY":
                    const copyInstructionRange = instruction.getInstructionRange();
                    let copy = instruction;
                    let flags = copy.getFlags();
                    if (flags.length > 0) {
                        for (let flag of flags) {
                            const name = flag.getName();
                            const flagRange = flag.getRange();
                            if (name === "") {
                                problems.push(Validator.createUnknownCopyFlag(copyInstructionRange.start.line, flagRange.start, flagRange.end, name));
                            }
                            else if (name === "link" || name === "parents") {
                                const problem = this.checkFlagBoolean(copyInstructionRange.start.line, flag);
                                if (problem !== null) {
                                    problems.push(problem);
                                }
                            }
                            else if (name !== "chmod" && name !== "chown" && name !== "from" && name !== "exclude" && name !== "parents") {
                                let range = flag.getNameRange();
                                problems.push(Validator.createUnknownCopyFlag(copyInstructionRange.start.line, flagRange.start, range.end, name));
                            }
                        }
                        let flag = copy.getFromFlag();
                        if (flag) {
                            let value = flag.getValue();
                            if (value !== null) {
                                let regexp = new RegExp(/^[a-zA-Z0-9].*$/);
                                if (!regexp.test(value)) {
                                    let range = value === "" ? flag.getRange() : flag.getValueRange();
                                    problems.push(Validator.createFlagInvalidFrom(copyInstructionRange.start.line, range.start, range.end, value));
                                }
                            }
                        }
                    }
                    const copyDestinationDiagnostic = this.checkDestinationIsDirectory(copy, Validator.createCOPYRequiresAtLeastTwoArguments, Validator.createCOPYDestinationNotDirectory);
                    if (copyDestinationDiagnostic !== null) {
                        problems.push(copyDestinationDiagnostic);
                    }
                    this.checkFlagValue(copyInstructionRange.start.line, flags, ["chmod", "chown", "from", "exclude"], problems);
                    this.checkDuplicateFlags(copyInstructionRange.start.line, flags, ["chmod", "chown", "from", "link", "parents"], problems);
                    this.checkJSONQuotes(instruction, problems);
                    break;
                case "WORKDIR":
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    let content = instruction.getArgumentsContent();
                    if (content) {
                        // strip out any surrounding quotes
                        const first = content.substring(0, 1);
                        const last = content.substring(content.length - 1);
                        if ((first === '\'' && last === '\'') || (first === '"' && last === '"')) {
                            content = content.substring(1, content.length - 1);
                        }
                        let regexp = new RegExp(/^(\$|([a-zA-Z](\$|:(\$|\\|\/)))).*$/);
                        if (!content.startsWith('/') && !regexp.test(content)) {
                            let problem = this.createWORKDIRNotAbsolute(instruction.getInstructionRange().start.line, instruction.getArgumentsRange());
                            if (problem) {
                                problems.push(problem);
                            }
                        }
                    }
                    break;
                default:
                    this.checkArguments(instruction, problems, [-1], function () {
                        return null;
                    });
                    break;
            }
        }
    }
    hasHeredocs(args) {
        for (const arg of args) {
            if (arg.getValue().startsWith("<<")) {
                return true;
            }
        }
        return false;
    }
    getDestinationArgument(args) {
        if (this.hasHeredocs(args)) {
            const initialLine = args[0].getRange().start.line;
            let candidate = null;
            for (let i = 1; i < args.length; i++) {
                if (args[i].getRange().start.line === initialLine) {
                    candidate = args[i];
                }
                else {
                    // stop searching once we're on another line
                    break;
                }
            }
            return candidate;
        }
        return args[args.length - 1];
    }
    checkDestinationIsDirectory(instruction, requiresTwoArgumentsFunction, notDirectoryFunction) {
        if (instruction.getClosingBracket()) {
            return this.checkJsonDestinationIsDirectory(instruction, requiresTwoArgumentsFunction, notDirectoryFunction);
        }
        const args = instruction.getArguments();
        if (args.length === 1) {
            return requiresTwoArgumentsFunction(instruction.getInstructionRange().start.line, args[0].getRange());
        }
        else if (args.length === 0) {
            const instructionRange = instruction.getInstructionRange();
            return requiresTwoArgumentsFunction(instructionRange.start.line, instructionRange);
        }
        else if (args.length > 2) {
            const lastArg = this.getDestinationArgument(args);
            if (lastArg === null) {
                const instructionRange = instruction.getInstructionRange();
                return requiresTwoArgumentsFunction(instructionRange.start.line, instructionRange);
            }
            else if (this.hasHeredocs(args)) {
                return null;
            }
            const variables = instruction.getVariables();
            if (variables.length !== 0) {
                const lastJsonStringOffset = this.document.offsetAt(lastArg.getRange().end);
                const lastVarOffset = this.document.offsetAt(variables[variables.length - 1].getRange().end);
                if (lastJsonStringOffset === lastVarOffset || lastJsonStringOffset - 1 === lastVarOffset) {
                    return null;
                }
            }
            const destination = lastArg.getValue();
            const lastChar = destination.charAt(destination.length - 1);
            if (lastChar !== '\\' && lastChar !== '/') {
                return notDirectoryFunction(instruction.getInstructionRange().start.line, lastArg.getRange());
            }
        }
        return null;
    }
    createDuplicatesDiagnostics(problems, severity, instruction, instructions) {
        if (instructions.length > 1) {
            // decrement length by 1 because we want to ignore the last one
            for (let i = 0; i < instructions.length - 1; i++) {
                const instructionRange = instructions[i].getInstructionRange();
                const diagnostic = this.createMultipleInstructions(instructionRange.start.line, instructionRange, severity, instruction);
                if (diagnostic) {
                    problems.push(diagnostic);
                }
            }
        }
    }
    createDuplicateBuildStageNameDiagnostics(problems, froms) {
        const names = {};
        for (let from of froms) {
            let name = from.getBuildStage();
            if (name !== null) {
                name = name.toLowerCase();
                if (names[name] === undefined) {
                    names[name] = [from];
                }
                else {
                    names[name].push(from);
                }
            }
        }
        for (const name in names) {
            // duplicates found
            if (names[name].length > 1) {
                for (const from of names[name]) {
                    problems.push(Validator.createDuplicateBuildStageName(from.getInstructionRange().start.line, from.getBuildStageRange(), name));
                }
            }
        }
    }
    checkJsonDestinationIsDirectory(instruction, requiresTwoArgumentsFunction, notDirectoryFunction) {
        const jsonStrings = instruction.getJSONStrings();
        if (jsonStrings.length === 0) {
            return requiresTwoArgumentsFunction(instruction.getInstructionRange().start.line, instruction.getArgumentsRange());
        }
        else if (jsonStrings.length === 1) {
            return requiresTwoArgumentsFunction(instruction.getInstructionRange().start.line, jsonStrings[0].getJSONRange());
        }
        else if (jsonStrings.length > 2) {
            const lastJsonString = jsonStrings[jsonStrings.length - 1];
            const variables = instruction.getVariables();
            if (variables.length !== 0) {
                const lastVar = variables[variables.length - 1];
                const lastJsonStringOffset = this.document.offsetAt(lastJsonString.getRange().end);
                const lastVarOffset = this.document.offsetAt(lastVar.getRange().end);
                if (lastJsonStringOffset === lastVarOffset || lastJsonStringOffset - 1 === lastVarOffset) {
                    return null;
                }
            }
            const destination = lastJsonString.getValue();
            const lastChar = destination.charAt(destination.length - 2);
            if (lastChar !== '\\' && lastChar !== '/') {
                return notDirectoryFunction(instruction.getInstructionRange().start.line, jsonStrings[jsonStrings.length - 1].getJSONRange());
            }
        }
        return null;
    }
    checkFlagValue(instructionLine, flags, validFlagNames, problems) {
        for (let flag of flags) {
            let flagName = flag.getName();
            // only validate flags with the right name
            if (flag.getValue() === null && validFlagNames.indexOf(flagName) !== -1) {
                problems.push(Validator.createFlagMissingValue(instructionLine, flag.getNameRange(), flagName));
            }
        }
    }
    /**
     * Checks that the given boolean flag is valid. A boolean flag should
     * either have no value defined (--flag) or the value should
     * case-insensitively be either "true" or "false" (--flag==tRUe is
     * valid).
     */
    checkFlagBoolean(instructionLine, flag) {
        const flagValue = flag.getValue();
        if (flagValue === "") {
            return Validator.createFlagMissingValue(instructionLine, flag.getNameRange(), flag.getName());
        }
        else if (flagValue !== null) {
            const convertedFlagValue = flagValue.toLowerCase();
            if (convertedFlagValue !== "true" && convertedFlagValue !== "false") {
                return Validator.createFlagExpectedBooleanValue(instructionLine, flag.getValueRange(), flag.getName(), flagValue);
            }
        }
        return null;
    }
    checkFlagDuration(instructionLine, flags, validFlagNames, problems) {
        flagCheck: for (let flag of flags) {
            let flagName = flag.getName();
            // only validate flags with the right name
            if (validFlagNames.indexOf(flagName) !== -1) {
                let value = flag.getValue();
                if (value !== null && value.length !== 0) {
                    switch (value.charAt(0)) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                        case '.':
                        case '-':
                            break;
                        default:
                            let range = flag.getValueRange();
                            problems.push(Validator.createFlagInvalidDuration(instructionLine, range.start, range.end, value));
                            continue flagCheck;
                    }
                    let durationSpecified = false;
                    let start = 0;
                    let duration = 0;
                    let digitFound = false;
                    let hyphenFound = false;
                    let periodsDetected = 0;
                    durationParse: for (let i = 0; i < value.length; i++) {
                        durationSpecified = false;
                        switch (value.charAt(i)) {
                            case '-':
                                if (digitFound) {
                                    let range = flag.getValueRange();
                                    problems.push(Validator.createFlagUnknownUnit(instructionLine, range, value.charAt(i), value));
                                    continue flagCheck;
                                }
                                else if (hyphenFound) {
                                    let range = flag.getValueRange();
                                    problems.push(Validator.createFlagInvalidDuration(instructionLine, range.start, range.end, value));
                                    continue flagCheck;
                                }
                                hyphenFound = true;
                                continue;
                            case '.':
                                periodsDetected++;
                                continue;
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                digitFound = true;
                                continue;
                            default:
                                if (periodsDetected > 1) {
                                    let range = flag.getValueRange();
                                    problems.push(Validator.createFlagMissingDuration(instructionLine, range.start, range.end, value));
                                    continue flagCheck;
                                }
                                periodsDetected = 0;
                                let time = parseFloat(value.substring(start, i));
                                for (let j = i + 1; j < value.length; j++) {
                                    if (Validator.isNumberRelated(value.charAt(j))) {
                                        let unit = value.substring(i, j);
                                        if (time < 0 || (value.charAt(start) === '-' && time === 0)) {
                                            let nameRange = flag.getNameRange();
                                            problems.push(Validator.createFlagLessThan1ms(instructionLine, nameRange.start, nameRange.end, flagName));
                                            continue flagCheck;
                                        }
                                        switch (unit) {
                                            case 'h':
                                                // hours
                                                duration += time * 1000 * 60 * 60;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            case 'm':
                                                // minutes
                                                duration += time * 1000 * 60;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            case 's':
                                                // seconds
                                                duration += time * 1000;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            case "ms":
                                                // milliseconds
                                                duration += time;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            case "us":
                                            case "µs":
                                                // microseconds
                                                duration += time / 1000;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            case "ns":
                                                // nanoseconds
                                                duration += time / 1000000;
                                                i = j - 1;
                                                start = i;
                                                durationSpecified = true;
                                                continue durationParse;
                                            default:
                                                let range = flag.getValueRange();
                                                problems.push(Validator.createFlagUnknownUnit(instructionLine, range, unit, value));
                                                continue flagCheck;
                                        }
                                    }
                                }
                                if (time < 0 || (value.charAt(start) === '-' && time === 0)) {
                                    let nameRange = flag.getNameRange();
                                    problems.push(Validator.createFlagLessThan1ms(instructionLine, nameRange.start, nameRange.end, flagName));
                                    continue flagCheck;
                                }
                                let unit = value.substring(i, value.length);
                                switch (unit) {
                                    case 'h':
                                        // hours
                                        duration += time * 1000 * 60 * 60;
                                        durationSpecified = true;
                                        break durationParse;
                                    case 'm':
                                        // minutes
                                        duration += time * 1000 * 60;
                                        durationSpecified = true;
                                        break durationParse;
                                    case 's':
                                        // seconds
                                        duration += time * 1000;
                                        durationSpecified = true;
                                        break durationParse;
                                    case "ms":
                                        // minutes
                                        duration += time;
                                        durationSpecified = true;
                                        break durationParse;
                                    case "us":
                                    case "µs":
                                        // microseconds
                                        duration += time / 1000;
                                        durationSpecified = true;
                                        break durationParse;
                                    case "ns":
                                        // nanoseconds
                                        duration += time / 1000000;
                                        durationSpecified = true;
                                        break durationParse;
                                    default:
                                        let range = flag.getValueRange();
                                        problems.push(Validator.createFlagUnknownUnit(instructionLine, range, unit, value));
                                        break;
                                }
                                continue flagCheck;
                        }
                    }
                    if (!durationSpecified) {
                        let range = flag.getValueRange();
                        problems.push(Validator.createFlagMissingDuration(instructionLine, range.start, range.end, value));
                    }
                    else if (duration < 1) {
                        let range = flag.getNameRange();
                        problems.push(Validator.createFlagLessThan1ms(instructionLine, range.start, range.end, flagName));
                    }
                }
            }
        }
    }
    static isNumberRelated(character) {
        switch (character) {
            case '.':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return true;
        }
        return false;
    }
    checkDuplicateFlags(instructionLine, flags, validFlags, problems) {
        let flagNames = flags.map(function (flag) {
            return flag.getName();
        });
        for (let validFlag of validFlags) {
            let index = flagNames.indexOf(validFlag);
            let lastIndex = flagNames.lastIndexOf(validFlag);
            if (index !== lastIndex) {
                let range = flags[index].getNameRange();
                problems.push(Validator.createFlagDuplicate(instructionLine, range.start, range.end, flagNames[index]));
                range = flags[lastIndex].getNameRange();
                problems.push(Validator.createFlagDuplicate(instructionLine, range.start, range.end, flagNames[index]));
            }
        }
    }
    checkJSON(document, instruction, problems) {
        let argsContent = instruction.getArgumentsContent();
        if (argsContent === null) {
            return;
        }
        let argsRange = instruction.getArgumentsRange();
        let args = instruction.getArguments();
        if ((args.length === 1 && args[0].getValue() === "[]") ||
            (args.length === 2 && args[0].getValue() === '[' && args[1].getValue() === ']')) {
            problems.push(Validator.createShellRequiresOne(instruction.getInstructionRange().start.line, argsRange));
            return;
        }
        const closing = instruction.getClosingBracket();
        if (closing) {
            let content = document.getText();
            content = content.substring(document.offsetAt(instruction.getOpeningBracket().getRange().end), document.offsetAt(closing.getRange().start));
            content = content.trim();
            if (content.charAt(content.length - 1) !== '"') {
                problems.push(Validator.createShellJsonForm(instruction.getInstructionRange().start.line, argsRange));
            }
        }
        else {
            problems.push(Validator.createShellJsonForm(instruction.getInstructionRange().start.line, argsRange));
        }
    }
    checkJSONQuotes(instruction, problems) {
        let argsContent = instruction.getArgumentsContent();
        if (argsContent === null) {
            return;
        }
        let argsRange = instruction.getArgumentsRange();
        let args = instruction.getArguments();
        if ((args.length === 1 && args[0].getValue() === "[]") ||
            (args.length === 2 && args[0].getValue() === '[' && args[1].getValue() === ']')) {
            return;
        }
        let jsonLike = false;
        let last = null;
        let quoted = false;
        argsCheck: for (let i = 0; i < argsContent.length; i++) {
            switch (argsContent.charAt(i)) {
                case '[':
                    if (last === null) {
                        last = '[';
                        jsonLike = true;
                    }
                    break;
                case '\'':
                    if (last === '[' || last === ',') {
                        quoted = true;
                        last = '\'';
                        continue;
                    }
                    else if (last === '\'') {
                        if (quoted) {
                            // quoted string done
                            quoted = false;
                        }
                        else {
                            // should be a , or a ]
                            break argsCheck;
                        }
                    }
                    else {
                        break argsCheck;
                    }
                    break;
                case ',':
                    if (!jsonLike) {
                        break argsCheck;
                    }
                    else if (!quoted) {
                        if (last === '\'') {
                            last = ',';
                        }
                        else {
                            break argsCheck;
                        }
                    }
                    break;
                case ']':
                    if (!quoted) {
                        if (last === '\'' || last === ',') {
                            last = null;
                            const problem = Validator.createJSONInSingleQuotes(instruction.getInstructionRange().start.line, argsRange, this.settings.instructionJSONInSingleQuotes);
                            if (problem) {
                                problems.push(problem);
                            }
                        }
                        break argsCheck;
                    }
                    break;
                case ' ':
                case '\t':
                    break;
                default:
                    if (!quoted) {
                        break argsCheck;
                    }
                    break;
            }
        }
    }
    static formatMessage(text, ...variables) {
        for (let i = 0; i < variables.length; i++) {
            text = text.replace("${" + i + "}", variables[i]);
        }
        return text;
    }
    static getDiagnosticMessage_DirectiveCasing() {
        return Validator.dockerProblems["directiveCasing"];
    }
    static getDiagnosticMessage_DirectiveEscapeDuplicated() {
        return Validator.dockerProblems["directiveEscapeDuplicated"];
    }
    static getDiagnosticMessage_DirectiveEscapeInvalid(value) {
        return Validator.formatMessage(Validator.dockerProblems["directiveEscapeInvalid"], value);
    }
    static getDiagnosticMessage_NoSourceImage() {
        return Validator.dockerProblems["noSourceImage"];
    }
    static getDiagnosticMessage_EmptyContinuationLine() {
        return Validator.dockerProblems["emptyContinuationLine"];
    }
    static getDiagnosticMessage_DuplicateBuildStageName(name) {
        return Validator.formatMessage(Validator.dockerProblems["duplicateBuildStageName"], name);
    }
    static getDiagnosticMessage_InvalidBuildStageName(name) {
        return Validator.formatMessage(Validator.dockerProblems["invalidBuildStageName"], name);
    }
    static getDiagnosticMessage_FlagAtLeastOne(flagName, flagValue) {
        return Validator.formatMessage(Validator.dockerProblems["flagAtLeastOne"], flagName, flagValue);
    }
    static getDiagnosticMessage_FlagDuplicate(flag) {
        return Validator.formatMessage(Validator.dockerProblems["flagDuplicate"], flag);
    }
    static getDiagnosticMessage_FlagInvalidDuration(flag) {
        return Validator.formatMessage(Validator.dockerProblems["flagInvalidDuration"], flag);
    }
    static getDiagnosticMessage_FlagLessThan1ms(flag) {
        return Validator.formatMessage(Validator.dockerProblems["flagLessThan1ms"], flag);
    }
    static getDiagnosticMessage_FlagMissingDuration(duration) {
        return Validator.formatMessage(Validator.dockerProblems["flagMissingDuration"], duration);
    }
    static getDiagnosticMessage_FlagInvalidFromValue(value) {
        return Validator.formatMessage(Validator.dockerProblems["flagInvalidFrom"], value);
    }
    static getDiagnosticMessage_FlagExpectedBooleanValue(flag, value) {
        return Validator.formatMessage(Validator.dockerProblems["flagExpectedBooleanValue"], flag, value);
    }
    static getDiagnosticMessage_FlagMissingValue(flag) {
        return Validator.formatMessage(Validator.dockerProblems["flagMissingValue"], flag);
    }
    static getDiagnosticMessage_FlagUnknown(flag) {
        return Validator.formatMessage(Validator.dockerProblems["flagUnknown"], flag);
    }
    static getDiagnosticMessage_FlagUnknownUnit(unit, duration) {
        return Validator.formatMessage(Validator.dockerProblems["flagUnknownUnit"], unit, duration);
    }
    static getDiagnosticMessage_BaseNameEmpty(name) {
        return Validator.formatMessage(Validator.dockerProblems["baseNameEmpty"], name);
    }
    static getDiagnosticMessage_InvalidAs() {
        return Validator.dockerProblems["invalidAs"];
    }
    static getDiagnosticMessage_InvalidPort(port) {
        return Validator.formatMessage(Validator.dockerProblems["invalidPort"], port);
    }
    static getDiagnosticMessage_InvalidProto(protocol) {
        return Validator.formatMessage(Validator.dockerProblems["invalidProtocol"], protocol);
    }
    static getDiagnosticMessage_InvalidReferenceFormat() {
        return Validator.dockerProblems["invalidReferenceFormat"];
    }
    static getDiagnosticMessage_InvalidSignal(signal) {
        return Validator.formatMessage(Validator.dockerProblems["invalidStopSignal"], signal);
    }
    static getDiagnosticMessage_InvalidSyntax(syntax) {
        return Validator.formatMessage(Validator.dockerProblems["invalidSyntax"], syntax);
    }
    static getDiagnosticMessage_InstructionExtraArgument() {
        return Validator.dockerProblems["instructionExtraArgument"];
    }
    static getDiagnosticMessage_InstructionMissingArgument() {
        return Validator.dockerProblems["instructionMissingArgument"];
    }
    static getDiagnosticMessage_ADDDestinationNotDirectory() {
        return Validator.formatMessage(Validator.dockerProblems["invalidDestination"], "ADD");
    }
    static getDiagnosticMessage_ADDRequiresAtLeastTwoArguments() {
        return Validator.formatMessage(Validator.dockerProblems["instructionRequiresAtLeastTwoArguments"], "ADD");
    }
    static getDiagnosticMessage_COPYDestinationNotDirectory() {
        return Validator.formatMessage(Validator.dockerProblems["invalidDestination"], "COPY");
    }
    static getDiagnosticMessage_COPYRequiresAtLeastTwoArguments() {
        return Validator.formatMessage(Validator.dockerProblems["instructionRequiresAtLeastTwoArguments"], "COPY");
    }
    static getDiagnosticMessage_HEALTHCHECKRequiresAtLeastOneArgument() {
        return Validator.formatMessage(Validator.dockerProblems["instructionRequiresAtLeastOneArgument"], "HEALTHCHECK");
    }
    static getDiagnosticMessage_ENVRequiresTwoArguments() {
        return Validator.formatMessage(Validator.dockerProblems["instructionRequiresTwoArguments"], "ENV");
    }
    static getDiagnosticMessage_InstructionRequiresOneOrThreeArguments() {
        return Validator.dockerProblems["fromRequiresOneOrThreeArguments"];
    }
    static getDiagnosticMessage_HealthcheckNoneUnnecessaryArgument() {
        return Validator.formatMessage(Validator.dockerProblems["instructionUnnecessaryArgument"], "HEALTHCHECK NONE");
    }
    static getDiagnosticMessage_InstructionMultiple(instruction) {
        return Validator.formatMessage(Validator.dockerProblems["instructionMultiple"], instruction);
    }
    static getDiagnosticMessage_InstructionUnknown(instruction) {
        return Validator.formatMessage(Validator.dockerProblems["instructionUnknown"], instruction);
    }
    static getDiagnosticMessage_SyntaxMissingEquals(argument) {
        return Validator.formatMessage(Validator.dockerProblems["syntaxMissingEquals"], argument);
    }
    static getDiagnosticMessage_SyntaxMissingNames(instruction) {
        return Validator.formatMessage(Validator.dockerProblems["syntaxMissingNames"], instruction);
    }
    static getDiagnosticMessage_SyntaxMissingSingleQuote(key) {
        return Validator.formatMessage(Validator.dockerProblems["syntaxMissingSingleQuote"], key);
    }
    static getDiagnosticMessage_SyntaxMissingDoubleQuote(key) {
        return Validator.formatMessage(Validator.dockerProblems["syntaxMissingDoubleQuote"], key);
    }
    static getDiagnosticMessage_InstructionCasing() {
        return Validator.dockerProblems["instructionCasing"];
    }
    static getDiagnosticMessage_InstructionJSONInSingleQuotes() {
        return Validator.dockerProblems["instructionJSONInSingleQuotes"];
    }
    static getDiagnosticMessage_OnbuildChainingDisallowed() {
        return Validator.dockerProblems["onbuildChainingDisallowed"];
    }
    static getDiagnosticMessage_OnbuildTriggerDisallowed(trigger) {
        return Validator.formatMessage(Validator.dockerProblems["onbuildTriggerDisallowed"], trigger);
    }
    static getDiagnosticMessage_VariableModifierUnsupported(variable, modifier) {
        return Validator.formatMessage(Validator.dockerProblems["variableModifierUnsupported"], variable, modifier);
    }
    static getDiagnosticMessage_ShellJsonForm() {
        return Validator.dockerProblems["shellJsonForm"];
    }
    static getDiagnosticMessage_ShellRequiresOne() {
        return Validator.dockerProblems["shellRequiresOne"];
    }
    static getDiagnosticMessage_DeprecatedMaintainer() {
        return Validator.dockerProblems["deprecatedMaintainer"];
    }
    static getDiagnosticMessage_HealthcheckCmdArgumentMissing() {
        return Validator.dockerProblems["healthcheckCmdArgumentMissing"];
    }
    static getDiagnosticMessage_HealthcheckTypeUnknown(type) {
        return Validator.formatMessage(Validator.dockerProblems["healthcheckTypeUnknown"], type);
    }
    static getDiagnosticMessage_WORKDIRPathNotAbsolute() {
        return Validator.formatMessage(Validator.dockerProblems["workdirPathNotAbsolute"]);
    }
    static createDuplicatedEscapeDirective(start, end) {
        return Validator.createError(null, start, end, Validator.getDiagnosticMessage_DirectiveEscapeDuplicated(), main_1.ValidationCode.DUPLICATED_ESCAPE_DIRECTIVE, [vscode_languageserver_types_1.DiagnosticTag.Unnecessary]);
    }
    static createInvalidEscapeDirective(start, end, value) {
        return Validator.createError(null, start, end, Validator.getDiagnosticMessage_DirectiveEscapeInvalid(value), main_1.ValidationCode.INVALID_ESCAPE_DIRECTIVE);
    }
    static createDuplicateBuildStageName(instructionLine, range, name) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_DuplicateBuildStageName(name), main_1.ValidationCode.DUPLICATE_BUILD_STAGE_NAME);
    }
    static createInvalidBuildStageName(instructionLine, range, name) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InvalidBuildStageName(name), main_1.ValidationCode.INVALID_BUILD_STAGE_NAME);
    }
    static createFlagAtLeastOne(instructionLine, start, end, flagName, flagValue) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagAtLeastOne(flagName, flagValue), main_1.ValidationCode.FLAG_AT_LEAST_ONE);
    }
    static createFlagDuplicate(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagDuplicate(flag), main_1.ValidationCode.FLAG_DUPLICATE);
    }
    static createFlagInvalidDuration(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagInvalidDuration(flag), main_1.ValidationCode.FLAG_INVALID_DURATION);
    }
    static createFlagLessThan1ms(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagLessThan1ms(flag), main_1.ValidationCode.FLAG_LESS_THAN_1MS);
    }
    static createFlagMissingDuration(instructionLine, start, end, duration) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagMissingDuration(duration), main_1.ValidationCode.FLAG_MISSING_DURATION);
    }
    static createFlagInvalidFrom(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagInvalidFromValue(flag), main_1.ValidationCode.FLAG_INVALID_FROM_VALUE);
    }
    static createFlagExpectedBooleanValue(instructionLine, range, flag, value) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_FlagExpectedBooleanValue(flag, value), main_1.ValidationCode.FLAG_EXPECTED_BOOLEAN_VALUE);
    }
    static createFlagMissingValue(instructionLine, range, flag) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_FlagMissingValue(flag), main_1.ValidationCode.FLAG_MISSING_VALUE);
    }
    static createUnknownAddFlag(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagUnknown(flag), main_1.ValidationCode.UNKNOWN_ADD_FLAG);
    }
    static createUnknownCopyFlag(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagUnknown(flag), main_1.ValidationCode.UNKNOWN_COPY_FLAG);
    }
    static createUnknownFromFlag(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagUnknown(flag), main_1.ValidationCode.UNKNOWN_FROM_FLAG);
    }
    static createUnknownHealthcheckFlag(instructionLine, start, end, flag) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_FlagUnknown(flag), main_1.ValidationCode.UNKNOWN_HEALTHCHECK_FLAG);
    }
    static createFlagUnknownUnit(instructionLine, range, unit, duration) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_FlagUnknownUnit(unit, duration), main_1.ValidationCode.FLAG_UNKNOWN_UNIT);
    }
    static createBaseNameEmpty(instructionLine, range, name) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_BaseNameEmpty(name), main_1.ValidationCode.BASE_NAME_EMPTY);
    }
    static createInvalidAs(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InvalidAs(), main_1.ValidationCode.INVALID_AS);
    }
    static createInvalidPort(instructionLine, range, port) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InvalidPort(port), main_1.ValidationCode.INVALID_PORT);
    }
    static createInvalidProto(instructionLine, start, end, protocol) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InvalidProto(protocol), main_1.ValidationCode.INVALID_PROTO);
    }
    static createInvalidReferenceFormat(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InvalidReferenceFormat(), main_1.ValidationCode.INVALID_REFERENCE_FORMAT);
    }
    static createInvalidStopSignal(instructionLine, start, end, signal) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InvalidSignal(signal), main_1.ValidationCode.INVALID_SIGNAL);
    }
    static createInvalidSyntax(instructionLine, start, end, syntax) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InvalidSyntax(syntax), main_1.ValidationCode.INVALID_SYNTAX);
    }
    static createMissingArgument(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionMissingArgument(), main_1.ValidationCode.ARGUMENT_MISSING);
    }
    static createExtraArgument(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionExtraArgument(), main_1.ValidationCode.ARGUMENT_EXTRA);
    }
    static createHealthcheckNoneUnnecessaryArgument(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_HealthcheckNoneUnnecessaryArgument(), main_1.ValidationCode.ARGUMENT_UNNECESSARY);
    }
    static createADDDestinationNotDirectory(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_ADDDestinationNotDirectory(), main_1.ValidationCode.INVALID_DESTINATION);
    }
    static createADDRequiresAtLeastTwoArguments(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_ADDRequiresAtLeastTwoArguments(), main_1.ValidationCode.ARGUMENT_REQUIRES_AT_LEAST_TWO);
    }
    static createCOPYDestinationNotDirectory(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_COPYDestinationNotDirectory(), main_1.ValidationCode.INVALID_DESTINATION);
    }
    static createCOPYRequiresAtLeastTwoArguments(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_COPYRequiresAtLeastTwoArguments(), main_1.ValidationCode.ARGUMENT_REQUIRES_AT_LEAST_TWO);
    }
    static createENVRequiresTwoArguments(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_ENVRequiresTwoArguments(), main_1.ValidationCode.ARGUMENT_REQUIRES_TWO);
    }
    static createHEALTHCHECKRequiresAtLeastOneArgument(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_HEALTHCHECKRequiresAtLeastOneArgument(), main_1.ValidationCode.ARGUMENT_REQUIRES_AT_LEAST_ONE);
    }
    static createHealthcheckCmdArgumentMissing(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_HealthcheckCmdArgumentMissing(), main_1.ValidationCode.HEALTHCHECK_CMD_ARGUMENT_MISSING);
    }
    static createHealthcheckTypeUnknown(instructionLine, range, type) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_HealthcheckTypeUnknown(type), main_1.ValidationCode.UNKNOWN_TYPE);
    }
    static createOnbuildChainingDisallowed(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_OnbuildChainingDisallowed(), main_1.ValidationCode.ONBUILD_CHAINING_DISALLOWED);
    }
    static createOnbuildTriggerDisallowed(instructionLine, range, trigger) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_OnbuildTriggerDisallowed(trigger), main_1.ValidationCode.ONBUILD_TRIGGER_DISALLOWED);
    }
    static createShellJsonForm(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_ShellJsonForm(), main_1.ValidationCode.SHELL_JSON_FORM);
    }
    static createShellRequiresOne(instructionLine, range) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_ShellRequiresOne(), main_1.ValidationCode.SHELL_REQUIRES_ONE);
    }
    static createRequiresOneOrThreeArguments(instructionLine, start, end) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionRequiresOneOrThreeArguments(), main_1.ValidationCode.ARGUMENT_REQUIRES_ONE_OR_THREE);
    }
    static createNoSourceImage(start, end) {
        return Validator.createError(null, start, end, Validator.getDiagnosticMessage_NoSourceImage(), main_1.ValidationCode.NO_SOURCE_IMAGE);
    }
    static createSyntaxMissingEquals(instructionLine, start, end, argument) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_SyntaxMissingEquals(argument), main_1.ValidationCode.SYNTAX_MISSING_EQUALS);
    }
    static createSyntaxMissingSingleQuote(instructionLine, start, end, argument) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_SyntaxMissingSingleQuote(argument), main_1.ValidationCode.SYNTAX_MISSING_SINGLE_QUOTE);
    }
    static createSyntaxMissingDoubleQuote(instructionLine, start, end, argument) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_SyntaxMissingDoubleQuote(argument), main_1.ValidationCode.SYNTAX_MISSING_DOUBLE_QUOTE);
    }
    static createSyntaxMissingNames(instructionLine, start, end, instruction) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_SyntaxMissingNames(instruction), main_1.ValidationCode.SYNTAX_MISSING_NAMES);
    }
    static createVariableUnsupportedModifier(instructionLine, range, variable, modifier) {
        return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_VariableModifierUnsupported(variable, modifier), main_1.ValidationCode.UNSUPPORTED_MODIFIER);
    }
    static createUnknownInstruction(instructionLine, start, end, instruction) {
        return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionUnknown(instruction), main_1.ValidationCode.UNKNOWN_INSTRUCTION);
    }
    static createError(instructionLine, start, end, description, code, tags) {
        return Validator.createDiagnostic(vscode_languageserver_types_1.DiagnosticSeverity.Error, instructionLine, start, end, description, code, tags);
    }
    static createJSONInSingleQuotes(instructionLine, range, severity) {
        if (severity === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InstructionJSONInSingleQuotes(), main_1.ValidationCode.JSON_IN_SINGLE_QUOTES);
        }
        else if (severity === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InstructionJSONInSingleQuotes(), main_1.ValidationCode.JSON_IN_SINGLE_QUOTES);
        }
        return null;
    }
    static createEmptyContinuationLine(start, end, severity) {
        if (severity === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(null, start, end, Validator.getDiagnosticMessage_EmptyContinuationLine(), main_1.ValidationCode.EMPTY_CONTINUATION_LINE);
        }
        else if (severity === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(null, start, end, Validator.getDiagnosticMessage_EmptyContinuationLine(), main_1.ValidationCode.EMPTY_CONTINUATION_LINE);
        }
        return null;
    }
    createMultipleInstructions(instructionLine, range, severity, instruction) {
        if (severity === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InstructionMultiple(instruction), main_1.ValidationCode.MULTIPLE_INSTRUCTIONS, [vscode_languageserver_types_1.DiagnosticTag.Unnecessary]);
        }
        else if (severity === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_InstructionMultiple(instruction), main_1.ValidationCode.MULTIPLE_INSTRUCTIONS, [vscode_languageserver_types_1.DiagnosticTag.Unnecessary]);
        }
        return null;
    }
    createLowercaseDirective(start, end) {
        if (this.settings.directiveCasing === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(null, start, end, Validator.getDiagnosticMessage_DirectiveCasing(), main_1.ValidationCode.CASING_DIRECTIVE);
        }
        else if (this.settings.directiveCasing === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(null, start, end, Validator.getDiagnosticMessage_DirectiveCasing(), main_1.ValidationCode.CASING_DIRECTIVE);
        }
        return null;
    }
    createUppercaseInstruction(instructionLine, start, end) {
        if (this.settings.instructionCasing === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionCasing(), main_1.ValidationCode.CASING_INSTRUCTION);
        }
        else if (this.settings.instructionCasing === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(instructionLine, start, end, Validator.getDiagnosticMessage_InstructionCasing(), main_1.ValidationCode.CASING_INSTRUCTION);
        }
        return null;
    }
    createMaintainerDeprecated(instructionLine, start, end) {
        if (this.settings.deprecatedMaintainer === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(instructionLine, start, end, Validator.getDiagnosticMessage_DeprecatedMaintainer(), main_1.ValidationCode.DEPRECATED_MAINTAINER, [vscode_languageserver_types_1.DiagnosticTag.Deprecated]);
        }
        else if (this.settings.deprecatedMaintainer === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(instructionLine, start, end, Validator.getDiagnosticMessage_DeprecatedMaintainer(), main_1.ValidationCode.DEPRECATED_MAINTAINER, [vscode_languageserver_types_1.DiagnosticTag.Deprecated]);
        }
        return null;
    }
    createWORKDIRNotAbsolute(instructionLine, range) {
        if (this.settings.instructionWorkdirRelative === main_1.ValidationSeverity.ERROR) {
            return Validator.createError(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_WORKDIRPathNotAbsolute(), main_1.ValidationCode.WORKDIR_IS_NOT_ABSOLUTE);
        }
        else if (this.settings.instructionWorkdirRelative === main_1.ValidationSeverity.WARNING) {
            return Validator.createWarning(instructionLine, range.start, range.end, Validator.getDiagnosticMessage_WORKDIRPathNotAbsolute(), main_1.ValidationCode.WORKDIR_IS_NOT_ABSOLUTE);
        }
        return null;
    }
    static createWarning(instructionLine, start, end, description, code, tags) {
        return Validator.createDiagnostic(vscode_languageserver_types_1.DiagnosticSeverity.Warning, instructionLine, start, end, description, code, tags);
    }
    static createDiagnostic(severity, instructionLine, start, end, description, code, tags) {
        return {
            instructionLine: instructionLine,
            range: {
                start: start,
                end: end
            },
            message: description,
            severity: severity,
            code: code,
            tags: tags,
            source: "dockerfile-utils"
        };
    }
}
exports.Validator = Validator;
Validator.dockerProblems = {
    "baseNameEmpty": "base name (${0}) should not be blank",
    "directiveCasing": "Parser directives should be written in lowercase letters",
    "directiveEscapeDuplicated": "only one escape parser directive can be used",
    "directiveEscapeInvalid": "invalid ESCAPE '${0}'. Must be ` or \\",
    "noSourceImage": "No source image provided with `FROM`",
    "emptyContinuationLine": "Empty continuation line",
    "fromRequiresOneOrThreeArguments": "FROM requires either one or three arguments",
    "invalidAs": "Second argument should be AS",
    "invalidPort": "Invalid containerPort: ${0}",
    "invalidProtocol": "Invalid proto: ${0}",
    "invalidReferenceFormat": "invalid reference format",
    "invalidStopSignal": "Invalid signal: ${0}",
    "invalidSyntax": "parsing \"${0}\": invalid syntax",
    "invalidDestination": "When using ${0} with more than one source file, the destination must be a directory and end with a / or a \\",
    "syntaxMissingEquals": "Syntax error - can't find = in \"${0}\". Must be of the form: name=value",
    "syntaxMissingNames": "${0} names cannot be blank",
    "syntaxMissingSingleQuote": "failed to process \"${0}\": unexpected end of statement while looking for matching single-quote",
    "syntaxMissingDoubleQuote": "failed to process \"${0}\": unexpected end of statement while looking for matching double-quote",
    "duplicateBuildStageName": "duplicate name ${0}",
    "invalidBuildStageName": "invalid name for build stage: \"${0}\", name can't start with a number or contain symbols",
    "flagAtLeastOne": "${0} must be at least 1 (not ${1})",
    "flagDuplicate": "Duplicate flag specified: ${0}",
    "flagInvalidDuration": "time: invalid duration ${0}",
    "flagInvalidFrom": "invalid from flag value ${0}: invalid reference format",
    "flagExpectedBooleanValue": "expecting boolean value for flag ${0}, not: ${1}",
    "flagLessThan1ms": "Interval \"${0}\" cannot be less than 1ms",
    "flagMissingDuration": "time: missing unit in duration ${0}",
    "flagMissingValue": "Missing a value on flag: ${0}",
    "flagUnknown": "Unknown flag: ${0}",
    "flagUnknownUnit": "time: unknown unit ${0} in duration ${1}",
    "instructionExtraArgument": "Instruction has an extra argument",
    "instructionMissingArgument": "Instruction has no arguments",
    "instructionMultiple": "There can only be one ${0} instruction in a Dockerfile or build stage. Only the last one will have an effect.",
    "instructionRequiresOneArgument": "${0} requires exactly one argument",
    "instructionRequiresAtLeastOneArgument": "${0} requires at least one argument",
    "instructionRequiresAtLeastTwoArguments": "${0} requires at least two arguments",
    "instructionRequiresTwoArguments": "${0} must have two arguments",
    "instructionUnnecessaryArgument": "${0} takes no arguments",
    "instructionUnknown": "Unknown instruction: ${0}",
    "instructionCasing": "Instructions should be written in uppercase letters",
    "instructionJSONInSingleQuotes": "Instruction written as a JSON array but is using single quotes instead of double quotes",
    "variableModifierUnsupported": "failed to process \"${0}\": unsupported modifier (${1}) in substitution",
    "onbuildChainingDisallowed": "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
    "onbuildTriggerDisallowed": "${0} isn't allowed as an ONBUILD trigger",
    "shellJsonForm": "SHELL requires the arguments to be in JSON form",
    "shellRequiresOne": "SHELL requires at least one argument",
    "deprecatedMaintainer": "MAINTAINER has been deprecated",
    "healthcheckCmdArgumentMissing": "Missing command after HEALTHCHECK CMD",
    "healthcheckTypeUnknown": "Unknown type\"${0}\" in HEALTHCHECK (try CMD)",
    "workdirPathNotAbsolute": "WORKDIR paths should be absolute"
};
