@sap/cds-compiler 2.10.2 → 2.11.4

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 (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
@@ -773,12 +773,8 @@ function arrayOf( fn, filter = undefined ) {
773
773
  } );
774
774
  const minLength = spec.minLength || 0;
775
775
  if (minLength > val.length) {
776
- error( 'syntax-csn-expected-length', location(true),
777
- { prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' },
778
- {
779
- std: 'Expected array in $(PROP) to have at least $(N) items',
780
- one: 'Expected array in $(PROP) to have at least one item',
781
- } );
776
+ message( 'syntax-csn-expected-length', location(true),
777
+ { prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' });
782
778
  }
783
779
  if (val.length)
784
780
  ++virtualLine; // [] in one JSON line
@@ -913,7 +909,7 @@ function definition( def, spec, xsn, csn, name ) {
913
909
  ++virtualLine;
914
910
  }
915
911
  }
916
- if (!r.name && name) {
912
+ if (!r.name && name != null) {
917
913
  r.name = { id: name, location: r.location };
918
914
  if (prop === 'columns' || prop === 'keys' || prop === 'foreignKeys')
919
915
  r.name.$inferred = 'as';
@@ -1002,13 +998,13 @@ function selectItem( def, spec, xsn, csn ) {
1002
998
  return definition( def, spec, xsn, csn, null ); // definer sets name
1003
999
  }
1004
1000
 
1005
- function returnsDefinition( def, spec, xsn, csn, name ) {
1001
+ function returnsDefinition( def, spec, xsn, csn ) {
1006
1002
  // TODO: be stricter in what is allowed inside returns
1007
1003
  if (!inExtensions)
1008
- return definition( def, spec, xsn, csn, name );
1004
+ return definition( def, spec, xsn, csn, '' );
1009
1005
  // for the moment, flatten elements in returns in an annotate
1010
1006
  // TODO: bigger Core Compiler changes would have to be done otherwise
1011
- xsn.elements = definition( def, spec, xsn, csn, name ).elements;
1007
+ xsn.elements = definition( def, spec, xsn, csn, '' ).elements;
1012
1008
  xsn.$syntax = 'returns';
1013
1009
  return undefined;
1014
1010
  }
@@ -1271,6 +1267,10 @@ function func( val, spec, xsn ) {
1271
1267
 
1272
1268
  function xpr( exprs, spec, xsn, csn ) {
1273
1269
  if (csn.func) {
1270
+ if (!exprs.length) {
1271
+ message( 'syntax-csn-expected-length', location(true),
1272
+ { prop: 'xpr', otherprop: 'func', '#': 'suffix' });
1273
+ }
1274
1274
  xsn.suffix = exprArgs( exprs, spec );
1275
1275
  }
1276
1276
  else {
@@ -33,12 +33,23 @@ let projectionAsQuery = false;
33
33
  let withLocations = false;
34
34
  let dictionaryPrototype = null;
35
35
 
36
+ // Properties for dictionaries, set in compileX() and TODO: parseX(), must be
37
+ // stored with symbols as keys, as we do not want to disallow any key name:
38
+ const $inferred = Symbol.for('cds.$inferred');
39
+
40
+ // XSN $inferred values mapped to Universal CSN $generated values:
41
+ const inferredAsGenerated = {
42
+ autoexposed: 'exposed',
43
+ 'localized-entity': 'localized',
44
+ localized: 'localized', // on elements (texts, localized)
45
+ 'composition-entity': 'composed', // ('aspect-composition' on element not in CSN)
46
+ };
47
+
36
48
  // IMPORTANT: the order of these properties determine the order of properties
37
49
  // in the resulting CSN !!! Also check const `csnPropertyNames`.
38
50
  const transformers = {
39
51
  // early and modifiers (without null / not null) ---------------------------
40
52
  kind,
41
- _outer: ( _, csn, node ) => addOrigin( csn, node ),
42
53
  id: n => n, // in path item
43
54
  doc: value,
44
55
  '@': value,
@@ -63,11 +74,11 @@ const transformers = {
63
74
  precision: value,
64
75
  scale: value,
65
76
  srid: value,
66
- cardinality: standard, // also for pathItem: after 'id', before 'where'
77
+ cardinality, // also in pathItem: after 'id', before 'where'
67
78
  targetAspect,
68
79
  target,
69
80
  foreignKeys,
70
- enum: insertOrderDict,
81
+ enum: enumDict,
71
82
  items,
72
83
  includes: arrayOf( artifactRef ), // also entities
73
84
  // late expressions / query properties -------------------------------------
@@ -199,6 +210,15 @@ const operators = {
199
210
  partitionBy: exprs => [
200
211
  'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
201
212
  ],
213
+ rows: exprs => [
214
+ 'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
215
+ ],
216
+ preceding: postfix( [ 'preceding' ] ),
217
+ unboundedPreceding: [ 'unbounded', 'preceding' ],
218
+ currentRow: [ 'current', 'row' ],
219
+ unboundedFollowing: [ 'unbounded', 'following' ],
220
+ following: postfix( [ 'following' ] ),
221
+ frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
202
222
  // xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
203
223
  };
204
224
 
@@ -597,10 +617,21 @@ function set( prop, csn, node ) {
597
617
  }
598
618
 
599
619
  function targetAspect( val, csn, node ) {
620
+ if (universalCsn) {
621
+ if (val.$inferred)
622
+ return undefined;
623
+ 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 );
628
+ return undefined;
629
+ }
630
+ }
600
631
  const ta = (val.elements)
601
632
  ? addLocation( val.location, standard( val ) )
602
633
  : artifactRef( val, true );
603
- if (!gensrcFlavor || node.target && !node.target.$inferred)
634
+ if (!gensrcFlavor && !universalCsn || node.target && !node.target.$inferred)
604
635
  return ta;
605
636
  // For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
606
637
  csn.target = ta;
@@ -622,7 +653,7 @@ function target( val, _csn, node ) {
622
653
  }
623
654
 
624
655
  function items( obj, csn, node ) {
625
- if (!keepElements( node ))
656
+ if (!keepElements( node, obj ))
626
657
  return undefined;
627
658
  return standard( obj ); // no 'elements' with inferred elements with gensrc
628
659
  }
@@ -637,23 +668,41 @@ function elements( dict, csn, node ) {
637
668
  return insertOrderDict( dict );
638
669
  }
639
670
 
671
+ function enumDict( dict, csn, node ) {
672
+ if (gensrcFlavor && dict[$inferred] ||
673
+ !keepElements( node ))
674
+ // no 'elements' with SELECT or inferred elements with gensrc;
675
+ // hidden or visible 'elements' will be set in query()
676
+ return undefined;
677
+ if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate')
678
+ // derived type of enum type with individual annotations: also set $origin
679
+ csn.$origin = originRef( node.type._artifact );
680
+ return insertOrderDict( dict );
681
+ }
682
+
640
683
  function enumerableQueryElements( select ) {
641
- if (!universalCsn || select === select._main._leadingQuery)
642
- return false;
643
- if (select.orderBy || select.$orderBy)
644
- return true;
645
- const alias = select._parent;
646
- return alias.query && (alias.query._leadingQuery || alias.query) === select;
684
+ return (universalCsn && select !== select._main._leadingQuery);
647
685
  }
648
686
 
649
687
  // Should we render the elements? (and items?)
650
- function keepElements( node ) {
688
+ function keepElements( node, line ) {
651
689
  if (universalCsn)
652
690
  // $expand = null/undefined: not elements not via expansion
653
691
  // $expand = 'target'/'annotate': with redirections / individual annotations
654
692
  return node.$expand !== 'origin';
655
693
  if (!node.type || node.kind === 'type')
656
694
  return true;
695
+ // keep many SimpleType/Entity
696
+ if (line) {
697
+ if (!node.type)
698
+ return true;
699
+ const array = node.type._artifact; // see function items() in propagator.js
700
+ const ltype = line.type && line.type._artifact;
701
+ if (!array || // reference errors
702
+ array._main && !line.elements && !line.enum && !line.items && !line.notNull &&
703
+ (!ltype || !ltype._main)) // many Foo:bar -> not SimpleType
704
+ return true;
705
+ }
657
706
  // even if expanded elements have no new target or direct annotation,
658
707
  // they might have got one via propagation – any new target/annos during their
659
708
  // way from the original structure type definition to the current usage
@@ -711,7 +760,8 @@ function ignore() { /* no-op: ignore property */ }
711
760
 
712
761
  function location( loc, csn, xsn ) {
713
762
  if (xsn.kind && xsn.kind.charAt(0) !== '$' && xsn.kind !== 'select' &&
714
- (!xsn.$inferred || !xsn._main)) { // TODO: also for 'select'
763
+ (!xsn.$inferred || !xsn._main) &&
764
+ xsn.$inferred !== 'REDIRECTED') { // TODO: also for 'select'
715
765
  // Also include $location for elements in queries (if not via '*')
716
766
  addLocation( xsn.name && xsn.name.location || loc, csn );
717
767
  }
@@ -767,8 +817,10 @@ function dictionary( dict, keys, prop ) {
767
817
  }
768
818
 
769
819
  function foreignKeys( dict, csn, node ) {
770
- if (universalCsn && !target( node.target, csn, node ))
771
- return;
820
+ if (universalCsn) {
821
+ if (!target( node.target, csn, node ) || dict[$inferred] === 'keys')
822
+ return;
823
+ }
772
824
  if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
773
825
  dict = node._origin.foreignKeys;
774
826
  const keys = [];
@@ -801,50 +853,151 @@ function definition( art, _csn, _node, prop ) {
801
853
  delete c.elements;
802
854
  c.returns = { elements: elems };
803
855
  }
856
+ // precondition already fulfilled: art.kind !== 'key'
857
+ addOrigin( c, art, art._origin );
804
858
  return c;
805
859
  }
806
860
 
807
- function addOrigin( csn, xsn ) {
808
- if (!universalCsn)
809
- return csn;
861
+ // create $origin specification for `includes` of `art`
862
+ function includesOrigin( includes, art ) {
863
+ const $origin = originRef( includes[0]._artifact );
864
+ if (includes.length === 1)
865
+ return $origin;
866
+ const result = { $origin };
867
+ for (const incl of includes.slice(1)) {
868
+ const aspect = incl._artifact;
869
+ for (const prop in aspect) {
870
+ if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
871
+ const anno = aspect[prop];
872
+ if (anno.val !== null)
873
+ // matererialize non-null annos (whether direct or inherited)
874
+ result[prop] = value( Object.create( anno, { $inferred: { value: null } } ) );
875
+ }
876
+ }
877
+ }
878
+ return (Object.keys( result ).length === 1) ? $origin : result;
879
+ }
880
+
881
+ function addOrigin( csn, xsn, origin ) {
882
+ if (!universalCsn || hasExplicitProp( xsn.type ))
883
+ return;
810
884
  if (xsn._from) {
811
- csn.$origin = originRef( xsn._from[0]._origin );
885
+ const source = xsn._from[0]._origin;
886
+ csn.$origin = originRef( source );
887
+ if (source.params && !xsn.params)
888
+ csn.params = null; // discontinue `params` inheritance
889
+ if (source.actions && !xsn.actions)
890
+ csn.actions = null; // discontinue `actions` inheritance
891
+ return;
812
892
  }
813
- else if (xsn.includes && xsn.includes.length > 1) {
814
- csn.$origin = { $origin: originRef( xsn.includes[0]._artifact ) };
893
+ else if (xsn.includes) {
894
+ csn.$origin = includesOrigin( xsn.includes, xsn );
895
+ return;
815
896
  }
816
- else if (xsn._origin && !hasExplicitProp( xsn.type ) && xsn._origin.kind !== 'builtin') {
817
- let origin = xsn._origin;
818
- while (origin._parent && origin._parent.$expand === 'origin')
819
- origin = origin._origin || origin.type._artifact;
820
- csn.$origin = originRef( origin );
897
+ else if (!xsn._main || xsn.kind === 'select') {
898
+ return;
821
899
  }
822
- return csn;
900
+ const parent = getParent( xsn );
901
+ const parentOrigin = getOrigin( parent );
902
+ if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
903
+ if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
904
+ csn.$origin = null;
905
+ return;
906
+ }
907
+ // Skip all proxies which do not make it into the CSN, as there are no
908
+ // individual annotations or redirection targets on it:
909
+ while (origin._parent && origin._parent.$expand === 'origin')
910
+ origin = origin._origin || origin.type._artifact;
911
+ // The while loop is not only for the else case below: when setting implicit
912
+ // prototypes, it is important that we do not have to follow the prototypes of
913
+ // other object; we would need to ensure a right order to avoid issues otherwise.
914
+ if (parentOrigin === getParent( origin )) {
915
+ // implicit prototype or shortened reference
916
+ const { id } = origin.name || {};
917
+ if (id && xsn.name && id !== xsn.name.id)
918
+ csn.$origin = id;
919
+ return;
920
+ }
921
+ if (origin.kind === 'mixin') {
922
+ // currently, target and on are always set - nothing to do here, just set type
923
+ csn.type = 'cds.Association';
924
+ return;
925
+ }
926
+ const ref = originRef( origin, xsn );
927
+ if (ref) {
928
+ csn.$origin = ref;
929
+ return;
930
+ }
931
+ // An element of a query with a query in FROM:
932
+ const anon = definition( origin ); // use $origin: {...} if necessary
933
+ // as there are no implicit $origin prototypes on sub query elements (yet),
934
+ // we do not have to care about $origin not being set
935
+ const { $origin } = anon;
936
+ if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
937
+ // repeated anon: flatten
938
+ csn.$origin = Object.assign( $origin, anon );
939
+ }
940
+ else if (Object.keys( anon )
941
+ // (we can use the properties in `csn`, because addOrigin() is called last)
942
+ .every( p => p in csn || p === '$origin' || p === '$location')) {
943
+ // nothing new in $origin: {...}
944
+ addOrigin( csn, xsn, origin._origin );
945
+ }
946
+ else {
947
+ csn.$origin = anon;
948
+ }
949
+ }
950
+
951
+ function getParent( art ) {
952
+ const parent = art._parent;
953
+ const main = parent._main;
954
+ return (main && parent === main._leadingQuery) ? main : parent;
955
+ }
956
+
957
+ function getOrigin( art ) {
958
+ if (art._origin)
959
+ return art._origin;
960
+ if (hasExplicitProp( art.type ))
961
+ return art.type._artifact;
962
+ if (art.includes)
963
+ return art.includes[0]._artifact;
964
+ if (art._from)
965
+ return art._from[0]._origin;
966
+ return undefined;
823
967
  }
824
968
 
825
969
  function hasExplicitProp( ref ) {
826
970
  return ref && !ref.$inferred;
827
971
  }
828
972
 
829
- function originRef( art ) {
973
+ function originRef( art, user ) {
830
974
  const r = [];
831
975
  // do not use name.element, as we allow `.`s in name
832
- let main = art;
833
- while (main._main && main.kind !== 'select') {
834
- const nkind = normalizedKind[main.kind];
835
- if (main.name.id || !r.length) // { param: "" } only for return, not elements inside
836
- r.push( nkind ? { [nkind]: main.name.id } : main.name.id );
837
- main = main._parent;
838
- }
839
- if (main._main) // well, an element of an query in FROM
840
- return definition( art ); // use $origin: {}
976
+ let parent = art;
977
+ while (parent._main && parent.kind !== 'select') {
978
+ const nkind = normalizedKind[parent.kind];
979
+ if (parent.name.id || !r.length)
980
+ // Return parameter is in XSN - kind: 'param', name.id: ''
981
+ // eslint-disable-next-line no-nested-ternary, max-len
982
+ r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
983
+ parent = parent._parent;
984
+ }
985
+ if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
986
+ // well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
987
+ return null; // probably use $origin: {...}
841
988
  // for sub query in FROM in sub query in FROM, we could condense the info
989
+
990
+ // Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
991
+ if (r.length === 1 && normalizedKind[art.kind] === 'action')
992
+ return [ art.name.absolute, art.name.id ];
842
993
  r.push( art.name.absolute );
843
994
  r.reverse();
844
995
  return r;
845
996
  }
846
997
 
847
998
  function kind( k, csn, node ) {
999
+ if (node.$inferred === 'REDIRECTED')
1000
+ return;
848
1001
  if (k === 'annotate' || k === 'extend') {
849
1002
  // We just use `name.absolute` because it is very likely a "constructed"
850
1003
  // extensions. The CSN parser must produce name.path like for other refs.
@@ -853,22 +1006,47 @@ function kind( k, csn, node ) {
853
1006
  else if (k === 'extend')
854
1007
  csn.kind = k;
855
1008
  }
856
- else {
857
- if (![
858
- 'element', 'key', 'param', 'enum', 'select', '$join',
859
- '$tableAlias', 'annotation', 'mixin',
860
- ].includes(k))
861
- csn.kind = k;
862
- addOrigin( csn, node );
1009
+ else if (k === 'action' && node._main && universalCsn && node.$inferred) {
1010
+ // Universal CSN: do not mention kind: 'action' on expanded action
863
1011
  }
1012
+ else if (![
1013
+ 'element', 'key', 'param', 'enum', 'select', '$join',
1014
+ '$tableAlias', 'annotation', 'mixin',
1015
+ ].includes(k)) {
1016
+ csn.kind = k;
1017
+ }
1018
+ const generated = universalCsn && inferredAsGenerated[node.$inferred];
1019
+ if (typeof generated === 'string')
1020
+ csn.$generated = generated;
864
1021
  }
865
1022
 
866
1023
  function type( node, csn, xsn ) {
867
- if (universalCsn && node.$inferred && xsn._origin)
1024
+ if (!universalCsn)
1025
+ return artifactRef( node, !node.$extra );
1026
+ if (node.$inferred)
1027
+ return undefined;
1028
+ if (xsn._origin) {
1029
+ if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1030
+ csn.$origin = definition( xsn._origin );
1031
+ return undefined;
1032
+ }
1033
+ }
1034
+ else if ( xsn.targetAspect && xsn.target ) {
1035
+ // type moved to $origin: { type: … }
868
1036
  return undefined;
1037
+ }
869
1038
  return artifactRef( node, !node.$extra );
870
1039
  }
871
1040
 
1041
+ function cardinality( node, csn, xsn ) {
1042
+ if (!universalCsn)
1043
+ return standard( node );
1044
+ // cardinality might be moved to $origin: { cardinality: … }
1045
+ if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
1046
+ return undefined;
1047
+ return standard( node );
1048
+ }
1049
+
872
1050
  function artifactRef( node, terse ) {
873
1051
  // When called as transformer function, a CSN node is provided as argument
874
1052
  // for `terse`, i.e. it is usually truthy, except for FROM
@@ -997,7 +1175,10 @@ function value( node ) {
997
1175
 
998
1176
  function enumValue( v, csn, node ) {
999
1177
  // Enums can have values but if enums are extended, their kind is 'element',
1000
- // so we check whether the node is inside an extension.
1178
+ // so we check whether the node is inside an extension. (TODO: still?)
1179
+ if (universalCsn && v.$inferred)
1180
+ return;
1181
+ // (with gensrc, the symbol itself would not make it into the CSN)
1001
1182
  if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
1002
1183
  Object.assign( csn, expression( v, true ) );
1003
1184
  }
@@ -1185,7 +1366,7 @@ function columns( xsnColumns, csn, xsn ) {
1185
1366
  addElementAsColumn( col, csnColumns );
1186
1367
  }
1187
1368
  }
1188
- else { // null = use elements
1369
+ else { // null = use elements - TODO: still used by A2J? -> remove
1189
1370
  for (const name in xsn.elements)
1190
1371
  addElementAsColumn( xsn.elements[name], csnColumns );
1191
1372
  }
@@ -162,6 +162,17 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
162
162
  if (rulespec.$frontend)
163
163
  ast.$frontend = rulespec.$frontend;
164
164
 
165
+ // Warn about unused doc-comments.
166
+ // Do not warn if docComments are explicitly disabled.
167
+ if (options.docComment !== false) {
168
+ for (const token of tokenStream.tokens) {
169
+ if (token.channel === antlr4.Token.HIDDEN_CHANNEL && token.type === parser.constructor.DocComment && !token.isUsed) {
170
+ messageFunctions.info('syntax-ignoring-doc-comment', parser.multiLineTokenLocation(token), {},
171
+ "Ignoring doc-comment as it does not belong to any artifact");
172
+ }
173
+ }
174
+ }
175
+
165
176
  // TODO: clarify with LSP colleagues: still necessary?
166
177
  if (parser.messages) {
167
178
  Object.defineProperty( ast, 'messages',
@@ -114,9 +114,10 @@ function sync( recognizer ) {
114
114
  }
115
115
  return;
116
116
  }
117
- // TODO: expected token is identifier, current is KEYWORD
118
117
 
119
118
  if (nextTokens.contains(antlr4.Token.EPSILON)) {
119
+ // when exiting a (innermost) rule, remember the state to make
120
+ // getExpectedTokensForMessage() calculate the full "expected set"
120
121
  if (recognizer.$nextTokensToken !== token) {
121
122
  // console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken && recognizer.$nextTokensToken.type)
122
123
  recognizer.$nextTokensToken = token;
@@ -126,13 +127,29 @@ function sync( recognizer ) {
126
127
  return;
127
128
  }
128
129
 
130
+ // Expected token is identifier, current is (reserved) KEYWORD:
131
+ // TODO: do not use this if "close enough" (1 char diff) to a keyword in nextTokens
132
+ //
133
+ // NOTE: it is important to do this only if EPSILON is not in `nextTokens`,
134
+ // which means that we cannot bring the better special syntax-fragile-ident
135
+ // in all cases. Reason: high performance impact of the alternative,
136
+ // i.e. calling method Parser#isExpectedToken() = invoking the ATN
137
+ // interpreter to see behind EPSILON.
138
+ const identType = recognizer.constructor.Identifier;
139
+ if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
140
+ recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text },
141
+ '$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
142
+ token.type = identType; // make next ANTLR decision assume identifier
143
+ return;
144
+ }
145
+
129
146
  if (recognizer._ctx._sync === 'nop')
130
147
  return;
131
148
  switch (s.stateType) {
132
- case ATNState.BLOCK_START:
133
- case ATNState.STAR_BLOCK_START:
134
- case ATNState.PLUS_BLOCK_START:
135
- case ATNState.STAR_LOOP_ENTRY:
149
+ case ATNState.BLOCK_START: // 3
150
+ case ATNState.STAR_BLOCK_START: // 5
151
+ case ATNState.PLUS_BLOCK_START: // 4
152
+ case ATNState.STAR_LOOP_ENTRY: // 10
136
153
  // report error and recover if possible
137
154
  if ( token.text !== '}' && // do not just delete a '}'
138
155
  this.singleTokenDeletion(recognizer) !== null) { // also calls reportUnwantedToken
@@ -145,8 +162,8 @@ function sync( recognizer ) {
145
162
  }
146
163
  throw new InputMismatchException(recognizer);
147
164
 
148
- case ATNState.PLUS_LOOP_BACK:
149
- case ATNState.STAR_LOOP_BACK: {
165
+ case ATNState.PLUS_LOOP_BACK: // 11
166
+ case ATNState.STAR_LOOP_BACK: { // 9
150
167
  // TODO: do not delete a '}'
151
168
  this.reportUnwantedToken(recognizer);
152
169
  const expecting = new IntervalSet.IntervalSet();
@@ -425,7 +442,8 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
425
442
  }
426
443
  else if (offendingToken && recognizer.$nextTokensContext &&
427
444
  offendingToken === recognizer.$nextTokensToken) {
428
- // We have a state (via sync()) with more "expecting" for the same token
445
+ // Before exiting a rule, we had a state (via sync()) with a bigger
446
+ // "expecting set" for the same token
429
447
  ll1._LOOK( atn.states[recognizer.$nextTokensState], null,
430
448
  predictionContext( atn, recognizer.$nextTokensContext ),
431
449
  expected, lookBusy, calledRules, true, true );