@sap/cds-compiler 4.7.4 → 4.8.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 (94) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/draft/odata.js +16 -17
  67. package/lib/transform/effective/main.js +6 -3
  68. package/lib/transform/effective/misc.js +18 -8
  69. package/lib/transform/effective/types.js +4 -3
  70. package/lib/transform/forOdata.js +8 -7
  71. package/lib/transform/forRelationalDB.js +103 -112
  72. package/lib/transform/odata/flattening.js +82 -44
  73. package/lib/transform/odata/toFinalBaseType.js +9 -25
  74. package/lib/transform/odata/typesExposure.js +28 -15
  75. package/lib/transform/parseExpr.js +0 -3
  76. package/lib/transform/transformUtils.js +12 -8
  77. package/lib/transform/translateAssocsToJoins.js +2 -2
  78. package/lib/transform/universalCsn/coreComputed.js +2 -1
  79. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  80. package/package.json +2 -2
  81. package/share/messages/README.md +4 -0
  82. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  83. package/share/messages/check-proper-type-of.md +1 -1
  84. package/share/messages/def-duplicate-autoexposed.md +1 -1
  85. package/share/messages/extend-repeated-intralayer.md +3 -16
  86. package/share/messages/extend-unrelated-layer.md +1 -1
  87. package/share/messages/message-explanations.json +1 -0
  88. package/share/messages/redirected-to-ambiguous.md +1 -1
  89. package/share/messages/redirected-to-complex.md +1 -1
  90. package/share/messages/redirected-to-unrelated.md +1 -1
  91. package/share/messages/rewrite-not-supported.md +1 -1
  92. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  93. package/share/messages/type-missing-enum-value.md +59 -0
  94. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -1,10 +1,85 @@
1
1
  'use strict';
2
2
 
3
3
  // This file contains functions related to XSN/CSN-location objects,
4
- // but not semantic locations (which are message-specific),
4
+ // and the class definition for semantic locations
5
5
 
6
6
  const { copyPropIfExist } = require('../utils/objectUtils');
7
- const { CsnLocation } = require('../compiler/classes');
7
+
8
+ class Location {
9
+ file;
10
+ line;
11
+ col;
12
+ endLine;
13
+ endCol;
14
+ constructor(file, line, col, endLine, endCol) {
15
+ this.file = file;
16
+ this.line = line;
17
+ this.col = col;
18
+ this.endLine = endLine;
19
+ this.endCol = endCol;
20
+ }
21
+ }
22
+
23
+ class SemanticLocation {
24
+ mainKind;
25
+ absolute;
26
+ action;
27
+ param;
28
+ select;
29
+ mixin;
30
+ element = [];
31
+ suffix;
32
+ innerKind;
33
+
34
+ toString() {
35
+ return semanticLocationString( this );
36
+ }
37
+ }
38
+
39
+ function semanticLocationString( name, extended = false ) {
40
+ const parts = [ `${ name.mainKind }:“${ name.absolute }”` ];
41
+ if (name.action != null)
42
+ parts.push( `“action:${ name.action }”` );
43
+ if (name.param != null)
44
+ parts.push( name.param ? `param:“${ name.param }”` : 'returns' );
45
+ if (name.select != null)
46
+ parts.push( name.select ? `select:${ name.select }` : 'select' );
47
+ if (name.mixin != null) {
48
+ const prop = (name.innerKind === 'alias') ? 'alias' : 'mixin';
49
+ parts.push( `${ prop }:${ stringOrRaw( name.mixin ) }` );
50
+ }
51
+ const { element, innerKind } = name;
52
+ if (element.length) {
53
+ const append = innerKind === 'item' || innerKind === 'aspect';
54
+ const prop = !append && innerKind || !innerKind && name.select != null && 'column';
55
+ if (!prop || append) {
56
+ parts.push( `element:${ stringOrRaw( element ) }` );
57
+ if (append)
58
+ parts.push( innerKind );
59
+ }
60
+ else if (prop === 'column') {
61
+ const pos = extended ? -1
62
+ : element.findIndex( ( e, idx ) => idx && typeof e !== 'string' );
63
+ parts.push( `column:${ stringOrRaw( pos > 0 ? element.slice( 0, pos ) : element ) }` );
64
+ }
65
+ else {
66
+ if (element.length > 1)
67
+ parts.push( `element:${ stringOrRaw( element.slice( 0, -1 ) ) }` );
68
+ parts.push( `${ innerKind }:“${ element[element.length - 1] }”` );
69
+ }
70
+ }
71
+ if (name.suffix)
72
+ parts.push( name.suffix );
73
+ return parts.join( '/' );
74
+ }
75
+
76
+ function stringOrRaw( val ) {
77
+ if (!Array.isArray( val ))
78
+ return (typeof val === 'string') ? `“${ val }”` : val;
79
+ return val.every( e => typeof e === 'string' ) // && no '.' ?
80
+ ? `“${ val.join( '.' ) }”`
81
+ : val.map( stringOrRaw ).join( '→' ); // for XSN output, is sliced otherwise
82
+ }
8
83
 
