Skip to content

Commit

Permalink
Fix position of errors in template literals after newlines
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jul 26, 2022
1 parent 229cc50 commit 0d1b983
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 108 deletions.
101 changes: 81 additions & 20 deletions packages/babel-helper-string-parser/src/index.ts
Expand Up @@ -43,6 +43,7 @@ export type StringContentsErrorHandlers = EscapedCharErrorHandlers & {
initialPos: number,
initialLineStart: number,
initialCurLine: number,
isTemplate: boolean,
): void;
};

Expand All @@ -54,6 +55,7 @@ export function readStringContents(
curLine: number,
errors: StringContentsErrorHandlers,
) {
const isTemplate = type === "template";
const initialPos = pos;
const initialLineStart = lineStart;
const initialCurLine = curLine;
Expand All @@ -64,7 +66,12 @@ export function readStringContents(
const { length } = input;
for (;;) {
if (pos >= length) {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
errors.unterminated(
initialPos,
initialLineStart,
initialCurLine,
isTemplate,
);
out += input.slice(chunkStart, pos);
break;
}
Expand Down Expand Up @@ -115,7 +122,12 @@ export function readStringContents(
++curLine;
chunkStart = lineStart = pos;
} else {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
errors.unterminated(
initialPos,
initialLineStart,
initialCurLine,
isTemplate,
);
}
} else {
++pos;
Expand All @@ -142,12 +154,12 @@ function isStringEnd(
);
}

export type EscapedCharErrorHandlers = HexCharErrorHandlers &
type EscapedCharErrorHandlers = HexCharErrorHandlers &
CodePointErrorHandlers & {
strictNumericEscape(pos: number): void;
strictNumericEscape(pos: number, lineStart: number, curLine: number): void;
};

export function readEscapedChar(
function readEscapedChar(
input: string,
pos: number,
lineStart: number,
Expand All @@ -171,6 +183,8 @@ export function readEscapedChar(
({ code, pos } = readHexChar(
input,
pos,
lineStart,
curLine,
2,
false,
throwOnInvalid,
Expand All @@ -180,7 +194,14 @@ export function readEscapedChar(
}
case charCodes.lowercaseU: {
let code;
({ code, pos } = readCodePoint(input, pos, throwOnInvalid, errors));
({ code, pos } = readCodePoint(
input,
pos,
lineStart,
curLine,
throwOnInvalid,
errors,
));
return res(code === null ? null : String.fromCodePoint(code));
}
case charCodes.lowercaseT:
Expand Down Expand Up @@ -208,7 +229,7 @@ export function readEscapedChar(
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(pos - 1);
errors.strictNumericEscape(pos - 1, lineStart, curLine);
}
// fall through
default:
Expand All @@ -233,7 +254,7 @@ export function readEscapedChar(
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(startPos);
errors.strictNumericEscape(startPos, lineStart, curLine);
}
}

Expand All @@ -245,24 +266,36 @@ export function readEscapedChar(
}

type HexCharErrorHandlers = IntErrorHandlers & {
invalidEscapeSequence(pos: number, startPos: number): void;
invalidEscapeSequence(pos: number, lineStart: number, curLine: number): void;
};

// Used to read character escape sequences ('\x', '\u').
function readHexChar(
input: string,
pos: number,
lineStart: number,
curLine: number,
len: number,
forceLen: boolean,
throwOnInvalid: boolean,
errors: HexCharErrorHandlers,
) {
const initialPos = pos;
let n;
({ n, pos } = readInt(input, pos, 16, len, forceLen, false, errors));
({ n, pos } = readInt(
input,
pos,
lineStart,
curLine,
16,
len,
forceLen,
false,
errors,
));
if (n === null) {
if (throwOnInvalid) {
errors.invalidEscapeSequence(pos, initialPos);
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
} else {
pos = initialPos - 1;
}
Expand All @@ -271,16 +304,31 @@ function readHexChar(
}

export type IntErrorHandlers = {
numericSeparatorInEscapeSequence(pos: number): void;
unexpectedNumericSeparator(pos: number): void;
numericSeparatorInEscapeSequence(
pos: number,
lineStart: number,
curLine: number,
): void;
unexpectedNumericSeparator(
pos: number,
lineStart: number,
curLine: number,
): void;
// It can return "true" to indicate that the error was handled
// and the int parsing should continue.
invalidDigit(pos: number, radix: number): boolean;
invalidDigit(
pos: number,
lineStart: number,
curLine: number,
radix: number,
): boolean;
};

export function readInt(
input: string,
pos: number,
lineStart: number,
curLine: number,
radix: number,
len: number | undefined,
forceLen: boolean,
Expand Down Expand Up @@ -313,14 +361,14 @@ export function readInt(
const next = input.charCodeAt(pos + 1);

if (!allowNumSeparator) {
errors.numericSeparatorInEscapeSequence(pos);
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
} else if (
Number.isNaN(next) ||
!isAllowedSibling(next) ||
forbiddenSiblings.has(prev) ||
forbiddenSiblings.has(next)
) {
errors.unexpectedNumericSeparator(pos);
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
}

// Ignore this _ character
Expand All @@ -340,7 +388,7 @@ export function readInt(
if (val >= radix) {
// If we found a digit which is too big, errors.invalidDigit can return true to avoid
// breaking the loop (this is used for error recovery).
if (val <= 9 && errors.invalidDigit(pos, radix)) {
if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
val = 0;
} else if (forceLen) {
val = 0;
Expand All @@ -360,12 +408,14 @@ export function readInt(
}

export type CodePointErrorHandlers = HexCharErrorHandlers & {
invalidCodePoint(pos: number): void;
invalidCodePoint(pos: number, lineStart: number, curLine: number): void;
};

export function readCodePoint(
input: string,
pos: number,
lineStart: number,
curLine: number,
throwOnInvalid: boolean,
errors: CodePointErrorHandlers,
) {
Expand All @@ -377,6 +427,8 @@ export function readCodePoint(
({ code, pos } = readHexChar(
input,
pos,
lineStart,
curLine,
input.indexOf("}", pos) - pos,
true,
throwOnInvalid,
Expand All @@ -385,13 +437,22 @@ export function readCodePoint(
++pos;
if (code !== null && code > 0x10ffff) {
if (throwOnInvalid) {
errors.invalidCodePoint(pos);
errors.invalidCodePoint(pos, lineStart, curLine);
} else {
return { code: null, pos };
}
}
} else {
({ code, pos } = readHexChar(input, pos, 4, false, throwOnInvalid, errors));
({ code, pos } = readHexChar(
input,
pos,
lineStart,
curLine,
4,
false,
throwOnInvalid,
errors,
));
}
return { code, pos };
}

0 comments on commit 0d1b983

Please sign in to comment.