@@ -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