Skip to content

Commit 09b63d2

Browse files
timotheeguerindmnorc
authored andcommitted
Fix: Infinite loop in language server due to not caching indeterminate entities (microsoft#5940)
fix microsoft#2964 The problem is everytime we created an indeterminate entity we created a new object instead of caching it from the type. This meant everytime we needed to reinstantiate the template it would create 2 separate instances. This caused an inifinite loop in the LSP as it would call getTypeForNode for diagnostics This should also solve some duplicate type issue we might have encountered.
1 parent 4c92d1a commit 09b63d2

File tree

3 files changed

+37
-13
lines changed

3 files changed

+37
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
3+
changeKind: fix
4+
packages:
5+
- "@typespec/compiler"
6+
---
7+
8+
Fix: Infinite loop in language server due to not caching indeterminate entities in templates

packages/compiler/src/core/checker.ts

+12-13
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ const TypeInstantiationMap = class
338338

339339
export function createChecker(program: Program, resolver: NameResolver): Checker {
340340
const stdTypes: Partial<StdTypes> = {};
341+
const indeterminateEntities = new WeakMap<Type, IndeterminateEntity>();
341342
const docFromCommentForSym = new Map<Sym, string>();
342343
const referenceSymCache = new WeakMap<
343344
TypeReferenceNode | MemberExpressionNode | IdentifierNode,
@@ -3390,10 +3391,17 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
33903391
}
33913392

33923393
function createIndeterminateEntity(type: IndeterminateEntity["type"]): IndeterminateEntity {
3393-
return {
3394+
const existing = indeterminateEntities.get(type);
3395+
if (existing) {
3396+
return existing;
3397+
}
3398+
const entity: IndeterminateEntity = {
33943399
entityKind: "Indeterminate",
33953400
type,
33963401
};
3402+
3403+
indeterminateEntities.set(type, entity);
3404+
return entity;
33973405
}
33983406
function stringifyTypeForTemplate(type: Type): string | undefined {
33993407
switch (type.kind) {
@@ -3448,24 +3456,15 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
34483456
}
34493457

34503458
function checkStringLiteral(str: StringLiteralNode): IndeterminateEntity {
3451-
return {
3452-
entityKind: "Indeterminate",
3453-
type: getLiteralType(str),
3454-
};
3459+
return createIndeterminateEntity(getLiteralType(str));
34553460
}
34563461

34573462
function checkNumericLiteral(num: NumericLiteralNode): IndeterminateEntity {
3458-
return {
3459-
entityKind: "Indeterminate",
3460-
type: getLiteralType(num),
3461-
};
3463+
return createIndeterminateEntity(getLiteralType(num));
34623464
}
34633465

34643466
function checkBooleanLiteral(bool: BooleanLiteralNode): IndeterminateEntity {
3465-
return {
3466-
entityKind: "Indeterminate",
3467-
type: getLiteralType(bool),
3468-
};
3467+
return createIndeterminateEntity(getLiteralType(bool));
34693468
}
34703469

34713470
function checkProgram() {

packages/compiler/test/checker/templates.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
createTestRunner,
1111
expectDiagnosticEmpty,
1212
expectDiagnostics,
13+
expectTypeEquals,
1314
extractCursor,
1415
extractSquiggles,
1516
} from "../../src/testing/index.js";
@@ -129,6 +130,22 @@ describe("compiler: templates", () => {
129130
expectDiagnosticEmpty(diagnostics);
130131
});
131132

133+
it("cache indeterminate types", async () => {
134+
testHost.addTypeSpecFile(
135+
"main.tsp",
136+
`
137+
model Template<T> {t: T}
138+
@test model Test {
139+
a: Template<"a">;
140+
b: Template<"a">;
141+
}
142+
`,
143+
);
144+
145+
const { Test } = (await testHost.compile("main.tsp")) as { Test: Model };
146+
expectTypeEquals(Test.properties.get("a")!.type, Test.properties.get("b")!.type);
147+
});
148+
132149
it("allows default template parameters that are models", async () => {
133150
testHost.addTypeSpecFile(
134151
"main.tsp",

0 commit comments

Comments
 (0)