Skip to content

Commit

Permalink
Breaking: interpret rule options as unicode regexes (fixes #11423) (#…
Browse files Browse the repository at this point in the history
…11516)

This updates the core rules that interpret user-provided options as regular expressions. Those rules now interpret the options as unicode regexes, which avoids some cases of unexpected behavior with astral symbols.
  • Loading branch information
not-an-aardvark authored Apr 1, 2019
1 parent 6e7da57 commit 0fb5fd4
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 43 deletions.
2 changes: 1 addition & 1 deletion lib/rules/camelcase.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module.exports = {
*/
function isAllowed(name) {
return allow.findIndex(
entry => name === entry || name.match(new RegExp(entry)) // eslint-disable-line require-unicode-regexp
entry => name === entry || name.match(new RegExp(entry, "u"))
) !== -1;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/rules/capitalized-comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ function createRegExpForIgnorePatterns(normalizedOptions) {
const ignorePatternStr = normalizedOptions[key].ignorePattern;

if (ignorePatternStr) {
const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`); // eslint-disable-line require-unicode-regexp
const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`, "u");

normalizedOptions[key].ignorePatternRegExp = regExp;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/default-case.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module.exports = {
create(context) {
const options = context.options[0] || {};
const commentPattern = options.commentPattern
? new RegExp(options.commentPattern) // eslint-disable-line require-unicode-regexp
? new RegExp(options.commentPattern, "u")
: DEFAULT_COMMENT_PATTERN;

const sourceCode = context.getSourceCode();
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/dot-notation.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ module.exports = {
let allowPattern;

if (options.allowPattern) {
allowPattern = new RegExp(options.allowPattern); // eslint-disable-line require-unicode-regexp
allowPattern = new RegExp(options.allowPattern, "u");
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/handle-callback-err.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module.exports = {
*/
function matchesConfiguredErrorName(name) {
if (isPattern(errorArgument)) {
const regexp = new RegExp(errorArgument); // eslint-disable-line require-unicode-regexp
const regexp = new RegExp(errorArgument, "u");

return regexp.test(name);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/id-match.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module.exports = {
// Options
//--------------------------------------------------------------------------
const pattern = context.options[0] || "^.+$",
regexp = new RegExp(pattern); // eslint-disable-line require-unicode-regexp
regexp = new RegExp(pattern, "u");

const options = context.options[1] || {},
properties = !!options.properties,
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/line-comment-position.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ module.exports = {

const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
const fallThroughRegExp = /^\s*falls?\s?through/u;
const customIgnoreRegExp = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
const customIgnoreRegExp = new RegExp(ignorePattern, "u");
const sourceCode = context.getSourceCode();

//--------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/lines-around-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ module.exports = {
const options = Object.assign({}, context.options[0]);
const ignorePattern = options.ignorePattern;
const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
const customIgnoreRegExp = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
const customIgnoreRegExp = new RegExp(ignorePattern, "u");
const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;

options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/max-len.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ module.exports = {
let ignorePattern = options.ignorePattern || null;

if (ignorePattern) {
ignorePattern = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
ignorePattern = new RegExp(ignorePattern, "u");
}

//--------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/new-cap.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ module.exports = {
const skipProperties = config.properties === false;

const newIsCapExceptions = checkArray(config, "newIsCapExceptions", []).reduce(invert, {});
const newIsCapExceptionPattern = config.newIsCapExceptionPattern ? new RegExp(config.newIsCapExceptionPattern) : null; // eslint-disable-line require-unicode-regexp
const newIsCapExceptionPattern = config.newIsCapExceptionPattern ? new RegExp(config.newIsCapExceptionPattern, "u") : null;

const capIsNewExceptions = calculateCapIsNewExceptions(config);
const capIsNewExceptionPattern = config.capIsNewExceptionPattern ? new RegExp(config.capIsNewExceptionPattern) : null; // eslint-disable-line require-unicode-regexp
const capIsNewExceptionPattern = config.capIsNewExceptionPattern ? new RegExp(config.capIsNewExceptionPattern, "u") : null;

const listeners = {};

Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-fallthrough.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ module.exports = {
let fallthroughCommentPattern = null;

if (options.commentPattern) {
fallthroughCommentPattern = new RegExp(options.commentPattern); // eslint-disable-line require-unicode-regexp
fallthroughCommentPattern = new RegExp(options.commentPattern, "u");
} else {
fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/no-unused-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ module.exports = {
config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;

if (firstOption.varsIgnorePattern) {
config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern); // eslint-disable-line require-unicode-regexp
config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
}

if (firstOption.argsIgnorePattern) {
config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern); // eslint-disable-line require-unicode-regexp
config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u");
}

if (firstOption.caughtErrorsIgnorePattern) {
config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern); // eslint-disable-line require-unicode-regexp
config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions lib/rules/no-warning-comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

"use strict";

const { escapeRegExp } = require("lodash");
const astUtils = require("../util/ast-utils");

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -58,7 +59,7 @@ module.exports = {
* @returns {RegExp} The term converted to a RegExp
*/
function convertToRegExp(term) {
const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/gu, "\\$&");
const escaped = escapeRegExp(term);
const wordBoundary = "\\b";
const eitherOrWordBoundary = `|${wordBoundary}`;
let prefix;
Expand Down Expand Up @@ -95,15 +96,15 @@ module.exports = {
* ^\s*TERM\b. This checks the word boundary
* at the beginning of the comment.
*/
return new RegExp(prefix + escaped + suffix, "i"); // eslint-disable-line require-unicode-regexp
return new RegExp(prefix + escaped + suffix, "iu");
}

/*
* For location "anywhere" the regex should be
* \bTERM\b|\bTERM\b, this checks the entire comment
* for the term.
*/
return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i"); // eslint-disable-line require-unicode-regexp
return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "iu");
}

const warningRegExps = warningTerms.map(convertToRegExp);
Expand Down
8 changes: 4 additions & 4 deletions lib/rules/spaced-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ function createAlwaysStylePattern(markers, exceptions) {
pattern += "?"; // or nothing.
pattern += createExceptionsPattern(exceptions);

return new RegExp(pattern); // eslint-disable-line require-unicode-regexp
return new RegExp(pattern, "u");
}

/**
Expand All @@ -142,7 +142,7 @@ function createAlwaysStylePattern(markers, exceptions) {
function createNeverStylePattern(markers) {
const pattern = `^(${markers.map(escape).join("|")})?[ \t]+`;

return new RegExp(pattern); // eslint-disable-line require-unicode-regexp
return new RegExp(pattern, "u");
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -250,9 +250,9 @@ module.exports = {
// Create RegExp object for valid patterns.
rule[type] = {
beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers),
endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`) : new RegExp(endNeverPattern), // eslint-disable-line require-unicode-regexp
endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"),
hasExceptions: exceptions.length > 0,
markers: new RegExp(`^(${markers.map(escape).join("|")})`) // eslint-disable-line require-unicode-regexp
markers: new RegExp(`^(${markers.map(escape).join("|")})`, "u")
};

return rule;
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/valid-jsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ module.exports = {
}

if (options.matchDescription) {
const regex = new RegExp(options.matchDescription); // eslint-disable-line require-unicode-regexp
const regex = new RegExp(options.matchDescription, "u");

if (!regex.test(jsdoc.description)) {
context.report({ node: jsdocNode, messageId: "unsatisfiedDesc" });
Expand Down
6 changes: 6 additions & 0 deletions tests/lib/rules/max-len.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ ruleTester.run("max-len", rule, {
{
code: "'🙂😀😆😎😊😜😉👍'",
options: [10]
},

// Astral symbols in pattern (only matched by unicode regexes)
{
code: "var longNameLongName = '𝌆𝌆'",
options: [5, { ignorePattern: "𝌆{2}" }]
}
],

Expand Down
40 changes: 20 additions & 20 deletions tests/lib/rules/no-unused-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,33 +330,33 @@ ruleTester.run("no-unused-vars", rule, {
{
code: "var _a; var b;",
options: [{ vars: "all", varsIgnorePattern: "^_" }],
errors: [{ message: "'b' is defined but never used. Allowed unused vars must match /^_/.", line: 1, column: 13 }]
errors: [{ message: "'b' is defined but never used. Allowed unused vars must match /^_/u.", line: 1, column: 13 }]
},
{
code: "var a; function foo() { var _b; var c_; } foo();",
options: [{ vars: "local", varsIgnorePattern: "^_" }],
errors: [{ message: "'c_' is defined but never used. Allowed unused vars must match /^_/.", line: 1, column: 37 }]
errors: [{ message: "'c_' is defined but never used. Allowed unused vars must match /^_/u.", line: 1, column: 37 }]
},
{
code: "function foo(a, _b) { } foo();",
options: [{ args: "all", argsIgnorePattern: "^_" }],
errors: [{ message: "'a' is defined but never used. Allowed unused args must match /^_/.", line: 1, column: 14 }]
errors: [{ message: "'a' is defined but never used. Allowed unused args must match /^_/u.", line: 1, column: 14 }]
},
{
code: "function foo(a, _b, c) { return a; } foo();",
options: [{ args: "after-used", argsIgnorePattern: "^_" }],
errors: [{ message: "'c' is defined but never used. Allowed unused args must match /^_/.", line: 1, column: 21 }]
errors: [{ message: "'c' is defined but never used. Allowed unused args must match /^_/u.", line: 1, column: 21 }]
},
{
code: "function foo(_a) { } foo();",
options: [{ args: "all", argsIgnorePattern: "[iI]gnored" }],
errors: [{ message: "'_a' is defined but never used. Allowed unused args must match /[iI]gnored/.", line: 1, column: 14 }]
errors: [{ message: "'_a' is defined but never used. Allowed unused args must match /[iI]gnored/u.", line: 1, column: 14 }]
},
{
code: "var [ firstItemIgnored, secondItem ] = items;",
options: [{ vars: "all", varsIgnorePattern: "[iI]gnored" }],
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "'secondItem' is assigned a value but never used. Allowed unused vars must match /[iI]gnored/.", line: 1, column: 25 }]
errors: [{ message: "'secondItem' is assigned a value but never used. Allowed unused vars must match /[iI]gnored/u.", line: 1, column: 25 }]
},

// for-in loops (see #2342)
Expand Down Expand Up @@ -524,23 +524,23 @@ ruleTester.run("no-unused-vars", rule, {
{
code: "try{}catch(err){};",
options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }]
errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }]
},

// multiple try catch with one success
{
code: "try{}catch(ignoreErr){}try{}catch(err){};",
options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }]
errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }]
},

// multiple try catch both fail
{
code: "try{}catch(error){}try{}catch(err){};",
options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
errors: [
{ message: "'error' is defined but never used. Allowed unused args must match /^ignore/." },
{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }
{ message: "'error' is defined but never used. Allowed unused args must match /^ignore/u." },
{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }
]
},

Expand Down Expand Up @@ -613,10 +613,10 @@ ruleTester.run("no-unused-vars", rule, {
options: [{ argsIgnorePattern: "c" }],
errors: [
{
message: "'a' is defined but never used. Allowed unused args must match /c/."
message: "'a' is defined but never used. Allowed unused args must match /c/u."
},
{
message: "'b' is defined but never used. Allowed unused args must match /c/."
message: "'b' is defined but never used. Allowed unused args must match /c/u."
}
]
},
Expand All @@ -626,10 +626,10 @@ ruleTester.run("no-unused-vars", rule, {
parserOptions: { ecmaVersion: 6 },
errors: [
{
message: "'a' is defined but never used. Allowed unused args must match /[cd]/."
message: "'a' is defined but never used. Allowed unused args must match /[cd]/u."
},
{
message: "'b' is defined but never used. Allowed unused args must match /[cd]/."
message: "'b' is defined but never used. Allowed unused args must match /[cd]/u."
}
]
},
Expand All @@ -639,13 +639,13 @@ ruleTester.run("no-unused-vars", rule, {
parserOptions: { ecmaVersion: 6 },
errors: [
{
message: "'a' is defined but never used. Allowed unused args must match /c/."
message: "'a' is defined but never used. Allowed unused args must match /c/u."
},
{
message: "'b' is defined but never used. Allowed unused args must match /c/."
message: "'b' is defined but never used. Allowed unused args must match /c/u."
},
{
message: "'d' is defined but never used. Allowed unused args must match /c/."
message: "'d' is defined but never used. Allowed unused args must match /c/u."
}
]
},
Expand All @@ -655,13 +655,13 @@ ruleTester.run("no-unused-vars", rule, {
parserOptions: { ecmaVersion: 6 },
errors: [
{
message: "'a' is defined but never used. Allowed unused args must match /d/."
message: "'a' is defined but never used. Allowed unused args must match /d/u."
},
{
message: "'b' is defined but never used. Allowed unused args must match /d/."
message: "'b' is defined but never used. Allowed unused args must match /d/u."
},
{
message: "'c' is defined but never used. Allowed unused args must match /d/."
message: "'c' is defined but never used. Allowed unused args must match /d/u."
}
]
},
Expand Down

0 comments on commit 0fb5fd4

Please sign in to comment.