Skip to content

Commit e0b4403

Browse files
committed
Throw when using XRegExp.replace with native named capture and numbered backref one higher than number of captures #317
1 parent c5290d6 commit e0b4403

File tree

2 files changed

+17
-2
lines changed

2 files changed

+17
-2
lines changed

src/xregexp.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,12 @@ fixed.replace = function(search, replacement) {
15561556

15571557
function replacer($0, bracketed, angled, dollarToken) {
15581558
bracketed = bracketed || angled;
1559+
1560+
// ES2018 added a new trailing `groups` arg that's passed to replacement functions
1561+
// when the search regex uses native named capture
1562+
const numNonCaptureArgs = isType(args[args.length - 1], 'Object') ? 4 : 3;
1563+
const numCaptures = args.length - numNonCaptureArgs;
1564+
15591565
// Named or numbered backreference with curly or angled braces
15601566
if (bracketed) {
15611567
// XRegExp behavior for `${n}` or `$<n>`:
@@ -1568,7 +1574,7 @@ fixed.replace = function(search, replacement) {
15681574
// 3. If the name or number does not refer to an existing capturing group, it's
15691575
// an error.
15701576
let n = +bracketed; // Type-convert; drop leading zeros
1571-
if (n <= args.length - 3) {
1577+
if (n <= numCaptures) {
15721578
return args[n] || '';
15731579
}
15741580
// Groups with the same name is an error, else would need `lastIndexOf`
@@ -1607,7 +1613,7 @@ fixed.replace = function(search, replacement) {
16071613
// - `$01` is `$1` if at least one capturing group, else it's a literal `$01`.
16081614
// - `$0` is a literal `$0`.
16091615
if (!isNaN(dollarToken)) {
1610-
if (dollarToken > args.length - 3) {
1616+
if (dollarToken > numCaptures) {
16111617
throw new SyntaxError(`Backreference to undefined group ${$0}`);
16121618
}
16131619
return args[dollarToken] || '';

tests/spec/s-xregexp-methods.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,15 @@ describe('XRegExp.replace()', function() {
12931293
expect(function() {XRegExp.replace('test', /(t)/, '$<2>');}).toThrowError(SyntaxError);
12941294
});
12951295

1296+
it('should throw an exception for backreferences to unknown group numbers when using named capture', function() {
1297+
expect(function() {XRegExp.replace('test', XRegExp('(?<n>t)'), '$2');}).toThrowError(SyntaxError);
1298+
expect(function() {XRegExp.replace('test', XRegExp('(?<n>t)'), '$<2>');}).toThrowError(SyntaxError);
1299+
1300+
// Native named capture introduced in ES2018
1301+
expect(function() {XRegExp.replace('test', /(?<n>t)/, '$2');}).toThrowError(SyntaxError);
1302+
expect(function() {XRegExp.replace('test', /(?<n>t)/, '$<2>');}).toThrowError(SyntaxError);
1303+
});
1304+
12961305
it('should allow ${0} to refer to the entire match', function() {
12971306
expect(XRegExp.replace('test', /../g, '${0}:')).toBe('te:st:');
12981307
expect(XRegExp.replace('test', /../g, '$<0>:')).toBe('te:st:');

0 commit comments

Comments
 (0)