Skip to content

Commit 5b71f59

Browse files
authored
Fix spurious circularity caused by removeOptionalityFromDeclaredType, cache result (#52696)
1 parent 6aba9b8 commit 5b71f59

File tree

4 files changed

+289
-14
lines changed

4 files changed

+289
-14
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,7 @@ const enum TypeSystemPropertyName {
12331233
ResolvedTypeArguments,
12341234
ResolvedBaseTypes,
12351235
WriteType,
1236+
ParameterInitializerContainsUndefined,
12361237
}
12371238

12381239
/** @internal */
@@ -9983,6 +9984,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
99839984
return !!(target as InterfaceType).baseTypesResolved;
99849985
case TypeSystemPropertyName.WriteType:
99859986
return !!getSymbolLinks(target as Symbol).writeType;
9987+
case TypeSystemPropertyName.ParameterInitializerContainsUndefined:
9988+
return getNodeLinks(target as ParameterDeclaration).parameterInitializerContainsUndefined !== undefined;
99869989
}
99879990
return Debug.assertNever(propertyName);
99889991
}
@@ -27295,22 +27298,37 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2729527298
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0;
2729627299
}
2729727300

27301+
function parameterInitializerContainsUndefined(declaration: ParameterDeclaration): boolean {
27302+
const links = getNodeLinks(declaration);
27303+
27304+
if (links.parameterInitializerContainsUndefined === undefined) {
27305+
if (!pushTypeResolution(declaration, TypeSystemPropertyName.ParameterInitializerContainsUndefined)) {
27306+
reportCircularityError(declaration.symbol);
27307+
return true;
27308+
}
27309+
27310+
const containsUndefined = !!(getTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFacts.IsUndefined);
27311+
27312+
if (!popTypeResolution()) {
27313+
reportCircularityError(declaration.symbol);
27314+
return true;
27315+
}
27316+
27317+
links.parameterInitializerContainsUndefined = containsUndefined;
27318+
}
27319+
27320+
return links.parameterInitializerContainsUndefined;
27321+
}
27322+
2729827323
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
2729927324
function removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type {
27300-
if (pushTypeResolution(declaration.symbol, TypeSystemPropertyName.DeclaredType)) {
27301-
const annotationIncludesUndefined = strictNullChecks &&
27302-
declaration.kind === SyntaxKind.Parameter &&
27303-
declaration.initializer &&
27304-
getTypeFacts(declaredType) & TypeFacts.IsUndefined &&
27305-
!(getTypeFacts(checkExpression(declaration.initializer)) & TypeFacts.IsUndefined);
27306-
popTypeResolution();
27325+
const removeUndefined = strictNullChecks &&
27326+
declaration.kind === SyntaxKind.Parameter &&
27327+
declaration.initializer &&
27328+
getTypeFacts(declaredType) & TypeFacts.IsUndefined &&
27329+
!parameterInitializerContainsUndefined(declaration);
2730727330

27308-
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
27309-
}
27310-
else {
27311-
reportCircularityError(declaration.symbol);
27312-
return declaredType;
27313-
}
27331+
return removeUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
2731427332
}
2731527333

