summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorPrince J Wesley <princejohnwesley@gmail.com>2016-11-13 11:11:38 +0530
committerRich Trott <rtrott@gmail.com>2016-11-23 22:06:37 -0800
commit5cf0157b82302cc23a4e7a27a2cb9c22bde5bd8f (patch)
tree33845b830bec8bd98f76575efd3c86b7acb38cb8 /tools
parent0f4c7b8c371caae4cd4a2cb1fa03d2079f75663d (diff)
downloadandroid-node-v8-5cf0157b82302cc23a4e7a27a2cb9c22bde5bd8f.tar.gz
android-node-v8-5cf0157b82302cc23a4e7a27a2cb9c22bde5bd8f.tar.bz2
android-node-v8-5cf0157b82302cc23a4e7a27a2cb9c22bde5bd8f.zip
tools: Add no useless regex char class rule
Eslint Rule: Disallow useless escape in regex character class with optional override characters option and auto fixable with eslint --fix option. Usage: no-useless-regex-char-class-escape: [2, { override: ['[', ']'] }] PR-URL: https://github.com/nodejs/node/pull/9591 Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/eslint-rules/no-useless-regex-char-class-escape.js190
1 files changed, 190 insertions, 0 deletions
diff --git a/tools/eslint-rules/no-useless-regex-char-class-escape.js b/tools/eslint-rules/no-useless-regex-char-class-escape.js
new file mode 100644
index 0000000000..934a3fa193
--- /dev/null
+++ b/tools/eslint-rules/no-useless-regex-char-class-escape.js
@@ -0,0 +1,190 @@
+/**
+ * @fileoverview Disallow useless escape in regex character class
+ * Based on 'no-useless-escape' rule
+ */
+'use strict';
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const REGEX_CHARCLASS_ESCAPES = new Set('\\bcdDfnrsStvwWxu0123456789]');
+
+/**
+ * Parses a regular expression into a list of regex character class list.
+ * @param {string} regExpText raw text used to create the regular expression
+ * @returns {Object[]} A list of character classes tokens with index and
+ * escape info
+ * @example
+ *
+ * parseRegExpCharClass('a\\b[cd-]')
+ *
+ * returns:
+ * [
+ * {
+ * empty: false,
+ * start: 4,
+ * end: 6,
+ * chars: [
+ * {text: 'c', index: 4, escaped: false},
+ * {text: 'd', index: 5, escaped: false},
+ * {text: '-', index: 6, escaped: false}
+ * ]
+ * }
+ * ]
+ */
+
+function parseRegExpCharClass(regExpText) {
+ const charList = [];
+ let charListIdx = -1;
+ const initState = {
+ escapeNextChar: false,
+ inCharClass: false,
+ startingCharClass: false
+ };
+
+ regExpText.split('').reduce((state, char, index) => {
+ if (!state.escapeNextChar) {
+ if (char === '\\') {
+ return Object.assign(state, { escapeNextChar: true });
+ }
+ if (char === '[' && !state.inCharClass) {
+ charListIdx += 1;
+ charList.push({ start: index + 1, chars: [], end: -1 });
+ return Object.assign(state, {
+ inCharClass: true,
+ startingCharClass: true
+ });
+ }
+ if (char === ']' && state.inCharClass) {
+ const charClass = charList[charListIdx];
+ charClass.empty = charClass.chars.length === 0;
+ if (charClass.empty) {
+ charClass.start = charClass.end = -1;
+ } else {
+ charList[charListIdx].end = index - 1;
+ }
+ return Object.assign(state, {
+ inCharClass: false,
+ startingCharClass: false
+ });
+ }
+ }
+ if (state.inCharClass) {
+ charList[charListIdx].chars.push({
+ text: char,
+ index, escaped:
+ state.escapeNextChar
+ });
+ }
+ return Object.assign(state, {
+ escapeNextChar: false,
+ startingCharClass: false
+ });
+ }, initState);
+
+ return charList;
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'disallow unnecessary regex characer class escape sequences',
+ category: 'Best Practices',
+ recommended: false
+ },
+ fixable: 'code',
+ schema: [{
+ 'type': 'object',
+ 'properties': {
+ 'override': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ 'uniqueItems': true
+ }
+ },
+ 'additionalProperties': false
+ }]
+ },
+
+ create(context) {
+ const overrideSet = new Set(context.options.length
+ ? context.options[0].override || []
+ : []);
+
+ /**
+ * Reports a node
+ * @param {ASTNode} node The node to report
+ * @param {number} startOffset The backslash's offset
+ * from the start of the node
+ * @param {string} character The uselessly escaped character
+ * (not including the backslash)
+ * @returns {void}
+ */
+ function report(node, startOffset, character) {
+ context.report({
+ node,
+ loc: {
+ line: node.loc.start.line,
+ column: node.loc.start.column + startOffset
+ },
+ message: 'Unnecessary regex escape in character' +
+ ' class: \\{{character}}',
+ data: { character },
+ fix: (fixer) => {
+ const start = node.range[0] + startOffset;
+ return fixer.replaceTextRange([start, start + 1], '');
+ }
+ });
+ }
+
+ /**
+ * Checks if a node has superflous escape character
+ * in regex character class.
+ *
+ * @param {ASTNode} node - node to check.
+ * @returns {void}
+ */
+ function check(node) {
+ if (node.regex) {
+ parseRegExpCharClass(node.regex.pattern)
+ .forEach((charClass) => {
+ charClass
+ .chars
+ // The '-' character is a special case if is not at
+ // either edge of the character class. To account for this,
+ // filter out '-' characters that appear in the middle of a
+ // character class.
+ .filter((charInfo) => !(charInfo.text === '-' &&
+ (charInfo.index !== charClass.start &&
+ charInfo.index !== charClass.end)))
+
+ // The '^' character is a special case if it's at the beginning
+ // of the character class. To account for this, filter out '^'
+ // characters that appear at the start of a character class.
+ //
+ .filter((charInfo) => !(charInfo.text === '^' &&
+ charInfo.index === charClass.start))
+
+ // Filter out characters that aren't escaped.
+ .filter((charInfo) => charInfo.escaped)
+
+ // Filter out characters that are valid to escape, based on
+ // their position in the regular expression.
+ .filter((charInfo) => !REGEX_CHARCLASS_ESCAPES.has(charInfo.text))
+
+ // Filter out overridden character list.
+ .filter((charInfo) => !overrideSet.has(charInfo.text))
+
+ // Report all the remaining characters.
+ .forEach((charInfo) =>
+ report(node, charInfo.index, charInfo.text));
+ });
+ }
+ }
+
+ return {
+ Literal: check
+ };
+ }
+};