diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 6bb7d2b777742..8d925784fc154 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6737,6 +6737,10 @@ "category": "Error", "code": 17020 }, + "Unicode escape sequence cannot appear here.": { + "category": "Error", + "code": 17021 + }, "Circularity detected while resolving configuration: {0}": { "category": "Error", "code": 18000 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b55ba9b70fc8e..bd8081d1267a1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2657,6 +2657,13 @@ namespace Parser { return createIdentifier(tokenIsIdentifierOrKeyword(token()), diagnosticMessage); } + function parseIdentifierNameErrorOnUnicodeEscapeSequence(): Identifier { + if (scanner.hasUnicodeEscape() || scanner.hasExtendedUnicodeEscape()) { + parseErrorAtCurrentToken(Diagnostics.Unicode_escape_sequence_cannot_appear_here); + } + return createIdentifier(tokenIsIdentifierOrKeyword(token())); + } + function isLiteralPropertyName(): boolean { return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.StringLiteral || @@ -3522,7 +3529,7 @@ namespace Parser { entity = finishNode( factory.createQualifiedName( entity, - parseRightSideOfDot(allowReservedWords, /*allowPrivateIdentifiers*/ false) as Identifier + parseRightSideOfDot(allowReservedWords, /*allowPrivateIdentifiers*/ false, /*allowUnicodeEscapeSequenceInIdentifierName*/ true) as Identifier ), pos ); @@ -3534,7 +3541,7 @@ namespace Parser { return finishNode(factory.createQualifiedName(entity, name), entity.pos); } - function parseRightSideOfDot(allowIdentifierNames: boolean, allowPrivateIdentifiers: boolean): Identifier | PrivateIdentifier { + function parseRightSideOfDot(allowIdentifierNames: boolean, allowPrivateIdentifiers: boolean, allowUnicodeEscapeSequenceInIdentifierName: boolean): Identifier | PrivateIdentifier { // Technically a keyword is valid here as all identifiers and keywords are identifier names. // However, often we'll encounter this in error situations when the identifier or keyword // is actually starting another valid construct. @@ -3570,7 +3577,11 @@ namespace Parser { return allowPrivateIdentifiers ? node : createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Identifier_expected); } - return allowIdentifierNames ? parseIdentifierName() : parseIdentifier(); + if (allowIdentifierNames) { + return allowUnicodeEscapeSequenceInIdentifierName ? parseIdentifierName() : parseIdentifierNameErrorOnUnicodeEscapeSequence(); + } + + return parseIdentifier(); } function parseTemplateSpans(isTaggedTemplate: boolean) { @@ -5945,7 +5956,7 @@ namespace Parser { // If it wasn't then just try to parse out a '.' and report an error. parseExpectedToken(SyntaxKind.DotToken, Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access); // private names will never work with `super` (`super.#foo`), but that's a semantic error, not syntactic - return finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true)), pos); + return finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true, /*allowUnicodeEscapeSequenceInIdentifierName*/ true)), pos); } function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean, topInvalidNodePosition?: number, openingTag?: JsxOpeningElement | JsxOpeningFragment, mustBeUnary = false): JsxElement | JsxSelfClosingElement | JsxFragment { @@ -6140,7 +6151,7 @@ namespace Parser { } let expression: PropertyAccessExpression | Identifier | ThisExpression = initialExpression; while (parseOptional(SyntaxKind.DotToken)) { - expression = finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos); + expression = finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false, /*allowUnicodeEscapeSequenceInIdentifierName*/ false)), pos); } return expression as JsxTagNameExpression; } @@ -6150,10 +6161,10 @@ namespace Parser { scanJsxIdentifier(); const isThis = token() === SyntaxKind.ThisKeyword; - const tagName = parseIdentifierName(); + const tagName = parseIdentifierNameErrorOnUnicodeEscapeSequence(); if (parseOptional(SyntaxKind.ColonToken)) { scanJsxIdentifier(); - return finishNode(factory.createJsxNamespacedName(tagName, parseIdentifierName()), pos); + return finishNode(factory.createJsxNamespacedName(tagName, parseIdentifierNameErrorOnUnicodeEscapeSequence()), pos); } return isThis ? finishNode(factory.createToken(SyntaxKind.ThisKeyword), pos) : tagName; } @@ -6214,10 +6225,10 @@ namespace Parser { const pos = getNodePos(); scanJsxIdentifier(); - const attrName = parseIdentifierName(); + const attrName = parseIdentifierNameErrorOnUnicodeEscapeSequence(); if (parseOptional(SyntaxKind.ColonToken)) { scanJsxIdentifier(); - return finishNode(factory.createJsxNamespacedName(attrName, parseIdentifierName()), pos); + return finishNode(factory.createJsxNamespacedName(attrName, parseIdentifierNameErrorOnUnicodeEscapeSequence()), pos); } return attrName; } @@ -6307,7 +6318,7 @@ namespace Parser { } function parsePropertyAccessExpressionRest(pos: number, expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) { - const name = parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true); + const name = parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true, /*allowUnicodeEscapeSequenceInIdentifierName*/ true); const isOptionalChain = questionDotToken || tryReparseOptionalChain(expression); const propertyAccess = isOptionalChain ? factoryCreatePropertyAccessChain(expression, questionDotToken, name) : diff --git a/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt b/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt new file mode 100644 index 0000000000000..cc664d0d2ece4 --- /dev/null +++ b/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt @@ -0,0 +1,64 @@ +file.tsx(15,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(16,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(17,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(18,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(19,6): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(20,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(21,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(22,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(23,4): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(26,9): error TS17021: Unicode escape sequence cannot appear here. +file.tsx(27,9): error TS17021: Unicode escape sequence cannot appear here. + + +==== file.tsx (11 errors) ==== + import * as React from "react"; + declare global { + namespace JSX { + interface IntrinsicElements { + "a-b": any; + "a-c": any; + } + } + } + const Compa = (x: {x: number}) =>
{"" + x}
; + const x = { video: () => null } + + // unicode escape sequence is not allowed in tag name or JSX attribute name. + // tag name: + ; <\u0061> + ~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; <\u0061-b> + ~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; <\u{0061}> + ~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; <\u{0061}-b> + ~~~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~~~ +!!! error TS17021: Unicode escape sequence cannot appear here. + + // attribute name + ;