Skip to content

Commit c191e7c

Browse files
authored
Fix diagnostic serialization crash (microsoft#47604)
* Add crashing test * Fix unsafe cast to DiagnosticMessageChain
1 parent 3206df8 commit c191e7c

File tree

4 files changed

+226
-1
lines changed

4 files changed

+226
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30121,7 +30121,7 @@ namespace ts {
3012130121
const diags = max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics);
3012230122
Debug.assert(diags.length > 0, "No errors reported for 3 or fewer overload signatures");
3012330123
const chain = chainDiagnosticMessages(
30124-
map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText),
30124+
map(diags, createDiagnosticMessageChainFromDiagnostic),
3012530125
Diagnostics.No_overload_matches_this_call);
3012630126
// The below is a spread to guarantee we get a new (mutable) array - our `flatMap` helper tries to do "smart" optimizations where it reuses input
3012730127
// arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic

src/compiler/utilities.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,15 @@ namespace ts {
10711071
};
10721072
}
10731073

1074+
export function createDiagnosticMessageChainFromDiagnostic(diagnostic: DiagnosticRelatedInformation): DiagnosticMessageChain {
1075+
return typeof diagnostic.messageText === "string" ? {
1076+
code: diagnostic.code,
1077+
category: diagnostic.category,
1078+
messageText: diagnostic.messageText,
1079+
next: (diagnostic as DiagnosticMessageChain).next,
1080+
} : diagnostic.messageText;
1081+
}
1082+
10741083
export function createDiagnosticForRange(sourceFile: SourceFile, range: TextRange, message: DiagnosticMessage): DiagnosticWithLocation {
10751084
return {
10761085
file: sourceFile,

src/testRunner/unittests/tsc/incremental.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,36 @@ declare global {
418418
incrementalScenarios: noChangeOnlyRuns,
419419
baselinePrograms: true
420420
});
421+
422+
verifyTscSerializedIncrementalEdits({
423+
scenario: "incremental",
424+
subScenario: "serializing error chains",
425+
commandLineArgs: ["-p", `src/project`],
426+
fs: () => loadProjectFromFiles({
427+
"/src/project/tsconfig.json": JSON.stringify({
428+
compilerOptions: {
429+
incremental: true,
430+
strict: true,
431+
jsx: "react",
432+
module: "esnext",
433+
},
434+
}),
435+
"/src/project/index.tsx": Utils.dedent`
436+
declare namespace JSX {
437+
interface ElementChildrenAttribute { children: {}; }
438+
interface IntrinsicElements { div: {} }
439+
}
440+
441+
declare var React: any;
442+
443+
declare function Component(props: never): any;
444+
declare function Component(props: { children?: number }): any;
445+
(<Component>
446+
<div />
447+
<div />
448+
</Component>)`
449+
}, `\ninterface ReadonlyArray<T> { readonly length: number }`),
450+
incrementalScenarios: noChangeOnlyRuns,
451+
});
421452
});
422453
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
Input::
2+
//// [/lib/lib.d.ts]
3+
/// <reference no-default-lib="true"/>
4+
interface Boolean {}
5+
interface Function {}
6+
interface CallableFunction {}
7+
interface NewableFunction {}
8+
interface IArguments {}
9+
interface Number { toExponential: any; }
10+
interface Object {}
11+
interface RegExp {}
12+
interface String { charAt: any; }
13+
interface Array<T> { length: number; [n: number]: T; }
14+
interface ReadonlyArray<T> {}
15+
declare const console: { log(msg: any): void; };
16+
interface ReadonlyArray<T> { readonly length: number }
17+
18+
//// [/src/project/index.tsx]
19+
declare namespace JSX {
20+
interface ElementChildrenAttribute { children: {}; }
21+
interface IntrinsicElements { div: {} }
22+
}
23+
24+
declare var React: any;
25+
26+
declare function Component(props: never): any;
27+
declare function Component(props: { children?: number }): any;
28+
(<Component>
29+
<div />
30+
<div />
31+
</Component>)
32+
33+
//// [/src/project/tsconfig.json]
34+
{"compilerOptions":{"incremental":true,"strict":true,"jsx":"react","module":"esnext"}}
35+
36+
37+
38+
Output::
39+
/lib/tsc -p src/project
40+
src/project/index.tsx:10:3 - error TS2746: This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.
41+
42+
10 (<Component>
43+
   ~~~~~~~~~
44+
45+
src/project/index.tsx:10:3 - error TS2746: This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.
46+
47+
10 (<Component>
48+
   ~~~~~~~~~
49+
50+
src/project/index.tsx:10:3 - error TS2769: No overload matches this call.
51+
This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.
52+
This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.
53+
54+
10 (<Component>
55+
   ~~~~~~~~~
56+
57+
58+
59+
Found 3 errors in the same file, starting at: src/project/index.tsx:10
60+
61+
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated
62+
63+
64+
//// [/src/project/index.js]
65+
"use strict";
66+
(React.createElement(Component, null,
67+
React.createElement("div", null),
68+
React.createElement("div", null)));
69+
70+
71+
//// [/src/project/tsconfig.tsbuildinfo]
72+
{"program":{"fileNames":["../../lib/lib.d.ts","./index.tsx"],"fileInfos":[{"version":"7198220534-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };\ninterface ReadonlyArray<T> { readonly length: number }","affectsGlobalScope":true},{"version":"42569361247-declare namespace JSX {\n interface ElementChildrenAttribute { children: {}; }\n interface IntrinsicElements { div: {} }\n}\n\ndeclare var React: any;\n\ndeclare function Component(props: never): any;\ndeclare function Component(props: { children?: number }): any;\n(<Component>\n <div />\n <div />\n</Component>)","affectsGlobalScope":true}],"options":{"jsx":2,"module":99,"strict":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,[2,[{"file":"./index.tsx","start":265,"length":9,"messageText":"This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.","category":1,"code":2746},{"file":"./index.tsx","start":265,"length":9,"messageText":"This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.","category":1,"code":2746},{"file":"./index.tsx","start":265,"length":9,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"code":2746,"category":1,"messageText":"This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided."},{"code":2746,"category":1,"messageText":"This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided."}]},"relatedInformation":[]}]]]},"version":"FakeTSVersion"}
73+
74+
//// [/src/project/tsconfig.tsbuildinfo.readable.baseline.txt]
75+
{
76+
"program": {
77+
"fileNames": [
78+
"../../lib/lib.d.ts",
79+
"./index.tsx"
80+
],
81+
"fileInfos": {
82+
"../../lib/lib.d.ts": {
83+
"version": "7198220534-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };\ninterface ReadonlyArray<T> { readonly length: number }",
84+
"signature": "7198220534-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };\ninterface ReadonlyArray<T> { readonly length: number }",
85+
"affectsGlobalScope": true
86+
},
87+
"./index.tsx": {
88+
"version": "42569361247-declare namespace JSX {\n interface ElementChildrenAttribute { children: {}; }\n interface IntrinsicElements { div: {} }\n}\n\ndeclare var React: any;\n\ndeclare function Component(props: never): any;\ndeclare function Component(props: { children?: number }): any;\n(<Component>\n <div />\n <div />\n</Component>)",
89+
"signature": "42569361247-declare namespace JSX {\n interface ElementChildrenAttribute { children: {}; }\n interface IntrinsicElements { div: {} }\n}\n\ndeclare var React: any;\n\ndeclare function Component(props: never): any;\ndeclare function Component(props: { children?: number }): any;\n(<Component>\n <div />\n <div />\n</Component>)",
90+
"affectsGlobalScope": true
91+
}
92+
},
93+
"options": {
94+
"jsx": 2,
95+
"module": 99,
96+
"strict": true
97+
},
98+
"referencedMap": {},
99+
"exportedModulesMap": {},
100+
"semanticDiagnosticsPerFile": [
101+
"../../lib/lib.d.ts",
102+
[
103+
"./index.tsx",
104+
[
105+
{
106+
"file": "./index.tsx",
107+
"start": 265,
108+
"length": 9,
109+
"messageText": "This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.",
110+
"category": 1,
111+
"code": 2746
112+
},
113+
{
114+
"file": "./index.tsx",
115+
"start": 265,
116+
"length": 9,
117+
"messageText": "This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.",
118+
"category": 1,
119+
"code": 2746
120+
},
121+
{
122+
"file": "./index.tsx",
123+
"start": 265,
124+
"length": 9,
125+
"code": 2769,
126+
"category": 1,
127+
"messageText": {
128+
"messageText": "No overload matches this call.",
129+
"category": 1,
130+
"code": 2769,
131+
"next": [
132+
{
133+
"code": 2746,
134+
"category": 1,
135+
"messageText": "This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided."
136+
},
137+
{
138+
"code": 2746,
139+
"category": 1,
140+
"messageText": "This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided."
141+
}
142+
]
143+
},
144+
"relatedInformation": []
145+
}
146+
]
147+
]
148+
]
149+
},
150+
"version": "FakeTSVersion",
151+
"size": 2053
152+
}
153+
154+
155+
156+
Change:: no-change-run
157+
Input::
158+
159+
160+
Output::
161+
/lib/tsc -p src/project
162+
src/project/index.tsx:10:3 - error TS2746: This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.
163+
164+
10 (<Component>
165+
   ~~~~~~~~~
166+
167+
src/project/index.tsx:10:3 - error TS2746: This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.
168+
169+
10 (<Component>
170+
   ~~~~~~~~~
171+
172+
src/project/index.tsx:10:3 - error TS2769: No overload matches this call.
173+
This JSX tag's 'children' prop expects a single child of type 'never', but multiple children were provided.
174+
This JSX tag's 'children' prop expects a single child of type 'number | undefined', but multiple children were provided.
175+
176+
10 (<Component>
177+
   ~~~~~~~~~
178+
179+
180+
181+
Found 3 errors in the same file, starting at: src/project/index.tsx:10
182+
183+
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated
184+
185+

0 commit comments

Comments
 (0)