@sap/cds-compiler 4.1.2 → 4.2.4

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +107 -1
  2. package/bin/cdsc.js +6 -3
  3. package/doc/CHANGELOG_BETA.md +5 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +2 -2
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +24 -24
  8. package/lib/base/message-registry.js +41 -6
  9. package/lib/base/messages.js +7 -0
  10. package/lib/base/model.js +38 -8
  11. package/lib/checks/elements.js +11 -10
  12. package/lib/checks/manyNavigations.js +33 -0
  13. package/lib/checks/onConditions.js +5 -2
  14. package/lib/checks/queryNoDbArtifacts.js +2 -3
  15. package/lib/checks/selectItems.js +4 -55
  16. package/lib/checks/utils.js +3 -2
  17. package/lib/checks/validator.js +3 -1
  18. package/lib/compiler/.eslintrc.json +2 -1
  19. package/lib/compiler/assert-consistency.js +27 -24
  20. package/lib/compiler/base.js +6 -2
  21. package/lib/compiler/builtins.js +34 -34
  22. package/lib/compiler/checks.js +179 -208
  23. package/lib/compiler/classes.js +2 -2
  24. package/lib/compiler/cycle-detector.js +6 -6
  25. package/lib/compiler/define.js +66 -45
  26. package/lib/compiler/extend.js +81 -72
  27. package/lib/compiler/finalize-parse-cdl.js +26 -26
  28. package/lib/compiler/generate.js +61 -45
  29. package/lib/compiler/index.js +47 -49
  30. package/lib/compiler/kick-start.js +8 -7
  31. package/lib/compiler/moduleLayers.js +1 -1
  32. package/lib/compiler/populate.js +42 -35
  33. package/lib/compiler/propagator.js +6 -6
  34. package/lib/compiler/resolve.js +170 -126
  35. package/lib/compiler/shared.js +122 -45
  36. package/lib/compiler/tweak-assocs.js +93 -40
  37. package/lib/compiler/utils.js +15 -12
  38. package/lib/edm/.eslintrc.json +40 -1
  39. package/lib/edm/annotations/genericTranslation.js +721 -707
  40. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  41. package/lib/edm/csn2edm.js +389 -378
  42. package/lib/edm/edm.js +678 -772
  43. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  44. package/lib/edm/edmInboundChecks.js +29 -27
  45. package/lib/edm/edmPreprocessor.js +686 -646
  46. package/lib/edm/edmUtils.js +277 -296
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +1 -1
  49. package/lib/gen/languageParser.js +1253 -1276
  50. package/lib/json/from-csn.js +34 -4
  51. package/lib/json/to-csn.js +4 -4
  52. package/lib/language/language.g4 +2 -5
  53. package/lib/main.d.ts +61 -1
  54. package/lib/model/csnUtils.js +31 -2
  55. package/lib/model/revealInternalProperties.js +1 -1
  56. package/lib/modelCompare/compare.js +37 -2
  57. package/lib/modelCompare/utils/filter.js +1 -1
  58. package/lib/optionProcessor.js +15 -3
  59. package/lib/render/toCdl.js +30 -4
  60. package/lib/render/toSql.js +5 -9
  61. package/lib/render/utils/common.js +8 -6
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/cdsPersistence.js +1 -1
  64. package/lib/transform/db/constraints.js +47 -17
  65. package/lib/transform/db/expansion.js +133 -50
  66. package/lib/transform/db/flattening.js +75 -7
  67. package/lib/transform/forOdata.js +4 -1
  68. package/lib/transform/forRelationalDB.js +80 -62
  69. package/lib/transform/localized.js +91 -54
  70. package/lib/transform/transformUtils.js +9 -10
  71. package/lib/utils/file.js +7 -7
  72. package/lib/utils/moduleResolve.js +210 -121
  73. package/lib/utils/objectUtils.js +1 -1
  74. package/package.json +5 -5
@@ -48,8 +48,8 @@ const {
48
48
  } = require('./utils');
49
49
  const { typeParameters } = require('./builtins');
50
50
 
51
- const $inferred = Symbol.for('cds.$inferred');
52
- const $location = Symbol.for('cds.$location');
51
+ const $inferred = Symbol.for( 'cds.$inferred' );
52
+ const $location = Symbol.for( 'cds.$location' );
53
53
 
