@sap/cds-compiler 3.1.2 → 3.3.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 (100) hide show
  1. package/CHANGELOG.md +80 -3
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +18 -0
  4. package/lib/api/main.js +8 -13
  5. package/lib/base/error.js +2 -2
  6. package/lib/base/keywords.js +2 -24
  7. package/lib/base/message-registry.js +43 -14
  8. package/lib/base/messages.js +20 -10
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/actionsFunctions.js +1 -1
  11. package/lib/checks/annotationsOData.js +2 -2
  12. package/lib/checks/arrayOfs.js +15 -7
  13. package/lib/checks/cdsPersistence.js +1 -1
  14. package/lib/checks/checkForTypes.js +48 -0
  15. package/lib/checks/defaultValues.js +2 -2
  16. package/lib/checks/elements.js +81 -6
  17. package/lib/checks/foreignKeys.js +12 -13
  18. package/lib/checks/invalidTarget.js +10 -11
  19. package/lib/checks/managedInType.js +21 -15
  20. package/lib/checks/nullableKeys.js +1 -1
  21. package/lib/checks/onConditions.js +9 -9
  22. package/lib/checks/parameters.js +21 -0
  23. package/lib/checks/selectItems.js +1 -1
  24. package/lib/checks/types.js +2 -2
  25. package/lib/checks/utils.js +17 -7
  26. package/lib/checks/validator.js +26 -14
  27. package/lib/compiler/assert-consistency.js +13 -6
  28. package/lib/compiler/builtins.js +8 -0
  29. package/lib/compiler/checks.js +40 -33
  30. package/lib/compiler/define.js +50 -44
  31. package/lib/compiler/extend.js +303 -37
  32. package/lib/compiler/kick-start.js +2 -35
  33. package/lib/compiler/populate.js +83 -62
  34. package/lib/compiler/propagator.js +1 -1
  35. package/lib/compiler/resolve.js +61 -104
  36. package/lib/compiler/shared.js +16 -6
  37. package/lib/compiler/tweak-assocs.js +25 -12
  38. package/lib/compiler/utils.js +2 -2
  39. package/lib/edm/annotations/genericTranslation.js +3 -3
  40. package/lib/edm/csn2edm.js +10 -10
  41. package/lib/edm/edm.js +17 -9
  42. package/lib/edm/edmPreprocessor.js +53 -30
  43. package/lib/edm/edmUtils.js +7 -2
  44. package/lib/gen/Dictionary.json +14 -0
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +3 -2
  47. package/lib/gen/languageParser.js +4205 -4100
  48. package/lib/inspect/inspectModelStatistics.js +1 -1
  49. package/lib/inspect/inspectPropagation.js +23 -9
  50. package/lib/json/csnVersion.js +1 -1
  51. package/lib/json/from-csn.js +26 -19
  52. package/lib/json/to-csn.js +47 -5
  53. package/lib/language/antlrParser.js +1 -1
  54. package/lib/language/genericAntlrParser.js +29 -13
  55. package/lib/language/language.g4 +28 -8
  56. package/lib/main.d.ts +3 -6
  57. package/lib/model/.eslintrc.json +13 -0
  58. package/lib/model/api.js +4 -2
  59. package/lib/model/csnRefs.js +74 -47
  60. package/lib/model/csnUtils.js +236 -218
  61. package/lib/model/enrichCsn.js +41 -31
  62. package/lib/model/revealInternalProperties.js +61 -57
  63. package/lib/model/sortViews.js +31 -31
  64. package/lib/modelCompare/compare.js +6 -6
  65. package/lib/optionProcessor.js +5 -0
  66. package/lib/render/manageConstraints.js +2 -2
  67. package/lib/render/toCdl.js +31 -44
  68. package/lib/render/toHdbcds.js +7 -5
  69. package/lib/render/toRename.js +4 -4
  70. package/lib/render/toSql.js +11 -5
  71. package/lib/render/utils/common.js +20 -9
  72. package/lib/render/utils/sql.js +5 -5
  73. package/lib/transform/db/applyTransformations.js +32 -3
  74. package/lib/transform/db/expansion.js +81 -37
  75. package/lib/transform/db/flattening.js +1 -1
  76. package/lib/transform/db/temporal.js +1 -1
  77. package/lib/transform/db/transformExists.js +1 -1
  78. package/lib/transform/forOdataNew.js +10 -7
  79. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
  80. package/lib/transform/localized.js +28 -19
  81. package/lib/transform/odata/toFinalBaseType.js +8 -11
  82. package/lib/transform/odata/typesExposure.js +1 -1
  83. package/lib/transform/transformUtilsNew.js +101 -39
  84. package/lib/transform/translateAssocsToJoins.js +5 -4
  85. package/lib/utils/moduleResolve.js +5 -5
  86. package/lib/utils/objectUtils.js +3 -3
  87. package/package.json +2 -2
  88. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  89. package/share/messages/check-proper-type-of.md +4 -4
  90. package/share/messages/check-proper-type.md +2 -2
  91. package/share/messages/duplicate-autoexposed.md +4 -4
  92. package/share/messages/extend-repeated-intralayer.md +4 -5
  93. package/share/messages/extend-unrelated-layer.md +4 -4
  94. package/share/messages/message-explanations.json +3 -1
  95. package/share/messages/redirected-to-ambiguous.md +7 -6
  96. package/share/messages/redirected-to-complex.md +63 -0
  97. package/share/messages/redirected-to-unrelated.md +6 -5
  98. package/share/messages/rewrite-not-supported.md +4 -4
  99. package/share/messages/syntax-expected-integer.md +3 -3
  100. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -229,11 +229,13 @@ function assertConsistency( model, stage ) {
229
229
  elements: { kind: true, inherits: 'definitions', also: [ 0 ] }, // 0 for cyclic expansions
230
230
  // specified elements in query entities (TODO: introduce real "specified elements" instead):
231
231
  elements$: { kind: true, enumerable: false, test: TODO },
232
+ enum$: { kind: true, enumerable: false, test: TODO },
232
233
  actions: { kind: true, inherits: 'definitions' },
233
234
  enum: { kind: true, inherits: 'definitions' },
234
235
  foreignKeys: { kind: true, inherits: 'definitions' },
235
236
  $keysNavigation: { kind: true, test: TODO },
236
237
  params: { kind: true, inherits: 'definitions' },
238
+ _extendType: { kind: true, test: TODO },
237
239
  mixin: { inherits: 'definitions' },
238
240
  query: {
239
241
  kind: true,
@@ -307,6 +309,7 @@ function assertConsistency( model, stage ) {
307
309
  kind: 'element',
308
310
  test: isDictionary( definition ), // definition since redef
309
311
  requires: [ 'location', 'name' ],
312
+ optional: [ '$duplicates' ],
310
313
  },
311
314
  orderBy: { inherits: 'value', test: isArray( expression ) },
312
315
  sort: { test: locationVal( isString ), enum: [ 'asc', 'desc' ] },
@@ -330,7 +333,7 @@ function assertConsistency( model, stage ) {
330
333
  requires: [ 'location' ],
331
334
  optional: [
332
335
  'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
333
- 'scope', '_artifact', '$inferred', '$expand', '$tableAliases', '_$next',
336
+ 'scope', '_artifact', '$inferred', '$expand', '$inCycle', '$tableAliases', '_$next',
334
337
  '_effectiveType', // by propagation
335
338
  ],
336
339
  },
@@ -504,8 +507,8 @@ function assertConsistency( model, stage ) {
504
507
  optional: [
505
508
  'enum',
506
509
  'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
507
- '_outer', '_effectiveType', 'notNull',
508
- '_origin', '_block', '$inferred', '$expand', '_deps',
510
+ '_outer', '_effectiveType', 'notNull', '_parent',
511
+ '_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
509
512
  '$syntax',
510
513
  '_status', '_redirected',
511
514
  ...typeProperties,
@@ -602,11 +605,13 @@ function assertConsistency( model, stage ) {
602
605
  kind: true,
603
606
  test: isOneOf([
604
607
  // Uppercase values are used in logic, lowercase value are "just for us", i.e.
605
- // debugging or to add non-enumerable properties such as $generated in Universal CSN.
608
+ // debugging or to add properties such as $generated in Universal CSN.
606
609
  // However, that is no longer true. For example, `autoexposed` is used in populate.js
607
610
  // as well.
608
611
  'IMPLICIT',
609
612
  'REDIRECTED',
613
+ 'NULL', // from propagator
614
+ 'prop', // from propagator
610
615
 
611
616
  '$autoElement', // for magicVars: $user is automatically changed to $user.id
612
617
  '$generated', // compiler generated annotations, e.g. @Core.Computed
@@ -618,8 +623,7 @@ function assertConsistency( model, stage ) {
618
623
  'composition-entity',
619
624
  'copy', // only used in rewriteCondition(): On-condition is copied
620
625
  'duplicate-autoexposed', // just like `autoexposed`, but with `duplicate` error.
621
- 'expand-element', // expanded elements
622
- 'expand-param', // expanded params (difference to expand-element only for debugging)
626
+ 'expanded', // expanded elements, items, params
623
627
  'include', // through includes, e.g. `entity E : F {}`
624
628
  'keys',
625
629
  'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
@@ -628,6 +632,8 @@ function assertConsistency( model, stage ) {
628
632
  'none', // only used in ensureColumnName(): Used in object representing empty alias
629
633
  'query', // inferred query properties, e.g. `key`
630
634
  'rewrite', // on-conditions or FKeys are rewritten
635
+ 'parent-origin', // annotation/property copied from parent that does not come through
636
+ // $origin and is not a direct annotation
631
637
  ]),
632
638
  },
633
639
 
@@ -639,6 +645,7 @@ function assertConsistency( model, stage ) {
639
645
  // See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
640
646
  test: isOneOf([ 'origin', 'annotate', 'target' ]),
641
647
  },
648
+ $inCycle: { kind: true, test: isBoolean },
642
649
 
643
650
  $autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
644
651
  $extra: { parser: true, test: TODO }, // for unexpected properties in CSN
@@ -19,6 +19,10 @@ const core = {
19
19
  DecimalFloat: { category: 'decimal', deprecated: true },
20
20
  Integer64: { category: 'integer' },
21
21
  Integer: { category: 'integer' },
22
+ UInt8: { category: 'integer' },
23
+ Int16: { category: 'integer' },
24
+ Int32: { category: 'integer' },
25
+ Int64: { category: 'integer' },
22
26
  Double: { category: 'decimal' },
23
27
  Date: { category: 'dateTime' },
24
28
  Time: { category: 'dateTime' },
@@ -54,6 +58,7 @@ const typeParameters = {
54
58
  srid: [ 'number' ],
55
59
  },
56
60
  };
61
+ // a.k.a "typeProperties"
57
62
  typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
58
63
 
59
64
  // const hana = {
@@ -137,6 +142,7 @@ const specialFunctions = compileFunctions( {
137
142
  separator: [ 'FLAG', 'IN', 'FROM', 'OCCURRENCE', 'GROUP' ],
138
143
  },
139
144
  ],
145
+ SUBSTR_REGEXPR: 'SUBSTRING_REGEXPR',
140
146
  } );
141
147
 
142
148
  function compileFunctions( special ) {
@@ -364,6 +370,8 @@ function isInReservedNamespace(absolute) {
364
370
  * check against their absolute names. Builtin types are "cds.<something>", i.e. they
365
371
  * are directly in 'cds', but not for example in 'cds.foundation'.
366
372
  *
373
+ * TODO: Handle `{ ref: [ "cds.Integer" ] }`
374
+ *
367
375
  * @param {string} type
368
376
  * @returns {boolean}
369
377
  */
@@ -52,7 +52,7 @@ function check( model ) { // = XSN
52
52
  checkLocalizedSubElement(elem);
53
53
  if (elem.key && elem.key.val) {
54
54
  if (elem.virtual && elem.virtual.val)
55
- error(null, [ elem.location, elem ], 'Element can\'t be virtual and key');
55
+ error(null, [ elem.location, elem ], {}, 'Element can\'t be virtual and key');
56
56
 
57
57
  checkForUnmanagedAssociations( elem, elem.key );
58
58
  }
@@ -73,7 +73,7 @@ function check( model ) { // = XSN
73
73
  // on it!): The code below misses to consider CSN input.
74
74
  if (construct.name.id && construct.name.id.indexOf('.') !== -1) {
75
75
  // TODO: No, we should not forbid this
76
- error(null, [ construct.name.location, construct ],
76
+ error(null, [ construct.name.location, construct ], {},
77
77
  'The character \'.\' is not allowed in identifiers');
78
78
  }
79
79
  }
@@ -84,8 +84,8 @@ function check( model ) { // = XSN
84
84
  if (elem.localized && elem.localized.val) {
85
85
  const type = elem._effectiveType;
86
86
  if (!type || !type.builtin || type.category !== 'string') {
87
- warning(null, [ elem.localized.location, elem ], {},
88
- 'Keyword “localized” may only be used in combination with string types');
87
+ warning(null, [ elem.type?.location, elem ], { keyword: 'localized' },
88
+ 'Keyword $(KEYWORD) should only be used in combination with string types');
89
89
  }
90
90
  }
91
91
  // "key" keyword at localized element in SELECT list.
@@ -159,7 +159,7 @@ function check( model ) { // = XSN
159
159
  // Special handling to print a more detailed error message.
160
160
  // Other cases like `null` as enum value are handled in `checkEnumValueType()`
161
161
  if (type === 'enum') {
162
- warning('enum-value-ref', [ loc, enumNode ],
162
+ warning('enum-value-ref', [ loc, enumNode ], {},
163
163
  'References to other values are not allowed as enum values');
164
164
  return;
165
165
  }
@@ -387,7 +387,7 @@ function check( model ) { // = XSN
387
387
  if (elem.cardinality[p[0]] && elem.cardinality[p[1]] &&
388
388
  elem.cardinality[p[1]].literal === 'number' &&
389
389
  elem.cardinality[p[0]].val > elem.cardinality[p[1]].val) {
390
- error(null, [ elem.cardinality.location, elem ],
390
+ error(null, [ elem.cardinality.location, elem ], {},
391
391
  `${ p[2] } minimum cardinality must not be greater than ${ p[2].toLowerCase() } maximum cardinality`);
392
392
  }
393
393
  });
@@ -422,7 +422,7 @@ function check( model ) { // = XSN
422
422
  if (groupByEntry._artifact && groupByEntry._artifact._effectiveType &&
423
423
  groupByEntry._artifact._effectiveType.on) {
424
424
  // Unmanaged association - complain
425
- error(null, [ groupByEntry.location, art ],
425
+ error(null, [ groupByEntry.location, art ], {},
426
426
  'Unmanaged associations are not allowed in GROUP BY');
427
427
  }
428
428
  }
@@ -430,7 +430,7 @@ function check( model ) { // = XSN
430
430
  if (orderByEntry._artifact && orderByEntry._artifact._effectiveType &&
431
431
  orderByEntry._artifact._effectiveType.on) {
432
432
  // Unmanaged association - complain
433
- error(null, [ orderByEntry.location, art ],
433
+ error(null, [ orderByEntry.location, art ], {},
434
434
  'Unmanaged associations are not allowed in ORDER BY');
435
435
  }
436
436
  }
@@ -459,7 +459,7 @@ function check( model ) { // = XSN
459
459
  for (const k in elem.foreignKeys) {
460
460
  const key = elem.foreignKeys[k].targetElement;
461
461
  if (key && key._artifact && key._artifact.virtual && key._artifact.virtual.val) {
462
- error(null, [ key.location, elem ],
462
+ error(null, [ key.location, elem ], {},
463
463
  'Virtual elements can\'t be used as a foreign key for a managed association');
464
464
  }
465
465
  }
@@ -517,7 +517,7 @@ function check( model ) { // = XSN
517
517
  if (argTarget.on) {
518
518
  const same = path0._artifact === elem;
519
519
  if (!same) {
520
- error(null, [ path0.location, elem ],
520
+ error(null, [ path0.location, elem ], {},
521
521
  'Unmanaged association condition can\'t follow another unmanaged association');
522
522
  }
523
523
  }
@@ -527,7 +527,7 @@ function check( model ) { // = XSN
527
527
  if (op && op.val === 'xpr') // no check for xpr
528
528
  return;
529
529
  if (op && op.val !== '=')
530
- error(null, [ op.location, elem ], '$self comparison is only allowed with \'=\'');
530
+ error(null, [ op.location, elem ], {}, '$self comparison is only allowed with \'=\'');
531
531
  }
532
532
 
533
533
  // A function like this could be part of the compiler
@@ -601,7 +601,7 @@ function check( model ) { // = XSN
601
601
  // Check for illegal argument usage within the expression
602
602
  for (const arg of args) {
603
603
  if (isVirtualElement(arg))
604
- error(null, arg.location, 'Virtual elements can\'t be used in an expression');
604
+ error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
605
605
 
606
606
 
607
607
  // Recursively traverse the argument expression
@@ -624,20 +624,23 @@ function check( model ) { // = XSN
624
624
  // Check for illegal argument usage within the expression
625
625
  for (const arg of Array.isArray(xpr.args) && xpr.args || []) { // TODO named args?
626
626
  if (isVirtualElement(arg))
627
- error(null, arg.location, 'Virtual elements can\'t be used in an expression');
627
+ error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
628
628
 
629
629
  // Arg must not be an association and not $self
630
630
  // Only if path is not approved exists path (that is non-query position)
631
631
  if (arg.path && arg.$expected !== undefined) { // not 'approved-exists'
632
- if (arg.$expected === 'exists')
633
- error(null, arg.location, 'An association can\'t be used as a value in an expression');
632
+ if (arg.$expected === 'exists') {
633
+ error(null, arg.location, {},
634
+ 'An association can\'t be used as a value in an expression');
635
+ }
634
636
  }
635
637
  else if (!allowAssocTail && isAssociationOperand(arg)) {
636
- error(null, arg.location, 'An association can\'t be used as a value in an expression');
638
+ error(null, arg.location, {},
639
+ 'An association can\'t be used as a value in an expression');
637
640
  }
638
641
 
639
642
  if (isDollarSelfOrProjectionOperand(arg))
640
- error(null, arg.location, `"${ arg.path[0].id }" can only be used as a value in a comparison to an association`);
643
+ error(null, arg.location, {}, `"${ arg.path[0].id }" can only be used as a value in a comparison to an association`);
641
644
 
642
645
  // Recursively traverse the argument expression
643
646
  checkTreeLikeExpression(arg, allowAssocTail);
@@ -736,7 +739,7 @@ function check( model ) { // = XSN
736
739
 
737
740
  // Element must exist in annotation
738
741
  if (!elementDecl) {
739
- warning(null, anno.location || anno.name.location, `Element "${ anno.name.path.map(step => step.id).join('.') }" not found for annotation "${ annoDecl.name.absolute }"`);
742
+ warning(null, anno.location || anno.name.location, {}, `Element "${ anno.name.path.map(step => step.id).join('.') }" not found for annotation "${ annoDecl.name.absolute }"`);
740
743
  return;
741
744
  }
742
745
 
@@ -747,10 +750,14 @@ function check( model ) { // = XSN
747
750
 
748
751
  // Must have literal or path unless it is a boolean
749
752
  if (!anno.literal && !anno.path && getFinalTypeNameOf(elementDecl) !== 'cds.Boolean') {
750
- if (elementDecl.type && elementDecl.type._artifact.name.absolute)
751
- warning(null, anno.location || anno.name.location, `Expecting a value of type "${ elementDecl.type._artifact.name.absolute }" for the annotation`);
752
- else
753
- warning(null, anno.location || anno.name.location, 'Expecting a value for the annotation');
753
+ if (elementDecl.type && elementDecl.type._artifact.name.absolute) {
754
+ warning(null, anno.location || anno.name.location, {},
755
+ `Expecting a value of type "${ elementDecl.type._artifact.name.absolute }" for the annotation`);
756
+ }
757
+ else {
758
+ warning(null, anno.location || anno.name.location, {},
759
+ 'Expecting a value for the annotation');
760
+ }
754
761
 
755
762
  return;
756
763
  }
@@ -775,7 +782,7 @@ function check( model ) { // = XSN
775
782
  if (elementDecl._effectiveType.items) {
776
783
  // Make sure we have an array value
777
784
  if (value.literal !== 'array') {
778
- warning(null, loc, 'An array value is required here');
785
+ warning(null, loc, {}, 'An array value is required here');
779
786
  return;
780
787
  }
781
788
  // Check each element
@@ -788,7 +795,7 @@ function check( model ) { // = XSN
788
795
  // Struct expected (can only happen within arrays)?
789
796
  if (elementDecl._effectiveType.elements) {
790
797
  if (value.literal !== 'struct') {
791
- warning(null, loc, 'A struct value is required here');
798
+ warning(null, loc, {}, 'A struct value is required here');
792
799
  return;
793
800
  }
794
801
  // FIXME: Should check each element
@@ -800,28 +807,28 @@ function check( model ) { // = XSN
800
807
  if (builtins.isStringTypeName(type)) {
801
808
  if (value.literal !== 'string' && value.literal !== 'enum' &&
802
809
  !elementDecl._effectiveType.enum)
803
- warning(null, loc, `A string value is required for type "${ type }"`);
810
+ warning(null, loc, {}, `A string value is required for type "${ type }"`);
804
811
  }
805
812
  else if (builtins.isBinaryTypeName(type)) {
806
813
  if (value.literal !== 'string' && value.literal !== 'x')
807
- warning(null, loc, `A hexadecimal string value is required for type "${ type }"`);
814
+ warning(null, loc, {}, `A hexadecimal string value is required for type "${ type }"`);
808
815
  }
809
816
  else if (builtins.isNumericTypeName(type)) {
810
817
  if (value.literal !== 'number' && value.literal !== 'enum' &&
811
818
  !elementDecl._effectiveType.enum)
812
- warning(null, loc, `A numerical value is required for type "${ type }"`);
819
+ warning(null, loc, {}, `A numerical value is required for type "${ type }"`);
813
820
  }
814
821
  else if (builtins.isDateOrTimeTypeName(type)) {
815
822
  if (value.literal !== 'date' && value.literal !== 'time' &&
816
823
  value.literal !== 'timestamp' && value.literal !== 'string')
817
- warning(null, loc, `A date/time value or a string is required for type "${ type }"`);
824
+ warning(null, loc, {}, `A date/time value or a string is required for type "${ type }"`);
818
825
  }
819
826
  else if (builtins.isBooleanTypeName(type)) {
820
827
  if (value.literal && value.literal !== 'boolean')
821
- warning(null, loc, `A boolean value is required for type "${ type }"`);
828
+ warning(null, loc, {}, `A boolean value is required for type "${ type }"`);
822
829
  }
823
830
  else if (builtins.isRelationTypeName(type) || builtins.isGeoTypeName(type)) {
824
- warning(null, loc, `Type "${ type }" can't be assigned a value`);
831
+ warning(null, loc, {}, `Type "${ type }" can't be assigned a value`);
825
832
  }
826
833
  else {
827
834
  throw new Error(`Unknown primitive type name: ${ type }`);
@@ -834,19 +841,19 @@ function check( model ) { // = XSN
834
841
  // Enum symbol provided and expected
835
842
  if (!expectedEnum[value.sym.id]) {
836
843
  // .. but no such constant
837
- warning(null, loc, `Enum symbol "#${ value.sym.id }" not found in enum`);
844
+ warning(null, loc, {}, `Enum symbol "#${ value.sym.id }" not found in enum`);
838
845
  }
839
846
  }
840
847
  else {
841
848
  // Enum symbol provided but not expected
842
- warning(null, loc, `Cannot use enum symbol "#${ value.sym.id }" for non-enum type "${ type }"`);
849
+ warning(null, loc, {}, `Can't use enum symbol "#${ value.sym.id }" for non-enum type "${ type }"`);
843
850
  }
844
851
  }
845
852
  else if (expectedEnum) {
846
853
  // Enum symbol not provided but expected
847
854
  if (!Object.keys(expectedEnum).some(symbol => expectedEnum[symbol].value.val === value.val)) {
848
855
  // ... and none of the valid enum symbols matches the value
849
- warning(null, loc, 'An enum value is required here');
856
+ warning(null, loc, {}, 'An enum value is required here');
850
857
  }
851
858
  }
852
859
  }
@@ -7,7 +7,7 @@
7
7
  // Definitions inside a context are not listed here (as opposed to
8
8
  // `definitions`, see below), but inside the property `artifacts` of that context.
9
9
 
10
- // The define phase (function 'define' below) enriches a dictionary of
10
+ // The 'define' phase (function 'define' below) enriches a dictionary of
11
11
  // (file names to) AST-like CSNs and restructure them a little bit, the result
12
12
  // is called "augmented CSN":
13
13
  // { sources: <dictionary of ASTs>, definitions: <dictionary of artifact defs> }
@@ -194,7 +194,7 @@ function define( model ) {
194
194
  dictForEach( model.definitions, initArtifact );
195
195
  dictForEach( model.vocabularies, initVocabulary );
196
196
 
197
- mergeI18nBlocks( model );
197
+ mergeI18nBlocks();
198
198
  }
199
199
 
200
200
  // Phase 1: ----------------------------------------------------------------
@@ -213,9 +213,8 @@ function define( model ) {
213
213
  let namespace = src.namespace && src.namespace.path;
214
214
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
215
215
  if (isInReservedNamespace(prefix)) {
216
- error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
217
- // TODO: use $(NAME)
218
- 'The namespace "cds" is reserved for CDS builtins' );
216
+ error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
217
+ 'The namespace $(NAME) is reserved for CDS builtins' );
219
218
  namespace = null;
220
219
  }
221
220
  if (src.$frontend !== 'json') { // CDL input
@@ -249,9 +248,8 @@ function define( model ) {
249
248
  const { absolute } = art.name;
250
249
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
251
250
  if (absolute === 'cds' || isInReservedNamespace(absolute)) {
252
- error( 'reserved-namespace-cds', [ art.name.location, art ], {},
253
- // TODO: use $(NAME)
254
- 'The namespace "cds" is reserved for CDS builtins' );
251
+ error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
252
+ 'The namespace $(NAME) is reserved for CDS builtins' );
255
253
  const builtin = model.definitions[absolute];
256
254
  if (builtin && builtin.builtin) // if already a builtin...
257
255
  return;
@@ -259,8 +257,6 @@ function define( model ) {
259
257
  }
260
258
  else if (art.query && (absolute === 'localized' || absolute.startsWith( 'localized.' ))) {
261
259
  // Due to recompilation, we don't emit this info message for JSON frontend.
262
- // TODO: generalize this for $generated (definitions starting with
263
- // "localized" just have `$generated: true` as default)
264
260
  if (block.$frontend !== 'json') {
265
261
  info( 'ignored-localized-definition', [ art.name.location, art ], {},
266
262
  'This definition in the namespace "localized" is ignored' );
@@ -419,6 +415,8 @@ function define( model ) {
419
415
  }
420
416
 
421
417
  function initNamespaceAndUsing( src ) {
418
+ if (src.$frontend && src.$frontend !== 'cdl')
419
+ return;
422
420
  if (src.namespace) {
423
421
  const decl = src.namespace;
424
422
  const { path } = decl;
@@ -478,12 +476,16 @@ function define( model ) {
478
476
  if (!setLink( art, '_leadingQuery', initQueryExpression( art.query, art ) ) )
479
477
  return; // null or undefined in case of parse error
480
478
  setLink( art._leadingQuery, '_$next', art );
481
- // the following we be removed soon if we have:
479
+ // the following may be removed soon if we have:
482
480
  // view elements as proxies to elements of leading query
483
481
  if (art.elements) { // specified element via compilation of client-style CSN
484
482
  setLink( art, 'elements$', art.elements );
485
483
  delete art.elements;
486
484
  }
485
+ if (art.enum) { // specified enum via compilation of client-style CSN
486
+ setLink( art, 'enum$', art.enum );
487
+ delete art.enum;
488
+ }
487
489
  }
488
490
 
489
491
  function initVocabulary( art ) {
@@ -607,13 +609,13 @@ function define( model ) {
607
609
  defineAnnotations( col, col, parent._block );
608
610
  if (col.inline) { // `@anno elem.{ * }` does not work
609
611
  if (col.doc)
610
- warning( 'syntax-anno-ignored', [ col.doc.location, col ], { '#': 'doc' } );
612
+ warning( 'syntax-ignoring-anno', [ col.doc.location, col ], { '#': 'doc' } );
611
613
 
612
614
  // col.$annotations no available for CSN input, have to search.
613
615
  // Warning about first annotation should be enough to avoid spam.
614
616
  const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
615
617
  if (firstAnno)
616
- warning( 'syntax-anno-ignored', [ col[firstAnno].name.location, col ] );
618
+ warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ] );
617
619
  }
618
620
  // TODO: allow sub queries? at least in top-level expand without parallel ref
619
621
  if (columns)
@@ -658,7 +660,7 @@ function define( model ) {
658
660
 
659
661
  /**
660
662
  * If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
661
- * since we will have a top-level subquery after exists-processing in the forHanaNew.
663
+ * since we will have a top-level subquery after exists-processing in the forRelationalDB.
662
664
  *
663
665
  * Recursively drill down into:
664
666
  * - the .path
@@ -886,17 +888,21 @@ function define( model ) {
886
888
  /**
887
889
  * Set property `_parent` for all elements in `parent` to `parent` and do so
888
890
  * recursively for all sub elements.
891
+ *
892
+ * If not for extensions: construct === parent
889
893
  */
890
- // If not for extensions: construct === parent
891
894
  function initMembers( construct, parent, block, initExtensions = false ) {
892
895
  // TODO: split extend from init
893
896
  const isQueryExtension = kindProperties[construct.kind].isExtension &&
894
897
  (parent._main || parent).query;
895
898
  let obj = construct;
896
- if (obj.items) { // TODO: while
897
- setLink( obj.items, '_outer', obj );
898
- obj = obj.items; // TODO: probably set parent = obj (is so in csnRefs)
899
- setLink( obj, '_block', block );
899
+ let { items } = obj;
900
+ while (items) {
901
+ setLink( items, '_outer', obj );
902
+ setLink( items, '_parent', obj._parent );
903
+ setLink( items, '_block', block );
904
+ obj = items;
905
+ items = obj.items;
900
906
  }
901
907
  if (obj.target && targetIsTargetAspect( obj )) {
902
908
  obj.targetAspect = obj.target;
@@ -913,7 +919,7 @@ function define( model ) {
913
919
  if (obj.on && !obj.target) {
914
920
  error( 'unexpected-on-for-composition', [ obj.on.location, construct ],
915
921
  {},
916
- 'A managed aspect composition can\'t have a specified ON condition' );
922
+ 'A managed aspect composition can\'t have a specified ON-condition' );
917
923
  delete obj.on; // continuation semantics: not specified
918
924
  }
919
925
  if (targetAspect.elements) {
@@ -1017,7 +1023,7 @@ function define( model ) {
1017
1023
  setMemberParent( elem, name, parent, construct !== parent && prop );
1018
1024
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1019
1025
  checkRedefinition( elem );
1020
- if (elem.kind === 'annotate')
1026
+ if (elem.kind === 'annotate' || elem.kind === 'extend')
1021
1027
  checkAnnotate( elem, elem );
1022
1028
  defineAnnotations( elem, elem, bl );
1023
1029
  initMembers( elem, elem, bl, initExtensions );
@@ -1039,7 +1045,7 @@ function define( model ) {
1039
1045
  std: 'Elements can\'t have a value',
1040
1046
  entity: 'Entity elements can\'t have a value',
1041
1047
  type: 'Type elements can\'t have a value',
1042
- extend: 'Cannot extend type/entity elements with values',
1048
+ extend: 'Can\'t extend type/entity elements with values',
1043
1049
  });
1044
1050
  return;
1045
1051
  }
@@ -1072,8 +1078,11 @@ function define( model ) {
1072
1078
  'Actions and functions only exist top-level and for entities' );
1073
1079
  }
1074
1080
  else if (parent.kind === 'action' || parent.kind === 'function') {
1075
- error( 'extend-action', [ construct.location, construct ], {},
1076
- 'Actions and functions can\'t be extended, only annotated' );
1081
+ error( 'extend-action', [ construct.location, construct ], { '#': parent.kind }, {
1082
+ std: 'Actions and functions can\'t be extended, only annotated',
1083
+ action: 'Actions can\'t be extended, only annotated',
1084
+ function: 'Functions can\'t be extended, only annotated',
1085
+ } );
1077
1086
  }
1078
1087
  else if (prop === 'params') {
1079
1088
  if (!feature) {
@@ -1105,29 +1114,27 @@ function define( model ) {
1105
1114
  }
1106
1115
  return construct === parent;
1107
1116
  }
1108
- }
1109
1117
 
1110
- /**
1111
- * Merge (optional) translations into the XSN model.
1112
- *
1113
- * @param {XSN.Model} model
1114
- */
1115
- function mergeI18nBlocks( model ) {
1116
- const sortedSources = Object.keys(model.sources)
1117
- .filter(name => !!model.sources[name].i18n)
1118
- .sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
1118
+ /**
1119
+ * Merge (optional) translations into the XSN model.
1120
+ */
1121
+ function mergeI18nBlocks() {
1122
+ const sortedSources = Object.keys(model.sources)
1123
+ .filter(name => !!model.sources[name].i18n)
1124
+ .sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
1119
1125
 
1120
- if (sortedSources.length === 0)
1121
- return;
1126
+ if (sortedSources.length === 0)
1127
+ return;
1122
1128
 
1123
- if (!model.i18n)
1124
- model.i18n = Object.create( null );
1129
+ if (!model.i18n)
1130
+ model.i18n = Object.create( null );
1125
1131
 
1126
- for (const name of sortedSources)
1127
- initI18nFromSource( model.sources[name] );
1132
+ for (const name of sortedSources)
1133
+ initI18nFromSource( model.sources[name] );
1134
+ }
1128
1135
 
1129
1136
  /**
1130
- * Add the source's translations to the model. Warns if the sources translations
1137
+ * Add the source's translations to the model. Warns if the source's translations
1131
1138
  * do not match the ones from previous sources.
1132
1139
  *
1133
1140
  * @param {XSN.SourceAst} src
@@ -1144,9 +1151,8 @@ function mergeI18nBlocks( model ) {
1144
1151
  model.i18n[langKey][textKey] = sourceVal;
1145
1152
  }
1146
1153
  else if (modelVal.val !== sourceVal.val) {
1147
- // TODO: put mergeI18nBlocks() into main function instead
1148
- model.$messageFunctions.warning( 'i18n-different-value', sourceVal.location,
1149
- { prop: textKey, otherprop: langKey } );
1154
+ warning( 'i18n-different-value', sourceVal.location,
1155
+ { prop: textKey, otherprop: langKey } );
1150
1156
  }
1151
1157
  }
1152
1158
  }