@sap/cds-compiler 3.5.4 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +56 -2
  2. package/bin/cdsc.js +14 -6
  3. package/doc/CHANGELOG_ARCHIVE.md +10 -10
  4. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  5. package/lib/api/main.js +32 -55
  6. package/lib/api/validate.js +5 -0
  7. package/lib/base/message-registry.js +104 -32
  8. package/lib/base/messages.js +277 -212
  9. package/lib/base/optionProcessorHelper.js +9 -2
  10. package/lib/base/shuffle.js +50 -0
  11. package/lib/checks/actionsFunctions.js +37 -20
  12. package/lib/checks/foreignKeys.js +13 -6
  13. package/lib/checks/nonexpandableStructured.js +1 -2
  14. package/lib/checks/onConditions.js +21 -19
  15. package/lib/checks/parameters.js +1 -1
  16. package/lib/checks/queryNoDbArtifacts.js +2 -0
  17. package/lib/checks/types.js +16 -22
  18. package/lib/compiler/assert-consistency.js +31 -28
  19. package/lib/compiler/builtins.js +20 -4
  20. package/lib/compiler/checks.js +72 -63
  21. package/lib/compiler/define.js +396 -314
  22. package/lib/compiler/extend.js +55 -49
  23. package/lib/compiler/index.js +5 -0
  24. package/lib/compiler/populate.js +28 -11
  25. package/lib/compiler/propagator.js +2 -1
  26. package/lib/compiler/resolve.js +28 -13
  27. package/lib/compiler/shared.js +15 -10
  28. package/lib/compiler/utils.js +7 -7
  29. package/lib/edm/annotations/genericTranslation.js +51 -46
  30. package/lib/edm/annotations/preprocessAnnotations.js +37 -40
  31. package/lib/edm/csn2edm.js +69 -21
  32. package/lib/edm/edm.js +2 -2
  33. package/lib/edm/edmInboundChecks.js +6 -8
  34. package/lib/edm/edmPreprocessor.js +88 -80
  35. package/lib/edm/edmUtils.js +6 -15
  36. package/lib/gen/Dictionary.json +81 -13
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +2 -1
  39. package/lib/gen/languageParser.js +4680 -4484
  40. package/lib/inspect/inspectModelStatistics.js +2 -1
  41. package/lib/inspect/inspectPropagation.js +2 -1
  42. package/lib/json/from-csn.js +131 -78
  43. package/lib/json/to-csn.js +39 -23
  44. package/lib/language/antlrParser.js +0 -3
  45. package/lib/language/docCommentParser.js +7 -3
  46. package/lib/language/errorStrategy.js +3 -2
  47. package/lib/language/genericAntlrParser.js +96 -41
  48. package/lib/language/language.g4 +112 -128
  49. package/lib/language/multiLineStringParser.js +2 -1
  50. package/lib/main.d.ts +115 -2
  51. package/lib/main.js +16 -3
  52. package/lib/model/csnRefs.js +3 -3
  53. package/lib/model/csnUtils.js +109 -179
  54. package/lib/model/enrichCsn.js +13 -8
  55. package/lib/model/revealInternalProperties.js +4 -3
  56. package/lib/optionProcessor.js +19 -3
  57. package/lib/render/manageConstraints.js +11 -15
  58. package/lib/render/toCdl.js +144 -47
  59. package/lib/render/toHdbcds.js +22 -22
  60. package/lib/render/toRename.js +3 -4
  61. package/lib/render/toSql.js +29 -20
  62. package/lib/render/utils/delta.js +3 -1
  63. package/lib/render/utils/sql.js +2 -14
  64. package/lib/transform/db/associations.js +6 -6
  65. package/lib/transform/db/cdsPersistence.js +3 -3
  66. package/lib/transform/db/constraints.js +4 -6
  67. package/lib/transform/db/expansion.js +4 -4
  68. package/lib/transform/db/flattening.js +12 -15
  69. package/lib/transform/db/temporal.js +4 -3
  70. package/lib/transform/db/transformExists.js +2 -1
  71. package/lib/transform/draft/db.js +7 -7
  72. package/lib/transform/forOdataNew.js +15 -4
  73. package/lib/transform/forRelationalDB.js +53 -39
  74. package/lib/transform/odata/toFinalBaseType.js +106 -82
  75. package/lib/transform/odata/typesExposure.js +26 -17
  76. package/lib/transform/odata/utils.js +1 -1
  77. package/lib/transform/parseExpr.js +1 -1
  78. package/lib/transform/transformUtilsNew.js +33 -10
  79. package/lib/transform/translateAssocsToJoins.js +8 -7
  80. package/lib/transform/universalCsn/coreComputed.js +7 -5
  81. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
  82. package/lib/utils/timetrace.js +2 -2
  83. package/package.json +1 -2
