From 019da8a260192e82d287c35138871b3c70740f2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 18:22:29 +0000 Subject: [PATCH 1/3] Initial plan From 2a4dd1fcf3184b2ee61d3c5d5fd8f586eefdd723 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 18:32:57 +0000 Subject: [PATCH 2/3] fix: update Sym.node and getSymNode to include undefined in return type Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/c9bd61fa-0967-4040-bf79-605fbc758e88 Co-authored-by: timotheeguerin <1031227+timotheeguerin@users.noreply.github.com> --- ...fix-sym-node-undefined-2026-5-6-18-23-0.md | 7 +++++++ packages/compiler/src/core/binder.ts | 2 +- packages/compiler/src/core/checker.ts | 21 +++++++++++++------ packages/compiler/src/core/name-resolver.ts | 7 ++++++- packages/compiler/src/core/types.ts | 3 ++- 5 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 .chronus/changes/fix-sym-node-undefined-2026-5-6-18-23-0.md diff --git a/.chronus/changes/fix-sym-node-undefined-2026-5-6-18-23-0.md b/.chronus/changes/fix-sym-node-undefined-2026-5-6-18-23-0.md new file mode 100644 index 00000000000..e5aa9e24f8d --- /dev/null +++ b/.chronus/changes/fix-sym-node-undefined-2026-5-6-18-23-0.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Fix `Sym.node` type to be `Node | undefined` and update `getSymNode` return type to accurately reflect that it may return `undefined` for symbols created without a node (e.g. the built-in `null` symbol). Update all callers to correctly handle the `undefined` case. diff --git a/packages/compiler/src/core/binder.ts b/packages/compiler/src/core/binder.ts index 09a5e5b5108..4b9920db625 100644 --- a/packages/compiler/src/core/binder.ts +++ b/packages/compiler/src/core/binder.ts @@ -705,6 +705,6 @@ export function createSymbol( * If a declaration symbol get the first one `.declarations[0]` * Otherwise get `.node` */ -export function getSymNode(sym: Sym): Node { +export function getSymNode(sym: Sym): Node | undefined { return sym.flags & SymbolFlags.Declaration ? sym.declarations[0] : sym.node; } diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index 6d6b43daf1e..60dcd13e824 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -682,15 +682,19 @@ export function createChecker(program: Program, resolver: NameResolver): Checker */ function checkMemberSym(ctx: CheckContext, sym: Sym): Type { const symbolLinks = getSymbolLinks(sym); - const memberContainer = getTypeForNode(getSymNode(sym.parent!), ctx); + const parentNode = getSymNode(sym.parent!); + compilerAssert(parentNode, "Expected member container parent symbol to have a node"); + const memberContainer = getTypeForNode(parentNode, ctx); const type = symbolLinks.declaredType ?? symbolLinks.type; if (type) { return type; } else { + const memberNode = getSymNode(sym); + compilerAssert(memberNode, "Expected member symbol to have a node"); return checkMember( ctx, - getSymNode(sym) as MemberNode, + memberNode as MemberNode, memberContainer as MemberContainerType, )!; } @@ -1737,6 +1741,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker compilerAssert(sym.type, `Expected late bound symbol to have type`); return sym.type; } else if (sym.flags & SymbolFlags.TemplateParameter) { + compilerAssert(symNode, "Expected template parameter symbol to have a node"); const mapped = checkTemplateParameterDeclaration( ctx, symNode as TemplateParameterDeclarationNode, @@ -1752,6 +1757,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker baseType = checkMemberSym(ctx, sym); } else { // don't have a cached type for this symbol, so go grab it and cache it + compilerAssert(symNode, "Expected symbol to have a node to resolve type"); baseType = getTypeForNode(symNode, ctx); symbolLinks.type = baseType; } @@ -3605,8 +3611,8 @@ export function createChecker(program: Program, resolver: NameResolver): Checker return undefined; } - function getMemberKindName(node: Node) { - switch (node.kind) { + function getMemberKindName(node: Node | undefined) { + switch (node?.kind) { case SyntaxKind.ModelStatement: case SyntaxKind.ModelExpression: return "Model"; @@ -3631,6 +3637,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker */ function getAliasedSymbol(ctx: CheckContext, aliasSymbol: Sym): Sym | undefined { const node = getSymNode(aliasSymbol); + compilerAssert(node, "Expected alias symbol to have a node"); const links = resolver.getSymbolLinks(aliasSymbol); if (!links.aliasResolutionIsTemplate) { return links.aliasedSymbol ?? resolver.getNodeLinks(node).resolvedSymbol; @@ -6036,7 +6043,8 @@ export function createChecker(program: Program, resolver: NameResolver): Checker }), ); } else if (links.finalSymbol?.flags && links.finalSymbol.flags & SymbolFlags.Alias) { - const aliasNode: AliasStatementNode = getSymNode(links.finalSymbol) as AliasStatementNode; + const aliasNode = getSymNode(links.finalSymbol) as AliasStatementNode | undefined; + compilerAssert(aliasNode, "Expected alias symbol to have a node"); program.reportDiagnostic( createDiagnostic({ @@ -7653,7 +7661,8 @@ function createFunctionContext(program: Program, fnCall: CallExpressionNode): Fu }; } -function isTemplatedNode(node: Node): node is TemplateableNode { +function isTemplatedNode(node: Node | undefined): node is TemplateableNode { + if (node === undefined) return false; return "templateParameters" in node && node.templateParameters.length > 0; } diff --git a/packages/compiler/src/core/name-resolver.ts b/packages/compiler/src/core/name-resolver.ts index 186de98e8c1..6e7fdf34ad0 100644 --- a/packages/compiler/src/core/name-resolver.ts +++ b/packages/compiler/src/core/name-resolver.ts @@ -715,6 +715,10 @@ export function createResolver(program: Program): NameResolver { function resolveMetaMemberByName(baseSym: Sym, sv: string): ResolutionResult { const baseNode = getSymNode(baseSym); + if (!baseNode) { + return failedResult(ResolutionResultFlags.NotFound); + } + const prototype = metaTypePrototypes.get(baseNode.kind); if (!prototype) { @@ -1322,7 +1326,8 @@ export function createResolver(program: Program): NameResolver { // model properties const modelPropertyPrototype: TypePrototype = new Map(); modelPropertyPrototype.set("type", (baseSym) => { - const node = baseSym.node as ModelPropertyNode; + const node = getSymNode(baseSym) as ModelPropertyNode | undefined; + compilerAssert(node, "Expected model property symbol to have a node"); return resolveExpression(node.value); }); nodeInterfaces.set(SyntaxKind.ModelProperty, modelPropertyPrototype); diff --git a/packages/compiler/src/core/types.ts b/packages/compiler/src/core/types.ts index a465e894c58..4fbbe32c66c 100644 --- a/packages/compiler/src/core/types.ts +++ b/packages/compiler/src/core/types.ts @@ -847,8 +847,9 @@ export interface Sym { /** * Node which resulted in this symbol, present if SymbolFlags.Declaration is not set. + * May be undefined if the symbol was created without a node (e.g. built-in symbols like `null`). */ - readonly node: Node; + readonly node: Node | undefined; /** * The name of the symbol From 6a1b498ccf34fc26a83405c057592988e2c8d72b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 18:35:25 +0000 Subject: [PATCH 3/3] fix: simplify type assertion in modelPropertyPrototype per code review Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/c9bd61fa-0967-4040-bf79-605fbc758e88 Co-authored-by: timotheeguerin <1031227+timotheeguerin@users.noreply.github.com> --- packages/compiler/src/core/name-resolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler/src/core/name-resolver.ts b/packages/compiler/src/core/name-resolver.ts index 6e7fdf34ad0..a1d3637a3aa 100644 --- a/packages/compiler/src/core/name-resolver.ts +++ b/packages/compiler/src/core/name-resolver.ts @@ -1326,9 +1326,9 @@ export function createResolver(program: Program): NameResolver { // model properties const modelPropertyPrototype: TypePrototype = new Map(); modelPropertyPrototype.set("type", (baseSym) => { - const node = getSymNode(baseSym) as ModelPropertyNode | undefined; + const node = getSymNode(baseSym); compilerAssert(node, "Expected model property symbol to have a node"); - return resolveExpression(node.value); + return resolveExpression((node as ModelPropertyNode).value); }); nodeInterfaces.set(SyntaxKind.ModelProperty, modelPropertyPrototype);