@sap/cds-compiler 4.7.6 → 4.9.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 (129) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +28 -1
  6. package/bin/cdshi.js +13 -3
  7. package/doc/CHANGELOG_BETA.md +24 -1
  8. package/lib/api/main.js +119 -46
  9. package/lib/api/options.js +51 -0
  10. package/lib/api/validate.js +1 -5
  11. package/lib/base/builtins.js +116 -0
  12. package/lib/base/keywords.js +5 -1
  13. package/lib/base/location.js +91 -14
  14. package/lib/base/message-registry.js +76 -46
  15. package/lib/base/messages.js +121 -35
  16. package/lib/base/model.js +4 -7
  17. package/lib/checks/actionsFunctions.js +3 -3
  18. package/lib/checks/annotationsOData.js +3 -0
  19. package/lib/checks/defaultValues.js +5 -2
  20. package/lib/checks/elements.js +2 -1
  21. package/lib/checks/enricher.js +2 -2
  22. package/lib/checks/queryNoDbArtifacts.js +5 -3
  23. package/lib/checks/utils.js +1 -1
  24. package/lib/checks/validator.js +8 -56
  25. package/lib/compiler/assert-consistency.js +11 -7
  26. package/lib/compiler/builtins.js +0 -74
  27. package/lib/compiler/checks.js +105 -29
  28. package/lib/compiler/define.js +37 -25
  29. package/lib/compiler/extend.js +35 -12
  30. package/lib/compiler/index.js +9 -10
  31. package/lib/compiler/lsp-api.js +5 -0
  32. package/lib/compiler/populate.js +13 -5
  33. package/lib/compiler/propagator.js +24 -18
  34. package/lib/compiler/resolve.js +47 -45
  35. package/lib/compiler/shared.js +61 -21
  36. package/lib/compiler/tweak-assocs.js +15 -90
  37. package/lib/compiler/utils.js +3 -3
  38. package/lib/compiler/xpr-rewrite.js +689 -0
  39. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  40. package/lib/edm/annotations/edmJson.js +7 -5
  41. package/lib/edm/annotations/genericTranslation.js +149 -71
  42. package/lib/edm/csn2edm.js +25 -9
  43. package/lib/edm/edm.js +7 -7
  44. package/lib/edm/edmInboundChecks.js +57 -5
  45. package/lib/edm/edmPreprocessor.js +54 -25
  46. package/lib/edm/edmUtils.js +3 -16
  47. package/lib/gen/Dictionary.json +138 -14
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +1 -1
  50. package/lib/gen/languageParser.js +2085 -1989
  51. package/lib/json/csnVersion.js +7 -4
  52. package/lib/json/from-csn.js +21 -11
  53. package/lib/json/to-csn.js +8 -4
  54. package/lib/language/antlrParser.js +1 -1
  55. package/lib/language/genericAntlrParser.js +23 -16
  56. package/lib/language/multiLineStringParser.js +2 -2
  57. package/lib/language/textUtils.js +1 -1
  58. package/lib/main.d.ts +90 -14
  59. package/lib/main.js +9 -1
  60. package/lib/model/cloneCsn.js +21 -9
  61. package/lib/model/csnRefs.js +153 -42
  62. package/lib/model/csnUtils.js +14 -11
  63. package/lib/model/enrichCsn.js +4 -2
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/model/sortViews.js +14 -6
  66. package/lib/modelCompare/compare.js +135 -122
  67. package/lib/optionProcessor.js +49 -2
  68. package/lib/render/DuplicateChecker.js +6 -6
  69. package/lib/render/manageConstraints.js +1 -0
  70. package/lib/render/toCdl.js +6 -3
  71. package/lib/render/toHdbcds.js +4 -48
  72. package/lib/render/toSql.js +6 -3
  73. package/lib/transform/addTenantFields.js +58 -35
  74. package/lib/transform/db/applyTransformations.js +34 -1
  75. package/lib/transform/db/constraints.js +1 -1
  76. package/lib/transform/db/expansion.js +11 -3
  77. package/lib/transform/db/flattening.js +71 -46
  78. package/lib/transform/db/groupByOrderBy.js +2 -2
  79. package/lib/transform/db/temporal.js +6 -3
  80. package/lib/transform/db/transformExists.js +2 -2
  81. package/lib/transform/db/views.js +1 -4
  82. package/lib/transform/effective/annotations.js +194 -0
  83. package/lib/transform/effective/main.js +11 -10
  84. package/lib/transform/effective/misc.js +45 -14
  85. package/lib/transform/effective/types.js +4 -3
  86. package/lib/transform/forOdata.js +29 -12
  87. package/lib/transform/forRelationalDB.js +104 -113
  88. package/lib/transform/localized.js +7 -6
  89. package/lib/transform/odata/flattening.js +228 -107
  90. package/lib/transform/odata/toFinalBaseType.js +10 -26
  91. package/lib/transform/odata/typesExposure.js +41 -25
  92. package/lib/transform/parseExpr.js +4 -7
  93. package/lib/transform/transformUtils.js +50 -43
  94. package/lib/transform/translateAssocsToJoins.js +48 -48
  95. package/lib/transform/universalCsn/coreComputed.js +2 -1
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  97. package/package.json +2 -2
  98. package/share/messages/README.md +4 -0
  99. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  100. package/share/messages/anno-missing-rewrite.md +45 -0
  101. package/share/messages/check-proper-type-of.md +1 -1
  102. package/share/messages/def-duplicate-autoexposed.md +1 -1
  103. package/share/messages/extend-repeated-intralayer.md +3 -16
  104. package/share/messages/extend-unrelated-layer.md +1 -1
  105. package/share/messages/message-explanations.json +2 -0
  106. package/share/messages/redirected-to-ambiguous.md +1 -1
  107. package/share/messages/redirected-to-complex.md +1 -1
  108. package/share/messages/redirected-to-unrelated.md +1 -1
  109. package/share/messages/rewrite-not-supported.md +1 -1
  110. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  111. package/share/messages/type-missing-enum-value.md +59 -0
  112. package/share/messages/wildcard-excluding-one.md +1 -1
  113. package/bin/.eslintrc.json +0 -17
  114. package/lib/api/.eslintrc.json +0 -37
  115. package/lib/checks/.eslintrc.json +0 -31
  116. package/lib/compiler/.eslintrc.json +0 -8
  117. package/lib/edm/.eslintrc.json +0 -46
  118. package/lib/inspect/.eslintrc.json +0 -4
  119. package/lib/json/.eslintrc.json +0 -4
  120. package/lib/language/.eslintrc.json +0 -4
  121. package/lib/model/.eslintrc.json +0 -13
  122. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  123. package/lib/render/.eslintrc.json +0 -22
  124. package/lib/transform/.eslintrc.json +0 -13
  125. package/lib/transform/db/.eslintrc.json +0 -41
  126. package/lib/transform/draft/.eslintrc.json +0 -4
  127. package/lib/transform/effective/.eslintrc.json +0 -4
  128. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  129. package/lib/utils/.eslintrc.json +0 -7
