From a47eb2c42ddb7353b330cf8dc296c31f5daf3fc3 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 6 May 2020 10:08:02 +0800 Subject: [PATCH 1/8] fix: range of ts2657 (jsx expr must have parent) and remove 2695 (LHS expr of comma has no side effects) --- src/compiler/checker.ts | 10 +++++++- src/compiler/parser.ts | 7 +++--- .../jsxEsprimaFbTestSuite.errors.txt | 14 ++++++----- .../jsxInvalidEsprimaTestSuite.errors.txt | 25 ++++++++----------- .../reference/tsxErrorRecovery2.errors.txt | 25 ++++++++----------- .../reference/tsxErrorRecovery3.errors.txt | 25 ++++++++----------- .../reference/tsxFragmentErrors.errors.txt | 9 ++++++- 7 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32965372e83fc..f5e3d68e3b36f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28742,7 +28742,15 @@ namespace ts { } case SyntaxKind.CommaToken: if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) { - error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); + const sf = getSourceFileOfNode(left); + const isInDiag2657 = sf.parseDiagnostics.some(diag => { + if (diag.code !== Diagnostics.JSX_expressions_must_have_one_parent_element.code) return false; + const start = diag.start; + const end = start + diag.length; + if (left.pos >= start && left.pos <= end) return true; + return false; + }); + if (!isInDiag2657) error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); } return rightType; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9754cdf64d362..d87a5b864887a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4503,7 +4503,7 @@ namespace ts { return finishNode(node); } - function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean): JsxElement | JsxSelfClosingElement | JsxFragment { + function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean, topInvalidNodePosition?: number): JsxElement | JsxSelfClosingElement | JsxFragment { const opening = parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext); let result: JsxElement | JsxSelfClosingElement | JsxFragment; if (opening.kind === SyntaxKind.JsxOpeningElement) { @@ -4541,15 +4541,16 @@ namespace ts { // Since JSX elements are invalid < operands anyway, this lookahead parse will only occur in error scenarios // of one sort or another. if (inExpressionContext && token() === SyntaxKind.LessThanToken) { - const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true)); + const topBadPos = typeof topInvalidNodePosition === "undefined" ? result.pos : topInvalidNodePosition; + const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true, topBadPos)); if (invalidElement) { - parseErrorAtCurrentToken(Diagnostics.JSX_expressions_must_have_one_parent_element); const badNode = createNode(SyntaxKind.BinaryExpression, result.pos); badNode.end = invalidElement.end; badNode.left = result; badNode.right = invalidElement; badNode.operatorToken = createMissingNode(SyntaxKind.CommaToken, /*reportAtCurrentPosition*/ false); badNode.operatorToken.pos = badNode.operatorToken.end = badNode.right.pos; + parseErrorAt(topBadPos, invalidElement.end, Diagnostics.JSX_expressions_must_have_one_parent_element); return badNode; } } diff --git a/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt b/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt index 91f3e22962cd3..aba1e2748813d 100644 --- a/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt +++ b/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt @@ -1,7 +1,7 @@ -tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,1): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(37,43): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,17): error TS1005: '{' expected. +tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS1005: ';' expected. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS2304: Cannot find name 'right'. -tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,41): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,57): error TS1109: Expression expected. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expression expected. @@ -45,16 +45,18 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expr
@test content
;

7x invalid-js-identifier
; + + right=monkeys /> gorillas />; - ~~~~~~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2657: JSX expressions must have one parent element. ~ !!! error TS1005: '{' expected. ~~~~~ -!!! error TS2304: Cannot find name 'right'. +!!! error TS1005: ';' expected. ~~~~~ -!!! error TS2657: JSX expressions must have one parent element. +!!! error TS2304: Cannot find name 'right'. ~ !!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? ~ diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt index 1195ff4457e32..85333814a85a0 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt @@ -33,10 +33,8 @@ tests/cases/conformance/jsx/16.tsx(1,2): error TS17008: JSX element 'a' has no c tests/cases/conformance/jsx/16.tsx(1,10): error TS1005: 'one
two
;; - ~~~~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. - ~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. -==== tests/cases/conformance/jsx/19.tsx (2 errors) ==== +==== tests/cases/conformance/jsx/19.tsx (1 errors) ==== var x =
one
/* intervening comment */
two
;; - ~~~~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. - ~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. ==== tests/cases/conformance/jsx/20.tsx (2 errors) ==== {"str";}; @@ -313,14 +307,15 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected. !!! error TS1005: '; - ~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. + ~~~~~~~~~ ~ !!! error TS1005: '{' expected. ~ !!! error TS1003: Identifier expected. +!!! error TS2657: JSX expressions must have one parent element. + !!! error TS1005: '}; diff --git a/tests/baselines/reference/tsxErrorRecovery2.errors.txt b/tests/baselines/reference/tsxErrorRecovery2.errors.txt index b1c759644675f..ad68001b85c09 100644 --- a/tests/baselines/reference/tsxErrorRecovery2.errors.txt +++ b/tests/baselines/reference/tsxErrorRecovery2.errors.txt @@ -1,23 +1,20 @@ -tests/cases/conformance/jsx/file1.tsx(3,1): error TS2695: Left side of comma operator is unused and has no side effects. -tests/cases/conformance/jsx/file1.tsx(5,1): error TS2657: JSX expressions must have one parent element. -tests/cases/conformance/jsx/file2.tsx(1,9): error TS2695: Left side of comma operator is unused and has no side effects. -tests/cases/conformance/jsx/file2.tsx(2,1): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file1.tsx(1,48): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file2.tsx(1,8): error TS2657: JSX expressions must have one parent element. -==== tests/cases/conformance/jsx/file1.tsx (2 errors) ==== +==== tests/cases/conformance/jsx/file1.tsx (1 errors) ==== declare namespace JSX { interface Element { } } + +
~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects.
- - + ~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. -==== tests/cases/conformance/jsx/file2.tsx (2 errors) ==== - var x =
- ~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. - -!!! error TS2657: JSX expressions must have one parent element. \ No newline at end of file +==== tests/cases/conformance/jsx/file2.tsx (1 errors) ==== + var x =
+ ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2657: JSX expressions must have one parent element. + \ No newline at end of file diff --git a/tests/baselines/reference/tsxErrorRecovery3.errors.txt b/tests/baselines/reference/tsxErrorRecovery3.errors.txt index 2ee6b582cd3b7..147c72354b099 100644 --- a/tests/baselines/reference/tsxErrorRecovery3.errors.txt +++ b/tests/baselines/reference/tsxErrorRecovery3.errors.txt @@ -1,35 +1,32 @@ -tests/cases/conformance/jsx/file1.tsx(3,1): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/conformance/jsx/file1.tsx(1,48): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file1.tsx(3,2): error TS2304: Cannot find name 'React'. tests/cases/conformance/jsx/file1.tsx(4,2): error TS2304: Cannot find name 'React'. -tests/cases/conformance/jsx/file1.tsx(5,1): error TS2657: JSX expressions must have one parent element. -tests/cases/conformance/jsx/file2.tsx(1,9): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/conformance/jsx/file2.tsx(1,8): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file2.tsx(1,10): error TS2304: Cannot find name 'React'. tests/cases/conformance/jsx/file2.tsx(1,21): error TS2304: Cannot find name 'React'. -tests/cases/conformance/jsx/file2.tsx(2,1): error TS2657: JSX expressions must have one parent element. -==== tests/cases/conformance/jsx/file1.tsx (4 errors) ==== +==== tests/cases/conformance/jsx/file1.tsx (3 errors) ==== declare namespace JSX { interface Element { } } + +
~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. ~~~ !!! error TS2304: Cannot find name 'React'.
+ ~~~~~~~~~~~ +!!! error TS2657: JSX expressions must have one parent element. ~~~ !!! error TS2304: Cannot find name 'React'. - -!!! error TS2657: JSX expressions must have one parent element. -==== tests/cases/conformance/jsx/file2.tsx (4 errors) ==== +==== tests/cases/conformance/jsx/file2.tsx (3 errors) ==== var x =
- ~~~~~~~~~~~ -!!! error TS2695: Left side of comma operator is unused and has no side effects. + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2657: JSX expressions must have one parent element. ~~~ !!! error TS2304: Cannot find name 'React'. ~~~ !!! error TS2304: Cannot find name 'React'. - - -!!! error TS2657: JSX expressions must have one parent element. \ No newline at end of file + \ No newline at end of file diff --git a/tests/baselines/reference/tsxFragmentErrors.errors.txt b/tests/baselines/reference/tsxFragmentErrors.errors.txt index 03397452abc11..f323dffe07658 100644 --- a/tests/baselines/reference/tsxFragmentErrors.errors.txt +++ b/tests/baselines/reference/tsxFragmentErrors.errors.txt @@ -1,9 +1,10 @@ +tests/cases/conformance/jsx/file.tsx(7,24): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file.tsx(9,7): error TS17015: Expected corresponding closing tag for JSX fragment. tests/cases/conformance/jsx/file.tsx(9,11): error TS17014: JSX fragment has no corresponding closing tag. tests/cases/conformance/jsx/file.tsx(11,17): error TS1005: 'hi // Error + ~~~~~~~~~~~~~~~~~~~ ~~~ !!! error TS17015: Expected corresponding closing tag for JSX fragment. ~~~~~~~~~ + <>eof // Error + ~~~~~~~~~~~~~~~~ +!!! error TS2657: JSX expressions must have one parent element. ~~ !!! error TS17014: JSX fragment has no corresponding closing tag. From 1a93e72d2f1dacade4ec70036064c402f94ed91e Mon Sep 17 00:00:00 2001 From: Jack Works Date: Mon, 25 May 2020 21:21:31 +0800 Subject: [PATCH 2/8] feat: add code fix for 2657 --- src/compiler/diagnosticMessages.json | 8 +++ src/services/codefixes/wrapJsxInFragment.ts | 71 +++++++++++++++++++ src/services/tsconfig.json | 1 + .../fourslash/codeFixWrapJsxInFragment.ts | 7 ++ .../fourslash/codeFixWrapJsxInFragment2.ts | 7 ++ 5 files changed, 94 insertions(+) create mode 100644 src/services/codefixes/wrapJsxInFragment.ts create mode 100644 tests/cases/fourslash/codeFixWrapJsxInFragment.ts create mode 100644 tests/cases/fourslash/codeFixWrapJsxInFragment2.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ef46447faacdb..2ecfefbaaaf69 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5705,6 +5705,14 @@ "category": "Message", "code": 95118 }, + "Wrap JSX in JSX Fragment": { + "category": "Message", + "code": 95119 + }, + "Wrap all JSX in JSX Fragment": { + "category": "Message", + "code": 95120 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/codefixes/wrapJsxInFragment.ts b/src/services/codefixes/wrapJsxInFragment.ts new file mode 100644 index 0000000000000..dcd79795974ed --- /dev/null +++ b/src/services/codefixes/wrapJsxInFragment.ts @@ -0,0 +1,71 @@ +/* @internal */ +namespace ts.codefix { + const fixID = "wrapJsxInFragment"; + const errorCodes = [Diagnostics.JSX_expressions_must_have_one_parent_element.code]; + registerCodeFix({ + errorCodes, + getCodeActions: context => { + const { jsx } = context.program.getCompilerOptions(); + if (jsx !== JsxEmit.React && jsx !== JsxEmit.ReactNative) { + return undefined; + } + const { sourceFile, span } = context; + const node = findNodeToFix(sourceFile, span.start); + if (!node) return undefined; + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); + return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_JSX_in_JSX_Fragment, fixID, Diagnostics.Wrap_all_JSX_in_JSX_Fragment)]; + }, + fixIds: [fixID], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { + const node = findNodeToFix(context.sourceFile, diag.start); + if (!node) return undefined; + doChange(changes, context.sourceFile, node); + }), + }); + + function findNodeToFix(sourceFile: SourceFile, pos: number): BinaryExpression | undefined { + // The error always at 1st token that is "<" in "" + const lessThanToken = getTokenAtPosition(sourceFile, pos); + const firstJsxElementOrOpenElement = lessThanToken.parent; + let binaryExpr = firstJsxElementOrOpenElement.parent; + if (!isBinaryExpression(binaryExpr)) { + // In case the start element is a JsxSelfClosingElement, it the end. + // For JsxOpenElement, find one more parent + binaryExpr = binaryExpr.parent; + if (!isBinaryExpression(binaryExpr)) return undefined; + } + if (!nodeIsMissing(binaryExpr.operatorToken)) return undefined; + return binaryExpr; + } + + function doChange(changeTracker: textChanges.ChangeTracker, sf: SourceFile, node: Node) { + const jsx = flattenInvalidBinaryExpr(node); + if (jsx) changeTracker.replaceNode(sf, node, createJsxFragment(createJsxOpeningFragment(), jsx, createJsxJsxClosingFragment())); + } + // The invalid syntax is constructed as + // InvalidJsxTree :: One of + // JsxElement CommaToken InvalidJsxTree + // JsxElement CommaToken JsxElement + function flattenInvalidBinaryExpr(node: Node): JsxChild[] | undefined { + const children: JsxChild[] = []; + let current = node; + while (true) { + if (isBinaryExpression(current) && nodeIsMissing(current.operatorToken) && current.operatorToken.kind === SyntaxKind.CommaToken) { + children.push(current.left); + if (isJsxChild(current.right)) { + children.push(current.right); + // Indicates the tree has go to the bottom + return children; + } + else if (isBinaryExpression(current.right)) { + current = current.right; + continue; + } + // Unreachable case + else return undefined; + } + // Unreachable case + else return undefined; + } + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index f3e6e5ce4566b..0da3e05f10a01 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -95,6 +95,7 @@ "codefixes/useDefaultImport.ts", "codefixes/useBigintLiteral.ts", "codefixes/fixAddModuleReferTypeMissingTypeof.ts", + "codefixes/wrapJsxInFragment.ts", "codefixes/convertToMappedObjectType.ts", "codefixes/removeUnnecessaryAwait.ts", "codefixes/splitTypeOnlyImport.ts", diff --git a/tests/cases/fourslash/codeFixWrapJsxInFragment.ts b/tests/cases/fourslash/codeFixWrapJsxInFragment.ts new file mode 100644 index 0000000000000..10acf5916b72f --- /dev/null +++ b/tests/cases/fourslash/codeFixWrapJsxInFragment.ts @@ -0,0 +1,7 @@ +/// + +// @jsx: react +// @Filename: /a.tsx +////[||] + +verify.rangeAfterCodeFix(`<>`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0); diff --git a/tests/cases/fourslash/codeFixWrapJsxInFragment2.ts b/tests/cases/fourslash/codeFixWrapJsxInFragment2.ts new file mode 100644 index 0000000000000..229f554c801a8 --- /dev/null +++ b/tests/cases/fourslash/codeFixWrapJsxInFragment2.ts @@ -0,0 +1,7 @@ +/// + +// @jsx: react +// @Filename: /a.tsx +////[||] + +verify.rangeAfterCodeFix(`<>`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0); From 446749bc7ec66651ae1887116e35d5cb2fdb44d6 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 6 May 2020 11:12:59 +0800 Subject: [PATCH 3/8] fix: resolve review --- src/compiler/checker.ts | 6 ++---- src/compiler/parser.ts | 2 +- src/services/codefixes/wrapJsxInFragment.ts | 2 +- .../baselines/reference/jsxEsprimaFbTestSuite.errors.txt | 4 +--- .../reference/jsxInvalidEsprimaTestSuite.errors.txt | 8 ++++---- tests/baselines/reference/tsxErrorRecovery2.errors.txt | 8 +++----- tests/baselines/reference/tsxErrorRecovery3.errors.txt | 8 +++----- tests/baselines/reference/tsxFragmentErrors.errors.txt | 4 +--- 8 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f5e3d68e3b36f..e622054849d41 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28743,12 +28743,10 @@ namespace ts { case SyntaxKind.CommaToken: if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) { const sf = getSourceFileOfNode(left); + const sourceText = sf.text; const isInDiag2657 = sf.parseDiagnostics.some(diag => { if (diag.code !== Diagnostics.JSX_expressions_must_have_one_parent_element.code) return false; - const start = diag.start; - const end = start + diag.length; - if (left.pos >= start && left.pos <= end) return true; - return false; + return textSpanContainsPosition(diag, skipTrivia(sourceText, left.pos)); }); if (!isInDiag2657) error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d87a5b864887a..e886c9e5477eb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4550,7 +4550,7 @@ namespace ts { badNode.right = invalidElement; badNode.operatorToken = createMissingNode(SyntaxKind.CommaToken, /*reportAtCurrentPosition*/ false); badNode.operatorToken.pos = badNode.operatorToken.end = badNode.right.pos; - parseErrorAt(topBadPos, invalidElement.end, Diagnostics.JSX_expressions_must_have_one_parent_element); + parseErrorAt(skipTrivia(sourceText, topBadPos), invalidElement.end, Diagnostics.JSX_expressions_must_have_one_parent_element); return badNode; } } diff --git a/src/services/codefixes/wrapJsxInFragment.ts b/src/services/codefixes/wrapJsxInFragment.ts index dcd79795974ed..1dd710645f6b2 100644 --- a/src/services/codefixes/wrapJsxInFragment.ts +++ b/src/services/codefixes/wrapJsxInFragment.ts @@ -13,7 +13,7 @@ namespace ts.codefix { const node = findNodeToFix(sourceFile, span.start); if (!node) return undefined; const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); - return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_JSX_in_JSX_Fragment, fixID, Diagnostics.Wrap_all_JSX_in_JSX_Fragment)]; + return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_elements_in_JSX_fragment, fixID, Diagnostics.Wrap_all_elements_in_JSX_fragment)]; }, fixIds: [fixID], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { diff --git a/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt b/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt index aba1e2748813d..c9e4aeb457cbe 100644 --- a/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt +++ b/tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(37,43): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,1): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,17): error TS1005: '{' expected. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS1005: ';' expected. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS2304: Cannot find name 'right'. @@ -45,8 +45,6 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expr
@test content
;

7x invalid-js-identifier
; - - right=monkeys /> gorillas />; ~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt index 85333814a85a0..cc78c53eb0fec 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt @@ -33,8 +33,8 @@ tests/cases/conformance/jsx/16.tsx(1,2): error TS17008: JSX element 'a' has no c tests/cases/conformance/jsx/16.tsx(1,10): error TS1005: 'one
two
;; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. ==== tests/cases/conformance/jsx/19.tsx (1 errors) ==== var x =
one
/* intervening comment */
two
;; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. ==== tests/cases/conformance/jsx/20.tsx (2 errors) ====
{"str";}; diff --git a/tests/baselines/reference/tsxErrorRecovery2.errors.txt b/tests/baselines/reference/tsxErrorRecovery2.errors.txt index ad68001b85c09..c64f09af9efa1 100644 --- a/tests/baselines/reference/tsxErrorRecovery2.errors.txt +++ b/tests/baselines/reference/tsxErrorRecovery2.errors.txt @@ -1,11 +1,9 @@ -tests/cases/conformance/jsx/file1.tsx(1,48): error TS2657: JSX expressions must have one parent element. -tests/cases/conformance/jsx/file2.tsx(1,8): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file1.tsx(3,1): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file2.tsx(1,9): error TS2657: JSX expressions must have one parent element. ==== tests/cases/conformance/jsx/file1.tsx (1 errors) ==== declare namespace JSX { interface Element { } } - -
~~~~~~~~~~~ @@ -15,6 +13,6 @@ tests/cases/conformance/jsx/file2.tsx(1,8): error TS2657: JSX expressions must h ==== tests/cases/conformance/jsx/file2.tsx (1 errors) ==== var x =
- ~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. \ No newline at end of file diff --git a/tests/baselines/reference/tsxErrorRecovery3.errors.txt b/tests/baselines/reference/tsxErrorRecovery3.errors.txt index 147c72354b099..12d562599e8bd 100644 --- a/tests/baselines/reference/tsxErrorRecovery3.errors.txt +++ b/tests/baselines/reference/tsxErrorRecovery3.errors.txt @@ -1,15 +1,13 @@ -tests/cases/conformance/jsx/file1.tsx(1,48): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file1.tsx(3,1): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file1.tsx(3,2): error TS2304: Cannot find name 'React'. tests/cases/conformance/jsx/file1.tsx(4,2): error TS2304: Cannot find name 'React'. -tests/cases/conformance/jsx/file2.tsx(1,8): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file2.tsx(1,9): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file2.tsx(1,10): error TS2304: Cannot find name 'React'. tests/cases/conformance/jsx/file2.tsx(1,21): error TS2304: Cannot find name 'React'. ==== tests/cases/conformance/jsx/file1.tsx (3 errors) ==== declare namespace JSX { interface Element { } } - -
~~~~~~~~~~~ @@ -23,7 +21,7 @@ tests/cases/conformance/jsx/file2.tsx(1,21): error TS2304: Cannot find name 'Rea ==== tests/cases/conformance/jsx/file2.tsx (3 errors) ==== var x =
- ~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. ~~~ !!! error TS2304: Cannot find name 'React'. diff --git a/tests/baselines/reference/tsxFragmentErrors.errors.txt b/tests/baselines/reference/tsxFragmentErrors.errors.txt index f323dffe07658..46d8a0c09b79c 100644 --- a/tests/baselines/reference/tsxFragmentErrors.errors.txt +++ b/tests/baselines/reference/tsxFragmentErrors.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/jsx/file.tsx(7,24): error TS2657: JSX expressions must have one parent element. +tests/cases/conformance/jsx/file.tsx(9,1): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/file.tsx(9,7): error TS17015: Expected corresponding closing tag for JSX fragment. tests/cases/conformance/jsx/file.tsx(9,11): error TS17014: JSX fragment has no corresponding closing tag. tests/cases/conformance/jsx/file.tsx(11,17): error TS1005: 'hi // Error ~~~~~~~~~~~~~~~~~~~ From fae94761c305aba912ada777681c346d208b1b75 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 6 May 2020 11:15:33 +0800 Subject: [PATCH 4/8] chore: hoist a var --- src/compiler/checker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e622054849d41..722199bf26e0b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28744,9 +28744,10 @@ namespace ts { if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) { const sf = getSourceFileOfNode(left); const sourceText = sf.text; + const start = skipTrivia(sourceText, left.pos); const isInDiag2657 = sf.parseDiagnostics.some(diag => { if (diag.code !== Diagnostics.JSX_expressions_must_have_one_parent_element.code) return false; - return textSpanContainsPosition(diag, skipTrivia(sourceText, left.pos)); + return textSpanContainsPosition(diag, start); }); if (!isInDiag2657) error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); } From 389efeb9b8e6c57bc4f6e19b94f8916247397ec3 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Thu, 7 May 2020 10:03:56 +0800 Subject: [PATCH 5/8] chore: add test for skipTrivia --- .../reference/jsxInvalidEsprimaTestSuite.errors.txt | 6 +++--- tests/baselines/reference/jsxInvalidEsprimaTestSuite.js | 4 ++-- .../baselines/reference/jsxInvalidEsprimaTestSuite.symbols | 2 +- tests/baselines/reference/jsxInvalidEsprimaTestSuite.types | 2 +- tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt index cc78c53eb0fec..a7deeac2aaaeb 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt @@ -33,7 +33,7 @@ tests/cases/conformance/jsx/16.tsx(1,2): error TS17008: JSX element 'a' has no c tests/cases/conformance/jsx/16.tsx(1,10): error TS1005: 'one
two
;; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + var x = /* Leading trivia */
one
two
;; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2657: JSX expressions must have one parent element. ==== tests/cases/conformance/jsx/19.tsx (1 errors) ==== var x =
one
/* intervening comment */
two
;; diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.js b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.js index b04a03079b717..fb128b51e4c3c 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.js +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.js @@ -37,7 +37,7 @@ declare var React: any; //// [17.tsx] ; //// [18.tsx] -var x =
one
two
;; +var x = /* Leading trivia */
one
two
;; //// [19.tsx] var x =
one
/* intervening comment */
two
;; //// [20.tsx] @@ -117,7 +117,7 @@ a['foo'] > ; //// [17.jsx]
;; //// [18.jsx] -var x =
one
,
two
; +var x = /* Leading trivia */
one
,
two
; ; //// [19.jsx] var x =
one
/* intervening comment */, /* intervening comment */
two
; diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.symbols b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.symbols index 7be7925fa39a0..3e029e49c79a6 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.symbols +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.symbols @@ -50,7 +50,7 @@ No type information for this code.=== tests/cases/conformance/jsx/17.tsx === >b : Symbol(b, Decl(17.tsx, 0, 2)) === tests/cases/conformance/jsx/18.tsx === -var x =
one
two
;; +var x = /* Leading trivia */
one
two
;; >x : Symbol(x, Decl(18.tsx, 0, 3), Decl(19.tsx, 0, 3)) === tests/cases/conformance/jsx/19.tsx === diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.types b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.types index adb7f6b7271d6..3b7ca699c8c86 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.types +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.types @@ -159,7 +159,7 @@ declare var React: any; > : any === tests/cases/conformance/jsx/18.tsx === -var x =
one
two
;; +var x = /* Leading trivia */
one
two
;; >x : any >
one
two
: any >
one
: any diff --git a/tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx b/tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx index 599efc358fcfb..5164dc1ce55ae 100644 --- a/tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx +++ b/tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx @@ -36,7 +36,7 @@ declare var React: any; // @filename: 17.tsx
; // @filename: 18.tsx -var x =
one
two
;; +var x = /* Leading trivia */
one
two
;; // @filename: 19.tsx var x =
one
/* intervening comment */
two
;; // @filename: 20.tsx From 2548581866977a6c17fb76a4ea211d4955075cda Mon Sep 17 00:00:00 2001 From: Jack Works Date: Mon, 25 May 2020 21:26:34 +0800 Subject: [PATCH 6/8] fix: rebase error --- src/services/codefixes/wrapJsxInFragment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/wrapJsxInFragment.ts b/src/services/codefixes/wrapJsxInFragment.ts index 1dd710645f6b2..1071d068f1c34 100644 --- a/src/services/codefixes/wrapJsxInFragment.ts +++ b/src/services/codefixes/wrapJsxInFragment.ts @@ -13,7 +13,7 @@ namespace ts.codefix { const node = findNodeToFix(sourceFile, span.start); if (!node) return undefined; const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); - return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_elements_in_JSX_fragment, fixID, Diagnostics.Wrap_all_elements_in_JSX_fragment)]; + return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_all_JSX_in_JSX_Fragment, fixID, Diagnostics.Wrap_all_JSX_in_JSX_Fragment)]; }, fixIds: [fixID], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { From 6a1f004d0c85dd94e9e82186eed7d328cca2299d Mon Sep 17 00:00:00 2001 From: Jack Works Date: Tue, 2 Jun 2020 03:03:37 +0800 Subject: [PATCH 7/8] Update src/compiler/diagnosticMessages.json Co-authored-by: Andrew Branch --- src/compiler/diagnosticMessages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a0977d2cf6625..be27a8ecc1cc1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5709,11 +5709,11 @@ "category": "Message", "code": 95119 }, - "Wrap JSX in JSX Fragment": { + "Wrap in JSX fragment": { "category": "Message", "code": 95120 }, - "Wrap all JSX in JSX Fragment": { + "Wrap all unparented JSX in JSX fragment": { "category": "Message", "code": 95121 }, From cb1fdb7d4ff8e0917b20246515ec93c26a6dc41d Mon Sep 17 00:00:00 2001 From: Jack Works Date: Tue, 2 Jun 2020 03:03:46 +0800 Subject: [PATCH 8/8] Update src/services/codefixes/wrapJsxInFragment.ts Co-authored-by: Andrew Branch --- src/services/codefixes/wrapJsxInFragment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/wrapJsxInFragment.ts b/src/services/codefixes/wrapJsxInFragment.ts index 1071d068f1c34..243bbf778da76 100644 --- a/src/services/codefixes/wrapJsxInFragment.ts +++ b/src/services/codefixes/wrapJsxInFragment.ts @@ -13,7 +13,7 @@ namespace ts.codefix { const node = findNodeToFix(sourceFile, span.start); if (!node) return undefined; const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); - return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_all_JSX_in_JSX_Fragment, fixID, Diagnostics.Wrap_all_JSX_in_JSX_Fragment)]; + return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_in_JSX_fragment, fixID, Diagnostics.Wrap_all_unparented_JSX_in_JSX_fragment)]; }, fixIds: [fixID], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {