mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
co-authored by
Daniel Rosenwasser
Jake Bailey
parent
8e8c1b6a64
commit
42f238bfbb
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user