@sap/cds-compiler 2.12.0 → 2.13.6

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 (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  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 +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  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 +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -89,6 +89,8 @@
89
89
 
90
90
  const { dictAdd } = require('../base/dictionaries');
91
91
 
92
+ const $location = Symbol.for('cds.$location');
93
+
92
94
  let inExtensions = null;
93
95
 
94
96
  let vocabInDefinitions = null; // must be reset!
@@ -661,6 +663,9 @@ const schema = compileSchema( {
661
663
  ignore: true, type: ignore,
662
664
  },
663
665
  // TODO: should we keep $parens ?
666
+ $generated: {
667
+ type: string,
668
+ },
664
669
  $: { type: ignore, ignore: true }, // including $origin
665
670
  _: { type: ignore, ignore: true },
666
671
  } );
@@ -931,8 +936,10 @@ function definition( def, spec, xsn, csn, name ) {
931
936
  popLocation( def );
932
937
  if (kind !== 'annotation' || prop === 'vocabularies')
933
938
  return r;
934
- if (!vocabInDefinitions)
939
+ if (!vocabInDefinitions) {
935
940
  vocabInDefinitions = Object.create(null);
941
+ vocabInDefinitions[$location] = location();
942
+ }
936
943
  vocabInDefinitions[name] = r; // deprecated: anno def in 'definitions'
937
944
  return undefined;
938
945
 
@@ -957,6 +964,7 @@ function dictionaryOf( elementFct ) {
957
964
  return ignore( dict );
958
965
  }
959
966
  const r = Object.create(null);
967
+ r[$location] = location();
960
968
  const allNames = Object.keys( dict );
961
969
  if (!allNames.length)
962
970
  return r; // {} in one JSON line
@@ -980,6 +988,7 @@ function keys( array, spec, xsn ) {
980
988
  if (!isArray( array, spec ))
981
989
  return;
982
990
  const r = Object.create(null);
991
+ r[$location] = location();
983
992
  ++virtualLine;
984
993
  for (const def of array) {
985
994
  const id = def.as || implicitName( def.ref );
@@ -1177,6 +1186,7 @@ function annoValue( val, spec ) {
1177
1186
  if (lit !== 'object')
1178
1187
  return { val, literal: lit, location: location() };
1179
1188
  if (Array.isArray( val )) {
1189
+ /** @type {string|boolean} */
1180
1190
  let seenEllipsis = false;
1181
1191
  if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
1182
1192
  if (val.some( isEllipsis )) {
@@ -1435,6 +1445,7 @@ function excluding( array, spec, xsn ) {
1435
1445
  if (!isArray( array, spec ))
1436
1446
  return;
1437
1447
  const r = Object.create(null);
1448
+ r[$location] = location();
1438
1449
  ++virtualLine;
1439
1450
  for (const ex of array) {
1440
1451
  const id = string( ex, spec ) || '';
@@ -57,7 +57,7 @@ const transformers = {
57
57
  key: value,
58
58
  unique: value,
59
59
  masked: value,
60
- params: insertOrderDict,
60
+ params,
61
61
  // early expression / query properties -------------------------------------
62
62
  op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
63
63
  from, // before elements!
@@ -185,7 +185,7 @@ const propertyOrder = (function orderPositions() {
185
185
 
186
186
  // sync with definition in from-csn.js:
187
187
  const typeProperties = [
188
- 'target', 'elements', 'enum', 'items',
188
+ 'target', 'elements', 'enum', 'items', // TODO: notNull?
189
189
  'type', 'length', 'precision', 'scale', 'srid', 'localized',
190
190
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
191
191
  ];
@@ -204,7 +204,10 @@ const operators = {
204
204
  when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
205
205
  case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
206
206
  over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
207
- orderBy: exprs => [
207
+ orderBy: exprs => [ // ORDER BY in generic functions
208
+ ...exprs[0], ...operators.overOrderBy(exprs.slice(1)),
209
+ ],
210
+ overOrderBy: exprs => [ // ORDER BY in OVER() clause
208
211
  'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
209
212
  ],
210
213
  partitionBy: exprs => [
@@ -277,7 +280,7 @@ function sortCsn( csn, cloneOptions = false ) {
277
280
  }
278
281
 
279
282
  /**
280
- * Check wether the given object has non enumerable property.
283
+ * Check whether the given object has non enumerable property.
281
284
  * Ensure that we don't take it from the prototype, only "directly" - we accidentally
282
285
  * cloned elements with a cds.linked input otherwise.
283
286
  *
@@ -621,10 +624,7 @@ function targetAspect( val, csn, node ) {
621
624
  if (val.$inferred)
622
625
  return undefined;
623
626
  if (node.target) {
624
- csn.$origin = { type: 'cds.Composition' };
625
- if (node.cardinality)
626
- csn.$origin.cardinality = standard( node.cardinality );
627
- csn.$origin.target = (val.elements) ? standard( val ) : artifactRef( val, true );
627
+ csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
628
628
  return undefined;
629
629
  }
630
630
  }
@@ -643,7 +643,7 @@ function target( val, _csn, node ) {
643
643
  val = node._origin.target;
644
644
  if (val.elements)
645
645
  return standard( val ); // elements in target (parse-cdl)
646
- if (!universalCsn || node.on)
646
+ if (!universalCsn || gensrcFlavor || node.on)
647
647
  return artifactRef( val, true );
648
648
  const tref = artifactRef( val, true );
649
649
  const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
@@ -779,7 +779,7 @@ function addLocation( loc, csn ) {
779
779
  // Remove endLine/endCol:
780
780
  // Reasoning: $location is mostly attached to definitions/members but the name
781
781
  // is often not the reason for an error or warning. So we gain little benefit for
782
- // two more properties.
782
+ // two more properties. It is also an indication that the location is not exact.
783
783
  const val = { file: loc.file, line: loc.line, col: loc.col };
784
784
  Object.defineProperty( csn, '$location', {
785
785
  value: val, configurable: true, writable: true, enumerable: withLocations,
@@ -807,6 +807,13 @@ function actions( dict ) {
807
807
  : undefined;
808
808
  }
809
809
 
810
+ function params( dict ) {
811
+ const keys = Object.keys( dict );
812
+ return (keys.length)
813
+ ? insertOrderDict( dict )
814
+ : undefined;
815
+ }
816
+
810
817
  function dictionary( dict, keys, prop ) {
811
818
  const csn = Object.create( dictionaryPrototype );
812
819
  for (const name of keys) {
@@ -863,7 +870,7 @@ function definition( art, _csn, _node, prop ) {
863
870
  c.returns = { elements: elems };
864
871
  }
865
872
  // precondition already fulfilled: art.kind !== 'key'
866
- addOrigin( c, art, art._origin );
873
+ addOrigin( c, art, art );
867
874
  return c;
868
875
  }
869
876
 
@@ -876,7 +883,8 @@ function includesOrigin( includes, art ) {
876
883
  for (const incl of includes.slice(1)) {
877
884
  const aspect = incl._artifact;
878
885
  for (const prop in aspect) {
879
- if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
886
+ if ((prop.charAt(0) === '@' || prop === 'doc') &&
887
+ (!art[prop] || art[prop].$inferred)) {
880
888
  const anno = aspect[prop];
881
889
  if (anno.val !== null)
882
890
  // matererialize non-null annos (whether direct or inherited)
@@ -887,9 +895,23 @@ function includesOrigin( includes, art ) {
887
895
  return (Object.keys( result ).length === 1) ? $origin : result;
888
896
  }
889
897
 
890
- function addOrigin( csn, xsn, origin ) {
891
- if (!universalCsn || hasExplicitProp( xsn.type ))
898
+ function addOrigin( csn, xsn, node ) {
899
+ if (!universalCsn)
892
900
  return;
901
+ if (hasExplicitProp( xsn.type, 'cast' )) {
902
+ const main = xsn._main || xsn;
903
+ let count = 0;
904
+ let source = xsn;
905
+ while (source && source._main === main) {
906
+ source = source.value && source.value._artifact;
907
+ ++count;
908
+ }
909
+ if (count > 0 && source && source.kind !== 'builtin')
910
+ csn.$source = originRef( source, xsn );
911
+ else if (count > 1)
912
+ csn.$source = null;
913
+ return;
914
+ }
893
915
  if (xsn._from) {
894
916
  const source = xsn._from[0]._origin;
895
917
  csn.$origin = originRef( source );
@@ -903,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
903
925
  csn.$origin = includesOrigin( xsn.includes, xsn );
904
926
  return;
905
927
  }
906
- else if (!xsn._main || xsn.kind === 'select') {
928
+ let origin = getOrigin( node );
929
+ if (xsn.$inferred === 'composition-entity') {
930
+ csn.$origin = originRef( origin, xsn );
907
931
  return;
908
932
  }
933
+ else if (!isMember( xsn ) || xsn.kind === 'select') {
934
+ return;
935
+ }
936
+ // from here on: member:
909
937
  const parent = getParent( xsn );
910
938
  const parentOrigin = getOrigin( parent );
911
- if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
912
- if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
939
+ if (!origin) {
940
+ if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
913
941
  csn.$origin = null;
914
942
  return;
915
943
  }
916
- // Skip all proxies which do not make it into the CSN, as there are no
917
- // individual annotations or redirection targets on it:
918
- while (origin._parent && origin._parent.$expand === 'origin')
919
- origin = origin._origin || origin.type._artifact;
920
- // The while loop is not only for the else case below: when setting implicit
921
- // prototypes, it is important that we do not have to follow the prototypes of
922
- // other object; we would need to ensure a right order to avoid issues otherwise.
923
944
  if (parentOrigin === getParent( origin )) {
924
945
  // implicit prototype or shortened reference
925
946
  const { id } = origin.name || {};
@@ -928,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
928
949
  return;
929
950
  }
930
951
  if (origin.kind === 'mixin') {
931
- // currently, target and on are always set - nothing to do here, just set type
932
- csn.type = 'cds.Association';
952
+ set( 'type', csn, origin );
953
+ set( 'cardinality', csn, origin );
954
+ // currently, target and on are always set - nothing to do here
933
955
  return;
934
956
  }
957
+ // Skip all proxies which do not make it into the CSN, as there are no
958
+ // individual annotations or redirection targets on it:
959
+ while (origin._parent && origin._parent.$expand === 'origin')
960
+ origin = origin._origin || origin.type._artifact;
935
961
  const ref = originRef( origin, xsn );
936
962
  if (ref) {
937
963
  csn.$origin = ref;
938
964
  return;
939
965
  }
940
- // An element of a query with a query in FROM:
966
+ // An element of a query with a query in FROM: -----------------------------
941
967
  const anon = definition( origin ); // use $origin: {...} if necessary
942
968
  // as there are no implicit $origin prototypes on sub query elements (yet),
943
969
  // we do not have to care about $origin not being set
@@ -945,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
945
971
  if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
946
972
  // repeated anon: flatten
947
973
  csn.$origin = Object.assign( $origin, anon );
974
+ return;
975
+ }
976
+ // Annotations and 'doc' must keep the distinction between direct or inherited,
977
+ // other properties can as well be set as direct element properties
978
+ const annos = {};
979
+ for (const prop of Object.keys( anon )) {
980
+ if (prop.charAt(0) === '@' || prop === 'doc')
981
+ annos[prop] = anon[prop];
982
+ else if (prop === '$source')
983
+ csn[prop] = anon[prop]; // overwrite from inner
984
+ else if (prop !== '$location' && prop !== '$origin' && !(prop in csn))
985
+ csn[prop] = anon[prop];
948
986
  }
949
- else if (Object.keys( anon )
950
- // (we can use the properties in `csn`, because addOrigin() is called last)
951
- .every( p => p in csn || p === '$origin' || p === '$location')) {
952
- // nothing new in $origin: {...}
953
- addOrigin( csn, xsn, origin._origin );
987
+ if (Object.keys( annos ).length) {
988
+ if (!csn.type && $origin)
989
+ annos.$origin = $origin;
990
+ csn.$origin = annos;
954
991
  }
955
- else {
956
- csn.$origin = anon;
992
+ else if (!csn.type) {
993
+ addOrigin( csn, xsn, origin );
957
994
  }
958
995
  }
959
996
 
@@ -963,10 +1000,27 @@ function getParent( art ) {
963
1000
  return (main && parent === main._leadingQuery) ? main : parent;
964
1001
  }
965
1002
 
1003
+ function isMember( art ) {
1004
+ // TODO: introduce art.kind = '$aspect' for anonymous aspect (is a member) ?
1005
+ return !!(art._main || art._outer);
1006
+ }
1007
+
1008
+ // function getDefinition( art ) {
1009
+ // let main = art._main || art;
1010
+ // while (main._outer) // anonymous aspect
1011
+ // main = main._outer._main;
1012
+ // return main;
1013
+ // }
1014
+
1015
+ // XSN `_origin` is (currently?) not the same as _origin in Universal CSN...
1016
+ // TODO: at least with expand, set it correctly (alias: keep, assoc: to entity, $builtin: no)
966
1017
  function getOrigin( art ) {
967
- if (art._origin)
968
- return art._origin;
969
- if (hasExplicitProp( art.type ))
1018
+ if (art.$noOrigin)
1019
+ return undefined;
1020
+ const { _origin } = art;
1021
+ if (_origin)
1022
+ return (_origin.kind === 'builtin') ? undefined : _origin; // not $dollarVariable
1023
+ if (hasExplicitProp( art.type, 'cast' ))
970
1024
  return art.type._artifact;
971
1025
  if (art.includes)
972
1026
  return art.includes[0]._artifact;
@@ -975,31 +1029,31 @@ function getOrigin( art ) {
975
1029
  return undefined;
976
1030
  }
977
1031
 
978
- function hasExplicitProp( ref ) {
979
- return ref && !ref.$inferred;
1032
+ function hasExplicitProp( ref, alsoLikeExplicit ) {
1033
+ return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
980
1034
  }
981
1035
 
982
1036
  function originRef( art, user ) {
983
1037
  const r = [];
984
- // do not use name.element, as we allow `.`s in name
1038
+ // do not use name.element, as we might allow `.`s in name
985
1039
  let parent = art;
986
- while (parent._main && parent.kind !== 'select') {
1040
+ if (parent._outer && parent.kind === 'aspect')
1041
+ r.push( { target: true } );
1042
+ while (isMember( parent ) && parent.kind !== 'select') {
987
1043
  const nkind = normalizedKind[parent.kind];
988
- if (parent.name.id || !r.length)
1044
+ const name = parent.name || parent._outer.name;
1045
+ if (name.id || !r.length)
989
1046
  // Return parameter is in XSN - kind: 'param', name.id: ''
990
1047
  // eslint-disable-next-line no-nested-ternary, max-len
991
- r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
1048
+ r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
992
1049
  parent = parent._parent;
993
1050
  }
994
1051
  if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
995
1052
  // well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
996
- return null; // probably use $origin: {...}
1053
+ return false; // do not write, probably use $origin: {...}
997
1054
  // for sub query in FROM in sub query in FROM, we could condense the info
998
1055
 
999
- // Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
1000
- if (r.length === 1 && normalizedKind[art.kind] === 'action')
1001
- return [ art.name.absolute, art.name.id ];
1002
- r.push( art.name.absolute );
1056
+ r.push( parent.name.absolute );
1003
1057
  r.reverse();
1004
1058
  return r;
1005
1059
  }
@@ -1018,6 +1072,9 @@ function kind( k, csn, node ) {
1018
1072
  else if (k === 'action' && node._main && universalCsn && node.$inferred) {
1019
1073
  // Universal CSN: do not mention kind: 'action' on expanded action
1020
1074
  }
1075
+ else if (k === 'aspect' && (node._outer || node.$inferred)) {
1076
+ return; // do not show kind for anonymous aspect
1077
+ }
1021
1078
  else if (![
1022
1079
  'element', 'key', 'param', 'enum', 'select', '$join',
1023
1080
  '$tableAlias', 'annotation', 'mixin',
@@ -1032,26 +1089,20 @@ function kind( k, csn, node ) {
1032
1089
  function type( node, csn, xsn ) {
1033
1090
  if (!universalCsn)
1034
1091
  return artifactRef( node, !node.$extra );
1035
- if (node.$inferred)
1092
+ if (node.$inferred && node.$inferred !== 'cast')
1036
1093
  return undefined;
1037
1094
  if (xsn._origin) {
1038
1095
  if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1039
1096
  csn.$origin = definition( xsn._origin );
1040
- return undefined;
1041
1097
  }
1042
1098
  }
1043
- else if ( xsn.targetAspect && xsn.target ) {
1044
- // type moved to $origin: { type: … }
1045
- return undefined;
1046
- }
1047
1099
  return artifactRef( node, !node.$extra );
1048
1100
  }
1049
1101
 
1050
- function cardinality( node, csn, xsn ) {
1102
+ function cardinality( node ) {
1051
1103
  if (!universalCsn)
1052
1104
  return standard( node );
1053
- // cardinality might be moved to $origin: { cardinality: … }
1054
- if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
1105
+ if (node.$inferred)
1055
1106
  return undefined;
1056
1107
  return standard( node );
1057
1108
  }
@@ -1159,8 +1210,12 @@ function value( node ) {
1159
1210
  // "Short" value form, e.g. for annotation assignments
1160
1211
  if (!node)
1161
1212
  return true; // `@aBool` short for `@aBool: true`
1162
- if (universalCsn && node.$inferred === 'prop') // via propagator.js
1163
- return undefined;
1213
+ if (universalCsn && node.$inferred) {
1214
+ if (node.$inferred === 'prop' || node.$inferred === '$generated') // via propagator.js
1215
+ return undefined;
1216
+ else if (node.$inferred === 'NULL')
1217
+ return null;
1218
+ }
1164
1219
  if (node.$inferred && gensrcFlavor)
1165
1220
  return undefined;
1166
1221
  if (node.path) {
@@ -1329,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1329
1384
  if (node.op.val === 'SELECT') {
1330
1385
  if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
1331
1386
  node.from && node.from.path && !projectionAsQuery) {
1332
- csn.projection = standard( node );
1387
+ csn.projection = addLocation( node.location, standard( node ) );
1333
1388
  return undefined;
1334
1389
  }
1335
1390
  const select = { SELECT: extra( standard( node ), node, expectedParens ) };
@@ -1351,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1351
1406
  gensrcFlavor = gensrcSaved;
1352
1407
  }
1353
1408
  }
1409
+ // the $location is better put inside the SELECT value, not as sibling (but
1410
+ // we keep it as sibling also for compatibility):
1411
+ addLocation( node.location, select.SELECT );
1354
1412
  return addLocation( node.location, select );
1355
1413
  }
1356
1414
  const union = {};
@@ -1399,11 +1457,13 @@ function from( node ) {
1399
1457
  else if (node.query) {
1400
1458
  return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
1401
1459
  }
1402
- else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
1403
- return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
1404
- }
1405
- return extra( addExplicitAs( artifactRef( node, false ), node.name, (id) => {
1406
- const name = node._artifact.name.absolute;
1460
+
1461
+ const ref = artifactRef( node, false );
1462
+ return extra( addExplicitAs( ref, node.name, (id) => {
1463
+ let name = ref.ref ? ref.ref[ref.ref.length - 1] : ref;
1464
+ name = name && name.id || name;
1465
+ if (!name)
1466
+ return false;
1407
1467
  const dot = name.lastIndexOf('.');
1408
1468
  return name.substring( dot + 1 ) !== id;
1409
1469
  }), node );
@@ -1452,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
1452
1512
  }
1453
1513
 
1454
1514
  function orderBy( node ) {
1455
- const expr = expression( node, 'ignoreExtra' );
1515
+ const expr = expression( node );
1456
1516
  if (node.sort)
1457
1517
  expr.sort = node.sort.val;
1458
1518
  if (node.nulls)
@@ -127,8 +127,8 @@ function removeFooterFence(line) {
127
127
  * @param {string[]} lines
128
128
  */
129
129
  function isFencedComment(lines) {
130
- const index = lines.findIndex((line, index) => {
131
- const exclude = (index === 0 || index === lines.length - 1);
130
+ const index = lines.findIndex((line, i) => {
131
+ const exclude = (i === 0 || i === lines.length - 1);
132
132
  return !exclude && !(/^\s*[*]/.test(line));
133
133
  });
134
134
  return index === -1 && lines.length > 2;
@@ -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,6 +86,7 @@ GenericAntlrParser.prototype = Object.assign(
70
86
  docComment,
71
87
  addDef,
72
88
  addItem,
89
+ artifactForElementAnnotateOrExtend,
73
90
  assignProps,
74
91
  createPrefixOp,
75
92
  setOnce,
@@ -79,6 +96,7 @@ GenericAntlrParser.prototype = Object.assign(
79
96
  reportExpandInline,
80
97
  notSupportedYet,
81
98
  csnParseOnly,
99
+ disallowElementExtension,
82
100
  noAssignmentInSameLine,
83
101
  noSemicolonHere,
84
102
  setLocalToken,
@@ -213,6 +231,22 @@ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
213
231
  // // throw new antlr4.error.InputMismatchException(this);
214
232
  // }
215
233
 
234
+ /**
235
+ * For element extensions (`extend E:elem` syntax).
236
+ * If `elemName.path` is set, remove the last extension from `$outer` and
237
+ * emit an error that the extension is invalid.
238
+ *
239
+ * @param {object} elemName
240
+ * @param {object} outer
241
+ * @param {string} extensionVariant
242
+ */
243
+ function disallowElementExtension(elemName, outer, extensionVariant) {
244
+ if (elemName.path) {
245
+ this.message( 'syntax-invalid-extend', this.tokenLocation(this.getCurrentToken()), { 'kind': extensionVariant } );
246
+ outer.extensions.length = outer.extensions.length - 1; // remove last, i.e. new extension
247
+ }
248
+ }
249
+
216
250
  function noAssignmentInSameLine() {
217
251
  const t = this.getCurrentToken();
218
252
  if (t.text === '@' && t.line <= this._input.LT(-1).line) {
@@ -373,6 +407,20 @@ function combinedLocation( start, end ) {
373
407
  return locUtils.combinedLocation( start, end );
374
408
  }
375
409
 
410
+ function createDict( location = null ) {
411
+ const dict = Object.create(null);
412
+ dict[$location] = location || this.startLocation( this._input.LT(-1) );
413
+ return dict;
414
+ }
415
+
416
+ function setDictEndLocation( dict ) {
417
+ const stop = this._input.LT(-1);
418
+ Object.assign( dict[$location], {
419
+ endLine: stop.line,
420
+ endCol: stop.stop - stop.start + stop.column + 2,
421
+ } );
422
+ }
423
+
376
424
  function surroundByParens( expr, open, close, asQuery = false ) {
377
425
  if (!expr)
378
426
  return expr;
@@ -664,13 +712,13 @@ function addDef( parent, env, kind, name, annos, props, location ) {
664
712
  }
665
713
  }
666
714
  else if (name && name.id == null) {
667
- name.id = pathName(name.path ); // A.B.C -> 'A.B.C'
715
+ name.id = pathName( name.path ); // A.B.C -> 'A.B.C'
668
716
  }
669
717
  const art = this.assignProps( { name }, annos, props, location );
670
718
  if (kind)
671
719
  art.kind = kind;
672
- if (!parent[env])
673
- parent[env] = Object.create(null);
720
+ if (!parent[env]) // TODO: dump with --test-mode, env !== 'artifacts'
721
+ parent[env] = env === 'args' ? Object.create(null) : this.createDict( { ...location } );
674
722
  if (!art.name || art.name.id == null) {
675
723
  // no id was parsed, but with error recovery: no further error
676
724
  // TODO: add to parent[env]['']
@@ -727,6 +775,31 @@ function addItem( parent, env, kind, annos, props, location ) {
727
775
  return art;
728
776
  }
729
777
 
778
+ /**
779
+ * For `annotate/extend E:elem.sub`, create the `elements` structure
780
+ * that can be used by the core compiler to annotate/extend elements.
781
+ *
782
+ * @param {string} kind Either `annotate` or `extend`
783
+ * @param {object} artifact Main artifact that shall have `elements`.
784
+ * @param {XSN.Path} elementPath Path as returned by `simplePath` token.
785
+ * @param {object[]} annos Existing annotations that shall be added to the _last_ path step.
786
+ * @param {XSN.Location} artifactLocation Start location of the `annotate` statement.
787
+ * @returns {object} Deepest element
788
+ */
789
+ function artifactForElementAnnotateOrExtend(kind, artifact, elementPath, annos, artifactLocation ) {
790
+ if (!Array.isArray(elementPath) || elementPath.broken || elementPath.length < 1)
791
+ return artifact;
792
+
793
+ for (const seg of elementPath.slice(0, -1)) {
794
+ artifact = this.addDef( artifact, 'elements', kind,
795
+ { path: [seg], location: seg.location }, null, {}, artifactLocation );
796
+ }
797
+ const last = elementPath[elementPath.length - 1];
798
+ artifact = this.addDef( artifact, 'elements', kind,
799
+ { path: [ last ], location: last.location }, annos, {}, artifactLocation );
800
+ return artifact;
801
+ }
802
+
730
803
  /** Assign all non-empty (undefined, null, {}, []) properties in argument
731
804
  * `props` and argument `annos` as property `$annotations` to `target`
732
805
  * and return it. Hack: if argument `annos` is exactly `true`, return