@sap/cds-compiler 3.1.2 → 3.4.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 (117) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/bin/cdsc.js +4 -2
  3. package/doc/CHANGELOG_BETA.md +35 -0
  4. package/lib/api/main.js +153 -29
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/error.js +2 -2
  8. package/lib/base/keywords.js +106 -24
  9. package/lib/base/message-registry.js +177 -79
  10. package/lib/base/messages.js +78 -57
  11. package/lib/base/model.js +2 -1
  12. package/lib/checks/actionsFunctions.js +1 -1
  13. package/lib/checks/annotationsOData.js +2 -2
  14. package/lib/checks/arrayOfs.js +15 -7
  15. package/lib/checks/cdsPersistence.js +1 -1
  16. package/lib/checks/checkForTypes.js +53 -0
  17. package/lib/checks/defaultValues.js +4 -2
  18. package/lib/checks/elements.js +81 -6
  19. package/lib/checks/foreignKeys.js +12 -13
  20. package/lib/checks/invalidTarget.js +10 -11
  21. package/lib/checks/managedInType.js +21 -15
  22. package/lib/checks/nullableKeys.js +1 -1
  23. package/lib/checks/onConditions.js +9 -9
  24. package/lib/checks/parameters.js +23 -0
  25. package/lib/checks/queryNoDbArtifacts.js +1 -1
  26. package/lib/checks/selectItems.js +1 -1
  27. package/lib/checks/sql-snippets.js +12 -10
  28. package/lib/checks/types.js +2 -2
  29. package/lib/checks/utils.js +17 -7
  30. package/lib/checks/validator.js +36 -14
  31. package/lib/compiler/assert-consistency.js +21 -13
  32. package/lib/compiler/builtins.js +8 -0
  33. package/lib/compiler/checks.js +57 -40
  34. package/lib/compiler/define.js +139 -69
  35. package/lib/compiler/extend.js +319 -50
  36. package/lib/compiler/finalize-parse-cdl.js +14 -9
  37. package/lib/compiler/kick-start.js +2 -35
  38. package/lib/compiler/populate.js +111 -68
  39. package/lib/compiler/propagator.js +5 -3
  40. package/lib/compiler/resolve.js +71 -108
  41. package/lib/compiler/shared.js +82 -54
  42. package/lib/compiler/tweak-assocs.js +26 -14
  43. package/lib/compiler/utils.js +13 -2
  44. package/lib/edm/annotations/genericTranslation.js +10 -7
  45. package/lib/edm/csn2edm.js +11 -11
  46. package/lib/edm/edm.js +17 -9
  47. package/lib/edm/edmPreprocessor.js +53 -30
  48. package/lib/edm/edmUtils.js +7 -2
  49. package/lib/gen/Dictionary.json +14 -0
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -2
  52. package/lib/gen/languageParser.js +4312 -4186
  53. package/lib/inspect/inspectModelStatistics.js +1 -1
  54. package/lib/inspect/inspectPropagation.js +23 -9
  55. package/lib/json/csnVersion.js +13 -13
  56. package/lib/json/from-csn.js +161 -172
  57. package/lib/json/to-csn.js +70 -10
  58. package/lib/language/.eslintrc.json +4 -0
  59. package/lib/language/antlrParser.js +8 -11
  60. package/lib/language/docCommentParser.js +1 -2
  61. package/lib/language/errorStrategy.js +54 -27
  62. package/lib/language/genericAntlrParser.js +140 -93
  63. package/lib/language/language.g4 +57 -33
  64. package/lib/language/multiLineStringParser.js +75 -63
  65. package/lib/main.d.ts +3 -6
  66. package/lib/main.js +1 -0
  67. package/lib/model/.eslintrc.json +13 -0
  68. package/lib/model/api.js +4 -2
  69. package/lib/model/csnRefs.js +78 -50
  70. package/lib/model/csnUtils.js +272 -222
  71. package/lib/model/enrichCsn.js +41 -31
  72. package/lib/model/revealInternalProperties.js +61 -57
  73. package/lib/model/sortViews.js +35 -31
  74. package/lib/modelCompare/compare.js +52 -18
  75. package/lib/modelCompare/filter.js +83 -0
  76. package/lib/optionProcessor.js +10 -1
  77. package/lib/render/manageConstraints.js +11 -7
  78. package/lib/render/toCdl.js +151 -106
  79. package/lib/render/toHdbcds.js +8 -6
  80. package/lib/render/toRename.js +4 -4
  81. package/lib/render/toSql.js +17 -7
  82. package/lib/render/utils/common.js +27 -9
  83. package/lib/render/utils/sql.js +5 -5
  84. package/lib/sql-identifier.js +7 -0
  85. package/lib/transform/db/applyTransformations.js +32 -3
  86. package/lib/transform/db/assertUnique.js +27 -38
  87. package/lib/transform/db/expansion.js +92 -41
  88. package/lib/transform/db/flattening.js +1 -1
  89. package/lib/transform/db/temporal.js +3 -1
  90. package/lib/transform/db/transformExists.js +8 -2
  91. package/lib/transform/db/views.js +42 -13
  92. package/lib/transform/draft/db.js +2 -2
  93. package/lib/transform/forOdataNew.js +10 -7
  94. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
  95. package/lib/transform/localized.js +29 -20
  96. package/lib/transform/odata/toFinalBaseType.js +8 -11
  97. package/lib/transform/odata/typesExposure.js +2 -1
  98. package/lib/transform/parseExpr.js +245 -0
  99. package/lib/transform/transformUtilsNew.js +122 -51
  100. package/lib/transform/translateAssocsToJoins.js +17 -16
  101. package/lib/utils/moduleResolve.js +5 -5
  102. package/lib/utils/objectUtils.js +3 -3
  103. package/lib/utils/term.js +5 -5
  104. package/package.json +2 -2
  105. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  106. package/share/messages/check-proper-type-of.md +4 -4
  107. package/share/messages/check-proper-type.md +2 -2
  108. package/share/messages/duplicate-autoexposed.md +4 -4
  109. package/share/messages/extend-repeated-intralayer.md +4 -5
  110. package/share/messages/extend-unrelated-layer.md +4 -4
  111. package/share/messages/message-explanations.json +3 -1
  112. package/share/messages/redirected-to-ambiguous.md +7 -6
  113. package/share/messages/redirected-to-complex.md +63 -0
  114. package/share/messages/redirected-to-unrelated.md +6 -5
  115. package/share/messages/rewrite-not-supported.md +4 -4
  116. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
  117. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -18,6 +18,8 @@ const builtins = require('../compiler/builtins');