@@ -29,10 +29,10 @@ function translateAssocsToJoinsCSN(csn, options){
29
29
  // Use the effective elements list as columns
30
30
  forEachDefinition(model, art => {
31
31
  if (art.$queries) {
32
- for (let query of art.$queries) {
32
+ for (const query of art.$queries) {
33
33
  query.columns = Object.values(query.elements);
34
34
  // TODO: Remove viaAll
35
- for (let elemName in query.elements) {
35
+ for (const elemName in query.elements) {
36
36
  const elem = query.elements[elemName];
37
37
  if (elem.$inferred === '*')
38
38
  delete elem.$inferred;
@@ -172,8 +172,8 @@ function translateAssocsToJoins(model, inputOptions = {})
172
172
  // Transform each FROM table path into a join tree and attach the tree to the path object
173
173
  function createInnerJoins(fromPathNode, env)
174
174
  {
175
- let fqat = env.lead.$tableAliases[fromPathNode.name.id].$fqat;
176
- let joinTree = createJoinTree(env, undefined, fqat, 'inner', '$fqat', undefined);
175
+ const fqat = env.lead.$tableAliases[fromPathNode.name.id].$fqat;
176
+ const joinTree = createJoinTree(env, undefined, fqat, 'inner', '$fqat', undefined);
177
177
 
178
178
  replaceTableAliasInPlace( fromPathNode, joinTree);
179
179
  }
@@ -185,11 +185,11 @@ function translateAssocsToJoins(model, inputOptions = {})
185
185
  {
186
186
  env.lead = query;
187
187
  let joinTree = query.from;
188
- for(let tan in query.$tableAliases)
188
+ for(const tan in query.$tableAliases)
189
189
  {
190
190
  if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
191
191
  {
192
- let ta = query.$tableAliases[tan];
192
+ const ta = query.$tableAliases[tan];
193
193
  joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
194
194
  }
195
195
  }
@@ -207,9 +207,9 @@ function translateAssocsToJoins(model, inputOptions = {})
207
207
  */
208
208
  function createQAForFromClauseSubQuery(query, env)
209
209
  {
210
- for (let taName in query.$tableAliases) {
210
+ for (const taName in query.$tableAliases) {
211
211
  if (query.$tableAliases[taName].kind !== '$self') {
212
- let ta = query.$tableAliases[taName];
212
+ const ta = query.$tableAliases[taName];
213
213
  if(!ta.$QA) {
214
214
  let alias = taName;
215
215
  if (ta.name.$inferred === '$internal') {
@@ -306,8 +306,8 @@ function translateAssocsToJoins(model, inputOptions = {})
306
306
  if(env.location === 'onCondFrom')
307
307
  {
308
308
  if(checkPathDictionary(pathNode, env)) {
309
- let [ tableAlias, tail ] = constructTableAliasAndTailPath(pathNode.path);
310
- let pathStr = translateONCondPath(tail).map(ps => ps.id).join(pathDelimiter);
309
+ const [ tableAlias, tail ] = constructTableAliasAndTailPath(pathNode.path);
310
+ const pathStr = translateONCondPath(tail).map(ps => ps.id).join(pathDelimiter);
311
311
  replaceNodeContent(pathNode,
312
312
  constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]));
313
313
  }
@@ -329,11 +329,11 @@ function translateAssocsToJoins(model, inputOptions = {})
329
329
  tail = pathNode.path;
330
330
  // if tail.length > 1, search bottom up for QA
331
331
  // default to rootQA, _parent.$QA has precedence
332
- let [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
332
+ const [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
333
333
  if(!QA) {
334
334
  error(null, pathNode.$location,
335
335
  { name: pathName(pathNode.path) },
336
- 'Please debug me: No QA found for generic path rewriting in $(NAME)')
336
+ 'Debug me: No QA found for generic path rewriting in $(NAME)')
337
337
  return;
338
338
  }
339
339
  // if the found QA is the mixin QA and if the path length is one,
@@ -360,7 +360,7 @@ function translateAssocsToJoins(model, inputOptions = {})
360
360
  // const revealInternalProperties = require('../model/revealInternalProperties.js');
361
361
  // console.log('++++++++ Path tail: ', revealInternalProperties(tail[tail.length-1]._artifact));
362
362
  // console.log('******** Flat FKs\n', tail[i]._artifact.$flatSrcFKs.map(f => revealInternalProperties(f._artifact)));
363
- throw new CompilerAssertion('Please debug me: No flat FK found for FK rewriting');
363
+ throw new CompilerAssertion('Debug me: No flat FK found for FK rewriting');
364
364
  }
365
365
  // replace tail path with flattened foreign key including prefix
366
366
  tail.splice(i, tail.length, fk);
@@ -431,7 +431,7 @@ function translateAssocsToJoins(model, inputOptions = {})
431
431
  */
432
432
  function createJoinTree(env, joinTree, parentQat, joinType, qatAttribName, lastAssocQA)
433
433
  {
434
- for(let childQatId in parentQat)
434
+ for(const childQatId in parentQat)
435
435
  {
436
436
  const childQat = parentQat[childQatId];
437
437
 
@@ -454,10 +454,10 @@ function translateAssocsToJoins(model, inputOptions = {})
454
454
  if(childQat._filter)
455
455
  {
456
456
  // Filter conditions are unique for each JOIN, they don't need to be copied
457
- let filter = childQat._filter;
457
+ const filter = childQat._filter;
458
458
  rewritePathsInFilterExpression(filter, function(pathNode) {
459
459
  return [ /* tableAlias=> */ constructTableAliasPathStep(childQat.$QA),
460
- /* filterPath=> */ pathNode.path ]; // eslint-disable-line indent-legacy
460
+ /* filterPath=> */ pathNode.path ];
461
461
  }, env);
462
462
 
463
463
  if(!env.lead.$startFilters)
@@ -486,28 +486,28 @@ function translateAssocsToJoins(model, inputOptions = {})
486
486
 
487
487
  function createJoinQA(joinType, lhs, rhs, assocQAT, assocSourceQA, env)
488
488
  {
489
- let node = { op: { val: 'join' }, join: { val: joinType }, args: [ lhs, rhs ] };
489
+ const node = { op: { val: 'join' }, join: { val: joinType }, args: [ lhs, rhs ] };
490
490
  const assoc = assocQAT._origin;
491
491
  if(isBetaEnabled(options, 'mapAssocToJoinCardinality')) {
492
492
  node.cardinality = mapAssocToJoinCardinality(assoc);
493
493
  }
494
494
  // 'path steps' for the src/tgt table alias
495
- let srcTableAlias = constructTableAliasPathStep(assocSourceQA);
496
- let tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
495
+ const srcTableAlias = constructTableAliasPathStep(assocSourceQA);
496
+ const tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
497
497
 
498
498
  node.on = createOnCondition(assoc, srcTableAlias, tgtTableAlias, options.tenantDiscriminator);
499
499
 
500
500
  if(assocQAT._filter)
501
501
  {
502
502
  // Filter conditions are unique for each JOIN, they don't need to be copied
503
- let filter = assocQAT._filter;
503
+ const filter = assocQAT._filter;
504
504
  rewritePathsInFilterExpression(filter, function(pathNode) {
505
505
  return [ tgtTableAlias, pathNode.path ];
506
506
  }, env);
507
507
 
508
508
  // If toplevel ON cond op is AND add filter condition to the args array,
509
509
  // create a new toplevel AND op otherwise
510
- let onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
510
+ const onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
511
511
 
512
512
  if(onCond.op.val === 'and')
513
513
  onCond.args.push(parenthesise(filter));
@@ -567,7 +567,7 @@ function translateAssocsToJoins(model, inputOptions = {})
567
567
  // produce the ON condition for a given association
568
568
  function createOnCondition(assoc, srcAlias, tgtAlias, compareTenants)
569
569
  {
570
- let prefixes = [ assoc.name.id ];
570
+ const prefixes = [ assoc.name.id ];
571
571
  /* This is no art and can be removed once ON cond for published
572
572
  and renamed backlink assocs are publicly available. Example:
573
573
 
@@ -600,12 +600,12 @@ function translateAssocsToJoins(model, inputOptions = {})
600
600
  Put all src/tgt path siblings into the EQ term and create the proper path objects
601
601
  with the src/tgt table alias path steps in front.
602
602
  */
603
- let args = compareTenants ? [ addTenantComparison(assoc) ] : [];
603
+ const args = compareTenants && addTenantComparison(assoc) || [];
604
604
  for(let i = 0; i < assoc.$flatSrcFKs.length; i++)
605
605
  {
606
606
  args.push({op: {val: '=' },
607
607
  args: [ constructPathNode( [ srcAlias, prefixFK(assoc.$elementPrefix, assoc.$flatSrcFKs[i]) ] ),
608
- constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] }); // eslint-disable-line indent-legacy
608
+ constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] });
609
609
  }
610
610
  return parenthesise((args.length > 1 ? { op: { val: 'and' }, args: [ ...args.map(parenthesise) ] } : args[0] ));
611
611
  }
@@ -678,7 +678,7 @@ function translateAssocsToJoins(model, inputOptions = {})
678
678
  args[1].path.push({ id: 'tenant' }); // no need for _artifact
679
679
  const comparison = { op: {val: '=' }, args };
680
680
  if (!cond) // for managed assoc
681
- return comparison;
681
+ return [ comparison ];
682
682
  return { op: { val: 'and' }, args: [ comparison, parenthesise(cond) ] };
683
683
  }
684
684
 
@@ -761,7 +761,7 @@ function translateAssocsToJoins(model, inputOptions = {})
761
761
  // If this is an ordinary expression, clone it and mangle its arguments
762
762
  // this will substitute multiple backlink conditions ($self = ... AND $self = ...AND ...)
763
763
  if(expr.op) {
764
- let x = clone(expr);
764
+ const x = clone(expr);
765
765
  if(expr.args)
766
766
  x.args = expr.args.map(cloneOnCondition);
767
767
  return x;
@@ -845,7 +845,7 @@ function translateAssocsToJoins(model, inputOptions = {})
845
845
  $projection must be removed from $projection.yid (get's aliased with the mixinAssocQAT.$QA)
846
846
  */
847
847
  if(env.assocStack.length < 2) {
848
- let value = env.lead.elements[path[0].id].value;
848
+ const value = env.lead.elements[path[0].id].value;
849
849
  /*
850
850
  If the value is an expression in the select block, return the unmodified
851
851
  expression. rewriteGenericPaths will check and rewrite these paths later
@@ -957,7 +957,7 @@ function translateAssocsToJoins(model, inputOptions = {})
957
957
  if(head.id === env.assocStack.id()) {
958
958
  // source side from view point of view (target side from forward point of view)
959
959
  path = tail; // pop assoc step
960
- let elt = env.lead._combined[path[0].id];
960
+ const elt = env.lead._combined[path[0].id];
961
961
 
962
962
  if(elt) {
963
963
  if(Array.isArray(elt)) {
@@ -1008,7 +1008,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1008
1008
  path = translateONCondPath(path, !hasDollarSelfPrefix ? assoc.$elementPrefix : undefined);
1009
1009
  }
1010
1010
  }
1011
- let pathStr = path.map(ps => ps.id).join(pathDelimiter);
1011
+ const pathStr = path.map(ps => ps.id).join(pathDelimiter);
1012
1012
  return constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]);
1013
1013
  }
1014
1014
 
@@ -1209,11 +1209,11 @@ function translateAssocsToJoins(model, inputOptions = {})
1209
1209
  */
1210
1210
  function constructTableAliasAndTailPath(path, navigation=undefined)
1211
1211
  {
1212
- let [head, ...tail] = path;
1212
+ const [head, ...tail] = path;
1213
1213
  if(navigation === undefined)
1214
1214
  navigation = head._navigation;
1215
1215
 
1216
- let QA = navigation.$QA || navigation._parent.$QA;
1216
+ const QA = navigation.$QA || navigation._parent.$QA;
1217
1217
 
1218
1218
  // First path step is table alias, use and pop it off
1219
1219
  if(navigation.$QA && tail.length > 0)
@@ -1251,7 +1251,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1251
1251
  {
1252
1252
  if(!pathStr)
1253
1253
  pathStr = '';
1254
- let assocStep = path.find(ps => {
1254
+ const assocStep = path.find(ps => {
1255
1255
  if(pathStr.length > 0)
1256
1256
  pathStr += pathDelimiter;
1257
1257
  pathStr += ps.id;
@@ -1303,7 +1303,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1303
1303
  pathStr += fk.name.id;
1304
1304
  }
1305
1305
 
1306
- let tail = path.slice(path.indexOf(fkPs)+1);
1306
+ const tail = path.slice(path.indexOf(fkPs)+1);
1307
1307
  // If foreign key is an association itself, apply substituteFKAliasForPath on tail
1308
1308
  if(fk && fk.targetElement._artifact.target && tail.length)
1309
1309
  return substituteFKAliasForPath(fk.targetElement, tail, pathStr);
@@ -1377,7 +1377,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1377
1377
  if(ps._artifact && ps._artifact.target)
1378
1378
  {
1379
1379
  // if this is not the last path step, complain
1380
- let la1 = pathDict.path[pathDict.path.indexOf(ps)+1];
1380
+ const la1 = pathDict.path[pathDict.path.indexOf(ps)+1];
1381
1381
  if(la1) {
1382
1382
  if(ps._artifact.on)
1383
1383
  {
@@ -1438,7 +1438,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1438
1438
  */
1439
1439
  function mergePathIntoQAT(pathDict, env)
1440
1440
  {
1441
- let path = pathDict.path;
1441
+ const path = pathDict.path;
1442
1442
 
1443
1443
  if(path.length === 0)
1444
1444
  return;
@@ -1505,7 +1505,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1505
1505
  if(qatParent == undefined)
1506
1506
  throw new CompilerAssertion('table alias/qathost not found for path: ' + pathAsStr(path));
1507
1507
 
1508
- let rootQat = qatParent;
1508
+ const rootQat = qatParent;
1509
1509
 
1510
1510
  // Create the very first QAT if it doesn't exist
1511
1511
  // (filter condition for table alias prefix not allowed)
@@ -1526,7 +1526,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1526
1526
  }
1527
1527
  if(pathStep.args) {
1528
1528
  // sort named arguments
1529
- let sortedNamedArgs = Object.create(null);
1529
+ const sortedNamedArgs = Object.create(null);
1530
1530
  Object.keys(pathStep.args).sort().forEach(p => {
1531
1531
  sortedNamedArgs[p] = compactExpr(pathStep.args[p]);
1532
1532
  })
@@ -1687,10 +1687,10 @@ function translateAssocsToJoins(model, inputOptions = {})
1687
1687
  // eslint-disable-next-line no-unused-vars
1688
1688
  function printPath(pathDict, env)
1689
1689
  {
1690
- let alias = (pathDict.name && pathDict.name.id) || '<undefined>'
1691
- let path = pathDict.path;
1692
- let s = pathAsStr(path, '"');
1693
- let me = env.lead && (env.lead.name.id || env.lead.op);
1690
+ const alias = (pathDict.name && pathDict.name.id) || '<undefined>'
1691
+ const path = pathDict.path;
1692
+ const s = pathAsStr(path, '"');
1693
+ const me = env.lead && (env.lead.name.id || env.lead.op);
1694
1694
  // eslint-disable-next-line no-console
1695
1695
  console.log(me + ': ' + env.location + ': ' + s + ' alias: ' + alias);
1696
1696
  }
@@ -1708,9 +1708,9 @@ function translateAssocsToJoins(model, inputOptions = {})
1708
1708
  else
1709
1709
  newObj = {};
1710
1710
 
1711
- let props = Object.getOwnPropertyNames(obj); // clone own properties only, not inherited ones
1712
- for (let p of props) {
1713
- let pd = Object.getOwnPropertyDescriptor(obj, p);
1711
+ const props = Object.getOwnPropertyNames(obj); // clone own properties only, not inherited ones
1712
+ for (const p of props) {
1713
+ const pd = Object.getOwnPropertyDescriptor(obj, p);
1714
1714
  if (pd && pd.enumerable === false)
1715
1715
  {
1716
1716
  pd.value = obj[p]; // don't copy references
@@ -1740,10 +1740,10 @@ function translateAssocsToJoins(model, inputOptions = {})
1740
1740
  */
1741
1741
  function constructPathNode(pathSteps, alias, rewritten=true)
1742
1742
  {
1743
- let node = {
1743
+ const node = {
1744
1744
  $rewritten: rewritten,
1745
1745
  path : pathSteps.map(p => {
1746
- let o = {};
1746
+ const o = {};
1747
1747
  Object.keys(p).forEach(k => {
1748
1748
  if(!(rewritten && ['_'].includes(k[0])))
1749
1749
  o[k] = p[k];
@@ -1784,7 +1784,7 @@ function walkQuery(query, env)
1784
1784
  env.location = 'select';
1785
1785
  if(env.walkover[env.location])
1786
1786
  {
1787
- for(let alias in query.elements)
1787
+ for(const alias in query.elements)
1788
1788
  walk(query.elements[alias].value, env);
1789
1789
 
1790
1790
  env.location = 'Where';
@@ -1809,7 +1809,7 @@ function walkQuery(query, env)
1809
1809
 
1810
1810
  function walkFrom(fromBlock)
1811
1811
  {
1812
- let aliases = [];
1812
+ const aliases = [];
1813
1813
  env.position = fromBlock;
1814
1814
  if(fromBlock)
1815
1815
  {
@@ -5,6 +5,7 @@ const {
5
5
  } = require('../../model/csnUtils');
6
6
  const { setAnnotationIfNotDefined } = require('./utils');
7
7
  const { CompilerAssertion } = require('../../base/error');
8
+ const { isMagicVariable } = require('../../base/builtins');
8
9
 
9
10
  /**
10
11
  * Set @Core.Computed on the elements of views (and projections) as well
@@ -157,7 +158,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
157
158
  (
158
159
  column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
159
160
  column.SELECT || column.SET ||
160
- column.ref && [ '$at', '$valid', '$now', '$user', '$tenant', '$session', '$parameters' ].includes(column.ref[0])
161
+ column.ref && (isMagicVariable(column.ref[0]) || column.ref[0] === '$parameters')
161
162
  );
162
163
  }
163
164
 
@@ -8,8 +8,8 @@ const {
8
8
  getUtils,
9
9
  applyTransformations,
10
10
  implicitAs,
11
- isBuiltinType,
12
11
  } = require('../../model/csnUtils');
12
+ const { isBuiltinType, propagationRules } = require('../../base/builtins');
13
13
  const {
14
14
  forEachValue, forEach,
15
15
  } = require('../../utils/objectUtils');
@@ -29,24 +29,10 @@ module.exports = (csn, options) => {
29
29
  } = csnUtils;
30
30
  // Properties on definition level that we treat specially.
31
31
  const definitionPropagationRules = {
32
- '@cds.autoexpose': onlyViaArtifact,
33
- '@cds.external': skip,
34
- '@fiori.draft.enabled': onlyViaArtifact,
32
+ __proto__: null,
35
33
  '@': nullStopsPropagation,
36
34
  // Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
37
35
  elements: onlyTypeDef,
38
- '@cds.persistence.exists': skip,
39
- '@cds.persistence.table': skip,
40
- '@cds.persistence.calcview': skip,
41
- '@cds.persistence.udf': skip,
42
- '@cds.persistence.skip': notWithPersistenceTable,
43
- '@sql.append': skip,
44
- '@sql.prepend': skip,
45
- '@sql.replace': skip,
46
- '@Analytics.hidden': skip,
47
- '@Analytics.visible': skip,
48
- '@cds.autoexposed': skip,
49
- '@cds.redirection.target': skip,
50
36
  type: always,
51
37
  doc: nullStopsPropagation,
52
38
  length: always,
@@ -65,6 +51,16 @@ module.exports = (csn, options) => {
65
51
  keys: always,
66
52
  };
67
53
 
54
+ const ruleToFunction = {
55
+ __proto__: null,
56
+ never: skip,
57
+ onlyViaArtifact,
58
+ notWithPersistenceTable,
59
+ };
60
+
61
+ for (const rule in propagationRules)
62
+ definitionPropagationRules[rule] = ruleToFunction[propagationRules[rule]];
63
+
68
64
  // Properties on member level that we treat specially
69
65
  const memberPropagationRules = {
70
66
  key: skip,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.7.6",
3
+ "version": "4.9.0",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -28,7 +28,7 @@
28
28
  "gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
29
29
  "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
30
30
  "coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
31
- "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
31
+ "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
32
32
  "tslint": "tsc --pretty -p .",
33
33
  "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
34
34
  "updateTocs": "node scripts/update-toc.js",
@@ -41,8 +41,12 @@ Usually mentions the severity.
41
41
 
42
42
  ## Example
43
43
 
44
+ Erroneous code example:
45
+
46
+ ```
44
47
  Code which also lead to the current message,
45
48
  with an explanation why it is problematic.
49
+ ```
46
50
 
47
51
  ## How to Fix
48
52
 
@@ -23,7 +23,7 @@ annotate FooBar with @Anno: 'Foo';
23
23
  using from './Base';
24
24
  annotate FooBar with @Anno: 'Bar';
25
25
 
26
- // (4) All.cds: Combine all files
26
+ // (4) All.cds: Combine all files
27
27
  using from './FooAnnotate';
28
28
  using from './BarAnnotate';
29
29
  ```
@@ -0,0 +1,45 @@
1
+ # anno-missing-rewrite
2
+
3
+ A propagated annotation containing expressions can't be rewritten and would
4
+ end up with invalid paths.
5
+
6
+ While propagating annotations containing expressions such as `@anno: (path)`,
7
+ the compiler ensures that the path remains valid. If necessary, the paths
8
+ have to be rewritten, e.g. when being propagated to projections that rename
9
+ their source's elements. If rewriting is not possible, this error is emitted.
10
+
11
+ ## Example
12
+
13
+ Erroneous code example:
14
+
15
+ ```cds
16
+ type T : {
17
+ @anno: (sibling)
18
+ elem: String;
19
+ sibling: String;
20
+ };
21
+ type TString : T:elem; // ❌ there is no `sibling`
22
+ ```
23
+
24
+ The annotating `@anno` would be propagated to `TString`. However, because its
25
+ path refers to an element that is not reachable at `TString`, the path can't
26
+ be rewritten and compilation fails.
27
+
28
+ ## How to Fix
29
+
30
+ Explicitly override the annotation. Either remove it by setting its value to
31
+ `null` or by using another value.
32
+
33
+ ```cds
34
+ // (1) direct annotation
35
+ @anno: null
36
+ type TString : T:elem;
37
+
38
+ // (2) annotate statement
39
+ type TString : T:elem;
40
+ annotate TString with @(anno: null);
41
+ ```
42
+
43
+ Variant (1) may not always be applicable, e.g. if annotations in a structured
44
+ type would need to be overridden. In those cases, use variant (2) and assign
45
+ annotations via the `annotate` statement.
@@ -18,7 +18,7 @@ view ViewFoo as select from Foo {
18
18
  1+1 as calculatedField @(anno)
19
19
  };
20
20
  entity Bar {
21
- // `e` has no proper type but has the annotation `@anno`.
21
+ // `e` has no proper type but has the annotation `@anno`.
22
22
  e : ViewFoo:calculatedField;
23
23
  };
24
24
  ```
@@ -31,7 +31,7 @@ entity ns.Base {
31
31
  to_second : Composition of many ns.second.Foo;
32
32
  }
33
33
  service ns.MyService {
34
- // (4)
34
+ // (4)
35
35
  entity BaseView as projection on ns.Base;
36
36
  };
37
37
  ```
@@ -9,30 +9,17 @@ They form a cyclic connection through their dependencies
9
9
 
10
10
  ## Example
11
11
 
12
- Erroneous code example with a single CDL file:
13
-
14
- ```cds
15
- entity FooBar { }
16
-
17
- extend FooBar { foo : Integer; }
18
- extend FooBar { bar : Integer; }
19
- ```
20
-
21
- Due to multiple extensions in the example above, the order of `foo` and `bar`
22
- inside `FooBar` may not be stable. You therefore can’t depend on it.
23
-
24
- It's also possible to trigger this warning with multiple files.
25
- Look at the following example:
12
+ Erroneous code example with multiple CDL files:
26
13
 
27
14
  ```cds
28
15
  // (1) Definition.cds
29
16
  using from './Extension.cds';
30
17
  entity FooBar { };
31
- extend FooBar { foo: Integer; };
18
+ extend FooBar { foo: Integer; }; // ❌
32
19
 
33
20
  // (2) Extension.cds
34
21
  using from './Definition.cds';
35
- extend FooBar { bar: Integer; }
22
+ extend FooBar { bar: Integer; }; // ❌
36
23
  ```
37
24
 
38
25
  Here we have a cyclic dependency between (1) and (2). Together they form one
@@ -23,7 +23,7 @@ extend FooBar { foo : Integer; }
23
23
  using from './Base';
24
24
  extend FooBar { bar : Integer; }
25
25
 
26
- // (4) All.cds: Combine all files
26
+ // (4) All.cds: Combine all files
27
27
  using from './FooExtend';
28
28
  using from './BarExtend';
29
29
  ```
@@ -2,6 +2,7 @@
2
2
  "$comment": "This is an auto-generated file! Do not edit this file directly!",
3
3
  "messages": [
4
4
  "anno-duplicate-unrelated-layer",
5
+ "anno-missing-rewrite",
5
6
  "check-proper-type-of",
6
7
  "def-duplicate-autoexposed",
7
8
  "def-missing-type",
@@ -12,6 +13,7 @@
12
13
  "redirected-to-unrelated",
13
14
  "rewrite-not-supported",
14
15
  "syntax-expecting-unsigned-int",
16
+ "type-missing-enum-value",
15
17
  "wildcard-excluding-one"
16
18
  ]
17
19
  }
@@ -26,7 +26,7 @@ view View as select from
26
26
  Target,
27
27
  Target as Duplicate
28
28
  {
29
- // This redirection can’t be resolved:
29
+ // This redirection can’t be resolved:
30
30
  Main.toTarget : redirected to View
31
31
  };
32
32
  ```
@@ -21,7 +21,7 @@ entity Secondary {
21
21
  entity CrossJoin as SELECT from Main, Secondary;
22
22
  entity RedirectToComplex as projection on Main {
23
23
  id,
24
- toMain: redirected to CrossJoin,
24
+ toMain: redirected to CrossJoin, // ❌
25
25
  };
26
26
  ```
27
27
 
@@ -21,7 +21,7 @@ entity Secondary {
21
21
  }
22
22
  entity InvalidRedirect as projection on Main {
23
23
  id,
24
- // Invalid redirection
24
+ // Invalid redirection
25
25
  toMain: redirected to Secondary,
26
26
  };
27
27
  ```
@@ -29,7 +29,7 @@ entity Secondary {
29
29
 
30
30
  entity View as select from Base {
31
31
  id,
32
- primary.secondary // Error: The ON condition isn’t rewritten here
32
+ primary.secondary // The ON condition isn’t rewritten here
33
33
  };
34
34
  ```
35
35
 
@@ -17,8 +17,8 @@ Erroneous code example:
17
17
 
18
18
  <!-- cds-mode: ignore -->
19
19
  ```cds
20
- type LengthIsUnsafe : String(9007199254740992);
21
- type NotAnInteger : String(42.1);
20
+ type LengthIsUnsafe : String(9007199254740992); // ❌
21
+ type NotAnInteger : String(42.1); // ❌
22
22
  ```
23
23
 
24
24
  In the erroneous example, the string length for the type `LengthIsUnsafe` is