@sap/cds-compiler 3.4.4 → 3.5.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 (129) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +12 -12
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +9 -1
  7. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  8. package/lib/api/main.js +58 -59
  9. package/lib/api/options.js +4 -2
  10. package/lib/api/validate.js +2 -2
  11. package/lib/base/cleanSymbols.js +2 -3
  12. package/lib/base/dictionaries.js +6 -6
  13. package/lib/base/error.js +2 -2
  14. package/lib/base/keywords.js +6 -6
  15. package/lib/base/location.js +11 -12
  16. package/lib/base/message-registry.js +124 -28
  17. package/lib/base/messages.js +247 -179
  18. package/lib/base/model.js +14 -11
  19. package/lib/base/node-helpers.js +9 -10
  20. package/lib/base/optionProcessorHelper.js +138 -129
  21. package/lib/checks/actionsFunctions.js +5 -5
  22. package/lib/checks/annotationsOData.js +4 -4
  23. package/lib/checks/arrayOfs.js +1 -1
  24. package/lib/checks/cdsPersistence.js +1 -1
  25. package/lib/checks/checkForTypes.js +3 -3
  26. package/lib/checks/defaultValues.js +3 -3
  27. package/lib/checks/elements.js +7 -7
  28. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  29. package/lib/checks/foreignKeys.js +1 -1
  30. package/lib/checks/invalidTarget.js +4 -4
  31. package/lib/checks/managedInType.js +1 -1
  32. package/lib/checks/managedWithoutKeys.js +1 -1
  33. package/lib/checks/nonexpandableStructured.js +5 -3
  34. package/lib/checks/nullableKeys.js +1 -1
  35. package/lib/checks/onConditions.js +5 -6
  36. package/lib/checks/parameters.js +1 -1
  37. package/lib/checks/queryNoDbArtifacts.js +2 -2
  38. package/lib/checks/selectItems.js +4 -4
  39. package/lib/checks/sql-snippets.js +4 -4
  40. package/lib/checks/types.js +7 -7
  41. package/lib/checks/utils.js +4 -4
  42. package/lib/checks/validator.js +16 -13
  43. package/lib/compiler/.eslintrc.json +1 -1
  44. package/lib/compiler/assert-consistency.js +0 -1
  45. package/lib/compiler/builtins.js +1 -1
  46. package/lib/compiler/checks.js +73 -15
  47. package/lib/compiler/define.js +3 -7
  48. package/lib/compiler/extend.js +212 -32
  49. package/lib/compiler/finalize-parse-cdl.js +7 -2
  50. package/lib/compiler/index.js +17 -14
  51. package/lib/compiler/populate.js +2 -5
  52. package/lib/compiler/propagator.js +2 -0
  53. package/lib/compiler/shared.js +23 -12
  54. package/lib/compiler/tweak-assocs.js +5 -6
  55. package/lib/compiler/utils.js +6 -0
  56. package/lib/edm/annotations/genericTranslation.js +553 -319
  57. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  58. package/lib/edm/csn2edm.js +88 -75
  59. package/lib/edm/edm.js +17 -3
  60. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  61. package/lib/edm/edmPreprocessor.js +106 -76
  62. package/lib/edm/edmUtils.js +41 -2
  63. package/lib/gen/Dictionary.json +34 -0
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +66 -63
  66. package/lib/gen/language.tokens +81 -81
  67. package/lib/gen/languageLexer.interp +4 -10
  68. package/lib/gen/languageLexer.js +854 -869
  69. package/lib/gen/languageLexer.tokens +79 -81
  70. package/lib/gen/languageParser.js +14360 -14146
  71. package/lib/inspect/inspectModelStatistics.js +2 -2
  72. package/lib/inspect/inspectPropagation.js +6 -6
  73. package/lib/inspect/inspectUtils.js +2 -2
  74. package/lib/json/from-csn.js +82 -40
  75. package/lib/json/to-csn.js +82 -157
  76. package/lib/language/.eslintrc.json +1 -4
  77. package/lib/language/genericAntlrParser.js +59 -38
  78. package/lib/language/language.g4 +1508 -1490
  79. package/lib/language/multiLineStringParser.js +1 -1
  80. package/lib/main.js +3 -3
  81. package/lib/model/csnUtils.js +130 -122
  82. package/lib/model/revealInternalProperties.js +1 -1
  83. package/lib/model/sortViews.js +4 -6
  84. package/lib/modelCompare/utils/filter.js +4 -3
  85. package/lib/optionProcessor.js +5 -0
  86. package/lib/render/DuplicateChecker.js +1 -1
  87. package/lib/render/manageConstraints.js +12 -12
  88. package/lib/render/toCdl.js +225 -159
  89. package/lib/render/toHdbcds.js +63 -63
  90. package/lib/render/toRename.js +5 -5
  91. package/lib/render/toSql.js +55 -65
  92. package/lib/render/utils/common.js +20 -37
  93. package/lib/render/utils/delta.js +3 -3
  94. package/lib/render/utils/sql.js +22 -6
  95. package/lib/render/utils/stringEscapes.js +3 -3
  96. package/lib/transform/db/applyTransformations.js +3 -3
  97. package/lib/transform/db/assertUnique.js +13 -12
  98. package/lib/transform/db/associations.js +5 -5
  99. package/lib/transform/db/cdsPersistence.js +10 -8
  100. package/lib/transform/db/constraints.js +14 -14
  101. package/lib/transform/db/expansion.js +20 -22
  102. package/lib/transform/db/flattening.js +24 -42
  103. package/lib/transform/db/groupByOrderBy.js +3 -3
  104. package/lib/transform/db/temporal.js +6 -6
  105. package/lib/transform/db/transformExists.js +23 -23
  106. package/lib/transform/db/views.js +16 -16
  107. package/lib/transform/draft/db.js +10 -10
  108. package/lib/transform/draft/odata.js +2 -2
  109. package/lib/transform/forOdataNew.js +12 -40
  110. package/lib/transform/forRelationalDB.js +17 -7
  111. package/lib/transform/localized.js +2 -2
  112. package/lib/transform/odata/toFinalBaseType.js +41 -27
  113. package/lib/transform/odata/typesExposure.js +106 -62
  114. package/lib/transform/parseExpr.js +209 -106
  115. package/lib/transform/transformUtilsNew.js +2 -2
  116. package/lib/transform/translateAssocsToJoins.js +24 -19
  117. package/lib/transform/universalCsn/coreComputed.js +10 -10
  118. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  119. package/lib/transform/universalCsn/utils.js +3 -3
  120. package/lib/utils/file.js +5 -5
  121. package/lib/utils/moduleResolve.js +13 -13
  122. package/lib/utils/objectUtils.js +6 -6
  123. package/lib/utils/term.js +5 -2
  124. package/lib/utils/timetrace.js +51 -24
  125. package/package.json +5 -7
  126. package/share/messages/check-proper-type-of.md +1 -1
  127. package/share/messages/message-explanations.json +1 -1
  128. package/share/messages/redirected-to-complex.md +4 -4
  129. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
