Repository: eslint/eslint
File: lib/rule-tester/rule-tester.js (JavaScript)

Code before:
    const matcher = getPlaceholderMatcher();

    return Array.from(message.matchAll(matcher), ([, name]) => name.trim());
}

/**
 * Returns the placeholders in the reported messages but
 * only includes the placeholders available in the raw message if data is not specified or
 * excludes introduced placeholders specified in the data values.
 * @param {string} message The reported message
 * @param {string} raw The raw message specified in the rule meta.messages
 * @param {undefined|Record<unknown, unknown>} data The passed
 * @returns {string[]} Missing placeholder names
 */
function getMissingMessagePlaceholders(message, raw, data) {
    const existing = getMessagePlaceholders(message);

    if (data === void 0) {
        const available = getMessagePlaceholders(raw);

        return existing.filter(name => available.includes(name));
    }

    const introduced = new Set(Object.values(data).flatMap(value => (typeof value === "string" ? getMessagePlaceholders(value) : [])));

    return existing.filter(name => !introduced.has(name));
}

const metaSchemaDescription = `
\t- If the rule has options, set \`meta.schema\` to an array or non-empty object to enable options validation.
\t- If the rule doesn't have options, omit \`meta.schema\` to enforce that no options can be passed to the rule.
\t- You can also set \`meta.schema\` to \`false\` to opt-out of options validation (not recommended).

\thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
`;

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

// default separators for testing
const DESCRIBE = Symbol("describe");
const IT = Symbol("it");
const IT_ONLY = Symbol("itOnly");

/**
 * This is `it` default handler if `it` don't exist.
 * @this {Mocha}
 * @param {string} text The description of the test case.
 * @param {Function} method The logic of the test case.
 * @throws {Error} Any error upon execution of `method`.

Review comment (suggestion):
```suggestion
    let existing = getMessagePlaceholders(message);

    //  no potential placeholders were found in the reported message, so there's nothing further to check
    if (existing.length === 0) {
        return existing;
    }

    const original = getMessagePlaceholders(raw);

    // filter out those that don't appear in the original template message as they're certainly not placeholders
    existing = existing.filter(name => original.includes(name));

    if (data) {
        const provided = Object.keys(data);

        // since the hydrated message texts are compared, we can ignore those for which the data is provided in the test
        existing = existing.filter(name => !provided.includes(name));
    }

    return existing;
```

I think this check would be more robust if we always exclude those that don't appear in the meta message and those for which the data is provided in the test.

Code after:
    const matcher = getPlaceholderMatcher();

    return Array.from(message.matchAll(matcher), ([, name]) => name.trim());
}

/**
 * Returns the placeholders in the reported messages but
 * only includes the placeholders available in the raw message and not in the provided data.
 * @param {string} message The reported message
 * @param {string} raw The raw message specified in the rule meta.messages
 * @param {undefined|Record<unknown, unknown>} data The passed
 * @returns {string[]} Missing placeholder names
 */
function getUnsubstitutedMessagePlaceholders(message, raw, data = {}) {
    const unsubstituted = getMessagePlaceholders(message);

    if (unsubstituted.length === 0) {
        return [];
    }

    // Remove false positives by only counting placeholders in the raw message, which were not provided in the data matcher or added with a data property
    const known = getMessagePlaceholders(raw);
    const provided = Object.keys(data);

    return unsubstituted.filter(name => known.includes(name) && !provided.includes(name));
}

const metaSchemaDescription = `
\t- If the rule has options, set \`meta.schema\` to an array or non-empty object to enable options validation.
\t- If the rule doesn't have options, omit \`meta.schema\` to enforce that no options can be passed to the rule.
\t- You can also set \`meta.schema\` to \`false\` to opt-out of options validation (not recommended).

\thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
`;

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

// default separators for testing
const DESCRIBE = Symbol("describe");
const IT = Symbol("it");
const IT_ONLY = Symbol("itOnly");

/**
 * This is `it` default handler if `it` don't exist.
 * @this {Mocha}
 * @param {string} text The description of the test case.
 * @param {Function} method The logic of the test case.
 * @throws {Error} Any error upon execution of `method`.
 * @returns {any} Returned value of `method`.