@@ -52,7 +52,12 @@ import {
5252} from 'langium' ;
5353import { match } from 'ts-pattern' ;
5454import { CancellationToken } from 'vscode-jsonrpc' ;
55- import { getAllDeclarationsFromImports , isAuthInvocation , getContainingDataModel } from '../utils/ast-utils' ;
55+ import {
56+ getAllDeclarationsFromImports ,
57+ getContainingDataModel ,
58+ isAuthInvocation ,
59+ isCollectionPredicate ,
60+ } from '../utils/ast-utils' ;
5661import { mapBuiltinTypeToExpressionType } from './validator/utils' ;
5762
5863interface DefaultReference extends Reference {
@@ -94,9 +99,21 @@ export class ZModelLinker extends DefaultLinker {
9499 extraScopes : ScopeProvider [ ] ,
95100 onlyFromExtraScopes = false
96101 ) {
97- if ( ! this . resolveFromScopeProviders ( container , property , document , extraScopes ) && ! onlyFromExtraScopes ) {
98- // eslint-disable-next-line @typescript-eslint/no-explicit-any
99- const reference : Reference < AstNode > = ( container as any ) [ property ] ;
102+ if ( this . resolveFromScopeProviders ( container , property , document , extraScopes ) ) {
103+ return ;
104+ }
105+
106+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107+ const reference : DefaultReference = ( container as any ) [ property ] ;
108+
109+ if ( onlyFromExtraScopes ) {
110+ // if reference is not resolved from explicit scope providers and automatic linking is not allowed,
111+ // we should explicitly create a linking error
112+ reference . _ref = this . createLinkingError ( { reference, container, property } ) ;
113+
114+ // Add the reference to the document's array of references
115+ document . references . push ( reference ) ;
116+ } else {
100117 this . doLink ( { reference, container, property } , document ) ;
101118 }
102119 }
@@ -118,6 +135,10 @@ export class ZModelLinker extends DefaultLinker {
118135 if ( target ) {
119136 reference . _ref = target ;
120137 reference . _nodeDescription = this . descriptions . createDescription ( target , target . name , document ) ;
138+
139+ // Add the reference to the document's array of references
140+ document . references . push ( reference ) ;
141+
121142 return target ;
122143 }
123144 }
@@ -244,6 +265,22 @@ export class ZModelLinker extends DefaultLinker {
244265 node . args . forEach ( ( arg ) => this . resolve ( arg , document , extraScopes ) ) ;
245266
246267 if ( node . target . ref ) {
268+ // if the reference is inside the RHS of a collection predicate, it cannot be resolve to a field
269+ // not belonging to the collection's model type
270+
271+ const collectionPredicateContext = this . getCollectionPredicateContextDataModel ( node ) ;
272+ if (
273+ // inside a collection predicate RHS
274+ collectionPredicateContext &&
275+ // current ref expr is resolved to a field
276+ isDataModelField ( node . target . ref ) &&
277+ // the resolved field doesn't belong to the collection predicate's operand's type
278+ node . target . ref . $container !== collectionPredicateContext
279+ ) {
280+ this . unresolvableRefExpr ( node ) ;
281+ return ;
282+ }
283+
247284 // resolve type
248285 if ( node . target . ref . $type === EnumField ) {
249286 this . resolveToBuiltinTypeOrDecl ( node , node . target . ref . $container ) ;
@@ -253,6 +290,26 @@ export class ZModelLinker extends DefaultLinker {
253290 }
254291 }
255292
293+ private getCollectionPredicateContextDataModel ( node : ReferenceExpr ) {
294+ let curr : AstNode | undefined = node ;
295+ while ( curr ) {
296+ if (
297+ curr . $container &&
298+ // parent is a collection predicate
299+ isCollectionPredicate ( curr . $container ) &&
300+ // the collection predicate's LHS is resolved to a DataModel
301+ isDataModel ( curr . $container . left . $resolvedType ?. decl ) &&
302+ // current node is the RHS
303+ curr . $containerProperty === 'right'
304+ ) {
305+ // return the resolved type of LHS
306+ return curr . $container . left . $resolvedType ?. decl ;
307+ }
308+ curr = curr . $container ;
309+ }
310+ return undefined ;
311+ }
312+
256313 private resolveArray ( node : ArrayExpr , document : LangiumDocument < AstNode > , extraScopes : ScopeProvider [ ] ) {
257314 node . items . forEach ( ( item ) => this . resolve ( item , document , extraScopes ) ) ;
258315
@@ -414,13 +471,8 @@ export class ZModelLinker extends DefaultLinker {
414471 if ( resolved ) {
415472 this . resolveToDeclaredType ( item , ( resolved as DataModelField ) . type ) ;
416473 } else {
417- // need to clear linked reference, because it's resolved in default scope by default
418- const ref = item . target as DefaultReference ;
419- ref . _ref = this . createLinkingError ( {
420- reference : ref ,
421- container : item ,
422- property : 'target' ,
423- } ) ;
474+ // mark unresolvable
475+ this . unresolvableRefExpr ( item ) ;
424476 }
425477 }
426478 } ) ;
@@ -432,13 +484,8 @@ export class ZModelLinker extends DefaultLinker {
432484 if ( resolved ) {
433485 this . resolveToDeclaredType ( node . value , ( resolved as DataModelField ) . type ) ;
434486 } else {
435- // need to clear linked reference, because it's resolved in default scope by default
436- const ref = node . value . target as DefaultReference ;
437- ref . _ref = this . createLinkingError ( {
438- reference : ref ,
439- container : node . value ,
440- property : 'target' ,
441- } ) ;
487+ // mark unresolvable
488+ this . unresolvableRefExpr ( node . value ) ;
442489 }
443490 }
444491 }
@@ -448,6 +495,15 @@ export class ZModelLinker extends DefaultLinker {
448495 node . $resolvedType = node . value . $resolvedType ;
449496 }
450497
498+ private unresolvableRefExpr ( item : ReferenceExpr ) {
499+ const ref = item . target as DefaultReference ;
500+ ref . _ref = this . createLinkingError ( {
501+ reference : ref ,
502+ container : item ,
503+ property : 'target' ,
504+ } ) ;
505+ }
506+
451507 private findAttrParamForArg ( arg : AttributeArg ) : AttributeParam | undefined {
452508 const attr = arg . $container . decl . ref ;
453509 if ( ! attr ) {
0 commit comments