11import {
2+ AbstractCallable ,
3+ AliasDecl ,
24 Argument ,
35 DataModel ,
46 DataModelAttribute ,
57 DataModelFieldAttribute ,
68 Expression ,
7- FunctionDecl ,
89 FunctionParam ,
910 InvocationExpr ,
11+ isAliasDecl ,
1012 isArrayExpr ,
1113 isDataModel ,
1214 isDataModelAttribute ,
@@ -47,24 +49,24 @@ function func(name: string) {
4749 */
4850export default class FunctionInvocationValidator implements AstValidator < Expression > {
4951 validate ( expr : InvocationExpr , accept : ValidationAcceptor ) : void {
50- const funcDecl = expr . function . ref ;
51- if ( ! funcDecl ) {
52- accept ( 'error' , 'function cannot be resolved' , { node : expr } ) ;
52+ const callableDecl = expr . function . ref ;
53+ if ( ! callableDecl ) {
54+ accept ( 'error' , 'function or alias cannot be resolved' , { node : expr } ) ;
5355 return ;
5456 }
5557
56- if ( ! this . validateArgs ( funcDecl , expr . args , accept ) ) {
58+ if ( ! this . validateArgs ( callableDecl , expr . args , accept ) ) {
5759 return ;
5860 }
5961
60- if ( isFromStdlib ( funcDecl ) ) {
62+ if ( isFromStdlib ( callableDecl ) ) {
6163 // validate standard library functions
6264
6365 // find the containing attribute context for the invocation
6466 let curr : AstNode | undefined = expr . $container ;
65- let containerAttribute : DataModelAttribute | DataModelFieldAttribute | undefined ;
67+ let containerAttribute : DataModelAttribute | DataModelFieldAttribute | AliasDecl | undefined ;
6668 while ( curr ) {
67- if ( isDataModelAttribute ( curr ) || isDataModelFieldAttribute ( curr ) ) {
69+ if ( isDataModelAttribute ( curr ) || isDataModelFieldAttribute ( curr ) || isAliasDecl ( curr ) ) {
6870 containerAttribute = curr ;
6971 break ;
7072 }
@@ -75,12 +77,12 @@ export default class FunctionInvocationValidator implements AstValidator<Express
7577 const exprContext = this . getExpressionContext ( containerAttribute ) ;
7678
7779 // get the context allowed for the function
78- const funcAllowedContext = getFunctionExpressionContext ( funcDecl ) ;
80+ const funcAllowedContext = getFunctionExpressionContext ( callableDecl ) ;
7981
8082 if ( funcAllowedContext . length > 0 && ( ! exprContext || ! funcAllowedContext . includes ( exprContext ) ) ) {
8183 accept (
8284 'error' ,
83- `function "${ funcDecl . name } " is not allowed in the current context${
85+ `function "${ callableDecl . name } " is not allowed in the current context${
8486 exprContext ? ': ' + exprContext : ''
8587 } `,
8688 {
@@ -93,7 +95,7 @@ export default class FunctionInvocationValidator implements AstValidator<Express
9395 // TODO: express function validation rules declaratively in ZModel
9496
9597 const allCasing = [ 'original' , 'upper' , 'lower' , 'capitalize' , 'uncapitalize' ] ;
96- if ( [ 'currentModel' , 'currentOperation' ] . includes ( funcDecl . name ) ) {
98+ if ( [ 'currentModel' , 'currentOperation' ] . includes ( callableDecl . name ) ) {
9799 const arg = getLiteral < string > ( expr . args [ 0 ] ?. value ) ;
98100 if ( arg && ! allCasing . includes ( arg ) ) {
99101 accept ( 'error' , `argument must be one of: ${ allCasing . map ( ( c ) => '"' + c + '"' ) . join ( ', ' ) } ` , {
@@ -108,6 +110,12 @@ export default class FunctionInvocationValidator implements AstValidator<Express
108110
109111 // first argument must refer to a model field
110112 const firstArg = expr . args ?. [ 0 ] ?. value ;
113+ const callableDecl = expr . function . ref ;
114+ if ( ! callableDecl ) {
115+ accept ( 'error' , 'function or rule cannot be resolved' , { node : expr } ) ;
116+ return ;
117+ }
118+
111119 if ( firstArg ) {
112120 if ( ! getFieldReference ( firstArg ) ) {
113121 accept ( 'error' , 'first argument must be a field reference' , { node : firstArg } ) ;
@@ -130,7 +138,7 @@ export default class FunctionInvocationValidator implements AstValidator<Express
130138 ! (
131139 isArrayExpr ( secondArg ) &&
132140 secondArg . items . every (
133- ( item ) =>
141+ ( item : Expression ) =>
134142 isLiteralExpr ( item ) || isEnumFieldReference ( item ) || isAuthOrAuthMemberAccess ( item )
135143 )
136144 )
@@ -144,19 +152,24 @@ export default class FunctionInvocationValidator implements AstValidator<Express
144152 ) ;
145153 }
146154 }
147- }
148155
149- // run checkers for specific functions
150- const checker = invocationCheckers . get ( expr . function . $refText ) ;
151- if ( checker ) {
152- checker . value . call ( this , expr , accept ) ;
156+ // run checkers for specific functions
157+ const checker = invocationCheckers . get ( expr . function . $refText ) ;
158+ if ( checker ) {
159+ checker . value . call ( this , expr , accept ) ;
160+ }
153161 }
154162 }
155163
156- private getExpressionContext ( containerAttribute : DataModelAttribute | DataModelFieldAttribute | undefined ) {
164+ private getExpressionContext (
165+ containerAttribute : DataModelAttribute | DataModelFieldAttribute | AliasDecl | undefined
166+ ) {
157167 if ( ! containerAttribute ) {
158168 return undefined ;
159169 }
170+ if ( isAliasDecl ( containerAttribute ) ) {
171+ return ExpressionContext . AliasFunction ;
172+ }
160173 if ( isValidationAttribute ( containerAttribute ) ) {
161174 return ExpressionContext . ValidationRule ;
162175 }
@@ -171,7 +184,7 @@ export default class FunctionInvocationValidator implements AstValidator<Express
171184 return isInvocationExpr ( expr ) && [ 'currentModel' , 'currentOperation' ] . includes ( expr . function . $refText ) ;
172185 }
173186
174- private validateArgs ( funcDecl : FunctionDecl , args : Argument [ ] , accept : ValidationAcceptor ) {
187+ private validateArgs ( funcDecl : AbstractCallable , args : Argument [ ] , accept : ValidationAcceptor ) {
175188 let success = true ;
176189 for ( let i = 0 ; i < funcDecl . params . length ; i ++ ) {
177190 const param = funcDecl . params [ i ] ;
@@ -289,7 +302,7 @@ export default class FunctionInvocationValidator implements AstValidator<Express
289302 }
290303
291304 const policyAttrs = currModel . attributes . filter (
292- ( attr ) => attr . decl . $refText === '@@allow' || attr . decl . $refText === '@@deny'
305+ ( attr : DataModelAttribute ) => attr . decl . $refText === '@@allow' || attr . decl . $refText === '@@deny'
293306 ) ;
294307 for ( const attr of policyAttrs ) {
295308 const rule = attr . args [ 1 ] ;
0 commit comments