Skip to content

Commit

Permalink
Fix alias cannot be used to pass indeterminate entities (#3377)
Browse files Browse the repository at this point in the history
fix #3376
  • Loading branch information
timotheeguerin authored May 16, 2024
1 parent aa91dcd commit 8424b2e
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- "@typespec/compiler"
---
Fixing change from this release
46 changes: 35 additions & 11 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,13 @@ export function createChecker(program: Program): Checker {
}
}

function getTypeForTypeOrIndeterminate(entity: Type | IndeterminateEntity): Type {
if (entity.entityKind === "Indeterminate") {
return entity.type;
}
return entity;
}

function getTypeForNode(node: Node, mapper?: TypeMapper): Type {
const entity = checkNode(node, mapper);
if (entity === null) {
Expand Down Expand Up @@ -1634,7 +1641,7 @@ export function createChecker(program: Program): Checker {

const argumentNodes = node.kind === SyntaxKind.TypeReference ? node.arguments : [];
const symbolLinks = getSymbolLinks(sym);
let baseType: Type;
let baseType: Type | IndeterminateEntity;
if (
sym.flags &
(SymbolFlags.Model |
Expand Down Expand Up @@ -1664,7 +1671,7 @@ export function createChecker(program: Program): Checker {
} else if (sym.flags & SymbolFlags.Member) {
baseType = checkMemberSym(sym, mapper);
} else {
baseType = checkDeclaredType(sym, decl, mapper);
baseType = checkDeclaredTypeOrIndeterminate(sym, decl, mapper);
}
} else {
const declaredType = getOrCheckDeclaredType(sym, decl, mapper);
Expand Down Expand Up @@ -1726,16 +1733,15 @@ export function createChecker(program: Program): Checker {
// don't raise deprecation when the usage site is also a deprecated
// declaration.
const declarationNode = sym?.declarations[0];
if (declarationNode && mapper === undefined) {
if (declarationNode && mapper === undefined && isType(baseType)) {
if (!isTypeReferenceContextDeprecated(node.parent!)) {
checkDeprecated(baseType, declarationNode, node);
}
}

// Elements that could be used as type or values depending on the context
if (
baseType.kind === "EnumMember" ||
baseType.kind === "UnionVariant" ||
(isType(baseType) && (baseType.kind === "EnumMember" || baseType.kind === "UnionVariant")) ||
isNullType(baseType)
) {
return createIndeterminateEntity(baseType);
Expand Down Expand Up @@ -1780,11 +1786,11 @@ export function createChecker(program: Program): Checker {
* @param mapper Type mapper for template resolution
* @returns The declared type for the given node.
*/
function checkDeclaredType(
function checkDeclaredTypeOrIndeterminate(
sym: Sym,
node: TemplateableNode,
mapper: TypeMapper | undefined
): Type {
): Type | IndeterminateEntity {
const type =
sym.flags & SymbolFlags.Model
? checkModelStatement(node as ModelStatementNode, mapper)
Expand All @@ -1801,6 +1807,14 @@ export function createChecker(program: Program): Checker {
return type;
}

function checkDeclaredType(
sym: Sym,
node: TemplateableNode,
mapper: TypeMapper | undefined
): Type {
return getTypeForTypeOrIndeterminate(checkDeclaredTypeOrIndeterminate(sym, node, mapper));
}

function getOrInstantiateTemplate(
templateNode: TemplateableNode,
params: TemplateParameter[],
Expand Down Expand Up @@ -5430,7 +5444,10 @@ export function createChecker(program: Program): Checker {
return finishType(member);
}

function checkAlias(node: AliasStatementNode, mapper: TypeMapper | undefined): Type {
function checkAlias(
node: AliasStatementNode,
mapper: TypeMapper | undefined
): Type | IndeterminateEntity {
const links = getSymbolLinks(node.symbol);

if (links.declaredType && mapper === undefined) {
Expand All @@ -5454,10 +5471,17 @@ export function createChecker(program: Program): Checker {
}

pendingResolutions.start(aliasSymId, ResolutionKind.Type);
const type = getTypeForNode(node.value, mapper);
if (!isValue(type)) {
linkType(links, type, mapper);
const type = checkNode(node.value, mapper);
if (type === null) {
links.declaredType = errorType;
return errorType;
}
if (isValue(type)) {
reportCheckerDiagnostic(createDiagnostic({ code: "value-in-type", target: node.value }));
links.declaredType = errorType;
return errorType;
}
linkType(links, type as any, mapper);
pendingResolutions.finish(aliasSymId, ResolutionKind.Type);

return type;
Expand Down
11 changes: 10 additions & 1 deletion packages/compiler/test/checker/relation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,16 @@ describe("compiler: checker: type relations", () => {
describe("Value constraint", () => {
describe("valueof string", () => {
it("can assign string literal", async () => {
await checkValueAssignableToConstraint({ source: `"foo bar"`, target: "string" });
await checkValueAssignableToConstraint({ source: `"foo bar"`, target: "valueof string" });
});

it("can assign string literal via alias", async () => {
const diagnostics = await runner.diagnose(`
model Foo<T extends valueof string> {}
alias Test = Foo<A>;
alias A = "abc";
`);
expectDiagnosticEmpty(diagnostics);
});

it("cannot assign numeric literal", async () => {
Expand Down

0 comments on commit 8424b2e

Please sign in to comment.