Provide Syntax Checking for Regular Expressions (#55600)

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
This commit is contained in:
形素叢集・ぐらふぃーむ
2024-04-19 16:51:08 -07:00
committed by GitHub
co-authored by Daniel Rosenwasser Jake Bailey
parent 8e8c1b6a64
commit 42f238bfbb
886 changed files with 7488 additions and 2880 deletions
+1 -1
View File
@@ -2289,7 +2289,7 @@ export function compareBooleans(a: boolean, b: boolean): Comparison {
*
* @internal
*/
export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined {
export function getSpellingSuggestion<T>(name: string, candidates: Iterable<T>, getName: (candidate: T) => string | undefined): T | undefined {
const maximumLengthDifference = Math.max(2, Math.floor(name.length * 0.34));
let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother.
let bestCandidate: T | undefined;
+148
View File
@@ -1649,6 +1649,154 @@
"category": "Error",
"code": 1498
},
"Unknown regular expression flag.": {
"category": "Error",
"code": 1499
},
"Duplicate regular expression flag.": {
"category": "Error",
"code": 1500
},
"This regular expression flag is only available when targeting '{0}' or later.": {
"category": "Error",
"code": 1501
},
"The Unicode (u) flag and the Unicode Sets (v) flag cannot be set simultaneously.": {
"category": "Error",
"code": 1502
},
"Named capturing groups are only available when targeting 'ES2018' or later.": {
"category": "Error",
"code": 1503
},
"Subpattern flags must be present when there is a minus sign.": {
"category": "Error",
"code": 1504
},
"Incomplete quantifier. Digit expected.": {
"category": "Error",
"code": 1505
},
"Numbers out of order in quantifier.": {
"category": "Error",
"code": 1506
},
"There is nothing available for repetition.": {
"category": "Error",
"code": 1507
},
"Unexpected '{0}'. Did you mean to escape it with backslash?": {
"category": "Error",
"code": 1508
},
"This regular expression flag cannot be toggled within a subpattern.": {
"category": "Error",
"code": 1509
},
"'\\k' must be followed by a capturing group name enclosed in angle brackets.": {
"category": "Error",
"code": 1510
},
"'\\q' is only available inside character class.": {
"category": "Error",
"code": 1511
},
"'\\c' must be followed by an ASCII letter.": {
"category": "Error",
"code": 1512
},
"Undetermined character escape.": {
"category": "Error",
"code": 1513
},
"Expected a capturing group name.": {
"category": "Error",
"code": 1514
},
"Named capturing groups with the same name must be mutually exclusive to each other.": {
"category": "Error",
"code": 1515
},
"A character class range must not be bounded by another character class.": {
"category": "Error",
"code": 1516
},
"Range out of order in character class.": {
"category": "Error",
"code": 1517
},
"Anything that would possibly match more than a single character is invalid inside a negated character class.": {
"category": "Error",
"code": 1518
},
"Operators must not be mixed within a character class. Wrap it in a nested class instead.": {
"category": "Error",
"code": 1519
},
"Expected a class set oprand.": {
"category": "Error",
"code": 1520
},
"'\\q' must be followed by string alternatives enclosed in braces.": {
"category": "Error",
"code": 1521
},
"A character class must not contain a reserved double punctuator. Did you mean to escape it with backslash?": {
"category": "Error",
"code": 1522
},
"Expected a Unicode property name.": {
"category": "Error",
"code": 1523
},
"Unknown Unicode property name.": {
"category": "Error",
"code": 1524
},
"Expected a Unicode property value.": {
"category": "Error",
"code": 1525
},
"Unknown Unicode property value.": {
"category": "Error",
"code": 1526
},
"Expected a Unicode property name or value.": {
"category": "Error",
"code": 1527
},
"Any Unicode property that would possibly match more than a single character is only available when the Unicode Sets (v) flag is set.": {
"category": "Error",
"code": 1528
},
"Unknown Unicode property name or value.": {
"category": "Error",
"code": 1529
},
"Unicode property value expressions are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": {
"category": "Error",
"code": 1530
},
"'\\{0}' must be followed by a Unicode property value expression enclosed in braces.": {
"category": "Error",
"code": 1531
},
"There is no capturing group named '{0}' in this regular expression.": {
"category": "Error",
"code": 1532
},
"A decimal escape must refer to an existent capturing group. There are only {0} capturing groups in this regular expression.": {
"category": "Error",
"code": 1533
},
"Decimal escapes are invalid when there are no capturing groups in a regular expression.": {
"category": "Error",
"code": 1534
},
"This character cannot be escaped in a regular expression.": {
"category": "Error",
"code": 1535
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
+8 -2
View File
@@ -62,6 +62,7 @@ import {
DeleteExpression,
Diagnostic,
DiagnosticArguments,
DiagnosticCategory,
DiagnosticMessage,
Diagnostics,
DiagnosticWithDetachedLocation,
@@ -113,6 +114,7 @@ import {
HasModifiers,
HeritageClause,
Identifier,
identity,
idText,
IfStatement,
ImportAttribute,
@@ -2142,7 +2144,11 @@ 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 (!lastError || start !== lastError.start) {
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) {
result = createDetachedDiagnostic(fileName, sourceText, start, length, message, ...args);
parseDiagnostics.push(result);
}
@@ -2398,7 +2404,7 @@ namespace Parser {
}
// The user alternatively might have misspelled or forgotten to add a space after a common keyword.
const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, n => n) ?? getSpaceSuggestion(expressionText);
const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, identity) ?? getSpaceSuggestion(expressionText);
if (suggestion) {
parseErrorAt(pos, node.end, Diagnostics.Unknown_keyword_or_identifier_Did_you_mean_0, suggestion);
return;
+2 -2
View File
@@ -126,6 +126,7 @@ import {
getLineStarts,
getMatchedFileSpec,
getMatchedIncludeSpec,
getNameOfScriptTarget,
getNewLineCharacter,
getNormalizedAbsolutePath,
getNormalizedAbsolutePathWithoutRoot,
@@ -307,7 +308,6 @@ import {
SyntaxKind,
sys,
System,
targetOptionDeclaration,
toFileNameLowerCase,
tokenToString,
toPath as ts_toPath,
@@ -4780,7 +4780,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
message = Diagnostics.File_is_library_specified_here;
break;
}
const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined);
const target = getNameOfScriptTarget(getEmitScriptTarget(options));
configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
message = Diagnostics.File_is_default_library_for_target_specified_here;
break;
+1139 -79
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -2762,6 +2762,22 @@ export interface RegularExpressionLiteral extends LiteralExpression {
readonly kind: SyntaxKind.RegularExpressionLiteral;
}
// 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,
}
export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration {
readonly kind: SyntaxKind.NoSubstitutionTemplateLiteral;
/** @internal */
+6
View File
@@ -530,6 +530,7 @@ import {
SymbolTable,
SyntaxKind,
TaggedTemplateExpression,
targetOptionDeclaration,
TemplateExpression,
TemplateLiteral,
TemplateLiteralLikeNode,
@@ -8986,6 +8987,11 @@ export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: Str
return compilerOptions[flag] === undefined ? !!compilerOptions.strict : !!compilerOptions[flag];
}
/** @internal */
export function getNameOfScriptTarget(scriptTarget: ScriptTarget): string | undefined {
return forEachEntry(targetOptionDeclaration.type, (value, key) => value === scriptTarget ? key : undefined);
}
/** @internal */
export function getEmitStandardClassFields(compilerOptions: CompilerOptions) {
return compilerOptions.useDefineForClassFields !== false && getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022;
+2 -3
View File
@@ -47,7 +47,6 @@ import {
findIndex,
flattenDiagnosticMessageText,
forEach,
forEachEntry,
ForegroundColorEscapeSequences,
formatColorAndReset,
formatDiagnostic,
@@ -59,6 +58,7 @@ import {
getEmitScriptTarget,
getImpliedNodeFormatForEmitWorker,
getLineAndCharacterOfPosition,
getNameOfScriptTarget,
getNewLineCharacter,
getNormalizedAbsolutePath,
getParsedCommandLineOfConfigFile,
@@ -95,7 +95,6 @@ import {
sourceMapCommentRegExpDontCareLineStart,
sys,
System,
targetOptionDeclaration,
WatchCompilerHost,
WatchCompilerHostOfConfigFile,
WatchCompilerHostOfFilesAndCompilerOptions,
@@ -542,7 +541,7 @@ export function fileIncludeReasonToDiagnostics(program: Program, reason: FileInc
}
case FileIncludeKind.LibFile: {
if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]);
const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined);
const target = getNameOfScriptTarget(getEmitScriptTarget(options));
const messageAndArgs: DiagnosticAndArguments = target ? [Diagnostics.Default_library_for_target_0, target] : [Diagnostics.Default_library];
return chainDiagnosticMessages(/*details*/ undefined, ...messageAndArgs);
}