Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make no-misleading-character-class report more granular errors #18082

Merged
merged 19 commits into from Mar 5, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 27 additions & 28 deletions lib/rules/utils/char-source.js
Expand Up @@ -32,6 +32,12 @@ class SourceReader {
this.source = source;
this.pos = 0;
}

read(offset = 0, length = 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some JSDoc here?

const start = offset + this.pos;

return this.source.slice(start, start + length);
}
}

const SIMPLE_ESCAPE_SEQUENCES =
Expand All @@ -44,11 +50,10 @@ const SIMPLE_ESCAPE_SEQUENCES =
* @returns {string} A code unit.
*/
function readHexSequence(reader, length) {
const { source, pos } = reader;
const str = source.slice(pos, pos + length);
const str = reader.read(0, length);
const charCode = parseInt(str, 16);

reader.pos = pos + length;
reader.pos += length;
return String.fromCharCode(charCode);
}

Expand All @@ -58,11 +63,10 @@ function readHexSequence(reader, length) {
* @returns {string} A code unit.
*/
function readUnicodeSequence(reader) {
const { source, pos } = reader;
const regExp = /\{(?<hexDigits>[\dA-Fa-f]+)\}/uy;

regExp.lastIndex = pos;
const match = regExp.exec(source);
regExp.lastIndex = reader.pos;
const match = regExp.exec(reader.source);

if (match) {
const codePoint = parseInt(match.groups.hexDigits, 16);
Expand All @@ -80,25 +84,23 @@ function readUnicodeSequence(reader) {
* @returns {string} A code unit.
*/
function readOctalSequence(reader, maxLength) {
const posAfterBackslash = reader.pos - 1;
const [octalStr] = reader.source.slice(posAfterBackslash, posAfterBackslash + maxLength).match(/^[0-7]+/u);
const [octalStr] = reader.read(-1, maxLength).match(/^[0-7]+/u);

reader.pos = posAfterBackslash + octalStr.length;
reader.pos += octalStr.length - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you refactored to include a read() method in SourceReader, it might make things clearer if you also created a advance() method that moves pos rather than augmenting the value like this. So this code would become:

Suggested change
reader.pos += octalStr.length - 1;
reader.advance(octalStr.length - 1);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea 👍🏻

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All done in b1cf05f.

const octal = parseInt(octalStr, 8);

return String.fromCharCode(octal);
}

/**
* Reads an escape sequence or line continuation.
* @param {SourceReader} reader The reader should be positioned after the backslash.
* @param {SourceReader} reader The reader should be positioned on the backslash.
* @returns {string} A string of zero, one or two code units.
*/
function readEscapeSequenceOrLineContinuation(reader) {
const { source, pos } = reader;
const char = source[pos];
const char = reader.read(1);

reader.pos = pos + 1;
reader.pos += 2;
const unitChar = SIMPLE_ESCAPE_SEQUENCES[char];

if (unitChar) {
Expand All @@ -110,8 +112,8 @@ function readEscapeSequenceOrLineContinuation(reader) {
case "u":
return readUnicodeSequence(reader);
case "\r":
if (source[pos + 1] === "\n") {
reader.pos = pos + 2;
if (reader.read() === "\n") {
reader.pos += 1;
}

// fallthrough
Expand Down Expand Up @@ -140,7 +142,7 @@ function readEscapeSequenceOrLineContinuation(reader) {
* @returns {Generator<CodeUnit>} Zero, one or two `CodeUnit` elements.
*/
function *mapEscapeSequenceOrLineContinuation(reader) {
const start = reader.pos++;
const start = reader.pos;
const str = readEscapeSequenceOrLineContinuation(reader);
const end = reader.pos;
const source = reader.source.slice(start, end);
Expand All @@ -165,23 +167,22 @@ function *mapEscapeSequenceOrLineContinuation(reader) {
*/
function parseStringLiteral(source) {
const reader = new SourceReader(source);
const quote = source[0];
const quote = reader.read();

reader.pos = 1;
const codeUnits = [];

for (;;) {
const { pos } = reader;
const char = source[pos];
const char = reader.read();

if (char === quote) {
break;
}
if (char === "\\") {
codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader));
} else {
reader.pos = pos + 1;
codeUnits.push(new CodeUnit(pos, char));
codeUnits.push(new CodeUnit(reader.pos, char));
reader.pos += 1;
}
}
return codeUnits;
Expand All @@ -199,25 +200,23 @@ function parseTemplateToken(source) {
const codeUnits = [];

for (;;) {
const { pos } = reader;
const char = source[pos];
const char = reader.read();

if (char === "`" || char === "$" && source[pos + 1] === "{") {
if (char === "`" || char === "$" && reader.read(1) === "{") {
break;
}
if (char === "\\") {
codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader));
} else {
let unitSource;

if (char === "\r" && source[pos + 1] === "\n") {
if (char === "\r" && reader.read(1) === "\n") {
unitSource = "\r\n";
reader.pos = pos + 2;
} else {
unitSource = char;
reader.pos = pos + 1;
}
codeUnits.push(new CodeUnit(pos, unitSource));
codeUnits.push(new CodeUnit(reader.pos, unitSource));
reader.pos += unitSource.length;
}
}
return codeUnits;
Expand Down