18
18
  const {
19
19
  forEachGeneric, forEachDefinition, forEachMember,
20
20
  } = require('../base/model');
21
+ const { CompilerAssertion } = require('../base/error');
22
+ const { pathName } = require('./utils');
21
23
 
22
24
  function check( model ) { // = XSN
23
25
  const {
@@ -52,7 +54,7 @@ function check( model ) { // = XSN
52
54
  checkLocalizedSubElement(elem);
53
55
  if (elem.key && elem.key.val) {
54
56
  if (elem.virtual && elem.virtual.val)
55
- error(null, [ elem.location, elem ], 'Element can\'t be virtual and key');
57
+ error(null, [ elem.location, elem ], {}, 'Element can\'t be virtual and key');
56
58
 
57
59
  checkForUnmanagedAssociations( elem, elem.key );
58
60
  }
@@ -66,26 +68,26 @@ function check( model ) { // = XSN
66
68
  forEachGeneric( elem, 'elements', checkElement );
67
69
  }
68
70
 
69
- function checkName( construct ) {
71
+ function checkName( construct ) { // TODO: move to define.js
70
72
  if (model.options.$skipNameCheck)
71
73
  return;
72
74
  // TODO: Move a corrected version of this check to definer (but do not rely
73
75
  // on it!): The code below misses to consider CSN input.
74
76
  if (construct.name.id && construct.name.id.indexOf('.') !== -1) {
75
77
  // TODO: No, we should not forbid this
76
- error(null, [ construct.name.location, construct ],
78
+ error(null, [ construct.name.location, construct ], {},
77
79
  'The character \'.\' is not allowed in identifiers');
78
80
  }
79
81
  }
80
82
 
81
- // TODO: move into definer.js
82
83
  function checkLocalizedElement(elem) {
83
84
  // if it is directly a localized element
84
85
  if (elem.localized && elem.localized.val) {
85
86
  const type = elem._effectiveType;
87
+ // See discussion issue #6520: should we allow all scalar types?
86
88
  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');
89
+ warning(null, [ elem.type?.location, elem ], { keyword: 'localized' },
90
+ 'Keyword $(KEYWORD) should only be used in combination with string types');
89
91
  }
90
92
  }
91
93
  // "key" keyword at localized element in SELECT list.
@@ -159,7 +161,7 @@ function check( model ) { // = XSN
159
161
  // Special handling to print a more detailed error message.
160
162
  // Other cases like `null` as enum value are handled in `checkEnumValueType()`
161
163
  if (type === 'enum') {
162
- warning('enum-value-ref', [ loc, enumNode ],
164
+ warning('enum-value-ref', [ loc, enumNode ], {},
163
165
  'References to other values are not allowed as enum values');
164
166
  return;
165
167
  }
@@ -339,6 +341,7 @@ function check( model ) { // = XSN
339
341
  }
340
342
 
341
343
  // Check that min and max cardinalities of 'elem' in 'art' have legal values
344
+ // TODO: move to define.js or parsers
342
345
  function checkCardinality(elem) {
343
346
  if (!elem.cardinality)
344
347
  return;
@@ -382,13 +385,16 @@ function check( model ) { // = XSN
382
385
 
383
386
  // If provided, min cardinality must not exceed max cardinality (note that
384
387
  // '*' is considered to be >= any number)
385
- const pair = [ [ 'sourceMin', 'sourceMax', 'Source' ], [ 'targetMin', 'targetMax', 'Target' ] ];
388
+ const pair = [ [ 'sourceMin', 'sourceMax', 'source' ], [ 'targetMin', 'targetMax', 'target' ] ];
386
389
  pair.forEach((p) => {
387
390
  if (elem.cardinality[p[0]] && elem.cardinality[p[1]] &&
388
391
  elem.cardinality[p[1]].literal === 'number' &&
389
392
  elem.cardinality[p[0]].val > elem.cardinality[p[1]].val) {
390
- error(null, [ elem.cardinality.location, elem ],
391
- `${ p[2] } minimum cardinality must not be greater than ${ p[2].toLowerCase() } maximum cardinality`);
393
+ error(null, [ elem.cardinality.location, elem ], { '#': p[2] }, {
394
+ std: 'Minimum cardinality must not be greater than maximum cardinality', // variant unused
395
+ source: 'Source minimum cardinality must not be greater than source maximum cardinality',
396
+ target: 'Target minimum cardinality must not be greater than target maximum cardinality',
397
+ });
392
398
  }
393
399
  });
394
400
  }
@@ -422,7 +428,7 @@ function check( model ) { // = XSN
422
428
  if (groupByEntry._artifact && groupByEntry._artifact._effectiveType &&
423
429
  groupByEntry._artifact._effectiveType.on) {
424
430
  // Unmanaged association - complain
425
- error(null, [ groupByEntry.location, art ],
431
+ error(null, [ groupByEntry.location, art ], {},
426
432
  'Unmanaged associations are not allowed in GROUP BY');
427
433
  }
428
434
  }
@@ -430,7 +436,7 @@ function check( model ) { // = XSN
430
436
  if (orderByEntry._artifact && orderByEntry._artifact._effectiveType &&
431
437
  orderByEntry._artifact._effectiveType.on) {
432
438
  // Unmanaged association - complain
433
- error(null, [ orderByEntry.location, art ],
439
+ error(null, [ orderByEntry.location, art ], {},
434
440
  'Unmanaged associations are not allowed in ORDER BY');
435
441
  }
436
442
  }
@@ -459,7 +465,7 @@ function check( model ) { // = XSN
459
465
  for (const k in elem.foreignKeys) {
460
466
  const key = elem.foreignKeys[k].targetElement;
461
467
  if (key && key._artifact && key._artifact.virtual && key._artifact.virtual.val) {
462
- error(null, [ key.location, elem ],
468
+ error(null, [ key.location, elem ], {},
463
469
  'Virtual elements can\'t be used as a foreign key for a managed association');
464
470
  }
465
471
  }
@@ -494,7 +500,7 @@ function check( model ) { // = XSN
494
500
  // associations can be followed (in the ON condition)
495
501
  //
496
502
  // TODO: this function must be completely reworked, probably even before
497
- // integration into name resulution - did the first step.
503
+ // integration into name resolution - did the first step.
498
504
  // It is also incomplete, as associations in structures are not checked.
499
505
  // Additionally, `$self.assoc` references are also not found.
500
506
  function singleCheckUnmanagedAssocCondArgumentNoFollowUnmanagedAssoc(elem, arg, op) {
@@ -517,7 +523,7 @@ function check( model ) { // = XSN
517
523
  if (argTarget.on) {
518
524
  const same = path0._artifact === elem;
519
525
  if (!same) {
520
- error(null, [ path0.location, elem ],
526
+ error(null, [ path0.location, elem ], {},
521
527
  'Unmanaged association condition can\'t follow another unmanaged association');
522
528
  }
523
529
  }
@@ -527,7 +533,7 @@ function check( model ) { // = XSN
527
533
  if (op && op.val === 'xpr') // no check for xpr
528
534
  return;
529
535
  if (op && op.val !== '=')
530
- error(null, [ op.location, elem ], '$self comparison is only allowed with \'=\'');
536
+ error(null, [ op.location, elem ], {}, '$self comparison is only allowed with \'=\'');
531
537
  }
532
538
 
533
539
  // A function like this could be part of the compiler
@@ -601,7 +607,7 @@ function check( model ) { // = XSN
601
607
  // Check for illegal argument usage within the expression
602
608
  for (const arg of args) {
603
609
  if (isVirtualElement(arg))
604
- error(null, arg.location, 'Virtual elements can\'t be used in an expression');
610
+ error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
605
611
 
606
612
 
607
613
  // Recursively traverse the argument expression
@@ -624,20 +630,25 @@ function check( model ) { // = XSN
624
630
  // Check for illegal argument usage within the expression
625
631
  for (const arg of Array.isArray(xpr.args) && xpr.args || []) { // TODO named args?
626
632
  if (isVirtualElement(arg))
627
- error(null, arg.location, 'Virtual elements can\'t be used in an expression');
633
+ error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
628
634
 
629
635
  // Arg must not be an association and not $self
630
636
  // Only if path is not approved exists path (that is non-query position)
631
637
  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');
638
+ if (arg.$expected === 'exists') {
639
+ error(null, arg.location, {},
640
+ 'An association can\'t be used as a value in an expression');
641
+ }
634
642
  }
635
643
  else if (!allowAssocTail && isAssociationOperand(arg)) {
636
- error(null, arg.location, 'An association can\'t be used as a value in an expression');
644
+ error(null, arg.location, {},
645
+ 'An association can\'t be used as a value in an expression');
637
646
  }
638
647
 
639
- 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`);
648
+ if (isDollarSelfOrProjectionOperand(arg)) {
649
+ error(null, arg.location, { id: arg.path[0].id },
650
+ '$(ID) can only be used as a value in a comparison to an association');
651
+ }
641
652
 
642
653
  // Recursively traverse the argument expression
643
654
  checkTreeLikeExpression(arg, allowAssocTail);
@@ -736,7 +747,9 @@ function check( model ) { // = XSN
736
747
 
737
748
  // Element must exist in annotation
738
749
  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 }"`);
750
+ warning(null, anno.location || anno.name.location,
751
+ { name: pathName(anno.name.path), anno: annoDecl.name.absolute },
752
+ 'Element $(NAME) not found for annotation $(ANNO)');
740
753
  return;
741
754
  }
742
755
 
@@ -747,10 +760,14 @@ function check( model ) { // = XSN
747
760
 
748
761
  // Must have literal or path unless it is a boolean
749
762
  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');
763
+ if (elementDecl.type && elementDecl.type._artifact.name.absolute) {
764
+ warning(null, anno.location || anno.name.location, { type: elementDecl.type._artifact },
765
+ 'Expecting a value of type $(TYPE) for the annotation');
766
+ }
767
+ else {
768
+ warning(null, anno.location || anno.name.location, {},
769
+ 'Expecting a value for the annotation');
770
+ }
754
771
 
755
772
  return;
756
773
  }
@@ -775,7 +792,7 @@ function check( model ) { // = XSN
775
792
  if (elementDecl._effectiveType.items) {
776
793
  // Make sure we have an array value
777
794
  if (value.literal !== 'array') {
778
- warning(null, loc, 'An array value is required here');
795
+ warning(null, loc, {}, 'An array value is required here');
779
796
  return;
780
797
  }
781
798
  // Check each element
@@ -788,7 +805,7 @@ function check( model ) { // = XSN
788
805
  // Struct expected (can only happen within arrays)?
789
806
  if (elementDecl._effectiveType.elements) {
790
807
  if (value.literal !== 'struct') {
791
- warning(null, loc, 'A struct value is required here');
808
+ warning(null, loc, {}, 'A struct value is required here');
792
809
  return;
793
810
  }
794
811
  // FIXME: Should check each element
@@ -800,31 +817,31 @@ function check( model ) { // = XSN
800
817
  if (builtins.isStringTypeName(type)) {
801
818
  if (value.literal !== 'string' && value.literal !== 'enum' &&
802
819
  !elementDecl._effectiveType.enum)
803
- warning(null, loc, `A string value is required for type "${ type }"`);
820
+ warning(null, loc, { type }, 'A string value is required for type $(TYPE)');
804
821
  }
805
822
  else if (builtins.isBinaryTypeName(type)) {
806
823
  if (value.literal !== 'string' && value.literal !== 'x')
807
- warning(null, loc, `A hexadecimal string value is required for type "${ type }"`);
824
+ warning(null, loc, { type }, 'A hexadecimal string value is required for type $(TYPE)');
808
825
  }
809
826
  else if (builtins.isNumericTypeName(type)) {
810
827
  if (value.literal !== 'number' && value.literal !== 'enum' &&
811
828
  !elementDecl._effectiveType.enum)
812
- warning(null, loc, `A numerical value is required for type "${ type }"`);
829
+ warning(null, loc, { type }, 'A numerical value is required for type $(TYPE)');
813
830
  }
814
831
  else if (builtins.isDateOrTimeTypeName(type)) {
815
832
  if (value.literal !== 'date' && value.literal !== 'time' &&
816
833
  value.literal !== 'timestamp' && value.literal !== 'string')
817
- warning(null, loc, `A date/time value or a string is required for type "${ type }"`);
834
+ warning(null, loc, { type }, 'A date/time value or a string is required for type $(TYPE)');
818
835
  }
819
836
  else if (builtins.isBooleanTypeName(type)) {
820
837
  if (value.literal && value.literal !== 'boolean')
821
- warning(null, loc, `A boolean value is required for type "${ type }"`);
838
+ warning(null, loc, { type }, 'A boolean value is required for type $(TYPE)');
822
839
  }
823
840
  else if (builtins.isRelationTypeName(type) || builtins.isGeoTypeName(type)) {
824
- warning(null, loc, `Type "${ type }" can't be assigned a value`);
841
+ warning(null, loc, { type }, 'Type $(TYPE) can\'t be assigned a value');
825
842
  }
826
843
  else {
827
- throw new Error(`Unknown primitive type name: ${ type }`);
844
+ throw new CompilerAssertion(`Unknown primitive type name: ${ type }`);
828
845
  }
829
846
 
830
847
  // Check enums
@@ -834,19 +851,19 @@ function check( model ) { // = XSN
834
851
  // Enum symbol provided and expected
835
852
  if (!expectedEnum[value.sym.id]) {
836
853
  // .. but no such constant
837
- warning(null, loc, `Enum symbol "#${ value.sym.id }" not found in enum`);
854
+ warning(null, loc, { id: `#${ value.sym.id }` }, 'Enum symbol $(ID) not found in enum');
838
855
  }
839
856
  }
840
857
  else {
841
858
  // Enum symbol provided but not expected
842
- warning(null, loc, `Cannot use enum symbol "#${ value.sym.id }" for non-enum type "${ type }"`);
859
+ warning(null, loc, { id: `#${ value.sym.id }`, type }, 'Can\'t use enum symbol $(ID) for non-enum type $(TYPE)');
843
860
  }
844
861
  }
845
862
  else if (expectedEnum) {
846
863
  // Enum symbol not provided but expected
847
864
  if (!Object.keys(expectedEnum).some(symbol => expectedEnum[symbol].value.val === value.val)) {
848
865
  // ... and none of the valid enum symbols matches the value
849
- warning(null, loc, 'An enum value is required here');
866
+ warning(null, loc, {}, 'An enum value is required here');
850
867
  }
851
868
  }
852
869
  }