@sap/cds-compiler 4.7.6 → 4.9.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 (129) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +28 -1
  6. package/bin/cdshi.js +13 -3
  7. package/doc/CHANGELOG_BETA.md +24 -1
  8. package/lib/api/main.js +119 -46
  9. package/lib/api/options.js +51 -0
  10. package/lib/api/validate.js +1 -5
  11. package/lib/base/builtins.js +116 -0
  12. package/lib/base/keywords.js +5 -1
  13. package/lib/base/location.js +91 -14
  14. package/lib/base/message-registry.js +76 -46
  15. package/lib/base/messages.js +121 -35
  16. package/lib/base/model.js +4 -7
  17. package/lib/checks/actionsFunctions.js +3 -3
  18. package/lib/checks/annotationsOData.js +3 -0
  19. package/lib/checks/defaultValues.js +5 -2
  20. package/lib/checks/elements.js +2 -1
  21. package/lib/checks/enricher.js +2 -2
  22. package/lib/checks/queryNoDbArtifacts.js +5 -3
  23. package/lib/checks/utils.js +1 -1
  24. package/lib/checks/validator.js +8 -56
  25. package/lib/compiler/assert-consistency.js +11 -7
  26. package/lib/compiler/builtins.js +0 -74
  27. package/lib/compiler/checks.js +105 -29
  28. package/lib/compiler/define.js +37 -25
  29. package/lib/compiler/extend.js +35 -12
  30. package/lib/compiler/index.js +9 -10
  31. package/lib/compiler/lsp-api.js +5 -0
  32. package/lib/compiler/populate.js +13 -5
  33. package/lib/compiler/propagator.js +24 -18
  34. package/lib/compiler/resolve.js +47 -45
  35. package/lib/compiler/shared.js +61 -21
  36. package/lib/compiler/tweak-assocs.js +15 -90
  37. package/lib/compiler/utils.js +3 -3
  38. package/lib/compiler/xpr-rewrite.js +689 -0
  39. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  40. package/lib/edm/annotations/edmJson.js +7 -5
  41. package/lib/edm/annotations/genericTranslation.js +149 -71
  42. package/lib/edm/csn2edm.js +25 -9
  43. package/lib/edm/edm.js +7 -7
  44. package/lib/edm/edmInboundChecks.js +57 -5
  45. package/lib/edm/edmPreprocessor.js +54 -25
  46. package/lib/edm/edmUtils.js +3 -16
  47. package/lib/gen/Dictionary.json +138 -14
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +1 -1
  50. package/lib/gen/languageParser.js +2085 -1989
  51. package/lib/json/csnVersion.js +7 -4
  52. package/lib/json/from-csn.js +21 -11
  53. package/lib/json/to-csn.js +8 -4
  54. package/lib/language/antlrParser.js +1 -1
  55. package/lib/language/genericAntlrParser.js +23 -16
  56. package/lib/language/multiLineStringParser.js +2 -2
  57. package/lib/language/textUtils.js +1 -1
  58. package/lib/main.d.ts +90 -14
  59. package/lib/main.js +9 -1
  60. package/lib/model/cloneCsn.js +21 -9
  61. package/lib/model/csnRefs.js +153 -42
  62. package/lib/model/csnUtils.js +14 -11
  63. package/lib/model/enrichCsn.js +4 -2
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/model/sortViews.js +14 -6
  66. package/lib/modelCompare/compare.js +135 -122
  67. package/lib/optionProcessor.js +49 -2
  68. package/lib/render/DuplicateChecker.js +6 -6
  69. package/lib/render/manageConstraints.js +1 -0
  70. package/lib/render/toCdl.js +6 -3
  71. package/lib/render/toHdbcds.js +4 -48
  72. package/lib/render/toSql.js +6 -3
  73. package/lib/transform/addTenantFields.js +58 -35
  74. package/lib/transform/db/applyTransformations.js +34 -1
  75. package/lib/transform/db/constraints.js +1 -1
  76. package/lib/transform/db/expansion.js +11 -3
  77. package/lib/transform/db/flattening.js +71 -46
  78. package/lib/transform/db/groupByOrderBy.js +2 -2
  79. package/lib/transform/db/temporal.js +6 -3
  80. package/lib/transform/db/transformExists.js +2 -2
  81. package/lib/transform/db/views.js +1 -4
  82. package/lib/transform/effective/annotations.js +194 -0
  83. package/lib/transform/effective/main.js +11 -10
  84. package/lib/transform/effective/misc.js +45 -14
  85. package/lib/transform/effective/types.js +4 -3
  86. package/lib/transform/forOdata.js +29 -12
  87. package/lib/transform/forRelationalDB.js +104 -113
  88. package/lib/transform/localized.js +7 -6
  89. package/lib/transform/odata/flattening.js +228 -107
  90. package/lib/transform/odata/toFinalBaseType.js +10 -26
  91. package/lib/transform/odata/typesExposure.js +41 -25
  92. package/lib/transform/parseExpr.js +4 -7
  93. package/lib/transform/transformUtils.js +50 -43
  94. package/lib/transform/translateAssocsToJoins.js +48 -48
  95. package/lib/transform/universalCsn/coreComputed.js +2 -1
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  97. package/package.json +2 -2
  98. package/share/messages/README.md +4 -0
  99. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  100. package/share/messages/anno-missing-rewrite.md +45 -0
  101. package/share/messages/check-proper-type-of.md +1 -1
  102. package/share/messages/def-duplicate-autoexposed.md +1 -1
  103. package/share/messages/extend-repeated-intralayer.md +3 -16
  104. package/share/messages/extend-unrelated-layer.md +1 -1
  105. package/share/messages/message-explanations.json +2 -0
  106. package/share/messages/redirected-to-ambiguous.md +1 -1
  107. package/share/messages/redirected-to-complex.md +1 -1
  108. package/share/messages/redirected-to-unrelated.md +1 -1
  109. package/share/messages/rewrite-not-supported.md +1 -1
  110. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  111. package/share/messages/type-missing-enum-value.md +59 -0
  112. package/share/messages/wildcard-excluding-one.md +1 -1
  113. package/bin/.eslintrc.json +0 -17
  114. package/lib/api/.eslintrc.json +0 -37
  115. package/lib/checks/.eslintrc.json +0 -31
  116. package/lib/compiler/.eslintrc.json +0 -8
  117. package/lib/edm/.eslintrc.json +0 -46
  118. package/lib/inspect/.eslintrc.json +0 -4
  119. package/lib/json/.eslintrc.json +0 -4
  120. package/lib/language/.eslintrc.json +0 -4
  121. package/lib/model/.eslintrc.json +0 -13
  122. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  123. package/lib/render/.eslintrc.json +0 -22
  124. package/lib/transform/.eslintrc.json +0 -13
  125. package/lib/transform/db/.eslintrc.json +0 -41
  126. package/lib/transform/draft/.eslintrc.json +0 -4
  127. package/lib/transform/effective/.eslintrc.json +0 -4
  128. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  129. package/lib/utils/.eslintrc.json +0 -7
