@sap/cds-compiler 3.3.2 → 3.4.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 (74) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/bin/cdsc.js +3 -1
  3. package/doc/CHANGELOG_BETA.md +17 -0
  4. package/lib/api/main.js +147 -18
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/keywords.js +104 -0
  8. package/lib/base/message-registry.js +136 -67
  9. package/lib/base/messages.js +59 -48
  10. package/lib/base/model.js +1 -0
  11. package/lib/checks/actionsFunctions.js +1 -1
  12. package/lib/checks/cdsPersistence.js +1 -1
  13. package/lib/checks/checkForTypes.js +13 -8
  14. package/lib/checks/defaultValues.js +3 -1
  15. package/lib/checks/elements.js +1 -1
  16. package/lib/checks/parameters.js +4 -2
  17. package/lib/checks/queryNoDbArtifacts.js +1 -1
  18. package/lib/checks/sql-snippets.js +12 -10
  19. package/lib/checks/validator.js +14 -4
  20. package/lib/compiler/assert-consistency.js +8 -7
  21. package/lib/compiler/checks.js +30 -20
  22. package/lib/compiler/define.js +89 -25
  23. package/lib/compiler/extend.js +21 -18
  24. package/lib/compiler/finalize-parse-cdl.js +14 -9
  25. package/lib/compiler/populate.js +30 -8
  26. package/lib/compiler/propagator.js +4 -2
  27. package/lib/compiler/resolve.js +11 -5
  28. package/lib/compiler/shared.js +66 -48
  29. package/lib/compiler/tweak-assocs.js +2 -3
  30. package/lib/compiler/utils.js +11 -0
  31. package/lib/edm/annotations/genericTranslation.js +7 -4
  32. package/lib/edm/csn2edm.js +1 -1
  33. package/lib/gen/language.checksum +1 -1
  34. package/lib/gen/language.interp +1 -1
  35. package/lib/gen/languageParser.js +3565 -3544
  36. package/lib/json/csnVersion.js +13 -13
  37. package/lib/json/from-csn.js +140 -158
  38. package/lib/json/to-csn.js +23 -5
  39. package/lib/language/.eslintrc.json +4 -0
  40. package/lib/language/antlrParser.js +7 -10
  41. package/lib/language/docCommentParser.js +1 -2
  42. package/lib/language/errorStrategy.js +54 -27
  43. package/lib/language/genericAntlrParser.js +115 -84
  44. package/lib/language/language.g4 +29 -25
  45. package/lib/language/multiLineStringParser.js +75 -63
  46. package/lib/main.js +1 -0
  47. package/lib/model/csnRefs.js +4 -3
  48. package/lib/model/csnUtils.js +39 -7
  49. package/lib/model/sortViews.js +7 -3
  50. package/lib/modelCompare/compare.js +49 -15
  51. package/lib/modelCompare/filter.js +83 -0
  52. package/lib/optionProcessor.js +5 -1
  53. package/lib/render/manageConstraints.js +9 -5
  54. package/lib/render/toCdl.js +120 -62
  55. package/lib/render/toHdbcds.js +1 -1
  56. package/lib/render/toSql.js +6 -2
  57. package/lib/render/utils/common.js +7 -0
  58. package/lib/sql-identifier.js +7 -0
  59. package/lib/transform/db/assertUnique.js +27 -38
  60. package/lib/transform/db/expansion.js +11 -4
  61. package/lib/transform/db/temporal.js +3 -1
  62. package/lib/transform/db/transformExists.js +7 -1
  63. package/lib/transform/db/views.js +42 -13
  64. package/lib/transform/draft/db.js +2 -2
  65. package/lib/transform/forRelationalDB.js +12 -6
  66. package/lib/transform/localized.js +1 -1
  67. package/lib/transform/odata/typesExposure.js +2 -1
  68. package/lib/transform/parseExpr.js +245 -0
  69. package/lib/transform/transformUtilsNew.js +23 -14
  70. package/lib/transform/translateAssocsToJoins.js +12 -12
  71. package/lib/utils/term.js +5 -5
  72. package/package.json +2 -2
  73. package/share/messages/message-explanations.json +1 -1
  74. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +1 -1
@@ -37,7 +37,11 @@ const { createDict } = require("../utils/objectUtils");
37
37
  * Central register of messages and their configuration.
38
38
  * Group by id-category.
39
39
  *
40
- * configurableFor: 'deprecated' = severity can only be changed with deprecated.downgradableErrors
40
+ * configurableFor: truthy = error can be downgraded in certain situations
41
+ * - true = can always be downgraded, we do not really care
42
+ * - [‹module›, …] = can be downgraded in compiler function ‹module›
43
+ * - 'deprecated' = severity can only be changed with deprecated.downgradableErrors
44
+ * - 'v3' = currently like true, but should be 'deprecated' in v4
41
45
  *
42
46
  * @type {Object<string, MessageConfig>}
43
47
  */
