@sap/cds-compiler 6.0.10 → 6.0.12

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 CHANGED
@@ -8,6 +8,19 @@ Note: `beta` fixes, changes and features are usually not listed in this ChangeLo
8
8
  but in [doc/CHANGELOG_BETA.md](doc/CHANGELOG_BETA.md).
9
9
  The compiler behavior concerning `beta` features can change at any time without notice.
10
10
 
11
+ ## Version 6.0.12 - 2025-06-06
12
+
13
+ ### Changed
14
+
15
+ - Update OData vocabularies: 'Common', 'Hierarchy'
16
+
17
+ ### Fixed
18
+
19
+ - compiler: Fix artifact refs in annotated annotation expressions,
20
+ i.e. the `Type` inside `annotate … with @SomeAnno: (cast( … as Type ))`.
21
+ - to.sql: Checks around managed associations for mocked entities have been relaxed.
22
+ - to.edm(x): Resolved a crash caused by references in annotation expressions that were not properly updated.
23
+
11
24
  ## Version 6.0.10 - 2025-05-28
12
25
 
13
26
  ### Fixed
package/bin/cdsc.js CHANGED
@@ -349,7 +349,8 @@ async function executeCommandLine( command, options, args ) {
349
349
  return;
350
350
  }
351
351
 
352
- options.messages = [];
352
+ if (!options.directMessages)
353
+ options.messages = [];
353
354
  args.files ??= [];
354
355
 
355
356
  // Load a file from stdin if no explicit file is given and stdin is not a TTY.
@@ -106,7 +106,6 @@ const centralMessages = {
106
106
  'def-unexpected-nested-proj': { severity: 'Error' },
107
107
  'def-unexpected-paramview-assoc': { severity: 'Error' },
108
108
  'def-unexpected-calcview-assoc': { severity: 'Error' },
109
- 'chained-array-of': { severity: 'Error' },
110
109
  'def-missing-type': { severity: 'Error', configurableFor: [ 'compile' ] },
111
110
  'def-missing-argument': { severity: 'Error' },
112
111
  'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename', 'for.effective' ] },
@@ -390,8 +389,6 @@ const centralMessageTexts = {
390
389
  param: 'Assign a value for $(ANNO); the value inherited from $(ART) can\'t be rewritten due to parameter reference $(ELEMREF)',
391
390
  },
392
391
 
