@sap/cds-compiler 4.9.6 → 5.1.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 (95) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/bin/cds_remove_invalid_whitespace.js +2 -1
  3. package/bin/cdsc.js +49 -19
  4. package/bin/cdshi.js +3 -1
  5. package/doc/CHANGELOG_BETA.md +7 -0
  6. package/lib/api/main.js +16 -19
  7. package/lib/api/options.js +5 -14
  8. package/lib/api/trace.js +0 -1
  9. package/lib/base/builtins.js +1 -0
  10. package/lib/base/location.js +4 -1
  11. package/lib/base/message-registry.js +43 -29
  12. package/lib/base/messages.js +23 -26
  13. package/lib/base/meta.js +10 -0
  14. package/lib/base/model.js +0 -2
  15. package/lib/base/node-helpers.js +0 -1
  16. package/lib/base/optionProcessorHelper.js +11 -0
  17. package/lib/checks/dbFeatureFlags.js +5 -0
  18. package/lib/checks/enricher.js +1 -5
  19. package/lib/checks/structuredAnnoExpressions.js +30 -0
  20. package/lib/checks/validator.js +8 -0
  21. package/lib/compiler/assert-consistency.js +5 -1
  22. package/lib/compiler/base.js +1 -1
  23. package/lib/compiler/builtins.js +18 -2
  24. package/lib/compiler/checks.js +2 -5
  25. package/lib/compiler/define.js +8 -8
  26. package/lib/compiler/extend.js +108 -37
  27. package/lib/compiler/generate.js +1 -1
  28. package/lib/compiler/index.js +27 -10
  29. package/lib/compiler/lsp-api.js +501 -2
  30. package/lib/compiler/populate.js +60 -13
  31. package/lib/compiler/propagator.js +10 -8
  32. package/lib/compiler/resolve.js +117 -94
  33. package/lib/compiler/shared.js +114 -32
  34. package/lib/compiler/tweak-assocs.js +31 -21
  35. package/lib/compiler/utils.js +2 -1
  36. package/lib/compiler/xsn-model.js +4 -0
  37. package/lib/edm/annotations/genericTranslation.js +69 -35
  38. package/lib/edm/csn2edm.js +16 -4
  39. package/lib/edm/edm.js +10 -3
  40. package/lib/edm/edmAnnoPreprocessor.js +1 -2
  41. package/lib/edm/edmPreprocessor.js +8 -10
  42. package/lib/gen/Dictionary.json +66 -2
  43. package/lib/gen/language.checksum +1 -1
  44. package/lib/gen/language.interp +2 -1
  45. package/lib/gen/languageParser.js +4995 -4817
  46. package/lib/json/csnVersion.js +1 -1
  47. package/lib/json/from-csn.js +4 -7
  48. package/lib/json/to-csn.js +25 -12
  49. package/lib/language/antlrParser.js +2 -2
  50. package/lib/language/errorStrategy.js +0 -1
  51. package/lib/language/genericAntlrParser.js +35 -12
  52. package/lib/language/multiLineStringParser.js +3 -2
  53. package/lib/language/textUtils.js +1 -0
  54. package/lib/main.d.ts +28 -9
  55. package/lib/main.js +9 -9
  56. package/lib/model/cloneCsn.js +1 -0
  57. package/lib/model/csnRefs.js +22 -5
  58. package/lib/model/csnUtils.js +0 -14
  59. package/lib/model/revealInternalProperties.js +1 -2
  60. package/lib/modelCompare/compare.js +13 -11
  61. package/lib/optionProcessor.js +30 -9
  62. package/lib/render/manageConstraints.js +1 -1
  63. package/lib/render/toCdl.js +44 -14
  64. package/lib/render/toHdbcds.js +1 -2
  65. package/lib/render/toSql.js +45 -8
  66. package/lib/render/utils/common.js +12 -9
  67. package/lib/render/utils/stringEscapes.js +1 -0
  68. package/lib/transform/db/applyTransformations.js +13 -8
  69. package/lib/transform/db/associations.js +62 -54
  70. package/lib/transform/db/backlinks.js +20 -5
  71. package/lib/transform/db/expansion.js +1 -6
  72. package/lib/transform/db/flattening.js +86 -109
  73. package/lib/transform/db/killAnnotations.js +3 -0
  74. package/lib/transform/db/processSqlServices.js +63 -0
  75. package/lib/transform/db/temporal.js +3 -4
  76. package/lib/transform/db/views.js +0 -1
  77. package/lib/transform/draft/odata.js +56 -3
  78. package/lib/transform/effective/annotations.js +3 -2
  79. package/lib/transform/effective/flattening.js +135 -0
  80. package/lib/transform/effective/main.js +6 -4
  81. package/lib/transform/effective/types.js +13 -9
  82. package/lib/transform/forOdata.js +0 -2
  83. package/lib/transform/forRelationalDB.js +9 -19
  84. package/lib/transform/localized.js +7 -8
  85. package/lib/transform/odata/flattening.js +39 -31
  86. package/lib/transform/odata/typesExposure.js +5 -17
  87. package/lib/transform/transformUtils.js +1 -1
  88. package/lib/transform/translateAssocsToJoins.js +0 -1
  89. package/lib/utils/file.js +87 -8
  90. package/lib/utils/moduleResolve.js +59 -8
  91. package/lib/utils/term.js +3 -2
  92. package/package.json +7 -3
  93. package/share/messages/message-explanations.json +2 -0
  94. package/share/messages/type-unexpected-foreign-keys.md +52 -0
  95. package/share/messages/type-unexpected-on-condition.md +52 -0
