diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 07996e0bcdd41..9e61193a1f962 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1091,6 +1091,16 @@ namespace ts { } } function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { + // Pseudo-synthesized input node + if (location.pos < 0 || location.end < 0) { + if (!isError) { + return; // Drop suggestions (we have no span to suggest on) + } + // Issue errors globally + const file = getSourceFileOfNode(location); + addErrorOrSuggestion(isError, "message" in message ? createFileDiagnostic(file, 0, 0, message, arg0, arg1, arg2, arg3) : createDiagnosticForFileFromMessageChain(file, message)); // eslint-disable-line no-in-operator + return; + } addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2dc1fb0475d34..295671b70449b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -989,6 +989,18 @@ namespace ts { }; } + export function createDiagnosticForFileFromMessageChain(sourceFile: SourceFile, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation { + return { + file: sourceFile, + start: 0, + length: 0, + code: messageChain.code, + category: messageChain.category, + messageText: messageChain.next ? messageChain : messageChain.messageText, + relatedInformation + }; + } + export function createDiagnosticForRange(sourceFile: SourceFile, range: TextRange, message: DiagnosticMessage): DiagnosticWithLocation { return { file: sourceFile, diff --git a/src/testRunner/unittests/tsc/incremental.ts b/src/testRunner/unittests/tsc/incremental.ts index fd42ccddf1843..36ea38edd0155 100644 --- a/src/testRunner/unittests/tsc/incremental.ts +++ b/src/testRunner/unittests/tsc/incremental.ts @@ -235,5 +235,42 @@ const a: string = 10;`, "utf-8"), } } }); + + const jsxLibraryContent = ` +export {}; +declare global { + namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { + propA?: boolean; + }; + } + } +}`; + + verifyTsc({ + scenario: "react-jsx-emit-mode", + subScenario: "with no backing types found doesn't crash", + fs: () => loadProjectFromFiles({ + "/src/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result + "/src/project/node_modules/@types/react/index.d.ts": jsxLibraryContent, // doesn't contain a jsx-runtime definition + "/src/project/src/index.tsx": `export const App = () =>
;`, + "/src/project/tsconfig.json": JSON.stringify({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }) + }), + commandLineArgs: ["--p", "src/project"] + }); + + verifyTsc({ + scenario: "react-jsx-emit-mode", + subScenario: "with no backing types found doesn't crash under --strict", + fs: () => loadProjectFromFiles({ + "/src/project/node_modules/react/jsx-runtime.js": "export {}", // js needs to be present so there's a resolution result + "/src/project/node_modules/@types/react/index.d.ts": jsxLibraryContent, // doesn't contain a jsx-runtime definition + "/src/project/src/index.tsx": `export const App = () =>
;`, + "/src/project/tsconfig.json": JSON.stringify({ compilerOptions: { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" } }) + }), + commandLineArgs: ["--p", "src/project", "--strict"] + }); }); } diff --git a/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash-under---strict.js b/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash-under---strict.js new file mode 100644 index 0000000000000..b0b8497aaf4eb --- /dev/null +++ b/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash-under---strict.js @@ -0,0 +1,115 @@ +Input:: +//// [/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +declare const console: { log(msg: any): void; }; + +//// [/src/project/node_modules/@types/react/index.d.ts] + +export {}; +declare global { + namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { + propA?: boolean; + }; + } + } +} + +//// [/src/project/node_modules/react/jsx-runtime.js] + + +//// [/src/project/src/index.tsx] +export const App = () =>
; + +//// [/src/project/tsconfig.json] +{"compilerOptions":{"module":"commonjs","jsx":"react-jsx","incremental":true,"jsxImportSource":"react"}} + + + +Output:: +/lib/tsc --p src/project --strict +src/project/src/index.tsx:1:26 - error TS7016: Could not find a declaration file for module 'react/jsx-runtime'. '/src/project/node_modules/react/jsx-runtime.js' implicitly has an 'any' type. + +1 export const App = () =>
; +   ~~~~~~~~~~~~~~~~~~~~~~~~ + + +Found 1 error. + +exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated + + +//// [/src/project/src/index.js] +"use strict"; +exports.__esModule = true; +exports.App = void 0; +var jsx_runtime_1 = require("react/jsx-runtime"); +var App = function () { return jsx_runtime_1.jsx("div", { propA: true }, void 0); }; +exports.App = App; + + +//// [/src/project/tsconfig.tsbuildinfo] +{ + "program": { + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/index.tsx": { + "version": "-14760199789-export const App = () =>
;", + "signature": "-8716173275-/// \r\nexport declare const App: () => JSX.Element;\r\n", + "affectsGlobalScope": false + }, + "./node_modules/@types/react/index.d.ts": { + "version": "-16587767667-\nexport {};\ndeclare global {\n namespace JSX {\n interface Element {}\n interface IntrinsicElements {\n div: {\n propA?: boolean;\n };\n }\n }\n}", + "signature": "-16587767667-\nexport {};\ndeclare global {\n namespace JSX {\n interface Element {}\n interface IntrinsicElements {\n div: {\n propA?: boolean;\n };\n }\n }\n}", + "affectsGlobalScope": true + } + }, + "options": { + "module": 1, + "jsx": 4, + "incremental": true, + "jsxImportSource": "react", + "project": "./", + "strict": true, + "configFilePath": "./tsconfig.json" + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./node_modules/@types/react/index.d.ts", + [ + "./src/index.tsx", + [ + { + "file": "./src/index.tsx", + "start": 25, + "length": 24, + "code": 7016, + "category": 1, + "messageText": "Could not find a declaration file for module 'react/jsx-runtime'. '/src/project/node_modules/react/jsx-runtime.js' implicitly has an 'any' type." + } + ] + ] + ] + }, + "version": "FakeTSVersion" +} + diff --git a/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash.js b/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash.js new file mode 100644 index 0000000000000..da35f20174d3c --- /dev/null +++ b/tests/baselines/reference/tsc/react-jsx-emit-mode/initial-build/with-no-backing-types-found-doesn't-crash.js @@ -0,0 +1,94 @@ +Input:: +//// [/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +declare const console: { log(msg: any): void; }; + +//// [/src/project/node_modules/@types/react/index.d.ts] + +export {}; +declare global { + namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { + propA?: boolean; + }; + } + } +} + +//// [/src/project/node_modules/react/jsx-runtime.js] + + +//// [/src/project/src/index.tsx] +export const App = () =>
; + +//// [/src/project/tsconfig.json] +{"compilerOptions":{"module":"commonjs","jsx":"react-jsx","incremental":true,"jsxImportSource":"react"}} + + + +Output:: +/lib/tsc --p src/project +exitCode:: ExitStatus.Success + + +//// [/src/project/src/index.js] +"use strict"; +exports.__esModule = true; +exports.App = void 0; +var jsx_runtime_1 = require("react/jsx-runtime"); +var App = function () { return jsx_runtime_1.jsx("div", { propA: true }, void 0); }; +exports.App = App; + + +//// [/src/project/tsconfig.tsbuildinfo] +{ + "program": { + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/index.tsx": { + "version": "-14760199789-export const App = () =>
;", + "signature": "-8716173275-/// \r\nexport declare const App: () => JSX.Element;\r\n", + "affectsGlobalScope": false + }, + "./node_modules/@types/react/index.d.ts": { + "version": "-16587767667-\nexport {};\ndeclare global {\n namespace JSX {\n interface Element {}\n interface IntrinsicElements {\n div: {\n propA?: boolean;\n };\n }\n }\n}", + "signature": "-16587767667-\nexport {};\ndeclare global {\n namespace JSX {\n interface Element {}\n interface IntrinsicElements {\n div: {\n propA?: boolean;\n };\n }\n }\n}", + "affectsGlobalScope": true + } + }, + "options": { + "module": 1, + "jsx": 4, + "incremental": true, + "jsxImportSource": "react", + "project": "./", + "configFilePath": "./tsconfig.json" + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./node_modules/@types/react/index.d.ts", + "./src/index.tsx" + ] + }, + "version": "FakeTSVersion" +} +