393
- 'chained-array-of': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
394
-
395
392
  'check-proper-type-of': {
396
393
  std: 'Referred element $(NAME) of $(ART) does not contain proper type information',
397
394
  derived: 'Referred type of $(ART) does not contain proper type information',
@@ -908,6 +905,7 @@ const centralMessageTexts = {
908
905
  nested: 'Unexpected $(PROP) inside $(PROP)',
909
906
  assoc: 'Unexpected association inside $(PROP)',
910
907
  comp: 'Unexpected composition inside $(PROP)',
908
+ 'chained-service': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
911
909
  },
912
910
 
913
911
  'type-unexpected-default': {
@@ -1494,12 +1494,17 @@ function homeName( art, absoluteOnly ) {
1494
1494
  return art;
1495
1495
  if (art._user) // when providing a path item with filter as “user”
1496
1496
  return homeName( art._user, absoluteOnly );
1497
- if (art._outer) { // in items property, or annotation with path
1497
+ if (art._outer) { // in items property, or annotation with path
1498
+ if (art._outer?.kind === '$annotation')
1499
+ art = art._outer;
1498
1500
  const outer = homeName( art._outer, absoluteOnly );
1499
1501
  if (art.kind === '$annotation') // eslint-disable-next-line sonarjs/no-nested-template-literals
1500
1502
  return `${ outer }/${ quoted( `@${ art.name.id }` ) }`;
1501
1503
  return outer;
1502
1504
  }
1505
+ else if (art.kind === '$annotation') { // in items property, or annotation with path
1506
+ return quoted( `@${ art.name.id }` );
1507
+ }
1503
1508
  else if (art.kind === 'source' || !art.name) { // error reported in parser or on source level
1504
1509
  return null;
1505
1510
  }
@@ -126,8 +126,13 @@ function checkManagedAssoc( member ) {
126
126
  if (targetMax === 1 || member.on)
127
127
  return;
128
128
 
129
+ // Special case for "--with-mocks" of the cds cli: For testing databases such as H2 and SQLite, we allow
130
+ // associations with neither on-condition nor foreign keys if the CSN is mocked.
131
+ // See #13916 for details.
132
+ const allowForMocked = this.csn._mocked && (this.options.sqlDialect === 'h2' || this.options.sqlDialect === 'sqlite');
133
+
129
134
  const isPersisted = !hasPersistenceSkipAnnotation(this.artifact) && !this.artifact['@cds.persistence.exists'];
130
- if (isPersisted && !member.keys && (targetMax === '*' || Number(targetMax) > 1) && this.options.transformation === 'sql') {
135
+ if (isPersisted && !allowForMocked && !member.keys && (targetMax === '*' || Number(targetMax) > 1) && this.options.transformation === 'sql') {
131
136
  // Since cds-compiler v6, managed to-many no longer get 'keys'.
132
137
  // As this would lead to DROP COLUMNs, emit an error instead.
133
138
  this.error('type-missing-on-condition', member.cardinality?.$path || member.$path, {
@@ -497,6 +497,7 @@ function assertConsistency( model, stage ) {
497
497
  // expressions as annotation values
498
498
  '$tokenTexts', 'op', 'args', 'func', '_artifact', 'type', '$typeArgs',
499
499
  'scale', 'srid', 'length', 'precision', 'scope', '$parens',
500
+ '_block', '_outer', // for annotation assignments
500
501
  ],
501
502
  // TODO: restrict path to #simplePath
502
503
  },
@@ -972,7 +972,7 @@ function check( model ) {
972
972
 
973
973
  function checkAnnotationAssignment1( art, anno ) {
974
974
  const name = anno.name?.id;
975
- if (art.$contains?.$annotation && anno.kind === '$annotation') {
975
+ if (art.$contains?.$annotation && anno.kind === '$annotation' && anno._outer) {
976
976
  if (checkAnnotationAcceptsExpressions( anno, art ))
977
977
  checkAnnotationExpressions( anno, art );
978
978
  }
@@ -1125,7 +1125,7 @@ function check( model ) {
1125
1125
  return;
1126
1126
  }
1127
1127
 
1128
- // Struct expected (can only happen within arrays)?
1128
+ // Struct expected (can only happen within arrays, or CSN input)?
1129
1129
  if (elementDecl._effectiveType.elements) {
1130
1130
  if (value.literal !== 'struct') {
1131
1131
  warning( null, loc, { anno }, 'A struct value is required here for annotation $(ANNO)' );
@@ -298,6 +298,7 @@ function define( model ) {
298
298
  return;
299
299
  }
300
300
  setLink( art, '_block', block );
301
+ initExprAnnoBlock( art, block );
301
302
  // dictAdd might set $duplicates
302
303
  dictAdd( model.definitions, absolute, art );
303
304
  }
@@ -393,6 +394,7 @@ function define( model ) {
393
394
 
394
395
  function addExtension( ext, block ) {
395
396
  setLink( ext, '_block', block );
397
+ initExprAnnoBlock( ext, block );
396
398
  const absolute = ext.name && resolveUncheckedPath( ext.name, '_extensions', ext );
397
399
  if (!absolute) // broken path
398
400
  return;
@@ -429,6 +431,7 @@ function define( model ) {
429
431
  if (prop === 'params' && name === '') // RETURNS
430
432
  sub.name = { id: '', location: sub.location };
431
433
  setLink( sub, '_block', parent._block );
434
+ initExprAnnoBlock( sub, parent._block );
432
435
  setLink( sub, '_parent', parent );
433
436
  setLink( sub, '_main', parent._main || parent );
434
437
  initExtension( sub );
@@ -446,6 +449,26 @@ function define( model ) {
446
449
  }
447
450
  }
448
451
 
452
+ function initExprAnnoBlock( art, block ) {
453
+ // remark: `art` could also be the extension
454
+ for (const prop in art) {
455
+ if (prop.charAt(0) !== '@')
456
+ continue;
457
+ const anno = art[prop];
458
+ // _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
459
+ if (anno.literal === 'array') {
460
+ anno.kind = '$annotation';
461
+ for (const item of anno.val)
462
+ setLink( item, '_block', block );
463
+ }
464
+ else if (anno.$tokenTexts) {
465
+ // remark: it wouldn't hurt to set it always...
466
+ anno.kind = '$annotation';
467
+ setLink( anno, '_block', block );
468
+ }
469
+ }
470
+ }
471
+
449
472
  function addVocabulary( vocab, block, prefix ) {
450
473
  setLink( vocab, '_block', block );
451
474
  const { name } = vocab;
@@ -1009,6 +1032,7 @@ function define( model ) {
1009
1032
  }
1010
1033
 
1011
1034
  initItemsLinks( col, parent._block );
1035
+ initExprAnnoBlock( col, parent._block );
1012
1036
  }
1013
1037
 
1014
1038
  if (hasItems && !wildcard && parent.excludingDict && !options.$recompile) {
@@ -1069,6 +1093,7 @@ function define( model ) {
1069
1093
  const main = parent._main || parent;
1070
1094
  const isQueryExtension = construct.kind === 'extend' && main.query;
1071
1095
  let obj = initItemsLinks( construct, block );
1096
+ initExprAnnoBlock( construct, block );
1072
1097
  if (obj.target && targetIsTargetAspect( obj )) {
1073
1098
  obj.targetAspect = obj.target;
1074
1099
  delete obj.target;
@@ -490,6 +490,7 @@ function extend( model ) {
490
490
  const location = firstEllipsis.location || anno.name.location;
491
491
  message( 'anno-unexpected-ellipsis', [ location, art ], { code: '...' } );
492
492
  previousAnno = {
493
+ kind: '$annotation',
493
494
  val: [],
494
495
  literal: 'array',
495
496
  name: { id: annoName.slice( 1 ) },
@@ -500,6 +501,7 @@ function extend( model ) {
500
501
  // TODO: If we introduce sub-messages, point to the non-array base value.
501
502
  error( 'anno-mismatched-ellipsis', [ anno.name.location, art ], { code: '...' } );
502
503
  previousAnno = {
504
+ kind: '$annotation',
503
505
  val: [],
504
506
  literal: 'array',
505
507
  name: previousAnno.name,
@@ -533,6 +535,7 @@ function extend( model ) {
533
535
  }
534
536
  // console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
535
537
  return {
538
+ kind: '$annotation',
536
539
  val: result,
537
540
  literal: 'array',
538
541
  name: previousAnno.name,
@@ -1466,19 +1466,35 @@ function resolve( model ) {
1466
1466
  }
1467
1467
  }
1468
1468
 
1469
- function resolveAnnoExpr( expr, art, anno = expr ) {
1469
+ function resolveAnnoExpr( expr, art, anno = expr, topItem = false ) {
1470
1470
  if (expr.$tokenTexts) {
1471
- if (!anno.kind)
1471
+ if (anno === expr) {
1472
1472
  initAnnotationForExpression( anno, art );
1473
+ }
1474
+ else if (topItem) { // item of top-level array annotation
1475
+ setLink( expr, '_outer', anno );
1476
+ anno = expr;
1477
+ }
1478
+
1473
1479
  const type = anno === expr && anno.name;
1474
1480
  // TODO: it might be best to set an _artifact link also for property values
1475
1481
  // like in `@Anno: [ { foo: #EnumSymbol }]
1476
1482
  resolveExpr( expr, 'annotation', anno, type );
1477
1483
  }
1478
1484
  else if (expr.literal === 'array') {
1479
- expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
1485
+ const withExpr = anno === expr &&
1486
+ // the following is needed for checkAnnotationAcceptsExpressions(),
1487
+ // otherwise @cds.persistence.skip: [hdi, hdbcds, sql.postgres] fails
1488
+ expr.val.some( item => item.$tokenTexts || item.literal === 'struct');
1489
+ if (withExpr)
1490
+ initAnnotationForExpression( anno, art );
1491
+ expr.val.forEach( val => resolveAnnoExpr( val, art, anno, withExpr ) );
1480
1492
  }
1481
1493
  else if (expr.literal === 'struct') {
1494
+ if (topItem) { // item of top-level array annotation
1495
+ setLink( expr, '_outer', anno );
1496
+ anno = expr;
1497
+ }
1482
1498
  Object.values( expr.struct ).forEach( val => resolveAnnoExpr( val, art, anno ) );
1483
1499
  }
1484
1500
  }
@@ -1490,10 +1506,9 @@ function resolve( model ) {
1490
1506
  * @param {XSN.Artifact} art
1491
1507
  */
1492
1508
  function initAnnotationForExpression( anno, art ) {
1493
- anno.kind = '$annotation';
1494
1509
  setLink( anno, '_outer', art );
1495
1510
  art.$contains ??= {};
1496
- art.$contains.$annotation = { // set in resolveExprNode
1511
+ art.$contains.$annotation ??= { // set in resolveExprPath
1497
1512
  $path: false,
1498
1513
  $self: false,
1499
1514
  };
@@ -1501,6 +1516,9 @@ function resolve( model ) {
1501
1516
  // Might be useful for future recursive types.
1502
1517
  }
1503
1518
 
1519
+ // Note: for annotation assignments, `art` should be the annotation, not the
1520
+ // assignee (element, ...); otherwise, artifact links could be resolved in the
1521
+ // wrong block
1504
1522
  function resolveExpr( expr, exprCtx, art, type = null ) {
1505
1523
  traverseTypedExpr( expr, exprCtx, art, type, resolveExprNode );
1506
1524
  }
@@ -1553,8 +1571,12 @@ function resolve( model ) {
1553
1571
  const ref = resolvePath( expr, expected, user );
1554
1572
 
1555
1573
  if (expected === 'annotation') {
1556
- user._outer.$contains.$annotation.$path = true;
1557
- user._outer.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
1574
+ const ruser = (user._outer.kind === '$annotation')
1575
+ ? user._outer._outer
1576
+ : user._outer;
1577
+ // if (!ruser.$contains) console.log('UO:',!!ruser,!!user._block,user._outer)
1578
+ ruser.$contains.$annotation.$path = true;
1579
+ ruser.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
1558
1580
  }
1559
1581
 
1560
1582
  // check whether arguments and filters are allowed on last path item;
@@ -775,8 +775,13 @@ function fns( model ) {
775
775
  if (head._artifact !== undefined)
776
776
  return head._artifact;
777
777
  let ruser = user._user || user; // TODO: nicer name if we keep this
778
- if (ruser.kind === '$annotation')
779
- ruser = ruser._outer;
778
+ // TODO: re-think _user link
779
+ if (ruser._outer && !semantics.isMainRef) {
780
+ if (ruser.kind === '$annotation')
781
+ ruser = ruser._outer; // for elem refs, use elem as real "user"
782
+ else if (ruser._outer.kind === '$annotation')
783
+ ruser = ruser._outer._outer;
784
+ }
780
785
 
781
786
  // Handle expand/inline, `type of`, :param, global (internally for CDL):
782
787
  if (user._columnParent && !semantics.isMainRef) { // in expand/inline
@@ -1002,7 +1007,8 @@ function fns( model ) {
1002
1007
  // TODO: remove again, should be easy enough in to-csn without.
1003
1008
  if (path.length === 1 && art.kind === '$tableAlias')
1004
1009
  (user._user || user).$noOrigin = true;
1005
- if (head.id === '$projection' && user.kind === '$annotation') {
1010
+ if (head.id === '$projection' &&
1011
+ (user.kind === '$annotation' || user._outer?.kind === '$annotation')) {
1006
1012
  error( 'ref-unsupported-projection', [ head.location, user ],
1007
1013
  { code: '$projection', newcode: '$self' },
1008
1014
  '$(CODE) is not supported in annotations; replace by $(NEWCODE)' );
@@ -510,12 +510,10 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
510
510
  evalArgs(td, typeFacets, typeDef);
511
511
  EdmTypeFacetNames.forEach((facetName) => {
512
512
  const facetDef = EdmTypeFacetMap[facetName];
513
- const optional
514
- = (facetDef.optional !== undefined)
515
- ? (Array.isArray(facetDef.optional)
513
+ const optional = (facetDef.optional !== undefined) &&
514
+ (Array.isArray(facetDef.optional)
516
515
  ? facetDef.optional.includes(typeDef)
517
- : facetDef.optional)
518
- : false;
516
+ : facetDef.optional);
519
517
 
520
518
  if (td[facetName]) {
521
519
  // ignore facets that are not type relevant
@@ -267,7 +267,8 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
267
267
 
268
268
  Object.entries(cObject.actions).forEach(([ n, action ]) => {
269
269
  setProp(action, '$isBound', true);
270
- const actionName = `${ serviceName }.${ isV2() ? `${ entityName }_` : '' }${ n }`;
270
+ const v2entity = isV2() ? `${ entityName }_` : '';
271
+ const actionName = `${ serviceName }.${ v2entity }${ n }`;
271
272
  handleAction(actionName, action, cObjectname, [ ...location, 'actions', n ]);
272
273
  });
273
274
  }
@@ -1662,7 +1663,9 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1662
1663
  message('odata-anno-dict', msg.location, { anno: msg.anno(), '#': 'experimental' });
1663
1664
  usedExperimentalTerms[termName] = true;
1664
1665
  }
1665
- if (dictTerm.$deprecated && !usedDeprecatedTerms[termName]) {
1666
+ // TODO: remove this part "&& termName !== 'Common.DraftNode'" once the annotation is not part
1667
+ // of the dictionaries
1668
+ if (dictTerm.$deprecated && !usedDeprecatedTerms[termName] && termName !== 'Common.DraftNode') {
1666
1669
  message('odata-anno-def', msg.location,
1667
1670
  { anno: msg.anno(), depr: dictTerm.$deprecationText, '#': 'deprecated' });
1668
1671
  usedDeprecatedTerms[termName] = true;
@@ -1172,12 +1172,10 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
1172
1172
  }
1173
1173
  EdmTypeFacetNames.forEach((name) => {
1174
1174
  const facet = EdmTypeFacetMap[name];
1175
- const optional
1176
- = (facet.optional !== undefined)
1177
- ? (Array.isArray(facet.optional)
1178
- ? facet.optional.includes(edmType)
1179
- : facet.optional)
1180
- : false;
1175
+ const optional = (facet.optional !== undefined) &&
1176
+ (Array.isArray(facet.optional)
1177
+ ? facet.optional.includes(edmType)
1178
+ : facet.optional);
1181
1179
 
1182
1180
  // facet is not in attributes
1183
1181
  // facet is member of type definition and mandatory
package/lib/edm/edm.js CHANGED
@@ -633,17 +633,17 @@ function getEdm( options ) {
633
633
  this._typeName = typeName;
634
634
  this._scalarType = undefined;
635
635
  if (this._edmAttributes[typeName] === undefined) {
636
- const typecsn = csn.type ? csn : (csn.items && csn.items.type ? csn.items : csn);
636
+ const typeCsn = csn.items?.type ? csn.items : csn;
637
637
  // Complex/EntityType are derived from TypeBase
638
638
  // but have no type attribute in their CSN
639
- if (typecsn.type) { // this thing has a type
639
+ if (typeCsn.type) { // this thing has a type
640
640
  // check whether this is a scalar type (or array of scalar type) or a named type
641
- if (typecsn.items && typecsn.items.type &&
642
- isBuiltinType(typecsn.items.type))
643
- this._scalarType = typecsn.items;
641
+ if (typeCsn.items?.type &&
642
+ isBuiltinType(typeCsn.items.type))
643
+ this._scalarType = typeCsn.items;
644
644
 
645
- else if (isBuiltinType(typecsn.type))
646
- this._scalarType = typecsn;
645
+ else if (isBuiltinType(typeCsn.type))
646
+ this._scalarType = typeCsn;
647
647
 
648
648
  if (this._scalarType) {
649
649
  this._edmAttributes[typeName] = csn._edmType;
@@ -658,7 +658,7 @@ function getEdm( options ) {
658
658
  }
659
659
  else {
660
660
  // it's either _edmType or type (_edmType only used for explicit binding param)
661
- this._edmAttributes[typeName] = typecsn._edmType || typecsn.type;
661
+ this._edmAttributes[typeName] = typeCsn._edmType || typeCsn.type;
662
662
  }
663
663
  }
664
664
  // CDXCORE-245:
@@ -666,7 +666,7 @@ function getEdm( options ) {
666
666
  // optionally add @odata { MaxLength, Precision, Scale, SRID }
667
667
  // but only in combination with @odata.Type
668
668
  // Allow to override type only on scalar and undefined types
669
- if ((this._scalarType || typecsn.type == null) && !csn.elements) {
669
+ if ((this._scalarType || typeCsn.type == null) && !csn.elements) {
670
670
  const odataType = csn['@odata.Type'];
671
671
  if (odataType) {
672
672
  const td = EdmPrimitiveTypeMap[odataType];
@@ -1053,15 +1053,15 @@ function getEdm( options ) {
1053
1053
  delete this._edmAttributes.Nullable;
1054
1054
  }
1055
1055
  // we have exactly one selfReference or the default partner
1056
- const partner
1057
- = !csn.$noPartner
1058
- ? csn._selfReferences.length === 1
1059
- ? csn._selfReferences[0]
1060
- : csn._constraints._partnerCsn
1061
- : undefined;
1062
- if (partner && partner['@odata.navigable'] !== false && this._csn._edmParentCsn.kind !== 'type') {
1063
- // $abspath[0] is main entity
1064
- this._edmAttributes.Partner = partner.$abspath.slice(1).join('/');
1056
+
1057
+ if ( !csn.$noPartner) {
1058
+ const partner = csn._selfReferences.length === 1
1059
+ ? csn._selfReferences[0]
1060
+ : csn._constraints._partnerCsn;
1061
+ if (partner && partner['@odata.navigable'] !== false && this._csn._edmParentCsn.kind !== 'type') {
1062
+ // $abspath[0] is main entity
1063
+ this._edmAttributes.Partner = partner.$abspath.slice(1).join('/');
1064
+ }
1065
1065
  }
1066
1066
 
1067
1067
  /*
@@ -1212,8 +1212,11 @@ function getEdm( options ) {
1212
1212
  /* short notation for Edm.Boolean, Edm.String and Edm.Float, see internal project:
1213
1213
  edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
1214
1214
  */
1215
- if (inline[0] === 'Edm.Boolean')
1216
- return (v === 'true' ? true : (v === 'false' ? false : v));
1215
+ if (inline[0] === 'Edm.Boolean') {
1216
+ if (v === 'true')
1217
+ return true;
1218
+ return (v === 'false') ? false : v;
1219
+ }
1217
1220
  return v;
1218
1221
  }
1219
1222
 
@@ -1292,7 +1295,8 @@ function getEdm( options ) {
1292
1295
  }
1293
1296
 
1294
1297
  getJsonFQTermName() {
1295
- return `@${ this._edmAttributes.Term }${ this._edmAttributes.Qualifier ? `#${ this._edmAttributes.Qualifier }` : '' }`;
1298
+ const qualifier = this._edmAttributes.Qualifier ? `#${ this._edmAttributes.Qualifier }` : '';
1299
+ return `@${ this._edmAttributes.Term }${ qualifier }`;
1296
1300
  }
1297
1301
  }
1298
1302
 
@@ -117,13 +117,13 @@ function inboundQualificationChecks( csn, options, messageFunctions,
117
117
  i++;
118
118
  }
119
119
  if (i > 1) {
120
- message('chained-array-of', path);
120
+ message('type-invalid-items', path, { '#': 'nested', prop: 'items' });
121
121
  return;
122
122
  }
123
123
 
124
124
  const itemsType = csnUtils.effectiveType(memberType.items);
125
125
  if (itemsType.items)
126
- message('chained-array-of', path);
126
+ message('type-invalid-items', path, { '#': 'chained-service', prop: 'items' });
127
127
  }
128
128
  }
129
129
 
@@ -275,7 +275,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
275
275
  // if this definition has a root def and the root def is not the service/schema name
276
276
  // => service C { type D.E }, replace the prefix dots with underscores
277
277
  if (rootDef && defName !== rootDef && rootDef !== edmUtils.getSchemaPrefix(defName)) {
278
- const newDefName = `${ rootDef }.${ defName.replace(`${ rootDef }.`, '').replace(/\./g, '_') }`;
278
+ const underscoredDefName = defName
279
+ .replace(`${ rootDef }.`, '')
280
+ .replace(/\./g, '_');
281
+ const newDefName = `${ rootDef }.${ underscoredDefName }`;
279
282
  // store renamed types in correlation maps for later renaming
280
283
  if (def.kind === 'entity')
281
284
  dotEntityNameMap[defName] = newDefName;
@@ -335,8 +338,20 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
335
338
  rewriteReferencesInActions(node);
336
339
  };
337
340
 
338
- forEachMemberRecursively(def, applyOnNode);
341
+ const applyOnAnnoXprs = (node) => {
342
+ Object.keys(node).filter(pn => pn[0] === '@').forEach((anno) => {
343
+ transformAnnotationExpression(node, anno, {
344
+ ref: (elemRef, _prop, ref) => {
345
+ if (dotEntityNameMap[ref[0]] || dotTypeNameMap[ref[0]])
346
+ elemRef.ref[0] = dotEntityNameMap[ref[0]] || dotTypeNameMap[ref[0]];
347
+ },
348
+ });
349
+ });
350
+ };
351
+
352
+ forEachMemberRecursively(def, [ applyOnNode, applyOnAnnoXprs ]);
339
353
  applyOnNode(def);
354
+ applyOnAnnoXprs(def);
340
355
  // handle unbound action/function and params in views
341
356
  rewriteReferencesInActions(def);
342
357
  };
@@ -876,7 +891,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
876
891
  // eslint-disable-next-line eqeqeq
877
892
  const srcMult = (partnerCsn.cardinality.src == 1) ? '0..1' : '*';
878
893
  const newMult
879
- = (element.cardinality?.min == 1 && element.cardinality?.max == 1) // eslint-disable-line eqeqeq
894
+ = (element.cardinality?.min == 1 && element.cardinality?.max == 1) // eslint-disable-line
880
895
  ? 1
881
896
  : (element.cardinality?.max === '*' || element.cardinality?.max > 1)
882
897
  ? '*'
@@ -1082,8 +1097,9 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1082
1097
 
1083
1098
  // 1) construct the proxy definition
1084
1099
  // proxyDefinitionName: strip the serviceName and replace '.' with '_'
1085
- const defName
1086
- = `${ assoc._target.name.replace(`${ proxySchemaName }.`, '').replace(/\./g, '_') }`;
1100
+ const defName = assoc._target.name
1101
+ .replace(`${ proxySchemaName }.`, '')
1102
+ .replace(/\./g, '_');
1087
1103
 
1088
1104
  // fullName: Prepend serviceName and if in same service add '_proxy'
1089
1105
  const proxy = isParamProxy
@@ -446,23 +446,20 @@ function determineMultiplicity( csn ) {
446
446
  if (!csn.cardinality)
447
447
  csn.cardinality = Object.create(null);
448
448
 
449
- if (!csn.cardinality.src)
450
- csn.cardinality.src = isAssoc ? '*' : '1';
451
- if (!csn.cardinality.min)
452
- csn.cardinality.min = 0;
453
- if (!csn.cardinality.max)
454
- csn.cardinality.max = 1;
449
+ csn.cardinality.src ??= isAssoc ? '*' : '1';
450
+ csn.cardinality.min ??= 0;
451
+ csn.cardinality.max ??= 1;
455
452
 
456
453
  const srcCardinality
457
- = (csn.cardinality.src == 1) // eslint-disable-line eqeqeq
458
- ? (!isAssoc || csn.cardinality.srcmin == 1) // eslint-disable-line eqeqeq
454
+ = (csn.cardinality.src == 1) // eslint-disable-line
455
+ ? (!isAssoc || csn.cardinality.srcmin == 1) // eslint-disable-line
459
456
  ? '1'
460
457
  : '0..1'
461
458
  : '*';
462
- const tgtCardinality
459
+ const tgtCardinality // eslint-disable-next-line no-nested-ternary
463
460
  = (csn.cardinality.max > 1 || csn.cardinality.max === '*')
464
461
  ? '*'
465
- : (csn.cardinality.min == 1) // eslint-disable-line eqeqeq
462
+ : (csn.cardinality.min == 1) // eslint-disable-line
466
463
  ? '1'
467
464
  : '0..1';
468
465
 
@@ -1,4 +1,4 @@
1
- // Base class for generated parser, for redepage v0.2.3
1
+ // Base class for generated parser, for redepage v0.2.4
2
2
 
3
3
  'use strict';
4
4
 
@@ -9,7 +9,7 @@
9
9
  // list of predicates which are tested when continue parsing after error starts,
10
10
  // i.e. there is a predicate on the first token to match after recover example
11
11
  // `afterBrace` or just method which by default just sets this.conditionTokenIdx
12
- // and this.conditionStackLength and returns true?
12
+ // and this.conditionStackLength and returns true?
13
13
 
14
14
  class BaseParser {
15
15
  keywords;
@@ -314,7 +314,7 @@ class BaseParser {
314
314
  }
315
315
  // calling the condition might have side effects (precendence conditions have)
316
316
  // → call tracing “name” before
317
- const fail = this[cond]( true, arg ); // TODO: use single-letter for run?
317
+ const fail = this[cond]( arg, true ); // TODO: use single-letter for run?
318
318
  if (this.constructor.tracingParser)
319
319
  this._traceSubPush( !fail );
320
320
  // The default case must not have actions. If written in grammar with action,
@@ -580,7 +580,7 @@ class BaseParser {
580
580
  if (!cond || lean && !this.leanConditions[cond])
581
581
  return false;
582
582
  if (!this.constructor.tracingParser)
583
- return !!this[cond]( mode, cmd[4] );
583
+ return !!this[cond]( cmd[4], mode );
584
584
  // TODO: let this[cond]( true ) return recovery badness in error case
585
585
  if (!lean) {
586
586
  const { traceName } = this[cond];
@@ -588,7 +588,7 @@ class BaseParser {
588
588
  // calling the condition might have side effects (precendence conditions have)
589
589
  // → call tracing “name” before
590
590
  }
591
- const succeed = !this[cond]( mode, cmd[4] );
591
+ const succeed = !this[cond]( cmd[4], mode );
592
592
  this._traceSubPush( lean ? { true: 'C✔', false: 'C✖' }[succeed] : succeed );
593
593
  return !succeed;
594
594
  }
@@ -934,24 +934,24 @@ class BaseParser {
934
934
 
935
935
  // Predefined conditions with extra option names:
936
936
 
937
- hide_( mode ) {
937
+ hide_( _arg, mode ) {
938
938
  return mode === 'M';
939
939
  }
940
- precLeft_( _test, prec ) { // <prec=…>, <…,assoc=left>, <…,prefix=once>
940
+ precLeft_( prec ) { // <prec=…>, <…,assoc=left>, <…,prefix=once>
941
941
  const parentPrec = this.stack.at( -1 ).prec;
942
942
  if (parentPrec != null && parentPrec >= prec)
943
943
  return true;
944
944
  this.prec_ = prec;
945
945
  return false;
946
946
  }
947
- precRight_( _test, prec ) { // <…,assoc=right>, <…,prefix>
947
+ precRight_( prec ) { // <…,assoc=right>, <…,prefix>
948
948
  const parentPrec = this.stack.at( -1 ).prec;
949
949
  if (parentPrec != null && parentPrec >= prec)
950
950
  return true;
951
951
  this.prec_ = prec - 1;
952
952
  return false;
953
953
  }
954
- precNone_( _test, prec ) { // <…,assoc=none>, <…,postfix=once>
954
+ precNone_( prec ) { // <…,assoc=none>, <…,postfix=once>
955
955
  const parentPrec = this.stack.at( -1 ).prec;
956
956
  if (parentPrec != null && parentPrec >= prec ||
957
957
  this.prec_ != null && this.prec_ <= prec)
@@ -959,7 +959,7 @@ class BaseParser {
959
959
  this.prec_ = prec;
960
960
  return false;
961
961
  }
962
- precPost_( _test, prec ) { // <…,postfix>
962
+ precPost_( prec ) { // <…,postfix>
963
963
  const parentPrec = this.stack.at( -1 ).prec;
964
964
  if (parentPrec != null && parentPrec >= prec ||
965
965
  this.prec_ != null && this.prec_ < prec)
@@ -1 +1 @@
1
- 539f33c85849442e96168a85adec09fb
1
+ eb778bab22f4006cfcf64e418dcae71c
@@ -1,4 +1,4 @@
1
- // Parser generated by redepage v0.2.3
1
+ // Parser generated by redepage v0.2.4
2
2
  'use strict;'
3
3
  const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' )
4
4
  const AstBuildingParser = require('../parsers/AstBuildingParser')
@@ -783,7 +783,7 @@ floating:['ck',0],variable:'floating',
783
783
  {
784
784
  '*':['c',430],
785
785
  Number:['c',432],
786
- '':434
786
+ ']':434,
787
787
  },
788
788
  {',':['c',431],'':434},
789
789
  [434,436],
@@ -1259,10 +1259,10 @@ Id:['ciA',720],
1259
1259
  },
1260
1260
  ['m',721,'=>'],
1261
1261
  [718,644],
1262
- {Id:[723,731],'#':'Id','(':'Id','*':'Id','+':'Id','-':'Id',':':'Id','?':'Id',Id_all:'Id',Number:'Id',String:'Id',QuotedLiteral:'Id',DeleteStarFromSet:'Id','':728},
1262
+ {Id:[723,731],'#':'Id','(':'Id','*':'Id','+':'Id','-':'Id',':':'Id','?':'Id',Number:'Id',String:'Id',QuotedLiteral:'Id',DeleteStarFromSet:'Id','':728},
1263
1263
  {',':['c',724,,,,'nextFunctionArgument'],'':725},
1264
1264
  {
1265
- Id:[723,731],'#':'Id','(':'Id','*':'Id','+':'Id','-':'Id',':':'Id','?':'Id',not:'Id',case:'Id',cast:'Id',null:'Id',true:'Id',false:'Id',Id_all:'Id',Number:'Id',String:'Id',exists:'Id',QuotedLiteral:'Id',DeleteStarFromSet:'Id',
1265
+ Id:[723,731],'#':'Id','(':'Id','*':'Id','+':'Id','-':'Id',':':'Id','?':'Id',not:'Id',case:'Id',cast:'Id',null:'Id',true:'Id',false:'Id',Number:'Id',String:'Id',exists:'Id',QuotedLiteral:'Id',DeleteStarFromSet:'Id',
1266
1266
  ')':['g',725,1,'atRightParen'],order:')',
1267
1267
  },
1268
1268
  {order:['ck',726],'':728},
@@ -1430,7 +1430,10 @@ Id:[825,815],'#':'Id','(':'Id','+':'Id','-':'Id','@':'Id','[':'Id','{':'Id',Numb
1430
1430
  '...':['c',822,,'arrayAnno','ellipsis'],
1431
1431
  '':826
1432
1432
  },
1433
- {up:['ck',823],'':825},
1433
+ {
1434
+ up:['ck',823],
1435
+ ',':825,']':825,
1436
+ },
1434
1437
  ['mk',824,'to'],
1435
1438
  [825,815],
1436
1439
  {
@@ -1447,7 +1450,7 @@ constructor(lexer,...args){super(lexer,keywords,table,...args)}
1447
1450
  start($,$next){
1448
1451
  $.source??=new XsnSource( 'cdl' )
1449
1452
  this.rule_(1,$next)
1450
- { this.afterBrace( null, 'init' ); }
1453
+ { this.afterBrace( 'init' ); }
1451
1454
  for(;;)switch(this.s){
1452
1455
  case 1:switch(this.lk()){
1453
1456
  case'namespace':this.gc(3,'namespaceRestriction')&&this.namespaceDeclaration({source:$.source},2);continue
@@ -1468,7 +1471,7 @@ default:return this.exit_()
1468
1471
  artifactsBlock($,$next){
1469
1472
  this.rule_(5,$next)
1470
1473
  for(;;)switch(this.s){
1471
- case 5:if(this.m(6,'{')){this.afterBrace(0,'init'); $.art.artifacts = this.createDict( $.start ); $.art.extensions = []; }continue
1474
+ case 5:if(this.m(6,'{')){this.afterBrace('init'); $.art.artifacts = this.createDict( $.start ); $.art.extensions = []; }continue
1472
1475
  case 6:switch(this.l()){
1473
1476
  case'Id':case'@':this.artifactDefOrExtend({outer:$.art},7);continue
1474
1477
  default:this.s=8;continue
@@ -1771,7 +1774,7 @@ default:this.e();continue
1771
1774
  case 81:this.elementsBlock({art:$.art},87);continue
1772
1775
  case 82:switch(this.lk()){
1773
1776
  case'(':case'select':if(this.queryExpression(_={},87)){query=_.expr; $.art.query = query; $.art.$syntax = 'entity'; }continue
1774
- case'projection':this.s=83;{this.afterBrace(0,'sloppy')}continue;
1777
+ case'projection':this.s=83;{this.afterBrace('sloppy')}continue;
1775
1778
  default:this.e();continue
1776
1779
  }
1777
1780
  case 83:if(this.projectionSpec(_={},84)){query=_.query; $.art.query = query; $.art.$syntax = 'projection'; }continue
@@ -1783,7 +1786,7 @@ case 85:switch(this.lk()){
1783
1786
  case'limit':case'order':this.orderByLimitOffset({query},86);continue
1784
1787
  default:this.s=86;continue
1785
1788
  }
1786
- case 86:this.s=87;{this.afterBrace(0,'normal'); ; }continue
1789
+ case 86:this.s=87;{this.afterBrace('normal'); ; }continue
1787
1790
  case 87:switch(this.lk()){
1788
1791
  case'actions':this.actionsBlock({art:$.art},0);continue
1789
1792
  default:this.gr([';']);continue
@@ -2021,7 +2024,7 @@ let art=new XsnArtifact()
2021
2024
  let ret
2022
2025
  this.rule_(154,$next)
2023
2026
  for(;;)switch(this.s){
2024
- case 154:if(this.mk(155,'returns')){ret=this.lb();this.elementRestriction(0,'default'); art.kind = 'param'; $.outer.returns = art;
2027
+ case 154:if(this.mk(155,'returns')){ret=this.lb();this.elementRestriction('default'); art.kind = 'param'; $.outer.returns = art;
2025
2028
  this.docComment( art ); }continue
2026
2029
  case 155:switch(this.l()){
2027
2030
  case'@':this.annoAssignStd({art},155);continue
@@ -2078,7 +2081,7 @@ case 167:switch(this.lk()){
2078
2081
  case'element':if(this.ckP(168,['Id'])){ $.art.$syntax = 'element'; }continue
2079
2082
  default:this.s=168;continue
2080
2083
  }
2081
- case 168:if(this.mi(169,'Element')){this.elementRestriction(0,'elem'); this.addDef( $.art, $.outer, 'elements', 'element', this.identAst() );
2084
+ case 168:if(this.mi(169,'Element')){this.elementRestriction('elem'); this.addDef( $.art, $.outer, 'elements', 'element', this.identAst() );
2082
2085
  this.docComment( $.art ); }continue
2083
2086
  case 169:switch(this.l()){
2084
2087
  case'@':this.annoAssignMid({art:$.art},169);continue
@@ -2811,11 +2814,11 @@ case 358:switch(this.lk()){
2811
2814
  case'Id':this.typeRefOptArgs({art:$.art},359);continue
2812
2815
  case'type':this.lP()&&this.typeTypeOf({art:$.art},359);continue
2813
2816
  case'localized':if(this.ckP(368,['Id'])){ $.art.localized = this.valueWithLocation( true ); }continue
2814
- case'association':if(this.ckP(370,['[','to'])){assoc=this.lb();this.elementRestriction(0,'calc')}continue;
2815
- case'composition':if(this.ckP(374,['[','of'])){assoc=this.lb();this.elementRestriction(0,'calc')}continue;
2816
- case'array':if(this.ckP(378,['of'])){this.elementRestriction(0,'calc')}continue;
2817
- case'many':if(this.ckP(379,['Id','{'])){this.elementRestriction(0,'calc'); $.art.items = { location: this.lb().location }; }continue
2818
- case'{':if(this.elementsBlock({art:$.art},385)){this.elementRestriction(0,'calc')}continue;
2817
+ case'association':if(this.ckP(370,['[','to'])){assoc=this.lb();this.elementRestriction('calc')}continue;
2818
+ case'composition':if(this.ckP(374,['[','of'])){assoc=this.lb();this.elementRestriction('calc')}continue;
2819
+ case'array':if(this.ckP(378,['of'])){this.elementRestriction('calc')}continue;
2820
+ case'many':if(this.ckP(379,['Id','{'])){this.elementRestriction('calc'); $.art.items = { location: this.lb().location }; }continue
2821
+ case'{':if(this.elementsBlock({art:$.art},385)){this.elementRestriction('calc')}continue;
2819
2822
  default:this.ei();continue
2820
2823
  }
2821
2824
  case 359:this.s=360;{ this.docComment( $.art ); }continue
@@ -2832,7 +2835,7 @@ case'@':this.annoAssignStd({art:$.art},362);continue
2832
2835
  default:this.s=363;continue
2833
2836
  }
2834
2837
  case 363:switch(this.lk()){
2835
- case'enum':if(this.enumSymbolsBlock({art:$.art},364)){this.elementRestriction(0,'anno')}continue;
2838
+ case'enum':if(this.enumSymbolsBlock({art:$.art},364)){this.elementRestriction('anno')}continue;
2836
2839
  case'@':case'not':case'null':case'default':this.typeProperties({art:$.art},0);continue
2837
2840
  default:this.gr([]);continue
2838
2841
  }
@@ -3062,7 +3065,8 @@ case 428:this.m(429,'[');continue
3062
3065
  case 429:switch(this.l()){
3063
3066
  case'*':if(this.c(430)){ card.targetMax = this.valueWithLocation(); }continue
3064
3067
  case'Number':if(this.c(432)){ card.targetMax = this.unsignedIntegerLiteral(); }continue
3065
- default:this.s=434;{ card.targetMax = this.valueWithLocation( '*' ); }continue
3068
+ case']':this.s=434;{ card.targetMax = this.valueWithLocation( '*' ); }continue
3069
+ default:this.e();continue
3066
3070
  }
3067
3071
  case 430:switch(this.l()){
3068
3072
  case',':this.c(431);continue
@@ -3228,7 +3232,7 @@ case 473:switch(this.lk()){
3228
3232
  case'excluding':this.excludingClause({query:$.query},480);continue
3229
3233
  default:this.s=480;continue
3230
3234
  }
3231
- case 474:this.s=475;{this.inSelectItem(0,'sqlStyle'); ; }continue
3235
+ case 474:this.s=475;{this.inSelectItem('sqlStyle'); ; }continue
3232
3236
  case 475:switch(this.l()){
3233
3237
  case'*':if(this.c(476)){ $.query.columns = [ this.valueWithLocation() ]; }continue
3234
3238
  case'Id':case'#':case'(':case'+':case'-':case':':case'?':case'@':case'{':case'Number':case'String':case'QuotedLiteral':this.selectItemDef({columns:($.query.columns = [])},476);continue
@@ -3324,12 +3328,12 @@ for(;;)switch(this.s){
3324
3328
  case 500:if(this.m(501,'(')){this.queryOnLeft()}continue;
3325
3329
  case 501:switch(this.lk()){
3326
3330
  case'(':this.tableOrQueryParens($,502);continue
3327
- case'Id':if(this.tableExpression($,503)){this.queryOnLeft(0,'table')}continue;
3331
+ case'Id':if(this.tableExpression($,503)){this.queryOnLeft('table')}continue;
3328
3332
  case'select':this.queryExpression($,503);continue
3329
3333
  default:this.ei();continue
3330
3334
  }
3331
3335
  case 502:switch(this.lk()){
3332
- case'full':case'join':case'left':case'cross':case'inner':case'right':if(this.tableExpression($,503,488)){this.queryOnLeft(0,'table')}continue;
3336
+ case'full':case'join':case'left':case'cross':case'inner':case'right':if(this.tableExpression($,503,488)){this.queryOnLeft('table')}continue;
3333
3337
  case'limit':case'minus':case'order':case'union':case'except':case'intersect':this.gc(503,'queryOnLeft')&&this.queryExpression($,503,456);continue
3334
3338
  default:this.s=503;continue
3335
3339
  }
@@ -3526,7 +3530,7 @@ default:return this.exit_()
3526
3530
  selectItemsList($,$next){
3527
3531
  this.rule_(559,$next)
3528
3532
  for(;;)switch(this.s){
3529
- case 559:if(this.m(560,'{')){this.inSelectItem(0,'top'); $.query.columns = this.createArray( $.start ); }continue
3533
+ case 559:if(this.m(560,'{')){this.inSelectItem('top'); $.query.columns = this.createArray( $.start ); }continue
3530
3534
  case 560:switch(this.l()){
3531
3535
  case'*':if(this.c(561)){ $.query.columns.push( this.valueWithLocation() ); }continue
3532
3536
  case'Id':case'#':case'(':case'+':case'-':case':':case'?':case'@':case'{':case'Number':case'String':case'QuotedLiteral':this.selectItemDef({columns:$.query.columns},561);continue
@@ -3574,7 +3578,7 @@ case 570:switch(this.lk()){
3574
3578
  case'virtual':if(this.lP(['Id','#','(','+','-',':','?','{','key','not','case','cast','null','true','false','Number','String','exists','QuotedLiteral'])&&this.gc(571,'modifierRestriction')&&this.ck(571)){ art.virtual = this.valueWithLocation( true ); }continue
3575
3579
  default:this.s=571;continue
3576
3580
  }
3577
- case 571:this.s=572;{this.columnExpr(0,'key'); ; }continue
3581
+ case 571:this.s=572;{this.columnExpr('key'); ; }continue
3578
3582
  case 572:switch(this.lk()){
3579
3583
  case'key':if(this.gc(573,'modifierRestriction')&&this.ck(573)){ art.key = this.valueWithLocation( true ); }continue
3580
3584
  default:this.s=573;continue
@@ -3588,7 +3592,7 @@ default:this.ei();continue
3588
3592
  case 574:switch(this.lk()){
3589
3593
  case'as':this.ck(575);continue
3590
3594
  case'Id':if(this.ci(584,'ItemAlias')){ art.name = this.fragileAlias( true ); }continue
3591
- default:this.gi(584);continue
3595
+ default:this.giR(584);continue
3592
3596
  }
3593
3597
  case 575:if(this.mi(584,'ItemAlias')){ art.name = this.identAst(); }continue
3594
3598
  case 576:switch(this.lk()){
@@ -3598,7 +3602,7 @@ case'as':this.ck(581);continue
3598
3602
  case'Id':if(this.ci(584,'ItemAlias')){this.nestedExpand(); art.name = this.fragileAlias( true ); }continue
3599
3603
  case'.':if(this.c(582)){ this.reportUnexpectedSpace( this.lb(), this.la().location, true );
3600
3604
  this.reportExpandInline( art, true ); }continue
3601
- default:if(this.gi(584)){this.nestedExpand(); this.virtualOrImplicit( art ); }continue
3605
+ default:if(this.giR(584)){this.nestedExpand(); this.virtualOrImplicit( art ); }continue
3602
3606
  }
3603
3607
  case 577:this.overClause({outer:expr.suffix},578);continue
3604
3608
  case 578:switch(this.lk()){
@@ -3608,7 +3612,7 @@ default:this.s=579;continue
3608
3612
  case 579:switch(this.lk()){
3609
3613
  case'as':this.ck(580);continue
3610
3614
  case'Id':if(this.ci(584,'ItemAlias')){ art.name = this.fragileAlias( true ); }continue
3611
- default:this.gi(584);continue
3615
+ default:this.giR(584);continue
3612
3616
  }
3613
3617
  case 580:if(this.mi(584,'ItemAlias')){ art.name = this.identAst(); }continue
3614
3618
  case 581:if(this.mi(584,'ItemAlias')){this.nestedExpand(); art.name = this.identAst(); }continue
@@ -3622,7 +3626,7 @@ case'excluding':this.excludingClause({query:art},0);continue
3622
3626
  default:this.gr([',']);continue
3623
3627
  }
3624
3628
  case 584:switch(this.l()){
3625
- case'{':if(this.gc(590,'nestedExpand')&&this.g(585)){ if (!this.nestedExpand(true)) this.reportExpandInline( art, false ); }continue
3629
+ case'{':if(this.gc(590,'nestedExpand')&&this.g(585)){ if (!this.nestedExpand(0,true)) this.reportExpandInline( art, false ); }continue
3626
3630
  default:this.s=590;continue
3627
3631
  }
3628
3632
  case 585:this.nestedSelectItemsList({query:art,clause:'expand'},586);continue
@@ -3929,13 +3933,13 @@ for(;;)switch(this.s){
3929
3933
  case 679:if(this.m(680,'(')){this.queryOnLeft()}continue;
3930
3934
  case 680:switch(this.lk()){
3931
3935
  case'(':this.expressionOrQueryParens($,681);continue
3932
- case'Id':case'#':case'+':case'-':case':':case'?':case'not':case'case':case'cast':case'null':case'true':case'false':case'Number':case'String':case'exists':case'QuotedLiteral':if(this.expression($,683)){this.queryOnLeft(0,'expr')}continue;
3936
+ case'Id':case'#':case'+':case'-':case':':case'?':case'not':case'case':case'cast':case'null':case'true':case'false':case'Number':case'String':case'exists':case'QuotedLiteral':if(this.expression($,683)){this.queryOnLeft('expr')}continue;
3933
3937
  case'select':this.queryExpression($,684);continue
3934
3938
  default:this.ei();continue
3935
3939
  }
3936
3940
  case 681:switch(this.lk()){
3937
- case'*':case'+':case'-':case'/':case'<':case'=':case'>':case'?':case'!=':case'<=':case'<>':case'==':case'>=':case'in':case'is':case'or':case'||':case'and':case'not':case'like':case'between':if(this.expression($,682,645)){this.queryOnLeft(0,'expr')}continue;
3938
- case',':if(this.continueExpressionslist($,684)){this.queryOnLeft(0,'expr')}continue;
3941
+ case'*':case'+':case'-':case'/':case'<':case'=':case'>':case'?':case'!=':case'<=':case'<>':case'==':case'>=':case'in':case'is':case'or':case'||':case'and':case'not':case'like':case'between':if(this.expression($,682,645)){this.queryOnLeft('expr')}continue;
3942
+ case',':if(this.continueExpressionslist($,684)){this.queryOnLeft('expr')}continue;
3939
3943
  case'limit':case'minus':case'order':case'union':case'except':case'intersect':this.gc(684,'queryOnLeft')&&this.queryExpression($,684,456);continue
3940
3944
  default:this.s=684;continue
3941
3945
  }
@@ -4063,7 +4067,7 @@ default:this.e();continue
4063
4067
  case 720:this.m(721,'=>');continue
4064
4068
  case 721:if(this.expression(_={},718)){expr=_.expr; this.addNamedArg( $.pathStep, id, expr ); }continue
4065
4069
  case 722:switch(this.l()){
4066
- case'Id':case'#':case'(':case'*':case'+':case'-':case':':case'?':case'Id_all':case'Number':case'String':case'QuotedLiteral':case'DeleteStarFromSet':if(this.funcExpression(_={},723)){expr=_.expr; $.pathStep.args.push( expr ); }continue
4070
+ case'Id':case'#':case'(':case'*':case'+':case'-':case':':case'?':case'Number':case'String':case'QuotedLiteral':case'DeleteStarFromSet':if(this.funcExpression(_={},723)){expr=_.expr; $.pathStep.args.push( expr ); }continue
4067
4071
  default:this.s=728;continue
4068
4072
  }
4069
4073
  case 723:switch(this.l()){
@@ -4071,7 +4075,7 @@ case',':if(this.c(724)){this.nextFunctionArgument()}continue;
4071
4075
  default:this.s=725;continue
4072
4076
  }
4073
4077
  case 724:switch(this.lk()){
4074
- case'Id':case'#':case'(':case'*':case'+':case'-':case':':case'?':case'not':case'case':case'cast':case'null':case'true':case'false':case'Id_all':case'Number':case'String':case'exists':case'QuotedLiteral':case'DeleteStarFromSet':if(this.funcExpression(_={},723)){expr=_.expr; $.pathStep.args.push( expr ); }continue
4078
+ case'Id':case'#':case'(':case'*':case'+':case'-':case':':case'?':case'not':case'case':case'cast':case'null':case'true':case'false':case'Number':case'String':case'exists':case'QuotedLiteral':case'DeleteStarFromSet':if(this.funcExpression(_={},723)){expr=_.expr; $.pathStep.args.push( expr ); }continue
4075
4079
  case')':case'order':this.lP()&&this.ec('atRightParen')&&this.g(725);continue
4076
4080
  default:this.ei();continue
4077
4081
  }
@@ -4452,7 +4456,8 @@ default:this.s=826;continue
4452
4456
  }
4453
4457
  case 822:switch(this.lk()){
4454
4458
  case'up':this.ck(823);continue
4455
- default:this.s=825;{ $.value.val.push( { literal: 'token', val: '...', location: ellipsis.location } ); }continue
4459
+ case',':case']':this.s=825;{ $.value.val.push( { literal: 'token', val: '...', location: ellipsis.location } ); }continue
4460
+ default:this.e();continue
4456
4461
  }
4457
4462
  case 823:this.mk(824,'to');continue
4458
4463
  case 824:if(this.annoValue(_={},825)){upTo=_.value; $.value.val.push( { literal: 'token', val: '...', location: ellipsis.location, upTo: upTo } ); }continue
@@ -557,9 +557,11 @@
557
557
  "AppliesTo": [
558
558
  "EntitySet"
559
559
  ],
560
- "Type": "Collection(Core.SimpleIdentifier)"
560
+ "Type": "Core.SimpleIdentifier"
561
561
  },
562
562
  "Common.DraftNode": {
563
+ "$deprecated": true,
564
+ "$deprecationText": "Draft nodes are marked with [`DraftActivationVia`](#DraftActivationVia)",
563
565
  "AppliesTo": [
564
566
  "EntitySet"
565
567
  ],
@@ -879,6 +881,14 @@
879
881
  ],
880
882
  "Type": "Core.Tag"
881
883
  },
884
+ "Common.OperationTemplate": {
885
+ "$experimental": true,
886
+ "AppliesTo": [
887
+ "Term",
888
+ "Property"
889
+ ],
890
+ "Type": "Common.QualifiedName"
891
+ },
882
892
  "Common.OriginalProtocolVersion": {
883
893
  "AppliesTo": [
884
894
  "Schema"
@@ -3198,6 +3208,8 @@
3198
3208
  }
3199
3209
  },
3200
3210
  "Common.DraftNodeType": {
3211
+ "$deprecated": true,
3212
+ "$deprecationText": "The deprecated term [`DraftNode`](#DraftNode) effectively only tags the entity set, its value is an empty record",
3201
3213
  "$kind": "ComplexType",
3202
3214
  "Properties": {
3203
3215
  "PreparationAction": "Common.QualifiedName",
@@ -3214,10 +3226,18 @@
3214
3226
  "EditAction": "Common.QualifiedName",
3215
3227
  "NewAction": "Common.QualifiedName",
3216
3228
  "PreparationAction": "Common.QualifiedName",
3229
+ "ResumeAction": "Common.QualifiedName",
3217
3230
  "ShareAction": "Common.QualifiedName",
3218
3231
  "ValidationFunction": "Common.QualifiedName"
3219
3232
  }
3220
3233
  },
3234
+ "Common.DraftUserAccessType": {
3235
+ "$kind": "ComplexType",
3236
+ "Properties": {
3237
+ "UserAccessRole": "Edm.String",
3238
+ "UserID": "Edm.String"
3239
+ }
3240
+ },
3221
3241
  "Common.EffectType": {
3222
3242
  "$deprecated": true,
3223
3243
  "$deprecationText": "All side effects are essentially value changes, differentiation not needed.",
@@ -108,6 +108,9 @@ function cloneCsn( csn, options, sort ) {
108
108
  setHidden( r, prop, csn[prop] );
109
109
  });
110
110
 
111
+ if (csn._mocked) // special case for cds cli's `--with-mocks`; used in SQL backend
112
+ setHidden(r, '_mocked', csn._mocked);
113
+
111
114
  return r;
112
115
  }
113
116
 
@@ -44,6 +44,7 @@ optionProcessor
44
44
  .option(' --beta <list>')
45
45
  .option(' --deprecated <list>')
46
46
  .option(' --direct-backend')
47
+ .option(' --direct-messages')
47
48
  .option(' --fallback-parser <type>', { valid: [ 'auto!', 'cdl', 'csn', 'csn!' ] })
48
49
  .option(' --shuffle <seed>') // 0 | 1..4294967296
49
50
  .option(' --test-mode')
@@ -138,6 +139,8 @@ optionProcessor
138
139
  Can only be used with certain new CSN based backends. Combination with
139
140
  other flags is limited, e.g. --test-mode will not run a consistency check.
140
141
  No recompilation is triggered in case of errors. cdsc will dump.
142
+ --direct-messages Messages are written directly to console.error when an error/warning/… is detected.
143
+ Useful to see some messages before a compiler dump.
141
144
  --shuffle <seed> If provided, some internal processing sequences are changed, most notably by
142
145
  using a shuffled version of ‹model›.definitions. <seed> should be a number
143
146
  between 1 and 4294967296, the compiler uses a random number in that range if the
@@ -113,6 +113,8 @@ class AstBuildingParser extends BaseParser {
113
113
  err.expectedTokens = this.expectingArray();
114
114
  }
115
115
 
116
+ // Guards, Prepare Commands and Lookahead Methods -----------------------------
117
+
116
118
  tableWithoutAs() { // not used in <guard=…>, only called by other guard
117
119
  // TODO TOOL: if the tool properly creates `default: this.giR()`, this
118
120
  // condition method is most likely not necessary
@@ -135,7 +137,7 @@ class AstBuildingParser extends BaseParser {
135
137
  * - <guard=queryOnLeft, arg=tableWithoutAs>: …after having checked
136
138
  * whether the next token is no (reserved or unreserved) keyword
137
139
  */
138
- queryOnLeft( test, arg ) {
140
+ queryOnLeft( arg, test ) {
139
141
  if (arg === 'tableWithoutAs') {
140
142
  if (this.tableWithoutAs())
141
143
  return true;
@@ -217,7 +219,7 @@ class AstBuildingParser extends BaseParser {
217
219
  }
218
220
  }
219
221
 
220
- inSelectItem( _test, arg ) { // only as action
222
+ inSelectItem( arg ) { // <prepare=…>
221
223
  this.dynamic_.inSelectItem = arg ||
222
224
  (this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand');
223
225
  }
@@ -248,7 +250,7 @@ class AstBuildingParser extends BaseParser {
248
250
  return next === '*' || next === '{';
249
251
  }
250
252
 
251
- notAfterEntityArgOrFilter( mode ) { // TODO: for <hide>
253
+ notAfterEntityArgOrFilter( _arg, mode ) { // TODO: for <hide>
252
254
  if (mode !== 'M')
253
255
  return false;
254
256
  const { type } = this.lb();
@@ -260,14 +262,14 @@ class AstBuildingParser extends BaseParser {
260
262
 
261
263
  // <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
262
264
  // completion for `… default 3 not ~;` → currently just `null` but hey
263
- isNegatedRelation( _test, prec ) {
265
+ isNegatedRelation( prec ) {
264
266
  return this.tokens[this.tokenIdx + 1]?.keyword === 'null' ||
265
- this.precNone_( _test, prec );
267
+ this.precNone_( prec );
266
268
  }
267
269
 
268
270
  // TODO: as leanCondition ? `order` should probably appear in the message for
269
271
  // test3/Compiler/GrammarRobustness/InvalidSelectInWhere.err.cds
270
- orderByLimitRestriction( mode ) {
272
+ orderByLimitRestriction( _arg, mode ) {
271
273
  if (mode && (!this.$allowOrderByLimit || this.precPost_( mode, 0 )))
272
274
  return true;
273
275
  this.$allowOrderByLimit = !mode;
@@ -309,7 +311,7 @@ class AstBuildingParser extends BaseParser {
309
311
  /**
310
312
  * `annotation` def is only allowed top-level. TODO: combine with `extensionRestriction`
311
313
  */
312
- vocabularyRestriction( test ) {
314
+ vocabularyRestriction( _arg, test ) {
313
315
  if (!test)
314
316
  this.dynamic_.inBlock = this.tokenIdx;
315
317
  return this.dynamic_.inBlock ?? this.dynamic_.inExtension;
@@ -325,7 +327,7 @@ class AstBuildingParser extends BaseParser {
325
327
  * Currently only to restrict it to a single `Id` for published associations.
326
328
  * No extra syntax-unexpected-assoc for failure.
327
329
  */
328
- columnExpr( mode, arg ) {
330
+ columnExpr( arg, mode ) {
329
331
  if (mode)
330
332
  return !this.columnExpr$;
331
333
  if (arg)
@@ -337,7 +339,7 @@ class AstBuildingParser extends BaseParser {
337
339
  return true;
338
340
  }
339
341
 
340
- nestedExpand( mode ) {
342
+ nestedExpand( _arg, mode ) {
341
343
  if (!mode)
342
344
  this.nestedExpand$ = this.tokenIdx;
343
345
  return this.nestedExpand$ !== this.tokenIdx;
@@ -381,7 +383,7 @@ class AstBuildingParser extends BaseParser {
381
383
  * - DEFAULT: true if `default` had been provided
382
384
  * - NOTNULL: true if `null` or `not null` had been provided
383
385
  */
384
- elementRestriction( test, arg ) {
386
+ elementRestriction( arg, test ) {
385
387
  let { elementCtx } = this.dynamic_;
386
388
  if (test) {
387
389
  if (elementCtx?.[0] === arg)
@@ -424,7 +426,7 @@ class AstBuildingParser extends BaseParser {
424
426
  'Unexpected $(OFFENDING) after $(KEYWORD) clause, expecting $(EXPECTING)' );
425
427
  }
426
428
 
427
- noRepeatedCardinality( mode ) {
429
+ noRepeatedCardinality( _arg, mode ) {
428
430
  if (this.tokens[this.tokenIdx - 2]?.type !== ']')
429
431
  return false;
430
432
  if (mode === 'M')
@@ -449,7 +451,7 @@ class AstBuildingParser extends BaseParser {
449
451
  *
450
452
  * Beware: mentioned in leanConditions, i.e. executed in predictions!
451
453
  */
452
- afterBrace( test, arg ) {
454
+ afterBrace( arg, test ) {
453
455
  if (!test) {
454
456
  if (arg === 'normal' && this.lb().type !== '}') {
455
457
  const { type, keyword } = this.la();
@@ -493,7 +495,7 @@ class AstBuildingParser extends BaseParser {
493
495
  /**
494
496
  * For annotations at the beginning of columns outside parentheses
495
497
  */
496
- annoInSameLine( test ) {
498
+ annoInSameLine( _arg, test ) {
497
499
  if (!test)
498
500
  this.dynamic_.safeAnno = true;
499
501
  return !this.dynamic_.safeAnno &&
@@ -505,7 +507,7 @@ class AstBuildingParser extends BaseParser {
505
507
  * - `...` can appear in the top-level array value only and not after `...`
506
508
  * without `up to`.
507
509
  */
508
- arrayAnno( test, arg ) {
510
+ arrayAnno( arg, test ) {
509
511
  if (!test) {
510
512
  this.dynamic_.arrayAnno = [ !this.dynamic_.arrayAnno ];
511
513
  }
@@ -538,7 +540,7 @@ class AstBuildingParser extends BaseParser {
538
540
  return this.tokens[this.tokenIdx + 1]?.text !== ':';
539
541
  }
540
542
 
541
- fail( mode ) {
543
+ fail( _arg, mode ) {
542
544
  // TODO TOOL: the following test belongs to the BaseParser.js:
543
545
  if (this.conditionTokenIdx === this.tokenIdx && // tested on same
544
546
  this.conditionStackLength == null && // after error recover
@@ -624,6 +626,8 @@ class AstBuildingParser extends BaseParser {
624
626
  return prefixLoc;
625
627
  }
626
628
 
629
+ // Locations for ASTs ---------------------------------------------------------
630
+
627
631
  startLocation( { location } = this.lr() ) {
628
632
  return {
629
633
  __proto__: Location.prototype,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "6.0.10",
3
+ "version": "6.0.12",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",