diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ac14f37653136..931a370e80bbb 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6409,6 +6409,10 @@ "category": "Error", "code": 17018 }, + "Unicode escape sequence cannot appear here.": { + "category": "Error", + "code": 17019 + }, "Circularity detected while resolving configuration: {0}": { "category": "Error", "code": 18000 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f9aa3e57498ed..ad6693f47b5a9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2045,6 +2045,13 @@ namespace ts { 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 || @@ -2898,7 +2905,7 @@ namespace ts { entity = finishNode( factory.createQualifiedName( entity, - parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier + parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false, /** allowUnicodeEscapeSequenceInIdentifierName */ true) as Identifier ), pos ); @@ -2910,7 +2917,7 @@ namespace ts { 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. @@ -2946,7 +2953,11 @@ namespace ts { 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) { @@ -5302,7 +5313,7 @@ namespace ts { // 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(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true)), pos); + return finishNode(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true, /** allowUnicodeEscapeSequenceInIdentifierName */ true)), pos); } function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean, topInvalidNodePosition?: number, openingTag?: JsxOpeningElement | JsxOpeningFragment): JsxElement | JsxSelfClosingElement | JsxFragment { @@ -5491,9 +5502,9 @@ namespace ts { // We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword // We only want to consider "this" as a primaryExpression let expression: JsxTagNameExpression = token() === SyntaxKind.ThisKeyword ? - parseTokenNode() : parseIdentifierName(); + parseTokenNode() : parseIdentifierNameErrorOnUnicodeEscapeSequence(); while (parseOptional(SyntaxKind.DotToken)) { - expression = finishNode(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos) as JsxTagNamePropertyAccess; + expression = finishNode(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false, /** allowUnicodeEscapeSequenceInIdentifierName */ false)), pos) as JsxTagNamePropertyAccess; } return expression; } @@ -5532,7 +5543,7 @@ namespace ts { scanJsxIdentifier(); const pos = getNodePos(); - return finishNode(factory.createJsxAttribute(parseIdentifierName(), parseJsxAttributeValue()), pos); + return finishNode(factory.createJsxAttribute(parseIdentifierNameErrorOnUnicodeEscapeSequence(), parseJsxAttributeValue()), pos); } function parseJsxAttributeValue(): JsxAttributeValue | undefined { @@ -5638,7 +5649,7 @@ namespace ts { } 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 ? factory.createPropertyAccessChain(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..74a19555c5b19 --- /dev/null +++ b/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt @@ -0,0 +1,64 @@ +tests/cases/conformance/jsx/file.tsx(15,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(16,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(17,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(18,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(19,6): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(20,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(21,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(22,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(23,4): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(26,9): error TS17019: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(27,9): error TS17019: Unicode escape sequence cannot appear here. + + +==== tests/cases/conformance/jsx/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 TS17019: Unicode escape sequence cannot appear here. + ; <\u0061-b> + ~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; <\u{0061}> + ~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; <\u{0061}-b> + ~~~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + ; + ~~~~~~~~~~~~ +!!! error TS17019: Unicode escape sequence cannot appear here. + + // attribute name + ;