@@ -66,10 +66,9 @@
66
66
 
67
67
  'use strict';
68
68
 
69
+ const { Location } = require('../base/location');
69
70
  const { locationString, hasErrors } = require('../base/messages');
70
- const {
71
- XsnSource, XsnName, XsnArtifact, CsnLocation,
72
- } = require('./classes');
71
+ const { XsnSource, XsnName, XsnArtifact } = require('./xsn-model');
73
72
 
74
73
 
75
74
  // Properties that can appear where a type can have type arguments.
@@ -130,14 +129,18 @@ function assertConsistency( model, stage ) {
130
129
  ],
131
130
  instanceOf: XsnSource,
132
131
  },
132
+ tokenIndex: { test: isNumber },
133
133
  location: {
134
134
  // every thing with a $location in CSN must have a XSN location even
135
135
  // with syntax errors (currently even internal artifacts like $using):
136
136
  isRequired: parent => noSyntaxErrors() || parent && parent.kind,
137
137
  kind: true,
138
- instanceOf: CsnLocation,
138
+ instanceOf: Location,
139
139
  requires: [ 'file' ], // line is optional in top-level location
140
- optional: [ 'line', 'col', 'endLine', 'endCol', '$notFound' ],
140
+ optional: [
141
+ 'line', 'col', 'endLine', 'endCol', '$notFound',
142
+ 'tokenIndex', // in parser for $lsp
143
+ ],
141
144
  schema: {
142
145
  line: { test: isNumber },
143
146
  col: { test: isNumber },
@@ -233,7 +236,7 @@ function assertConsistency( model, stage ) {
233
236
  test: (model.$frontend !== 'json') ? standard : TODO,
234
237
  // TODO: the JSON parser should augment 'namespace' correctly or better: hide it
235
238
  requires: [ 'location' ],
236
- optional: [ 'path' ],
239
+ optional: [ 'kind', 'name' ],
237
240
  },
238
241
  usings: {
239
242
  test: isArray(),
@@ -317,7 +320,6 @@ function assertConsistency( model, stage ) {
317
320
  'kind', 'name', '_block', '_parent', '_main', 'elements',
318
321
  '_effectiveType', '$effectiveSeqNo', '_origin', '_joinParent', '$joinArgsIndex',
319
322
  '$duplicates', // duplicate query in FROM clause
320
- '$inferred', // table alias with $inferred: '$internal'
321
323
  ],
322
324
  },
323
325
  none: { optional: () => true }, // parse error
@@ -517,11 +519,13 @@ function assertConsistency( model, stage ) {
517
519
  '_effectiveType', '$effectiveSeqNo', '_origin', '_deps',
518
520
  // CSN parser may let these properties slip through to XSN, even if input is invalid.
519
521
  'args', 'op', 'func', 'suffix',
522
+ '$invalidPaths',
520
523
  ],
521
524
  // TODO: name requires if not in parser?
522
525
  },
523
526
  $priority: { test: isOneOf( [ undefined, false, 'extend', 'annotate' ] ) },
524
527
  $annotations: { parser: true, kind: true, test: TODO }, // deprecated, still there for cds-lsp
528
+ $invalidPaths: { test: isBoolean },
525
529
  name: {
526
530
  isRequired: stageParser && (() => false), // not required in parser
527
531
  kind: true,
@@ -64,18 +64,6 @@ const typeParameters = {
64
64
  // a.k.a "typeProperties"
65
65
  typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
66
66
 
67
- /**
68
- * Properties that are required next to `=` to make an annotation value an actual expression
69
- * and not some foreign structure.
70
- *
71
- * @type {string[]}
72
- */
73
- const xprInAnnoProperties = [
74
- 'ref', 'xpr', 'list', 'literal', 'val',
75
- '#', 'func', 'args', 'SELECT', 'SET',
76
- 'cast',
77
- ];
78
-
79
67
 
80
68
  // const hana = {
81
69
  // BinaryFloat: {},
@@ -87,15 +75,6 @@ const xprInAnnoProperties = [
87
75
  // hana: { kind: 'context' },
88
76
  // };
89
77
 
90
- /**
91
- * Functions without parentheses in CDL (common standard SQL-92 functions)
92
- * (do not add more - make it part of the SQL renderer to remove parentheses for
93
- * other funny SQL functions like CURRENT_UTCTIMESTAMP).
94
- */
95
- const functionsWithoutParens = [
96
- 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
97
- 'CURRENT_USER', 'SESSION_USER', 'SYSTEM_USER',
98
- ];
99
78
 
100
79
  const specialFunctions = compileFunctions( {
101
80
  '': [ // the default
@@ -317,13 +296,6 @@ function checkDate( year, month, day ) {
317
296
  return !Number.isNaN( year ) && month > 0 && month < 13 && day > 0 && day < 32;
318
297
  }
319
298
 
320
- /**
321
- * Return whether JSON object `val` is a representation for an annotation expression
322
- */
323
- function isAnnotationExpression( val ) {
324
- return val?.['='] !== undefined && xprInAnnoProperties.some( prop => val[prop] !== undefined );
325
- }
326
-
327
299
  /**
328
300
  * Check that the given time is within boundaries.
329
301
  * Checks according to ISO 8601.
@@ -371,44 +343,6 @@ function isGeoTypeName( typeName ) {
371
343
  return typeCategories.geo.includes( typeName );
372
344
  }
373
345
 
374
- /**
375
- * Checks whether the given absolute path is inside a reserved namespace.
376
- *
377
- * @param {string} absolute
378
- * @returns {boolean}
379
- */
380
- function isInReservedNamespace( absolute ) {
381
- return absolute === 'cds' || absolute.startsWith( 'cds.' ) &&
382
- !absolute.match( /^cds\.foundation(\.|$)/ ) &&
383
- !absolute.match( /^cds\.outbox(\.|$)/ ) && // Requested by Node runtime
384
- !absolute.match( /^cds\.xt(\.|$)/ ); // Requested by Mtx
385
- }
386
-
387
- /**
388
- * Tell if a type is (directly) a builtin type
389
- * Note that in CSN builtins are not in the definition of the model, so we can only
390
- * check against their absolute names. Builtin types are "cds.<something>", i.e. they
391
- * are directly in 'cds', but not for example in 'cds.foundation'.
392
- *
393
- * TODO: Handle `{ ref: [ "cds.Integer" ] }`
394
- *
395
- * @param {string} type
396
- * @returns {boolean}
397
- */
398
- function isBuiltinType( type ) {
399
- return typeof type === 'string' && isInReservedNamespace( type );
400
- }
401
-
402
- /**
403
- * Tell if a name is a magic variable
404
- *
405
- * @param {string} name
406
- * @returns {boolean}
407
- */
408
- function isMagicVariable( name ) {
409
- return typeof name === 'string' && Object.prototype.hasOwnProperty.call(magicVariables, name);
410
- }
411
-
412
346
  /**
413
347
  * Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
414
348
  * `definitions` of the XSN model as well as to `$builtins`.
@@ -424,8 +358,6 @@ function initBuiltins( model ) {
424
358
 
425
359
  // Also add the core artifacts to model.definitions`
426
360
  const c = { ...core };
427
- if (!isBetaEnabled( model.options, 'vectorType' ))
428
- delete c.Vector;
429
361
  model.$builtins = env( c, 'cds.', cds );
430
362
  model.$builtins.cds = cds;
431
363
 
@@ -541,14 +473,8 @@ function initBuiltins( model ) {
541
473
 
542
474
  module.exports = {
543
475
  typeParameters,
544
- xprInAnnoProperties,
545
- functionsWithoutParens,
546
476
  specialFunctions,
547
477
  quotedLiteralPatterns,
548
478
  initBuiltins,
549
- isAnnotationExpression,
550
- isInReservedNamespace,
551
- isBuiltinType,
552
- isMagicVariable,
553
479
  isGeoTypeName,
554
480
  };
@@ -19,6 +19,7 @@ const {
19
19
  } = require('../base/model');
20
20
  const { CompilerAssertion } = require('../base/error');
21
21
  const { typeParameters } = require('./builtins');
22
+ const { propagationRules } = require('../base/builtins');
22
23
 
23
24
  const $location = Symbol.for( 'cds.$location' );
24
25
 
@@ -29,7 +30,7 @@ const $location = Symbol.for( 'cds.$location' );
29
30
  */
30
31
  function check( model ) {
31
32
  const {
32
- error, warning, info,
33
+ error, warning, info, message,
33
34
  } = model.$messageFunctions;
34
35
 
35
36
  checkSapCommonLocale( model );
@@ -41,6 +42,7 @@ function check( model ) {
41
42
  return;
42
43
 
43
44
  function checkDefinition( def ) {
45
+ checkEvent( def );
44
46
  checkGenericConstruct( def );
45
47
  if (def.includes && def.elements)
46
48
  checkElementIncludeOverride( def );
@@ -49,6 +51,16 @@ function check( model ) {
49
51
  def.$queries.forEach( checkQuery );
50
52
  }
51
53
 
54
+ function checkEvent( def ) {
55
+ // Ensure that events are structured. Up to compiler v4, we allowed non-structured events,
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
+ if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)
61
+ message( 'def-expected-structured', [ (def.type || def.name).location, def ] );
62
+ }
63
+
52
64
  function checkAnnotationDefinition( art ) {
53
65
  // TODO: Should we check elements similar to definition-elements as well?
54
66
  checkEnumType( art );
@@ -58,14 +70,23 @@ function check( model ) {
58
70
  } );
59
71
  }
60
72
 
73
+ function* iterateAnnotations( art ) {
74
+ for (const prop in art) {
75
+ if (prop.charAt(0) === '@')
76
+ yield prop;
77
+ }
78
+ }
79
+
61
80
  function checkGenericConstruct( art ) {
62
81
  checkName( art );
63
82
  checkTypeArguments( art );
64
- if (model.vocabularies) {
65
- Object.keys( art )
66
- .filter( a => a.startsWith( '@' ) )
67
- .forEach( a => checkAnnotationAssignment1( art, art[a] ) );
68
- }
83
+
84
+ if (art.value && !art.$calcDepElement && art.type)
85
+ checkTypeCast( art.value, art );
86
+
87
+ for (const anno of iterateAnnotations( art ))
88
+ checkAnnotationAssignment1( art, art[anno] );
89
+
69
90
  checkTypeStructure( art );
70
91
  checkAssociation( art ); // type def could be assoc
71
92
  if (art.kind === 'enum')
@@ -202,13 +223,40 @@ function check( model ) {
202
223
  }
203
224
  }
204
225
 
205
- function requireExplicitTypeInSqlCast( art, user ) {
206
- if (!art.type) {
207
- error( 'expr-missing-type', [ art.location, user ], { },
226
+ /**
227
+ * Check the type in an SQL cast expression.
228
+ *
229
+ * @param xpr
230
+ * @param {XSN.Artifact} user
231
+ */
232
+ function requireExplicitTypeInSqlCast( xpr, user ) {
233
+ if (!xpr.type) {
234
+ error( 'expr-missing-type', [ xpr.location, user ], { },
208
235
  'Missing type in SQL cast function' );
209
236
  }
210
237
  }
211
238
 
239
+ function checkTypeCast( xpr, user ) {
240
+ const isCast = (xpr.op?.val === 'cast');
241
+ const elem = isCast
242
+ ? xpr.args[0]?._artifact
243
+ : xpr._artifact;
244
+ const type = isCast ? xpr.type : user.type;
245
+ if (!isCast && type.$inferred)
246
+ return; // e.g. $inferred:'generated'
247
+ if (elem && type) { // has explicit type
248
+ if (type._artifact?.elements)
249
+ error( 'type-invalid-cast', [ type.location, user ], { '#': 'to-structure' } );
250
+ else if (elem.elements) // TODO: calc elements
251
+ error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-structure' } );
252
+ else if (elem.target && !type._artifact?.target)
253
+ error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-assoc' } );
254
+ else if (!elem.target && type._artifact?.target && !user.type?.$inferred)
255
+ // $inferred already reported in resolve.js
256
+ error( 'type-invalid-cast', [ type.location, user ], { '#': 'assoc' } );
257
+ }
258
+ }
259
+
212
260
  function checkLocalizedElement( elem ) {
213
261
  if (elem.localized?.val) {
214
262
  const type = elem._effectiveType;
@@ -224,9 +272,11 @@ function check( model ) {
224
272
  // "key" keyword at localized element in SELECT list.
225
273
  // TODO: not in inferred elements, but also inside aspects
226
274
  if (elem.key?.val && elem._main?.query) {
275
+ // either the element was casted to localized (no `_origin`) or
227
276
  // original element is localized but not key, as that would have
228
277
  // already resulted in a warning by localized.js
229
- if (elem._origin?.localized?.val && !elem._origin.key?.val) {
278
+ if ((!elem._origin && elem.localized?.val) ||
279
+ (elem._origin?.localized?.val && !elem._origin.key?.val)) {
230
280
  warning( 'def-ignoring-localized', [ elem.key.location, elem ], { keyword: 'localized' },
231
281
  'Keyword $(KEYWORD) is ignored for primary keys' );
232
282
  }
@@ -340,11 +390,8 @@ function check( model ) {
340
390
  .find( name => !enumNode.enum[name].value );
341
391
  if (emptyValue) {
342
392
  const failedEnum = enumNode.enum[emptyValue];
343
- warning( 'type-missing-value', [ failedEnum.location, failedEnum ], {
393
+ message( 'type-missing-enum-value', [ failedEnum.location, failedEnum ], {
344
394
  '#': isNumeric ? 'numeric' : 'std', name: emptyValue,
345
- }, {
346
- std: 'Missing value for non-string enum element $(NAME)',
347
- numeric: 'Missing value for numeric enum element $(NAME)',
348
395
  } );
349
396
  }
350
397
  }
@@ -551,15 +598,26 @@ function check( model ) {
551
598
  if (xorElements) {
552
599
  // one of the two elements is not structured
553
600
  const prop = !elem.elements ? 'new-not-structured' : 'old-not-structured';
554
- const name = elem.name.id;
555
601
  // Position at type/struct, not name
556
602
  const loc = elem.type?.location || elem.elements?.[$location] || elem.location;
557
603
  error( 'ref-invalid-override', [ loc, elem ],
558
- { '#': prop, art: original._main, name } );
604
+ { '#': prop, art: original._main, name: elem.name.id } );
559
605
  return false;
560
606
  }
561
607
  else if (original.elements &&
562
- !checkSubStructureOverride( elem, elem.elements, original.elements )) {
608
+ !checkSubStructureOverride( elem, elem.elements, original.elements )) {
609
+ return false;
610
+ }
611
+
612
+ const xorTarget = !(elem.target || elem.targetAspect) !==
613
+ !(original.target || original.targetAspect);
614
+ if (xorTarget) {
615
+ // one of the two elements is not an association
616
+ const prop = !elem.target ? 'new-not-target' : 'old-not-target';
617
+ // Position at type/assoc, not name
618
+ const loc = elem.target?.location || elem.type?.location || elem.location;
619
+ error( 'ref-invalid-override', [ loc, elem ],
620
+ { '#': prop, art: original._main, name: elem.name.id } );
563
621
  return false;
564
622
  }
565
623
  return true;
@@ -599,6 +657,7 @@ function check( model ) {
599
657
  checkExpressionAssociationUsage( xpr, user, false );
600
658
  if (xpr.op?.val === 'cast') {
601
659
  requireExplicitTypeInSqlCast( xpr, user );
660
+ checkTypeCast( xpr, user );
602
661
  checkTypeArguments( xpr, user );
603
662
  }
604
663
  }
@@ -631,6 +690,7 @@ function check( model ) {
631
690
  // the cast, as otherwise we will check it twice (once here, once via element).
632
691
  if (elem.value?.op?.val === 'cast' && elem.type?.$inferred !== 'cast') {
633
692
  requireExplicitTypeInSqlCast( elem.value, elem );
693
+ checkTypeCast( elem.value, elem );
634
694
  checkTypeArguments( elem.value, elem );
635
695
  }
636
696
  visitSubExpression( elem.value, elem, (xpr) => {
@@ -810,21 +870,37 @@ function check( model ) {
810
870
  return false;
811
871
  }
812
872
 
873
+ /**
874
+ * Returns true if the given annotation accepts expressions as values.
875
+ *
876
+ * @param {object} anno
877
+ * @param {XSN.Artifact} art
878
+ * @returns {boolean}
879
+ */
880
+ function checkAnnotationAcceptsExpressions( anno, art ) {
881
+ const name = anno.name?.id;
882
+ if (!name)
883
+ return true;
884
+ if (!propagationRules[`@${ name }`])
885
+ return true;
886
+ error( 'anno-unexpected-expr', [ anno.location, art ], { anno: name },
887
+ 'Unexpected expression as value for $(ANNO)' );
888
+ return false;
889
+ }
813
890
 
814
- // Former checkAnnotationAssignments.js ------------------------------------
815
-
816
- // Check the annotation assignments (if any) of 'annotatable', possibly using annotation
817
- // definitions from 'model'. Report errors on 'options.messages.
818
- //
819
- // TODO: rework completely!
820
- // TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
821
-
822
- // Has been slightly adapted for model.vocabularies but comments need to be
823
- // adapted, etc.
824
891
  function checkAnnotationAssignment1( art, anno ) {
825
- if (art.$contains?.$annotation)
826
- checkAnnotationExpressions( anno, art );
892
+ if (art.$contains?.$annotation && anno.kind === '$annotation') {
893
+ if (checkAnnotationAcceptsExpressions( anno, art ))
894
+ checkAnnotationExpressions( anno, art );
895
+ }
896
+
897
+ if (!model.vocabularies)
898
+ return;
827
899
 
900
+ // Has been slightly adapted for model.vocabularies but comments need to be
901
+ // adapted, etc.
902
+ // TODO: rework completely!
903
+ // TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
828
904
  // Sanity checks (ignore broken assignments)
829
905
  if (!anno.name?.id)
830
906
  return;
@@ -140,7 +140,8 @@ const {
140
140
  targetCantBeAspect,
141
141
  } = require('./utils');
142
142
  const { compareLayer } = require('./moduleLayers');
143
- const { initBuiltins, isInReservedNamespace } = require('./builtins');
143
+ const { initBuiltins } = require('./builtins');
144
+ const { isInReservedNamespace } = require('../base/builtins');
144
145
 
145
146
  const $location = Symbol.for( 'cds.$location' );
146
147
  const $inferred = Symbol.for( 'cds.$inferred' );
@@ -212,6 +213,12 @@ function define( model ) {
212
213
  dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
213
214
 
214
215
  addI18nBlocks();
216
+
217
+ const { $self } = model.definitions;
218
+ if ($self) {
219
+ message( 'name-deprecated-$self', [ $self.location, $self ], { name: '$self' },
220
+ 'Do not use $(NAME) as name for an artifact definition' );
221
+ }
215
222
  }
216
223
 
217
224
  // Phase 1: ----------------------------------------------------------------
@@ -227,14 +234,15 @@ function define( model ) {
227
234
  if (!src.kind)
228
235
  src.kind = 'source';
229
236
 
230
- let { namespace } = src;
237
+ let namespace = src.namespace?.name;
231
238
  let prefix = '';
232
239
  if (namespace?.path && !namespace.path.broken) {
233
240
  namespace.id = pathName( namespace.path );
234
241
  prefix = `${ namespace.id }.`;
235
242
  }
236
243
  if (isInReservedNamespace( prefix )) {
237
- error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
244
+ error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
245
+ { name: 'cds' },
238
246
  'The namespace $(NAME) is reserved for CDS builtins' );
239
247
  namespace = null;
240
248
  }
@@ -522,7 +530,7 @@ function define( model ) {
522
530
  if (src.$frontend && src.$frontend !== 'cdl')
523
531
  return;
524
532
  if (src.namespace) {
525
- const decl = src.namespace;
533
+ const decl = src.namespace.name;
526
534
  if (!decl.id) // parsing may have failed
527
535
  return;
528
536
  if (!model.definitions[decl.id]) {
@@ -561,8 +569,8 @@ function define( model ) {
561
569
  initArtifactParentLink( art, model.definitions );
562
570
  const block = art._block;
563
571
  checkRedefinition( art );
564
- initMembers( art, art, block );
565
572
  initDollarSelf( art ); // $self
573
+ initMembers( art, art, block );
566
574
  if (art.params)
567
575
  initDollarParameters( art ); // $parameters
568
576
 
@@ -635,6 +643,7 @@ function define( model ) {
635
643
  name: { id: '$parameters' },
636
644
  kind: '$parameters',
637
645
  location: art.location,
646
+ deprecated: true, // hide in code completion
638
647
  };
639
648
  setLink( parameters, '_parent', art );
640
649
  setLink( parameters, '_main', art );
@@ -678,8 +687,14 @@ function define( model ) {
678
687
  setLink( self, '_origin', query );
679
688
  setLink( self, '_parent', query );
680
689
  setLink( self, '_main', query._main );
690
+
691
+ const projection = { ...self, deprecated: true }; // hide in code completion
692
+ setLink( projection, '_origin', query );
693
+ setLink( projection, '_parent', query );
694
+ setLink( projection, '_main', query._main );
695
+
681
696
  query.$tableAliases.$self = self;
682
- query.$tableAliases.$projection = self;
697
+ query.$tableAliases.$projection = projection;
683
698
  }
684
699
  initSubQuery( query ); // check for SELECT clauses after from / mixin
685
700
  }
@@ -747,7 +762,6 @@ function define( model ) {
747
762
  // We don't worry about duplicate names here.
748
763
  const id = `$_select_${ query._main.$queries.length + 1 }__`;
749
764
  table.name = { id, location: table.location, $inferred: '$internal' };
750
- table.$inferred = '$internal';
751
765
  }
752
766
  addAsAlias();
753
767
  // Store _origin to leading query of table.query for name resolution
@@ -796,7 +810,7 @@ function define( model ) {
796
810
  setMemberParent( table, table.name.id, query );
797
811
  setLink( table, '_block', query._block );
798
812
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
799
- if (tableAlias.$inferred === '$internal') {
813
+ if (tableAlias.name.$inferred === '$internal') {
800
814
  const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
801
815
  // TODO: the semanticLoc query is not initialized yet, and thus cannot
802
816
  // be used here
@@ -858,12 +872,13 @@ function define( model ) {
858
872
  }
859
873
 
860
874
  function initMixins( query, art ) {
861
- // TODO: re-check if mixins have already duplicates
862
- for (const name in query.mixin) {
863
- const mixin = query.mixin[name];
875
+ forEachInOrder( query, 'mixin', initMixin );
876
+
877
+ function initMixin( mixin, name ) {
878
+ setLink( mixin, '_block', art._block );
879
+ setMemberParent( mixin, name, query );
880
+ checkRedefinition( mixin );
864
881
  if (!(mixin.$duplicates)) {
865
- setMemberParent( mixin, name, query );
866
- setLink( mixin, '_block', art._block );
867
882
  // TODO: do some initMembers() ? If people had annotation
868
883
  // assignments on the mixin... (also for future mixin definitions
869
884
  // with generated values)
@@ -924,14 +939,14 @@ function define( model ) {
924
939
  setLink( col, '_block', parent._block );
925
940
  if (col.inline) { // `@anno elem.{ * }` does not work
926
941
  if (col.doc) {
927
- warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
942
+ message( 'syntax-ignoring-anno', [ col.doc.location, col ],
928
943
  { '#': 'doc', code: '.{ ‹inline› }' } );
929
944
  }
930
945
  // col.$annotations no available for CSN input, have to search.
931
- // Warning about first annotation should be enough to avoid spam.
946
+ // Message about first annotation should be enough to avoid spam.
932
947
  const firstAnno = Object.keys( col ).find( key => key.startsWith( '@' ) );
933
948
  if (firstAnno) {
934
- warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
949
+ message( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
935
950
  { code: '.{ ‹inline› }' } );
936
951
  }
937
952
  }
@@ -1151,7 +1166,7 @@ function define( model ) {
1151
1166
  checkRedefinition( elem );
1152
1167
  initMembers( elem, elem, bl, initExtensions );
1153
1168
  if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
1154
- initBoundSelfParam( elem.params );
1169
+ initBoundSelfParam( elem.params, elem._main );
1155
1170
 
1156
1171
  // for a correct home path, setMemberParent needed to be called
1157
1172
 
@@ -1177,27 +1192,24 @@ function define( model ) {
1177
1192
  }
1178
1193
  }
1179
1194
 
1180
- function initBoundSelfParam( params ) {
1195
+ function initBoundSelfParam( params, main ) {
1181
1196
  if (!params)
1182
1197
  return;
1183
1198
  if (boundSelfParamType === true) { // first try
1184
1199
  const def = model.definitions.$self;
1185
1200
  if (def) {
1186
- // TODO v5: bring this always, probably even as error
1187
- warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
1188
- 'Do not use $(NAME) as name for an artifact definition' );
1189
1201
  boundSelfParamType = false;
1190
1202
  return;
1191
1203
  }
1192
- const location = { file: '' };
1193
- boundSelfParamType = { name: { id: '$self', location }, location };
1204
+ boundSelfParamType = '$self';
1194
1205
  }
1195
1206
  const first = params[Object.keys( params )[0] || ''];
1196
1207
  const type = first?.type || first?.items?.type; // this sequence = no derived type
1197
1208
  const path = type?.path;
1198
1209
  if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
1199
- setLink( type, '_artifact', boundSelfParamType );
1200
- setLink( path[0], '_artifact', boundSelfParamType );
1210
+ const { $self } = main.$tableAliases;
1211
+ setLink( type, '_artifact', $self );
1212
+ setLink( path[0], '_artifact', $self );
1201
1213
  }
1202
1214
  }
1203
1215