@sap/cds-compiler 3.7.2 → 3.8.2

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 (70) hide show
  1. package/CHANGELOG.md +71 -4
  2. package/bin/cdsc.js +3 -0
  3. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  4. package/doc/CHANGELOG_BETA.md +15 -0
  5. package/doc/DeprecatedOptions_v2.md +1 -1
  6. package/doc/NameResolution.md +1 -1
  7. package/lib/api/main.js +61 -22
  8. package/lib/api/options.js +1 -0
  9. package/lib/api/validate.js +5 -0
  10. package/lib/base/dictionaries.js +5 -3
  11. package/lib/base/keywords.js +2 -0
  12. package/lib/base/message-registry.js +64 -22
  13. package/lib/base/messages.js +12 -7
  14. package/lib/base/model.js +3 -2
  15. package/lib/checks/arrayOfs.js +1 -1
  16. package/lib/checks/defaultValues.js +1 -1
  17. package/lib/checks/hasPersistedElements.js +1 -1
  18. package/lib/checks/invalidTarget.js +1 -1
  19. package/lib/checks/onConditions.js +9 -6
  20. package/lib/checks/sql-snippets.js +2 -2
  21. package/lib/checks/types.js +1 -2
  22. package/lib/compiler/assert-consistency.js +25 -6
  23. package/lib/compiler/base.js +51 -2
  24. package/lib/compiler/builtins.js +15 -6
  25. package/lib/compiler/checks.js +4 -4
  26. package/lib/compiler/define.js +59 -80
  27. package/lib/compiler/extend.js +717 -498
  28. package/lib/compiler/finalize-parse-cdl.js +4 -3
  29. package/lib/compiler/index.js +1 -1
  30. package/lib/compiler/kick-start.js +2 -2
  31. package/lib/compiler/populate.js +17 -9
  32. package/lib/compiler/propagator.js +12 -5
  33. package/lib/compiler/resolve.js +26 -173
  34. package/lib/compiler/shared.js +20 -58
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +2 -2
  37. package/lib/edm/annotations/genericTranslation.js +124 -46
  38. package/lib/edm/csn2edm.js +22 -1
  39. package/lib/edm/edmPreprocessor.js +41 -21
  40. package/lib/gen/Dictionary.json +4 -0
  41. package/lib/gen/language.checksum +1 -1
  42. package/lib/gen/language.interp +3 -1
  43. package/lib/gen/languageLexer.js +1 -1
  44. package/lib/gen/languageParser.js +4844 -4508
  45. package/lib/inspect/inspectPropagation.js +20 -36
  46. package/lib/json/from-csn.js +56 -7
  47. package/lib/json/to-csn.js +71 -110
  48. package/lib/language/errorStrategy.js +1 -0
  49. package/lib/language/genericAntlrParser.js +49 -9
  50. package/lib/language/language.g4 +106 -83
  51. package/lib/language/textUtils.js +13 -0
  52. package/lib/main.d.ts +43 -3
  53. package/lib/main.js +4 -2
  54. package/lib/model/csnRefs.js +19 -4
  55. package/lib/model/csnUtils.js +11 -74
  56. package/lib/model/revealInternalProperties.js +3 -0
  57. package/lib/optionProcessor.js +3 -0
  58. package/lib/render/toCdl.js +203 -104
  59. package/lib/render/toHdbcds.js +0 -1
  60. package/lib/render/toRename.js +14 -51
  61. package/lib/transform/braceExpression.js +6 -0
  62. package/lib/transform/db/rewriteCalculatedElements.js +55 -14
  63. package/lib/transform/forOdataNew.js +20 -15
  64. package/lib/transform/forRelationalDB.js +21 -14
  65. package/lib/transform/parseExpr.js +2 -0
  66. package/lib/transform/transformUtilsNew.js +36 -9
  67. package/lib/transform/translateAssocsToJoins.js +11 -4
  68. package/lib/transform/universalCsn/coreComputed.js +15 -7
  69. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  70. package/package.json +2 -1
@@ -119,7 +119,11 @@
119
119
 
