@@ -28,7 +28,8 @@ namespace ts {
28
28
getTokenFlags ( ) : TokenFlags ;
29
29
reScanGreaterToken ( ) : SyntaxKind ;
30
30
reScanSlashToken ( ) : SyntaxKind ;
31
- reScanTemplateToken ( ) : SyntaxKind ;
31
+ reScanTemplateToken ( isTaggedTemplate : boolean ) : SyntaxKind ;
32
+ reScanTemplateHeadOrNoSubstitutionTemplate ( ) : SyntaxKind ;
32
33
scanJsxIdentifier ( ) : SyntaxKind ;
33
34
scanJsxAttributeValue ( ) : SyntaxKind ;
34
35
reScanJsxAttributeValue ( ) : SyntaxKind ;
@@ -468,6 +469,14 @@ namespace ts {
468
469
return ch >= CharacterCodes . _0 && ch <= CharacterCodes . _9 ;
469
470
}
470
471
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
+
471
480
/* @internal */
472
481
export function isOctalDigit ( ch : number ) : boolean {
473
482
return ch >= CharacterCodes . _0 && ch <= CharacterCodes . _7 ;
@@ -901,6 +910,7 @@ namespace ts {
901
910
reScanGreaterToken,
902
911
reScanSlashToken,
903
912
reScanTemplateToken,
913
+ reScanTemplateHeadOrNoSubstitutionTemplate,
904
914
scanJsxIdentifier,
905
915
scanJsxAttributeValue,
906
916
reScanJsxAttributeValue,
@@ -1164,7 +1174,7 @@ namespace ts {
1164
1174
* Sets the current 'tokenValue' and returns a NoSubstitutionTemplateLiteral or
1165
1175
* a literal component of a TemplateExpression.
1166
1176
*/
1167
- function scanTemplateAndSetTokenValue ( ) : SyntaxKind {
1177
+ function scanTemplateAndSetTokenValue ( isTaggedTemplate : boolean ) : SyntaxKind {
1168
1178
const startedWithBacktick = text . charCodeAt ( pos ) === CharacterCodes . backtick ;
1169
1179
1170
1180
pos ++ ;
@@ -1202,7 +1212,7 @@ namespace ts {
1202
1212
// Escape character
1203
1213
if ( currChar === CharacterCodes . backslash ) {
1204
1214
contents += text . substring ( start , pos ) ;
1205
- contents += scanEscapeSequence ( ) ;
1215
+ contents += scanEscapeSequence ( isTaggedTemplate ) ;
1206
1216
start = pos ;
1207
1217
continue ;
1208
1218
}
@@ -1231,7 +1241,8 @@ namespace ts {
1231
1241
return resultingToken ;
1232
1242
}
1233
1243
1234
- function scanEscapeSequence ( ) : string {
1244
+ function scanEscapeSequence ( isTaggedTemplate ?: boolean ) : string {
1245
+ const start = pos ;
1235
1246
pos ++ ;
1236
1247
if ( pos >= end ) {
1237
1248
error ( Diagnostics . Unexpected_end_of_text ) ;
@@ -1241,6 +1252,12 @@ namespace ts {
1241
1252
pos ++ ;
1242
1253
switch ( ch ) {
1243
1254
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
+ }
1244
1261
return "\0" ;
1245
1262
case CharacterCodes . b :
1246
1263
return "\b" ;
@@ -1259,10 +1276,41 @@ namespace ts {
1259
1276
case CharacterCodes . doubleQuote :
1260
1277
return "\"" ;
1261
1278
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
+ }
1262
1289
// '\u{DDDDDDDD}'
1263
1290
if ( pos < end && text . charCodeAt ( pos ) === CharacterCodes . openBrace ) {
1264
- tokenFlags |= TokenFlags . ExtendedUnicodeEscape ;
1265
1291
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 ;
1266
1314
return scanExtendedUnicodeEscape ( ) ;
1267
1315
}
1268
1316
@@ -1271,6 +1319,17 @@ namespace ts {
1271
1319
return scanHexadecimalEscape ( /*numDigits*/ 4 ) ;
1272
1320
1273
1321
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
+ }
1274
1333
// '\xDD'
1275
1334
return scanHexadecimalEscape ( /*numDigits*/ 2 ) ;
1276
1335
@@ -1561,7 +1620,7 @@ namespace ts {
1561
1620
tokenValue = scanString ( ) ;
1562
1621
return token = SyntaxKind . StringLiteral ;
1563
1622
case CharacterCodes . backtick :
1564
- return token = scanTemplateAndSetTokenValue ( ) ;
1623
+ return token = scanTemplateAndSetTokenValue ( /* isTaggedTemplate */ false ) ;
1565
1624
case CharacterCodes . percent :
1566
1625
if ( text . charCodeAt ( pos + 1 ) === CharacterCodes . equals ) {
1567
1626
return pos += 2 , token = SyntaxKind . PercentEqualsToken ;
@@ -2019,10 +2078,15 @@ namespace ts {
2019
2078
/**
2020
2079
* Unconditionally back up and scan a template expression portion.
2021
2080
*/
2022
- function reScanTemplateToken ( ) : SyntaxKind {
2081
+ function reScanTemplateToken ( isTaggedTemplate : boolean ) : SyntaxKind {
2023
2082
Debug . assert ( token === SyntaxKind . CloseBraceToken , "'reScanTemplateToken' should only be called on a '}'" ) ;
2024
2083
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 ) ;
2026
2090
}
2027
2091
2028
2092
function reScanJsxToken ( ) : JsxTokenSyntaxKind {
0 commit comments