@sap/cds-compiler 6.6.0 → 6.7.1

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 (45) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/bin/cdsc.js +2 -0
  3. package/bin/cdsse.js +1 -1
  4. package/lib/base/message-registry.js +6 -7
  5. package/lib/base/model.js +0 -72
  6. package/lib/checks/elements.js +1 -1
  7. package/lib/checks/featureFlags.js +2 -2
  8. package/lib/compiler/assert-consistency.js +3 -4
  9. package/lib/compiler/base.js +8 -0
  10. package/lib/compiler/builtins.js +8 -9
  11. package/lib/compiler/checks.js +27 -6
  12. package/lib/compiler/cycle-detector.js +4 -4
  13. package/lib/compiler/define.js +65 -83
  14. package/lib/compiler/extend.js +357 -325
  15. package/lib/compiler/finalize-parse-cdl.js +3 -4
  16. package/lib/compiler/generate.js +205 -203
  17. package/lib/compiler/kick-start.js +34 -49
  18. package/lib/compiler/populate.js +95 -28
  19. package/lib/compiler/propagator.js +3 -5
  20. package/lib/compiler/resolve.js +17 -13
  21. package/lib/compiler/shared.js +47 -19
  22. package/lib/compiler/tweak-assocs.js +2 -4
  23. package/lib/compiler/utils.js +84 -31
  24. package/lib/gen/BaseParser.js +924 -1055
  25. package/lib/gen/CdlGrammar.checksum +1 -1
  26. package/lib/gen/CdlParser.js +5 -2
  27. package/lib/json/from-csn.js +25 -16
  28. package/lib/main.d.ts +13 -0
  29. package/lib/model/revealInternalProperties.js +18 -0
  30. package/lib/parsers/AstBuildingParser.js +22 -5
  31. package/lib/render/toHdbcds.js +2 -2
  32. package/lib/render/utils/sql.js +2 -2
  33. package/lib/render/utils/standardDatabaseFunctions.js +2 -2
  34. package/lib/transform/db/constraints.js +3 -4
  35. package/lib/transform/db/killAnnotations.js +1 -1
  36. package/lib/transform/db/processSqlServices.js +10 -11
  37. package/lib/transform/effective/associations.js +1 -1
  38. package/lib/transform/forOdata.js +7 -124
  39. package/lib/transform/odata/fioriTreeViews.js +173 -0
  40. package/lib/transform/odata/flattening.js +2 -2
  41. package/lib/transform/translateAssocsToJoins.js +7 -4
  42. package/package.json +1 -1
  43. package/share/messages/message-explanations.json +0 -2
  44. package/share/messages/type-unexpected-foreign-keys.md +0 -52
  45. package/share/messages/type-unexpected-on-condition.md +0 -52
@@ -120,17 +120,13 @@
120
120
 
121
121
  'use strict';
122
122
 
123
- const {
124
- forEachGeneric,
125
- forEachInOrder,
126
- forEachMember,
127
- } = require('../base/model');
128
- const { weakLocation } = require('../base/location');
123
+ const { weakLocation, builtinLocation } = require('../base/location');
124
+
129
125
  const shuffleGen = require('../base/shuffle');
130
126
  const {
131
127
  dictAdd, dictAddArray, dictForEach, pushToDict,
132
128
  } = require('../base/dictionaries');
