Skip to content

Commit 287c4b7

Browse files
JoshuaKGoldbergfasttimemdjermanovic
authoredJan 7, 2024
feat: no-misleading-character-class granular errors (#17515)
* feat: `no-misleading-character-class` granular errors * fix: column offsets * fix: missing CallExpression * Apply suggestions from code review Co-authored-by: Francesco Trotta <github@fasttime.org> * All tests passing again * Edge case: quadruple back slashes * Apply suggestions from code review Co-authored-by: Francesco Trotta <github@fasttime.org> * Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta <github@fasttime.org> * Adjusted for repeat characters * Separated kinds into a Set * Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta <github@fasttime.org> * Adjust unit tests for accepted changes * Split into adjustment function for sequence pairs * Reduced complexity because of edge cases, as reqiested * Use chars array as suggested - mostly there * Checked that slices include first and last * Used sourceCode.getText(node) over node.raw, and added comments * Update lib/rules/no-misleading-character-class.js * Drastically limit applicability * Restricted regexp literals on \u * Update lib/rules/no-misleading-character-class.js Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * Corrected erroneous escaped u exclusion * Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta <github@fasttime.org> * If a report range is missing, skip other reports * Update lib/rules/no-misleading-character-class.js Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * Merge branch 'main' * Implement fasttime suggestion for a Map * nit: for loop * Unify zwj reports * Added a couple of multiline tests * use .at(-1) --------- Co-authored-by: Francesco Trotta <github@fasttime.org> Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
1 parent d31c180 commit 287c4b7

File tree

2 files changed

+777
-152
lines changed

2 files changed

+777
-152
lines changed
 

‎lib/rules/no-misleading-character-class.js

+186-70
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ function *iterateCharacterSequence(nodes) {
6262
}
6363
}
6464