54
54
  /**
55
55
  * These properties are copied from specified elements.
@@ -166,7 +166,7 @@ function populate( model ) {
166
166
  *
167
167
  * Calculating an effective association/composition implies calculating its
168
168
  * target entity (including redirections), but not induce calculating the
169
- * target's elements. Calculating an effective structure (entities, …) does
169
+ * target's elements. Calculating an effective structure (entities, …) does
170
170
  * not imply calculating the effective types of its elements. Calculating an
171
171
  * effective array does not imply calculating its effective line type.
172
172
  */
@@ -215,7 +215,7 @@ function populate( model ) {
215
215
  // console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
216
216
  extendArtifactAfter( a ); // after setting _effectiveType (for messages)
217
217
  if (a.typeProps$)
218
- setSpecifiedElementTypeProperties(a);
218
+ setSpecifiedElementTypeProperties( a );
219
219
  }
220
220
  // console.log( 'ET-END:', art?.kind, art?.name )
221
221
  return art;
@@ -393,7 +393,7 @@ function populate( model ) {
393
393
  proxyCopyMembers( art, 'elements', struct.elements, art.path?.location, '$navElement' );
394
394
  return true;
395
395
  }
396
- if (art.elements || art.kind === '$tableAlias' || art.kind === '$inline' ||
396
+ if (art.elements || art.kind === '$inline' ||
397
397
  // no element expansions for "non-proper" types like
398
398
  // entities (as parameter types) etc:
399
399
  struct.kind !== 'type' && struct.kind !== 'element' && struct.kind !== 'param' &&
@@ -407,7 +407,8 @@ function populate( model ) {
407
407
  const location = ref && ref.location || art.location;
408
408
  // console.log( message( null, location, art, {target:struct,art}, 'Info','EXPAND-ELEM')
409
409
  // .toString(), Object.keys(struct.elements))
410
- proxyCopyMembers( art, 'elements', struct.elements, weakLocation( location ) );
410
+ proxyCopyMembers( art, 'elements', struct.elements, weakLocation( location ),
411
+ null, isDeprecatedEnabled( options, 'noKeyPropagationWithExpansions' ) );
411
412
  // Set elements expansion status (the if condition is always true, as no
412
413
  // elements expansion will take place on artifact with existing other
413
414
  // member property):
@@ -541,7 +542,7 @@ function populate( model ) {
541
542
  }
542
543
  else if (typePropertiesFromSpecifiedElements[prop]) {
543
544
  if (!ielem.typeProps$)
544
- setLink(ielem, 'typeProps$', Object.create(null));
545
+ setLink( ielem, 'typeProps$', Object.create( null ) );
545
546
  // Note: At this point in time, effectiveType() was likely not called on the
546
547
  // element, yet. Setting it here, we can't compare it to its value from _origin.
547
548
  ielem.typeProps$[prop] = selem[prop];
@@ -550,14 +551,14 @@ function populate( model ) {
550
551
 
551
552
  selem.$replacement = true;
552
553
  if (selem.elements)
553
- setLink(ielem, 'elements$', selem.elements);
554
+ setLink( ielem, 'elements$', selem.elements );
554
555
  if (selem.enum)
555
- setLink(ielem, 'enum$', selem.enum);
556
+ setLink( ielem, 'enum$', selem.enum );
556
557
  }
557
558
  }
558
559
 
559
560
  if (wasAnnotated)
560
- setExpandStatusAnnotate(art, 'annotate');
561
+ setExpandStatusAnnotate( art, 'annotate' );
561
562
 
562
563
  // TODO: We don't check enum$, yet! We first need to fix expansion for
563
564
  // `cast(elem as EnumType)` (see #9421)
@@ -577,8 +578,8 @@ function populate( model ) {
577
578
  for (const prop in art.typeProps$) {
578
579
  let o = art;
579
580
  if (o._effectiveType !== 0) { // cyclic
580
- while (!o[prop] && getOrigin(o))
581
- o = getOrigin(o);
581
+ while (!o[prop] && getOrigin( o ))
582
+ o = getOrigin( o );
582
583
  }
583
584
 
584
585
  if (typePropertiesFromSpecifiedElements[prop] === 'if-undefined') {
@@ -595,7 +596,7 @@ function populate( model ) {
595
596
  if (query._combined || !query.from || !query.$tableAliases)
596
597
  // already done (TODO: re-check!) or $join query or parse error
597
598
  return query;
598
- setLink( query, '_combined', Object.create(null) );
599
+ setLink( query, '_combined', Object.create( null ) );
599
600
  query.$inlines = [];
600
601
  forEachGeneric( query, '$tableAliases', resolveTabRef );
601
602
  initFromColumns( query, query.columns );
@@ -621,7 +622,7 @@ function populate( model ) {
621
622
  forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
622
623
  if (elem.$duplicates !== true)
623
624
  dictAddArray( query._combined, name, elem, null ); // not dictAdd()
624
- });
625
+ } );
625
626
  }
626
627
  }
627
628
 
@@ -666,7 +667,7 @@ function populate( model ) {
666
667
  function initFromColumns( query, columns, inlineHead = undefined ) {
667
668
  const elemsParent = query.items || query;
668
669
  if (!inlineHead) {
669
- elemsParent.elements = Object.create(null);
670
+ elemsParent.elements = Object.create( null );
670
671
  if (query._main._leadingQuery === query) // never the case for 'expand'
671
672
  query._main.elements = elemsParent.elements;
672
673
  }
@@ -700,7 +701,7 @@ function populate( model ) {
700
701
  col.kind = 'element';
701
702
  dictAdd( elemsParent.elements, id, col, ( name, location ) => {
702
703
  error( 'duplicate-definition', [ location, query ], { name, '#': 'element' } );
703
- });
704
+ } );
704
705
  setMemberParent( col, id, query );
705
706
  }
706
707
  }
@@ -773,7 +774,7 @@ function populate( model ) {
773
774
  // col ($replacement set before *)
774
775
  // false if two cols have same name
775
776
  function wildcardSiblings( columns, query ) {
776
- const siblings = Object.create(null);
777
+ const siblings = Object.create( null );
777
778
  if (!columns)
778
779
  return siblings;
779
780
 
@@ -798,7 +799,7 @@ function populate( model ) {
798
799
  const { elements } = query.items || query;
799
800
  let location = wildcard.location || query.from && query.from.location || query.location;
800
801
  const inferred = query._main.$inferred;
801
- const excludingDict = (colParent || query).excludingDict || Object.create(null);
802
+ const excludingDict = (colParent || query).excludingDict || Object.create( null );
802
803
 
803
804
  const envParent = wildcard._pathHead; // TODO: rename _pathHead to _columnParent
804
805
  // console.log('S1:',location.line,location.col,
@@ -825,15 +826,15 @@ function populate( model ) {
825
826
  dictAdd( elements, name, sibling, ( _name, loc ) => {
826
827
  // there can be a definition from a previous inline with the same name:
827
828
  error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
828
- });
829
+ } );
829
830
  setMemberParent( sibling, name, query );
830
831
  }
831
832
  // else {
832
833
  // sibling.$inferred = 'query';
833
834
  // }
834
835
  }
835
- else if (Array.isArray(navElem)) {
836
- const names = navElem.filter( e => !e.$duplicates)
836
+ else if (Array.isArray( navElem )) {
837
+ const names = navElem.filter( e => !e.$duplicates )
837
838
  .map( e => `${ e.name.alias }.${ e.name.element }` );
838
839
  if (names.length) {
839
840
  error( 'wildcard-ambiguous', [ location, query ], { id: name, names },
@@ -848,7 +849,7 @@ function populate( model ) {
848
849
  dictAdd( elements, name, elem, ( _name, loc ) => {
849
850
  // there can be a definition from a previous inline with the same name:
850
851
  error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
851
- });
852
+ } );
852
853
  elem.$inferred = '*';
853
854
  elem.name.$inferred = '*';
854
855
  if (envParent)
@@ -895,7 +896,7 @@ function populate( model ) {
895
896
  if (!sibling.target || sibling.target.$inferred || // not explicit REDIRECTED TO
896
897
  path && path[path.length - 1].id !== sibling.name.id) { // or renamed
897
898
  const { id } = sibling.name;
898
- if (Array.isArray(navElem)) {
899
+ if (Array.isArray( navElem )) {
899
900
  // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
900
901
  info( 'wildcard-excluding-many', [ sibling.name.location, query ],
901
902
  { id, keyword: 'excluding' },
@@ -1000,6 +1001,12 @@ function populate( model ) {
1000
1001
 
1001
1002
  function redirectImplicitlyDo( elem, assoc, target, service ) {
1002
1003
  // console.log('ES:',elem.name.absolute,elem.name.element);
1004
+ if (assoc._main === target && elem._main?.kind === 'entity' &&
1005
+ elem._main?._ancestors?.includes( target )) {
1006
+ // source and target of the model association are the same entity, and
1007
+ // the current main artifact is a suitable auto-redirection target → return it
1008
+ return elem._main;
1009
+ }
1003
1010
  const elemScope = scopedRedirections && // null if no scoped redirections
1004
1011
  preferredElemScope( target, service, elem, assoc._main || assoc );
1005
1012
  const exposed = minimalExposure( target, service, elemScope );
@@ -1009,7 +1016,7 @@ function populate( model ) {
1009
1016
  if (isAutoExposed( target ))
1010
1017
  target = createAutoExposed( origTarget, service, elemScope );
1011
1018
  const desc = origTarget._descendants ||
1012
- setLink( origTarget, '_descendants', Object.create(null) );
1019
+ setLink( origTarget, '_descendants', Object.create( null ) );
1013
1020
  if (!desc[service.name.absolute]) // could be the target itself (no repeated msgs)!
1014
1021
  desc[service.name.absolute] = [ target ];
1015
1022
  else
@@ -1031,16 +1038,16 @@ function populate( model ) {
1031
1038
  std: 'Replace target $(TARGET) by one of $(SORTED_ARTS); can\'t auto-redirect this association if multiple projections exist in this service',
1032
1039
  // eslint-disable-next-line max-len
1033
1040
  two: 'Replace target $(TARGET) by $(SORTED_ARTS) or $(SECOND); can\'t auto-redirect this association if multiple projections exist in this service',
1034
- });
1041
+ } );
1035
1042
  // continuation semantics: no auto-redirection
1036
1043
  }
1037
1044
  else {
1038
1045
  // referred (and probably inferred) assoc (without a user-provided target at that place)
1039
1046
  // HINT: consider bin/cdsv2m.js when changing the following message text
1040
- // No grouped and sub messages yet (TODO v3): mention at all target places with all assocs
1047
+ // No grouped and sub messages yet (TODO v5): mention at all target places with all assocs
1041
1048
  const withAnno = annotationVal( exposed[0]['@cds.redirection.target'] );
1042
1049
  for (const proj of exposed) {
1043
- // TODO: def-ambiguous-target (just v3, as the current is infamous and used in options),
1050
+ // TODO: def-ambiguous-target (just v5, as the current is infamous and used in options),
1044
1051
  message( 'redirected-implicitly-ambiguous',
1045
1052
  [ weakLocation( proj.name.location ), proj ],
1046
1053
  {
@@ -1064,7 +1071,7 @@ function populate( model ) {
1064
1071
  return target;
1065
1072
  }
1066
1073
 
1067
- // Return projections of `target` in `service`. Shorted by
1074
+ // Return projections of `target` in `service`. Sorted by
1068
1075
  // - first, only consider projections with @cds.redirection.target=true
1069
1076
  // - exclude all indirect projections, i.e. those which are projection on others in list
1070
1077
  //
@@ -1081,7 +1088,7 @@ function populate( model ) {
1081
1088
  return exposed || [];
1082
1089
  let min = [];
1083
1090
  for (const e of exposed) {
1084
- if (min.every( m => m._ancestors?.includes( e ))) {
1091
+ if (min.every( m => m._ancestors?.includes( e ) )) {
1085
1092
  min = [ e ];
1086
1093
  }
1087
1094
  else if (min.length !== 1 || !e._ancestors?.includes( min[0] )) {
@@ -1228,7 +1235,7 @@ function populate( model ) {
1228
1235
  return `${ service.name.absolute }.${ absolute }`;
1229
1236
  const base = definitionScope( target );
1230
1237
  if (base === target)
1231
- return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf('.') + 1 ) }`;
1238
+ return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf( '.' ) + 1 ) }`;
1232
1239
  // for scoped (e.g. calculated) entities, use exposed name of base:
1233
1240
  const exposed = minimalExposure( base, service, elemScope );
1234
1241
  // console.log(exposed.map( a => a.name.absolute ));
@@ -1300,18 +1307,18 @@ function populate( model ) {
1300
1307
  };
1301
1308
  // forward target parameters to projection
1302
1309
  if (target.params) {
1303
- art.params = Object.create(null);
1310
+ art.params = Object.create( null );
1304
1311
  // is art.query.from.path[0].$syntax: ':' required?
1305
- art.query.from.path[0].args = Object.create(null);
1306
- forEachGeneric(target, 'params', (p, pn) => {
1307
- art.params[pn] = linkToOrigin(p, pn, art, 'params', p.location);
1312
+ art.query.from.path[0].args = Object.create( null );
1313
+ forEachGeneric( target, 'params', (p, pn) => {
1314
+ art.params[pn] = linkToOrigin( p, pn, art, 'params', p.location );
1308
1315
  art.query.from.path[0].args[pn] = {
1309
1316
  name: { id: p.name.id, location: p.location },
1310
1317
  location: p.location,
1311
1318
  scope: 'param',
1312
1319
  path: [ { id: pn, location: p.location } ],
1313
1320
  };
1314
- });
1321
+ } );
1315
1322
  }
1316
1323
  // TODO: do we need to tag the generated entity with elemScope = 'auto'?
1317
1324
  if (autoexposed) {
@@ -20,7 +20,7 @@ const {
20
20
  withAssociation,
21
21
  viewFromPrimary,
22
22
  } = require('./utils');
23
- const $inferred = Symbol.for('cds.$inferred');
23
+ const $inferred = Symbol.for( 'cds.$inferred' );
24
24
  // const { refString } = require( '../base/messages')
25
25
 
26
26
  // Note that propagation here is also used for deep-copying (function `onlyViaParent`)
@@ -104,7 +104,7 @@ function propagate( model ) {
104
104
  if (target._calcOrigin?._origin && target.value?._artifact) {
105
105
  chain.push({ target, source: target.value._artifact });
106
106
  if (checkAndSetStatus( target.value._artifact ))
107
- news.push(target.value._artifact);
107
+ news.push( target.value._artifact );
108
108
 
109
109
  if (target.value?._artifact.$inferred !== 'include') {
110
110
  // If the referred to element is not inferred, it is a new one and not the original.
@@ -242,11 +242,11 @@ function propagate( model ) {
242
242
  target.location ||
243
243
  target._outer && target._outer.location;
244
244
  const dict = source[prop];
245
- target[prop] = Object.create(null); // also propagate empty elements
245
+ target[prop] = Object.create( null ); // also propagate empty elements
246
246
  for (const name in dict) {
247
247
  const member = linkToOrigin( dict[name], name, target, prop, location );
248
248
  member.$inferred = 'proxy';
249
- setEffectiveType(member, dict[name]);
249
+ setEffectiveType( member, dict[name] );
250
250
  }
251
251
  target[prop][$inferred] = 'prop';
252
252
  }
@@ -286,7 +286,7 @@ function propagate( model ) {
286
286
  if (target.kind === 'param' && source.kind === 'entity')
287
287
  return; // Don't propagate from entity types to parameters (+ return type).
288
288
  if (target.kind)
289
- always(prop, target, source); // not in 'items'
289
+ always( prop, target, source ); // not in 'items'
290
290
  }
291
291
 
292
292
  function notNull( prop, target, source, viaType ) {
@@ -379,7 +379,7 @@ function checkAndSetStatus( art ) {
379
379
 
380
380
  function setEffectiveType( target, source ) {
381
381
  if (source._effectiveType !== undefined)
382
- setLink( target, '_effectiveType', source._effectiveType);
382
+ setLink( target, '_effectiveType', source._effectiveType );
383
383
  }
384
384
 
385
385
  module.exports = {