@sap/cds-compiler 2.12.0 → 2.15.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 (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -14,6 +14,7 @@ const { parseDocComment } = require('./docCommentParser');
14
14
  const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
15
15
  const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
16
16
 
17
+ const $location = Symbol.for('cds.$location');
17
18
 
18
19
  // Push message `msg` with location `loc` to array of errors:
19
20
  function _message( parser, severity, id, loc, ...args ) {
@@ -35,6 +36,19 @@ function GenericAntlrParser( ...args ) {
35
36
  // ANTLR restriction: we cannot add parameters to the constructor.
36
37
  antlr4.Parser.call( this, ...args );
37
38
  this.buildParseTrees = false;
39
+
40
+ // Common properties.
41
+ // We set them here so that they are available in the prototype.
42
+ // This improved performance by 25% for certain scenario tests.
43
+ // Probably because there was no need to look up the prototype chain anymore.
44
+ this.$adaptExpectedToken = null;
45
+ this.$adaptExpectedExcludes = [ ];
46
+ this.$nextTokensToken = null;
47
+ this.$nextTokensContext = null;
48
+
49
+ this.prepareGenericKeywords();
50
+ this.options = {};
51
+
38
52
  return this;
39
53
  }
40
54
 
@@ -55,6 +69,8 @@ GenericAntlrParser.prototype = Object.assign(
55
69
  valueWithTokenLocation,
56
70
  previousTokenAtLocation,
57
71
  combinedLocation,
72
+ createDict,
73
+ setDictEndLocation,
58
74
  surroundByParens,
59
75
  unaryOpForParens,
60
76
  leftAssocBinaryOp,
@@ -70,15 +86,19 @@ GenericAntlrParser.prototype = Object.assign(
70
86
  docComment,
71
87
  addDef,
72
88
  addItem,
89
+ artifactForElementAnnotateOrExtend,
73
90
  assignProps,
74
91
  createPrefixOp,
75
92
  setOnce,
76
93
  setMaxCardinality,
77
94
  pushIdent,
78
95
  handleComposition,
96
+ associationInSelectItem,
79
97
  reportExpandInline,
98
+ checkTypeFacet,
80
99
  notSupportedYet,
81
100
  csnParseOnly,
101
+ disallowElementExtension,
82
102
  noAssignmentInSameLine,
83
103
  noSemicolonHere,
84
104
  setLocalToken,
@@ -213,6 +233,22 @@ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
213
233
  // // throw new antlr4.error.InputMismatchException(this);
214
234
  // }
215
235
 
236
+ /**
237
+ * For element extensions (`extend E:elem` syntax).
238
+ * If `elemName.path` is set, remove the last extension from `$outer` and
239
+ * emit an error that the extension is invalid.
240
+ *
241
+ * @param {object} elemName
242
+ * @param {object} outer
243
+ * @param {string} extensionVariant
244
+ */
245
+ function disallowElementExtension(elemName, outer, extensionVariant) {
246
+ if (elemName.path) {
247
+ this.message( 'syntax-invalid-extend', this.tokenLocation(this.getCurrentToken()), { 'kind': extensionVariant } );
248
+ outer.extensions.length = outer.extensions.length - 1; // remove last, i.e. new extension
249
+ }
250
+ }
251
+
216
252
  function noAssignmentInSameLine() {
217
253
  const t = this.getCurrentToken();
218
254
  if (t.text === '@' && t.line <= this._input.LT(-1).line) {
@@ -373,6 +409,20 @@ function combinedLocation( start, end ) {
373
409
  return locUtils.combinedLocation( start, end );
374
410
  }
375
411
 
412
+ function createDict( location = null ) {
413
+ const dict = Object.create(null);
414
+ dict[$location] = location || this.startLocation( this._input.LT(-1) );
415
+ return dict;
416
+ }
417
+
418
+ function setDictEndLocation( dict ) {
419
+ const stop = this._input.LT(-1);
420
+ Object.assign( dict[$location], {
421
+ endLine: stop.line,
422
+ endCol: stop.stop - stop.start + stop.column + 2,
423
+ } );
424
+ }
425
+
376
426
  function surroundByParens( expr, open, close, asQuery = false ) {
377
427
  if (!expr)
378
428
  return expr;
@@ -546,11 +596,11 @@ function numberLiteral( token, sign, text = token.text ) {
546
596
  location.endCol = endCol;
547
597
  text = sign.text + text;
548
598
  }
599
+
549
600
  const num = Number.parseFloat( text || '0' ); // not Number.parseInt() !
550
601
  if (!Number.isSafeInteger(num)) {
551
602
  if (sign == null) {
552
- this.error( 'syntax-no-integer', token, {},
553
- 'An integer number is expected here' );
603
+ this.error( 'syntax-expected-integer', token, { '#': !text.match(/^[0-9]*$/) ? 'normal' : 'unsafe'} );
554
604
  }
555
605
  else if (text !== `${num}`) {
556
606
  return { literal: 'number', val: text, location };
@@ -664,13 +714,13 @@ function addDef( parent, env, kind, name, annos, props, location ) {
664
714
  }
665
715
  }
666
716
  else if (name && name.id == null) {
667
- name.id = pathName(name.path ); // A.B.C -> 'A.B.C'
717
+ name.id = pathName( name.path ); // A.B.C -> 'A.B.C'
668
718
  }
669
719
  const art = this.assignProps( { name }, annos, props, location );
670
720
  if (kind)
671
721
  art.kind = kind;
672
- if (!parent[env])
673
- parent[env] = Object.create(null);
722
+ if (!parent[env]) // TODO: dump with --test-mode, env !== 'artifacts'
723
+ parent[env] = env === 'args' ? Object.create(null) : this.createDict( { ...location } );
674
724
  if (!art.name || art.name.id == null) {
675
725
  // no id was parsed, but with error recovery: no further error
676
726
  // TODO: add to parent[env]['']
@@ -727,6 +777,31 @@ function addItem( parent, env, kind, annos, props, location ) {
727
777
  return art;
728
778
  }
729
779
 
780
+ /**
781
+ * For `annotate/extend E:elem.sub`, create the `elements` structure
782
+ * that can be used by the core compiler to annotate/extend elements.
783
+ *
784
+ * @param {string} kind Either `annotate` or `extend`
785
+ * @param {object} artifact Main artifact that shall have `elements`.
786
+ * @param {XSN.Path} elementPath Path as returned by `simplePath` token.
787
+ * @param {object[]} annos Existing annotations that shall be added to the _last_ path step.
788
+ * @param {XSN.Location} artifactLocation Start location of the `annotate` statement.
789
+ * @returns {object} Deepest element
790
+ */
791
+ function artifactForElementAnnotateOrExtend(kind, artifact, elementPath, annos, artifactLocation ) {
792
+ if (!Array.isArray(elementPath) || elementPath.broken || elementPath.length < 1)
793
+ return artifact;
794
+
795
+ for (const seg of elementPath.slice(0, -1)) {
796
+ artifact = this.addDef( artifact, 'elements', kind,
797
+ { path: [seg], location: seg.location }, null, {}, artifactLocation );
798
+ }
799
+ const last = elementPath[elementPath.length - 1];
800
+ artifact = this.addDef( artifact, 'elements', kind,
801
+ { path: [ last ], location: last.location }, annos, {}, artifactLocation );
802
+ return artifact;
803
+ }
804
+
730
805
  /** Assign all non-empty (undefined, null, {}, []) properties in argument
731
806
  * `props` and argument `annos` as property `$annotations` to `target`
732
807
  * and return it. Hack: if argument `annos` is exactly `true`, return
@@ -829,6 +904,31 @@ function handleComposition( cardinality, isComposition ) {
829
904
  this.excludeExpected( [ [ "'}'", 'COMPOSITIONofBRACE' ], brace1, ...manyOne ] );
830
905
  }
831
906
 
907
+ function associationInSelectItem( art ) {
908
+ const isPath = art.value.path && art.value.path.length
909
+ const isIdentifier = isPath && art.value.path.length === 1;
910
+ if (isIdentifier) {
911
+ if (!art.name) {
912
+ art.name = art.value.path[0];
913
+ } else {
914
+ // Use alias if provided, i.e. ignore art.value.path.
915
+ this.error( 'query-unexpected-alias', art.name.location, {},
916
+ 'Unexpected alias for association' );
917
+ }
918
+ delete art.value;
919
+ } else {
920
+ const loc = isPath ? art.value.path[1].location : art.value.location;
921
+ // If neither path nor alias are present, `query-req-name` is emitted in `populate.js`.
922
+ if (isPath || art.name) {
923
+ this.error( 'query-expected-identifier', loc, { '#': 'assoc' } );
924
+ if (isPath) {
925
+ art.name = art.value.path[art.value.path.length - 1];
926
+ }
927
+ delete art.value;
928
+ }
929
+ }
930
+ }
931
+
832
932
  function reportExpandInline( clauseName ) {
833
933
  let token = this.getCurrentToken();
834
934
  // improve error location when using "inline" `.{…}` after ref (arguments and
@@ -840,6 +940,24 @@ function reportExpandInline( clauseName ) {
840
940
  'Unexpected nested $(PROP), can only be used after a reference' );
841
941
  }
842
942
 
943
+ function checkTypeFacet( art, argIdent ) {
944
+ const id = argIdent.id;
945
+ if (id === 'length' || id === 'scale' || id === 'precision' || id === 'srid') {
946
+ if (art[id] !== undefined) {
947
+ this.error( 'syntax-duplicate-argument', argIdent.location,
948
+ { '#': 'duplicate', code: id } );
949
+ this.error( 'syntax-duplicate-argument', art[id].location,
950
+ { '#': 'duplicate', code: id } );
951
+ }
952
+ return true;
953
+
954
+ } else {
955
+ this.error( 'syntax-duplicate-argument', argIdent.location,
956
+ { '#': 'unknown', code: id } );
957
+ return false;
958
+ }
959
+ }
960
+
843
961
  module.exports = {
844
962
  genericAntlrParser: GenericAntlrParser,
845
963
  };