From 061f02cd64e679eb2aff408f99c0de400136428a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 30 Sep 2021 03:31:59 +0900 Subject: [PATCH] fix(44021): reference jsx pragma when JsxFragment is used (#45894) --- src/compiler/checker.ts | 44 +++++--- .../inlineJsxAndJsxFragPragma.errors.txt | 87 +++++++++++++++ .../reference/inlineJsxAndJsxFragPragma.js | 104 +++++++++++++++++- .../inlineJsxAndJsxFragPragma.symbols | 71 ++++++++++++ .../reference/inlineJsxAndJsxFragPragma.types | 87 ++++++++++++++- .../jsx/inline/inlineJsxAndJsxFragPragma.tsx | 53 ++++++++- 6 files changed, 426 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/inlineJsxAndJsxFragPragma.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 77d83a04d25..1f2ec82c9f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1036,17 +1036,9 @@ namespace ts { } } else { - if (file.localJsxNamespace) { - return file.localJsxNamespace; - } - const jsxPragma = file.pragmas.get("jsx"); - if (jsxPragma) { - const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma; - file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion); - visitNode(file.localJsxFactory, markAsSynthetic); - if (file.localJsxFactory) { - return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText; - } + const localJsxNamespace = getLocalJsxNamespace(file); + if (localJsxNamespace) { + return file.localJsxNamespace = localJsxNamespace; } } } @@ -1068,11 +1060,26 @@ namespace ts { _jsxFactoryEntity = factory.createQualifiedName(factory.createIdentifier(unescapeLeadingUnderscores(_jsxNamespace)), "createElement"); } return _jsxNamespace; + } - function markAsSynthetic(node: Node): VisitResult { - setTextRangePosEnd(node, -1, -1); - return visitEachChild(node, markAsSynthetic, nullTransformationContext); + function getLocalJsxNamespace(file: SourceFile): __String | undefined { + if (file.localJsxNamespace) { + return file.localJsxNamespace; } + const jsxPragma = file.pragmas.get("jsx"); + if (jsxPragma) { + const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma; + file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion); + visitNode(file.localJsxFactory, markAsSynthetic); + if (file.localJsxFactory) { + return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText; + } + } + } + + function markAsSynthetic(node: Node): VisitResult { + setTextRangePosEnd(node, -1, -1); + return visitEachChild(node, markAsSynthetic, nullTransformationContext); } function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { @@ -27598,6 +27605,15 @@ namespace ts { markAliasSymbolAsReferenced(jsxFactorySym); } } + + // For JsxFragment, mark jsx pragma as referenced via resolveName + if (isJsxOpeningFragment(node)) { + const file = getSourceFileOfNode(node); + const localJsxNamespace = getLocalJsxNamespace(file); + if (localJsxNamespace) { + resolveName(jsxFactoryLocation, localJsxNamespace, SymbolFlags.Value, jsxFactoryRefErr, localJsxNamespace, /*isUse*/ true); + } + } } if (isNodeOpeningLikeElement) { diff --git a/tests/baselines/reference/inlineJsxAndJsxFragPragma.errors.txt b/tests/baselines/reference/inlineJsxAndJsxFragPragma.errors.txt new file mode 100644 index 00000000000..18eecd18ab6 --- /dev/null +++ b/tests/baselines/reference/inlineJsxAndJsxFragPragma.errors.txt @@ -0,0 +1,87 @@ +tests/cases/conformance/jsx/inline/preacty-no-fragment.tsx(5,12): error TS6133: 'Fragment' is declared but its value is never read. +tests/cases/conformance/jsx/inline/preacty-only-fragment-no-jsx.tsx(6,1): error TS2304: Cannot find name 'h'. +tests/cases/conformance/jsx/inline/snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2304: Cannot find name 'jsx'. + + +==== tests/cases/conformance/jsx/inline/renderer.d.ts (0 errors) ==== + declare global { + namespace JSX { + interface IntrinsicElements { + [e: string]: any; + } + } + } + export function h(): void; + export function jsx(): void; + export function Fragment(): void; + +==== tests/cases/conformance/jsx/inline/preacty.tsx (0 errors) ==== + /** + * @jsx h + * @jsxFrag Fragment + */ + import {h, Fragment} from "./renderer"; + <>
+ +==== tests/cases/conformance/jsx/inline/snabbdomy.tsx (0 errors) ==== + /* @jsx jsx */ + /* @jsxfrag null */ + import {jsx} from "./renderer"; + <> + +==== tests/cases/conformance/jsx/inline/preacty-only-fragment.tsx (0 errors) ==== + /** + * @jsx h + * @jsxFrag Fragment + */ + import {h, Fragment} from "./renderer"; + <> + +==== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment.tsx (0 errors) ==== + /* @jsx jsx */ + /* @jsxfrag null */ + import {jsx} from "./renderer"; + <> + +==== tests/cases/conformance/jsx/inline/preacty-only-fragment-no-jsx.tsx (1 errors) ==== + /** + * @jsx h + * @jsxFrag Fragment + */ + import {Fragment} from "./renderer"; + <> + ~~ +!!! error TS2304: Cannot find name 'h'. + +==== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment-no-jsx.tsx (1 errors) ==== + /* @jsx jsx */ + /* @jsxfrag null */ + import {} from "./renderer"; + <> + ~~ +!!! error TS2304: Cannot find name 'jsx'. + +==== tests/cases/conformance/jsx/inline/preacty-no-fragment.tsx (1 errors) ==== + /** + * @jsx h + * @jsxFrag Fragment + */ + import {h, Fragment} from "./renderer"; + ~~~~~~~~ +!!! error TS6133: 'Fragment' is declared but its value is never read. +
+ +==== tests/cases/conformance/jsx/inline/snabbdomy-no-fragment.tsx (0 errors) ==== + /* @jsx jsx */ + /* @jsxfrag null */ + import {jsx} from "./renderer"; +
+ +==== tests/cases/conformance/jsx/inline/preacty-only-component.tsx (0 errors) ==== + /** + * @jsx h + */ + import {h} from "./renderer"; + function Component() { return null; } + + \ No newline at end of file diff --git a/tests/baselines/reference/inlineJsxAndJsxFragPragma.js b/tests/baselines/reference/inlineJsxAndJsxFragPragma.js index 98624989f40..a59b5d74616 100644 --- a/tests/baselines/reference/inlineJsxAndJsxFragPragma.js +++ b/tests/baselines/reference/inlineJsxAndJsxFragPragma.js @@ -24,7 +24,58 @@ import {h, Fragment} from "./renderer"; /* @jsx jsx */ /* @jsxfrag null */ import {jsx} from "./renderer"; -<> +<> + +//// [preacty-only-fragment.tsx] +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +<> + +//// [snabbdomy-only-fragment.tsx] +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +<> + +//// [preacty-only-fragment-no-jsx.tsx] +/** + * @jsx h + * @jsxFrag Fragment + */ +import {Fragment} from "./renderer"; +<> + +//// [snabbdomy-only-fragment-no-jsx.tsx] +/* @jsx jsx */ +/* @jsxfrag null */ +import {} from "./renderer"; +<> + +//// [preacty-no-fragment.tsx] +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +
+ +//// [snabbdomy-no-fragment.tsx] +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +
+ +//// [preacty-only-component.tsx] +/** + * @jsx h + */ +import {h} from "./renderer"; +function Component() { return null; } + + //// [preacty.js] "use strict"; @@ -44,3 +95,54 @@ exports.__esModule = true; var renderer_1 = require("./renderer"); (0, renderer_1.jsx)(null, null, (0, renderer_1.jsx)("span", null)); +//// [preacty-only-fragment.js] +"use strict"; +exports.__esModule = true; +/** + * @jsx h + * @jsxFrag Fragment + */ +var renderer_1 = require("./renderer"); +(0, renderer_1.h)(renderer_1.Fragment, null); +//// [snabbdomy-only-fragment.js] +"use strict"; +exports.__esModule = true; +(0, renderer_1.jsx)(null, null); +//// [preacty-only-fragment-no-jsx.js] +"use strict"; +exports.__esModule = true; +/** + * @jsx h + * @jsxFrag Fragment + */ +var renderer_1 = require("./renderer"); +h(renderer_1.Fragment, null); +//// [snabbdomy-only-fragment-no-jsx.js] +"use strict"; +exports.__esModule = true; +jsx(null, null); +//// [preacty-no-fragment.js] +"use strict"; +exports.__esModule = true; +/** + * @jsx h + * @jsxFrag Fragment + */ +var renderer_1 = require("./renderer"); +(0, renderer_1.h)("div", null); +//// [snabbdomy-no-fragment.js] +"use strict"; +exports.__esModule = true; +/* @jsx jsx */ +/* @jsxfrag null */ +var renderer_1 = require("./renderer"); +(0, renderer_1.jsx)("div", null); +//// [preacty-only-component.js] +"use strict"; +exports.__esModule = true; +/** + * @jsx h + */ +var renderer_1 = require("./renderer"); +function Component() { return null; } +(0, renderer_1.h)(Component, null); diff --git a/tests/baselines/reference/inlineJsxAndJsxFragPragma.symbols b/tests/baselines/reference/inlineJsxAndJsxFragPragma.symbols index dd5d7bb6abb..9945b1306fc 100644 --- a/tests/baselines/reference/inlineJsxAndJsxFragPragma.symbols +++ b/tests/baselines/reference/inlineJsxAndJsxFragPragma.symbols @@ -45,3 +45,74 @@ import {jsx} from "./renderer"; >span : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) >span : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) +=== tests/cases/conformance/jsx/inline/preacty-only-fragment.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +>h : Symbol(h, Decl(preacty-only-fragment.tsx, 4, 8)) +>Fragment : Symbol(Fragment, Decl(preacty-only-fragment.tsx, 4, 10)) + +<> + +=== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment.tsx === +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +>jsx : Symbol(jsx, Decl(snabbdomy-only-fragment.tsx, 2, 8)) + +<> + +=== tests/cases/conformance/jsx/inline/preacty-only-fragment-no-jsx.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {Fragment} from "./renderer"; +>Fragment : Symbol(Fragment, Decl(preacty-only-fragment-no-jsx.tsx, 4, 8)) + +<> + +=== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment-no-jsx.tsx === +/* @jsx jsx */ +No type information for this code./* @jsxfrag null */ +No type information for this code.import {} from "./renderer"; +No type information for this code.<> +No type information for this code. +No type information for this code.=== tests/cases/conformance/jsx/inline/preacty-no-fragment.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +>h : Symbol(h, Decl(preacty-no-fragment.tsx, 4, 8)) +>Fragment : Symbol(Fragment, Decl(preacty-no-fragment.tsx, 4, 10)) + +
+>div : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) +>div : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) + +=== tests/cases/conformance/jsx/inline/snabbdomy-no-fragment.tsx === +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +>jsx : Symbol(jsx, Decl(snabbdomy-no-fragment.tsx, 2, 8)) + +
+>div : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) +>div : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19)) + +=== tests/cases/conformance/jsx/inline/preacty-only-component.tsx === +/** + * @jsx h + */ +import {h} from "./renderer"; +>h : Symbol(h, Decl(preacty-only-component.tsx, 3, 8)) + +function Component() { return null; } +>Component : Symbol(Component, Decl(preacty-only-component.tsx, 3, 29)) + + +>Component : Symbol(Component, Decl(preacty-only-component.tsx, 3, 29)) + diff --git a/tests/baselines/reference/inlineJsxAndJsxFragPragma.types b/tests/baselines/reference/inlineJsxAndJsxFragPragma.types index d7fdf1b6652..d03eae1c79d 100644 --- a/tests/baselines/reference/inlineJsxAndJsxFragPragma.types +++ b/tests/baselines/reference/inlineJsxAndJsxFragPragma.types @@ -28,8 +28,8 @@ import {h, Fragment} from "./renderer"; >Fragment : () => void <>
-><>
: error ->
: error +><>
: any +>
: any >div : any >div : any @@ -40,8 +40,87 @@ import {jsx} from "./renderer"; >jsx : () => void <> -><> : error -> : error +><> : any +> : any >span : any >span : any +=== tests/cases/conformance/jsx/inline/preacty-only-fragment.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +>h : () => void +>Fragment : () => void + +<> +><> : any + +=== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment.tsx === +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +>jsx : () => void + +<> +><> : any + +=== tests/cases/conformance/jsx/inline/preacty-only-fragment-no-jsx.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {Fragment} from "./renderer"; +>Fragment : () => void + +<> +><> : any + +=== tests/cases/conformance/jsx/inline/snabbdomy-only-fragment-no-jsx.tsx === +/* @jsx jsx */ +/* @jsxfrag null */ +import {} from "./renderer"; +<> +><> : any + +=== tests/cases/conformance/jsx/inline/preacty-no-fragment.tsx === +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +>h : () => void +>Fragment : () => void + +
+>
: any +>div : any +>div : any + +=== tests/cases/conformance/jsx/inline/snabbdomy-no-fragment.tsx === +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +>jsx : () => void + +
+>
: any +>div : any +>div : any + +=== tests/cases/conformance/jsx/inline/preacty-only-component.tsx === +/** + * @jsx h + */ +import {h} from "./renderer"; +>h : () => void + +function Component() { return null; } +>Component : () => any +>null : null + + +> : any +>Component : () => any + diff --git a/tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx b/tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx index 59fa10dbfbc..e500a3eb5f3 100644 --- a/tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx +++ b/tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx @@ -1,4 +1,5 @@ // @jsx: react +// @noUnusedLocals: true // @filename: renderer.d.ts declare global { namespace JSX { @@ -23,4 +24,54 @@ import {h, Fragment} from "./renderer"; /* @jsx jsx */ /* @jsxfrag null */ import {jsx} from "./renderer"; -<> \ No newline at end of file +<> + +// @filename: preacty-only-fragment.tsx +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +<> + +// @filename: snabbdomy-only-fragment.tsx +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +<> + +// @filename: preacty-only-fragment-no-jsx.tsx +/** + * @jsx h + * @jsxFrag Fragment + */ +import {Fragment} from "./renderer"; +<> + +// @filename: snabbdomy-only-fragment-no-jsx.tsx +/* @jsx jsx */ +/* @jsxfrag null */ +import {} from "./renderer"; +<> + +// @filename: preacty-no-fragment.tsx +/** + * @jsx h + * @jsxFrag Fragment + */ +import {h, Fragment} from "./renderer"; +
+ +// @filename: snabbdomy-no-fragment.tsx +/* @jsx jsx */ +/* @jsxfrag null */ +import {jsx} from "./renderer"; +
+ +// @filename: preacty-only-component.tsx +/** + * @jsx h + */ +import {h} from "./renderer"; +function Component() { return null; } +