no-useless-backreference
Запрещает бесполезные обратные ссылки в регулярных выражениях
Использование конфигурации recommended из @eslint/js в файле конфигурации включает это правило
В регулярных выражениях JavaScript синтаксически допустимо определить обратную ссылку на группу, принадлежащую другой альтернативной части шаблона, обратную ссылку на группу, которая встречается после обратной ссылки, обратную ссылку на группу, содержащую эту обратную ссылку, или обратную ссылку на группу, находящуюся внутри отрицательной проверки просмотра. Однако согласно спецификации во всех этих случаях обратная ссылка всегда совпадает только с последовательностью нулевой длины (пустой строкой), независимо от контекста, в котором встречаются обратная ссылка и группа.
Обратные ссылки, которые всегда успешно совпадают с последовательностью нулевой длины и не могут совпасть ни с чем другим, бесполезны. По сути, они игнорируются, и их можно удалить, не изменив поведение регулярного выражения.
const regex = /^(?:(a)|\1b)$/;
regex.test("a"); // true
regex.test("b"); // true!
regex.test("ab"); // false
const equivalentRegex = /^(?:(a)|b)$/;
equivalentRegex.test("a"); // true
equivalentRegex.test("b"); // true
equivalentRegex.test("ab"); // false
Бесполезная обратная ссылка может указывать на ошибку в коде. Обычно это означает, что регулярное выражение работает не так, как задумано.
Подробности правила
Это правило обнаруживает и запрещает следующие обратные ссылки в регулярных выражениях:
- Обратная ссылка на группу из другой альтернативы, например,
/(a)|\1b/. В таком регулярном выражении предполагается, что обратная ссылка совпадёт с тем, что было захвачено на данном этапе группой, которая не участвует в совпадении. - Обратная ссылка на группу, которая встречается позже в шаблоне, например,
/\1(a)/. Группа ещё ничего не захватила, а ECMAScript не поддерживает ссылки вперёд. Внутри ретроспективных проверок, которые сопоставляются в обратном направлении, действует обратное, и это правило запрещает обратную ссылку на группу, которая встречается раньше в той же ретроспективной проверке, например,/(?<=(a)\1)b/. - Обратная ссылка на группу изнутри этой же группы, например,
/(\1)/. Как и в предыдущем случае, группа ещё ничего не захватила, а ECMAScript не поддерживает вложенные ссылки. - Обратная ссылка на группу, находящуюся в отрицательной проверке просмотра, если сама обратная ссылка не находится в той же отрицательной проверке просмотра, например,
/a(?!(b)).\1/. Отрицательная проверка просмотра (опережающая или ретроспективная) завершается успешно, только если её шаблон не совпадает, то есть группа не участвует в совпадении.
Согласно спецификации ECMAScript, все перечисленные выше обратные ссылки допустимы, всегда успешно совпадают с последовательностью нулевой длины и не могут совпасть ни с чем другим. Следовательно, они не приводят к ошибкам разбора или выполнения, но и не влияют на поведение регулярных выражений. Они синтаксически допустимы, но бесполезны.
Это может удивить разработчиков, пришедших из других языков, в которых некоторые из таких обратных ссылок можно использовать с пользой.
// in some other languages, this pattern would successfully match "aab"
/^(?:(a)(?=a)|\1b)+$/.test("aab"); // false
Примеры неправильного кода для этого правила:
/*eslint no-useless-backreference: "error"*/
; // reference to (a) into another alternative
; // reference to (a) into another alternative
; // reference to (c) into another alternative
; // forward reference to (a)
; // forward reference to (b)
; // forward reference to (c)
; // forward reference to (?<foo>a)
; // backward reference to (a) from within the same lookbehind
; // backward reference to (a) from within the same lookbehind
; // nested reference to (\1)
; // nested reference to ((a)\1)
; // nested reference to (?<foo>(.)b\1)
; // reference to (b) into a negative lookahead
; // reference to (a) into a negative lookbehind
Примеры правильного кода для этого правила:
/*eslint no-useless-backreference: "error"*/
/^(?:(a)|(b)\2)$/; // reference to (b)
/(a)\1/; // reference to (a)
RegExp('(a)\\1(b)'); // reference to (a)
/(a)(b)\2(c)/; // reference to (b)
/(?<foo>a)\k<foo>/; // reference to (?<foo>a)
/(?<=\1(a))b/; // reference to (a), correctly before the group as they're in the same lookbehind
/(?<=(a))b\1/; // reference to (a), correctly after the group as the backreference isn't in the lookbehind
new RegExp('(.)\\1'); // reference to (.)
/^(?:(a)\1)$/; // reference to (a)
/^((a)\2)$/; // reference to (a)
/a(?<foo>(.)b\2)/; // reference to (.)
/a(?!(b|c)\1)./; // reference to (b|c), correct as it's from within the same negative lookahead
/(?<!\1(a))b/; // reference to (a), correct as it's from within the same negative lookbehind
Обратите внимание: это правило не предназначено для обнаружения и запрета потенциально ошибочного использования синтаксиса обратных ссылок в регулярных выражениях, например, в классах символов или при попытке сослаться на несуществующую группу. В зависимости от контекста последовательность \1…\9, которая не является синтаксически допустимой обратной ссылкой, может привести к синтаксической ошибке или интерпретироваться иначе (например, как устаревшая восьмеричная escape-последовательность).
Примеры дополнительного правильного кода для этого правила:
/*eslint no-useless-backreference: "error"*/
// comments describe behavior in a browser
/^[\1](a)$/.test("\x01a"); // true. In a character class, \1 is treated as an octal escape sequence.
/^\1$/.test("\x01"); // true. Since the group 1 doesn't exist, \1 is treated as an octal escape sequence.
/^(a)\1\2$/.test("aa\x02"); // true. In this case, \1 is a backreference, \2 is an octal escape sequence.
Параметры
У этого правила нет параметров.
Связанные правила
Версия
Это правило появилось в ESLint v7.0.0-alpha.0.
Дополнительная литература
Ресурсы
© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/rules/no-useless-backreference