9
84
  /**
10
85
  * Create a location with properties `file`, `line` and `col` from argument
@@ -42,7 +117,7 @@ function combinedLocation( start, end ) {
42
117
  */
43
118
  function emptyLocation( filename ) {
44
119
  return {
45
- __proto__: CsnLocation.prototype,
120
+ __proto__: Location.prototype,
46
121
  file: filename,
47
122
  line: 1,
48
123
  col: 1,
@@ -62,7 +137,7 @@ function emptyLocation( filename ) {
62
137
  */
63
138
  function emptyWeakLocation( filename ) {
64
139
  return {
65
- __proto__: CsnLocation.prototype,
140
+ __proto__: Location.prototype,
66
141
  file: filename,
67
142
  line: 1,
68
143
  col: 1,
@@ -72,12 +147,12 @@ function emptyWeakLocation( filename ) {
72
147
  }
73
148
 
74
149
  /**
75
- * @param {CsnLocation} loc
76
- * @returns {CsnLocation}
150
+ * @param {Location} loc
151
+ * @returns {Location}
77
152
  */
78
153
  function weakLocation( loc ) {
79
154
  return (!loc?.endLine) ? loc : {
80
- __proto__: CsnLocation.prototype,
155
+ __proto__: Location.prototype,
81
156
  file: loc.file,
82
157
  line: loc.line,
83
158
  col: loc.col,
@@ -96,8 +171,8 @@ function weakLocation( loc ) {
96
171
  * a double-click on the _last_ identifier token of the reference jumps to the artifact
97
172
  * represented by the complete reference.
98
173
  *
99
- * @param {CsnLocation} loc
100
- * @returns {CsnLocation}
174
+ * @param {Location} loc
175
+ * @returns {Location}
101
176
  */
102
177
  function weakRefLocation( ref ) {
103
178
  if (!ref)
@@ -105,7 +180,7 @@ function weakRefLocation( ref ) {
105
180
  const { path } = ref;
106
181
  const loc = path?.length ? path[path.length - 1].location : ref.location;
107
182
  return (!loc?.endLine) ? loc : {
108
- __proto__: CsnLocation.prototype,
183
+ __proto__: Location.prototype,
109
184
  file: loc.file,
110
185
  line: loc.line,
111
186
  col: loc.col,
@@ -115,12 +190,12 @@ function weakRefLocation( ref ) {
115
190
  }
116
191
 
117
192
  /**
118
- * @param {CsnLocation} loc
119
- * @returns {CsnLocation}
193
+ * @param {Location} loc
194
+ * @returns {Location}
120
195
  */
121
196
  function weakEndLocation( loc ) {
122
197
  return loc && {
123
- __proto__: CsnLocation.prototype,
198
+ __proto__: Location.prototype,
124
199
  file: loc.file,
125
200
  line: loc.endLine,
126
201
  col: loc.endCol && loc.endCol - 1,
@@ -203,7 +278,7 @@ function dictLocation( dict, extraLocation ) {
203
278
  const lineB = (b.endLine || b.line);
204
279
  return (lineA > lineB || (lineA === lineB && (a.endCol || a.col) > (b.endCol || b.col)) ? a : b);
205
280
  });
206
- return new CsnLocation( min.file, min.line, min.col, max.endLine, max.endCol );
281
+ return new Location( min.file, min.line, min.col, max.endLine, max.endCol );
207
282
  }
208
283
 
209
284
  function _objLocations( obj ) {
@@ -211,6 +286,8 @@ function _objLocations( obj ) {
211
286
  }
212
287
 
213
288
  module.exports = {
289
+ Location,
290
+ SemanticLocation,
214
291
  combinedLocation,
215
292
  emptyLocation,
216
293
  emptyWeakLocation,
@@ -162,31 +162,32 @@ const centralMessages = {
162
162
 
163
163
  'type-unsupported-precision-change': { severity: 'Error' },
164
164
  'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
165
+ 'type-missing-enum-value': { severity: 'Warning', errorFor: [ 'v5' ] },
165
166
 
166
167
  'def-missing-element': { severity: 'Error' },
167
-
168
+ 'def-expected-structured': { severity: 'Warning', configurableFor: true, errorFor: [ 'v5' ] },
168
169
  'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
169
170
 
170
171
  'def-invalid-key-cardinality': { severity: 'Error' },
171
172
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
172
- 'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
173
+ 'odata-spec-violation-array': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
173
174
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
174
- 'odata-spec-violation-assoc': { severity: 'Warning' }, // more than 30 chars
175
+ 'odata-spec-violation-assoc': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
175
176
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
176
177
  'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
177
178
  'odata-spec-violation-id': { severity: 'Error', configurableFor: true },
178
- 'odata-spec-violation-namespace': { severity: 'Warning' }, // more than 30 chars
179
+ 'odata-spec-violation-namespace': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
179
180
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
180
- 'odata-spec-violation-param': { severity: 'Warning' }, // more than 30 chars
181
+ 'odata-spec-violation-param': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
181
182
  // Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
182
- 'odata-spec-violation-returns': { severity: 'Warning' }, // more than 30 chars
183
+ 'odata-spec-violation-returns': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
183
184
  'odata-spec-violation-type': { severity: 'Error', configurableFor: true },
184
- 'odata-spec-violation-type-unknown': { severity: 'Warning' },
185
- 'odata-spec-violation-no-key': { severity: 'Warning' },
185
+ 'odata-spec-violation-type-unknown': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
186
+ 'odata-spec-violation-no-key': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
186
187
  'odata-spec-violation-key-array': { severity: 'Error', configurableFor: true }, // more than 30 chars
187
188
  'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
188
- 'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
189
- 'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
189
+ 'odata-spec-violation-key-type': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
190
+ 'odata-spec-violation-property-name': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
190
191
  'odata-anno-preproc': { severity: 'Warning' },
191
192
  'odata-anno-dict': { severity: 'Warning' },
192
193
  'odata-anno-vocref': { severity: 'Warning' },
@@ -251,6 +252,7 @@ const centralMessageTexts = {
251
252
  'valid-structured': 'Structured OData is only supported with OData version v4',
252
253
  'sql-dialect-and-naming': 'sqlDialect $(NAME) can\'t be combined with sqlMapping $(PROP)',
253
254
  'sql-dialect-and-localized': 'Option $(OPTION) can\'t be combined with SQL dialect $(VALUE) or the to.hdi()/to.hdbcds() backend',
255
+ 'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)'
254
256
  },
255
257
  'api-unexpected-combination': {
256
258
  std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
@@ -631,7 +633,6 @@ const centralMessageTexts = {
631
633
  expr: 'Associations can\'t be used as values in expressions',
632
634
  'expr-comp': 'Compositions can\'t be used as values in expressions',
633
635
  'assoc-stored': 'Associations and compositions can\'t be used as values in stored calculated elements',
634
- cast: 'Casting to an association is not supported',
635
636
 
636
637
  'managed-filter': 'Unexpected managed association $(NAME) in filter expression of $(ID)',
637
638
  'unmanaged-filter': 'Unexpected unmanaged association $(NAME) in filter expression of $(ID)'
@@ -688,7 +689,10 @@ const centralMessageTexts = {
688
689
  'type-unexpected-assoc': {
689
690
  std: 'An unmanaged association can\'t be used as type',
690
691
  },
691
-
692
+ 'type-missing-enum-value': {
693
+ std: 'Missing value for non-string enum element $(NAME)',
694
+ numeric: 'Missing value for numeric enum element $(NAME)',
695
+ },
692
696
  'type-missing-argument': 'Missing value for argument $(NAME) in reference to type $(ID)',
693
697
  'type-ignoring-argument': 'Too many arguments for type $(ART)',
694
698
  'type-unexpected-argument': {
@@ -803,6 +807,8 @@ const centralMessageTexts = {
803
807
  'texts-aspect-locale': 'Element $(ELEMREF) of $(ART) must be of type $(TYPE) or $(OTHERTYPE)',
804
808
  },
805
809
 
810
+ 'def-expected-structured': 'Events must either be structured or be projections',
811
+
806
812
  'duplicate-definition': {
807
813
  std: 'Duplicate definition of $(NAME)',
808
814
  absolute: 'Duplicate definition of artifact $(NAME)',
@@ -829,10 +835,13 @@ const centralMessageTexts = {
829
835
  $self: 'Can\'t refer to the query\'s own elements',
830
836
  },
831
837
  'ref-invalid-override': {
832
- std: 'Overridden element of include must not change element structure', // unused
838
+ std: 'Overridden element of include must not change its type drastically', // unused
833
839
  'new-not-structured': 'Expected element $(NAME) to be structured, because it overrides the included element from $(ART)',
834
840
  'old-not-structured': 'Expected element $(NAME) to be scalar, because it overrides the included element from $(ART)',
835
- missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
841
+ missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)',
842
+
843
+ 'new-not-target': 'Expected element $(NAME) to be an association, because it overrides the included element from $(ART)',
844
+ 'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
836
845
  },
837
846
 
838
847
  'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
@@ -974,7 +983,8 @@ const centralMessageTexts = {
974
983
  'from-structure': 'Structured elements can\'t be cast to a different type',
975
984
  'expr-to-structure': 'Can\'t cast an expression to a structured type',
976
985
  'val-to-structure': 'Can\'t cast $(VALUE) to a structured type',
977
- 'from-assoc': 'Invalid type cast on an association'
986
+ 'from-assoc': 'Invalid type cast on an association',
987
+ 'assoc': 'Can\'t cast to an association',
978
988
  },
979
989
 
980
990
  // -----------------------------------------------------------------------------------
@@ -997,6 +1007,21 @@ const centralMessageTexts = {
997
1007
  publishingFilter: 'Can\'t publish managed association $(ID) with filter, as it must have at least one foreign key',
998
1008
  },
999
1009
 
1010
+ // tenenat isolation via discriminator column:
1011
+ 'tenant-invalid-alias-name': {
1012
+ std: 'Can\'t have a table alias named $(NAME) in a tenant-dependent entity',
1013
+ implicit: 'Provide an explicit table alias name; do not use $(NAME)',
1014
+ mixin: 'Can\'t define a mixin named $(NAME) in a tenant-dependent entity',
1015
+ },
1016
+ 'tenant-invalid-composition': {
1017
+ std: 'Can\'t define a composition of a tenant-independent entity $(TARGET) in a tenant-dependent entity',
1018
+ type: 'Can\'t use type $(TYPE) with a composition of a tenant-independent entity in a tenant-dependent entity',
1019
+ },
1020
+ 'tenant-invalid-target': {
1021
+ std: 'Can\'t define an association to a tenant-dependent entity $(TARGET) in a tenant-independent entity',
1022
+ type: 'Can\'t use type $(TYPE) with an association to a tenant-dependent entity in a tenant-independent entity',
1023
+ },
1024
+
1000
1025
  // -----------------------------------------------------------------------------------
1001
1026
  // OData Message section starts here
1002
1027
  // -----------------------------------------------------------------------------------
@@ -1125,6 +1150,7 @@ const centralMessageTexts = {
1125
1150
  'notadynexpr': '$(OP) is not a renderable dynamic expression in $(ANNO)',
1126
1151
  'use': 'Function $(OP) is not a renderable dynamic expression in $(ANNO), please use $(CODE) instead',
1127
1152
  'canonfuncalias': 'Expected function name $(CODE) to be of the form $(META).$(OTHERMETA) for $(OP) in $(ANNO)',
1153
+ 'unexpected': 'Unexpected expression in $(ANNO)',
1128
1154
  }
1129
1155
  ,
1130
1156
  'odata-anno-xpr-type': {
@@ -1136,30 +1162,21 @@ const centralMessageTexts = {
1136
1162
  'atleast': 'Expected at least $(COUNT) argument(s) for $(OP) in $(ANNO)',
1137
1163
  'atmost': 'Expected at most $(COUNT) argument(s) for $(OP) in $(ANNO)',
1138
1164
  'wrongcount': 'Expected exactly one $(PROP) for $(OP) in $(ANNO)',
1139
- 'wrongref': 'Unexpected arguments or filters in $(ELEMREF) in $(ANNO)',
1140
1165
  'wrongval': 'Unexpected value for $(OP) in $(ANNO)',
1141
1166
  'wrongval_meta': 'Expected value for $(OP) to be a $(META) in $(ANNO)',
1142
1167
  'wrongval_meta_list': 'Expected value for $(OP) to be a $(META) or $(RAWVALUES) in $(ANNO)',
1143
1168
  },
1144
1169
  'odata-anno-xpr-ref': {
1145
1170
  'std': '$(ANNO) can\'t be propagated to $(NAME) because path $(ELEMREF) is not resolvable via type reference $(CODE)',
1146
- 'flatten_builtin': 'Expected path $(ELEMREF) in $(ANNO) to resolve to a builtin type while flattening $(NAME)',
1147
- 'notaparam': 'Unexpected path $(ELEMREF) for parameter entity in $(ANNO)',
1148
- 'notaneelement': 'Unexpected path $(ELEMREF) for type entity in $(ANNO)'
1149
- },
1150
- // tenenat isolation via discriminator column:
1151
- 'tenant-invalid-alias-name': {
1152
- std: 'Can\'t have a table alias named $(NAME) in a tenant-dependent entity',
1153
- implicit: 'Provide an explicit table alias name; do not use $(NAME)',
1154
- mixin: 'Can\'t define a mixin named $(NAME) in a tenant-dependent entity',
1155
- },
1156
- 'tenant-invalid-composition': {
1157
- std: 'Can\'t define a composition of a tenant-independent entity $(TARGET) in a tenant-dependent entity',
1158
- type: 'Can\'t use type $(TYPE) with a composition of a tenant-independent entity in a tenant-dependent entity',
1159
- },
1160
- 'tenant-invalid-target': {
1161
- std: 'Can\'t define an association to a tenant-dependent entity $(TARGET) in a tenant-independent entity',
1162
- type: 'Can\'t use type $(TYPE) with an association to a tenant-dependent entity in a tenant-independent entity',
1171
+ 'args': 'Unexpected arguments or filters in $(ELEMREF) in $(ANNO)',
1172
+ 'flatten_builtin': 'Expected path $(ELEMREF) in $(ANNO) to end with a leaf element while flattening $(NAME)',
1173
+ 'flatten_builtin_type': 'Expected path $(ELEMREF) in $(ANNO) to end with a leaf element while flattening',
1174
+ 'invalid': 'Invalid path $(ELEMREF) in $(ANNO)',
1175
+ // genericTranslation
1176
+ 'notaparam': 'Element path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a parameter entity',
1177
+ 'notaneelement': 'Parameter path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a type entity',
1178
+ 'notrendered': 'Path step $(COUNT) of $(ELEMREF) in $(ANNO) refers to an unrendered propery in the OData API',
1179
+ 'tomany': 'Unexpected to-many transition in path step $(COUNT) of $(ELEMREF) in $(ANNO)',
1163
1180
  },
1164
1181
  // -----------------------------------------------------------------------------------
1165
1182
  // OData Message section ends here, no messages below this line
@@ -5,8 +5,8 @@
5
5
  'use strict';
6
6
 
7
7
  const { term } = require('../utils/term');
8
- const { locationString } = require('./location');
9
- const { isDeprecatedEnabled } = require('./model');
8
+ const { Location, locationString } = require('./location');
9
+ const { isDeprecatedEnabled, isBetaEnabled } = require('./model');
10
10
  const { centralMessages, centralMessageTexts, oldMessageIds } = require('./message-registry');
11
11
  const _messageIdsWithExplanation = require('../../share/messages/message-explanations.json').messages;
12
12
  const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
@@ -17,7 +17,6 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
17
17
  const fs = require('fs');
18
18
  const path = require('path');
19
19
  const { inspect } = require('util')
20
- const { CsnLocation } = require('../compiler/classes');
21
20
 
22
21
  // term instance for messages
23
22
  const colorTerm = term();
@@ -74,6 +73,9 @@ function isDowngradable( messageId, moduleName, options ) {
74
73
  return false;
75
74
  if (msg.severity !== 'Error')
76
75
  return true;
76
+ // v5 messages are downgradable (except if errorFor also contains the current module).
77
+ if (msg.errorFor && msg.errorFor.includes('v5'))
78
+ return true;
77
79
  const { configurableFor } = msg;
78
80
  return (Array.isArray( configurableFor ))
79
81
  ? configurableFor.includes( moduleName )
@@ -88,11 +90,12 @@ function isDowngradable( messageId, moduleName, options ) {
88
90
  class CompilationError extends Error {
89
91
  /**
90
92
  * @param {CompileMessage[]} messages
93
+ * @param {boolean} [dontSerializeMessages] If true, compiler messages are NOT part of the errors message text.
91
94
  * @param {XSN.Model} [model] the XSN model, only to be set with options.attachValidNames
92
95
  */
93
- constructor(messages, model) {
94
- // TODO(v5): Don't store stringified messages.
95
- super( `CDS compilation failed\n${messages?.map( m => m.toString() ).join('\n') || ''}` );
96
+ constructor(messages, model, dontSerializeMessages) {
97
+ const msg = !dontSerializeMessages && messages?.map( m => m.toString() ).join('\n') || '';
98
+ super( `CDS compilation failed\n${msg}` );
96
99
  /** @since v4.0.0 */
97
100
  this.code = 'ERR_CDS_COMPILATION_FAILURE';
98
101
  this.messages = [ ...messages ].sort(compareMessageSeverityAware);
@@ -113,6 +116,27 @@ class CompilationError extends Error {
113
116
  return this.stack || this.message;
114
117
  }
115
118
 
119
+ /**
120
+ * Called when the exception is printed, e.g. when it is not caught.
121
+ * To give users a bit of information what went wrong, return stringified
122
+ * error messages. But only errors to avoid spamming users.
123
+ *
124
+ * Compiler consumers should catch compilation errors and properly handle
125
+ * them by printing messages themselves.
126
+ *
127
+ * @returns {string}
128
+ */
129
+ toString() {
130
+ let messages = [ 'CDS compilation failed' ];
131
+ if (this.messages) {
132
+ messages = messages.concat(this.messages
133
+ .filter(msg => msg.severity === 'Error')
134
+ .map( m => m.toString())
135
+ );
136
+ }
137
+ return messages.join('\n');
138
+ }
139
+
116
140
  /**
117
141
  * @deprecated Use `.messages` instead.
118
142
  */
@@ -140,7 +164,7 @@ class CompileMessage {
140
164
  */
141
165
  constructor(location, msg, severity = 'Error', id = null, home = null, moduleName = null) {
142
166
  this.message = msg;
143
- this.$location = { __proto__: CsnLocation.prototype, ...location, address: undefined };
167
+ this.$location = { __proto__: Location.prototype, ...location, address: undefined };
144
168
  this.validNames = null;
145
169
  this.home = home; // semantic location, e.g. 'entity:"E"/element:"x"'
146
170
  this.severity = severity;
@@ -183,18 +207,28 @@ const severitySpecs = {
183
207
  */
184
208
  function reclassifiedSeverity( msg, options, moduleName ) {
185
209
  const spec = centralMessages[msg.messageId] || { severity: msg.severity, configurableFor: null, errorFor: null };
210
+ let severity = spec.severity;
211
+
186
212
  if (spec.severity === 'Error') {
187
213
  if (!isDowngradable(msg.messageId, moduleName, options))
188
214
  return 'Error';
189
215
  }
190
216
  else {
191
217
  const { errorFor } = spec;
192
- if (Array.isArray( errorFor ) && errorFor.includes( moduleName ))
193
- return 'Error';
218
+ if (Array.isArray( errorFor )) {
219
+ if (errorFor.includes(moduleName))
220
+ return 'Error';
221
+
222
+ if (errorFor.includes('v5') && isBetaEnabled(options, 'v5preview')) {
223
+ severity = 'Error';
224
+ if (!isDowngradable(msg.messageId, moduleName, options))
225
+ return severity;
226
+ }
227
+ }
194
228
  }
195
229
 
196
230
  if (!options.severities)
197
- return spec.severity;
231
+ return severity;
198
232
 
199
233
  let newSeverity = options.severities[msg.messageId];
200
234
  // The user could have specified a severity through an old message ID.
@@ -202,7 +236,7 @@ function reclassifiedSeverity( msg, options, moduleName ) {
202
236
  const oldName = spec.oldNames.find((name => options.severities[name]));
203
237
  newSeverity = options.severities[oldName];
204
238
  }
205
- return normalizedSeverity( newSeverity ) || spec.severity;
239
+ return normalizedSeverity( newSeverity ) || severity;
206
240
  }
207
241
 
208
242
  function normalizedSeverity( severity ) {
@@ -333,6 +367,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
333
367
  callTransparently,
334
368
  moduleName,
335
369
  setModel,
370
+ setOptions,
336
371
  };
337
372
 
338
373
  function _message( id, location, textOrArguments, severity, texts = null ) {
@@ -352,7 +387,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
352
387
  if (options.internalMsg)
353
388
  msg.error = new Error( 'stack' );
354
389
  if (definition)
355
- msg.$location.address = { definition };
390
+ msg.$location.address = { definition }; // TODO: remove
356
391
 
357
392
  if (id) {
358
393
  if (options.testMode && !options.$recompile)
@@ -406,6 +441,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
406
441
  /**
407
442
  * Normalize the given location. Location may be a CSN path, XSN/CSN location or an
408
443
  * array of the form `[CSN.Location, XSN user, suffix]`.
444
+ * TODO: normalize to [ Location, SemanticLocation ]
409
445
  *
410
446
  * @param {any} location
411
447
  * @returns {[CSN.Location, string, string]} Location, semantic location and definition.
@@ -428,6 +464,9 @@ function makeMessageFunction( model, options, moduleName = null ) {
428
464
  ];
429
465
  }
430
466
 
467
+ if (location[1]?.mainKind)
468
+ return [ location[0], location[1].toString(), null ];
469
+
431
470
  let semanticLocation = location[1] ? homeName( location[1], false ) : null;
432
471
  if (location[2]) { // optional suffix, e.g. annotation
433
472
  semanticLocation += `/${ (typeof location[2] === 'string') ? location[2]: homeName(location[2]) }`;
@@ -494,8 +533,10 @@ function makeMessageFunction( model, options, moduleName = null ) {
494
533
  }
495
534
 
496
535
  function throwWithError() {
497
- if (hasNewError)
498
- throw new CompilationError( messages, options.attachValidNames && model );
536
+ if (hasNewError) {
537
+ const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
538
+ throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
539
+ }
499
540
  }
500
541
 
501
542
  /**
@@ -510,8 +551,10 @@ function makeMessageFunction( model, options, moduleName = null ) {
510
551
  if (!messages || !messages.length)
511
552
  return;
512
553
  const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
513
- if (hasError( messages, moduleName, options ))
514
- throw new CompilationError( messages, options.attachValidNames && model );
554
+ if (hasError( messages, moduleName, options )) {
555
+ const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
556
+ throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
557
+ }
515
558
  }
516
559
 
517
560
  /**
@@ -561,6 +604,18 @@ function makeMessageFunction( model, options, moduleName = null ) {
561
604
  function setModel( _model ) {
562
605
  model = _model;
563
606
  }
607
+
608
+ /**
609
+ * Change the options used to determine message severities.
610
+ * This is necessary if you change `options.severities`, as otherwise they may not be picked up.
611
+ *
612
+ * @param {CSN.Model} _model
613
+ */
614
+ function setOptions( _options ) {
615
+ options = _options;
616
+ }
617
+
618
+
564
619
  }
565
620
 
566
621
  /**
package/lib/base/model.js CHANGED
@@ -29,7 +29,6 @@ const availableBetaFlags = {
29
29
  annotationExpressions: true,
30
30
  odataPathsInAnnotationExpressions: true,
31
31
  odataAnnotationExpressions: true,
32
- assocsWithParams: true, // beta, because runtimes don't support it, yet.
33
32
  hanaAssocRealCardinality: true,
34
33
  mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
35
34
  enableUniversalCsn: true,
@@ -39,7 +38,6 @@ const availableBetaFlags = {
39
38
  effectiveCsn: true,
40
39
  tenantVariable: true,
41
40
  calcAssoc: true,
42
- vectorType: true,
43
41
  v5preview: true,
44
42
  // disabled by --beta-mode
45
43
  nestedServices: false,
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isBuiltinType } = require('../model/csnUtils');
3
+ const { isBuiltinType } = require('../base/builtins');
4
4
  const { isBetaEnabled } = require('../base/model');
5
5
 
6
6
  // Only to be used with validator.js - a correct this value needs to be provided!
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- forEachMember, forEachMemberRecursively, isBuiltinType, cardinality2str,
4
+ forEachMember, forEachMemberRecursively, cardinality2str,
5
5
  } = require('../model/csnUtils');
6
+ const { isBuiltinType } = require('../base/builtins');
6
7
  const { isGeoTypeName } = require('../compiler/builtins');
7
8
  const { setProp } = require('../base/model');
8
9
  // Only to be used with validator.js - a correct `this` value needs to be provided!
@@ -6,7 +6,7 @@
6
6
 
7
7
  const { csnRefs } = require('../model/csnRefs');
8
8
  const { setProp } = require('../base/model');
9
- const { xprInAnnoProperties } = require('../compiler/builtins');
9
+ const { isAnnotationExpression } = require('../base/builtins');
10
10
 
11
11
  /**
12
12
  * The following properties are attached as non-enumerable where appropriate:
@@ -96,7 +96,7 @@ function enrichCsn( csn, options ) {
96
96
  */
97
97
  function annotation( _parent, _prop, node ) {
98
98
  if (options.enrichAnnotations) {
99
- if (node?.['='] !== undefined && xprInAnnoProperties.some(xProp => node[xProp] !== undefined)) {
99
+ if (isAnnotationExpression(node)) {
100
100
  standard(_parent, _prop, node);
101
101
  }
102
102
  else if (node && typeof node === 'object') {
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { hasAnnotationValue, isPersistedOnDatabase, isBuiltinType } = require('../model/csnUtils');
3
+ const { hasAnnotationValue, isPersistedOnDatabase } = require('../model/csnUtils');
4
+ const { isBuiltinType } = require('../base/builtins');
4
5
  const { requireForeignKeyAccess } = require('./onConditions');
5
6
  const { pathId } = require('../model/csnRefs');
6
7
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isBuiltinType } = require('../model/csnUtils');
3
+ const { isBuiltinType } = require('../base/builtins');
4
4
  const { RelationalOperators } = require('../transform/transformUtils');
5
5
  /**
6
6
  * Prepare the ref steps so that they are loggable