mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Improve Recovery of Unterminated Regular Expressions (#58289)
Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>
This commit is contained in:
@@ -31974,7 +31974,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
scanner.setScriptTarget(sourceFile.languageVersion);
|
||||
scanner.setLanguageVariant(sourceFile.languageVariant);
|
||||
scanner.setOnError((message, length, arg0) => {
|
||||
// emulate `parseErrorAtPosition` from parser.ts
|
||||
// For providing spelling suggestions
|
||||
const start = scanner!.getTokenEnd();
|
||||
if (message.category === DiagnosticCategory.Message && lastError && start === lastError.start && length === lastError.length) {
|
||||
const error = createDetachedDiagnostic(sourceFile.fileName, sourceFile.text, start, length, message, arg0);
|
||||
|
||||
@@ -62,7 +62,6 @@ import {
|
||||
DeleteExpression,
|
||||
Diagnostic,
|
||||
DiagnosticArguments,
|
||||
DiagnosticCategory,
|
||||
DiagnosticMessage,
|
||||
Diagnostics,
|
||||
DiagnosticWithDetachedLocation,
|
||||
@@ -2144,11 +2143,7 @@ namespace Parser {
|
||||
// Don't report another error if it would just be at the same position as the last error.
|
||||
const lastError = lastOrUndefined(parseDiagnostics);
|
||||
let result: DiagnosticWithDetachedLocation | undefined;
|
||||
if (message.category === DiagnosticCategory.Message && lastError && start === lastError.start && length === lastError.length) {
|
||||
result = createDetachedDiagnostic(fileName, sourceText, start, length, message, ...args);
|
||||
addRelatedInfo(lastError, result);
|
||||
}
|
||||
else if (!lastError || start !== lastError.start) {
|
||||
if (!lastError || start !== lastError.start) {
|
||||
result = createDetachedDiagnostic(fileName, sourceText, start, length, message, ...args);
|
||||
parseDiagnostics.push(result);
|
||||
}
|
||||
|
||||
+101
-71
@@ -1613,7 +1613,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
isRegularExpression && shouldEmitInvalidEscapeError && escapedValue >= 0xD800 && escapedValue <= 0xDBFF &&
|
||||
pos + 6 < end && text.substring(pos, pos + 2) === "\\u" && charCodeUnchecked(pos + 2) !== CharacterCodes.openBrace
|
||||
) {
|
||||
// For regular expressions in Unicode mode, \u HexLeadSurrogate \u HexTrailSurrogate is treated as a single character
|
||||
// For regular expressions in any Unicode mode, \u HexLeadSurrogate \u HexTrailSurrogate is treated as a single character
|
||||
// for the purpose of determining whether a character class range is out of order
|
||||
// https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence
|
||||
const nextStart = pos;
|
||||
@@ -2424,10 +2424,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
function reScanSlashToken(reportErrors?: boolean): SyntaxKind {
|
||||
if (token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) {
|
||||
// Quickly get to the end of regex such that we know the flags
|
||||
let p = tokenStart + 1;
|
||||
const startOfRegExpBody = tokenStart + 1;
|
||||
pos = startOfRegExpBody;
|
||||
let inEscape = false;
|
||||
// Although nested character classes are allowed in Unicode Sets mode,
|
||||
// an unescaped slash is nevertheless invalid even in a character class in Unicode mode.
|
||||
// an unescaped slash is nevertheless invalid even in a character class in any Unicode mode.
|
||||
// Additionally, parsing nested character classes will misinterpret regexes like `/[[]/`
|
||||
// as unterminated, consuming characters beyond the slash. (This even applies to `/[[]/v`,
|
||||
// which should be parsed as a well-terminated regex with an incomplete character class.)
|
||||
@@ -2436,16 +2437,9 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
while (true) {
|
||||
// If we reach the end of a file, or hit a newline, then this is an unterminated
|
||||
// regex. Report error and return what we have so far.
|
||||
if (p >= end) {
|
||||
const ch = charCodeChecked(pos);
|
||||
if (ch === CharacterCodes.EOF || isLineBreak(ch)) {
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_regular_expression_literal);
|
||||
break;
|
||||
}
|
||||
|
||||
const ch = charCodeUnchecked(p);
|
||||
if (isLineBreak(ch)) {
|
||||
tokenFlags |= TokenFlags.Unterminated;
|
||||
error(Diagnostics.Unterminated_regular_expression_literal);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2457,7 +2451,6 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
else if (ch === CharacterCodes.slash && !inCharacterClass) {
|
||||
// A slash within a character class is permissible,
|
||||
// but in general it signals the end of the regexp literal.
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
else if (ch === CharacterCodes.openBracket) {
|
||||
@@ -2469,47 +2462,89 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
else if (ch === CharacterCodes.closeBracket) {
|
||||
inCharacterClass = false;
|
||||
}
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
const isUnterminated = !!(tokenFlags & TokenFlags.Unterminated);
|
||||
const endOfBody = p - (isUnterminated ? 0 : 1);
|
||||
let regExpFlags = RegularExpressionFlags.None;
|
||||
while (p < end) {
|
||||
const ch = charCodeUnchecked(p);
|
||||
if (!isIdentifierPart(ch, languageVersion)) {
|
||||
break;
|
||||
const endOfRegExpBody = pos;
|
||||
if (tokenFlags & TokenFlags.Unterminated) {
|
||||
// Search for the nearest unbalanced bracket for better recovery. Since the expression is
|
||||
// invalid anyways, we take nested square brackets into consideration for the best guess.
|
||||
pos = startOfRegExpBody;
|
||||
inEscape = false;
|
||||
let characterClassDepth = 0;
|
||||
let inDecimalQuantifier = false;
|
||||
let groupDepth = 0;
|
||||
while (pos < endOfRegExpBody) {
|
||||
const ch = charCodeUnchecked(pos);
|
||||
if (inEscape) {
|
||||
inEscape = false;
|
||||
}
|
||||
else if (ch === CharacterCodes.backslash) {
|
||||
inEscape = true;
|
||||
}
|
||||
else if (ch === CharacterCodes.openBracket) {
|
||||
characterClassDepth++;
|
||||
}
|
||||
else if (ch === CharacterCodes.closeBracket && characterClassDepth) {
|
||||
characterClassDepth--;
|
||||
}
|
||||
else if (!characterClassDepth) {
|
||||
if (ch === CharacterCodes.openBrace) {
|
||||
inDecimalQuantifier = true;
|
||||
}
|
||||
else if (ch === CharacterCodes.closeBrace && inDecimalQuantifier) {
|
||||
inDecimalQuantifier = false;
|
||||
}
|
||||
else if (!inDecimalQuantifier) {
|
||||
if (ch === CharacterCodes.openParen) {
|
||||
groupDepth++;
|
||||
}
|
||||
else if (ch === CharacterCodes.closeParen && groupDepth) {
|
||||
groupDepth--;
|
||||
}
|
||||
else if (ch === CharacterCodes.closeParen || ch === CharacterCodes.closeBracket || ch === CharacterCodes.closeBrace) {
|
||||
// We encountered an unbalanced bracket outside a character class. Treat this position as the end of regex.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
// Whitespaces and semicolons at the end are not likely to be part of the regex
|
||||
while (isWhiteSpaceLike(charCodeChecked(pos - 1)) || charCodeChecked(pos - 1) === CharacterCodes.semicolon) pos--;
|
||||
error(Diagnostics.Unterminated_regular_expression_literal, tokenStart, pos - tokenStart);
|
||||
}
|
||||
else {
|
||||
// Consume the slash character
|
||||
pos++;
|
||||
let regExpFlags = RegularExpressionFlags.None;
|
||||
while (true) {
|
||||
const ch = codePointChecked(pos);
|
||||
if (ch === CharacterCodes.EOF || !isIdentifierPart(ch, languageVersion)) {
|
||||
break;
|
||||
}
|
||||
if (reportErrors) {
|
||||
const flag = characterToRegularExpressionFlag(String.fromCharCode(ch));
|
||||
if (flag === undefined) {
|
||||
error(Diagnostics.Unknown_regular_expression_flag, pos, 1);
|
||||
}
|
||||
else if (regExpFlags & flag) {
|
||||
error(Diagnostics.Duplicate_regular_expression_flag, pos, 1);
|
||||
}
|
||||
else if (((regExpFlags | flag) & RegularExpressionFlags.AnyUnicodeMode) === RegularExpressionFlags.AnyUnicodeMode) {
|
||||
error(Diagnostics.The_Unicode_u_flag_and_the_Unicode_Sets_v_flag_cannot_be_set_simultaneously, pos, 1);
|
||||
}
|
||||
else {
|
||||
regExpFlags |= flag;
|
||||
checkRegularExpressionFlagAvailable(flag, pos);
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (reportErrors) {
|
||||
const flag = characterToRegularExpressionFlag(String.fromCharCode(ch));
|
||||
if (flag === undefined) {
|
||||
error(Diagnostics.Unknown_regular_expression_flag, p, 1);
|
||||
}
|
||||
else if (regExpFlags & flag) {
|
||||
error(Diagnostics.Duplicate_regular_expression_flag, p, 1);
|
||||
}
|
||||
else if (((regExpFlags | flag) & RegularExpressionFlags.UnicodeMode) === RegularExpressionFlags.UnicodeMode) {
|
||||
error(Diagnostics.The_Unicode_u_flag_and_the_Unicode_Sets_v_flag_cannot_be_set_simultaneously, p, 1);
|
||||
}
|
||||
else {
|
||||
regExpFlags |= flag;
|
||||
checkRegularExpressionFlagAvailable(flag, p);
|
||||
}
|
||||
scanRange(startOfRegExpBody, endOfRegExpBody - startOfRegExpBody, () => {
|
||||
scanRegularExpressionWorker(regExpFlags, /*annexB*/ true);
|
||||
});
|
||||
}
|
||||
p++;
|
||||
}
|
||||
pos = p;
|
||||
if (reportErrors) {
|
||||
const saveTokenStart = tokenStart;
|
||||
const saveTokenFlags = tokenFlags;
|
||||
const savePos = pos;
|
||||
const saveEnd = end;
|
||||
pos = tokenStart + 1;
|
||||
end = endOfBody;
|
||||
scanRegularExpressionWorker(regExpFlags, isUnterminated, /*annexB*/ true);
|
||||
tokenStart = saveTokenStart;
|
||||
tokenFlags = saveTokenFlags;
|
||||
pos = savePos;
|
||||
end = saveEnd;
|
||||
}
|
||||
tokenValue = text.substring(tokenStart, pos);
|
||||
token = SyntaxKind.RegularExpressionLiteral;
|
||||
@@ -2517,7 +2552,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
return token;
|
||||
}
|
||||
|
||||
function scanRegularExpressionWorker(regExpFlags: RegularExpressionFlags, isUnterminated: boolean, annexB: boolean) {
|
||||
function scanRegularExpressionWorker(regExpFlags: RegularExpressionFlags, annexB: boolean) {
|
||||
// Why var? It avoids TDZ checks in the runtime which can be costly.
|
||||
// See: https://github.com/microsoft/TypeScript/issues/52924
|
||||
/* eslint-disable no-var */
|
||||
@@ -2525,9 +2560,9 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
/** Grammar parameter */
|
||||
var unicodeSetsMode = !!(regExpFlags & RegularExpressionFlags.UnicodeSets);
|
||||
/** Grammar parameter */
|
||||
var unicodeMode = !!(regExpFlags & RegularExpressionFlags.UnicodeMode);
|
||||
var anyUnicodeMode = !!(regExpFlags & RegularExpressionFlags.AnyUnicodeMode);
|
||||
|
||||
if (unicodeMode) {
|
||||
if (anyUnicodeMode) {
|
||||
// Annex B treats any unicode mode as the strict syntax.
|
||||
annexB = false;
|
||||
}
|
||||
@@ -2535,7 +2570,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
/** @see {scanClassSetExpression} */
|
||||
var mayContainStrings = false;
|
||||
|
||||
/** The number of numeric (anonymous) capturing groups defined in the regex. */
|
||||
/** The number of all (named and unnamed) capturing groups defined in the regex. */
|
||||
var numberOfCapturingGroups = 0;
|
||||
/** All named capturing groups defined in the regex. */
|
||||
var groupSpecifiers: Set<string> | undefined;
|
||||
@@ -2684,7 +2719,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
error(Diagnostics.Incomplete_quantifier_Digit_expected, digitsStart, 0);
|
||||
}
|
||||
else {
|
||||
if (unicodeMode) {
|
||||
if (anyUnicodeMode) {
|
||||
error(Diagnostics.Unexpected_0_Did_you_mean_to_escape_it_with_backslash, start, 1, String.fromCharCode(ch));
|
||||
}
|
||||
isPreviousTermQuantifiable = true;
|
||||
@@ -2696,7 +2731,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
else if (!min) {
|
||||
if (unicodeMode) {
|
||||
if (anyUnicodeMode) {
|
||||
error(Diagnostics.Unexpected_0_Did_you_mean_to_escape_it_with_backslash, start, 1, String.fromCharCode(ch));
|
||||
}
|
||||
isPreviousTermQuantifiable = true;
|
||||
@@ -2740,11 +2775,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
// falls through
|
||||
case CharacterCodes.closeBracket:
|
||||
case CharacterCodes.closeBrace:
|
||||
if (isUnterminated && !isInGroup) {
|
||||
// Assume what starting from the character to be outside of the regex
|
||||
return;
|
||||
}
|
||||
if (unicodeMode || ch === CharacterCodes.closeParen) {
|
||||
if (anyUnicodeMode || ch === CharacterCodes.closeParen) {
|
||||
error(Diagnostics.Unexpected_0_Did_you_mean_to_escape_it_with_backslash, pos, 1, String.fromCharCode(ch));
|
||||
}
|
||||
pos++;
|
||||
@@ -2801,7 +2832,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
scanGroupName(/*isReference*/ true);
|
||||
scanExpectedChar(CharacterCodes.greaterThan);
|
||||
}
|
||||
else if (unicodeMode) {
|
||||
else if (anyUnicodeMode) {
|
||||
error(Diagnostics.k_must_be_followed_by_a_capturing_group_name_enclosed_in_angle_brackets, pos - 2, 2);
|
||||
}
|
||||
break;
|
||||
@@ -2844,6 +2875,9 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
Debug.assertEqual(charCodeUnchecked(pos - 1), CharacterCodes.backslash);
|
||||
let ch = charCodeChecked(pos);
|
||||
switch (ch) {
|
||||
case CharacterCodes.EOF:
|
||||
error(Diagnostics.Undetermined_character_escape, pos - 1, 1);
|
||||
return "\\";
|
||||
case CharacterCodes.c:
|
||||
pos++;
|
||||
ch = charCodeChecked(pos);
|
||||
@@ -2851,7 +2885,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return String.fromCharCode(ch & 0x1f);
|
||||
}
|
||||
if (unicodeMode) {
|
||||
if (anyUnicodeMode) {
|
||||
error(Diagnostics.c_must_be_followed_by_an_ASCII_letter, pos - 2, 2);
|
||||
}
|
||||
else if (atomEscape && annexB) {
|
||||
@@ -2882,12 +2916,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
pos++;
|
||||
return String.fromCharCode(ch);
|
||||
default:
|
||||
if (pos >= end) {
|
||||
error(Diagnostics.Undetermined_character_escape, pos - 1, 1);
|
||||
return "\\";
|
||||
}
|
||||
pos--;
|
||||
return scanEscapeSequence(/*shouldEmitInvalidEscapeError*/ unicodeMode, /*isRegularExpression*/ annexB ? "annex-b" : true);
|
||||
return scanEscapeSequence(/*shouldEmitInvalidEscapeError*/ anyUnicodeMode, /*isRegularExpression*/ annexB ? "annex-b" : true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3433,11 +3463,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
}
|
||||
scanExpectedChar(CharacterCodes.closeBrace);
|
||||
if (!unicodeMode) {
|
||||
if (!anyUnicodeMode) {
|
||||
error(Diagnostics.Unicode_property_value_expressions_are_only_available_when_the_Unicode_u_flag_or_the_Unicode_Sets_v_flag_is_set, start, pos - start);
|
||||
}
|
||||
}
|
||||
else if (unicodeMode) {
|
||||
else if (anyUnicodeMode) {
|
||||
error(Diagnostics._0_must_be_followed_by_a_Unicode_property_value_expression_enclosed_in_braces, pos - 2, 2, String.fromCharCode(ch));
|
||||
}
|
||||
return true;
|
||||
@@ -3459,7 +3489,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
|
||||
}
|
||||
|
||||
function scanSourceCharacter(): string {
|
||||
const size = unicodeMode ? charSize(charCodeChecked(pos)) : 1;
|
||||
const size = anyUnicodeMode ? charSize(charCodeChecked(pos)) : 1;
|
||||
pos += size;
|
||||
return size > 0 ? text.substring(pos - size, pos) : "";
|
||||
}
|
||||
|
||||
+11
-11
@@ -2765,17 +2765,17 @@ export interface RegularExpressionLiteral extends LiteralExpression {
|
||||
// dprint-ignore
|
||||
/** @internal */
|
||||
export const enum RegularExpressionFlags {
|
||||
None = 0,
|
||||
HasIndices = 1 << 0, // d
|
||||
Global = 1 << 1, // g
|
||||
IgnoreCase = 1 << 2, // i
|
||||
Multiline = 1 << 3, // m
|
||||
DotAll = 1 << 4, // s
|
||||
Unicode = 1 << 5, // u
|
||||
UnicodeSets = 1 << 6, // v
|
||||
Sticky = 1 << 7, // y
|
||||
UnicodeMode = Unicode | UnicodeSets,
|
||||
Modifiers = IgnoreCase | Multiline | DotAll,
|
||||
None = 0,
|
||||
HasIndices = 1 << 0, // d
|
||||
Global = 1 << 1, // g
|
||||
IgnoreCase = 1 << 2, // i
|
||||
Multiline = 1 << 3, // m
|
||||
DotAll = 1 << 4, // s
|
||||
Unicode = 1 << 5, // u
|
||||
UnicodeSets = 1 << 6, // v
|
||||
Sticky = 1 << 7, // y
|
||||
AnyUnicodeMode = Unicode | UnicodeSets,
|
||||
Modifiers = IgnoreCase | Multiline | DotAll,
|
||||
}
|
||||
|
||||
export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration {
|
||||
|
||||
@@ -47,6 +47,7 @@ export * from "./unittests/paths.js";
|
||||
export * from "./unittests/printer.js";
|
||||
export * from "./unittests/programApi.js";
|
||||
export * from "./unittests/publicApi.js";
|
||||
export * from "./unittests/regExpScannerRecovery.js";
|
||||
export * from "./unittests/reuseProgramStructure.js";
|
||||
export * from "./unittests/semver.js";
|
||||
export * from "./unittests/services/cancellableLanguageServiceOperations.js";
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("unittests:: incrementalParser::", () => {
|
||||
const oldText = ts.ScriptSnapshot.fromString(source);
|
||||
const newTextAndChange = withInsert(oldText, semicolonIndex, "/");
|
||||
|
||||
compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0);
|
||||
compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4);
|
||||
});
|
||||
|
||||
it("Regular expression 2", () => {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as ts from "../_namespaces/ts.js";
|
||||
|
||||
describe("unittests:: regExpScannerRecovery", () => {
|
||||
const testCases = [
|
||||
"/",
|
||||
"/[]",
|
||||
"/{}",
|
||||
"/()",
|
||||
"/foo",
|
||||
"/foo[]",
|
||||
"/foo{}",
|
||||
"/foo()",
|
||||
"/[]foo",
|
||||
"/{}foo",
|
||||
"/()foo",
|
||||
"/{[]}",
|
||||
"/([])",
|
||||
"/[)}({]",
|
||||
"/({[]})",
|
||||
"/\\[",
|
||||
"/\\{",
|
||||
"/\\(",
|
||||
"/[\\[]",
|
||||
"/(\\[)",
|
||||
"/{\\[}",
|
||||
"/[\\(]",
|
||||
"/(\\()",
|
||||
"/{\\(}",
|
||||
"/[\\{]",
|
||||
"/(\\{)",
|
||||
"/{\\{}",
|
||||
"/\\{(\\[\\([{])",
|
||||
"/\\]",
|
||||
"/\\}",
|
||||
"/\\)",
|
||||
"/[\\]]",
|
||||
"/(\\])",
|
||||
"/{\\]}",
|
||||
"/[\\)]",
|
||||
"/(\\))",
|
||||
"/{\\)}",
|
||||
"/[\\}]",
|
||||
"/(\\})",
|
||||
"/{\\}}",
|
||||
"/({[\\])]})",
|
||||
];
|
||||
const whiteSpaceSequences = [
|
||||
"",
|
||||
" ",
|
||||
"\t\f",
|
||||
"\u3000\u2003",
|
||||
];
|
||||
for (const testCase of testCases) {
|
||||
for (const whiteSpaces of whiteSpaceSequences) {
|
||||
const testCaseWithWhiteSpaces = testCase + whiteSpaces;
|
||||
const sources = [
|
||||
`const regex = ${testCaseWithWhiteSpaces};`,
|
||||
`(${testCaseWithWhiteSpaces});`,
|
||||
`([${testCaseWithWhiteSpaces}]);`,
|
||||
`({prop: ${testCaseWithWhiteSpaces}});`,
|
||||
`({prop: ([(${testCaseWithWhiteSpaces})])});`,
|
||||
`({[(${testCaseWithWhiteSpaces}).source]: 42});`,
|
||||
];
|
||||
for (const source of sources) {
|
||||
it("stops parsing unterminated regexes at correct position: " + JSON.stringify(source), () => {
|
||||
const { parseDiagnostics } = ts.createLanguageServiceSourceFile(
|
||||
/*fileName*/ "",
|
||||
ts.ScriptSnapshot.fromString(source),
|
||||
ts.ScriptTarget.Latest,
|
||||
/*version*/ "0",
|
||||
/*setNodeParents*/ false,
|
||||
);
|
||||
const diagnostic = ts.find(parseDiagnostics, ({ code }) => code === ts.Diagnostics.Unterminated_regular_expression_literal.code);
|
||||
assert(diagnostic, "There should be an 'Unterminated regular expression literal.' error");
|
||||
assert.equal(diagnostic.start, source.indexOf("/"), "Diagnostic should start at where the regex starts");
|
||||
assert.equal(diagnostic.length, testCase.length, "Diagnostic should end at where the regex ends");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,13 +1,10 @@
|
||||
parser645086_1.ts(1,13): error TS1005: ',' expected.
|
||||
parser645086_1.ts(1,14): error TS1134: Variable declaration expected.
|
||||
parser645086_1.ts(1,15): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== parser645086_1.ts (3 errors) ====
|
||||
==== parser645086_1.ts (2 errors) ====
|
||||
var v = /[]/]/
|
||||
~
|
||||
!!! error TS1005: ',' expected.
|
||||
~
|
||||
!!! error TS1134: Variable declaration expected.
|
||||
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
!!! error TS1134: Variable declaration expected.
|
||||
@@ -1,13 +1,10 @@
|
||||
parser645086_2.ts(1,14): error TS1005: ',' expected.
|
||||
parser645086_2.ts(1,15): error TS1134: Variable declaration expected.
|
||||
parser645086_2.ts(1,16): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== parser645086_2.ts (3 errors) ====
|
||||
==== parser645086_2.ts (2 errors) ====
|
||||
var v = /[^]/]/
|
||||
~
|
||||
!!! error TS1005: ',' expected.
|
||||
~
|
||||
!!! error TS1134: Variable declaration expected.
|
||||
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
!!! error TS1134: Variable declaration expected.
|
||||
@@ -1,7 +1,7 @@
|
||||
parserMissingToken2.ts(1,2): error TS1161: Unterminated regular expression literal.
|
||||
parserMissingToken2.ts(1,1): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== parserMissingToken2.ts (1 errors) ====
|
||||
/ b;
|
||||
|
||||
~~~
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
@@ -4,4 +4,4 @@
|
||||
/ b;
|
||||
|
||||
//// [parserMissingToken2.js]
|
||||
/ b;;
|
||||
/ b;
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
=== parserMissingToken2.ts ===
|
||||
/ b;
|
||||
>/ b; : RegExp
|
||||
> : ^^^^^^
|
||||
>/ b : RegExp
|
||||
> : ^^^^^^
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
parserRegularExpressionDivideAmbiguity4.ts(1,1): error TS2304: Cannot find name 'foo'.
|
||||
parserRegularExpressionDivideAmbiguity4.ts(1,6): error TS1161: Unterminated regular expression literal.
|
||||
parserRegularExpressionDivideAmbiguity4.ts(1,17): error TS1005: ')' expected.
|
||||
parserRegularExpressionDivideAmbiguity4.ts(1,5): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== parserRegularExpressionDivideAmbiguity4.ts (3 errors) ====
|
||||
==== parserRegularExpressionDivideAmbiguity4.ts (2 errors) ====
|
||||
foo(/notregexp);
|
||||
~~~
|
||||
!!! error TS2304: Cannot find name 'foo'.
|
||||
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
|
||||
!!! error TS1005: ')' expected.
|
||||
~~~~~~~~~~
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
@@ -4,4 +4,4 @@
|
||||
foo(/notregexp);
|
||||
|
||||
//// [parserRegularExpressionDivideAmbiguity4.js]
|
||||
foo(/notregexp););
|
||||
foo(/notregexp);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
=== parserRegularExpressionDivideAmbiguity4.ts ===
|
||||
foo(/notregexp);
|
||||
>foo(/notregexp); : any
|
||||
> : ^^^
|
||||
>foo(/notregexp) : any
|
||||
> : ^^^
|
||||
>foo : any
|
||||
> : ^^^
|
||||
>/notregexp); : RegExp
|
||||
> : ^^^^^^
|
||||
>/notregexp : RegExp
|
||||
> : ^^^^^^
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ file.tsx(11,1): error TS2362: The left-hand side of an arithmetic operation must
|
||||
file.tsx(11,8): error TS1003: Identifier expected.
|
||||
file.tsx(11,9): error TS2304: Cannot find name 'data'.
|
||||
file.tsx(11,13): error TS1005: ';' expected.
|
||||
file.tsx(11,20): error TS1161: Unterminated regular expression literal.
|
||||
file.tsx(11,19): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== file.tsx (13 errors) ====
|
||||
@@ -49,5 +49,5 @@ file.tsx(11,20): error TS1161: Unterminated regular expression literal.
|
||||
!!! error TS2304: Cannot find name 'data'.
|
||||
~
|
||||
!!! error TS1005: ';' expected.
|
||||
|
||||
~~
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
@@ -22,4 +22,4 @@ data = { 32: } / > ;
|
||||
{
|
||||
32;
|
||||
}
|
||||
/>;;
|
||||
/>;
|
||||
|
||||
@@ -56,6 +56,6 @@ declare module JSX {
|
||||
> : ^^^
|
||||
>32 : 32
|
||||
> : ^^
|
||||
>/>; : RegExp
|
||||
> : ^^^^^^
|
||||
>/> : RegExp
|
||||
> : ^^^^^^
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
unterminatedRegexAtEndOfSource1.ts(1,10): error TS1161: Unterminated regular expression literal.
|
||||
unterminatedRegexAtEndOfSource1.ts(1,9): error TS1161: Unterminated regular expression literal.
|
||||
|
||||
|
||||
==== unterminatedRegexAtEndOfSource1.ts (1 errors) ====
|
||||
var a = /
|
||||
|
||||
~
|
||||
!!! error TS1161: Unterminated regular expression literal.
|
||||
@@ -5,4 +5,4 @@
|
||||
goTo.marker('1');
|
||||
edit.insert("\n");
|
||||
|
||||
verify.currentFileContentIs("var re = /\\w+ \n /;");
|
||||
verify.currentFileContentIs("var re = /\\w+\n /;");
|
||||
|
||||
Reference in New Issue
Block a user