133
- const { kindProperties, dictKinds } = require('./base');
129
+ const { kindProperties } = require('./base');
134
130
  const {
135
131
  setLink,
136
132
  initItemsLinks,
@@ -142,14 +138,14 @@ const {
142
138
  initBoundSelfParam,
143
139
  dependsOnSilent,
144
140
  pathName,
145
- targetCantBeAspect,
141
+ forEachGeneric,
142
+ forEachInOrder,
146
143
  } = require('./utils');
147
144
  const { compareLayer } = require('./moduleLayers');
148
145
  const { initBuiltins } = require('./builtins');
149
146
  const { isInReservedNamespace } = require('../base/builtins');
150
147
 
151
148
  const $location = Symbol.for( 'cds.$location' );
152
- const $inferred = Symbol.for( 'cds.$inferred' );
153
149
 
154
150
  /**
155
151
  * Export function of this file. Transform argument `sources` = dictionary of
@@ -178,10 +174,9 @@ function define( model ) {
178
174
  shuffleDict,
179
175
  shuffleArray,
180
176
  initMainArtifact,
181
- initMembers, // for finalize-parser-cdl.js
182
- targetIsTargetAspect,
177
+ initArtifactParentLink,
183
178
  checkRedefinition,
184
- initSelectItems,
179
+ createGapArtifact,
185
180
  } );
186
181
  return doDefine();
187
182
 
@@ -200,7 +195,6 @@ function define( model ) {
200
195
  }
201
196
  model.definitions = Object.create( null );
202
197
  setLink( model, '_entities', [] ); // for entities with includes
203
- model.$entity = 0;
204
198
  model.$compositionTargets = Object.create( null );
205
199
  model.$collectedExtensions = Object.create( null );
206
200
 
@@ -404,7 +398,7 @@ function define( model ) {
404
398
  const absolute = ext.name && resolveUncheckedPath( ext.name, '_uncheckedExtension', ext );
405
399
  if (!absolute) // broken path
406
400
  return;
407
- delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
401
+ delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1 - TODO: still?
408
402
  ext.name.id = absolute; // definition might not be there yet, no _artifact link
409
403
  const location = { file: '' }; // stupid required location
410
404
  const late = model.$collectedExtensions[absolute] ||
@@ -529,12 +523,13 @@ function define( model ) {
529
523
  initArtifactParentLink( art, model.definitions );
530
524
  checkRedefinition( art );
531
525
  initDollarSelf( art ); // $self, TODO: also for 'namespace'?
532
- initMembers( art );
533
526
  if (art.params)
534
527
  initDollarParameters( art ); // $parameters
528
+ initMembers( art );
529
+
535
530
  if (art.query) {
536
531
  initArtifactQuery( art );
537
- restrictToSimpleProjection( art );
532
+ restrictToSimpleProjection( art ); // late syntax check, TODO: do in parsers
538
533
  }
539
534
  }
540
535
 
@@ -551,38 +546,23 @@ function define( model ) {
551
546
  }
552
547
 
553
548
  function initExtension( parent ) {
554
- // TODO: re-think
555
- forEachMember( parent, function initExtensionMember( sub, name, prop ) {
556
- if (sub.kind !== 'extend' && sub.kind !== 'annotate')
557
- return; // for defs inside, set somewhere else - TODO: rethink
558
- if (prop === 'params' && name === '') // RETURNS
559
- sub.name = { id: '', location: sub.location };
560
- setLink( sub, '_block', parent._block );
561
- initExprAnnoBlock( sub, parent._block );
562
- setLink( sub, '_parent', parent );
563
- setLink( sub, '_main', parent._main || parent );
564
- initExtension( sub );
565
- } );
566
- if (parent.kind !== 'extend')
567
- return;
568
- // TODO: sub queries? expand/inline?
569
- parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
570
- if (parent.scale && !parent.precision) {
571
- // TODO: where could we store the location of the name?
572
- error( 'syntax-missing-type-property', [ parent.scale.location ],
573
- { prop: 'scale', otherprop: 'precision' },
574
- 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
575
- parent.scale = undefined; // no consequential error
576
- }
549
+ initMembers( parent );
550
+ if (parent.columns)
551
+ initSelectItems( parent, parent.columns, parent, true );
552
+ // there are no sub queries in extensions yet
553
+ // Remark: sub queries not yet allowed in `extend … with columns`
577
554
  }
578
555
 
556
+ //-----------------------------------------------------------------------------
557
+
579
558
  /**
580
559
  * Make the `_parent` pointer of artifact `A.B.C` point to `A.B`. If no `A.B`
581
560
  * exists in XSN.definitions, create an artifact with kind `namespace` (better
582
- * would be `$gap`). Add `A.B.C` to the `_subArtifacts` dictionary of `A.B`.
561
+ * would be `$gap`). Add `A.B.C` to the `_subArtifacts` dictionary of `A.B`
562
+ * if `noSubArtifacts` is not truthy (not for gap artifacts).
583
563
  * Do recursively if necessary, `_parent` of `A.B` is `A` in this example.
584
564
  */
585
- function initArtifactParentLink( art, definitions, path, pathIndex ) {
565
+ function initArtifactParentLink( art, definitions, path, pathIndex, noSubArtifacts ) {
586
566
  setLink( art, '_parent', null );
587
567
  const { id } = art.name;
588
568
  const dot = id.lastIndexOf( '.' );
@@ -592,7 +572,7 @@ function define( model ) {
592
572
  let parent = definitions[prefix];
593
573
  if (!parent) {
594
574
  path ??= art.name.path;
595
- pathIndex ??= path?.length - 1;
575
+ pathIndex ??= path?.length - 1; // just used for location
596
576
  const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
597
577
  const location = weakLocation( pathItemOrName.location );
598
578
  parent = { kind: 'namespace', name: { id: prefix, location }, location };
@@ -600,6 +580,8 @@ function define( model ) {
600
580
  initArtifactParentLink( parent, definitions, path, pathIndex );
601
581
  }
602
582
  setLink( art, '_parent', parent );
583
+ if (noSubArtifacts)
584
+ return;
603
585
  if (!parent._subArtifacts)
604
586
  setLink( parent, '_subArtifacts', Object.create( null ) );
605
587
  if (art.$duplicates !== true) // no redef or "first def"
@@ -1059,29 +1041,31 @@ function define( model ) {
1059
1041
  /**
1060
1042
  * Set property `_parent` for all elements in `parent` to `parent` and do so
1061
1043
  * recursively for all sub elements.
1062
- *
1063
- * Param `initExtensions` is for parse.cdl - TODO delete
1064
1044
  */
1065
1045
  function initMembers( parent ) {
1046
+ // TODO: combine with initMembers() - better structuring
1066
1047
  const block = parent._block;
1067
- let obj = initItemsLinks( parent, block );
1068
1048
  initExprAnnoBlock( parent, block );
1069
- if (obj.target && targetIsTargetAspect( obj )) {
1049
+ const obj = initItemsLinks( parent, block ); // down many / array of
1050
+
1051
+ if (obj.scale && !obj.precision &&
1052
+ (parent._main || parent).kind === 'extend') { // TODO v7: why just with extend?
1053
+ // TODO: where could we store the location of the name?
1054
+ error( 'syntax-missing-type-property', [ obj.scale.location ],
1055
+ { prop: 'scale', otherprop: 'precision' },
1056
+ 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
1057
+ }
1058
+
1059
+ if (obj.target?.elements) {
1060
+ // in kick-start.js, we consider a target refererence to an aspect
1070
1061
  obj.targetAspect = obj.target;
1071
1062
  delete obj.target;
1072
1063
  }
1073
- const { targetAspect } = obj;
1074
- if (targetAspect) {
1075
- if (obj.foreignKeys) {
1076
- error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], parent ] );
1077
- delete obj.foreignKeys; // continuation semantics: not specified
1078
- }
1079
- if (obj.on && !obj.target) {
1080
- error( 'type-unexpected-on-condition', [ obj.on.location, parent ] );
1081
- delete obj.on; // continuation semantics: not specified
1082
- }
1083
- if (targetAspect.elements) // eslint-disable-next-line no-multi-assign
1084
- parent = obj = initAnonymousAspect( parent, obj, targetAspect );
1064
+ if (obj.targetAspect?.elements) {
1065
+ initAnonymousAspect( parent, obj, obj.targetAspect );
1066
+ forEachInOrder( obj.targetAspect, 'elements',
1067
+ (...args) => initArtifact( obj.targetAspect, ...args ) );
1068
+ return;
1085
1069
  }
1086
1070
  forEachInOrder( obj, 'elements', (...args) => initArtifact( parent, ...args ) );
1087
1071
  forEachGeneric( obj, 'enum', (...args) => initArtifact( parent, ...args ) );
@@ -1107,7 +1091,7 @@ function define( model ) {
1107
1091
 
1108
1092
  targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1109
1093
  setLink( targetAspect, '_block', parent._block );
1110
- initDollarSelf( targetAspect );
1094
+ initDollarSelf( targetAspect ); // not with extend
1111
1095
  // allow ref of up_ in anonymous aspect inside entity
1112
1096
  // (TODO: complain if used and the managed composition is included into
1113
1097
  // another entity - might induce auto-redirection):
@@ -1131,9 +1115,8 @@ function define( model ) {
1131
1115
  return targetAspect;
1132
1116
  }
1133
1117
 
1134
- function initArtifact( parent, elem, name, prop ) {
1135
- if (!elem.kind) // wrong CSN input
1136
- elem.kind = dictKinds[prop];
1118
+ // TODO: combine with initMembers()
1119
+ function initArtifact( parent, elem, name ) {
1137
1120
  if (!elem.name && !elem._outer) {
1138
1121
  const ref = elem.targetElement || elem.kind === 'element' && elem.value;
1139
1122
  if (ref && ref.path) {
@@ -1157,9 +1140,9 @@ function define( model ) {
1157
1140
 
1158
1141
  // for a correct home path, setMemberParent needed to be called
1159
1142
 
1160
- if (!elem.value || elem.kind !== 'element' ||
1161
- elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1162
- return;
1143
+ if (!elem.value || elem.kind !== 'element' || elem.$syntax === 'or-enum')
1144
+ return; // not an element with value
1145
+ // remark: $syntax: 'or-enum' is set in CDL+CSN parser for potential enums
1163
1146
  // -> it's a calculated element
1164
1147
  if (!elem.type && elem.value.type) { // top-level CAST( expr AS type )
1165
1148
  if (!elem.target)
@@ -1180,25 +1163,24 @@ function define( model ) {
1180
1163
  }
1181
1164
 
1182
1165
  /**
1183
- * Return whether the `target` is actually a `targetAspect`
1166
+ * Create "gap" artifact (kind: "namespace") for references which might turn out
1167
+ * to refer to a compiler-generated texts or target entity later.
1168
+ *
1169
+ * A _parent link is set as usual, but the parent does not list the gap artifact
1170
+ * in its _subArtifacts (to allow more precise and processing
1171
+ * sequence-independent resolve errors). When the gap artifact is replaced by
1172
+ * the generated entity, it is added to the _subArtifacts of the parent.
1184
1173
  */
1185
- function targetIsTargetAspect( elem ) {
1186
- const { target } = elem;
1187
- if (target.elements) // CSN parser ensures: has no targetAspect then
1188
- return true;
1189
-
1190
- if (elem.targetAspect) {
1191
- // Ensure that a compiled CSN is parseable - not inside query, only on element
1192
- return false;
1193
- }
1194
- if (targetCantBeAspect( elem ) || options.parseCdl)
1195
- return false;
1196
- // Compare this check with check in acceptEntity() called by resolvePath()
1197
- // Remark: do not check `on` and `foreignKeys` here, we want error for those, not the aspect
1198
- const name = resolveUncheckedPath( target, 'target', elem );
1199
- const aspect = name && model.definitions[name];
1200
- return (aspect?.kind === 'aspect' || aspect?.kind === 'type') && // type is sloppy
1201
- aspect.elements && !aspect.elements[$inferred];
1174
+ function createGapArtifact( name, location = builtinLocation() ) {
1175
+ // TODO: make it work without location (or value undefined/null)
1176
+ const art = {
1177
+ kind: 'namespace', name: { id: name, location }, location,
1178
+ };
1179
+ model.definitions[name] = art;
1180
+ // set _parent link, but do not add in _subArtifacts
1181
+ // (TODO: in the future, test better for "valid" completion, and add anyway.)
1182
+ initArtifactParentLink( art, model.definitions, undefined, undefined, true );
1183
+ return art;
1202
1184
  }
1203
1185
  }
1204
1186