65-
6665
/**
6766
* Checks whether the given character node is a Unicode code point escape or not.
6867
* @param {Character} char the character node to check.
@@ -73,80 +72,120 @@ function isUnicodeCodePointEscape(char) {
7372
}
7473

7574
/**
76-
* Each function returns `true` if it detects that kind of problem.
77-
* @type {Record<string, (chars: Character[]) => boolean>}
75+
* Each function returns matched characters if it detects that kind of problem.
76+
* @type {Record<string, (char: Character, index: number, chars: Character[]) => Character[] | null>}
7877
*/
79-
const hasCharacterSequence = {
80-
surrogatePairWithoutUFlag(chars) {
81-
return chars.some((c, i) => {
82-
if (i === 0) {
83-
return false;
84-
}
85-
const c1 = chars[i - 1];
86-
87-
return (
88-
isSurrogatePair(c1.value, c.value) &&
89-
!isUnicodeCodePointEscape(c1) &&
90-
!isUnicodeCodePointEscape(c)
91-
);
92-
});
78+
const characterSequenceIndexFilters = {
79+
surrogatePairWithoutUFlag(char, index, chars) {
80+
if (index === 0) {
81+
return null;
82+
}
83+
84+
const previous = chars[index - 1];
85+
86+
if (
87+
isSurrogatePair(previous.value, char.value) &&
88+
!isUnicodeCodePointEscape(previous) &&
89+
!isUnicodeCodePointEscape(char)
90+
) {
91+
return [previous, char];
92+
}
93+
94+
return null;
9395
},
9496

95-
surrogatePair(chars) {
96-
return chars.some((c, i) => {
97-
if (i === 0) {
98-
return false;
99-
}
100-
const c1 = chars[i - 1];
101-
102-
return (
103-
isSurrogatePair(c1.value, c.value) &&
104-
(
105-
isUnicodeCodePointEscape(c1) ||
106-
isUnicodeCodePointEscape(c)
107-
)
108-
);
109-
});
97+
surrogatePair(char, index, chars) {
98+
if (index === 0) {
99+
return null;
100+
}
101+
102+
const previous = chars[index - 1];
103+
104+
if (
105+
isSurrogatePair(previous.value, char.value) &&
106+
(
107+
isUnicodeCodePointEscape(previous) ||
108+
isUnicodeCodePointEscape(char)
109+
)
110+
) {
111+
return [previous, char];
112+
}
113+
114+
return null;
110115
},
111116

112-
combiningClass(chars) {
113-
return chars.some((c, i) => (
114-
i !== 0 &&
115-
isCombiningCharacter(c.value) &&
116-
!isCombiningCharacter(chars[i - 1].value)
117-
));
117+
combiningClass(char, index, chars) {
118+
if (
119+
index !== 0 &&
120+
isCombiningCharacter(char.value) &&
121+
!isCombiningCharacter(chars[index - 1].value)
122+
) {
123+
return [chars[index - 1], char];
124+
}
125+
126+
return null;
118127
},
119128

120-
emojiModifier(chars) {
121-
return chars.some((c, i) => (
122-
i !== 0 &&
123-
isEmojiModifier(c.value) &&
124-
!isEmojiModifier(chars[i - 1].value)
125-
));
129+
emojiModifier(char, index, chars) {
130+
if (
131+
index !== 0 &&
132+
isEmojiModifier(char.value) &&
133+
!isEmojiModifier(chars[index - 1].value)
134+
) {
135+
return [chars[index - 1], char];
136+
}
137+
138+
return null;
126139
},
127140

128-
regionalIndicatorSymbol(chars) {
129-
return chars.some((c, i) => (
130-
i !== 0 &&
131-
isRegionalIndicatorSymbol(c.value) &&
132-
isRegionalIndicatorSymbol(chars[i - 1].value)
133-
));
141+
regionalIndicatorSymbol(char, index, chars) {
142+
if (
143+
index !== 0 &&
144+
isRegionalIndicatorSymbol(char.value) &&
145+
isRegionalIndicatorSymbol(chars[index - 1].value)
146+
) {
147+
return [chars[index - 1], char];
148+
}
149+
150+
return null;
134151
},
135152

136-
zwj(chars) {
137-
const lastIndex = chars.length - 1;
153+
zwj(char, index, chars) {
154+
if (
155+
index !== 0 &&
156+
index !== chars.length - 1 &&
157+
char.value === 0x200d &&
158+
chars[index - 1].value !== 0x200d &&
159+
chars[index + 1].value !== 0x200d
160+
) {
161+
return chars.slice(index - 1, index + 2);
162+
}
138163

139-
return chars.some((c, i) => (
140-
i !== 0 &&
141-
i !== lastIndex &&
142-
c.value === 0x200d &&
143-
chars[i - 1].value !== 0x200d &&
144-
chars[i + 1].value !== 0x200d
145-
));
164+
return null;
146165
}
147166
};
148167

149-
const kinds = Object.keys(hasCharacterSequence);
168+
const kinds = Object.keys(characterSequenceIndexFilters);
169+
170+
/**
171+
* Collects the indices where the filter returns an array.
172+
* @param {Character[]} chars Characters to run the filter on.
173+
* @param {(char: Character, index: number, chars: Character[]) => Character[] | null} filter Finds matches for an index.
174+
* @returns {Character[][]} Indices where the filter returned true.
175+
*/
176+
function accumulate(chars, filter) {
177+
const matchingChars = [];
178+
179+
chars.forEach((char, index) => {
180+
const matches = filter(char, index, chars);
181+
182+
if (matches) {
183+
matchingChars.push(matches);
184+
}
185+
});
186+
187+
return matchingChars;
188+
}
150189

151190
//------------------------------------------------------------------------------
152191
// Rule Definition
@@ -181,6 +220,62 @@ module.exports = {
181220
const sourceCode = context.sourceCode;
182221
const parser = new RegExpParser();
183222

223+
/**
224+
* Generates a granular loc for context.report, if directly calculable.
225+
* @param {Character[]} chars Individual characters being reported on.
226+
* @param {Node} node Parent string node to report within.
227+
* @returns {Object | null} Granular loc for context.report, if directly calculable.
228+
* @see https://github.com/eslint/eslint/pull/17515
229+
*/
230+
function generateReportLocation(chars, node) {
231+
232+
// Limit to to literals and expression-less templates with raw values === their value.
233+
switch (node.type) {
234+
case "TemplateLiteral":
235+
if (node.expressions.length || node.quasis[0].value.raw !== node.quasis[0].value.cooked) {
236+
return null;
237+
}
238+
break;
239+
240+
case "Literal":
241+
if (typeof node.value === "string" && node.value !== node.raw.slice(1, -1)) {
242+
return null;
243+
}
244+
break;
245+
246+
default:
247+
return null;
248+
}
249+
250+
return {
251+
start: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[0].start),
252+
end: sourceCode.getLocFromIndex(node.range[0] + 1 + chars.at(-1).end)
253+
};
254+
}
255+
256+
/**
257+
* Finds the report loc(s) for a range of matches.
258+
* @param {Character[][]} matches Characters that should trigger a report.
259+
* @param {Node} node The node to report.
260+
* @returns {Object | null} Node loc(s) for context.report.
261+
*/
262+
function getNodeReportLocations(matches, node) {
263+
const locs = [];
264+
265+
for (const chars of matches) {
266+
const loc = generateReportLocation(chars, node);
267+
268+
// If a report can't match to a range, don't report any others
269+
if (!loc) {
270+
return [node.loc];
271+
}
272+
273+
locs.push(loc);
274+
}
275+
276+
return locs;
277+
}
278+
184279
/**
185280
* Verify a given regular expression.
186281
* @param {Node} node The node to report.
@@ -208,21 +303,26 @@ module.exports = {
208303
return;
209304
}
210305

211-
const foundKinds = new Set();
306+
const foundKindMatches = new Map();
212307

213308
visitRegExpAST(patternNode, {
214309
onCharacterClassEnter(ccNode) {
215310
for (const chars of iterateCharacterSequence(ccNode.elements)) {
216311
for (const kind of kinds) {
217-
if (hasCharacterSequence[kind](chars)) {
218-
foundKinds.add(kind);
312+
const matches = accumulate(chars, characterSequenceIndexFilters[kind]);
313+
314+
if (foundKindMatches.has(kind)) {
315+
foundKindMatches.get(kind).push(...matches);
316+
} else {
317+
foundKindMatches.set(kind, matches);
219318
}
319+
220320
}
221321
}
222322
}
223323
});
224324

225-
for (const kind of foundKinds) {
325+
for (const [kind, matches] of foundKindMatches) {
226326
let suggest;
227327

228328
if (kind === "surrogatePairWithoutUFlag") {
@@ -232,11 +332,27 @@ module.exports = {
232332
}];
233333
}
234334

235-
context.report({
236-
node,
237-
messageId: kind,
238-
suggest
239-
});
335+
const locs = getNodeReportLocations(matches, node);
336+
337+
// Grapheme zero-width joiners (e.g. in 👨‍👩‍👦) visually show as one emoji
338+
if (kind === "zwj" && locs.length > 1) {
339+
context.report({
340+
loc: {
341+
start: locs[0].start,
342+
end: locs[1].end
343+
},
344+
messageId: kind,
345+
suggest
346+
});
347+
} else {
348+
for (const loc of locs) {
349+
context.report({
350+
loc,
351+
messageId: kind,
352+
suggest
353+
});
354+
}
355+
}
240356
}
241357
}
242358

@@ -267,7 +383,7 @@ module.exports = {
267383
const flags = getStringIfConstant(flagsNode, scope);
268384

269385
if (typeof pattern === "string") {
270-
verify(refNode, pattern, flags || "", fixer => {
386+
verify(patternNode, pattern, flags || "", fixer => {
271387

272388
if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) {
273389
return null;

‎tests/lib/rules/no-misleading-character-class.js

+591-82
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ ruleTester.run("no-misleading-character-class", rule, {
6666
"var r = /[\\u200D]/u",
6767

6868
// don't report and don't crash on invalid regex
69+
"new RegExp('[Á] [ ');",
6970
"var r = new RegExp('[Á] [ ');",
7071
"var r = RegExp('{ [Á]', 'u');",
7172
{ code: "var r = new globalThis.RegExp('[Á] [ ');", languageOptions: { ecmaVersion: 2020 } },
@@ -84,21 +85,75 @@ ruleTester.run("no-misleading-character-class", rule, {
8485
{
8586
code: "var r = /[👍]/",
8687
errors: [{
88+
column: 11,
89+
endColumn: 13,
90+
line: 1,
8791
messageId: "surrogatePairWithoutUFlag",
8892
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }]
8993
}]
9094
},
9195
{
9296
code: "var r = /[\\uD83D\\uDC4D]/",
9397
errors: [{
98+
column: 11,
99+
endColumn: 23,
94100
messageId: "surrogatePairWithoutUFlag",
95101
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }]
96102
}]
97103
},
104+
{
105+
code: "var r = /[\\uD83D\\uDC4D-\\uffff]/",
106+
languageOptions: { ecmaVersion: 3, sourceType: "script" },
107+
errors: [{
108+
column: 11,
109+
endColumn: 23,
110+
messageId: "surrogatePairWithoutUFlag",
111+
suggestions: null // pattern would be invalid with the 'u' flag
112+
}]
113+
},
98114
{
99115
code: "var r = /[👍]/",
100116
languageOptions: { ecmaVersion: 3, sourceType: "script" },
101117
errors: [{
118+
column: 11,
119+
endColumn: 13,
120+
messageId: "surrogatePairWithoutUFlag",
121+
suggestions: null // pattern would be invalid with the 'u' flag
122+
}]
123+
},
124+
{
125+
code: "var r = /before[\\uD83D\\uDC4D]after/",
126+
errors: [{
127+
column: 17,
128+
endColumn: 29,
129+
messageId: "surrogatePairWithoutUFlag",
130+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }]
131+
}]
132+
},
133+
{
134+
code: "var r = /[before\\uD83D\\uDC4Dafter]/",
135+
errors: [{
136+
column: 17,
137+
endColumn: 29,
138+
messageId: "surrogatePairWithoutUFlag",
139+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }]
140+
}]
141+
},
142+
{
143+
code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/",
144+
errors: [{
145+
column: 17,
146+
endColumn: 29,
147+
messageId: "surrogatePairWithoutUFlag",
148+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }]
149+
}]
150+
},
151+
{
152+
code: "var r = /[👍]/",
153+
languageOptions: { ecmaVersion: 3, sourceType: "script" },
154+
errors: [{
155+
column: 11,
156+
endColumn: 13,
102157
messageId: "surrogatePairWithoutUFlag",
103158
suggestions: null // ecmaVersion doesn't support the 'u' flag
104159
}]
@@ -107,13 +162,26 @@ ruleTester.run("no-misleading-character-class", rule, {
107162
code: "var r = /[👍]/",
108163
languageOptions: { ecmaVersion: 5, sourceType: "script" },
109164
errors: [{
165+
column: 11,
166+
endColumn: 13,
110167
messageId: "surrogatePairWithoutUFlag",
111168
suggestions: null // ecmaVersion doesn't support the 'u' flag
112169
}]
113170
},
114171
{
115172
code: "var r = /[👍]\\a/",
116173
errors: [{
174+
column: 11,
175+
endColumn: 13,
176+
messageId: "surrogatePairWithoutUFlag",
177+
suggestions: null // pattern would be invalid with the 'u' flag
178+
}]
179+
},
180+
{
181+
code: "var r = /\\a[👍]\\a/",
182+
errors: [{
183+
column: 13,
184+
endColumn: 15,
117185
messageId: "surrogatePairWithoutUFlag",
118186
suggestions: null // pattern would be invalid with the 'u' flag
119187
}]
@@ -122,6 +190,8 @@ ruleTester.run("no-misleading-character-class", rule, {
122190
code: "var r = /(?<=[👍])/",
123191
languageOptions: { ecmaVersion: 9 },
124192
errors: [{
193+
column: 15,
194+
endColumn: 17,
125195
messageId: "surrogatePairWithoutUFlag",
126196
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }]
127197
}]
@@ -130,139 +200,211 @@ ruleTester.run("no-misleading-character-class", rule, {
130200
code: "var r = /(?<=[👍])/",
131201
languageOptions: { ecmaVersion: 2018 },
132202
errors: [{
203+
column: 15,
204+
endColumn: 17,
133205
messageId: "surrogatePairWithoutUFlag",
134206
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }]
135207
}]
136208
},
137209
{
138210
code: "var r = /[Á]/",
139211
errors: [{
212+
column: 11,
213+
endColumn: 13,
140214
messageId: "combiningClass",
141215
suggestions: null
142216
}]
143217
},
144218
{
145219
code: "var r = /[Á]/u",
146220
errors: [{
221+
column: 11,
222+
endColumn: 13,
147223
messageId: "combiningClass",
148224
suggestions: null
149225
}]
150226
},
151227
{
152228
code: "var r = /[\\u0041\\u0301]/",
153229
errors: [{
230+
column: 11,
231+
endColumn: 23,
154232
messageId: "combiningClass",
155233
suggestions: null
156234
}]
157235
},
158236
{
159237
code: "var r = /[\\u0041\\u0301]/u",
160238
errors: [{
239+
column: 11,
240+
endColumn: 23,
161241
messageId: "combiningClass",
162242
suggestions: null
163243
}]
164244
},
165245
{
166246
code: "var r = /[\\u{41}\\u{301}]/u",
167247
errors: [{
248+
column: 11,
249+
endColumn: 24,
168250
messageId: "combiningClass",
169251
suggestions: null
170252
}]
171253
},
172254
{
173255
code: "var r = /[❇️]/",
174256
errors: [{
257+
column: 11,
258+
endColumn: 13,
175259
messageId: "combiningClass",
176260
suggestions: null
177261
}]
178262
},
179263
{
180264
code: "var r = /[❇️]/u",
181265
errors: [{
266+
column: 11,
267+
endColumn: 13,
182268
messageId: "combiningClass",
183269
suggestions: null
184270
}]
185271
},
186272
{
187273
code: "var r = /[\\u2747\\uFE0F]/",
188274
errors: [{
275+
column: 11,
276+
endColumn: 23,
189277
messageId: "combiningClass",
190278
suggestions: null
191279
}]
192280
},
193281
{
194282
code: "var r = /[\\u2747\\uFE0F]/u",
195283
errors: [{
284+
column: 11,
285+
endColumn: 23,
196286
messageId: "combiningClass",
197287
suggestions: null
198288
}]
199289
},
200290
{
201291
code: "var r = /[\\u{2747}\\u{FE0F}]/u",
202292
errors: [{
293+
column: 11,
294+
endColumn: 27,
203295
messageId: "combiningClass",
204296
suggestions: null
205297
}]
206298
},
207299
{
208300
code: "var r = /[👶🏻]/",
209-
errors: [{
210-
messageId: "surrogatePairWithoutUFlag",
211-
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }]
212-
}]
301+
errors: [
302+
{
303+
column: 11,
304+
endColumn: 13,
305+
messageId: "surrogatePairWithoutUFlag",
306+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }]
307+
},
308+
{
309+
column: 13,
310+
endColumn: 15,
311+
messageId: "surrogatePairWithoutUFlag",
312+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }]
313+
}
314+
]
213315
},
214316
{
215317
code: "var r = /[👶🏻]/u",
216318
errors: [{
319+
column: 11,
320+
endColumn: 15,
217321
messageId: "emojiModifier",
218322
suggestions: null
219323
}]
220324
},
325+
{
326+
code: "var r = /[a\\uD83C\\uDFFB]/u",
327+
errors: [{
328+
column: 11,
329+
endColumn: 24,
330+
messageId: "emojiModifier"
331+
}]
332+
},
221333
{
222334
code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u",
223335
errors: [{
336+
column: 11,
337+
endColumn: 35,
224338
messageId: "emojiModifier",
225339
suggestions: null
226340
}]
227341
},
228342
{
229343
code: "var r = /[\\u{1F476}\\u{1F3FB}]/u",
230344
errors: [{
345+
column: 11,
346+
endColumn: 29,
231347
messageId: "emojiModifier",
232348
suggestions: null
233349
}]
234350
},
235351
{
236352
code: "var r = /[🇯🇵]/",
237-
errors: [{
238-
messageId: "surrogatePairWithoutUFlag",
239-
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }]
240-
}]
353+
errors: [
354+
{
355+
column: 11,
356+
endColumn: 13,
357+
messageId: "surrogatePairWithoutUFlag",
358+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }]
359+
},
360+
{
361+
column: 13,
362+
endColumn: 15,
363+
messageId: "surrogatePairWithoutUFlag",
364+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }]
365+
}
366+
]
241367
},
242368
{
243369
code: "var r = /[🇯🇵]/i",
244-
errors: [{
245-
messageId: "surrogatePairWithoutUFlag",
246-
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }]
247-
}]
370+
errors: [
371+
{
372+
column: 11,
373+
endColumn: 13,
374+
messageId: "surrogatePairWithoutUFlag",
375+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }]
376+
},
377+
{
378+
column: 13,
379+
endColumn: 15,
380+
messageId: "surrogatePairWithoutUFlag",
381+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }]
382+
}
383+
]
248384
},
249385
{
250386
code: "var r = /[🇯🇵]/u",
251387
errors: [{
388+
column: 11,
389+
endColumn: 15,
252390
messageId: "regionalIndicatorSymbol",
253391
suggestions: null
254392
}]
255393
},
256394
{
257395
code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u",
258396
errors: [{
397+
column: 11,
398+
endColumn: 35,
259399
messageId: "regionalIndicatorSymbol",
260400
suggestions: null
261401
}]
262402
},
263403
{
264404
code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u",
265405
errors: [{
406+
column: 11,
407+
endColumn: 29,
266408
messageId: "regionalIndicatorSymbol",
267409
suggestions: null
268410
}]
@@ -271,77 +413,205 @@ ruleTester.run("no-misleading-character-class", rule, {
271413
code: "var r = /[👨‍👩‍👦]/",
272414
errors: [
273415
{
416+
column: 11,
417+
endColumn: 13,
274418
messageId: "surrogatePairWithoutUFlag",
275419
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }]
276420
},
277421
{
422+
column: 12,
423+
endColumn: 18,
278424
messageId: "zwj",
279425
suggestions: null
426+
},
427+
{
428+
column: 14,
429+
endColumn: 16,
430+
messageId: "surrogatePairWithoutUFlag",
431+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }]
432+
},
433+
{
434+
column: 17,
435+
endColumn: 19,
436+
messageId: "surrogatePairWithoutUFlag",
437+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }]
280438
}
281439
]
282440
},
283441
{
284442
code: "var r = /[👨‍👩‍👦]/u",
285-
errors: [{
286-
messageId: "zwj",
287-
suggestions: null
288-
}]
443+
errors: [
444+
{
445+
column: 11,
446+
endColumn: 19,
447+
messageId: "zwj",
448+
suggestions: null
449+
}
450+
]
289451
},
290452
{
291453
code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u",
292-
errors: [{
293-
messageId: "zwj",
294-
suggestions: null
295-
}]
454+
errors: [
455+
{
456+
column: 11,
457+
endColumn: 59,
458+
messageId: "zwj",
459+
suggestions: null
460+
}
461+
]
296462
},
297463
{
298464
code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u",
299-
errors: [{
300-
messageId: "zwj",
301-
suggestions: null
302-
}]
465+
errors: [
466+
{
467+
column: 11,
468+
endColumn: 54,
469+
messageId: "zwj",
470+
suggestions: null
471+
}
472+
]
303473
},
304474

305475
// RegExp constructors.
476+
{
477+
code: String.raw`var r = RegExp("[👍]", "")`,
478+
errors: [{
479+
column: 18,
480+
endColumn: 20,
481+
messageId: "surrogatePairWithoutUFlag",
482+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[👍]", "u")` }]
483+
}]
484+
},
306485
{
307486
code: String.raw`var r = new RegExp("[👍]", "")`,
308487
errors: [{
488+
column: 22,
489+
endColumn: 24,
309490
messageId: "surrogatePairWithoutUFlag",
310491
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👍]", "u")` }]
311492
}]
312493
},
313494
{
314495
code: "var r = new RegExp('[👍]', ``)",
315496
errors: [{
497+
column: 22,
498+
endColumn: 24,
316499
messageId: "surrogatePairWithoutUFlag",
317500
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[👍]', `u`)" }]
318501
}]
319502
},
503+
{
504+
code: `var r = new RegExp(\`
505+
[👍]\`)`,
506+
errors: [{
507+
line: 2,
508+
endLine: 2,
509+
column: 18,
510+
endColumn: 20,
511+
messageId: "surrogatePairWithoutUFlag",
512+
suggestions: [{
513+
messageId: "suggestUnicodeFlag", output: `var r = new RegExp(\`
514+
[👍]\`, "u")`
515+
}]
516+
}]
517+
},
518+
{
519+
code: `var r = new RegExp(\`
520+
[❇️]\`)`,
521+
errors: [{
522+
column: 18,
523+
endColumn: 20,
524+
messageId: "combiningClass",
525+
suggestions: null
526+
}]
527+
},
320528
{
321529
code: String.raw`var r = new RegExp("[👍]", flags)`,
322530
errors: [{
531+
column: 22,
532+
endColumn: 24,
323533
messageId: "surrogatePairWithoutUFlag",
324534
suggestions: null
325535
}]
326536
},
327537
{
328538
code: String.raw`const flags = ""; var r = new RegExp("[👍]", flags)`,
329539
errors: [{
540+
column: 40,
541+
endColumn: 42,
330542
messageId: "surrogatePairWithoutUFlag",
331543
suggestions: null
332544
}]
333545
},
334546
{
335-
code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`,
547+
code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`,
336548
errors: [{
549+
column: 16,
550+
endColumn: 34,
337551
messageId: "surrogatePairWithoutUFlag",
338-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }]
552+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }]
553+
}]
554+
},
555+
{
556+
code: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "")`,
557+
errors: [{
558+
column: 16,
559+
endColumn: 45,
560+
messageId: "surrogatePairWithoutUFlag",
561+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "u")` }]
562+
}]
563+
},
564+
{
565+
code: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "")`,
566+
errors: [{
567+
column: 16,
568+
endColumn: 45,
569+
messageId: "surrogatePairWithoutUFlag",
570+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }]
571+
}]
572+
},
573+
{
574+
code: String.raw`var r = RegExp("\t\t\t👍[👍]")`,
575+
errors: [{
576+
column: 16,
577+
endColumn: 30,
578+
messageId: "surrogatePairWithoutUFlag",
579+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("\t\t\t👍[👍]", "u")` }]
580+
}]
581+
},
582+
{
583+
code: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]")`,
584+
errors: [{
585+
column: 20,
586+
endColumn: 44,
587+
messageId: "surrogatePairWithoutUFlag",
588+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]", "u")` }]
589+
}]
590+
},
591+
{
592+
code: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]")`,
593+
errors: [{
594+
column: 20,
595+
endColumn: 42,
596+
messageId: "surrogatePairWithoutUFlag",
597+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]", "u")` }]
598+
}]
599+
},
600+
{
601+
code: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]")`,
602+
errors: [{
603+
column: 20,
604+
endColumn: 42,
605+
messageId: "surrogatePairWithoutUFlag",
606+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]", "u")` }]
339607
}]
340608
},
341609
{
342610
code: String.raw`var r = new RegExp("[👍]", "")`,
343611
languageOptions: { ecmaVersion: 3, sourceType: "script" },
344612
errors: [{
613+
column: 22,
614+
endColumn: 24,
345615
messageId: "surrogatePairWithoutUFlag",
346616
suggestions: null // ecmaVersion doesn't support the 'u' flag
347617
}]
@@ -350,13 +620,17 @@ ruleTester.run("no-misleading-character-class", rule, {
350620
code: String.raw`var r = new RegExp("[👍]", "")`,
351621
languageOptions: { ecmaVersion: 5, sourceType: "script" },
352622
errors: [{
623+
column: 22,
624+
endColumn: 24,
353625
messageId: "surrogatePairWithoutUFlag",
354626
suggestions: null // ecmaVersion doesn't support the 'u' flag
355627
}]
356628
},
357629
{
358630
code: String.raw`var r = new RegExp("[👍]\\a", "")`,
359631
errors: [{
632+
column: 20,
633+
endColumn: 29,
360634
messageId: "surrogatePairWithoutUFlag",
361635
suggestions: null // pattern would be invalid with the 'u' flag
362636
}]
@@ -365,6 +639,8 @@ ruleTester.run("no-misleading-character-class", rule, {
365639
code: String.raw`var r = new RegExp("/(?<=[👍])", "")`,
366640
languageOptions: { ecmaVersion: 9 },
367641
errors: [{
642+
column: 27,
643+
endColumn: 29,
368644
messageId: "surrogatePairWithoutUFlag",
369645
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }]
370646
}]
@@ -373,190 +649,371 @@ ruleTester.run("no-misleading-character-class", rule, {
373649
code: String.raw`var r = new RegExp("/(?<=[👍])", "")`,
374650
languageOptions: { ecmaVersion: 2018 },
375651
errors: [{
652+
column: 27,
653+
endColumn: 29,
376654
messageId: "surrogatePairWithoutUFlag",
377655
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }]
378656
}]
379657
},
380658
{
381659
code: String.raw`var r = new RegExp("[Á]", "")`,
382660
errors: [{
661+
column: 22,
662+
endColumn: 24,
383663
messageId: "combiningClass",
384664
suggestions: null
385665
}]
386666
},
387667
{
388668
code: String.raw`var r = new RegExp("[Á]", "u")`,
389669
errors: [{
670+
column: 22,
671+
endColumn: 24,
390672
messageId: "combiningClass",
391673
suggestions: null
392674
}]
393675
},
394676
{
395677
code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`,
396678
errors: [{
679+
column: 20,
680+
endColumn: 38,
397681
messageId: "combiningClass",
398682
suggestions: null
399683
}]
400684
},
401685
{
402686
code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`,
403687
errors: [{
688+
column: 20,
689+
endColumn: 38,
404690
messageId: "combiningClass",
405691
suggestions: null
406692
}]
407693
},
408694
{
409695
code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`,
410696
errors: [{
697+
column: 20,
698+
endColumn: 39,
411699
messageId: "combiningClass",
412700
suggestions: null
413701
}]
414702
},
415703
{
416704
code: String.raw`var r = new RegExp("[❇️]", "")`,
417705
errors: [{
706+
column: 22,
707+
endColumn: 24,
418708
messageId: "combiningClass",
419709
suggestions: null
420710
}]
421711
},
422712
{
423713
code: String.raw`var r = new RegExp("[❇️]", "u")`,
424714
errors: [{
715+
column: 22,
716+
endColumn: 24,
717+
messageId: "combiningClass",
718+
suggestions: null
719+
}]
720+
},
721+
{
722+
code: String.raw`new RegExp("[ \\ufe0f]", "")`,
723+
errors: [{
724+
column: 12,
725+
endColumn: 24,
425726
messageId: "combiningClass",
426727
suggestions: null
427728
}]
428729
},
730+
{
731+
code: String.raw`new RegExp("[ \\ufe0f]", "u")`,
732+
errors: [{
733+
column: 12,
734+
endColumn: 24,
735+
messageId: "combiningClass",
736+
suggestions: null
737+
}]
738+
},
739+
{
740+
code: String.raw`new RegExp("[ \\ufe0f][ \\ufe0f]")`,
741+
errors: [
742+
{
743+
column: 12,
744+
endColumn: 34,
745+
messageId: "combiningClass",
746+
suggestions: null
747+
}
748+
]
749+
},
429750
{
430751
code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`,
431752
errors: [{
753+
column: 20,
754+
endColumn: 38,
432755
messageId: "combiningClass",
433756
suggestions: null
434757
}]
435758
},
436759
{
437760
code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`,
438761
errors: [{
762+
column: 20,
763+
endColumn: 38,
439764
messageId: "combiningClass",
440765
suggestions: null
441766
}]
442767
},
443768
{
444769
code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`,
445770
errors: [{
771+
column: 20,
772+
endColumn: 42,
446773
messageId: "combiningClass",
447774
suggestions: null
448775
}]
449776
},
450777
{
451778
code: String.raw`var r = new RegExp("[👶🏻]", "")`,
452-
errors: [{
453-
messageId: "surrogatePairWithoutUFlag",
454-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }]
455-
}]
779+
errors: [
780+
{
781+
column: 22,
782+
endColumn: 24,
783+
messageId: "surrogatePairWithoutUFlag",
784+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }]
785+
},
786+
{
787+
column: 24,
788+
endColumn: 26,
789+
messageId: "surrogatePairWithoutUFlag",
790+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }]
791+
}
792+
]
456793
},
457794
{
458795
code: String.raw`var r = new RegExp("[👶🏻]", "u")`,
459796
errors: [{
797+
column: 22,
798+
endColumn: 26,
460799
messageId: "emojiModifier",
461800
suggestions: null
462801
}]
463802
},
464803
{
465804
code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`,
466805
errors: [{
806+
column: 20,
807+
endColumn: 52,
467808
messageId: "emojiModifier",
468809
suggestions: null
469810
}]
470811
},
471812
{
472813
code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`,
473814
errors: [{
815+
column: 20,
816+
endColumn: 44,
474817
messageId: "emojiModifier",
475818
suggestions: null
476819
}]
477820
},
478821
{
479-
code: String.raw`var r = new RegExp("[🇯🇵]", "")`,
822+
code: "var r = RegExp(`\t\t\t👍[👍]`)",
480823
errors: [{
824+
column: 23,
825+
endColumn: 25,
481826
messageId: "surrogatePairWithoutUFlag",
482-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
827+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = RegExp(`\t\t\t👍[👍]`, \"u\")" }]
483828
}]
484829
},
485830
{
486-
code: String.raw`var r = new RegExp("[🇯🇵]", "i")`,
831+
code: "var r = RegExp(`\\t\\t\\t👍[👍]`)",
487832
errors: [{
833+
column: 16,
834+
endColumn: 30,
488835
messageId: "surrogatePairWithoutUFlag",
489-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }]
836+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = RegExp(`\\t\\t\\t👍[👍]`, \"u\")" }]
490837
}]
491838
},
839+
{
840+
code: String.raw`var r = new RegExp("[🇯🇵]", "")`,
841+
errors: [
842+
{
843+
column: 22,
844+
endColumn: 24,
845+
messageId: "surrogatePairWithoutUFlag",
846+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
847+
},
848+
{
849+
column: 24,
850+
endColumn: 26,
851+
messageId: "surrogatePairWithoutUFlag",
852+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
853+
}
854+
]
855+
},
856+
{
857+
code: String.raw`var r = new RegExp("[🇯🇵]", "i")`,
858+
errors: [
859+
{
860+
column: 22,
861+
endColumn: 24,
862+
messageId: "surrogatePairWithoutUFlag",
863+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }]
864+
},
865+
{
866+
column: 24,
867+
endColumn: 26,
868+
messageId: "surrogatePairWithoutUFlag",
869+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }]
870+
}
871+
]
872+
},
492873
{
493874
code: "var r = new RegExp('[🇯🇵]', `i`)",
494-
errors: [{
495-
messageId: "surrogatePairWithoutUFlag",
496-
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }]
497-
}]
875+
errors: [
876+
{
877+
column: 22,
878+
endColumn: 24,
879+
messageId: "surrogatePairWithoutUFlag",
880+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }]
881+
},
882+
{
883+
column: 24,
884+
endColumn: 26,
885+
messageId: "surrogatePairWithoutUFlag",
886+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }]
887+
}
888+
]
498889
},
499890
{
500891
code: "var r = new RegExp('[🇯🇵]', `${foo}`)",
501-
errors: [{
502-
messageId: "surrogatePairWithoutUFlag",
503-
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }]
504-
}]
892+
errors: [
893+
{
894+
column: 22,
895+
endColumn: 24,
896+
messageId: "surrogatePairWithoutUFlag",
897+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }]
898+
},
899+
{
900+
column: 24,
901+
endColumn: 26,
902+
messageId: "surrogatePairWithoutUFlag",
903+
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }]
904+
}
905+
]
505906
},
506907
{
507908
code: String.raw`var r = new RegExp("[🇯🇵]")`,
508-
errors: [{
509-
messageId: "surrogatePairWithoutUFlag",
510-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
511-
}]
909+
errors: [
910+
{
911+
column: 22,
912+
endColumn: 24,
913+
messageId: "surrogatePairWithoutUFlag",
914+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
915+
},
916+
{
917+
column: 24,
918+
endColumn: 26,
919+
messageId: "surrogatePairWithoutUFlag",
920+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
921+
}
922+
]
512923
},
513924
{
514925
code: String.raw`var r = new RegExp("[🇯🇵]",)`,
515926
languageOptions: { ecmaVersion: 2017 },
516-
errors: [{
517-
messageId: "surrogatePairWithoutUFlag",
518-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }]
519-
}]
927+
errors: [
928+
{
929+
column: 22,
930+
endColumn: 24,
931+
messageId: "surrogatePairWithoutUFlag",
932+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }]
933+
},
934+
{
935+
column: 24,
936+
endColumn: 26,
937+
messageId: "surrogatePairWithoutUFlag",
938+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }]
939+
}
940+
]
520941
},
521942
{
522943
code: String.raw`var r = new RegExp(("[🇯🇵]"))`,
523-
errors: [{
524-
messageId: "surrogatePairWithoutUFlag",
525-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }]
526-
}]
944+
errors: [
945+
{
946+
column: 23,
947+
endColumn: 25,
948+
messageId: "surrogatePairWithoutUFlag",
949+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }]
950+
},
951+
{
952+
column: 25,
953+
endColumn: 27,
954+
messageId: "surrogatePairWithoutUFlag",
955+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }]
956+
}
957+
]
527958
},
528959
{
529960
code: String.raw`var r = new RegExp((("[🇯🇵]")))`,
530-
errors: [{
531-
messageId: "surrogatePairWithoutUFlag",
532-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }]
533-
}]
961+
errors: [
962+
{
963+
column: 24,
964+
endColumn: 26,
965+
messageId: "surrogatePairWithoutUFlag",
966+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }]
967+
},
968+
{
969+
column: 26,
970+
endColumn: 28,
971+
messageId: "surrogatePairWithoutUFlag",
972+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }]
973+
}
974+
]
534975
},
535976
{
536977
code: String.raw`var r = new RegExp(("[🇯🇵]"),)`,
537978
languageOptions: { ecmaVersion: 2017 },
538-
errors: [{
539-
messageId: "surrogatePairWithoutUFlag",
540-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }]
541-
}]
979+
errors: [
980+
{
981+
column: 23,
982+
endColumn: 25,
983+
messageId: "surrogatePairWithoutUFlag",
984+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }]
985+
},
986+
{
987+
column: 25,
988+
endColumn: 27,
989+
messageId: "surrogatePairWithoutUFlag",
990+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }]
991+
}
992+
]
542993
},
543994
{
544995
code: String.raw`var r = new RegExp("[🇯🇵]", "u")`,
545996
errors: [{
997+
column: 22,
998+
endColumn: 26,
546999
messageId: "regionalIndicatorSymbol",
5471000
suggestions: null
5481001
}]
5491002
},
5501003
{
5511004
code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`,
5521005
errors: [{
1006+
column: 20,
1007+
endColumn: 52,
5531008
messageId: "regionalIndicatorSymbol",
5541009
suggestions: null
5551010
}]
5561011
},
5571012
{
5581013
code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`,
5591014
errors: [{
1015+
column: 20,
1016+
endColumn: 44,
5601017
messageId: "regionalIndicatorSymbol",
5611018
suggestions: null
5621019
}]
@@ -565,40 +1022,70 @@ ruleTester.run("no-misleading-character-class", rule, {
5651022
code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "")`,
5661023
errors: [
5671024
{
1025+
column: 22,
1026+
endColumn: 24,
5681027
messageId: "surrogatePairWithoutUFlag",
5691028
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }]
5701029
},
5711030
{
1031+
column: 23,
1032+
endColumn: 29,
5721033
messageId: "zwj",
5731034
suggestions: null
1035+
},
1036+
{
1037+
column: 25,
1038+
endColumn: 27,
1039+
messageId: "surrogatePairWithoutUFlag",
1040+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }]
1041+
},
1042+
{
1043+
column: 28,
1044+
endColumn: 30,
1045+
messageId: "surrogatePairWithoutUFlag",
1046+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }]
5741047
}
5751048
]
5761049
},
5771050
{
5781051
code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")`,
579-
errors: [{
580-
messageId: "zwj",
581-
suggestions: null
582-
}]
1052+
errors: [
1053+
{
1054+
column: 22,
1055+
endColumn: 30,
1056+
messageId: "zwj",
1057+
suggestions: null
1058+
}
1059+
]
5831060
},
5841061
{
5851062
code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`,
586-
errors: [{
587-
messageId: "zwj",
588-
suggestions: null
589-
}]
1063+
errors: [
1064+
{
1065+
column: 20,
1066+
endColumn: 80,
1067+
messageId: "zwj",
1068+
suggestions: null
1069+
}
1070+
]
5901071
},
5911072
{
5921073
code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`,
593-
errors: [{
594-
messageId: "zwj",
595-
suggestions: null
596-
}]
1074+
errors: [
1075+
{
1076+
column: 20,
1077+
endColumn: 72,
1078+
messageId: "zwj",
1079+
suggestions: null
1080+
}
1081+
]
5971082
},
5981083
{
5991084
code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`,
6001085
languageOptions: { ecmaVersion: 2020 },
6011086
errors: [{
1087+
column: 33,
1088+
endColumn: 35,
6021089
messageId: "combiningClass",
6031090
suggestions: null
6041091
}]
@@ -607,25 +1094,41 @@ ruleTester.run("no-misleading-character-class", rule, {
6071094
code: String.raw`var r = new globalThis.RegExp("[👶🏻]", "u")`,
6081095
languageOptions: { ecmaVersion: 2020 },
6091096
errors: [{
1097+
column: 33,
1098+
endColumn: 37,
6101099
messageId: "emojiModifier",
6111100
suggestions: null
6121101
}]
6131102
},
6141103
{
6151104
code: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "")`,
6161105
languageOptions: { ecmaVersion: 2020 },
617-
errors: [{
618-
messageId: "surrogatePairWithoutUFlag",
619-
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }]
620-
}]
1106+
errors: [
1107+
{
1108+
column: 33,
1109+
endColumn: 35,
1110+
messageId: "surrogatePairWithoutUFlag",
1111+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }]
1112+
},
1113+
{
1114+
column: 35,
1115+
endColumn: 37,
1116+
messageId: "surrogatePairWithoutUFlag",
1117+
suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }]
1118+
}
1119+
]
6211120
},
6221121
{
6231122
code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`,
6241123
languageOptions: { ecmaVersion: 2020 },
625-
errors: [{
626-
messageId: "zwj",
627-
suggestions: null
628-
}]
1124+
errors: [
1125+
{
1126+
column: 31,
1127+
endColumn: 83,
1128+
messageId: "zwj",
1129+
suggestions: null
1130+
}
1131+
]
6291132
},
6301133
{
6311134
code: String.raw`/[\ud83d\u{dc4d}]/u`,
@@ -662,6 +1165,8 @@ ruleTester.run("no-misleading-character-class", rule, {
6621165
code: "var r = /[[👶🏻]]/v",
6631166
languageOptions: { ecmaVersion: 2024 },
6641167
errors: [{
1168+
column: 12,
1169+
endColumn: 16,
6651170
messageId: "emojiModifier",
6661171
suggestions: null
6671172
}]
@@ -673,6 +1178,8 @@ ruleTester.run("no-misleading-character-class", rule, {
6731178
sourceType: "script"
6741179
},
6751180
errors: [{
1181+
column: 11,
1182+
endColumn: 13,
6761183
messageId: "surrogatePairWithoutUFlag",
6771184
suggestions: null // ecmaVersion doesn't support the 'u' flag
6781185
}]
@@ -683,6 +1190,8 @@ ruleTester.run("no-misleading-character-class", rule, {
6831190
ecmaVersion: 2015
6841191
},
6851192
errors: [{
1193+
column: 11,
1194+
endColumn: 13,
6861195
messageId: "surrogatePairWithoutUFlag",
6871196
suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }]
6881197
}]

0 commit comments

Comments
 (0)
Please sign in to comment.