Skip to content

Commit 70399e1

Browse files
Kingwlsandersn
andauthoredFeb 5, 2020
add support for Lift Template Literal Restriction (#23801)
* add support for Lift Template Literal Restriction * rename file and improve comment and tests * fix NoSubstitutionTemplateLiteral support * extract tagged template and add more test * avoid useless parameter * fix incorrect return node if cannot transform * accept baseline * correctly baseline * accept baseline * fix merge break * fix merge break * inline rescan template head or no subsititution template * update scan error * add comment and fix lint * refactor and fix lint * avoid blank * fix merge conflict * fix again * fix again * use multiple target * fix space lint Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
1 parent 2047118 commit 70399e1

31 files changed

+1647
-125
lines changed
 

‎src/compiler/binder.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -4124,8 +4124,18 @@ namespace ts {
41244124
case SyntaxKind.TemplateHead:
41254125
case SyntaxKind.TemplateMiddle:
41264126
case SyntaxKind.TemplateTail:
4127-
case SyntaxKind.TemplateExpression:
4127+
if ((<NoSubstitutionTemplateLiteral | TemplateHead | TemplateMiddle | TemplateTail>node).templateFlags) {
4128+
transformFlags |= TransformFlags.AssertES2018;
4129+
break;
4130+
}
4131+
// falls through
41284132
case SyntaxKind.TaggedTemplateExpression:
4133+
if (hasInvalidEscape((<TaggedTemplateExpression>node).template)) {
4134+
transformFlags |= TransformFlags.AssertES2018;
4135+
break;
4136+
}
4137+
// falls through
4138+
case SyntaxKind.TemplateExpression:
41294139
case SyntaxKind.ShorthandPropertyAssignment:
41304140
case SyntaxKind.StaticKeyword:
41314141
case SyntaxKind.MetaProperty:

‎src/compiler/factoryPublic.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,7 @@ namespace ts {
14861486

14871487
let token = rawTextScanner.scan();
14881488
if (token === SyntaxKind.CloseBracketToken) {
1489-
token = rawTextScanner.reScanTemplateToken();
1489+
token = rawTextScanner.reScanTemplateToken(/* isTaggedTemplate */ false);
14901490
}
14911491

14921492
if (rawTextScanner.isUnterminated()) {

‎src/compiler/parser.ts

+22-11
Original file line numberDiff line numberDiff line change
@@ -1137,8 +1137,12 @@ namespace ts {
11371137
return currentToken = scanner.reScanSlashToken();
11381138
}
11391139

1140-
function reScanTemplateToken(): SyntaxKind {
1141-
return currentToken = scanner.reScanTemplateToken();
1140+
function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind {
1141+
return currentToken = scanner.reScanTemplateToken(isTaggedTemplate);
1142+
}
1143+
1144+
function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind {
1145+
return currentToken = scanner.reScanTemplateHeadOrNoSubstitutionTemplate();
11421146
}
11431147

11441148
function reScanLessThanToken(): SyntaxKind {
@@ -2329,17 +2333,17 @@ namespace ts {
23292333
return allowIdentifierNames ? parseIdentifierName() : parseIdentifier();
23302334
}
23312335

2332-
function parseTemplateExpression(): TemplateExpression {
2336+
function parseTemplateExpression(isTaggedTemplate: boolean): TemplateExpression {
23332337
const template = <TemplateExpression>createNode(SyntaxKind.TemplateExpression);
23342338

2335-
template.head = parseTemplateHead();
2339+
template.head = parseTemplateHead(isTaggedTemplate);
23362340
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
23372341

23382342
const list = [];
23392343
const listPos = getNodePos();
23402344

23412345
do {
2342-
list.push(parseTemplateSpan());
2346+
list.push(parseTemplateSpan(isTaggedTemplate));
23432347
}
23442348
while (last(list).literal.kind === SyntaxKind.TemplateMiddle);
23452349

@@ -2348,13 +2352,13 @@ namespace ts {
23482352
return finishNode(template);
23492353
}
23502354

2351-
function parseTemplateSpan(): TemplateSpan {
2355+
function parseTemplateSpan(isTaggedTemplate: boolean): TemplateSpan {
23522356
const span = <TemplateSpan>createNode(SyntaxKind.TemplateSpan);
23532357
span.expression = allowInAnd(parseExpression);
23542358

23552359
let literal: TemplateMiddle | TemplateTail;
23562360
if (token() === SyntaxKind.CloseBraceToken) {
2357-
reScanTemplateToken();
2361+
reScanTemplateToken(isTaggedTemplate);
23582362
literal = parseTemplateMiddleOrTemplateTail();
23592363
}
23602364
else {
@@ -2369,7 +2373,10 @@ namespace ts {
23692373
return <LiteralExpression>parseLiteralLikeNode(token());
23702374
}
23712375

2372-
function parseTemplateHead(): TemplateHead {
2376+
function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead {
2377+
if (isTaggedTemplate) {
2378+
reScanTemplateHeadOrNoSubstitutionTemplate();
2379+
}
23732380
const fragment = parseLiteralLikeNode(token());
23742381
Debug.assert(fragment.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
23752382
return <TemplateHead>fragment;
@@ -2413,6 +2420,10 @@ namespace ts {
24132420
(<NumericLiteral>node).numericLiteralFlags = scanner.getTokenFlags() & TokenFlags.NumericLiteralFlags;
24142421
}
24152422

2423+
if (isTemplateLiteralKind(node.kind)) {
2424+
(<TemplateHead | TemplateMiddle | TemplateTail | NoSubstitutionTemplateLiteral>node).templateFlags = scanner.getTokenFlags() & TokenFlags.ContainsInvalidEscape;
2425+
}
2426+
24162427
nextToken();
24172428
finishNode(node);
24182429

@@ -4772,8 +4783,8 @@ namespace ts {
47724783
tagExpression.questionDotToken = questionDotToken;
47734784
tagExpression.typeArguments = typeArguments;
47744785
tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral
4775-
? <NoSubstitutionTemplateLiteral>parseLiteralNode()
4776-
: parseTemplateExpression();
4786+
? (reScanTemplateHeadOrNoSubstitutionTemplate(), <NoSubstitutionTemplateLiteral>parseLiteralNode())
4787+
: parseTemplateExpression(/*isTaggedTemplate*/ true);
47774788
if (questionDotToken || tag.flags & NodeFlags.OptionalChain) {
47784789
tagExpression.flags |= NodeFlags.OptionalChain;
47794790
}
@@ -4945,7 +4956,7 @@ namespace ts {
49454956
}
49464957
break;
49474958
case SyntaxKind.TemplateHead:
4948-
return parseTemplateExpression();
4959+
return parseTemplateExpression(/* isTaggedTemplate */ false);
49494960
}
49504961

49514962
return parseIdentifier(Diagnostics.Expression_expected);

‎src/compiler/scanner.ts

+72-8
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ namespace ts {
2828
getTokenFlags(): TokenFlags;
2929
reScanGreaterToken(): SyntaxKind;
3030
reScanSlashToken(): SyntaxKind;
31-
reScanTemplateToken(): SyntaxKind;
31+
reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind;
32+
reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind;
3233
scanJsxIdentifier(): SyntaxKind;
3334
scanJsxAttributeValue(): SyntaxKind;
3435
reScanJsxAttributeValue(): SyntaxKind;
@@ -468,6 +469,14 @@ namespace ts {
468469
return ch >= CharacterCodes._0 && ch <= CharacterCodes._9;
469470
}
470471

472+
function isHexDigit(ch: number): boolean {
473+
return isDigit(ch) || ch >= CharacterCodes.A && ch <= CharacterCodes.F || ch >= CharacterCodes.a && ch <= CharacterCodes.f;
474+
}
475+
476+
function isCodePoint(code: number): boolean {
477+
return code <= 0x10FFFF;
478+
}
479+
471480
/* @internal */
472481
export function isOctalDigit(ch: number): boolean {
473482
return ch >= CharacterCodes._0 && ch <= CharacterCodes._7;
@@ -901,6 +910,7 @@ namespace ts {
901910
reScanGreaterToken,
902911
reScanSlashToken,
903912
reScanTemplateToken,
913+
reScanTemplateHeadOrNoSubstitutionTemplate,
904914
scanJsxIdentifier,
905915
scanJsxAttributeValue,
906916
reScanJsxAttributeValue,
@@ -1164,7 +1174,7 @@ namespace ts {
11641174
* Sets the current 'tokenValue' and returns a NoSubstitutionTemplateLiteral or
11651175
* a literal component of a TemplateExpression.
11661176
*/
1167-
function scanTemplateAndSetTokenValue(): SyntaxKind {
1177+
function scanTemplateAndSetTokenValue(isTaggedTemplate: boolean): SyntaxKind {
11681178
const startedWithBacktick = text.charCodeAt(pos) === CharacterCodes.backtick;
11691179

11701180
pos++;
@@ -1202,7 +1212,7 @@ namespace ts {
12021212
// Escape character
12031213
if (currChar === CharacterCodes.backslash) {
12041214
contents += text.substring(start, pos);
1205-
contents += scanEscapeSequence();
1215+
contents += scanEscapeSequence(isTaggedTemplate);
12061216
start = pos;
12071217
continue;
12081218
}
@@ -1231,7 +1241,8 @@ namespace ts {
12311241
return resultingToken;
12321242
}
12331243

1234-
function scanEscapeSequence(): string {
1244+
function scanEscapeSequence(isTaggedTemplate?: boolean): string {
1245+
const start = pos;
12351246
pos++;
12361247
if (pos >= end) {
12371248
error(Diagnostics.Unexpected_end_of_text);
@@ -1241,6 +1252,12 @@ namespace ts {
12411252
pos++;
12421253
switch (ch) {
12431254
case CharacterCodes._0:
1255+
// '\01'
1256+
if (isTaggedTemplate && pos < end && isDigit(text.charCodeAt(pos))) {
1257+
pos++;
1258+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1259+
return text.substring(start, pos);
1260+
}
12441261
return "\0";
12451262
case CharacterCodes.b:
12461263
return "\b";
@@ -1259,10 +1276,41 @@ namespace ts {
12591276
case CharacterCodes.doubleQuote:
12601277
return "\"";
12611278
case CharacterCodes.u:
1279+
if (isTaggedTemplate) {
1280+
// '\u' or '\u0' or '\u00' or '\u000'
1281+
for (let escapePos = pos; escapePos < pos + 4; escapePos++) {
1282+
if (escapePos < end && !isHexDigit(text.charCodeAt(escapePos)) && text.charCodeAt(escapePos) !== CharacterCodes.openBrace) {
1283+
pos = escapePos;
1284+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1285+
return text.substring(start, pos);
1286+
}
1287+
}
1288+
}
12621289
// '\u{DDDDDDDD}'
12631290
if (pos < end && text.charCodeAt(pos) === CharacterCodes.openBrace) {
1264-
tokenFlags |= TokenFlags.ExtendedUnicodeEscape;
12651291
pos++;
1292+
1293+
// '\u{'
1294+
if (isTaggedTemplate && !isHexDigit(text.charCodeAt(pos))) {
1295+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1296+
return text.substring(start, pos);
1297+
}
1298+
1299+
if (isTaggedTemplate) {
1300+
const savePos = pos;
1301+
const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false);
1302+
const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1;
1303+
1304+
// '\u{Not Code Point' or '\u{CodePoint'
1305+
if (!isCodePoint(escapedValue) || text.charCodeAt(pos) !== CharacterCodes.closeBrace) {
1306+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1307+
return text.substring(start, pos);
1308+
}
1309+
else {
1310+
pos = savePos;
1311+
}
1312+
}
1313+
tokenFlags |= TokenFlags.ExtendedUnicodeEscape;
12661314
return scanExtendedUnicodeEscape();
12671315
}
12681316

@@ -1271,6 +1319,17 @@ namespace ts {
12711319
return scanHexadecimalEscape(/*numDigits*/ 4);
12721320

12731321
case CharacterCodes.x:
1322+
if (isTaggedTemplate) {
1323+
if (!isHexDigit(text.charCodeAt(pos))) {
1324+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1325+
return text.substring(start, pos);
1326+
}
1327+
else if (!isHexDigit(text.charCodeAt(pos + 1))) {
1328+
pos++;
1329+
tokenFlags |= TokenFlags.ContainsInvalidEscape;
1330+
return text.substring(start, pos);
1331+
}
1332+
}
12741333
// '\xDD'
12751334
return scanHexadecimalEscape(/*numDigits*/ 2);
12761335

@@ -1561,7 +1620,7 @@ namespace ts {
15611620
tokenValue = scanString();
15621621
return token = SyntaxKind.StringLiteral;
15631622
case CharacterCodes.backtick:
1564-
return token = scanTemplateAndSetTokenValue();
1623+
return token = scanTemplateAndSetTokenValue(/* isTaggedTemplate */ false);
15651624
case CharacterCodes.percent:
15661625
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
15671626
return pos += 2, token = SyntaxKind.PercentEqualsToken;
@@ -2019,10 +2078,15 @@ namespace ts {
20192078
/**
20202079
* Unconditionally back up and scan a template expression portion.
20212080
*/
2022-
function reScanTemplateToken(): SyntaxKind {
2081+
function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind {
20232082
Debug.assert(token === SyntaxKind.CloseBraceToken, "'reScanTemplateToken' should only be called on a '}'");
20242083
pos = tokenPos;
2025-
return token = scanTemplateAndSetTokenValue();
2084+
return token = scanTemplateAndSetTokenValue(isTaggedTemplate);
2085+
}
2086+
2087+
function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind {
2088+
pos = tokenPos;
2089+
return token = scanTemplateAndSetTokenValue(/* isTaggedTemplate */ true);
20262090
}
20272091

20282092
function reScanJsxToken(): JsxTokenSyntaxKind {

0 commit comments

Comments
 (0)