@sap/cds-compiler 4.4.4 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/bin/cdsc.js +5 -0
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/main.js +68 -47
- package/lib/api/options.js +10 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +28 -6
- package/lib/base/messages.js +18 -13
- package/lib/base/model.js +3 -0
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/validator.js +6 -4
- package/lib/compiler/assert-consistency.js +38 -16
- package/lib/compiler/builtins.js +10 -49
- package/lib/compiler/checks.js +16 -8
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +4 -1
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +68 -14
- package/lib/compiler/shared.js +44 -27
- package/lib/compiler/tweak-assocs.js +158 -37
- package/lib/compiler/utils.js +9 -0
- package/lib/edm/annotations/edmJson.js +35 -61
- package/lib/edm/annotations/genericTranslation.js +13 -5
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +4 -1
- package/lib/edm/edmInboundChecks.js +59 -15
- package/lib/edm/edmPreprocessor.js +1 -7
- package/lib/gen/Dictionary.json +8 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -2
- package/lib/gen/languageParser.js +6095 -5195
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +22 -3
- package/lib/language/errorStrategy.js +7 -3
- package/lib/language/genericAntlrParser.js +120 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +2 -3
- package/lib/render/toCdl.js +31 -13
- package/lib/render/toHdbcds.js +20 -30
- package/lib/render/toSql.js +33 -54
- package/lib/render/utils/common.js +24 -6
- package/lib/transform/db/applyTransformations.js +59 -2
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +2 -2
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/forOdata.js +13 -46
- package/lib/transform/forRelationalDB.js +2 -1
- package/lib/transform/translateAssocsToJoins.js +13 -4
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
package/lib/compiler/populate.js
CHANGED
|
@@ -38,6 +38,7 @@ const {
|
|
|
38
38
|
annotationLocation,
|
|
39
39
|
linkToOrigin,
|
|
40
40
|
setMemberParent,
|
|
41
|
+
dependsOn,
|
|
41
42
|
proxyCopyMembers,
|
|
42
43
|
setExpandStatus,
|
|
43
44
|
setExpandStatusAnnotate,
|
|
@@ -855,6 +856,9 @@ function populate( model ) {
|
|
|
855
856
|
const elemLocation = !query._main.$inferred && location;
|
|
856
857
|
const origin = envParent ? navElem : navElem._origin;
|
|
857
858
|
const elem = linkToOrigin( origin, name, query, null, elemLocation );
|
|
859
|
+
if (origin.$calcDepElement) // TODO: this will be changed in the next PR
|
|
860
|
+
dependsOn( elem, origin.$calcDepElement, location );
|
|
861
|
+
|
|
858
862
|
// TODO: check assocToMany { * }
|
|
859
863
|
dictAdd( elements, name, elem, ( _name, loc ) => {
|
|
860
864
|
// there can be a definition from a previous inline with the same name:
|
|
@@ -1183,7 +1187,7 @@ function populate( model ) {
|
|
|
1183
1187
|
function isDirectProjection( proj, base ) {
|
|
1184
1188
|
return proj.kind === 'entity' && // not event
|
|
1185
1189
|
// direct proj (TODO: or should we add them to another list?)
|
|
1186
|
-
// TODO: delete ENTITY._from
|
|
1190
|
+
// TODO: delete ENTITY._from - maybe not...
|
|
1187
1191
|
proj.query && proj.query.op && proj.query.op.val === 'SELECT' &&
|
|
1188
1192
|
proj._from && proj._from.length === 1 &&
|
|
1189
1193
|
base === resolvePath( proj._from[0], 'from', proj.query );
|
|
@@ -21,7 +21,7 @@ const {
|
|
|
21
21
|
viewFromPrimary,
|
|
22
22
|
} = require('./utils');
|
|
23
23
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
24
|
-
// const {
|
|
24
|
+
// const { ref } = require( '../model/revealInternalProperties' )
|
|
25
25
|
|
|
26
26
|
// Note that propagation here is also used for deep-copying (function `onlyViaParent`)
|
|
27
27
|
function propagate( model ) {
|
|
@@ -59,7 +59,7 @@ function propagate( model ) {
|
|
|
59
59
|
srid: always,
|
|
60
60
|
localized: always,
|
|
61
61
|
target: notWithExpand,
|
|
62
|
-
targetAspect
|
|
62
|
+
targetAspect,
|
|
63
63
|
cardinality: notWithExpand,
|
|
64
64
|
on: notWithExpand,
|
|
65
65
|
foreignKeys: expensive, // includes "notWithExpand", dictionary copy
|
|
@@ -68,6 +68,7 @@ function propagate( model ) {
|
|
|
68
68
|
enum: expensive,
|
|
69
69
|
params: expensive, // actually only with parent action
|
|
70
70
|
returns,
|
|
71
|
+
$filtered: annotation,
|
|
71
72
|
};
|
|
72
73
|
const { options } = model;
|
|
73
74
|
// eslint-disable-next-line max-len
|
|
@@ -90,7 +91,7 @@ function propagate( model ) {
|
|
|
90
91
|
runMembers( art );
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
93
|
-
// console.log('RUN:',
|
|
94
|
+
// if (!art.builtin)console.log('RUN:', ref(art))
|
|
94
95
|
|
|
95
96
|
const chain = [];
|
|
96
97
|
let targets = [ art ];
|
|
@@ -109,6 +110,7 @@ function propagate( model ) {
|
|
|
109
110
|
if (target.value?._artifact.$inferred !== 'include') {
|
|
110
111
|
// If the referred to element is not inferred, it is a new one and not the original.
|
|
111
112
|
// The new one was not originally referred to => error;
|
|
113
|
+
// TODO: no messages in propagator.js
|
|
112
114
|
warning( 'ref-unexpected-override', [ target.name.location, target ],
|
|
113
115
|
{ id: target.name.id, target: target.value?._artifact },
|
|
114
116
|
'Calculated element $(ID) does not originally refer to $(TARGET)' );
|
|
@@ -134,11 +136,11 @@ function propagate( model ) {
|
|
|
134
136
|
chain.reverse();
|
|
135
137
|
chain.forEach( step );
|
|
136
138
|
runMembers( art );
|
|
137
|
-
// console.log('DONE:',
|
|
139
|
+
// if(!art.builtin)console.log('DONE:',ref(art),art.elements?Object.keys(art.elements):0);
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
function runMembers( art ) {
|
|
141
|
-
// console.log('MEMBERS:',
|
|
143
|
+
// if(!art.builtin)console.log('MEMBERS:',ref(art))
|
|
142
144
|
forEachMember( art, run ); // after propagation in parent!
|
|
143
145
|
// propagate to sub query elements even if not requested:
|
|
144
146
|
if (art.$queries)
|
|
@@ -150,16 +152,29 @@ function propagate( model ) {
|
|
|
150
152
|
}
|
|
151
153
|
if (obj.items)
|
|
152
154
|
run( obj.items );
|
|
155
|
+
obj = obj.targetAspect;
|
|
156
|
+
// if(obj)console.log('TA:',ref(art),!!getOrigin( obj ))
|
|
157
|
+
if (obj && isAnonymousAspect( obj ))
|
|
158
|
+
run( obj );
|
|
153
159
|
setLink( art, '_status', 'propagated' );
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
function isAnonymousAspect( aspect ) {
|
|
163
|
+
while (aspect) {
|
|
164
|
+
if (aspect.elements)
|
|
165
|
+
return true;
|
|
166
|
+
aspect = getOrigin( aspect );
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
156
171
|
function step({ target, source }) {
|
|
157
|
-
// console.log('PROPS:',source&&source.name,'->',target.name)
|
|
158
172
|
const viaType = target.type && // TODO: falsy $inferred value instead of 'cast'?
|
|
159
173
|
(!target.type.$inferred || target.type.$inferred === 'cast');
|
|
160
174
|
const keys = Object.keys( source );
|
|
175
|
+
// console.log('PROPS:',ref(source),'->',ref(target),keys.join('+'))
|
|
161
176
|
for (const prop of keys) {
|
|
162
|
-
// TODO: warning with competing props from multi-includes
|
|
177
|
+
// TODO: warning with competing props from multi-includes, but not in propagator.js
|
|
163
178
|
if (target[prop] !== undefined || source[prop] === undefined)
|
|
164
179
|
continue;
|
|
165
180
|
const transformer = props[prop] || props[prop.charAt(0)];
|
|
@@ -227,7 +242,7 @@ function propagate( model ) {
|
|
|
227
242
|
// * `enum`: an enum cannot be used with `expand`
|
|
228
243
|
// * `keys`: should also not be propagated with `expand`
|
|
229
244
|
function expensive( prop, target, source ) {
|
|
230
|
-
// console.log(prop,source
|
|
245
|
+
// console.log('EXP:',prop,ref(source),'->',ref(target));
|
|
231
246
|
if (source.kind === 'builtin')
|
|
232
247
|
return;
|
|
233
248
|
if (target.expand) // do not propagate `keys` with expand
|
|
@@ -245,8 +260,12 @@ function propagate( model ) {
|
|
|
245
260
|
target._outer && target._outer.location;
|
|
246
261
|
const dict = source[prop];
|
|
247
262
|
target[prop] = Object.create( null ); // also propagate empty elements
|
|
263
|
+
const propagateKey = target.kind === 'aspect'; // anonymous aspect
|
|
248
264
|
for (const name in dict) {
|
|
249
|
-
const
|
|
265
|
+
const origin = dict[name];
|
|
266
|
+
const member = linkToOrigin( origin, name, target, prop, location );
|
|
267
|
+
if (propagateKey && origin.key)
|
|
268
|
+
member.key = Object.assign( { $inferred: 'expanded' }, origin.key );
|
|
250
269
|
member.$inferred = 'proxy';
|
|
251
270
|
if (prop === 'foreignKeys')
|
|
252
271
|
setLink( member, '_effectiveType', member );
|
|
@@ -262,6 +281,24 @@ function propagate( model ) {
|
|
|
262
281
|
always( prop, target, source );
|
|
263
282
|
}
|
|
264
283
|
|
|
284
|
+
function targetAspect( prop, target, source ) {
|
|
285
|
+
if (target.targetAspect)
|
|
286
|
+
return;
|
|
287
|
+
const ta = source.targetAspect;
|
|
288
|
+
if (!ta.elements && !ta._origin) { // _origin set for elements in source
|
|
289
|
+
notWithExpand( prop, target, source );
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
const tat = { location: ta.location, $inferred: 'prop', kind: 'aspect' };
|
|
293
|
+
setLink( tat, '_origin', ta );
|
|
294
|
+
setLink( tat, '_outer', target );
|
|
295
|
+
setLink( tat, '_parent', target._parent );
|
|
296
|
+
setLink( tat, '_main', null );
|
|
297
|
+
target.targetAspect = tat;
|
|
298
|
+
// console.log('TAC:',ref(tat),'via',ref(ta))
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
265
302
|
function notWithExpand( prop, target, source ) {
|
|
266
303
|
if (!target.expand || prop === 'type' && source.elements)
|
|
267
304
|
always( prop, target, source );
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -44,6 +44,7 @@ const {
|
|
|
44
44
|
forEachGeneric,
|
|
45
45
|
forEachInOrder,
|
|
46
46
|
isDeprecatedEnabled,
|
|
47
|
+
isBetaEnabled,
|
|
47
48
|
} = require('../base/model');
|
|
48
49
|
const { dictAdd } = require('../base/dictionaries');
|
|
49
50
|
const { dictLocation, weakLocation } = require('../base/location');
|
|
@@ -132,7 +133,9 @@ function resolve( model ) {
|
|
|
132
133
|
if (location) {
|
|
133
134
|
model.$assert = null;
|
|
134
135
|
const msg = semanticLoc && 'target';
|
|
135
|
-
error( 'ref-cyclic', [ location, semanticLoc || user ], {
|
|
136
|
+
error( 'ref-cyclic', [ location, semanticLoc || user ], {
|
|
137
|
+
art, '#': msg,
|
|
138
|
+
}, {
|
|
136
139
|
std: 'Illegal circular reference to $(ART)',
|
|
137
140
|
element: 'Illegal circular reference to element $(MEMBER) of $(ART)',
|
|
138
141
|
target: 'Illegal circular reference to target $(ART)',
|
|
@@ -638,8 +641,11 @@ function resolve( model ) {
|
|
|
638
641
|
|
|
639
642
|
// If cardinality is not specified, the compiler uses the inferred one.
|
|
640
643
|
if (specifiedElement.cardinality) {
|
|
644
|
+
// Users can change the origin's cardinality via filter: We can't rely on the origin.
|
|
645
|
+
const ref = inferredElement.value?.path;
|
|
646
|
+
const assocFilterCardinality = ref?.[ref.length - 1]?.cardinality;
|
|
641
647
|
const sCardinality = specifiedElement.cardinality;
|
|
642
|
-
const iCardinality =
|
|
648
|
+
const iCardinality = assocFilterCardinality || getInferredCardinality();
|
|
643
649
|
if (!iCardinality) {
|
|
644
650
|
error( 'query-mismatched-element', [
|
|
645
651
|
sCardinality.location || specifiedElement.location, user,
|
|
@@ -747,6 +753,20 @@ function resolve( model ) {
|
|
|
747
753
|
}
|
|
748
754
|
return element[prop];
|
|
749
755
|
}
|
|
756
|
+
|
|
757
|
+
function getInferredCardinality() {
|
|
758
|
+
let element = inferredElement;
|
|
759
|
+
if (element._effectiveType !== 0) {
|
|
760
|
+
while (getOrigin( element )) {
|
|
761
|
+
const ref = element.value?.path;
|
|
762
|
+
if (element.cardinality || ref?.[ref.length - 1]?.cardinality)
|
|
763
|
+
break;
|
|
764
|
+
element = getOrigin( element );
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const ref = element.value?.path;
|
|
768
|
+
return element.cardinality || ref?.[ref.length - 1]?.cardinality;
|
|
769
|
+
}
|
|
750
770
|
}
|
|
751
771
|
|
|
752
772
|
|
|
@@ -1353,18 +1373,50 @@ function resolve( model ) {
|
|
|
1353
1373
|
}
|
|
1354
1374
|
}
|
|
1355
1375
|
|
|
1356
|
-
function resolveAnnoExpr( expr, art ) {
|
|
1357
|
-
if (expr.$tokenTexts)
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1376
|
+
function resolveAnnoExpr( expr, art, anno = expr ) {
|
|
1377
|
+
if (expr.$tokenTexts) {
|
|
1378
|
+
if (!anno.kind)
|
|
1379
|
+
initAnnotationForExpression( anno, art );
|
|
1380
|
+
resolveExpr( expr, 'annotation', anno );
|
|
1381
|
+
reportUnsupportedAnnoExpr( expr );
|
|
1382
|
+
}
|
|
1383
|
+
else if (expr.literal === 'array') {
|
|
1384
|
+
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
1385
|
+
}
|
|
1386
|
+
else if (expr.literal === 'struct') {
|
|
1387
|
+
Object.values( expr.struct ).forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
function reportUnsupportedAnnoExpr( expr ) {
|
|
1392
|
+
if (isBetaEnabled( model.options, 'annotationExpressions' ))
|
|
1393
|
+
return;
|
|
1394
|
+
const alreadyReported = model.$messageFunctions.messages
|
|
1395
|
+
.find(msg => msg.messageId === 'anno-experimental-expressions');
|
|
1396
|
+
if (!alreadyReported) {
|
|
1397
|
+
info( 'anno-experimental-expressions', [ expr.location ], {
|
|
1398
|
+
option: 'annotationExpressions',
|
|
1399
|
+
// eslint-disable-next-line max-len
|
|
1400
|
+
}, 'Expressions in annotation values are a beta feature. Use at your own risk. (This message can be suppressed with beta flag $(OPTION))' );
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// for faster processing, mark artifacts and annotations which contain anno expressions
|
|
1405
|
+
function initAnnotationForExpression( anno, art ) {
|
|
1406
|
+
anno.kind = '$annotation';
|
|
1407
|
+
setLink( anno, '_outer', art );
|
|
1408
|
+
while (!art.$contains?.$annotation) {
|
|
1409
|
+
art.$contains ??= {};
|
|
1410
|
+
art.$contains.$annotation = true; // TODO: extra values for elem and $self refs
|
|
1411
|
+
if (!art._main)
|
|
1412
|
+
break;
|
|
1413
|
+
art = art._parent; // TODO: really go up?
|
|
1414
|
+
}
|
|
1363
1415
|
}
|
|
1364
1416
|
|
|
1365
1417
|
function resolveExpr( expr, exprCtx, user ) {
|
|
1366
1418
|
// console.log(expr?.location.line,exprCtx)
|
|
1367
|
-
traverseExpr( expr, exprCtx, user,
|
|
1419
|
+
traverseExpr( expr, exprCtx, user, resolveExprItem );
|
|
1368
1420
|
}
|
|
1369
1421
|
|
|
1370
1422
|
function resolveExprItem( expr, expected, user ) {
|
|
@@ -1374,8 +1426,10 @@ function resolve( model ) {
|
|
|
1374
1426
|
if (expr.path) {
|
|
1375
1427
|
// TODO: re-think this $expected: 'exists' thing
|
|
1376
1428
|
if (expr.$expected === 'exists') {
|
|
1377
|
-
|
|
1378
|
-
|
|
1429
|
+
if (expected !== 'annotation') { // `exists e[…]` allowed in annotation expressions
|
|
1430
|
+
error( 'expr-unexpected-exists', [ expr.location, user ], {},
|
|
1431
|
+
'An EXISTS predicate is not expected here' );
|
|
1432
|
+
}
|
|
1379
1433
|
// We complain about the EXISTS before, as EXISTS subquery is also not supported
|
|
1380
1434
|
// TODO: location of EXISTS, TODO: really do this in define.js
|
|
1381
1435
|
expr.$expected = 'approved-exists'; // only complain once
|
|
@@ -1414,8 +1468,8 @@ function resolve( model ) {
|
|
|
1414
1468
|
if (step.args)
|
|
1415
1469
|
resolveParams( step.args, art, entity, expected, user, step.location );
|
|
1416
1470
|
|
|
1417
|
-
// Publishing an association with filters is ok in
|
|
1418
|
-
const publishAssoc = art.kind === 'entity' && expected === 'column';
|
|
1471
|
+
// Publishing an association with filters is ok in columns and also ok in calculated elements.
|
|
1472
|
+
const publishAssoc = art.kind === 'entity' && (expected === 'column' || expected === 'calc');
|
|
1419
1473
|
|
|
1420
1474
|
if (entity || publishAssoc) {
|
|
1421
1475
|
if (step.where) {
|
package/lib/compiler/shared.js
CHANGED
|
@@ -286,7 +286,6 @@ function fns( model ) {
|
|
|
286
286
|
'ref-undefined-element': 'anno-undefined-element',
|
|
287
287
|
'ref-undefined-param': 'anno-undefined-param',
|
|
288
288
|
},
|
|
289
|
-
// check: checkAssocOn,
|
|
290
289
|
param: paramSemantics,
|
|
291
290
|
nestedColumn: () => ({ // in expand and inline - TODO
|
|
292
291
|
lexical: justDollarSelf,
|
|
@@ -297,6 +296,17 @@ function fns( model ) {
|
|
|
297
296
|
rewriteProjectionToSelf: true,
|
|
298
297
|
}),
|
|
299
298
|
},
|
|
299
|
+
// TODO: introduce some kind of inheritance
|
|
300
|
+
annoRewrite: { // annotation assignments
|
|
301
|
+
lexical: justDollarSelf, // TODO: forbid $projection
|
|
302
|
+
dollar: true,
|
|
303
|
+
dynamic: parentElements,
|
|
304
|
+
navigation: assocOnNavigation,
|
|
305
|
+
noDep: true,
|
|
306
|
+
notFound: null, // no error, just falsy links
|
|
307
|
+
param: paramSemantics,
|
|
308
|
+
// nestedColumn: // in expand and inline - TODO
|
|
309
|
+
},
|
|
300
310
|
};
|
|
301
311
|
|
|
302
312
|
Object.assign( model.$functions, {
|
|
@@ -314,25 +324,25 @@ function fns( model ) {
|
|
|
314
324
|
// Expression traversal function ----------------------------------------------
|
|
315
325
|
function traverseExpr( expr, exprCtx, user, callback ) {
|
|
316
326
|
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
317
|
-
return;
|
|
327
|
+
return null;
|
|
318
328
|
|
|
319
329
|
if (expr.path) {
|
|
320
|
-
callback( expr, exprCtx, user );
|
|
321
330
|
// TODO: move arguments and filter traversal to here
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
else if (expr.type || expr.query) {
|
|
325
|
-
callback( expr, exprCtx, user );
|
|
331
|
+
return callback( expr, exprCtx, user );
|
|
326
332
|
}
|
|
333
|
+
let first = (expr.type || expr.query) && callback( expr, exprCtx, user );
|
|
327
334
|
|
|
328
|
-
if (expr.args) {
|
|
335
|
+
if (expr.args && !first) {
|
|
329
336
|
const args = Array.isArray( expr.args ) ? expr.args : Object.values( expr.args );
|
|
330
337
|
// TODO: re-think $expected
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
first = callback.traverse // TODO: still in use?
|
|
339
|
+
? callback.traverse( args, exprCtx, user, callback )
|
|
340
|
+
: args.find( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
333
341
|
}
|
|
334
|
-
|
|
335
|
-
|
|
342
|
+
|
|
343
|
+
first ??= expr.suffix && // fn( … ) OVER …
|
|
344
|
+
expr.suffix.find( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
345
|
+
return first;
|
|
336
346
|
}
|
|
337
347
|
|
|
338
348
|
// Return absolute name for unchecked path `ref`. We first try searching for
|
|
@@ -395,7 +405,6 @@ function fns( model ) {
|
|
|
395
405
|
return setArtifactLink( ref, root );
|
|
396
406
|
|
|
397
407
|
// how many path items are for artifacts (rest: elements)
|
|
398
|
-
// console.log(expected, ref.path.map(a=>a.id),artItemsCount)
|
|
399
408
|
let art = getPathItem( ref, semantics, user );
|
|
400
409
|
if (!art)
|
|
401
410
|
return setArtifactLink( ref, art );
|
|
@@ -413,14 +422,19 @@ function fns( model ) {
|
|
|
413
422
|
const target = art._effectiveType?.target?._artifact;
|
|
414
423
|
if (target)
|
|
415
424
|
dependsOn( user._main, target, location, user );
|
|
425
|
+
if (target?.$calcDepElement)
|
|
426
|
+
dependsOn( user._main, target.$calcDepElement, location, user );
|
|
416
427
|
}
|
|
417
428
|
else if (art._main && art.kind !== 'select' || path[0]._navigation?.kind !== '$self') {
|
|
418
429
|
// no real dependency to bare $self (or actually: the underlying query)
|
|
419
430
|
dependsOn( user, art, location );
|
|
431
|
+
if (art.$calcDepElement)
|
|
432
|
+
dependsOn( user, art.$calcDepElement, location );
|
|
420
433
|
// Without on-demand resolve, we can simply signal 'undefined "x"'
|
|
421
|
-
|
|
422
|
-
|
|
434
|
+
// instead of 'illegal cycle' in the following case:
|
|
435
|
+
// element elem: type of elem.x;
|
|
423
436
|
}
|
|
437
|
+
|
|
424
438
|
// TODO: really write dependency with expand/inline? write test
|
|
425
439
|
// (removing it is not incompatible => not urgent)
|
|
426
440
|
}
|
|
@@ -498,7 +512,9 @@ function fns( model ) {
|
|
|
498
512
|
return undefined; // parse error
|
|
499
513
|
if (head._artifact !== undefined)
|
|
500
514
|
return head._artifact;
|
|
501
|
-
|
|
515
|
+
let ruser = user._user || user; // TODO: nicer name if we keep this
|
|
516
|
+
if (ruser.kind === '$annotation')
|
|
517
|
+
ruser = ruser._outer;
|
|
502
518
|
|
|
503
519
|
// Handle expand/inline, `type of`, :param, global (internally for CDL):
|
|
504
520
|
if (user._pathHead && !semantics.isMainRef) // in expand/inline
|
|
@@ -545,8 +561,9 @@ function fns( model ) {
|
|
|
545
561
|
else
|
|
546
562
|
valid.push( model.$magicVariables.elements, removeDollarNames( dynamicDict ) );
|
|
547
563
|
// TODO: streamline function arguments (probably: user, path, semantics )
|
|
548
|
-
const undef = semantics.notFound(
|
|
549
|
-
|
|
564
|
+
const undef = semantics.notFound?.( user._user || user, head, valid, dynamicDict,
|
|
565
|
+
!isMainRef && user._user && user._artifact,
|
|
566
|
+
path, semantics );
|
|
550
567
|
return setArtifactLink( head, undef || null );
|
|
551
568
|
}
|
|
552
569
|
|
|
@@ -913,9 +930,8 @@ function fns( model ) {
|
|
|
913
930
|
if (target && assocSpec && user) {
|
|
914
931
|
if (assocSpec !== 'calc')
|
|
915
932
|
dependsOn( user._main || user, target, location || user.location, user );
|
|
916
|
-
else
|
|
917
|
-
dependsOn( user, target, location || user.location );
|
|
918
|
-
// TODO: have some _delayedDeps for calc elements
|
|
933
|
+
else
|
|
934
|
+
dependsOn( user.$calcDepElement, target, location || user.location, user );
|
|
919
935
|
}
|
|
920
936
|
const effectiveTarget = Functions.effectiveType( target );
|
|
921
937
|
// if (effectiveTarget === 0 && location)
|
|
@@ -960,11 +976,11 @@ function fns( model ) {
|
|
|
960
976
|
}
|
|
961
977
|
|
|
962
978
|
function undefinedTargetElement( user, head, valid, _dict, pathItemArtifact ) {
|
|
963
|
-
//
|
|
964
|
-
const
|
|
979
|
+
// `art.target` may not set in case target entities `myEntity[unknown > 2]`
|
|
980
|
+
const art = pathItemArtifact?._effectiveType || user._parent;
|
|
965
981
|
// TODO: better with $refs in filter conditions
|
|
966
982
|
signalNotFound( 'ref-undefined-element', [ head.location, user ], valid,
|
|
967
|
-
{ '#': 'target', art: target, id: head.id } );
|
|
983
|
+
{ '#': 'target', art: art.target || art, id: head.id } );
|
|
968
984
|
}
|
|
969
985
|
|
|
970
986
|
function undefinedVariable( user, head, valid ) {
|
|
@@ -1033,14 +1049,16 @@ function fns( model ) {
|
|
|
1033
1049
|
return null;
|
|
1034
1050
|
}
|
|
1035
1051
|
|
|
1036
|
-
function undefinedNestedElement( user, head, valid, _dict, _art, path ) {
|
|
1052
|
+
function undefinedNestedElement( user, head, valid, _dict, _art, path, semantics ) {
|
|
1037
1053
|
const art = user._pathHead._origin;
|
|
1038
1054
|
if (!art)
|
|
1039
1055
|
return null; // no consequential error
|
|
1040
|
-
return undefinedItemElement( user, head, valid, null, art, path );
|
|
1056
|
+
return undefinedItemElement( user, head, valid, null, art, path, semantics );
|
|
1041
1057
|
}
|
|
1042
1058
|
|
|
1043
1059
|
function undefinedItemElement( user, item, valid, _dict, art, path, semantics ) {
|
|
1060
|
+
if (semantics.notFound === null)
|
|
1061
|
+
return;
|
|
1044
1062
|
const query = userQuery( art );
|
|
1045
1063
|
if (query?.name?.id > 1) {
|
|
1046
1064
|
const root = userQuery( user ) !== query && path[0]._navigation;
|
|
@@ -1084,7 +1102,6 @@ function fns( model ) {
|
|
|
1084
1102
|
{ '#': variant, art: a, id: item.id }, semantics );
|
|
1085
1103
|
}
|
|
1086
1104
|
}
|
|
1087
|
-
return null;
|
|
1088
1105
|
}
|
|
1089
1106
|
|
|
1090
1107
|
// Functions called via semantics.accept: -------------------------------------
|