@@ -52,7 +52,12 @@ import {
52
52
} from 'langium' ;
53
53
import { match } from 'ts-pattern' ;
54
54
import { 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' ;
56
61
import { mapBuiltinTypeToExpressionType } from './validator/utils' ;
57
62
58
63
interface DefaultReference extends Reference {
@@ -94,9 +99,21 @@ export class ZModelLinker extends DefaultLinker {
94
99
extraScopes : ScopeProvider [ ] ,
95
100
onlyFromExtraScopes = false
96
101
) {
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 {
100
117
this . doLink ( { reference, container, property } , document ) ;
101
118
}
102
119
}
@@ -118,6 +135,10 @@ export class ZModelLinker extends DefaultLinker {
118
135
if ( target ) {
119
136
reference . _ref = target ;
120
137
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
+
121
142
return target ;
122
143
}
123
144
}
@@ -244,6 +265,22 @@ export class ZModelLinker extends DefaultLinker {
244
265
node . args . forEach ( ( arg ) => this . resolve ( arg , document , extraScopes ) ) ;
245
266
246
267
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
+
247
284
// resolve type
248
285
if ( node . target . ref . $type === EnumField ) {
249
286
this . resolveToBuiltinTypeOrDecl ( node , node . target . ref . $container ) ;
@@ -253,6 +290,26 @@ export class ZModelLinker extends DefaultLinker {
253
290
}
254
291
}
255
292
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
+
256
313
private resolveArray ( node : ArrayExpr , document : LangiumDocument < AstNode > , extraScopes : ScopeProvider [ ] ) {
257
314
node . items . forEach ( ( item ) => this . resolve ( item , document , extraScopes ) ) ;
258
315
@@ -414,13 +471,8 @@ export class ZModelLinker extends DefaultLinker {
414
471
if ( resolved ) {
415
472
this . resolveToDeclaredType ( item , ( resolved as DataModelField ) . type ) ;
416
473
} 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 ) ;
424
476
}
425
477
}
426
478
} ) ;
@@ -432,13 +484,8 @@ export class ZModelLinker extends DefaultLinker {
432
484
if ( resolved ) {
433
485
this . resolveToDeclaredType ( node . value , ( resolved as DataModelField ) . type ) ;
434
486
} 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 ) ;
442
489
}
443
490
}
444
491
}
@@ -448,6 +495,15 @@ export class ZModelLinker extends DefaultLinker {
448
495
node . $resolvedType = node . value . $resolvedType ;
449
496
}
450
497
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
+
451
507
private findAttrParamForArg ( arg : AttributeArg ) : AttributeParam | undefined {
452
508
const attr = arg . $container . decl . ref ;
453
509
if ( ! attr ) {
0 commit comments