120
120
  'use strict';
121
121
 
122
- const { forEachGeneric, forEachInOrder, isBetaEnabled } = require('../base/model');
122
+ const {
123
+ forEachGeneric,
124
+ forEachInOrder,
125
+ forEachMember,
126
+ } = require('../base/model');
123
127
  const shuffleGen = require('../base/shuffle');
124
128
  const {
125
129
  dictAdd, dictAddArray, dictForEach, pushToDict,
@@ -132,7 +136,6 @@ const {
132
136
  dependsOnSilent,
133
137
  pathName,
134
138
  splitIntoPath,
135
- annotationHasEllipsis,
136
139
  isDirectComposition,
137
140
  } = require('./utils');
138
141
  const { compareLayer } = require('./moduleLayers');
@@ -156,22 +159,19 @@ function define( model ) {
156
159
  const { options } = model;
157
160
  // Get simplified "resolve" functionality and the message function:
158
161
  const {
159
- error, warning, info, message, messages,
162
+ error, warning, info, messages,
160
163
  } = model.$messageFunctions;
161
164
  const {
162
165
  resolveUncheckedPath,
163
166
  } = model.$functions;
164
167
  const { shuffleDict, shuffleArray } = shuffleGen( options.testMode );
165
168
 
166
- const extensionsDict = Object.create(null);
167
169
  Object.assign( model.$functions, {
168
170
  shuffleDict,
169
171
  shuffleArray,
170
172
  initArtifact,
171
173
  initMembers,
172
- extensionsDict, // a dictionary - TODO: put directly into model?
173
- checkDefinitions,
174
- initAnnotations,
174
+ checkDefinitions, // TODO: remove
175
175
  } );
176
176
  // During the definer, we can only resolve artifact references, i.e,
177
177
  // after a `.`, we only search in the `_subArtifacts` dictionary:
@@ -201,7 +201,7 @@ function define( model ) {
201
201
  setLink( model, '_entities', [] ); // for entities with includes
202
202
  model.$entity = 0;
203
203
  model.$compositionTargets = Object.create(null);
204
- model.$lateExtensions = Object.create(null); // for generated artifacts
204
+ model.$lateExtensions = Object.create(null); // TODO: rename to $collectedExtensions
205
205
 
206
206
  initBuiltins( model );
207
207
  const sourceNames = shuffleArray( Object.keys( model.sources ) );
@@ -211,7 +211,7 @@ function define( model ) {
211
211
  initNamespaceAndUsing( model.sources[name] );
212
212
  dictForEach( model.definitions, initArtifact );
213
213
  dictForEach( model.vocabularies, initVocabulary );
214
- dictForEach( extensionsDict, initExtension );
214
+ dictForEach( model.$lateExtensions, e => e._extensions.forEach( initExtension ) );
215
215
 
216
216
  addI18nBlocks();
217
217
  }
@@ -397,19 +397,52 @@ function define( model ) {
397
397
  return;
398
398
  delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
399
399
  ext.name.absolute = absolute; // definition might not be there yet, no _artifact link
400
- pushToDict( extensionsDict, absolute, ext );
400
+ const location = { file: '' }; // stupid required location
401
+ const late = model.$lateExtensions[absolute] ||
402
+ (model.$lateExtensions[absolute] = {
403
+ kind: 'annotate',
404
+ name: { absolute, location },
405
+ $inferred: '',
406
+ location,
407
+ });
408
+ pushToDict( late, '_extensions', ext );
401
409
  if (!ext.artifacts)
402
410
  return;
411
+
403
412
  // Directly add the artifacts of context and service extension:
404
413
  if (!model.$blocks)
405
414
  model.$blocks = Object.create( null );
406
415
  // Set block number for debugging (--raw-output):
407
416
  // eslint-disable-next-line no-multi-assign
408
417
  ext.name.select = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
418
+ // add "namespace" for the case that ext.artifacts is empty (TODO: later)
419
+ // now add all definitions in ext.artifacts:
409
420
  const prefix = `${ absolute }.`;
410
421
  dictForEach( ext.artifacts, a => addArtifact( a, ext, prefix ) );
411
422
  }
412
423
 
424
+ function initExtension( parent ) {
425
+ forEachMember( parent, function init( sub ) {
426
+ if (sub.kind !== 'extend' && sub.kind !== 'annotate')
427
+ return; // for defs inside, set somewhere else - TODO: rethink
428
+ setLink( sub, '_block', parent._block );
429
+ setLink( sub, '_parent', parent );
430
+ setLink( sub, '_main', parent._main || parent );
431
+ initExtension( sub );
432
+ } );
433
+ if (parent.kind !== 'extend')
434
+ return;
435
+ if (parent.columns) // TODO: sub queries? expand/inline?
436
+ parent.columns.forEach( c => setLink( c, '_block', parent._block ) );
437
+ if (parent.scale && !parent.precision) {
438
+ // TODO: where could we store the location of the name?
439
+ error( 'syntax-missing-type-property', [ parent.scale.location ],
440
+ { prop: 'scale', otherprop: 'precision' },
441
+ 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
442
+ parent.scale = undefined; // no consequential error
443
+ }
444
+ }
445
+
413
446
  function addVocabulary( vocab, block, prefix ) {
414
447
  setLink( vocab, '_block', block );
415
448
  const { name } = vocab;
@@ -533,13 +566,10 @@ function define( model ) {
533
566
  initArtifactParentLink( art, model.definitions );
534
567
  const block = art._block;
535
568
  checkRedefinition( art );
536
- initAnnotations( art, block );
537
569
  initMembers( art, art, block );
538
570
  initDollarSelf( art ); // $self
539
571
  if (art.params)
540
572
  initDollarParameters( art );
541
- if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
542
- extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
543
573
 
544
574
  if (!art.query)
545
575
  return;
@@ -560,7 +590,6 @@ function define( model ) {
560
590
  initArtifactParentLink( art, model.vocabularies );
561
591
  checkRedefinition( art );
562
592
  const block = art._block;
563
- initAnnotations( art, block );
564
593
  initMembers( art, art, block );
565
594
  }
566
595
 
@@ -586,57 +615,6 @@ function define( model ) {
586
615
  parent._subArtifacts[absolute.substring( dot + 1 )] = art; // not dictAdd()
587
616
  }
588
617
 
589
- /** Initialize the extension `ext`.
590
- *
591
- * Currently:
592
- *
593
- * - initialize annotations (set _block, $priority, `...` check) on "main"
594
- * extension and its columns do more later
595
- * - for members in compile(): init annotations via extendMembers/annotateMembers
596
- * - for members in parse.cdl(): init annotation via initMembers
597
- *
598
- * In the future (after name cleanup): -- TODO --
599
- *
600
- * - also initialize members and member extensions/annotations here
601
- * - we might also do other things, like calculating whether an `extend` is
602
- * `annotate`-like, i.e. only contains name-resolution irrelevant extensions.
603
- */
604
- function initExtension( ext ) {
605
- const block = ext._block;
606
- initAnnotations( ext, block, ext.kind );
607
- if (ext.columns) // the columns themselves are "definitions"
608
- ext.columns.forEach( col => initAnnotations( col, block ) );
609
- }
610
-
611
- // Set _block links for annotations (necessary for layering) and do a late
612
- // syntax check (`...` only with extensions, not definitions).
613
- // extKind is either ext.kind (=art is extension) or false (=art is not an extension)
614
- function initAnnotations( art, block, extKind = false ) {
615
- // TODO: think of removing $priority, then
616
- // no _block: define, _block: annotate/extend/edmx
617
- // would fit with extending defs with props like length
618
- for (const prop in art) {
619
- if (prop.charAt(0) === '@' || prop === 'doc') {
620
- const anno = art[prop];
621
- // TODO: make anno never be an array, see addAnnotation() in genericAntlrParser
622
- if (Array.isArray( anno ))
623
- anno.forEach( init );
624
- else
625
- init( anno );
626
- }
627
- }
628
- return;
629
-
630
- function init( anno ) {
631
- setLink( anno, '_block', block );
632
- anno.$priority = extKind;
633
- if (!extKind && annotationHasEllipsis( anno )) {
634
- error( 'anno-unexpected-ellipsis',
635
- [ anno.name.location, art ], { code: '...' } );
636
- }
637
- }
638
- }
639
-
640
618
  // Init special things: -------------------------------------------------------
641
619
 
642
620
  function initDollarSelf( art ) {
@@ -772,10 +750,11 @@ function define( model ) {
772
750
  // (tab refs on the right of union are unnecessary)
773
751
  }
774
752
  else if (table.query) {
775
- if (!table.name || !table.name.id) {
776
- error( 'query-req-alias', [ table.location, query ], {}, // TODO: not subquery.location ?
777
- 'Table alias is required for this subquery' );
778
- return;
753
+ if (!table.name?.id) {
754
+ // We don't worry about duplicate names here.
755
+ const id = `$_select_${ query._main.$queries.length + 1 }__`;
756
+ table.name = { id, location: table.location, $inferred: '$internal' };
757
+ table.$inferred = '$internal';
779
758
  }
780
759
  addAsAlias();
781
760
  // Store _origin to leading query of table.query for name resolution
@@ -822,8 +801,15 @@ function define( model ) {
822
801
  table.kind = '$tableAlias';
823
802
  setMemberParent( table, table.name.id, query );
824
803
  setLink( table, '_block', query._block );
825
- dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
826
- error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
804
+ dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
805
+ if (tableAlias.$inferred === '$internal') {
806
+ const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
807
+ error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
808
+ { '#': 'duplicate', code: 'as ‹alias›' });
809
+ }
810
+ else {
811
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
812
+ }
827
813
  } );
828
814
  // also add to JOIN nodes for name restrictions:
829
815
  for (const p of joinParents) {
@@ -831,7 +817,7 @@ function define( model ) {
831
817
  // already used for duplicate aliases of queries:
832
818
  dictAddArray( p.$tableAliases, table.name.id, table );
833
819
  }
834
- if (table.name.id[0] === '$') {
820
+ if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
835
821
  warning( 'name-invalid-dollar-alias', [ table.name.location, table ], {
836
822
  '#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
837
823
  name: '$',
@@ -934,7 +920,6 @@ function define( model ) {
934
920
  // Either expression (value), expand or new association (target && type)
935
921
  else if (col.value || col.expand || (col.target && col.type)) {
936
922
  setLink( col, '_block', parent._block );
937
- initAnnotations( col, parent._block );
938
923
  if (col.inline) { // `@anno elem.{ * }` does not work
939
924
  if (col.doc) {
940
925
  warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
@@ -1009,7 +994,7 @@ function define( model ) {
1009
994
  *
1010
995
  * If not for extensions: construct === parent
1011
996
  *
1012
- * Param `initExtensions` is for parse.cdl
997
+ * Param `initExtensions` is for parse.cdl - TODO delete
1013
998
  *
1014
999
  * TODO: separate extension!
1015
1000
  */
@@ -1166,7 +1151,6 @@ function define( model ) {
1166
1151
  setMemberParent( elem, name, parent, add && prop );
1167
1152
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1168
1153
  checkRedefinition( elem );
1169
- initAnnotations( elem, bl );
1170
1154
  initMembers( elem, elem, bl, initExtensions );
1171
1155
  if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
1172
1156
  initBoundSelfParam( elem.params );
@@ -1181,11 +1165,6 @@ function define( model ) {
1181
1165
  if (!elem.target)
1182
1166
  elem.type = { ...elem.value.type, $inferred: 'cast' };
1183
1167
  }
1184
- if (!isBetaEnabled( options, 'calculatedElements' )) {
1185
- const loc = [ elem.value.location, elem ];
1186
- // TODO: this could be considered a syntax check
1187
- message( 'def-unsupported-calc-elem', loc, { '#': 'std' } );
1188
- }
1189
1168
  elem.$syntax = 'calc';
1190
1169
  }
1191
1170
  }