@@ -41,7 +41,6 @@ const { createDict } = require('../utils/objectUtils');
41
41
  * - true = can always be downgraded, we do not really care
42
42
  * - [‹module›, …] = can be downgraded in compiler function ‹module›
43
43
  * - 'deprecated' = severity can only be changed with deprecated.downgradableErrors
44
- * - 'v4' = currently like true, but should be 'deprecated' in v5
45
44
  *
46
45
  * @type {Object<string, MessageConfig>}
47
46
  */
@@ -61,13 +60,13 @@ const centralMessages = {
61
60
  'ext-undefined-art': { severity: 'Warning' }, // for annotate statement (for CDL path root)
62
61
  'ext-undefined-def': { severity: 'Warning' }, // for annotate statement (for CSN or CDL path cont)
63
62
  'ext-undefined-element': { severity: 'Warning' },
63
+ 'ext-undefined-key': { severity: 'Warning' },
64
64
  'ext-undefined-param': { severity: 'Warning' },
65
65
  'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
66
66
  'anno-unexpected-localized-skip': { severity: 'Error', configurableFor: true },
67
- 'anno-unexpected-mixin': { severity: 'Warning', errorFor: [ 'v5' ] },
68
67
 
69
68
  'name-invalid-dollar-alias': { severity: 'Error', configurableFor: true },
70
- 'name-deprecated-$self': { severity: 'Warning', errorFor: [ 'v5' ], configurableFor: true },
69
+ 'name-deprecated-$self': { severity: 'Error', configurableFor: true },
71
70
 
72
71
  'type-invalid-items': { severity: 'Error' }, // not supported yet
73
72
  'assoc-as-type': { severity: 'Error' }, // TODO: allow more, but not all
@@ -86,7 +85,7 @@ const centralMessages = {
86
85
  'empty-type': { severity: 'Info' }, // only still an error in old transformers
87
86
 
88
87
  'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
89
- 'ref-deprecated-self-element': { severity: 'Warning', configurableFor: true, errorFor: [ 'v5' ] },
88
+ 'ref-deprecated-self-element': { severity: 'Error', configurableFor: true },
90
89
  'ref-invalid-type': { severity: 'Error' },
91
90
  'ref-unexpected-self': { severity: 'Error' },
92
91
  'ref-invalid-include': { severity: 'Error' },
@@ -98,6 +97,7 @@ const centralMessages = {
98
97
  'ref-expecting-foreign-key': { severity: 'Error' },
99
98
  'ref-invalid-source': { severity: 'Error' },
100
99
  'ref-invalid-target': { severity: 'Error' },
100
+ 'ref-missing-self-counterpart': { severity: 'Error', configurableFor: true },
101
101
  'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
102
102
 
103
103
  'extend-repeated-intralayer': { severity: 'Warning' },
@@ -152,7 +152,7 @@ const centralMessages = {
152
152
  // 'syntax-duplicate-annotate' came late with v3 - make it configurable as
153
153
  // fallback, but then parse.cdl is not supposed to work correctly (it can
154
154
  // then either issue an error or produce a CSN missing some annotations):
155
- 'syntax-duplicate-annotate': { severity: 'Error', configurableFor: 'deprecated' },
155
+ 'syntax-duplicate-annotate': { severity: 'Error' },
156
156
  'syntax-duplicate-clause': { severity: 'Error', configurableFor: true },
157
157
  'syntax-duplicate-equal-clause': { severity: 'Warning' },
158
158
  'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
@@ -162,36 +162,38 @@ const centralMessages = {
162
162
  'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
163
163
  'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'deprecated' },
164
164
  'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
165
- 'syntax-ignoring-anno': { severity: 'Warning', errorFor: [ 'v5' ] },
165
+ 'syntax-invalid-space': { severity: 'Error', configurableFor: 'test' },
166
+ 'syntax-expecting-space': { severity: 'Error' },
167
+ 'syntax-unexpected-anno': { severity: 'Error' },
166
168
 
167
169
  'type-unsupported-precision-change': { severity: 'Error' },
168
170
  'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
169
- 'type-missing-enum-value': { severity: 'Warning', errorFor: [ 'v5' ] },
171
+ 'type-missing-enum-value': { severity: 'Error', configurableFor: 'test' },
170
172
 
171
173
  'def-missing-element': { severity: 'Error' },
172
- 'def-expected-structured': { severity: 'Warning', configurableFor: true, errorFor: [ 'v5' ] },
174
+ 'def-expected-structured': { severity: 'Error', configurableFor: true },
173
175
  'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
174
176
 
175
177
  'def-invalid-key-cardinality': { severity: 'Error' },
176
178
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
177
- 'odata-spec-violation-array': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
179
+ 'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
178
180
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
179
- 'odata-spec-violation-assoc': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
181
+ 'odata-spec-violation-assoc': { severity: 'Warning' }, // more than 30 chars
180
182
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
181
183
  'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
182
184
  'odata-spec-violation-id': { severity: 'Error', configurableFor: true },
183
- 'odata-spec-violation-namespace': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
185
+ 'odata-spec-violation-namespace': { severity: 'Warning' }, // more than 30 chars
184
186
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
185
- 'odata-spec-violation-param': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
187
+ 'odata-spec-violation-param': { severity: 'Warning' }, // more than 30 chars
186
188
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
187
- 'odata-spec-violation-returns': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
189
+ 'odata-spec-violation-returns': { severity: 'Warning' }, // more than 30 chars
188
190
  'odata-spec-violation-type': { severity: 'Error', configurableFor: true },
189
- 'odata-spec-violation-type-unknown': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
190
- 'odata-spec-violation-no-key': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
191
+ 'odata-spec-violation-type-unknown': { severity: 'Error', configurableFor: true },
192
+ 'odata-spec-violation-no-key': { severity: 'Error', configurableFor: true },
191
193
  'odata-spec-violation-key-array': { severity: 'Error', configurableFor: true }, // more than 30 chars
192
194
  'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
193
- 'odata-spec-violation-key-type': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
194
- 'odata-spec-violation-property-name': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
195
+ 'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
196
+ 'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
195
197
  'odata-anno-preproc': { severity: 'Warning' },
196
198
  'odata-anno-dict': { severity: 'Warning' },
197
199
  'odata-anno-vocref': { severity: 'Warning' },
@@ -290,7 +292,6 @@ const centralMessageTexts = {
290
292
  'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)',
291
293
  'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
292
294
  'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
293
- 'anno-unexpected-mixin': 'Unexpected annotation on mixin definition',
294
295
 
295
296
  'anno-unexpected-localized-skip': {
296
297
  std: 'Compiler generated entity $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
@@ -351,7 +352,7 @@ const centralMessageTexts = {
351
352
  'syntax-ignoring-decimal': {
352
353
  std: 'Ignoring decimal places, because an integer was expected'
353
354
  },
354
- 'syntax-ignoring-anno': {
355
+ 'syntax-unexpected-anno': {
355
356
  std: 'Annotations can\'t be used in a column with $(CODE)',
356
357
  doc: 'Doc comments can\'t be used in a column with $(CODE)',
357
358
  },
@@ -435,7 +436,6 @@ const centralMessageTexts = {
435
436
  service: 'Annotations can\'t be defined inside services',
436
437
  context: 'Annotations can\'t be defined inside contexts',
437
438
  },
438
- // 'syntax-unexpected-space' (TODO: also use for :param, #variant, @(…) and @Begin),
439
439
  // 'syntax-unexpected-alias' (is 'syntax-unexpected-property' in CSN)
440
440
  'syntax-unsupported-param': {
441
441
  std: 'Parameter not supported', // unused
@@ -557,6 +557,10 @@ const centralMessageTexts = {
557
557
  // Messages for erroneous references -----------------------------------------
558
558
  // location at erroneous reference (if possible)
559
559
  'ref-deprecated-orderby': 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected',
560
+ 'ref-missing-self-counterpart' : {
561
+ std: 'Expected to find a matching element in $self-comparison for foreign key $(PROP) of association $(NAME)',
562
+ unmanaged: 'Expected to find a matching element in $self-comparison for $(PROP) of association $(NAME)'
563
+ },
560
564
  'ref-unexpected-self': {
561
565
  std: 'Unexpected $(ID) reference; is valid only in ON-conditions of unmanaged associations',
562
566
  on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
@@ -642,6 +646,8 @@ const centralMessageTexts = {
642
646
  std: 'Unexpected reference to association $(NAME)', // "std" currently unused
643
647
  unmanaged: 'Unexpected reference to an unmanaged association',
644
648
  'self-unmanaged': 'Unexpected column reference starting with $(ALIAS) to an unmanaged association',
649
+ 'with-filter': 'Unexpected reference to an association with filter',
650
+ 'self-with-filter': 'Unexpected column reference starting with $(ALIAS) to an association with filter',
645
651
  self: 'A reference to an unmanaged association is only valid when compared via $(CODE)',
646
652
  expr: 'Associations can\'t be used as values in expressions',
647
653
  'expr-comp': 'Compositions can\'t be used as values in expressions',
@@ -683,6 +689,10 @@ const centralMessageTexts = {
683
689
  hdbcds:'Type $(TYPE) is not supported in HDBCDS',
684
690
  odata: 'Type $(TYPE) is not supported for OData'
685
691
  },
692
+ 'ref-unexpected-var': {
693
+ std: 'Variable $(NAME) can\'t be used here',
694
+ 'annotation': 'Variable $(NAME) can only be used in annotation values',
695
+ },
686
696
 
687
697
  // TODO: Better text ?
688
698
  'rewrite-not-supported': 'The ON-condition is not rewritten here - provide an explicit ON-condition',
@@ -746,6 +756,7 @@ const centralMessageTexts = {
746
756
  returns: 'Return value of $(ART) has no element $(NAME)',
747
757
  'enum-returns': 'Return value of $(ART) has no enum $(NAME)',
748
758
  },
759
+ 'ext-undefined-key': 'Foreign key $(NAME) has not been found',
749
760
  'ext-undefined-action': {
750
761
  std: 'Action $(ART) has not been found',
751
762
  action: 'Artifact $(ART) has no action $(NAME)'
@@ -837,12 +848,6 @@ const centralMessageTexts = {
837
848
  'include-elements': 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
838
849
  'include-actions': 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
839
850
  },
840
- // TODO: Remove in v5, use duplicate-definition (or another ID?)
841
- 'ref-duplicate-include-member': {
842
- std: 'Duplicate member $(NAME) through multiple includes $(SORTED_ARTS)',
843
- elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
844
- actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
845
- },
846
851
  'ref-invalid-element': {
847
852
  std: 'Invalid element reference',
848
853
  $tableAlias: 'Can\'t refer to source elements of table alias $(ID)',
@@ -859,6 +864,7 @@ const centralMessageTexts = {
859
864
  'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
860
865
  },
861
866
 
867
+ 'ref-expecting-$self': 'Use $(NEWCODE) instead of $(CODE) here or remove $(CODE) altogether if possible; the compiler has rewritten it to $(NEWCODE) in CSN',
862
868
  'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
863
869
  'ref-expecting-const': 'A constant expression or variable is expected here',
864
870
  'ref-expecting-foreign-key': 'Expecting foreign key access after managed association $(NAME) in filter expression of $(ID), but found $(ALIAS)',
@@ -908,9 +914,7 @@ const centralMessageTexts = {
908
914
  'indirect': 'Type property $(PROP) can only be extended if directly provided at the definition',
909
915
  'new-prop': 'Type property $(PROP) can only be extended, not added',
910
916
  string: 'Only numerical properties can be extended, but found string for $(PROP)',
911
- // eslint-disable-next-line max-len
912
917
  number: 'Value of type property $(PROP) must be $(NUMBER) or higher, it can\'t be smaller than originally provided',
913
- // eslint-disable-next-line max-len
914
918
  scale: 'With the extension for type property $(OTHERPROP), the value of $(PROP) must be $(NUMBER) or higher',
915
919
  },
916
920
  'ext-missing-type-property': 'Type extension with property $(PROP) must also have property $(OTHERPROP) because $(ART) has both',
@@ -966,6 +970,15 @@ const centralMessageTexts = {
966
970
  'managed': 'Ignoring managed association $(NAME) that is published in a UNION',
967
971
  'std': 'Ignoring association $(NAME) that is published in a UNION'
968
972
  },
973
+ 'query-missing-element': {
974
+ std: 'Element $(ID) is missing in specified elements',
975
+ enum: 'Enum $(ID) is missing in specified enum values',
976
+ foreignKeys: 'Foreign key $(ID) is missing in specified foreign keys',
977
+ },
978
+ 'query-unspecified-element': {
979
+ std: 'Element $(ID) does not result from the query',
980
+ foreignKeys: 'Foreign key $(ID) does not result from the query',
981
+ },
969
982
 
970
983
  'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
971
984
 
@@ -1193,7 +1206,8 @@ const centralMessageTexts = {
1193
1206
  'notaneelement': 'EDM Parameter path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a type entity',
1194
1207
  'notrendered': 'EDM Path step $(COUNT) of $(ELEMREF) in $(ANNO) refers to an unrendered property in the OData API',
1195
1208
  'magic': 'Unexpected magic variable $(ELEMREF) in $(ANNO)',
1196
- 'bparam_v2': 'Unexpected explicit binding parameter path $(ELEMREF) for OData $(VERSION) in $(ANNO)',
1209
+ 'bparam_v2_expl': 'Unexpected explicit binding parameter path $(ELEMREF) for OData $(VERSION) in $(ANNO)',
1210
+ 'bparam_v2_impl': 'Unexpected implicit binding parameter path $(ELEMREF) for OData $(VERSION) in $(ANNO)',
1197
1211
  },
1198
1212
  // -----------------------------------------------------------------------------------
1199
1213
  // OData Message section ends here, no messages below this line
@@ -13,6 +13,7 @@ const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
13
13
  const { CompilerAssertion } = require('./error');
14
14
  const { getArtifactName } = require('../compiler/base');
15
15
  const { cdlNewLineRegEx } = require('../language/textUtils');
16
+ const meta = require('./meta');
16
17
 
17
18
  const fs = require('fs');
18
19
  const path = require('path');
@@ -73,8 +74,8 @@ function isDowngradable( messageId, moduleName, options ) {
73
74
  return false;
74
75
  if (msg.severity !== 'Error')
75
76
  return true;
76
- // v5 messages are downgradable (except if errorFor also contains the current module).
77
- if (msg.errorFor && msg.errorFor.includes('v5'))
77
+ // v6 messages are downgradable (except if errorFor also contains the current module).
78
+ if (msg.errorFor && msg.errorFor.includes('v6'))
78
79
  return true;
79
80
  const { configurableFor } = msg;
80
81
  return (Array.isArray( configurableFor ))
@@ -91,9 +92,11 @@ function isDowngradable( messageId, moduleName, options ) {
91
92
  function severityChangeMarker(msg, config) {
92
93
  const severity = msg.severity || 'Error';
93
94
  if (config.moduleForMarker) {
94
- if (severity === 'Error' && isDowngradable(msg.messageId, config.moduleForMarker, {}))
95
+ if (severity === 'Error' &&
96
+ isDowngradable( msg.messageId, config.moduleForMarker,
97
+ { deprecated: { downgradableErrors: true } }))
95
98
  return '‹↓›';
96
- if (msg.messageId && centralMessages[msg.messageId]?.errorFor?.includes('v5'))
99
+ if (msg.messageId && centralMessages[msg.messageId]?.errorFor?.includes('v6'))
97
100
  return '‹↑›';
98
101
  }
99
102
  return '';
@@ -107,12 +110,16 @@ function severityChangeMarker(msg, config) {
107
110
  class CompilationError extends Error {
108
111
  /**
109
112
  * @param {CompileMessage[]} messages
110
- * @param {boolean} [dontSerializeMessages] If true, compiler messages are NOT part of the errors message text.
111
113
  * @param {XSN.Model} [model] the XSN model, only to be set with options.attachValidNames
112
114
  */
113
- constructor(messages, model, dontSerializeMessages) {
114
- const msg = !dontSerializeMessages && messages?.map( m => m.toString() ).join('\n') || '';
115
- super( `CDS compilation failed\n${msg}` );
115
+ constructor(messages, model) {
116
+ // Because test frameworks such as mocha and jest to not call `toString()` on
117
+ // an unhandled CompilationError and instead use `e.stack` directly, there is
118
+ // no proper message about _what_ the root cause of the exception was.
119
+ // To mitigate that, we serialize the first error in the message as well.
120
+ const firstError = messages.find( m => m.severity === 'Error' )?.toString() || '';
121
+ super( `CDS compilation failed (@sap/cds-compiler v${ meta.version() })\n${firstError}` );
122
+
116
123
  /** @since v4.0.0 */
117
124
  this.code = 'ERR_CDS_COMPILATION_FAILURE';
118
125
  this.messages = [ ...messages ].sort(compareMessageSeverityAware);
@@ -240,7 +247,7 @@ function reclassifiedSeverity( msg, options, moduleName ) {
240
247
  if (errorFor.includes(moduleName))
241
248
  return 'Error';
242
249
 
243
- if (errorFor.includes('v5') && isBetaEnabled(options, 'v5preview')) {
250
+ if (errorFor.includes('v6') && isBetaEnabled(options, 'v6preview')) {
244
251
  severity = 'Error';
245
252
  if (!isDowngradable(msg.messageId, moduleName, options))
246
253
  return severity;
@@ -555,8 +562,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
555
562
 
556
563
  function throwWithError() {
557
564
  if (hasNewError) {
558
- const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
559
- throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
565
+ throw new CompilationError(messages, options.attachValidNames && model);
560
566
  }
561
567
  }
562
568
 
@@ -573,8 +579,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
573
579
  return;
574
580
  const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
575
581
  if (hasError( messages, moduleName, options )) {
576
- const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
577
- throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
582
+ throw new CompilationError(messages, options.attachValidNames && model);
578
583
  }
579
584
  }
580
585
 
@@ -1018,10 +1023,6 @@ function replaceInString( text, params ) {
1018
1023
  * @param {boolean} [config.noMessageId]
1019
1024
  * If true, will _not_ show the message ID (+ explanation hint) in the output.
1020
1025
  *
1021
- * @param {boolean} [config.idInBrackets]
1022
- * If true, the message ID (if there is one and noMessageId is falsey) will be put in brackets.
1023
- * This will be the default in cds-compiler v5.
1024
- *
1025
1026
  * @param {boolean} [config.noHome]
1026
1027
  * If true, will _not_ show message's semantic location.
1027
1028
  *
@@ -1049,11 +1050,7 @@ function messageString( err, config ) {
1049
1050
  const downgradable = severityChangeMarker(err, config);
1050
1051
  // even with noHome, print err.home if the location is weak
1051
1052
  const home = !err.home || config.noHome && err.$location?.endLine ? '' : ` (in ${ err.home })`;
1052
-
1053
- let msgId = ''; // TODO(v5): Use brackets only
1054
- if (err.messageId && !config.noMessageId)
1055
- msgId = (config.idInBrackets) ? `[${err.messageId}]` : ` ${err.messageId}`;
1056
-
1053
+ const msgId = (err.messageId && !config.noMessageId) ? `[${err.messageId}]` : '';
1057
1054
  return `${ location }${ severity }${ downgradable }${ msgId }: ${ err.message }${ home }`;
1058
1055
  }
1059
1056
 
@@ -1065,7 +1062,7 @@ function messageString( err, config ) {
1065
1062
  * @returns {string} can be used to uniquely identify a message
1066
1063
  */
1067
1064
  function messageHash( msg ) {
1068
- // parser messages do not provide semantic location, therefore we need to use the file location
1065
+ // parser messages do not provide semantic location, therefore$ we need to use the file location
1069
1066
  if (!msg.home)
1070
1067
  return messageString(msg);
1071
1068
  const copy = { ...msg };
@@ -1118,7 +1115,7 @@ function messageHash( msg ) {
1118
1115
  * If true/'always', ANSI escape codes will be used for coloring the severity. If false/'never',
1119
1116
  * no coloring will be used. If 'auto', we will decide based on certain factors such
1120
1117
  * as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
1121
- * unset.
1118
+ * unset or whether `FORCE_COLOR` is set.
1122
1119
  *
1123
1120
  * @returns {string}
1124
1121
  */
@@ -1285,7 +1282,7 @@ function _messageContext( err, config ) {
1285
1282
  * @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
1286
1283
  * coloring will be used. If 'auto', we will decide based on certain factors such
1287
1284
  * as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
1288
- * unset.
1285
+ * unset or `FORCE_COLOR` is set.
1289
1286
  * @returns {string}
1290
1287
  *
1291
1288
  * @deprecated Use `messageStringMultiline()` with `config.sourceMap` and `config.sourceLineMap` instead!
@@ -1353,7 +1350,7 @@ function compareMessageSeverityAware( a, b ) {
1353
1350
  /**
1354
1351
  * Return sort-relevant part of semantic location (after the ':').
1355
1352
  * Messages without semantic locations are considered smaller (for syntax errors)
1356
- * and (currently - should not happen in v5) larger for other messages.
1353
+ * and (currently - should not happen in v6) larger for other messages.
1357
1354
  *
1358
1355
  * @param {CompileMessage} msg
1359
1356
  */
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ // Metadata, e.g. version.
4
+
5
+ /** The compiler version (taken from package.json) */
6
+ function version() {
7
+ return require('../../package.json').version;
8
+ }
9
+
10
+ module.exports = { version };
package/lib/base/model.js CHANGED
@@ -31,11 +31,9 @@ const availableBetaFlags = {
31
31
  enableUniversalCsn: true,
32
32
  odataTerms: true,
33
33
  optionalActionFunctionParameters: true, // not supported by runtime, yet.
34
- annotateForeignKeys: true,
35
34
  effectiveCsn: true,
36
35
  tenantVariable: true,
37
36
  calcAssoc: true,
38
- v5preview: true,
39
37
  temporalRawProjection: true,
40
38
  // disabled by --beta-mode
41
39
  nestedServices: false,
@@ -28,7 +28,6 @@ class PromiseAllError extends Error {
28
28
  }
29
29
  }
30
30
 
31
-
32
31
  module.exports = {
33
32
  promiseAllDoNotRejectImmediately,
34
33
  };
@@ -42,6 +42,7 @@ function createOptionProcessor() {
42
42
  },
43
43
  help,
44
44
  processCmdLine,
45
+ makePositionalArgumentsOptional,
45
46
  };
46
47
  return optionProcessor;
47
48
 
@@ -291,6 +292,16 @@ function createOptionProcessor() {
291
292
  };
292
293
  }
293
294
 
295
+ function makePositionalArgumentsOptional() {
296
+ for (const arg of optionProcessor.positionalArguments || [])
297
+ arg.required = false;
298
+
299
+ for (const cmd in optionProcessor.commands) {
300
+ for (const arg of optionProcessor.commands[cmd].positionalArguments || [])
301
+ arg.required = false;
302
+ }
303
+ }
304
+
294
305
  // API: Let the option processor digest a command line 'argv'
295
306
  // The expectation is to get a commandline like this:
296
307
  // $ node cdsc.js -x 1 --foo toXyz -y --bar-wiz bla arg1 arg2
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { setProp } = require('../base/model');
4
4
  const { featureFlags } = require('../transform/db/featureFlags');
5
+ const { isSqlService } = require('../transform/db/processSqlServices');
5
6
 
6
7
  /**
7
8
  *
@@ -25,4 +26,8 @@ module.exports = {
25
26
  value: setFeatureFlag('$calculatedElements'),
26
27
  expand: setFeatureFlag('$expandInline'),
27
28
  inline: setFeatureFlag('$expandInline'),
29
+ kind: function setFeatureFlagForSqlService( artifact ) {
30
+ if (isSqlService(artifact))
31
+ setFeatureFlag( '$sqlService' ).call(this);
32
+ },
28
33
  };
@@ -49,7 +49,6 @@ function enrichCsn( csn, options ) {
49
49
  dictionary( csn, 'definitions', csn.definitions );
50
50
  return { csn, cleanup };
51
51
 
52
- // eslint-disable-next-line jsdoc/require-jsdoc
53
52
  function standard( parent, prop, node ) {
54
53
  if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ))
55
54
  return;
@@ -69,7 +68,7 @@ function enrichCsn( csn, options ) {
69
68
  }
70
69
  csnPath.pop();
71
70
  }
72
- // eslint-disable-next-line jsdoc/require-jsdoc
71
+
73
72
  function dictionary( node, prop, dict ) {
74
73
  setProp(node, '$path', [ ...csnPath ]);
75
74
  cleanupCallbacks.push(() => delete node.$path);
@@ -115,7 +114,6 @@ function enrichCsn( csn, options ) {
115
114
  }
116
115
  }
117
116
 
118
- // eslint-disable-next-line jsdoc/require-jsdoc
119
117
  function columns( parent, prop, node ) {
120
118
  // Establish the link relationships
121
119
  parent[prop].forEach((column) => {
@@ -130,7 +128,6 @@ function enrichCsn( csn, options ) {
130
128
  standard(parent, prop, node);
131
129
  }
132
130
 
133
- // eslint-disable-next-line jsdoc/require-jsdoc
134
131
  function simpleRef( node, prop, ref ) {
135
132
  setProp(node, '$path', [ ...csnPath ]);
136
133
  cleanupCallbacks.push(() => delete node.$path);
@@ -158,7 +155,6 @@ function enrichCsn( csn, options ) {
158
155
  }
159
156
  }
160
157
 
161
- // eslint-disable-next-line jsdoc/require-jsdoc
162
158
  function pathRef( node, prop, path ) {
163
159
  const {
164
160
  links, art, scope, $env,
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { isBuiltinType } = require('../base/builtins');
4
+ const { transformExpression, applyTransformationsOnNonDictionary } = require('../model/csnUtils');
5
+ /**
6
+ *
7
+ * @param {object} member
8
+ */
9
+ function checkAnnotationExpression( member, _memberName, _prop, path ) {
10
+ Object.keys(member).filter(pn => pn[0] === '@').forEach((anno) => {
11
+ applyTransformationsOnNonDictionary(member, anno, {
12
+ xpr: (parent, prop, _xpr, xprPath) => {
13
+ transformExpression(parent, prop, {
14
+ ref: (elemref, __prop, ref, refPath) => {
15
+ const { art, scope } = this.csnUtils.inspectRef(refPath);
16
+ if (scope !== '$magic' && art) {
17
+ const ft = this.csnUtils.getFinalTypeInfo(art.type);
18
+ if (!isBuiltinType(ft?.type))
19
+ this.error('odata-anno-xpr-ref', refPath, { anno, elemref, '#': 'flatten_builtin_type' });
20
+ }
21
+ },
22
+ }, xprPath);
23
+ },
24
+ }, {}, path);
25
+ });
26
+ }
27
+
28
+ module.exports = {
29
+ checkAnnotationExpression,
30
+ };
@@ -15,6 +15,7 @@ const navigationIntoMany = require('./manyNavigations');
15
15
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
16
16
  const validateHasPersistedElements = require('./hasPersistedElements');
17
17
  const checkForHanaTypes = require('./checkForTypes');
18
+ const { checkAnnotationExpression } = require('./structuredAnnoExpressions');
18
19
  const checkForParams = require('./parameters');
19
20
  // forOdata
20
21
  const { validateDefaultValues } = require('./defaultValues');
@@ -210,6 +211,13 @@ function forRelationalDB( csn, that ) {
210
211
  duplicate messages due to the forEachMemberRecursively.
211
212
  TODO: check if this recursion can be factored out of the validator */
212
213
  forEachMember(artifact, checkUsedTypesForAnonymousAspectComposition.bind(that));
214
+ },
215
+ (artifact, artifactName) => {
216
+ if (that.options.transformation === 'effective') {
217
+ forEachMemberRecursively(artifact, checkAnnotationExpression.bind(that), [ 'definitions', artifactName ], false, {
218
+ skipArtifact: a => a.returns || (a.params && !a.query),
219
+ });
220
+ }
213
221
  }
214
222
  ),
215
223
  forRelationalDBQueryValidators.concat(commonQueryValidators),
@@ -208,6 +208,7 @@ function assertConsistency( model, stage ) {
208
208
  'elements', '$autoElement', '$uncheckedElements', '_origin', '_extensions',
209
209
  '$requireElementAccess', '_effectiveType', '$effectiveSeqNo', '_deps',
210
210
  '$calcDepElement', '$filtered', '$enclosed', '_parent',
211
+ 'deprecated', '$onlyInExprCtx',
211
212
  ],
212
213
  schema: {
213
214
  kind: { test: isString, enum: [ 'builtin' ] },
@@ -215,6 +216,8 @@ function assertConsistency( model, stage ) {
215
216
  $autoElement: { test: isString },
216
217
  $uncheckedElements: { test: isBoolean },
217
218
  $requireElementAccess: { test: isBoolean },
219
+ deprecated: { test: isBoolean },
220
+ $onlyInExprCtx: { test: TODO },
218
221
  // missing location for normal "elements"
219
222
  elements: { test: TODO },
220
223
  },
@@ -252,6 +255,7 @@ function assertConsistency( model, stage ) {
252
255
  elements: { kind: true, inherits: 'definitions', also: [ 0 ] }, // 0 for cyclic expansions
253
256
  // specified elements in query entities (TODO: introduce real "specified elements" instead):
254
257
  elements$: { kind: true, enumerable: false, test: TODO },
258
+ foreignKeys$: { kind: true, enumerable: false, test: TODO },
255
259
  enum$: { kind: true, enumerable: false, test: TODO },
256
260
  typeProps$: { kind: true, enumerable: false, test: TODO },
257
261
  // helper property for faster processing:
@@ -703,7 +707,7 @@ function assertConsistency( model, stage ) {
703
707
  'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
704
708
  'localized-entity', // `.texts` entity
705
709
  'localized-origin', // `.texts` entity
706
- 'nav', // only used for MASKED, TODO(v5): Remove
710
+ 'nav', // only used for MASKED, TODO(v6): Remove
707
711
  'none', // only used in ensureColumnName(): Used in object representing empty alias
708
712
  'query', // inferred query properties, e.g. `key`
709
713
  'rewrite', // on-conditions or FKeys are rewritten
@@ -48,7 +48,7 @@ const kindProperties = {
48
48
  normalized: 'action',
49
49
  dict: 'actions',
50
50
  }, // no extend params, only annotate
51
- key: { normalized: 'element' },
51
+ key: { normalized: 'element', dict: 'elements' }, // dict for annotate
52
52
  param: { elements: () => false, enum: () => false, dict: 'params' },
53
53
  source: { artifacts: true }, // TODO -> $source
54
54
  using: {},
@@ -178,14 +178,15 @@ const magicVariables = {
178
178
  // Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
179
179
  $autoElement: 'id',
180
180
  },
181
- $at: {
181
+ $at: { // $at is considered deprecated since cds-compiler v5
182
182
  elements: {
183
183
  from: {}, to: {},
184
184
  },
185
185
  // Require that elements are accessed, i.e. no $at, only $at.<element>.
186
186
  $requireElementAccess: true,
187
+ deprecated: true, // $at is deprecated; use $valid
187
188
  },
188
- $valid: { // dito
189
+ $valid: {
189
190
  elements: {
190
191
  from: {}, to: {},
191
192
  },
@@ -200,6 +201,15 @@ const magicVariables = {
200
201
  $uncheckedElements: true,
201
202
  $requireElementAccess: true,
202
203
  },
204
+ $draft: {
205
+ elements: {
206
+ IsActiveEntity: {},
207
+ },
208
+ // Require that elements are accessed, i.e. no $draft, only $draft.<element>.
209
+ $requireElementAccess: true,
210
+ // See reference semantics in shared.js
211
+ $onlyInExprCtx: [ 'annotation', 'annoRewrite' ],
212
+ },
203
213
  };
204
214
 
205
215
  // see lib/render/renderUtil.js for DB-specific magic vars, specified in CAP CDS via function
@@ -437,6 +447,10 @@ function initBuiltins( model ) {
437
447
  art.$uncheckedElements = magic.$uncheckedElements;
438
448
  if (magic.$requireElementAccess)
439
449
  art.$requireElementAccess = magic.$requireElementAccess;
450
+ if (magic.deprecated)
451
+ art.deprecated = magic.deprecated;
452
+ if (magic.$onlyInExprCtx)
453
+ art.$onlyInExprCtx = magic.$onlyInExprCtx;
440
454
 
441
455
  createMagicElements( art, magic.elements );
442
456
  if (options.variableReplacements?.[id])
@@ -461,6 +475,8 @@ function initBuiltins( model ) {
461
475
  // Propagate this property so that it is available for sub-elements.
462
476
  if (art.$uncheckedElements)
463
477
  magic.$uncheckedElements = art.$uncheckedElements;
478
+ if (art.$onlyInExprCtx)
479
+ magic.$onlyInExprCtx = art.$onlyInExprCtx;
464
480
  setProp( magic, '_parent', art );
465
481
  // setProp( magic, '_effectiveType', magic );
466
482
  if (elements[id] && typeof elements[id] === 'object')
@@ -54,9 +54,6 @@ function check( model ) {
54
54
  function checkEvent( def ) {
55
55
  // Ensure that events are structured. Up to compiler v4, we allowed non-structured events,
56
56
  // because when we introduced them, it was not fully specified what they are.
57
- // TODO(v5):
58
- // - Make this a (configurable) error; move to acceptTypeOrElement
59
- // - require type/elements to be set in parser
60
57
  if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)
61
58
  message( 'def-expected-structured', [ (def.type || def.name).location, def ] );
62
59
  }
@@ -862,8 +859,8 @@ function check( model ) {
862
859
  // Tree-ish expression from the compiler (not augmented)
863
860
  // eslint-disable-next-line max-len
864
861
  return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[2] ) ||
865
- // eslint-disable-next-line max-len
866
- isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
862
+ // eslint-disable-next-line max-len
863
+ isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
867
864
  }
868
865
 
869
866
  // Nothing else qualifies