From 9851e90cc7c93e157ee58a04ffe97d5b87d24d9e Mon Sep 17 00:00:00 2001 From: Jack Works Date: Sat, 21 Jan 2023 13:40:56 +0800 Subject: [PATCH 1/5] chore: disallows unicode escape sequence in jsx --- src/compiler/diagnosticMessages.json | 4 ++ src/compiler/parser.ts | 27 +++++--- .../getSupportedCodeFixes-can-be-proxied.js | 3 + .../unicodeEscapesInJsxtags.errors.txt | 64 +++++++++++++++++++ .../reference/unicodeEscapesInJsxtags.js | 43 ++++++++----- .../reference/unicodeEscapesInJsxtags.symbols | 48 ++++++++------ .../reference/unicodeEscapesInJsxtags.types | 58 +++++++++++------ .../jsx/unicodeEscapesInJsxtags.tsx | 23 ++++--- 8 files changed, 200 insertions(+), 70 deletions(-) create mode 100644 tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 31af975442d7b..d2a238a7ec23a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6637,6 +6637,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 b448910799401..6cd3f702b1848 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2605,6 +2605,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 || @@ -3454,7 +3461,7 @@ namespace Parser { entity = finishNode( factory.createQualifiedName( entity, - parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier + parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false, /** allowUnicodeEscapeSequenceInIdentifierName */ true) as Identifier ), pos ); @@ -3466,7 +3473,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. @@ -3502,7 +3509,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) { @@ -5869,7 +5880,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(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 { @@ -6058,9 +6069,9 @@ namespace Parser { // 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; } @@ -6099,7 +6110,7 @@ namespace Parser { scanJsxIdentifier(); const pos = getNodePos(); - return finishNode(factory.createJsxAttribute(parseIdentifierName(), parseJsxAttributeValue()), pos); + return finishNode(factory.createJsxAttribute(parseIdentifierNameErrorOnUnicodeEscapeSequence(), parseJsxAttributeValue()), pos); } function parseJsxAttributeValue(): JsxAttributeValue | undefined { @@ -6205,7 +6216,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 ? factory.createPropertyAccessChain(expression, questionDotToken, name) : diff --git a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js index 5a82b7befb823..2613f68b205a7 100644 --- a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js +++ b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js @@ -1444,6 +1444,7 @@ Info 32 [00:01:13.000] response: "17016", "17017", "17018", + "17021", "18000", "18002", "18003", @@ -2792,6 +2793,7 @@ Info 38 [00:01:19.000] response: "17016", "17017", "17018", + "17021", "18000", "18002", "18003", @@ -4052,6 +4054,7 @@ Info 40 [00:01:21.000] response: "17016", "17017", "17018", + "17021", "18000", "18002", "18003", diff --git a/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt b/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt new file mode 100644 index 0000000000000..33e81cb7a4627 --- /dev/null +++ b/tests/baselines/reference/unicodeEscapesInJsxtags.errors.txt @@ -0,0 +1,64 @@ +tests/cases/conformance/jsx/file.tsx(15,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(16,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(17,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(18,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(19,6): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(20,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(21,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(22,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(23,4): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(26,9): error TS17021: Unicode escape sequence cannot appear here. +tests/cases/conformance/jsx/file.tsx(27,9): error TS17021: 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 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 + ;