Skip to content

Commit 42f238b

Browse files
graphemeclusterDanielRosenwasserjakebailey
authored
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>
1 parent 8e8c1b6 commit 42f238b

File tree

1,100 files changed

+8941
-4333
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,100 files changed

+8941
-4333
lines changed

src/compiler/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2289,7 +2289,7 @@ export function compareBooleans(a: boolean, b: boolean): Comparison {
22892289
*
22902290
* @internal
22912291
*/
2292-
export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined {
2292+
export function getSpellingSuggestion<T>(name: string, candidates: Iterable<T>, getName: (candidate: T) => string | undefined): T | undefined {
22932293
const maximumLengthDifference = Math.max(2, Math.floor(name.length * 0.34));
22942294
let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother.
22952295
let bestCandidate: T | undefined;

src/compiler/diagnosticMessages.json

+148
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,154 @@
16491649
"category": "Error",
16501650
"code": 1498
16511651
},
1652+
"Unknown regular expression flag.": {
1653+
"category": "Error",
1654+
"code": 1499
1655+
},
1656+
"Duplicate regular expression flag.": {
1657+
"category": "Error",
1658+
"code": 1500
1659+
},
1660+
"This regular expression flag is only available when targeting '{0}' or later.": {
1661+
"category": "Error",
1662+
"code": 1501
1663+
},
1664+
"The Unicode (u) flag and the Unicode Sets (v) flag cannot be set simultaneously.": {
1665+
"category": "Error",
1666+
"code": 1502
1667+
},
1668+
"Named capturing groups are only available when targeting 'ES2018' or later.": {
1669+
"category": "Error",
1670+
"code": 1503
1671+
},
1672+
"Subpattern flags must be present when there is a minus sign.": {
1673+
"category": "Error",
1674+
"code": 1504
1675+
},
1676+
"Incomplete quantifier. Digit expected.": {
1677+
"category": "Error",
1678+
"code": 1505
1679+
},
1680+
"Numbers out of order in quantifier.": {
1681+
"category": "Error",
1682+
"code": 1506
1683+
},
1684+
"There is nothing available for repetition.": {
1685+
"category": "Error",
1686+
"code": 1507
1687+
},
1688+
"Unexpected '{0}'. Did you mean to escape it with backslash?": {
1689+
"category": "Error",
1690+
"code": 1508
1691+
},
1692+
"This regular expression flag cannot be toggled within a subpattern.": {
1693+
"category": "Error",
1694+
"code": 1509
1695+
},
1696+
"'\\k' must be followed by a capturing group name enclosed in angle brackets.": {
1697+
"category": "Error",
1698+
"code": 1510
1699+
},
1700+
"'\\q' is only available inside character class.": {
1701+
"category": "Error",
1702+
"code": 1511
1703+
},
1704+
"'\\c' must be followed by an ASCII letter.": {
1705+
"category": "Error",
1706+
"code": 1512
1707+
},
1708+
"Undetermined character escape.": {
1709+
"category": "Error",
1710+
"code": 1513
1711+
},
1712+
"Expected a capturing group name.": {
1713+
"category": "Error",
1714+
"code": 1514
1715+
},
1716+
"Named capturing groups with the same name must be mutually exclusive to each other.": {
1717+
"category": "Error",
1718+
"code": 1515
1719+
},
1720+
"A character class range must not be bounded by another character class.": {
1721+
"category": "Error",
1722+
"code": 1516
1723+
},
1724+
"Range out of order in character class.": {
1725+
"category": "Error",
1726+
"code": 1517
1727+
},
1728+
"Anything that would possibly match more than a single character is invalid inside a negated character class.": {
1729+
"category": "Error",
1730+
"code": 1518
1731+
},
1732+
"Operators must not be mixed within a character class. Wrap it in a nested class instead.": {
1733+
"category": "Error",
1734+
"code": 1519
1735+
},
1736+
"Expected a class set oprand.": {
1737+
"category": "Error",
1738+
"code": 1520
1739+
},
1740+
"'\\q' must be followed by string alternatives enclosed in braces.": {
1741+
"category": "Error",
1742+
"code": 1521
1743+
},
1744+
"A character class must not contain a reserved double punctuator. Did you mean to escape it with backslash?": {
1745+
"category": "Error",
1746+
"code": 1522
1747+
},
1748+
"Expected a Unicode property name.": {
1749+
"category": "Error",
1750+
"code": 1523
1751+
},
1752+
"Unknown Unicode property name.": {
1753+
"category": "Error",
1754+
"code": 1524
1755+
},
1756+
"Expected a Unicode property value.": {
1757+
"category": "Error",
1758+
"code": 1525
1759+
},
1760+
"Unknown Unicode property value.": {
1761+
"category": "Error",
1762+
"code": 1526
1763+
},
1764+
"Expected a Unicode property name or value.": {
1765+
"category": "Error",
1766+
"code": 1527
1767+
},
1768+
"Any Unicode property that would possibly match more than a single character is only available when the Unicode Sets (v) flag is set.": {
1769+
"category": "Error",
1770+
"code": 1528
1771+
},
1772+
"Unknown Unicode property name or value.": {
1773+
"category": "Error",
1774+
"code": 1529
1775+
},
1776+
"Unicode property value expressions are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": {
1777+
"category": "Error",
1778+
"code": 1530
1779+
},
1780+
"'\\{0}' must be followed by a Unicode property value expression enclosed in braces.": {
1781+
"category": "Error",
1782+
"code": 1531
1783+
},
1784+
"There is no capturing group named '{0}' in this regular expression.": {
1785+
"category": "Error",
1786+
"code": 1532
1787+
},
1788+
"A decimal escape must refer to an existent capturing group. There are only {0} capturing groups in this regular expression.": {
1789+
"category": "Error",
1790+
"code": 1533
1791+
},
1792+
"Decimal escapes are invalid when there are no capturing groups in a regular expression.": {
1793+
"category": "Error",
1794+
"code": 1534
1795+
},
1796+
"This character cannot be escaped in a regular expression.": {
1797+
"category": "Error",
1798+
"code": 1535
1799+
},
16521800

16531801
"The types of '{0}' are incompatible between these types.": {
16541802
"category": "Error",

src/compiler/parser.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import {
6262
DeleteExpression,
6363
Diagnostic,
6464
DiagnosticArguments,
65+
DiagnosticCategory,
6566
DiagnosticMessage,
6667
Diagnostics,
6768
DiagnosticWithDetachedLocation,
@@ -113,6 +114,7 @@ import {
113114
HasModifiers,
114115
HeritageClause,
115116
Identifier,
117+
identity,
116118
idText,
117119
IfStatement,
118120
ImportAttribute,
@@ -2142,7 +2144,11 @@ namespace Parser {
21422144
// Don't report another error if it would just be at the same position as the last error.
21432145
const lastError = lastOrUndefined(parseDiagnostics);
21442146
let result: DiagnosticWithDetachedLocation | undefined;
2145-
if (!lastError || start !== lastError.start) {
2147+
if (message.category === DiagnosticCategory.Message && lastError && start === lastError.start && length === lastError.length) {
2148+
result = createDetachedDiagnostic(fileName, sourceText, start, length, message, ...args);
2149+
addRelatedInfo(lastError, result);
2150+
}
2151+
else if (!lastError || start !== lastError.start) {
21462152
result = createDetachedDiagnostic(fileName, sourceText, start, length, message, ...args);
21472153
parseDiagnostics.push(result);
21482154
}
@@ -2398,7 +2404,7 @@ namespace Parser {
23982404
}
23992405

24002406
// The user alternatively might have misspelled or forgotten to add a space after a common keyword.
2401-
const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, n => n) ?? getSpaceSuggestion(expressionText);
2407+
const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, identity) ?? getSpaceSuggestion(expressionText);
24022408
if (suggestion) {
24032409
parseErrorAt(pos, node.end, Diagnostics.Unknown_keyword_or_identifier_Did_you_mean_0, suggestion);
24042410
return;

src/compiler/program.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ import {
126126
getLineStarts,
127127
getMatchedFileSpec,
128128
getMatchedIncludeSpec,
129+
getNameOfScriptTarget,
129130
getNewLineCharacter,
130131
getNormalizedAbsolutePath,
131132
getNormalizedAbsolutePathWithoutRoot,
@@ -307,7 +308,6 @@ import {
307308
SyntaxKind,
308309
sys,
309310
System,
310-
targetOptionDeclaration,
311311
toFileNameLowerCase,
312312
tokenToString,
313313
toPath as ts_toPath,
@@ -4780,7 +4780,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
47804780
message = Diagnostics.File_is_library_specified_here;
47814781
break;
47824782
}
4783-
const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined);
4783+
const target = getNameOfScriptTarget(getEmitScriptTarget(options));
47844784
configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
47854785
message = Diagnostics.File_is_default_library_for_target_specified_here;
47864786
break;

0 commit comments

Comments
 (0)