@sap/cds-compiler 4.2.4 → 4.3.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 +26 -0
- package/bin/cdsc.js +8 -0
- package/bin/cdshi.js +3 -3
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +19 -0
- package/lib/base/location.js +16 -0
- package/lib/base/message-registry.js +47 -16
- package/lib/base/messages.js +49 -38
- package/lib/base/model.js +1 -1
- package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
- package/lib/checks/existsMustEndInAssoc.js +27 -0
- package/lib/checks/onConditions.js +47 -1
- package/lib/checks/validator.js +10 -1
- package/lib/compiler/assert-consistency.js +23 -15
- package/lib/compiler/base.js +31 -14
- package/lib/compiler/builtins.js +21 -20
- package/lib/compiler/checks.js +36 -49
- package/lib/compiler/define.js +71 -91
- package/lib/compiler/extend.js +27 -25
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +67 -87
- package/lib/compiler/kick-start.js +7 -5
- package/lib/compiler/populate.js +32 -30
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +29 -25
- package/lib/compiler/shared.js +57 -31
- package/lib/compiler/tweak-assocs.js +203 -22
- package/lib/compiler/utils.js +0 -18
- package/lib/gen/Dictionary.json +10 -4
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/languageParser.js +3 -3
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +63 -28
- package/lib/json/to-csn.js +23 -13
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/errorStrategy.js +5 -1
- package/lib/language/genericAntlrParser.js +67 -61
- package/lib/main.d.ts +26 -1
- package/lib/main.js +2 -1
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +28 -0
- package/lib/model/revealInternalProperties.js +3 -9
- package/lib/optionProcessor.js +17 -1
- package/lib/render/toCdl.js +1 -1
- package/lib/transform/db/associations.js +3 -4
- package/lib/transform/db/backlinks.js +293 -0
- package/lib/transform/db/expansion.js +9 -7
- package/lib/transform/db/flattening.js +3 -2
- package/lib/transform/db/rewriteCalculatedElements.js +1 -67
- package/lib/transform/db/transformExists.js +3 -58
- package/lib/transform/db/views.js +8 -14
- package/lib/transform/effective/.eslintrc.json +4 -0
- package/lib/transform/effective/associations.js +101 -0
- package/lib/transform/effective/main.js +88 -0
- package/lib/transform/effective/misc.js +61 -0
- package/lib/transform/effective/queries.js +42 -0
- package/lib/transform/effective/types.js +121 -0
- package/lib/transform/forRelationalDB.js +12 -235
- package/lib/transform/localized.js +22 -3
- package/lib/transform/parseExpr.js +7 -3
- package/lib/transform/transformUtils.js +5 -22
- package/lib/transform/translateAssocsToJoins.js +42 -38
- package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
- package/package.json +1 -2
- package/lib/language/language.g4 +0 -3260
|
@@ -6,8 +6,7 @@ const {
|
|
|
6
6
|
forEachGeneric,
|
|
7
7
|
forEachInOrder,
|
|
8
8
|
} = require('../base/model');
|
|
9
|
-
const { dictLocation } = require('../base/location');
|
|
10
|
-
const { weakLocation } = require('../base/messages');
|
|
9
|
+
const { dictLocation, weakLocation } = require('../base/location');
|
|
11
10
|
|
|
12
11
|
const {
|
|
13
12
|
setLink,
|
|
@@ -21,6 +20,7 @@ const {
|
|
|
21
20
|
setExpandStatus,
|
|
22
21
|
} = require('./utils');
|
|
23
22
|
const { CsnLocation } = require('./classes');
|
|
23
|
+
const { CompilerAssertion } = require('../base/error');
|
|
24
24
|
|
|
25
25
|
const $location = Symbol.for( 'cds.$location' );
|
|
26
26
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
@@ -131,20 +131,37 @@ function tweakAssocs( model ) {
|
|
|
131
131
|
// eslint-disable-next-line max-len
|
|
132
132
|
'Do not specify an $(KEYWORD) condition when redirecting the managed association $(ART)' );
|
|
133
133
|
}
|
|
134
|
+
checkIgnoredFilter( elem );
|
|
134
135
|
}
|
|
135
136
|
else if (elem.foreignKeys && !inferredForeignKeys( elem.foreignKeys )) {
|
|
136
137
|
const assoc = getOrigin( elem );
|
|
137
|
-
if (assoc
|
|
138
|
+
if (assoc?.on) {
|
|
138
139
|
error( 'rewrite-on-for-managed',
|
|
139
140
|
[ elem.foreignKeys[$location] || dictLocation( elem.foreignKeys ), elem ],
|
|
140
141
|
{ art: assocWithExplicitSpec( assoc ) },
|
|
141
142
|
'Do not specify foreign keys when redirecting the unmanaged association $(ART)' );
|
|
142
143
|
}
|
|
143
|
-
else if (assoc
|
|
144
|
+
else if (assoc?.foreignKeys) {
|
|
144
145
|
// same sequence is not checked
|
|
145
146
|
rewriteKeysMatch( elem, assoc );
|
|
146
147
|
rewriteKeysCovered( assoc, elem );
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
checkIgnoredFilter( elem );
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Publishing an association with filters is allowed, but the filter is ignored
|
|
156
|
+
* if the association is redirected. That indicates modeling mistakes, so we
|
|
157
|
+
* emit a warning.
|
|
158
|
+
*/
|
|
159
|
+
function checkIgnoredFilter( elem ) {
|
|
160
|
+
const lastStep = elem.value?.path?.[elem.value.path.length - 1];
|
|
161
|
+
if (lastStep?.where) {
|
|
162
|
+
const loc = lastStep.where.location;
|
|
163
|
+
const variant = elem.foreignKeys ? 'fKey' : 'onCond';
|
|
164
|
+
warning( 'query-ignoring-filter', [ loc, elem ], { '#': variant } );
|
|
148
165
|
}
|
|
149
166
|
}
|
|
150
167
|
|
|
@@ -208,7 +225,7 @@ function tweakAssocs( model ) {
|
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
function rewriteAssociation( element ) {
|
|
211
|
-
let elem = element.items || element; // TODO
|
|
228
|
+
let elem = element.items || element; // TODO v5: nested items
|
|
212
229
|
if (elem.elements)
|
|
213
230
|
forEachGeneric( elem, 'elements', rewriteAssociation );
|
|
214
231
|
if (!originTarget( elem ))
|
|
@@ -276,6 +293,8 @@ function tweakAssocs( model ) {
|
|
|
276
293
|
} );
|
|
277
294
|
if (elem.foreignKeys) // Possibly no fk was set
|
|
278
295
|
elem.foreignKeys[$inferred] = 'rewrite';
|
|
296
|
+
|
|
297
|
+
addConditionFromAssocPublishing( elem, assoc );
|
|
279
298
|
}
|
|
280
299
|
|
|
281
300
|
// TODO: there is no need to rewrite the on condition of non-leading queries,
|
|
@@ -301,8 +320,11 @@ function tweakAssocs( model ) {
|
|
|
301
320
|
const cond = copyExpr( assoc.on,
|
|
302
321
|
// replace location in ON except if from mixin element
|
|
303
322
|
nav.tableAlias && elem.name.location );
|
|
304
|
-
cond.$inferred = 'copy';
|
|
305
323
|
elem.on = cond;
|
|
324
|
+
addConditionFromAssocPublishing( elem, assoc );
|
|
325
|
+
// `cond` still points to the original condition; does not include possible assoc filter
|
|
326
|
+
elem.on.$inferred = 'copy';
|
|
327
|
+
|
|
306
328
|
// console.log(message( null, elem.location, elem, {art:assoc,target:assoc.target},
|
|
307
329
|
// 'Info','ON').toString(), nav)
|
|
308
330
|
const { navigation } = nav;
|
|
@@ -312,11 +334,9 @@ function tweakAssocs( model ) {
|
|
|
312
334
|
// Currently, having an unmanaged association inside a struct is not
|
|
313
335
|
// supported by this function:
|
|
314
336
|
if (navigation !== assoc && navigation._origin !== assoc) { // TODO: re-check
|
|
315
|
-
// For "assoc1.assoc2" and "
|
|
316
|
-
if (elem._redirected !== null)
|
|
317
|
-
error( 'rewrite-not-supported', [ elem.target.location, elem ]
|
|
318
|
-
'The ON-condition is not rewritten here - provide an explicit ON-condition' );
|
|
319
|
-
}
|
|
337
|
+
// For "assoc1.assoc2" and "struct.elem1.assoc2"
|
|
338
|
+
if (elem._redirected !== null) // null = already reported
|
|
339
|
+
error( 'rewrite-not-supported', [ elem.target.location, elem ] );
|
|
320
340
|
return;
|
|
321
341
|
}
|
|
322
342
|
if (!nav.tableAlias || nav.tableAlias.path) {
|
|
@@ -328,7 +348,167 @@ function tweakAssocs( model ) {
|
|
|
328
348
|
error( null, [ elem.value.location, elem ], {},
|
|
329
349
|
'Selecting unmanaged associations from a sub query is not supported' );
|
|
330
350
|
}
|
|
331
|
-
|
|
351
|
+
elem.on.$inferred = 'rewrite';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* If an unmanaged association is being published, we add a potential
|
|
356
|
+
* filter to the ON-condition and use its cardinality.
|
|
357
|
+
* If a managed association is published, we transform it into an unmanaged
|
|
358
|
+
* and do the same.
|
|
359
|
+
*
|
|
360
|
+
* The added condition (filter) is already rewritten relative to `elem`.
|
|
361
|
+
*/
|
|
362
|
+
function addConditionFromAssocPublishing( elem, assoc ) {
|
|
363
|
+
const publishAssoc = (elem._main?.query && elem.value?.path?.length > 0);
|
|
364
|
+
if (!publishAssoc)
|
|
365
|
+
return;
|
|
366
|
+
|
|
367
|
+
const { location } = elem.name;
|
|
368
|
+
const lastStep = elem.value.path[elem.value.path.length - 1];
|
|
369
|
+
|
|
370
|
+
if (lastStep?.cardinality) {
|
|
371
|
+
if (!elem.cardinality)
|
|
372
|
+
elem.cardinality = assoc.cardinality || { location };
|
|
373
|
+
for (const card of [ 'sourceMin', 'targetMin', 'targetMax' ]) {
|
|
374
|
+
if (lastStep.cardinality[card])
|
|
375
|
+
elem.cardinality[card] = copyExpr( lastStep.cardinality[card], location );
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (lastStep?.where) {
|
|
380
|
+
// If there are foreign keys, transform them into an ON-condition first.
|
|
381
|
+
if (assoc.foreignKeys) {
|
|
382
|
+
const cond = foreignKeysToOnCondition( elem );
|
|
383
|
+
if (cond) {
|
|
384
|
+
elem.on = cond;
|
|
385
|
+
elem.foreignKeys = undefined;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (elem.on) {
|
|
390
|
+
elem.on = {
|
|
391
|
+
op: { val: 'and', location },
|
|
392
|
+
args: [
|
|
393
|
+
// TODO: Get rid of $parens
|
|
394
|
+
{ ...elem.on, $parens: [ assoc.location ] },
|
|
395
|
+
filterToCondition( lastStep, elem ),
|
|
396
|
+
],
|
|
397
|
+
location,
|
|
398
|
+
$inferred: 'copy',
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Transform a filter on `assoc` into an ON-condition.
|
|
406
|
+
* Paths inside the filter are rewritten relative to `elem`.
|
|
407
|
+
*/
|
|
408
|
+
function filterToCondition( assoc, elem ) {
|
|
409
|
+
const cond = copyExpr( assoc.where );
|
|
410
|
+
// TODO: Get rid of $parens
|
|
411
|
+
cond.$parens = [ assoc.location ];
|
|
412
|
+
traverseExpr( cond, 'rewrite-filter', elem, (expr) => {
|
|
413
|
+
if (!expr.path || expr.path.length === 0)
|
|
414
|
+
return;
|
|
415
|
+
|
|
416
|
+
const root = expr.path[0]._navigation || expr.path[0]._artifact;
|
|
417
|
+
if (!root)
|
|
418
|
+
return; // only for compile error, e.g. missing definition
|
|
419
|
+
if (root.kind === '$self') {
|
|
420
|
+
// $projection -> $self for recompilability
|
|
421
|
+
expr.path[0].id = '$self';
|
|
422
|
+
}
|
|
423
|
+
else if (!root.builtin && root.kind !== 'builtin') {
|
|
424
|
+
expr.path.unshift({
|
|
425
|
+
id: elem.name.id,
|
|
426
|
+
location: elem.name.location,
|
|
427
|
+
});
|
|
428
|
+
setLink( expr.path[0], '_artifact', elem );
|
|
429
|
+
// _navigation link not necessary because this condition is not rewritten
|
|
430
|
+
// inside the same view (would otherwise be needed for mixins).
|
|
431
|
+
}
|
|
432
|
+
} );
|
|
433
|
+
return cond;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Caller must ensure ON-condition correctness via rewriteExpr()!
|
|
437
|
+
function foreignKeysToOnCondition( elem ) {
|
|
438
|
+
const nav = pathNavigation( elem.value );
|
|
439
|
+
if (!nav.tableAlias && model.options.testMode && !elem._pathHead)
|
|
440
|
+
throw new CompilerAssertion('rewriting keys to ON-condition: no tableAlias but not inline');
|
|
441
|
+
|
|
442
|
+
if (!nav.tableAlias || elem._parent?.kind === 'element' ||
|
|
443
|
+
(nav && nav.item !== elem.value.path[elem.value.path.length - 1])) {
|
|
444
|
+
// - no nav.tableAlias for mixins or inside inline; mixins can't have managed assocs, though.
|
|
445
|
+
// - _parent is element for expand
|
|
446
|
+
// - nav.item is different for multi-path steps e.g. `sub.assoc`, which is not supported, yet
|
|
447
|
+
// TODO: Support this
|
|
448
|
+
error( 'rewrite-not-supported', [ elem.value.location, elem ] );
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
let cond = [];
|
|
453
|
+
forEachInOrder( elem, 'foreignKeys', function keyToCond( fKey ) {
|
|
454
|
+
// Format: lhs = rhs
|
|
455
|
+
// assoc.id = assoc_id
|
|
456
|
+
// lhs and rhs look the same, but have different ids and _artifact links.
|
|
457
|
+
// rhs is rewritten further down to ensure that the foreign key is projected.
|
|
458
|
+
const lhs = {
|
|
459
|
+
path: [
|
|
460
|
+
{ id: elem.name.id, location: elem.name.location },
|
|
461
|
+
...copyExpr( fKey.targetElement.path ),
|
|
462
|
+
],
|
|
463
|
+
location: elem.name.location,
|
|
464
|
+
};
|
|
465
|
+
setLink( lhs.path[0], '_artifact', elem ); // different to rhs!
|
|
466
|
+
setLink( lhs, '_artifact', lhs.path[lhs.path.length - 1] );
|
|
467
|
+
|
|
468
|
+
const rhs = {
|
|
469
|
+
path: [
|
|
470
|
+
// use origin's name; elem could have alias
|
|
471
|
+
{ id: elem._origin.name.id, location: elem._origin.name.location },
|
|
472
|
+
...copyExpr( fKey.targetElement.path ),
|
|
473
|
+
],
|
|
474
|
+
location: elem.name.location,
|
|
475
|
+
};
|
|
476
|
+
setLink( rhs.path[0], '_artifact', elem._origin ); // different to lhs!
|
|
477
|
+
setLink( rhs, '_artifact', rhs.path[rhs.path.length - 1] );
|
|
478
|
+
|
|
479
|
+
// Can't use rewriteExpr as that would use `assoc[…]` itself as well.
|
|
480
|
+
const projectedFk = firstProjectionForPath( rhs.path, nav.tableAlias, elem );
|
|
481
|
+
rewritePath( rhs, projectedFk.item, elem, projectedFk.elem, elem.value.location );
|
|
482
|
+
|
|
483
|
+
const fkCond = {
|
|
484
|
+
op: { val: 'ixpr', location: elem.name.location },
|
|
485
|
+
args: [
|
|
486
|
+
lhs,
|
|
487
|
+
{ val: '=', literal: 'token', location: elem.name.location },
|
|
488
|
+
rhs,
|
|
489
|
+
],
|
|
490
|
+
location: elem.name.location,
|
|
491
|
+
};
|
|
492
|
+
cond.push(fkCond);
|
|
493
|
+
} );
|
|
494
|
+
|
|
495
|
+
if (cond.length === 0) {
|
|
496
|
+
const lastStep = elem.value.path[elem.value.path.length - 1];
|
|
497
|
+
error( 'expr-missing-foreign-key', [ lastStep.location, elem ], {
|
|
498
|
+
'#': 'publishingFilter',
|
|
499
|
+
id: lastStep.id,
|
|
500
|
+
} );
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
cond = (cond.length === 1) ? cond[0]
|
|
505
|
+
: {
|
|
506
|
+
op: { val: 'and', location: elem.name.location },
|
|
507
|
+
args: cond,
|
|
508
|
+
location: elem.name.location,
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
return cond;
|
|
332
512
|
}
|
|
333
513
|
|
|
334
514
|
function rewriteExpr( expr, assoc, tableAlias ) {
|
|
@@ -347,14 +527,16 @@ function tweakAssocs( model ) {
|
|
|
347
527
|
if (tableAlias) { // from ON cond of element in source ref/d by table alias
|
|
348
528
|
const source = tableAlias._origin;
|
|
349
529
|
const root = expr.path[0]._navigation || expr.path[0]._artifact;
|
|
350
|
-
// console.log( info(null, [ assoc.name.location, assoc ],
|
|
351
|
-
// { names: expr.path.map(i => i.id), art: root }, 'TA').toString());
|
|
352
530
|
if (!root || root._main !== source)
|
|
353
531
|
return; // not $self or source element
|
|
354
532
|
if (expr.scope === 'param' || root.kind === '$parameters')
|
|
355
533
|
return; // are not allowed anyway - there was an error before
|
|
356
|
-
const
|
|
357
|
-
|
|
534
|
+
const result = firstProjectionForPath( expr.path, tableAlias, assoc );
|
|
535
|
+
// For `assoc[…]`, ensure that we don't rewrite to another projection on `assoc`.
|
|
536
|
+
if (result.item && assoc._origin === result.item._artifact)
|
|
537
|
+
result.elem = assoc;
|
|
538
|
+
|
|
539
|
+
rewritePath( expr, result.item, assoc, result.elem, assoc.value.location );
|
|
358
540
|
}
|
|
359
541
|
else if (assoc._main.query) { // from ON cond of mixin element in query
|
|
360
542
|
const root = expr.path[0]._navigation || expr.path[0]._artifact;
|
|
@@ -370,8 +552,8 @@ function tweakAssocs( model ) {
|
|
|
370
552
|
}
|
|
371
553
|
const nav = pathNavigation( expr );
|
|
372
554
|
if (nav.navigation || nav.tableAlias) { // rewrite src elem, mixin, $self[.elem]
|
|
373
|
-
|
|
374
|
-
|
|
555
|
+
const elem = (assoc._origin === root) ? assoc : navProjection( nav.navigation, assoc );
|
|
556
|
+
rewritePath( expr, nav.item, assoc, elem,
|
|
375
557
|
nav.item ? nav.item.location : expr.path[0].location );
|
|
376
558
|
}
|
|
377
559
|
}
|
|
@@ -384,6 +566,8 @@ function tweakAssocs( model ) {
|
|
|
384
566
|
return; // just $self
|
|
385
567
|
// corresponding elem in including structure
|
|
386
568
|
const elem = (assoc._main.items || assoc._main).elements[item.id];
|
|
569
|
+
if (!elem)
|
|
570
|
+
return; // See #11755
|
|
387
571
|
if (!(Array.isArray( elem ) || // no msg for redefs
|
|
388
572
|
elem === item._artifact || // redirection for explicit def
|
|
389
573
|
elem._origin === item._artifact)) {
|
|
@@ -410,7 +594,7 @@ function tweakAssocs( model ) {
|
|
|
410
594
|
name: assoc.name.id, art: item._artifact, elemref: { ref: path },
|
|
411
595
|
}, {
|
|
412
596
|
std: 'Projected association $(NAME) uses non-projected element $(ELEMREF)',
|
|
413
|
-
element: 'Projected association $(NAME) uses non-projected element $(
|
|
597
|
+
element: 'Projected association $(NAME) uses non-projected element $(ELEMREF) of $(ART)',
|
|
414
598
|
} );
|
|
415
599
|
}
|
|
416
600
|
delete root._navigation;
|
|
@@ -461,9 +645,6 @@ function tweakAssocs( model ) {
|
|
|
461
645
|
}
|
|
462
646
|
|
|
463
647
|
function rewriteItem( elem, item, name, assoc, forKeys ) {
|
|
464
|
-
// TODO: for rewriting ON conditions of explicitly provided model targets,
|
|
465
|
-
// we need to only rewrite the current element, not all sibling elements
|
|
466
|
-
// TODO: this will be different in v4
|
|
467
648
|
if (!elem._redirected)
|
|
468
649
|
return true;
|
|
469
650
|
for (const alias of elem._redirected) {
|
package/lib/compiler/utils.js
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
|
|
14
|
-
const { kindProperties } = require('./base');
|
|
15
14
|
const { XsnName, XsnArtifact, CsnLocation } = require('./classes');
|
|
16
15
|
|
|
17
16
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
@@ -149,23 +148,6 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
149
148
|
parent = parent._outer;
|
|
150
149
|
setLink( elem, '_parent', parent );
|
|
151
150
|
setLink( elem, '_main', parent._main || parent );
|
|
152
|
-
const parentName = parent.name || parent._outer?.name;
|
|
153
|
-
if (parentName) // may not be available in e.g. cast() - TODO recheck (#9503)
|
|
154
|
-
elem.name.absolute = parentName.absolute;
|
|
155
|
-
if (!parentName || name == null)
|
|
156
|
-
return;
|
|
157
|
-
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
158
|
-
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
159
|
-
if (normalized === kind)
|
|
160
|
-
elem.name[kind] = (parentName[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parentName[kind] }.${ name }` : name;
|
|
161
|
-
|
|
162
|
-
else if (parentName[kind] != null)
|
|
163
|
-
elem.name[kind] = parentName[kind];
|
|
164
|
-
|
|
165
|
-
else
|
|
166
|
-
delete elem.name[kind];
|
|
167
|
-
} );
|
|
168
|
-
// try { throw new CompilerAssertion('Foo') } catch (e) { elem.name.stack = e; };
|
|
169
151
|
}
|
|
170
152
|
|
|
171
153
|
/**
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -485,7 +485,8 @@
|
|
|
485
485
|
"Common.ExternalID": {
|
|
486
486
|
"Type": "Edm.String",
|
|
487
487
|
"AppliesTo": [
|
|
488
|
-
"Property"
|
|
488
|
+
"Property",
|
|
489
|
+
"Parameter"
|
|
489
490
|
],
|
|
490
491
|
"$experimental": true
|
|
491
492
|
},
|
|
@@ -2666,6 +2667,7 @@
|
|
|
2666
2667
|
"$kind": "ComplexType",
|
|
2667
2668
|
"BaseType": "Capabilities.ReadRestrictionsBase",
|
|
2668
2669
|
"Properties": {
|
|
2670
|
+
"TypecastSegmentSupported": "Edm.Boolean",
|
|
2669
2671
|
"ReadByKeyRestrictions": "Capabilities.ReadByKeyRestrictionsType",
|
|
2670
2672
|
"Readable": "Edm.Boolean",
|
|
2671
2673
|
"Permissions": "Collection(Capabilities.PermissionType)",
|
|
@@ -3513,7 +3515,9 @@
|
|
|
3513
3515
|
"Padding": "Edm.Boolean",
|
|
3514
3516
|
"HeaderFooter": "Edm.Boolean",
|
|
3515
3517
|
"ResultSizeDefault": "Edm.Int32",
|
|
3516
|
-
"ResultSizeMaximum": "Edm.Int32"
|
|
3518
|
+
"ResultSizeMaximum": "Edm.Int32",
|
|
3519
|
+
"IANATimezoneFormat": "Edm.Boolean",
|
|
3520
|
+
"Treeview": "Edm.Boolean"
|
|
3517
3521
|
}
|
|
3518
3522
|
},
|
|
3519
3523
|
"PersonalData.EntitySemanticsType": {
|
|
@@ -3570,8 +3574,8 @@
|
|
|
3570
3574
|
"Value": "UserID",
|
|
3571
3575
|
"Type": "String"
|
|
3572
3576
|
},
|
|
3573
|
-
"
|
|
3574
|
-
"Value": "
|
|
3577
|
+
"EndOfBusinessDate": {
|
|
3578
|
+
"Value": "EndOfBusinessDate",
|
|
3575
3579
|
"Type": "String"
|
|
3576
3580
|
}
|
|
3577
3581
|
},
|
|
@@ -3640,6 +3644,7 @@
|
|
|
3640
3644
|
"$kind": "ComplexType",
|
|
3641
3645
|
"Properties": {
|
|
3642
3646
|
"Url": "Edm.String",
|
|
3647
|
+
"Stream": "Edm.Stream",
|
|
3643
3648
|
"ContentType": "Edm.String",
|
|
3644
3649
|
"ByteSize": "Edm.Int64",
|
|
3645
3650
|
"ChangedAt": "Edm.DateTimeOffset",
|
|
@@ -3652,6 +3657,7 @@
|
|
|
3652
3657
|
"$kind": "ComplexType",
|
|
3653
3658
|
"Properties": {
|
|
3654
3659
|
"Url": "Edm.String",
|
|
3660
|
+
"Stream": "Edm.Stream",
|
|
3655
3661
|
"Width": "Edm.String",
|
|
3656
3662
|
"Height": "Edm.String"
|
|
3657
3663
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
462a4274cec75ca772064210a7421aec
|
|
@@ -2674,7 +2674,7 @@ class languageParser extends genericAntlrParser_js_1.default {
|
|
|
2674
2674
|
this.state = 290;
|
|
2675
2675
|
this.match(languageParser.T__0);
|
|
2676
2676
|
this._ctx.stop = this._input.LT(-1);
|
|
2677
|
-
localctx.source.namespace =
|
|
2677
|
+
localctx.source.namespace = localctx.decl;
|
|
2678
2678
|
}
|
|
2679
2679
|
catch (re) {
|
|
2680
2680
|
if (re instanceof antlr4_1.default.error.RecognitionException) {
|
|
@@ -15997,7 +15997,7 @@ class languageParser extends genericAntlrParser_js_1.default {
|
|
|
15997
15997
|
this.state = 3698;
|
|
15998
15998
|
this.match(languageParser.Identifier);
|
|
15999
15999
|
this._ctx.stop = this._input.LT(-1);
|
|
16000
|
-
localctx.id = this.identAst(
|
|
16000
|
+
localctx.id = this.identAst(null, localctx.category);
|
|
16001
16001
|
}
|
|
16002
16002
|
catch (re) {
|
|
16003
16003
|
if (re instanceof antlr4_1.default.error.RecognitionException) {
|
|
@@ -16030,7 +16030,7 @@ class languageParser extends genericAntlrParser_js_1.default {
|
|
|
16030
16030
|
this.consume();
|
|
16031
16031
|
}
|
|
16032
16032
|
this._ctx.stop = this._input.LT(-1);
|
|
16033
|
-
localctx.id = this.identAst(
|
|
16033
|
+
localctx.id = this.identAst(null, localctx.category);
|
|
16034
16034
|
}
|
|
16035
16035
|
catch (re) {
|
|
16036
16036
|
if (re instanceof antlr4_1.default.error.RecognitionException) {
|
|
@@ -41,6 +41,7 @@ function inspectPropagation( xsn, options, artifactName ) {
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
result.push(color.underline('analyzing propagation for artifact:'));
|
|
44
|
+
// TODO: back to artifactXsn.name.id (not ok now, and not before this change!)
|
|
44
45
|
result.push(` name: ${ artifactXsn.name.id }`);
|
|
45
46
|
result.push(` kind: ${ artifactXsn.kind }`);
|
|
46
47
|
|
|
@@ -131,7 +132,7 @@ function _inspectElements( artifactXsn ) {
|
|
|
131
132
|
const elementXsn = artifactXsn.elements[element];
|
|
132
133
|
const loc = locationString(_origin(elementXsn).name.location);
|
|
133
134
|
let origin;
|
|
134
|
-
const originName = elementXsn._origin?.name?.
|
|
135
|
+
const originName = (elementXsn._origin?._main || elementXsn._origin)?.name?.id || '';
|
|
135
136
|
|
|
136
137
|
if (elementXsn.$inferred) {
|
|
137
138
|
// Use nice(r) output for known $inferred
|
package/lib/json/from-csn.js
CHANGED
|
@@ -267,6 +267,7 @@ const schema = compileSchema( {
|
|
|
267
267
|
dictionaryOf: definition,
|
|
268
268
|
defaultKind: 'element',
|
|
269
269
|
validKinds: [ 'element' ],
|
|
270
|
+
requires: requiresOnWithBothTargetProps,
|
|
270
271
|
inKind: [
|
|
271
272
|
'element',
|
|
272
273
|
'type',
|
|
@@ -365,15 +366,15 @@ const schema = compileSchema( {
|
|
|
365
366
|
},
|
|
366
367
|
targetAspect: {
|
|
367
368
|
type: artifactRef,
|
|
368
|
-
xorException:
|
|
369
|
+
xorException: inferredTargetEntityForAspect, // usually allows `target`
|
|
369
370
|
msgVariant: 'or-object', // for 'syntax-expecting-string',
|
|
370
371
|
requires: 'elements',
|
|
371
372
|
optional: [ 'elements' ], // 'elements' for ad-hoc aspect compositions
|
|
372
|
-
inKind: [ 'element'
|
|
373
|
+
inKind: [ 'element' ],
|
|
373
374
|
},
|
|
374
375
|
target: {
|
|
375
376
|
type: artifactRef,
|
|
376
|
-
xorException:
|
|
377
|
+
xorException: inferredTargetEntityForAspect, // usually allows `targetAspect`
|
|
377
378
|
msgVariant: 'or-object', // for 'syntax-expecting-string',
|
|
378
379
|
requires: 'elements',
|
|
379
380
|
optional: [ 'elements' ], // 'elements' for ad-hoc COMPOSITION OF (gensrc style CSN)
|
|
@@ -980,9 +981,7 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
980
981
|
if (!isObject( def, spec )) {
|
|
981
982
|
return {
|
|
982
983
|
kind: (inExtensions ? 'annotate' : spec.defaultKind),
|
|
983
|
-
name: {
|
|
984
|
-
id: '', path: [], absolute: name || '', location: location(),
|
|
985
|
-
},
|
|
984
|
+
name: { id: '', location: location() },
|
|
986
985
|
location: location(),
|
|
987
986
|
};
|
|
988
987
|
}
|
|
@@ -1442,20 +1441,19 @@ function annoValue( val, spec ) {
|
|
|
1442
1441
|
// - there is exactly one property ('=')
|
|
1443
1442
|
// - there is at least one other expression property (e.g. "xpr")
|
|
1444
1443
|
// TODO: Have xprInAnnoProperties centrally for other backends to use as well (toCdl)
|
|
1445
|
-
const valKeys = Object.keys(val);
|
|
1446
|
-
if (valKeys.length
|
|
1447
|
-
xprInAnnoProperties.some(prop => val[prop] !== undefined)) {
|
|
1448
|
-
const s = schema['@'].schema['-expr'];
|
|
1449
|
-
const r = { location: location() };
|
|
1450
|
-
Object.assign(r, object(val, s));
|
|
1451
|
-
return r;
|
|
1452
|
-
}
|
|
1453
|
-
else if (valKeys.length === 1) {
|
|
1444
|
+
const valKeys = Object.keys( val );
|
|
1445
|
+
if (valKeys.length === 1) {
|
|
1454
1446
|
++virtualLine;
|
|
1455
|
-
const r = refSplit( val['='], '=' );
|
|
1447
|
+
const r = refSplit( val['='], '=' ); // i.e. no extra `variant` stuff
|
|
1456
1448
|
++virtualLine;
|
|
1457
1449
|
return r;
|
|
1458
1450
|
}
|
|
1451
|
+
else if (xprInAnnoProperties.some( prop => val[prop] !== undefined) ) {
|
|
1452
|
+
const s = schema['@'].schema['-expr'];
|
|
1453
|
+
const r = { location: location() };
|
|
1454
|
+
Object.assign( r, object( val, s ) );
|
|
1455
|
+
return r;
|
|
1456
|
+
}
|
|
1459
1457
|
// fallthrough -> unchecked structure
|
|
1460
1458
|
}
|
|
1461
1459
|
if (typeof val['#'] === 'string') {
|
|
@@ -1491,15 +1489,12 @@ function annoValue( val, spec ) {
|
|
|
1491
1489
|
}
|
|
1492
1490
|
|
|
1493
1491
|
function annotation( val, spec, xsn, csn, name ) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
n.absolute = absolute;
|
|
1501
|
-
if (variantIndex < absolute.length)
|
|
1502
|
-
n.variant = refSplit( name.substring( variantIndex ), null );
|
|
1492
|
+
// not used for the value
|
|
1493
|
+
const id = (xsn ? name.substring(1) : name);
|
|
1494
|
+
if (!id) { // `"@": …` is already syntax-unknown-property
|
|
1495
|
+
message( 'syntax-invalid-name', location(true), { '#': '{}' } );
|
|
1496
|
+
}
|
|
1497
|
+
const n = { id, location: location() };
|
|
1503
1498
|
const r = annoValue( val, spec );
|
|
1504
1499
|
r.name = n;
|
|
1505
1500
|
return r;
|
|
@@ -1785,7 +1780,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1785
1780
|
return { prop, type: ignore };
|
|
1786
1781
|
return { prop, type: extra };
|
|
1787
1782
|
}
|
|
1788
|
-
// TODO
|
|
1783
|
+
// TODO v5: No warning with --sloppy
|
|
1789
1784
|
warning( 'syntax-unknown-property', location(true), { prop },
|
|
1790
1785
|
'Unknown CSN property $(PROP)' );
|
|
1791
1786
|
return { type: ignore };
|
|
@@ -1820,7 +1815,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1820
1815
|
kind,
|
|
1821
1816
|
} );
|
|
1822
1817
|
}
|
|
1823
|
-
else if (checkAndSetXorGroup( s.xorGroups, s.xorException, prop, xor )) {
|
|
1818
|
+
else if (checkAndSetXorGroup( s.xorGroups, s.xorException, prop, xor, csn )) {
|
|
1824
1819
|
// TODO: If all targets of onlyWith are xor-excluded/ignore, also exclude/ignore this one.
|
|
1825
1820
|
onlyWith( s, s.onlyWith, csn, prop, xor, expected );
|
|
1826
1821
|
return s;
|
|
@@ -1849,6 +1844,18 @@ function calculateKind( def, spec ) {
|
|
|
1849
1844
|
: kind;
|
|
1850
1845
|
}
|
|
1851
1846
|
|
|
1847
|
+
function requiresOnWithBothTargetProps( csn ) {
|
|
1848
|
+
if (!csn.on && csn.target && csn.targetAspect &&
|
|
1849
|
+
inferredTargetEntityForAspect( 'target', 'targetAspect', csn )) {
|
|
1850
|
+
error( 'syntax-missing-property', location(true), {
|
|
1851
|
+
'#': 'bothTargets',
|
|
1852
|
+
siblingprop: 'targetAspect',
|
|
1853
|
+
otherprop: 'target',
|
|
1854
|
+
prop: 'on',
|
|
1855
|
+
} );
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1852
1859
|
function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
1853
1860
|
if (!need)
|
|
1854
1861
|
return spec;
|
|
@@ -1861,6 +1868,10 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1861
1868
|
if (need in csn) // TODO: enumerable ?
|
|
1862
1869
|
return spec;
|
|
1863
1870
|
}
|
|
1871
|
+
else if (typeof need === 'function') {
|
|
1872
|
+
need( csn, prop );
|
|
1873
|
+
return spec;
|
|
1874
|
+
}
|
|
1864
1875
|
else if (need.some( n => n in csn )) {
|
|
1865
1876
|
return spec;
|
|
1866
1877
|
}
|
|
@@ -1895,7 +1906,7 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1895
1906
|
* @param {object} xor
|
|
1896
1907
|
* @return {boolean}
|
|
1897
1908
|
*/
|
|
1898
|
-
function checkAndSetXorGroup( groups, exception, prop, xor ) {
|
|
1909
|
+
function checkAndSetXorGroup( groups, exception, prop, xor, csn ) {
|
|
1899
1910
|
if (!groups || groups.length === 0)
|
|
1900
1911
|
return true;
|
|
1901
1912
|
let silent = false;
|
|
@@ -1907,6 +1918,13 @@ function checkAndSetXorGroup( groups, exception, prop, xor ) {
|
|
|
1907
1918
|
}
|
|
1908
1919
|
if (siblingprop === exception)
|
|
1909
1920
|
return true;
|
|
1921
|
+
if (typeof exception === 'function') {
|
|
1922
|
+
const r = exception( prop, siblingprop, csn, silent );
|
|
1923
|
+
if (r === false) // error reported
|
|
1924
|
+
silent = true;
|
|
1925
|
+
if (r != null)
|
|
1926
|
+
return r;
|
|
1927
|
+
}
|
|
1910
1928
|
if (!silent) {
|
|
1911
1929
|
error( 'syntax-unexpected-property', location(true), { '#': 'sibling', prop, siblingprop } );
|
|
1912
1930
|
silent = true;
|
|
@@ -1915,6 +1933,23 @@ function checkAndSetXorGroup( groups, exception, prop, xor ) {
|
|
|
1915
1933
|
});
|
|
1916
1934
|
}
|
|
1917
1935
|
|
|
1936
|
+
function inferredTargetEntityForAspect( prop, siblingprop, csn, silent = true ) {
|
|
1937
|
+
if (siblingprop !== 'target' && siblingprop !== 'targetAspect')
|
|
1938
|
+
return null;
|
|
1939
|
+
const { target } = csn;
|
|
1940
|
+
if (typeof target !== 'object' || !target?.elements)
|
|
1941
|
+
return true;
|
|
1942
|
+
if (!silent) {
|
|
1943
|
+
error( 'syntax-unexpected-property', location(true), {
|
|
1944
|
+
'#': prop,
|
|
1945
|
+
prop,
|
|
1946
|
+
siblingprop,
|
|
1947
|
+
subprop: 'elements',
|
|
1948
|
+
} );
|
|
1949
|
+
}
|
|
1950
|
+
return false;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1918
1953
|
function implicitName( ref ) {
|
|
1919
1954
|
// careful, the input CSN might be wrong!
|
|
1920
1955
|
const item = ref && ref[ref.length - 1];
|