@@ -20,6 +20,7 @@ const {
20
20
  } = require('../base/model');
21
21
  const { CompilerAssertion } = require('../base/error');
22
22
  const { pathName } = require('./utils');
23
+ const { forEachMemberRecursively } = require('../model/csnUtils');
23
24
 
24
25
  function check( model ) { // = XSN
25
26
  const {
@@ -40,6 +41,10 @@ function check( model ) { // = XSN
40
41
 
41
42
  function checkAnnotationDefinition( art ) {
42
43
  checkEnumType( art );
44
+ forEachMemberRecursively( art, (member) => {
45
+ if (member.localized?.val)
46
+ warning( 'def-unexpected-localized-anno', [ member.localized.location, member ] );
47
+ });
43
48
  // TODO: Should we check elements similar to definition-elements as well?
44
49
  }
45
50
 
@@ -54,24 +59,33 @@ function check( model ) { // = XSN
54
59
  if (art.kind === 'enum')
55
60
  checkEnum( art );
56
61
  checkEnumType( art );
62
+
57
63
  forEachMember( art, checkGenericConstruct );
58
64
  }
59
65
 
60
66
  function checkElement( elem ) {
61
67
  checkLocalizedSubElement(elem);
62
- if (elem.key && elem.key.val) {
63
- if (elem.virtual && elem.virtual.val)
64
- error(null, [ elem.location, elem ], {}, 'Element can\'t be virtual and key');
65
-
66
- checkForUnmanagedAssociations( elem, elem.key );
68
+ if (elem.key?.val) {
69
+ if (elem.virtual?.val) {
70
+ error('def-unexpected-key', [ elem.key.location, elem ],
71
+ { '#': 'virtual', name: elem.name.element, prop: 'key' });
72
+ }
73
+ checkForUnmanagedAssociationsAsKey( elem, elem.key );
67
74
  }
68
75
  checkAssociation( elem );
69
76
  checkLocalizedElement( elem );
70
77
  if (elem.on && !elem.on.$inferred)
71
78
  checkExpression(elem.on, true);
72
- if (elem.value)
73
- checkExpression(elem.value);
79
+
80
+ if (elem.value) {
81
+ if (elem.$syntax === 'calc')
82
+ checkCalculatedElement( elem );
83
+ else
84
+ checkExpression( elem.value );
85
+ }
86
+
74
87
  checkCardinality(elem); // TODO: also for assoc types
88
+
75
89
  forEachGeneric( elem, 'elements', checkElement );
76
90
  }
77
91
 
@@ -111,6 +125,24 @@ function check( model ) { // = XSN
111
125
  }
112
126
  }
113
127
 
128
+ function checkCalculatedElement( elem ) {
129
+ if (elem.value.path) {
130
+ checkExpressionsInPaths(elem.value);
131
+
132
+ const loc = [ elem.value.location, elem ];
133
+ if (isVirtualElement(elem.value._artifact))
134
+ error('ref-unexpected-virtual', loc, { '#': 'expr' });
135
+ else if (isStructuredElement(elem.value._artifact))
136
+ error('ref-unexpected-structured', loc, { '#': 'expr' } );
137
+ else if (elem.value._artifact?.target !== undefined)
138
+ error('ref-unexpected-assoc', loc, { '#': 'expr' });
139
+ }
140
+ else {
141
+ // TODO: The checks above should also be run for each path in expressions.
142
+ checkExpression( elem.value );
143
+ }
144
+ }
145
+
114
146
  function checkQuery( query ) {
115
147
  checkNoUnmanagedAssocsInGroupByOrderBy( query );
116
148
  // TODO: check too simple (just one source), as most of those in this file
@@ -169,9 +201,8 @@ function check( model ) { // = XSN
169
201
  // Special handling to print a more detailed error message.
170
202
  // Other cases like `null` as enum value are handled in `checkEnumValueType()`
171
203
  if (type === 'enum') {
172
- warning('enum-value-ref', [ loc, enumNode ], {},
204
+ warning('enum-value-ref', [ loc, enumNode ], {}, // TODO: make this an error in v4
173
205
  'References to other values are not allowed as enum values');
174
- return;
175
206
  }
176
207
  }
177
208
 
@@ -329,7 +360,7 @@ function check( model ) { // = XSN
329
360
  *
330
361
  * @param {any} element Element to check recursively
331
362
  */
332
- function checkForUnmanagedAssociations( element, keyObj ) {
363
+ function checkForUnmanagedAssociationsAsKey( element, keyObj ) {
333
364
  if (element.targetAspect) {
334
365
  // TODO: bad location / message
335
366
  message('composition-as-key', [ keyObj.location, element ], {},
@@ -345,7 +376,7 @@ function check( model ) { // = XSN
345
376
  // TODO: ease check for subelements: using unmanaged assocs is OK there, as
346
377
  // long as the whole key is "closed", i.e., no ref in ON refers to element
347
378
  // outside.
348
- forEachGeneric( element, 'elements', e => checkForUnmanagedAssociations( e, keyObj ) );
379
+ forEachGeneric( element, 'elements', e => checkForUnmanagedAssociationsAsKey( e, keyObj ) );
349
380
  }
350
381
 
351
382
  // Check that min and max cardinalities of 'elem' in 'art' have legal values
@@ -468,14 +499,12 @@ function check( model ) { // = XSN
468
499
 
469
500
  function checkAssociation( elem ) {
470
501
  // TODO: yes, a check similar to this could make it into the compiler)
471
- // when virtual element is part of association
502
+ // when virtual element is part of association
472
503
  if (elem.foreignKeys) {
473
504
  for (const k in elem.foreignKeys) {
474
505
  const key = elem.foreignKeys[k].targetElement;
475
- if (key && key._artifact && key._artifact.virtual && key._artifact.virtual.val) {
476
- error(null, [ key.location, elem ], {},
477
- 'Virtual elements can\'t be used as a foreign key for a managed association');
478
- }
506
+ if (key && isVirtualElement(key._artifact))
507
+ error('ref-unexpected-virtual', [ key.location, elem ], { '#': 'fkey' });
479
508
  }
480
509
  }
481
510
  if (elem.on && !elem.on.$inferred)
@@ -601,13 +630,24 @@ function check( model ) { // = XSN
601
630
  *
602
631
  * TO CLARIFY: do we want the "no virtual element" check for virtual elements/columns, too?
603
632
  *
604
- * @param {any} arg Argument to check (part of an expression)
633
+ * @param {any} elem Element to check (part of an expression)
605
634
  * @returns {Boolean}
606
635
  */
607
- function isVirtualElement( arg ) {
608
- return arg.path &&
609
- arg._artifact && arg._artifact.virtual && arg._artifact.virtual.val === true &&
610
- arg._artifact.kind && arg._artifact.kind === 'element';
636
+ function isVirtualElement( elem ) {
637
+ let parent = elem?._origin || elem;
638
+ while (parent) {
639
+ if (parent.virtual?.val === true)
640
+ return true;
641
+ parent = parent._parent;
642
+ }
643
+ return false;
644
+ }
645
+
646
+ function isStructuredElement( elem ) {
647
+ // The effective type always points to something with elements _if_ the
648
+ // type is structured. But `elem` should already have `elements` if its
649
+ // structured due to element expansion.
650
+ return !!(elem?._effectiveType || elem)?.elements;
611
651
  }
612
652
 
613
653
  /**
@@ -620,9 +660,8 @@ function check( model ) { // = XSN
620
660
  const args = Array.isArray(xpr.args) ? xpr.args : Object.values(xpr.args || {});
621
661
  // Check for illegal argument usage within the expression
622
662
  for (const arg of args) {
623
- if (isVirtualElement(arg))
624
- error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
625
-
663
+ if (isVirtualElement(arg._artifact || arg))
664
+ error('ref-unexpected-virtual', arg.location, { '#': 'expr' });
626
665
 
627
666
  // Recursively traverse the argument expression
628
667
  checkTokenStreamExpression(arg, allowAssocTail);
@@ -643,20 +682,17 @@ function check( model ) { // = XSN
643
682
 
644
683
  // Check for illegal argument usage within the expression
645
684
  for (const arg of Array.isArray(xpr.args) && xpr.args || []) { // TODO named args?
646
- if (isVirtualElement(arg))
647
- error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
685
+ if (isVirtualElement(arg._artifact || arg))
686
+ error('ref-unexpected-virtual', arg.location, { '#': 'expr' });
648
687
 
649
688
  // Arg must not be an association and not $self
650
689
  // Only if path is not approved exists path (that is non-query position)
651
690
  if (arg.path && arg.$expected !== undefined) { // not 'approved-exists'
652
- if (arg.$expected === 'exists') {
653
- error(null, arg.location, {},
654
- 'An association can\'t be used as a value in an expression');
655
- }
691
+ if (arg.$expected === 'exists')
692
+ error('ref-unexpected-assoc', arg.location, { '#': 'expr' } );
656
693
  }
657
694
  else if (!allowAssocTail && isAssociationOperand(arg)) {
658
- error(null, arg.location, {},
659
- 'An association can\'t be used as a value in an expression');
695
+ error('ref-unexpected-assoc', arg.location, { '#': 'expr' } );
660
696
  }
661
697
 
662
698
  if (isDollarSelfOrProjectionOperand(arg)) {
@@ -769,7 +805,7 @@ function check( model ) { // = XSN
769
805
 
770
806
  // Sanity checks
771
807
  if (!elementDecl._effectiveType)
772
- throw new Error(`Expecting annotation declaration to have _finalType: ${ JSON.stringify(annoDecl) }`);
808
+ throw new CompilerAssertion(`Expecting annotation declaration to have _finalType: ${ JSON.stringify(annoDecl) }`);
773
809
 
774
810
 
775
811
  // Must have literal or path unless it is a boolean
@@ -929,40 +965,13 @@ function check( model ) { // = XSN
929
965
  }
930
966
 
931
967
  /**
932
- * Ensure that the sap.common.[Fiori]TextsAspect has proper types for
933
- * e.g. `locale` and `ID_texts`.
934
- *
935
- * @param {XSN.Model} model
936
- */
937
- function checkSapCommonTextsAspects( model ) {
938
- checkSapCommonTextsAspectLocale( model, 'sap.common.TextsAspect' );
939
- checkSapCommonTextsAspectLocale( model, 'sap.common.FioriTextsAspect' );
940
-
941
- // Check ID_texts: Fiori requires it to be UUID.
942
- const fioriTextsAspect = model.definitions['sap.common.FioriTextsAspect'];
943
- const id = fioriTextsAspect?.elements?.ID_texts;
944
- if (id) {
945
- const idType = id._effectiveType;
946
- if (!idType || idType.name?.absolute !== 'cds.UUID') {
947
- const { error } = model.$messageFunctions;
948
- error('def-invalid-element-type', [ id.type.location, id ], {
949
- '#': 'std',
950
- art: 'sap.common.FioriTextsAspect',
951
- elemref: 'ID_texts',
952
- type: 'cds.UUID',
953
- });
954
- }
955
- }
956
- }
957
-
958
- /**
959
- * Ensure that the `locale` element of sap.common.[Fiori]TextsAspects
968
+ * Ensure that the `locale` element of sap.common.TextsAspects
960
969
  * is a string type. This is required by CAP runtimes to work properly.
961
970
  *
962
971
  * @param {XSN.Model} model
963
- * @param {string} name Either sap.common.TextsAspects or sap.common.FioriTextsAspects
964
972
  */
965
- function checkSapCommonTextsAspectLocale( model, name ) {
973
+ function checkSapCommonTextsAspects( model ) {
974
+ const name = 'sap.common.TextsAspect';
966
975
  const locale = model.definitions[name]?.elements?.locale;
967
976
  if (locale) {
968
977
  // `locale` could also be `sap.common.Locale`, which must also be a string.