@@ -10,7 +10,7 @@ const { term } = require('../utils/term');
10
10
  * @param {CSN.Options} options
11
11
  * @returns {string}
12
12
  */
13
- function inspectModelStatistics(xsn, options) {
13
+ function inspectModelStatistics( xsn, options ) {
14
14
  let result = '';
15
15
 
16
16
  // Default color mode is 'auto'
@@ -45,7 +45,7 @@ ${ color.underline('vocabularies') }: ${ Object.keys(xsn.vocabularies || {}).len
45
45
  return result;
46
46
  }
47
47
 
48
- function countDefinitionKinds(xsn) {
48
+ function countDefinitionKinds( xsn ) {
49
49
  const result = {
50
50
  definitions: 0,
51
51
  entity: 0,
@@ -11,7 +11,7 @@ const { term } = require('../utils/term');
11
11
  * @param {string} artifactName
12
12
  * @returns {string|null}
13
13
  */
14
- function inspectPropagation(xsn, options, artifactName) {
14
+ function inspectPropagation( xsn, options, artifactName ) {
15
15
  const { error } = createMessageFunctions(options, 'inspect', xsn);
16
16
  const result = [];
17
17
 
@@ -56,7 +56,7 @@ function inspectPropagation(xsn, options, artifactName) {
56
56
  @param {string} indent
57
57
  * @returns {string[]}
58
58
  */
59
- function _indent(lines, indent = ' ') {
59
+ function _indent( lines, indent = ' ' ) {
60
60
  return lines.map(str => `${ indent }${ str }`);
61
61
  }
62
62
 
@@ -65,7 +65,7 @@ function _indent(lines, indent = ' ') {
65
65
  * @returns {string[]}
66
66
  * @private
67
67
  */
68
- function _inspectAnnotations(artifactXsn) {
68
+ function _inspectAnnotations( artifactXsn ) {
69
69
  const result = [];
70
70
  const annos = Object.keys(artifactXsn).filter(str => str.startsWith('@')).sort();
71
71
 
@@ -114,7 +114,7 @@ function _inspectAnnotations(artifactXsn) {
114
114
  * @returns {string[]}
115
115
  * @private
116
116
  */
117
- function _inspectElements(artifactXsn) {
117
+ function _inspectElements( artifactXsn ) {
118
118
  if (!artifactXsn.elements)
119
119
  return [ 'does not have elements' ];
120
120
 
@@ -173,7 +173,7 @@ function _inspectElements(artifactXsn) {
173
173
  return result;
174
174
  }
175
175
 
176
- function _origin(elementXsn) {
176
+ function _origin( elementXsn ) {
177
177
  while (elementXsn._origin)
178
178
  elementXsn = elementXsn._origin;
179
179
  return elementXsn;
@@ -186,7 +186,7 @@ function _origin(elementXsn) {
186
186
  * @param parent
187
187
  * @returns {boolean}
188
188
  */
189
- function isContainedInParentLocation(art, parent) {
189
+ function isContainedInParentLocation( art, parent ) {
190
190
  const artLoc = art.location;
191
191
  const parentLoc = parent.location;
192
192
  if (artLoc.file !== parentLoc.file)
@@ -5,7 +5,7 @@
5
5
  * @param str
6
6
  * @returns {*[]|*}
7
7
  */
8
- function stringRefToPath(str) {
8
+ function stringRefToPath( str ) {
9
9
  // e.g. `ns.service.E:sub.elem.structured`
10
10
  const path = str.split(':');
11
11
  if (path.length === 1)
@@ -20,7 +20,7 @@ function stringRefToPath(str) {
20
20
  * @param {string} path
21
21
  * @private
22
22
  */
23
- function findArtifact(xsn, path) {
23
+ function findArtifact( xsn, path ) {
24
24
  const segments = [ ...path ];
25
25
  const topLevelName = segments[0];
26
26
  let art = (xsn.definitions && xsn.definitions[topLevelName]) ||
@@ -73,6 +73,8 @@
73
73
  * @property {string} [vZeroIgnore] Marks the property as a CSN 0.1.0 property. The
74
74
  * property is ignored and a warning may be issues about
75
75
  * it.
76
+ * @property {string} [xorException] A property name that is allowed besides another property
77
+ * of an xorGroup (as an exception to the rule).
76
78
  */
77
79
 
78
80
  /**
@@ -585,7 +587,7 @@ const schema = compileSchema( {
585
587
  inKind: () => true, // allowed in all definitions (including columns and extensions)
586
588
  },
587
589
  abstract: { // v1: with 'abstract', an entity becomes an aspect
588
- type: ( val, spec ) => boolOrNull( val, spec ) && undefined,
590
+ type: abstract,
589
591
  inKind: [ 'entity', 'aspect' ], // 'aspect' because 'entity' is replaced by 'aspect' early
590
592
  },
591
593
  key: {
@@ -633,7 +635,7 @@ const schema = compileSchema( {
633
635
  inKind: [ 'entity' ],
634
636
  },
635
637
  $syntax: {
636
- type: string,
638
+ type: dollarSyntax,
637
639
  ignore: true,
638
640
  inKind: [ 'entity', 'type', 'aspect' ],
639
641
  },
@@ -719,7 +721,7 @@ let arrayLevelCount = 0;
719
721
  * @param {object} [proto]
720
722
  * @returns {Object.<string, SchemaSpec>}
721
723
  */
722
- function compileSchema( specs, proto = null) {
724
+ function compileSchema( specs, proto = null ) {
723
725
  // no prototype to protect against evil-CSN properties 'toString' etc.
724
726
  const r = Object.assign( Object.create( proto ), specs );
725
727
  for (const p of Object.keys( specs )) {
@@ -978,7 +980,8 @@ function keys( array, spec, xsn ) {
978
980
  return;
979
981
  const r = Object.create(null);
980
982
  r[$location] = location();
981
- ++virtualLine;
983
+ if (array.length)
984
+ ++virtualLine; // possibly empty array
982
985
  for (const def of array) {
983
986
  const id = def.as || implicitName( def.ref );
984
987
  const name = (typeof id === 'string') ? id : '';
@@ -1033,6 +1036,39 @@ function explicitName( id, spec, xsn ) {
1033
1036
  xsn.name = { id, location: location() };
1034
1037
  }
1035
1038
 
1039
+ function abstract( val, spec, xsn, csn ) {
1040
+ const strange = csn.kind !== 'entity';
1041
+ if (strange || !csnVersionZero) {
1042
+ warning( 'syntax-deprecated-abstract', location(true),
1043
+ { '#': strange ? 'strange-kind' : 'std', prop: 'abstract', kind: 'entity' } );
1044
+ }
1045
+ boolOrNull( val, spec );
1046
+ }
1047
+
1048
+ function dollarSyntax( val, spec, xsn, csn ) {
1049
+ if (csn.kind === 'type' && val === 'aspect') {
1050
+ warning( 'syntax-deprecated-dollar-syntax', location(true),
1051
+ { '#': 'aspect', prop: '$syntax', kind: 'aspect' } );
1052
+ return ignore( val );
1053
+ }
1054
+ else if (xsn.kind === 'entity') {
1055
+ if (val === 'projection') {
1056
+ warning( 'syntax-deprecated-dollar-syntax', location(true),
1057
+ {
1058
+ '#': 'projection',
1059
+ prop: '$syntax',
1060
+ siblingprop: 'projection',
1061
+ otherprop: 'query',
1062
+ } );
1063
+ return string( val, spec );
1064
+ }
1065
+ if (val === 'entity' || val === 'view')
1066
+ return string( val, spec );
1067
+ }
1068
+ warning( 'syntax-deprecated-dollar-syntax', location(true), { prop: '$syntax' } );
1069
+ return ignore( val );
1070
+ }
1071
+
1036
1072
  function validKind( val, spec, xsn ) {
1037
1073
  if (val === xsn.kind) // has been set in definition - the same = ok!
1038
1074
  return undefined; // already set in definition
@@ -1040,17 +1076,7 @@ function validKind( val, spec, xsn ) {
1040
1076
  warning( 'syntax-deprecated-value', location(true),
1041
1077
  { '#': 'replace', prop: spec.msgProp, value: 'entity' } );
1042
1078
  }
1043
- // TODO: rather issue info at `abstract` and `$syntax`, i.e. current location is strange
1044
- // change message id in a later change
1045
- else if ((val === 'entity' || val === 'type') && xsn.kind === 'aspect') {
1046
- info( 'syntax-aspect', location(true), { kind: 'aspect', '#': val },
1047
- {
1048
- std: 'Use the dedicated kind $(KIND) for aspect definitions',
1049
- // eslint-disable-next-line max-len
1050
- entity: 'Abstract entity definitions are deprecated; use aspect definitions (having kind $(KIND)) instead',
1051
- } );
1052
- }
1053
- else {
1079
+ else if (val !== 'entity' && val !== 'type' || xsn.kind !== 'aspect') {
1054
1080
  error( 'syntax-invalid-kind', location(true), { prop: spec.msgProp },
1055
1081
  'Invalid value for property $(PROP)' );
1056
1082
  }
@@ -1145,14 +1171,14 @@ function scalenum( val, spec ) {
1145
1171
  }
1146
1172
 
1147
1173
  function natnum( val, spec ) {
1148
- if (typeof val === 'number' && val >= 0)
1174
+ if (typeof val === 'number' && val >= 0 && Number.isSafeInteger( val ))
1149
1175
  // XSN TODO: do not require literal
1150
1176
  return { val, literal: 'number', location: location() };
1151
1177
  const loc = location(true);
1152
1178
  if (spec.msgId)
1153
1179
  error( spec.msgId, loc, { prop: spec.msgProp } );
1154
1180
  else
1155
- error( 'syntax-expecting-natnum', loc, { prop: spec.msgProp } );
1181
+ error( 'syntax-expecting-unsigned-int', loc, { '#': 'csn', prop: spec.msgProp } );
1156
1182
  return ignore( val );
1157
1183
  }
1158
1184
 
@@ -1322,14 +1348,16 @@ function xpr( exprs, spec, xsn, csn ) {
1322
1348
  xsn.suffix = exprArgs( exprs, spec );
1323
1349
  }
1324
1350
  else {
1351
+ // setting $parens here would not always be correct; thus, keep distinction
1352
+ // between 'xpr' and 'ixpr' (”internal” `xpr` = without implicit parens)
1325
1353
  xsn.op = { val: 'xpr', location: location() };
1326
- xsn.args = exprArgs( exprs, spec, xsn );
1354
+ xsn.args = exprArgs( exprs, spec );
1327
1355
  }
1328
1356
  }
1329
1357
 
1330
1358
  function list( exprs, spec, xsn ) {
1331
- xsn.op = { val: ',', location: location() };
1332
- xsn.args = arrayOf( exprOrString )( exprs, spec, xsn );
1359
+ xsn.op = { val: 'list', location: location() };
1360
+ xsn.args = arrayOf( exprOrString )( exprs, spec );
1333
1361
  }
1334
1362
 
1335
1363
  function xprInValue( exprs, spec, xsn, csn ) {
@@ -1369,20 +1397,30 @@ function args( exprs, spec ) {
1369
1397
  }
1370
1398
 
1371
1399
  function expr( e, spec ) {
1372
- if (Array.isArray( e ) && e.length === 1) { // CSN v.0.1.0 way for parentheses
1373
- const loc = location();
1374
- if (e[0] && !e[0].op) // do not complain with 'op' (for which we complain)
1375
- replaceZeroValue( spec, 'zero-parens' );
1376
- ++virtualLine;
1377
- const r = expr( e[0], spec );
1378
- if (!r)
1400
+ if (Array.isArray( e )) {
1401
+ if (e.length > 1) { // struct-xpr
1402
+ const loc = location();
1403
+ return {
1404
+ op: { val: 'ixpr', location: loc },
1405
+ args: exprArgs( e, spec ),
1406
+ location: loc,
1407
+ };
1408
+ }
1409
+ else if (e.length === 1) { // CSN v.0.1.0 way for parentheses
1410
+ const loc = location();
1411
+ if (e[0] && !e[0].op) // do not complain with 'op' (for which we complain)
1412
+ replaceZeroValue( spec, 'zero-parens' );
1413
+ ++virtualLine;
1414
+ const r = expr( e[0], spec );
1415
+ if (!r)
1416
+ return r;
1417
+ if (r.$parens)
1418
+ r.$parens.push( loc );
1419
+ else
1420
+ r.$parens = [ loc ];
1421
+ ++virtualLine;
1379
1422
  return r;
1380
- if (r.$parens)
1381
- r.$parens.push( loc );
1382
- else
1383
- r.$parens = [ loc ];
1384
- ++virtualLine;
1385
- return r;
1423
+ }
1386
1424
  }
1387
1425
  else if (e === null || [ 'string', 'number', 'boolean' ].includes( typeof e )) {
1388
1426
  // && spec.optional.includes( 'val' )) ?
@@ -1392,16 +1430,19 @@ function expr( e, spec ) {
1392
1430
  return object( e, spec );
1393
1431
  }
1394
1432
 
1395
- function exprOrString( e, spec ) {
1396
- return (typeof e === 'string' && !csnVersionZero) ? e : expr( e, spec );
1433
+ function exprOrString( val, spec ) {
1434
+ return (typeof val === 'string' && !csnVersionZero)
1435
+ ? { val, literal: 'token', location: location() }
1436
+ : expr( val, spec );
1397
1437
  }
1398
1438
 
1399
1439
  // mark path argument of 'exits' predicate with $expected:'exists'
1400
- function exprArgs( cond, spec, xsn, csn ) {
1401
- const rxsn = arrayOf( exprOrString )( cond, spec, xsn, csn );
1402
- if (Array.isArray( rxsn ) && rxsn.some( x => x === 'exists' )) {
1440
+ function exprArgs( cond, spec ) {
1441
+ const rxsn = arrayOf( exprOrString )( cond, spec );
1442
+ // TODO: do that in definer.js, neither here nor in CDL parser
1443
+ if (Array.isArray( rxsn )) {
1403
1444
  for (let i = 0; i < rxsn.length - 1; i++) {
1404
- if (rxsn[i] === 'exists' && rxsn[i + 1].path)
1445
+ if (rxsn[i]?.val === 'exists' && rxsn[i].literal === 'token' && rxsn[i + 1].path)
1405
1446
  rxsn[++i].$expected = 'exists';
1406
1447
  }
1407
1448
  }
@@ -1448,7 +1489,8 @@ function excluding( array, spec, xsn ) {
1448
1489
  return;
1449
1490
  const r = Object.create(null);
1450
1491
  r[$location] = location();
1451
- ++virtualLine;
1492
+ if (array.length)
1493
+ ++virtualLine; // possibly empty array
1452
1494
  for (const ex of array) {
1453
1495
  const id = string( ex, spec ) || '';
1454
1496
  dictAdd( r, id, { name: { id, location: location() }, location: location() },