2731627334
function isConstraintPosition(type: Type, node: Node) {

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5997,6 +5997,7 @@ export interface NodeLinks {
59975997
declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter.
59985998
serializedTypes?: Map<string, SerializedTypeEntry>; // Collection of types serialized at this location
59995999
decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime.
6000+
parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined".
60006001
}
60016002

60026003
/** @internal */

src/testRunner/unittests/tsserver/inconsistentErrorInEditor.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,36 @@ describe("unittests:: tsserver:: inconsistentErrorInEditor", () => {
3838
verifyGetErrRequest({ session, host, files: ["^/untitled/ts-nul-authority/Untitled-1"] });
3939
baselineTsserverLogs("inconsistentErrorInEditor", "should not error", session);
4040
});
41-
});
41+
});
42+
43+
describe("unittests:: tsserver:: inconsistentErrorInEditor2", () => {
44+
it("should not error", () => {
45+
const host = createServerHost([]);
46+
const session = createSession(host, { canUseEvents: true, noGetErrOnBackgroundUpdate: true, logger: createLoggerWithInMemoryLogs(host) });
47+
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({
48+
command: ts.server.protocol.CommandTypes.UpdateOpen,
49+
arguments: {
50+
changedFiles: [],
51+
closedFiles: [],
52+
openFiles: [
53+
{
54+
file: "^/untitled/ts-nul-authority/Untitled-1",
55+
fileContent: "function fn(Foo: number) {\r\n type Foo = typeof Foo;\r\n return 0 as any as {x: Foo};\r\n}",
56+
scriptKindName: "TS"
57+
}
58+
]
59+
}
60+
});
61+
session.executeCommandSeq<ts.server.protocol.EncodedSemanticClassificationsRequest>({
62+
command: ts.server.protocol.CommandTypes.EncodedSemanticClassificationsFull,
63+
arguments: {
64+
file: "^/untitled/ts-nul-authority/Untitled-1",
65+
start: 0,
66+
length: 128,
67+
format: "2020"
68+
}
69+
});
70+
verifyGetErrRequest({ session, host, files: ["^/untitled/ts-nul-authority/Untitled-1"] });
71+
baselineTsserverLogs("inconsistentErrorInEditor2", "should not error", session);
72+
});
73+
});
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
Info 0 [00:00:02.000] Provided types map file "/a/lib/typesMap.json" doesn't exist
2+
Info 1 [00:00:03.000] request:
3+
{
4+
"command": "updateOpen",
5+
"arguments": {
6+
"changedFiles": [],
7+
"closedFiles": [],
8+
"openFiles": [
9+
{
10+
"file": "^/untitled/ts-nul-authority/Untitled-1",
11+
"fileContent": "function fn(Foo: number) {\r\n type Foo = typeof Foo;\r\n return 0 as any as {x: Foo};\r\n}",
12+
"scriptKindName": "TS"
13+
}
14+
]
15+
},
16+
"seq": 1,
17+
"type": "request"
18+
}
19+
Before request
20+
21+
PolledWatches::
22+
23+
FsWatches::
24+
25+
FsWatchesRecursive::
26+
27+
Info 2 [00:00:04.000] Search path: ^/untitled/ts-nul-authority
28+
Info 3 [00:00:05.000] For info: ^/untitled/ts-nul-authority/Untitled-1 :: No config files found.
29+
Info 4 [00:00:06.000] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
30+
Info 5 [00:00:07.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
31+
Info 6 [00:00:08.000] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
32+
Info 7 [00:00:09.000] Project '/dev/null/inferredProject1*' (Inferred)
33+
Info 8 [00:00:10.000] Files (1)
34+
^/untitled/ts-nul-authority/Untitled-1
35+
36+
37+
^/untitled/ts-nul-authority/Untitled-1
38+
Root file specified for compilation
39+
40+
Info 9 [00:00:11.000] -----------------------------------------------
41+
Info 10 [00:00:12.000] Project '/dev/null/inferredProject1*' (Inferred)
42+
Info 10 [00:00:13.000] Files (1)
43+
44+
Info 10 [00:00:14.000] -----------------------------------------------
45+
Info 10 [00:00:15.000] Open files:
46+
Info 10 [00:00:16.000] FileName: ^/untitled/ts-nul-authority/Untitled-1 ProjectRootPath: undefined
47+
Info 10 [00:00:17.000] Projects: /dev/null/inferredProject1*
48+
After request
49+
50+
PolledWatches::
51+
/a/lib/lib.d.ts:
52+
{"pollingInterval":500}
53+
54+
FsWatches::
55+
56+
FsWatchesRecursive::
57+
58+
Info 10 [00:00:18.000] response:
59+
{
60+
"response": true,
61+
"responseRequired": true
62+
}
63+
Info 11 [00:00:19.000] request:
64+
{
65+
"command": "encodedSemanticClassifications-full",
66+
"arguments": {
67+
"file": "^/untitled/ts-nul-authority/Untitled-1",
68+
"start": 0,
69+
"length": 128,
70+
"format": "2020"
71+
},
72+
"seq": 2,
73+
"type": "request"
74+
}
75+
Before request
76+
77+
PolledWatches::
78+
/a/lib/lib.d.ts:
79+
{"pollingInterval":500}
80+
81+
FsWatches::
82+
83+
FsWatchesRecursive::
84+
85+
After request
86+
87+
PolledWatches::
88+
/a/lib/lib.d.ts:
89+
{"pollingInterval":500}
90+
91+
FsWatches::
92+
93+
FsWatchesRecursive::
94+
95+
Info 12 [00:00:20.000] response:
96+
{
97+
"response": {
98+
"spans": [
99+
9,
100+
2,
101+
2817,
102+
12,
103+
3,
104+
1536,
105+
38,
106+
3,
107+
1537,
108+
51,
109+
3,
110+
1536,
111+
81,
112+
1,
113+
2561,
114+
84,
115+
3,
116+
1536
117+
],
118+
"endOfLineState": 0
119+
},
120+
"responseRequired": true
121+
}
122+
Info 13 [00:00:21.000] request:
123+
{
124+
"command": "geterr",
125+
"arguments": {
126+
"delay": 0,
127+
"files": [
128+
"^/untitled/ts-nul-authority/Untitled-1"
129+
]
130+
},
131+
"seq": 3,
132+
"type": "request"
133+
}
134+
Before request
135+
136+
PolledWatches::
137+
/a/lib/lib.d.ts:
138+
{"pollingInterval":500}
139+
140+
FsWatches::
141+
142+
FsWatchesRecursive::
143+
144+
After request
145+
146+
PolledWatches::
147+
/a/lib/lib.d.ts:
148+
{"pollingInterval":500}
149+
150+
FsWatches::
151+
152+
FsWatchesRecursive::
153+
154+
Info 14 [00:00:22.000] response:
155+
{
156+
"responseRequired": false
157+
}
158+
Before checking timeout queue length (1) and running
159+
160+
PolledWatches::
161+
/a/lib/lib.d.ts:
162+
{"pollingInterval":500}
163+
164+
FsWatches::
165+
166+
FsWatchesRecursive::
167+
168+
Info 15 [00:00:23.000] event:
169+
{"seq":0,"type":"event","event":"syntaxDiag","body":{"file":"^/untitled/ts-nul-authority/Untitled-1","diagnostics":[]}}
170+
After checking timeout queue length (1) and running
171+
172+
PolledWatches::
173+
/a/lib/lib.d.ts:
174+
{"pollingInterval":500}
175+
176+
FsWatches::
177+
178+
FsWatchesRecursive::
179+
180+
Before running immediate callbacks and checking length (1)
181+
182+
PolledWatches::
183+
/a/lib/lib.d.ts:
184+
{"pollingInterval":500}
185+
186+
FsWatches::
187+
188+
FsWatchesRecursive::
189+
190+
Info 16 [00:00:24.000] event:
191+
{"seq":0,"type":"event","event":"semanticDiag","body":{"file":"^/untitled/ts-nul-authority/Untitled-1","diagnostics":[]}}
192+
Before running immediate callbacks and checking length (1)
193+
194+
PolledWatches::
195+
/a/lib/lib.d.ts:
196+
{"pollingInterval":500}
197+
198+
FsWatches::
199+
200+
FsWatchesRecursive::
201+
202+
Before running immediate callbacks and checking length (1)
203+
204+
PolledWatches::
205+
/a/lib/lib.d.ts:
206+
{"pollingInterval":500}
207+
208+
FsWatches::
209+
210+
FsWatchesRecursive::
211+
212+
Info 17 [00:00:25.000] event:
213+
{"seq":0,"type":"event","event":"suggestionDiag","body":{"file":"^/untitled/ts-nul-authority/Untitled-1","diagnostics":[]}}
214+
Info 18 [00:00:26.000] event:
215+
{"seq":0,"type":"event","event":"requestCompleted","body":{"request_seq":3}}
216+
Before running immediate callbacks and checking length (1)
217+
218+
PolledWatches::
219+
/a/lib/lib.d.ts:
220+
{"pollingInterval":500}
221+
222+
FsWatches::
223+
224+
FsWatchesRecursive::

0 commit comments

Comments
 (0)