@@ -74,12 +78,11 @@ const centralMessages = {
74
78
 
75
79
  'empty-type': { severity: 'Info' }, // only still an error in old transformers
76
80
 
77
- // Structured types were warned about but made CSN un-recompilable.
78
- 'enum-invalid-type': { severity: 'Error', configurableFor: 'deprecated' },
79
-
80
81
  // TODO: rename to ref-expected-XYZ
81
82
  'expected-type': { severity: 'Error' },
82
83
  'ref-sloppy-type': { severity: 'Error' },
84
+ 'ref-unexpected-self': { severity: 'Error' },
85
+ 'ref-expecting-bare-aspect': { severity: 'Error' },
83
86
  'type-unexpected-typeof': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: make it non-config
84
87
  'type-ignoring-argument': { severity: 'Error', configurableFor: true },
85
88
  'type-expected-builtin': { severity: 'Error', configurableFor: true },
@@ -124,13 +127,10 @@ const centralMessages = {
124
127
  'ref-rejected-on': { severity: 'Error' },
125
128
  'ref-expected-element': { severity: 'Error' },
126
129
 
127
- 'rewrite-key-not-covered-explicit': { severity: 'Error', configurableFor: 'deprecated' },
128
- 'rewrite-key-not-covered-implicit': { severity: 'Error', configurableFor: 'deprecated' },
129
- 'rewrite-key-not-matched-explicit': { severity: 'Error', configurableFor: 'deprecated' },
130
- 'rewrite-key-not-matched-implicit': { severity: 'Error', configurableFor: 'deprecated' },
131
- 'rewrite-key-for-unmanaged': { severity: 'Error', configurableFor: 'deprecated' },
132
- 'rewrite-not-supported': { severity: 'Error' },
133
- 'rewrite-on-for-managed': { severity: 'Error', configurableFor: 'deprecated' },
130
+ 'rewrite-key-not-covered-explicit': { severity: 'Error' },
131
+ 'rewrite-key-not-covered-implicit': { severity: 'Error' },
132
+ 'rewrite-key-not-matched-explicit': { severity: 'Error' },
133
+ 'rewrite-key-not-matched-implicit': { severity: 'Error' },
134
134
 
135
135
  'service-nested-context': { severity: 'Error', configurableFor: true }, // does not hurt compile, TODO
136
136
  'service-nested-service': { severity: 'Error', configurableFor: 'deprecated' }, // not supported yet
@@ -141,24 +141,13 @@ const centralMessages = {
141
141
  // fallback, but then parse.cdl is not supposed to work correctly (it can
142
142
  // then either issue an error or produce a CSN missing some annotations):
143
143
  'syntax-duplicate-annotate': { severity: 'Error', configurableFor: true },
144
- 'syntax-expected-cardinality': { severity: 'Error' },
145
- 'syntax-expected-length': { severity: 'Error' },
146
- 'syntax-expected-translation': { severity: 'Error' },
147
- 'syntax-required-subproperty': { severity: 'Error' },
148
- 'syntax-unexpected-property': { severity: 'Error', configurableFor: true }, // is the removed
144
+ 'syntax-unexpected-property': { severity: 'Error' },
149
145
  'syntax-deprecated-ident': { severity: 'Error', configurableFor: true },
150
146
  'syntax-fragile-alias': { severity: 'Error', configurableFor: true },
151
147
  'syntax-fragile-ident': { severity: 'Error', configurableFor: true },
152
-
153
- 'syntax-invalid-extend': { severity: 'Error' },
154
-
155
- 'syntax-invalid-text-block' : { severity: 'Error' },
156
148
  'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
157
- 'syntax-invalid-escape': { severity: 'Error' },
158
- 'syntax-missing-escape': { severity: 'Error' },
159
149
 
160
- 'syntax-expected-integer': { severity: 'Error' },
161
- 'syntax-invalid-masked': { severity: 'Error', configurableFor: true },
150
+ 'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'v3' },
162
151
  'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
163
152
 
164
153
  'type-managed-composition': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: non-config
@@ -212,7 +201,10 @@ for (const oldName in oldMessageIds) {
212
201
  }
213
202
 
214
203
 
215
- // For messageIds, where no text has been provided via code (central def)
204
+ // For messageIds, where no text has been provided via code (central def).
205
+ // DO NOT CHANGE THE VARIABLE NAME!
206
+ // If you change it, keep in sync with scripts/eslint/rules/message-text.js
207
+
216
208
  const centralMessageTexts = {
217
209
  'api-invalid-option': {
218
210
  // TODO: too many different error situations for one message id,
@@ -239,42 +231,118 @@ const centralMessageTexts = {
239
231
  'flatten-fkey-gen': 'Duplicate definition of foreign key element $(NAME) for association $(ART)',
240
232
  'flatten-fkey-exists': 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element',
241
233
  },
242
-
234
+
235
+ // Syntax messages, both CDL and CSN parser: ----------------------------------
236
+ 'syntax-dollar-ident': { // Warning, TODO: make it name-invalid-alias
237
+ std: 'An artifact starting with $(NAME) might shadow a special variable - replace by another name',
238
+ $tableAlias: 'A table alias name starting with $(NAME) might shadow a special variable - replace by another name',
239
+ $tableImplicit: 'The resulting table alias starts with $(NAME) and might shadow a special variable - specify another name with $(KEYWORD)',
240
+ mixin: 'A mixin name starting with $(NAME) might shadow a special variable - replace by another name',
241
+ },
242
+ // 'syntax-duplicate-excluding' (TODO: also CDL)
243
243
  'syntax-ignoring-anno': {
244
244
  std: 'Annotations can\'t be used at prefix references',
245
245
  doc: 'Doc comments can\'t be used at prefix references',
246
246
  },
247
+ 'syntax-invalid-name': {
248
+ std: 'Identifier for name must not be empty', // TODO: use
249
+ csn: 'Property name in dictionary $(PARENTPROP) must not be empty',
250
+ },
251
+ 'syntax-invalid-literal': { // TODO: write texts less CDL specific
252
+ 'std': 'Invalid literal',
253
+ 'uneven-hex': 'A binary literal must have an even number of characters',
254
+ 'invalid-hex': 'A binary literal must only contain characters 0-9, a-f and A-F',
255
+ 'time': 'Expecting time\'hh:mm:ss\' where hh, mm and the optional ss are numbers',
256
+ 'date': 'Expecting date\'YYYY-MM-DD\' where YYYY, MM and DD are numbers',
257
+ 'timestamp': 'Expecting timestamp\'YYYY-MM-DD hh:mm:ss.u…u\' where YYYY, MM, DD, hh, mm, ss and u are numbers (optional 1-7×u)',
258
+ },
259
+ 'syntax-missing-ellipsis': 'Expecting an array item $(NEWCODE) after an item with $(CODE)',
247
260
  'syntax-unexpected-ellipsis': {
248
- std: 'Expected no more than one $(CODE)',
249
- 'nested-array': 'Unexpected $(CODE) in nested array'
250
- },
251
- 'syntax-expected-object': 'Expected object for property $(PROP)',
252
- 'syntax-expected-column': 'Expected object or string \'*\' for property $(PROP)',
253
- 'syntax-expected-natnum': 'Expected non-negative number for property $(PROP)',
254
- 'syntax-expected-cardinality': 'Expected non-negative number or string \'*\' for property $(PROP)',
255
- 'syntax-expected-reference': 'Expected non-empty string or object for property $(PROP)',
256
- 'syntax-expected-term': 'Expected non-empty string or object for property $(PROP)',
257
- 'syntax-dollar-ident': {
258
- std: 'An artifact starting with $(NAME) might shadow a special variable - replace by another name',
259
- $tableAlias: 'A table alias name starting with $(NAME) might shadow a special variable - replace by another name',
260
- $tableImplicit: 'The resulting table alias starts with $(NAME) and might shadow a special variable - specify another name with $(KEYWORD)',
261
- mixin: 'A mixin name starting with $(NAME) might shadow a special variable - replace by another name' ,
261
+ std: 'Unexpected array item $(CODE)',
262
+ duplicate: 'Unexpected array item $(CODE) after previous $(CODE) without $(KEYWORD)',
263
+ 'csn-duplicate': 'Unexpected object with property $(PROP) after previous array item $(CODE)',
264
+ 'csn-nested': 'Unexpected object with property $(PROP) in nested array',
265
+ },
266
+ 'syntax-unsupported-masked': { // configurable Error
267
+ std: 'The keyword $(KEYWORD) is not supported',
268
+ csn: 'The property $(PROP) is not supported',
262
269
  },
263
- 'syntax-expected-length': {
264
- std: 'Expected array in $(PROP) to have at least $(N) items',
265
- one: 'Expected array in $(PROP) to have at least one item',
266
- suffix: 'With sibling property $(OTHERPROP), expected array in $(PROP) to have at least one item',
270
+
271
+ // Syntax messages, CDL parser - default: Error ------------------------------
272
+ 'syntax-duplicate-annotate': 'You can\'t refer to $(NAME) repeatedly with property $(PROP) in the same annotate statement',
273
+ 'syntax-duplicate-extend': {
274
+ std: 'You can\'t define and refer to $(NAME) repeatedly in the same extend statement',
275
+ define: 'You can\'t refer to $(NAME) in the same extend statement where it was defined',
276
+ extend: 'You can\'t refer to $(NAME) repeatedly in the same extend statement',
267
277
  },
268
278
  'syntax-invalid-extend': 'Can\'t extend an element with $(KIND)',
269
279
  'syntax-invalid-text-block': 'Missing newline in text block',
280
+ 'syntax-unsupported-param': {
281
+ std: 'Parameter not supported', // unused
282
+ dynamic: 'Dynamic parameter $(NAME) is not supported',
283
+ positional: 'Positional parameter $(NAME) is not supported',
284
+ },
285
+
286
+ // Syntax messages, CSN parser - default: Error ------------------------------
287
+ // TODO: use one id with text variants instead of several message ids
288
+ 'syntax-deprecated-property': { // Warning
289
+ std: 'Deprecated property $(PROP)', // unused
290
+ 'zero': 'Deprecated CSN v0.1.0 property $(PROP) is ignored',
291
+ 'zero-replace': 'Replace CSN v0.1.0 property $(PROP) by $(OTHERPROP)',
292
+ },
293
+ 'syntax-deprecated-value': { // Warning
294
+ std: 'Deprecated representation of the value in property $(PROP)',
295
+ replace: 'Replace value in $(PROP) by $(VALUE)',
296
+ 'zero-parens': 'Deprecated CSN v.0.1.0 representation of expressions in parentheses',
297
+ 'zero-replace': 'Replace CSN v0.1.0 value in $(PROP) by $(VALUE)',
298
+ },
299
+
300
+ 'syntax-expecting-object': 'Expecting object for property $(PROP)',
301
+ 'syntax-expecting-column': 'Expecting object or string \'*\' for property $(PROP)',
302
+ 'syntax-expecting-natnum': 'Expecting non-negative number for property $(PROP)',
303
+ 'syntax-expecting-cardinality': 'Expecting non-negative number or string \'*\' for property $(PROP)',
304
+ 'syntax-expecting-reference': 'Expecting non-empty string or object for property $(PROP)',
305
+ 'syntax-expecting-term': 'Expecting non-empty string or object for property $(PROP)',
306
+ // 'syntax-expecting-string', 'syntax-expecting-boolean' (Warning), 'syntax-expecting-scalar',
307
+ // 'syntax-expecting-args', 'syntax-expecting-translation', 'syntax-expecting-array'
308
+ 'syntax-incomplete-array': { // location at ']'
309
+ std: 'Expecting array in $(PROP) to have at least $(N) items',
310
+ one: 'Expecting array in $(PROP) to have at least one item',
311
+ suffix: 'With sibling property $(SIBLINGPROP), expecting array in $(PROP) to have at least one item',
312
+ },
313
+ 'syntax-incomplete-object': { // location at '}'
314
+ std: 'Object in $(PROP) must have at least one valid CSN property',
315
+ as: 'Object in $(PROP) must have at least one valid CSN property other than $(OTHERPROP)',
316
+ },
317
+ // 'syntax-invalid-ref' (Warning?), 'syntax-invalid-kind', 'syntax-invalid-literal' (Warning)
318
+ 'syntax-invalid-string': {
319
+ std: 'Invalid string value in property $(PROP)',
320
+ },
321
+ 'syntax-missing-property': { // location at sibling or '}' otherwise
322
+ std: 'Object in $(PARENTPROP) must have the property $(PROP)',
323
+ sibling: 'Object with property $(SIBLINGPROP) must also have a property $(PROP)',
324
+ columns: 'Object in $(PARENTPROP) must have an expression property like $(PROP)',
325
+ extensions: 'Object in $(PARENTPROP) must have the property $(PROP) or $(OTHERPROP)',
326
+ },
327
+ 'syntax-unexpected-property': {
328
+ std: 'Unexpected CSN property $(PROP)',
329
+ sibling: 'CSN property $(PROP) is not expected in an object with property $(SIBLINGPROP)',
330
+ prop: 'CSN property $(PROP) is not expected in $(PARENTPROP)',
331
+ top: 'CSN property $(PROP) is not expected top-level',
332
+ kind: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
333
+ extend: 'CSN property $(PROP) is not expected by an extend in $(PARENTPROP)',
334
+ annotate: 'CSN property $(PROP) is not expected by an annotate in $(PARENTPROP)',
335
+ },
336
+ // 'syntax-unknown-property' (Warning? Better configurable Error)
337
+
270
338
  'syntax-unknown-escape': 'Unknown escape sequence $(CODE)',
271
339
  'syntax-invalid-escape': {
272
340
  std: 'Invalid escape sequence $(CODE)',
273
341
  octal: 'Octal escape sequences are not supported. Use unicode escapes instead',
274
342
  whitespace: 'Unknown escape sequence: Can\'t escape whitespace',
275
343
  codepoint: 'Undefined code-point for $(CODE)',
276
- 'unicode-hex': 'Expected hexadecimal numbers for unicode escape but found $(CODE)',
277
- 'hex-count': 'Expected $(NUMBER) hexadecimal numbers for escape sequence but found $(CODE)',
344
+ 'unicode-hex': 'Expecting hexadecimal numbers for unicode escape but found $(CODE)',
345
+ 'hex-count': 'Expecting $(NUMBER) hexadecimal numbers for escape sequence but found $(CODE)',
278
346
  'unicode-brace': 'Missing closing brace for unicode escape sequence',
279
347
  'language-identifier': 'Escape sequences in text-block\'s language identifier are not allowed',
280
348
  },
@@ -282,41 +350,34 @@ const centralMessageTexts = {
282
350
  std: 'Missing escape. Replace $(CODE) with $(NEWCODE)',
283
351
  placeholder: 'Placeholders are not supported. Replace $(CODE) with $(NEWCODE)',
284
352
  },
285
- 'syntax-expected-integer': {
353
+ 'syntax-expecting-integer': {
286
354
  std: 'A safe integer is expected here',
287
355
  normal: 'An integer number is expected here',
288
356
  unsafe: 'The provided integer is too large',
289
357
  },
290
- 'syntax-duplicate-argument': {
358
+ 'syntax-duplicate-argument': { // TODO: also CDL
291
359
  std: 'Unexpected argument $(CODE)',
292
- unknown: 'Unknown argument $(CODE)',
360
+ unknown: 'Unknown argument $(CODE)', // huh?
293
361
  duplicate: 'Duplicate argument $(CODE)',
294
362
  },
295
- 'syntax-duplicate-annotate': 'You can\'t refer to $(NAME) repeatedly with property $(PROP) in the same annotate statement',
296
- 'syntax-duplicate-extend': {
297
- std: 'You can\'t define and refer to $(NAME) repeatedly in the same extend statement',
298
- define: 'You can\'t refer to $(NAME) in the same extend statement where it was defined',
299
- extend: 'You can\'t refer to $(NAME) repeatedly in the same extend statement',
300
- },
301
- 'syntax-invalid-literal': {
302
- 'std': 'Invalid literal',
303
- 'uneven-hex': 'A binary literal must have an even number of characters',
304
- 'invalid-hex': 'A binary literal must only contain characters 0-9, a-f and A-F',
305
- 'time': 'Expected time\'hh:mm:ss\' where hh, mm and the optional ss are numbers',
306
- 'date': 'Expected date\'YYYY-MM-DD\' where YYYY, MM and DD are numbers',
307
- 'timestamp': 'Expected timestamp\'YYYY-MM-DD hh:mm:ss.u…u\' where YYYY, MM, DD, hh, mm, ss and u are numbers (optional 1-7×u)',
308
- },
309
363
  'syntax-unexpected-null': 'Keyword $(KEYWORD) must appear after the enum definition and not before',
310
364
  'syntax-unexpected-vocabulary': {
311
365
  std: 'Annotations can\'t be defined inside contexts or services',
312
366
  service: 'Annotations can\'t be defined inside services',
313
367
  context: 'Annotations can\'t be defined inside contexts',
314
368
  },
369
+ 'syntax-fragile-ident': '$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it',
370
+ 'syntax-unsupported-field': 'Calculated fields are not supported, yet',
371
+
372
+ // Syntax messages for errorneous references ----------------------------------
373
+ // location at errorneous reference (if possible)
374
+ 'ref-unexpected-self': 'Unexpected $(ID) reference; is valid only in ON-conditions',
315
375
  'ref-undefined-def': {
316
376
  std: 'Artifact $(ART) has not been found',
317
377
  // TODO: proposal 'No definition of $(NAME) found',
318
- element: 'Artifact $(ART) has no element $(MEMBER)'
378
+ element: 'Artifact $(ART) has no element $(MEMBER)',
319
379
  },
380
+ 'ref-undefined-param': 'Entity $(ART) has no parameter $(ID)',
320
381
  'ref-undefined-art': 'No artifact has been found with name $(NAME)',
321
382
  // TODO: proposal 'No definition found for $(NAME)',
322
383
  'ref-undefined-element': {
@@ -324,6 +385,7 @@ const centralMessageTexts = {
324
385
  element: 'Artifact $(ART) has no element $(MEMBER)',
325
386
  aspect: 'Element $(ID) has not been found in the anonymous target aspect'
326
387
  },
388
+ 'ref-undefined-var': 'Element or variable $(ID) has not been found',
327
389
  'ref-unknown-var': {
328
390
  std: 'No replacement found for special variable $(ID)'
329
391
  },
@@ -416,7 +478,8 @@ const centralMessageTexts = {
416
478
  'expected-target': 'An entity or an aspect is expected here',
417
479
  'extend-columns': 'Artifact $(ART) can\'t be extended with columns, only projections can',
418
480
  'extend-repeated-intralayer': 'Unstable element order due to repeated extensions in same layer',
419
-
481
+ 'extend-unexpected-include': 'Can\'t extend $(KIND) with includes',
482
+ 'ref-expecting-bare-aspect': 'An aspect without elements is expected here',
420
483
 
421
484
  'ext-duplicate-extend-type': 'Duplicate type extension for type $(TYPE)',
422
485
  'ext-duplicate-extend-type-unrelated-layer': 'Duplicate type extension for type $(TYPE)',
@@ -424,9 +487,9 @@ const centralMessageTexts = {
424
487
  std: 'Property $(PROP) can only be extended',
425
488
  'new-prop': 'Property $(PROP) can only be extended, not added',
426
489
  // eslint-disable-next-line max-len
427
- smaller: 'Property $(PROP) can only be extended, but $(LITERAL) is smaller than $(NUMBER) of type definition',
490
+ smaller: 'Property $(PROP) can only be extended, but $(VALUE) is smaller than $(NUMBER) of type definition',
428
491
  // eslint-disable-next-line max-len
429
- 'ext-smaller': 'Property $(PROP) can only be extended, but extended value $(LITERAL) is smaller than $(NUMBER) from previous type extension',
492
+ 'ext-smaller': 'Property $(PROP) can only be extended, but extended value $(VALUE) is smaller than $(NUMBER) from previous type extension',
430
493
  prop: 'Type property $(PROP) can\'t be extended',
431
494
  scale: 'If property $(PROP) is increased, then so must $(OTHERPROP)',
432
495
  string: 'Only numerical properties can be extended, but found string for $(PROP)',
@@ -437,6 +500,10 @@ const centralMessageTexts = {
437
500
  inferred: 'Only direct types can be extended',
438
501
  },
439
502
 
503
+ 'query-undefined-element': {
504
+ std: 'Element $(ID) has not been found in the elements of the query',
505
+ redirected: 'Element $(ID) has not been found in the elements of the query; please use REDIRECTED TO with an explicit ON-condition',
506
+ },
440
507
  'query-unexpected-assoc-hdbcds': 'Publishing a managed association in a view is not possible for “hdbcds” naming mode',
441
508
  'query-unexpected-structure-hdbcds': 'Publishing a structured element in a view is not possible for “hdbcds” naming mode',
442
509
  'query-ignoring-param-nullability': {
@@ -530,4 +597,6 @@ const centralMessageTexts = {
530
597
  * Don't set this property directly! Append to object oldMessageIds instead!
531
598
  */
532
599
 
600
+ //console.log('FOO')
601
+
533
602
  module.exports = { centralMessages, centralMessageTexts, oldMessageIds };
@@ -42,9 +42,10 @@ function hasErrors( messages ) {
42
42
  * @param {string} moduleName
43
43
  * @returns {boolean}
44
44
  */
45
- function hasNonDowngradableErrors( messages, moduleName ) {
46
- return messages && messages.some( m => m.severity === 'Error' &&
47
- (!m.messageId || !isDowngradable( m.messageId, moduleName )));
45
+ function hasNonDowngradableErrors( messages, moduleName, deprecatedDowngradable ) {
46
+ return messages &&
47
+ messages.some( m => m.severity === 'Error' &&
48
+ !isDowngradable( m.messageId, moduleName, deprecatedDowngradable ));
48
49
  }
49
50
 
50
51
  /**
@@ -54,10 +55,11 @@ function hasNonDowngradableErrors( messages, moduleName ) {
54
55
  *
55
56
  * @param {string} messageId
56
57
  * @param {string} moduleName
58
+ * @param {boolean} deprecatedDowngradable
57
59
  * @returns {boolean}
58
60
  */
59
- function isDowngradable( messageId, moduleName ) {
60
- if (!centralMessages[messageId])
61
+ function isDowngradable( messageId, moduleName, deprecatedDowngradable ) {
62
+ if (!messageId || !centralMessages[messageId])
61
63
  return false;
62
64
 
63
65
  const msg = centralMessages[messageId];
@@ -66,10 +68,12 @@ function isDowngradable( messageId, moduleName ) {
66
68
  // the module, it is NEVER downgradable.
67
69
  if (msg.errorFor && msg.errorFor.includes(moduleName))
68
70
  return false;
69
-
70
- return (msg.severity !== 'Error' ||
71
- msg.configurableFor === true || // useful with error for syntax variants
72
- msg.configurableFor && msg.configurableFor.includes( moduleName ));
71
+ if (msg.severity !== 'Error')
72
+ return true;
73
+ const { configurableFor } = msg;
74
+ return (Array.isArray( configurableFor ))
75
+ ? configurableFor.includes( moduleName )
76
+ : configurableFor && (configurableFor !== 'deprecated' || deprecatedDowngradable);
73
77
  }
74
78
 
75
79
  /**
@@ -173,7 +177,7 @@ const severitySpecs = {
173
177
  * @param {boolean} deprecatedDowngradable
174
178
  * @returns {MessageSeverity}
175
179
  */
176
- function reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable ) {
180
+ function reclassifiedSeverity( msg, options, moduleName, deprecatedDowngradable ) {
177
181
  const spec = centralMessages[msg.messageId] || { severity: msg.severity, configurableFor: null, errorFor: null };
178
182
  if (spec.severity === 'Error') {
179
183
  const { configurableFor } = spec;
@@ -350,7 +354,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
350
354
 
351
355
  messages.push( msg );
352
356
  hasNewError = hasNewError || msg.severity === 'Error' &&
353
- !(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
357
+ !(options.testMode && isDowngradable( msg.messageId, moduleName, deprecatedDowngradable ));
354
358
  if (!hasMessageArray)
355
359
  console.error( messageString( msg ) );
356
360
  return msg;
@@ -496,7 +500,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
496
500
  if (!messages || !messages.length)
497
501
  return;
498
502
  const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
499
- if (hasError( messages, moduleName ))
503
+ if (hasError( messages, moduleName, deprecatedDowngradable ))
500
504
  throw new CompilationError( messages, options.attachValidNames && model );
501
505
  }
502
506
 
@@ -514,7 +518,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
514
518
  // Re-set the module regardless of severity, since we reclassified it.
515
519
  Object.defineProperty( msg, '$module', { value: moduleName, configurable: true } );
516
520
  hasNewError = hasNewError || severity === 'Error' &&
517
- !(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
521
+ !(options.testMode && isDowngradable( msg.messageId, moduleName, deprecatedDowngradable ));
518
522
  }
519
523
  }
520
524
  }
@@ -602,7 +606,7 @@ function _check$Severities( id, moduleName, severity ) {
602
606
  return;
603
607
  }
604
608
  // now try whether the message could be something less than an Error in the module due to user wishes
605
- if (!isDowngradable( id, moduleName )) { // always an error in module
609
+ if (!isDowngradable( id, moduleName, true )) { // always an error in module
606
610
  if (severity !== 'Error')
607
611
  throw new CompilerAssertion( `Inconsistent severity: Expecting "Error", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
608
612
  }
@@ -636,14 +640,12 @@ function _check$Texts( id, prop, value ) {
636
640
  throw new CompilerAssertion( `Different texts for the same message ID. Expecting "${expected}", not "${value}" for ID "${id}" and text variant "${prop}"`);
637
641
  }
638
642
 
639
- const quote = { // could be an option in the future
640
- name: n => `“${ n }”`,
641
- prop: p => `‘${ p }’`,
642
- file: f => `‘${ f }’`,
643
- code: c => `«${ c }»`,
644
- meta: m => `‹${ m }›`,
645
- // TODO: probably use keyword as function name, but its name would not have length 4 :-(
646
- word: w => w.toUpperCase(), // keyword
643
+ const quote = { // could be an option in the future
644
+ double: p => `“${ p }”`, // for names, including annotation names (with preceeding `@`)
645
+ single: p => `‘${ p }’`, // for other things cited from the model
646
+ angle: p => `‹${ p }›`, // for tokens like ‹Identifier›, and similar
647
+ number: p => p, // for numbers like 42
648
+ upper: p => p.toUpperCase(), // for keywords reported by ANTLR, use prop.single in v4
647
649
  }
648
650
 
649
651
  const paramsTransform = {
@@ -651,33 +653,38 @@ const paramsTransform = {
651
653
  name: quoted,
652
654
  id: quoted,
653
655
  alias: quoted,
654
- anno: a => (a.charAt(0) === '@' ? quote.name( a ) : quote.name( '@' + a )),
655
- delimited: n => '![' + n + ']',
656
- file: quote.file,
657
- prop: quote.prop,
658
- otherprop: quote.prop,
659
- code: quote.code,
660
- newcode: quote.code,
661
- kind: quote.meta,
662
- keyword: quote.word,
656
+ anno: a => (a.charAt(0) === '@' ? quote.double( a ) : quote.double( '@' + a )),
657
+ annos: anno => anno.map(paramsTransform.anno).join(', '),
658
+ delimited: n => '![' + n + ']', // TODO: use quote.single around?
659
+ file: quote.single,
660
+ prop: quote.single,
661
+ siblingprop: quote.single,
662
+ parentprop: quote.single,
663
+ otherprop: quote.single,
664
+ code: quote.single,
665
+ newcode: quote.single,
666
+ kind: quote.angle, // consider using text variants instead
667
+ keyword: quote.upper,
663
668
  // more complex convenience:
664
669
  names: transformManyWith( quoted ),
665
- number: n => n,
666
- line: l => l,
667
- col: c => c,
668
- literal: l => l,
670
+ number: quote.number,
671
+ line: quote.number,
672
+ col: quote.number,
673
+ value: n => (typeof n !== 'number' ? quote.single( n ) : quote.number( n )),
674
+ othervalue: n => (typeof n !== 'number' ? quote.single( n ) : quote.number( n )),
669
675
  art: transformArg,
670
676
  service: transformArg,
671
677
  sorted_arts: transformManyWith( transformArg, true ),
672
678
  target: transformArg,
679
+ source: transformArg,
673
680
  elemref: transformElementRef,
674
681
  type: transformArg,
675
682
  offending: tokenSymbol,
676
- op: quote.prop,
683
+ op: quote.single,
677
684
  expecting: transformManyWith( tokenSymbol ),
678
685
  // msg: m => m,
679
686
  $reviewed: ignoreTextTransform,
680
- version: quote.meta,
687
+ version: quote.single, // TODO delete: just use for OData $(VERSION), with version: 2.0
681
688
  };
682
689
 
683
690
  function ignoreTextTransform() {
@@ -700,20 +707,20 @@ function transformManyWith( t, sorted ) {
700
707
  }
701
708
 
702
709
  function quoted( name ) {
703
- return (name) ? quote.name( name ) : '<?>'; // TODO: failure in --test-mode, then remove
710
+ return (name) ? quote.double( name ) : quote.angle( '?' ); // TODO: failure in --test-mode, then remove
704
711
  }
705
712
 
706
713
  function tokenSymbol( token ) {
707
714
  if (token.match( /^[A-Z][A-Z]/ )) // keyword
708
- return quote.word( token );
715
+ return quote.upper( token );
709
716
  else if (token.match( /^[A-Z][a-z]/ )) // Number, Identifier, ...
710
- return quote.meta( token );
717
+ return quote.angle( token );
711
718
  if (token.startsWith("'") && token.endsWith("'")) // operator token symbol
712
- return quote.prop( token.slice( 1, -1 ));
719
+ return quote.single( token.slice( 1, -1 ));
713
720
  else if (token === '<EOF>')
714
- return quote.meta( token.slice( 1, -1 ) );
721
+ return quote.angle( 'EOF' );
715
722
  else
716
- return quote.code( token ); // should not happen
723
+ return quote.single( token ); // should not happen
717
724
  }
718
725
 
719
726
  /**
@@ -1107,7 +1114,7 @@ function shortArtName( art ) {
1107
1114
  const { name } = art;
1108
1115
  if ([ 'select', 'action', 'alias', 'param' ].every( n => name[n] == null || name[n] === 1 ) &&
1109
1116
  !name.absolute.includes(':'))
1110
- return quote.name( name.element ? `${ name.absolute }:${ name.element }` : name.absolute );
1117
+ return quote.double( name.element ? `${ name.absolute }:${ name.element }` : name.absolute );
1111
1118
  return artName( art );
1112
1119
  }
1113
1120
 
@@ -1230,11 +1237,15 @@ function constructSemanticLocationFromCsnPath(csnPath, model) {
1230
1237
 
1231
1238
  let { query } = analyseCsnPath(csnPath, model, false);
1232
1239
 
1233
- // remove definitions
1234
- csnPath.shift();
1240
+ const dictName = csnPath.shift();
1241
+ const dict = model[dictName];
1235
1242
  const artifactName = csnPath.shift();
1236
- let currentThing = model.definitions[artifactName];
1237
- let result = `${ (currentThing && currentThing.kind) ? currentThing.kind : 'artifact' }:${ _quoted(artifactName) }`;
1243
+ let currentThing = dict ? dict[artifactName] : undefined;
1244
+ let result = `${ (currentThing?.kind)
1245
+ ? currentThing.kind
1246
+ : (dictName === 'vocabularies'
1247
+ ? 'annotation'
1248
+ : 'artifact') }:${ _quoted(artifactName) }`;
1238
1249
 
1239
1250
  if (!currentThing)
1240
1251
  return result;
package/lib/base/model.js CHANGED
@@ -28,6 +28,7 @@ const availableBetaFlags = {
28
28
  ignoreAssocPublishingInUnion: true,
29
29
  enableUniversalCsn: true,
30
30
  postgres: true,
31
+ aspectWithoutElements: true,
31
32
  // disabled by --beta-mode
32
33
  nestedServices: false,
33
34
  odataOpenType: true,
@@ -22,7 +22,7 @@ function checkActionOrFunction(art, artName, prop, path) {
22
22
 
23
23
  const serviceName = this.csnUtils.getServiceName(artName);
24
24
  if (!serviceName)
25
- this.warning(null, path, {}, `Functions and actions must be declared in a service`);
25
+ this.warning(null, path, {}, 'Functions and actions must be declared in a service');
26
26
 
27
27
  if (art.kind === 'entity') {
28
28
  for (const [ actName, act ] of Object.entries(art.actions)) {
@@ -17,7 +17,7 @@ function validateCdsPersistenceAnnotation(artifact, artifactName, prop, path) {
17
17
  // TODO: Why not filter over persistenceAnnos, is shorter!
18
18
  const TableUdfCv = Object.keys(artifact).filter(p => persistenceAnnos.includes(p) && artifact[p]);
19
19
  if (TableUdfCv.length > 1)
20
- this.error(null, path, {}, `Annotations ${ TableUdfCv.join(', ') } can't be used in combination`);
20
+ this.error(null, path, { annos: TableUdfCv }, 'Annotations $(ANNOS) can\'t be used in combination');
21
21
  }
22
22
  }
23
23
 
@@ -12,26 +12,30 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
12
12
  */
13
13
  function checkForHanaTypes(parent, name, type, path) {
14
14
  const artifact = this.csn.definitions[path[1]];
15
- if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && typeof parent.type === 'string' && parent.type.startsWith('cds.hana.'))
16
- this.error('ref-unexpected-hana-type', [ ...path, 'type' ], {}, `Types in the ”cds.hana“ namespace can't be used with sqlDialect “postgres”`);
15
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && typeof parent.type === 'string' && parent.type.startsWith('cds.hana.')) {
16
+ this.error('ref-unexpected-hana-type', [ ...path, 'type' ], { type: 'cds.hana', value: this.options.sqlDialect },
17
+ 'Types in the $(TYPE) namespace can\'t be used with sqlDialect $(VALUE)');
18
+ }
17
19
  }
18
20
 
19
21
  /**
20
- * Check that `cds.UInt8`is not used - we don't have a clear idea how to represent it on postgres
22
+ * Check that `cds.UInt8` is not used - we don't have a clear idea how to represent it on postgres and h2
21
23
  *
22
24
  * @param {object} parent Object with a type
23
25
  * @param {string} name Name of the type property on parent
24
26
  * @param {Array} type type to check
25
27
  * @param {CSN.Path} path
26
28
  */
27
- function checkForUInt8(parent, name, type, path) {
29
+ function CheckForUInt8(parent, name, type, path) {
28
30
  const artifact = this.csn.definitions[path[1]];
29
- if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && parent.type === 'cds.UInt8')
30
- this.error('ref-unexpected-type', [ ...path, 'type' ], {}, `Type cds.UInt8“ can't be used with sqlDialect “postgres”`);
31
+ if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && parent.type === 'cds.UInt8') {
32
+ this.error('ref-unexpected-type', [ ...path, 'type' ], { type: 'cds.UInt8', value: this.options.sqlDialect },
33
+ 'Type $(TYPE) can\'t be used with sqlDialect $(VALUE)');
34
+ }
31
35
  }
32
36
 
33
37
  /**
34
- * Check types - specifically for postgres
38
+ * Check types - specifically for postgres and h2
35
39
  *
36
40
  * @param {object} parent Object with a type
37
41
  * @param {string} name Name of the type property on parent
@@ -40,7 +44,8 @@ function checkForUInt8(parent, name, type, path) {
40
44
  */
41
45
  function checkTypes(parent, name, type, path) {
42
46
  checkForHanaTypes.bind(this)(parent, name, type, path);
43
- checkForUInt8.bind(this)(parent, name, type, path);
47
+ if (this.options.sqlDialect === 'postgres' || this.options.sqlDialect === 'h2')
48
+ CheckForUInt8.bind(this)(parent, name, type, path);
44
49
  }
45
50
 
46
51
  module.exports = {
@@ -20,8 +20,10 @@ function validateDefaultValues(member, memberName, prop, path) {
20
20
  // consume all unary signs
21
21
  while (member.default.xpr[i] === '-' || member.default.xpr[i] === '+')
22
22
  i++;
23
+ // TODO: This check only counts the number of leading signs, not inbetween (e.g. 1 - - 1).
24
+ // The message also needs to be improved.
23
25
  if (i > 1)
24
- this.error(null, path, {}, `Illegal number of unary '+/-' operators`);
26
+ this.error(null, path, {}, 'Illegal number of unary ‘+’/‘-’ operators');
25
27
  }
26
28
  }
27
29
  }
@@ -93,7 +93,7 @@ function checkPrimaryKey(art) {
93
93
  function checkVirtualElement(member) {
94
94
  if (member.virtual) {
95
95
  if (this.csnUtils.isAssociation(member.type)) { // or Composition ???
96
- this.error(null, member.$path, {}, `Element can't be virtual and an association`);
96
+ this.error(null, member.$path, {}, 'Element can\'t be virtual and an association');
97
97
  }
98
98
  }
99
99
  }