Skip to content

Commit 6d2a08b

Browse files
committed
fix: link invoked alias to its declaration
1 parent afa982d commit 6d2a08b

File tree

7 files changed

+43
-23
lines changed

7 files changed

+43
-23
lines changed

packages/schema/src/language-server/validator/attribute-application-validator.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
DataModelFieldAttribute,
99
InternalAttribute,
1010
ReferenceExpr,
11+
isAliasDecl,
1112
isArrayExpr,
1213
isAttribute,
1314
isDataModel,
@@ -28,7 +29,12 @@ import {
2829
import { ValidationAcceptor, streamAllContents, streamAst } from 'langium';
2930
import pluralize from 'pluralize';
3031
import { AstValidator } from '../types';
31-
import { getStringLiteral, mapBuiltinTypeToExpressionType, typeAssignable } from './utils';
32+
import {
33+
getStringLiteral,
34+
mapBuiltinTypeToExpressionType,
35+
mappedRawExpressionTypeToResolvedShape,
36+
typeAssignable,
37+
} from './utils';
3238

3339
// a registry of function handlers marked with @check
3440
const attributeCheckers = new Map<string, PropertyDescriptor>();
@@ -264,6 +270,16 @@ function assignableToAttributeParam(arg: AttributeArg, param: AttributeParam, at
264270
let dstType = param.type.type;
265271
let dstIsArray = param.type.array;
266272

273+
if (isAliasDecl(arg.$resolvedType?.decl)) {
274+
if (dstType === 'ContextType') {
275+
// TODO: what is context type? Passed to true to avoid error, to be fixed later
276+
return true;
277+
}
278+
const aliasExpression = arg.$resolvedType.decl.expression;
279+
const mappedType = mappedRawExpressionTypeToResolvedShape(aliasExpression.$type);
280+
return dstType === mappedType;
281+
}
282+
267283
if (dstType === 'ContextType') {
268284
// ContextType is inferred from the attribute's container's type
269285
if (isDataModelField(attr.$container)) {

packages/schema/src/language-server/validator/expression-validator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ export default class ExpressionValidator implements AstValidator<Expression> {
5050
}
5151
return false;
5252
});
53-
if (!hasReferenceResolutionError) {
53+
if (hasReferenceResolutionError) {
5454
// report silent errors not involving linker errors
55-
accept('error', 'Expression cannot be resolved', {
55+
accept('error', `Expression cannot be resolved: ${expr.$cstNode?.text}`, {
5656
node: expr,
5757
});
5858
}

packages/schema/src/language-server/zmodel-linker.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ import {
6767
isAliasInvocation,
6868
} from '../utils/ast-utils';
6969
import { isMemberContainer } from './utils';
70-
import { mapBuiltinTypeToExpressionType, mappedRawExpressionTypeToResolvedShape } from './validator/utils';
70+
import { mapBuiltinTypeToExpressionType } from './validator/utils';
7171

7272
interface DefaultReference extends Reference {
7373
_ref?: AstNode | LinkingError;
@@ -312,19 +312,18 @@ export class ZModelLinker extends DefaultLinker {
312312
} else if (isFutureExpr(node)) {
313313
// future() function is resolved to current model
314314
node.$resolvedType = { decl: getContainingDataModel(node) };
315-
} else if (isAliasInvocation(node) || !!getContainerOfType(node, isAliasDecl)) {
315+
} else if (isAliasInvocation(node)) {
316316
// function is resolved to matching alias declaration
317-
const expressionType = funcDecl.expression?.$type;
318-
if (!expressionType) {
319-
this.createLinkingError({
320-
reference: node.function,
321-
container: node,
322-
property: 'alias',
323-
});
324-
return;
317+
const containingAlias = getContainerOfType(node, isAliasDecl);
318+
const allAlias = getContainerOfType(node, isModel)?.declarations.filter(isAliasDecl) ?? [];
319+
const matchingAlias =
320+
isAliasInvocation(node) && !containingAlias
321+
? allAlias.find((alias) => alias.name === node.function.$refText)
322+
: containingAlias;
323+
324+
if (matchingAlias) {
325+
node.$resolvedType = { decl: matchingAlias, nullable: false };
325326
}
326-
const mappedType = mappedRawExpressionTypeToResolvedShape(expressionType);
327-
this.resolveToBuiltinTypeOrDecl(node, mappedType);
328327
} else {
329328
this.resolveToDeclaredType(node, (funcDecl as FunctionDecl).returnType);
330329
}

packages/schema/src/language-server/zmodel-semantic.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
isAliasDecl,
23
isAttribute,
34
isAttributeArg,
45
isConfigField,
@@ -83,7 +84,7 @@ export class ZModelSemanticTokenProvider extends AbstractSemanticTokenProvider {
8384
property: 'function',
8485
type: SemanticTokenTypes.function,
8586
});
86-
} else if (isFunctionDecl(node) || isAttribute(node)) {
87+
} else if (isFunctionDecl(node) || isAliasDecl(node) || isAttribute(node)) {
8788
acceptor({
8889
node,
8990
property: 'name',

packages/schema/src/plugins/enhancer/policy/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
getIdFields,
1515
getLiteral,
1616
getQueryGuardFunctionName,
17-
isAuthInvocation,
1817
hasAuthInvocation,
18+
isAuthInvocation,
1919
isDataModelFieldReference,
2020
isEnumFieldReference,
2121
isFromStdlib,

packages/sdk/src/utils.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
FunctionDecl,
1515
GeneratorDecl,
1616
InternalAttribute,
17-
InvocationExpr,
17+
isAliasDecl,
1818
isArrayExpr,
1919
isConfigArrayExpr,
2020
isDataModel,
@@ -41,6 +41,7 @@ import fs from 'node:fs';
4141
import path from 'path';
4242
import { ExpressionContext, STD_LIB_MODULE_NAME } from './constants';
4343
import { PluginError, type PluginDeclaredOptions, type PluginOptions } from './types';
44+
import { streamAst } from 'langium';
4445

4546
/**
4647
* Gets data models in the ZModel schema.
@@ -457,10 +458,13 @@ export function isAuthInvocation(node: AstNode) {
457458
}
458459

459460
export function hasAuthInvocation(node: AstNode) {
460-
return (
461-
isAuthInvocation(node) ||
462-
(isAliasExpr(node) && (node as InvocationExpr).function?.ref?.expression?.$cstNode?.text.includes('auth()'))
463-
);
461+
return streamAst(node).some((node) => {
462+
const hasAuth =
463+
isAuthInvocation(node) ||
464+
(isAliasDecl(node.$resolvedType?.decl) &&
465+
node.$resolvedType?.decl.expression?.$cstNode?.text.includes('auth()'));
466+
return hasAuth;
467+
});
464468
}
465469

466470
export function isFromStdlib(node: AstNode) {

tests/integration/tests/plugins/policy.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ model M {
112112
});
113113

114114
it('alias expressions', async () => {
115-
const { policy, projectDir } = await loadSchema(
115+
const { policy } = await loadSchema(
116116
`
117117
alias allowAll() {
118118
true

0 commit comments

Comments
 (0)