diff --git a/.chronus/changes/fix-compiler-crash-alias-union-2024-3-18-2-39-55.md b/.chronus/changes/fix-compiler-crash-alias-union-2024-3-18-2-39-55.md new file mode 100644 index 0000000000..e727e7e96f --- /dev/null +++ b/.chronus/changes/fix-compiler-crash-alias-union-2024-3-18-2-39-55.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Fix: Do not crash when trying to access member of aliased expressions diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index ab97ef8fb3..e42e5f52b7 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -2402,10 +2402,24 @@ export function createChecker(program: Program): Checker { // when resolving a type reference based on an alias, unwrap the alias. if (base.flags & SymbolFlags.Alias) { - base = getAliasedSymbol(base, mapper, options); - if (!base) { + const aliasedSym = getAliasedSymbol(base, mapper, options); + if (!aliasedSym) { + reportCheckerDiagnostic( + createDiagnostic({ + code: "invalid-ref", + messageId: "node", + format: { + id: node.id.sv, + nodeName: base.declarations[0] + ? SyntaxKind[base.declarations[0].kind] + : "Unknown node", + }, + target: node, + }) + ); return undefined; } + base = aliasedSym; } if (node.selector === ".") { @@ -2561,11 +2575,19 @@ export function createChecker(program: Program): Checker { while (current.flags & SymbolFlags.Alias) { const node = current.declarations[0]; const targetNode = node.kind === SyntaxKind.AliasStatement ? node.value : node; - const sym = resolveTypeReferenceSymInternal(targetNode as any, mapper, options); - if (sym === undefined) { + if ( + targetNode.kind === SyntaxKind.TypeReference || + targetNode.kind === SyntaxKind.MemberExpression || + targetNode.kind === SyntaxKind.Identifier + ) { + const sym = resolveTypeReferenceSymInternal(targetNode, mapper, options); + if (sym === undefined) { + return undefined; + } + current = sym; + } else { return undefined; } - current = sym; } const sym = current; const node = aliasSymbol.declarations[0]; diff --git a/packages/compiler/test/checker/alias.test.ts b/packages/compiler/test/checker/alias.test.ts index 551ea8fe57..3cc2201ed8 100644 --- a/packages/compiler/test/checker/alias.test.ts +++ b/packages/compiler/test/checker/alias.test.ts @@ -258,4 +258,38 @@ describe("compiler: aliases", () => { const diagnostics = await testHost.diagnose("main.tsp"); expectDiagnosticEmpty(diagnostics); }); + + // REGRESSION TEST: https://github.com/microsoft/typespec/issues/3125 + it("trying to access member of aliased union expression shouldn't crash", async () => { + testHost.addTypeSpecFile( + "main.tsp", + ` + alias A = {foo: string} | {bar: string}; + + alias Aliased = A.prop; + + ` + ); + const diagnostics = await testHost.diagnose("main.tsp"); + expectDiagnostics(diagnostics, { + code: "invalid-ref", + message: `Cannot resolve 'prop' in node AliasStatement since it has no members. Did you mean to use "::" instead of "."?`, + }); + }); + it("trying to access member of aliased model expression shouldn't crash", async () => { + testHost.addTypeSpecFile( + "main.tsp", + ` + alias A = {foo: string}; + + alias Aliased = A.prop; + + ` + ); + const diagnostics = await testHost.diagnose("main.tsp"); + expectDiagnostics(diagnostics, { + code: "invalid-ref", + message: `Cannot resolve 'prop' in node AliasStatement since it has no members. Did you mean to use "::" instead of "."?`, + }); + }); });