@sap/cds-compiler 6.4.6 → 6.5.2

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 (68) hide show
  1. package/CHANGELOG.md +42 -1156
  2. package/README.md +1 -10
  3. package/bin/cdsc.js +1 -1
  4. package/doc/IncompatibleChanges_v5.md +436 -0
  5. package/doc/IncompatibleChanges_v6.md +659 -0
  6. package/doc/Versioning.md +3 -7
  7. package/lib/api/main.js +1 -0
  8. package/lib/api/options.js +5 -0
  9. package/lib/api/validate.js +3 -0
  10. package/lib/base/message-registry.js +31 -8
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +3 -2
  13. package/lib/checks/actionsFunctions.js +6 -3
  14. package/lib/checks/existsInForbiddenPlaces.js +32 -0
  15. package/lib/checks/validator.js +2 -0
  16. package/lib/compiler/assert-consistency.js +3 -5
  17. package/lib/compiler/checks.js +4 -8
  18. package/lib/compiler/define.js +330 -558
  19. package/lib/compiler/extend.js +302 -18
  20. package/lib/compiler/finalize-parse-cdl.js +2 -10
  21. package/lib/compiler/generate.js +33 -5
  22. package/lib/compiler/kick-start.js +8 -7
  23. package/lib/compiler/populate.js +25 -70
  24. package/lib/compiler/propagator.js +1 -2
  25. package/lib/compiler/resolve.js +4 -13
  26. package/lib/compiler/shared.js +18 -5
  27. package/lib/compiler/tweak-assocs.js +13 -9
  28. package/lib/compiler/utils.js +98 -2
  29. package/lib/compiler/xpr-rewrite.js +2 -1
  30. package/lib/edm/annotations/edmJson.js +9 -6
  31. package/lib/edm/annotations/genericTranslation.js +8 -4
  32. package/lib/edm/csn2edm.js +3 -4
  33. package/lib/edm/edmInboundChecks.js +1 -2
  34. package/lib/edm/edmPreprocessor.js +3 -3
  35. package/lib/gen/CdlGrammar.checksum +1 -1
  36. package/lib/gen/CdlParser.js +1 -1
  37. package/lib/gen/Dictionary.json +16 -1
  38. package/lib/json/from-csn.js +4 -6
  39. package/lib/json/to-csn.js +3 -3
  40. package/lib/model/csnRefs.js +19 -5
  41. package/lib/model/enrichCsn.js +4 -2
  42. package/lib/optionProcessor.js +8 -4
  43. package/lib/render/toSql.js +0 -4
  44. package/lib/render/utils/sql.js +3 -2
  45. package/lib/transform/db/applyTransformations.js +1 -1
  46. package/lib/transform/db/assertUnique.js +3 -3
  47. package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
  48. package/lib/transform/db/assocsToQueries/transformExists.js +14 -3
  49. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  50. package/lib/transform/db/backlinks.js +4 -4
  51. package/lib/transform/db/cdsPersistence.js +4 -4
  52. package/lib/transform/db/constraints.js +4 -4
  53. package/lib/transform/db/expansion.js +5 -5
  54. package/lib/transform/db/flattening.js +4 -5
  55. package/lib/transform/db/rewriteCalculatedElements.js +3 -3
  56. package/lib/transform/db/temporal.js +11 -11
  57. package/lib/transform/draft/db.js +2 -0
  58. package/lib/transform/draft/odata.js +5 -7
  59. package/lib/transform/effective/flattening.js +1 -2
  60. package/lib/transform/forOdata.js +3 -3
  61. package/lib/transform/forRelationalDB.js +1 -1
  62. package/lib/transform/odata/createForeignKeys.js +1 -2
  63. package/lib/transform/odata/flattening.js +1 -2
  64. package/lib/transform/odata/toFinalBaseType.js +52 -55
  65. package/lib/transform/transformUtils.js +3 -4
  66. package/package.json +1 -1
  67. package/doc/CHANGELOG_BETA.md +0 -464
  68. package/doc/CHANGELOG_DEPRECATED.md +0 -235
@@ -87,7 +87,8 @@
87
87
  // --------- TODO: end in extra markdown document -------------------------------
88
88
 
89
89
  // Sub phase 1 (addXYZ) - only for main artifacts
90
- // - set _block links for main definitions, vocabulary and extensions
90
+ // - set _block links for main definitions, vocabulary, extensions and
91
+ // annotations on those
91
92
  // - store definitions (including context extensions), NO duplicate check
92
93
  // - artifact name check
93
94
  // - Note: the only allow name resolving is resolveUncheckedPath(),
@@ -132,9 +133,13 @@ const {
132
133
  const { kindProperties, dictKinds } = require('./base');
133
134
  const {
134
135
  setLink,
136
+ initItemsLinks,
135
137
  setMemberParent,
136
138
  createAndLinkCalcDepElement,
137
- storeExtension,
139
+ initExprAnnoBlock,
140
+ initDollarSelf,
141
+ initDollarParameters,
142
+ initBoundSelfParam,
138
143
  dependsOnSilent,
139
144
  pathName,
140
145
  targetCantBeAspect,
@@ -172,13 +177,12 @@ function define( model ) {
172
177
  Object.assign( model.$functions, {
173
178
  shuffleDict,
174
179
  shuffleArray,
175
- initArtifact,
176
- initMembers,
180
+ initMainArtifact,
181
+ initMembers, // for finalize-parser-cdl.js
182
+ targetIsTargetAspect,
183
+ checkRedefinition,
177
184
  initSelectItems,
178
185
  } );
179
-
180
- let boundSelfParamType = true; // special `$self` for binding param must still be initialised
181
-
182
186
  return doDefine();
183
187
 
184
188
  /**
@@ -204,16 +208,18 @@ function define( model ) {
204
208
  const sourceNames = shuffleArray( Object.keys( model.sources ) );
205
209
  for (const name of sourceNames)
206
210
  addSource( model.sources[name] );
211
+
212
+ // Phase 2:
207
213
  for (const name of sourceNames)
208
214
  initNamespaceAndUsing( model.sources[name] );
209
- dictForEach( model.definitions, initArtifact );
215
+ dictForEach( model.definitions, initMainArtifact );
210
216
  dictForEach( model.vocabularies, initVocabulary );
211
217
  dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
212
218
 
213
- addI18nBlocks();
219
+ addI18nBlocks(); // TODO: part of extend.js?
214
220
 
215
221
  const { $self } = model.definitions;
216
- if ($self) {
222
+ if ($self && $self.kind !== 'namespace') { // TODO v7: non-config error
217
223
  message( 'name-deprecated-$self', [ $self.name.location, $self ], { name: '$self' },
218
224
  'Do not use $(NAME) as name for an artifact definition' );
219
225
  }
@@ -234,7 +240,7 @@ function define( model ) {
234
240
 
235
241
  let namespace = src.namespace?.name;
236
242
  let prefix = '';
237
- if (namespace?.path && !namespace.path.broken) {
243
+ if (namespace?.path) {
238
244
  namespace.id = pathName( namespace.path );
239
245
  prefix = `${ namespace.id }.`;
240
246
  }
@@ -242,12 +248,12 @@ function define( model ) {
242
248
  error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
243
249
  { name: 'cds' },
244
250
  'The namespace $(NAME) is reserved for CDS builtins' );
245
- namespace = null;
251
+ namespace = null; // TODO: reconsider
246
252
  }
247
253
  if (src.$frontend !== 'json') { // CDL input
248
254
  // TODO: set _block to builtin
249
255
  if (src.artifacts) {
250
- // addArtifact() adds usings to src.artifacts: shuffleDict must be assigned first
256
+ // addBlockArtifact() adds usings to src.artifacts: shuffleDict must be assigned first
251
257
  src.artifacts = shuffleDict( src.artifacts );
252
258
  addPathPrefixes( src.artifacts, prefix ); // before addUsing
253
259
  }
@@ -258,14 +264,14 @@ function define( model ) {
258
264
  shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
259
265
  if (namespace?.id) // successfully set a full name for namespace
260
266
  addNamespace( namespace, src );
261
- if (src.artifacts) { // addArtifact needs usings for context extensions
267
+ if (src.artifacts) { // addBlockArtifact needs usings for context extensions
262
268
  src.artifacts = shuffleDict( src.artifacts );
263
- dictForEach( src.artifacts, a => addArtifact( a, src, prefix ) );
269
+ dictForEach( src.artifacts, a => addBlockArtifact( a, src, prefix ) );
264
270
  }
265
271
  }
266
272
  else if (src.definitions) { // CSN input
267
273
  prefix = ''; // also for addVocabulary() below
268
- dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ) );
274
+ dictForEach( shuffleDict( src.definitions ), def => addMainArtifact( def, src, prefix ) );
269
275
  }
270
276
  if (src.vocabularies) {
271
277
  if (!model.vocabularies)
@@ -277,11 +283,13 @@ function define( model ) {
277
283
  }
278
284
  }
279
285
 
280
- function addDefinition( art, block, prefix ) {
286
+ function addMainArtifact( art, block, prefix ) {
287
+ setLink( art, '_block', block );
288
+ initExprAnnoBlock( art, block );
281
289
  art.name.id ??= prefix + pathName( art.name.path );
282
290
  const absolute = art.name.id;
283
291
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
284
- if (absolute === 'cds' || isInReservedNamespace( absolute )) {
292
+ if (isInReservedNamespace( absolute )) {
285
293
  error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
286
294
  'The namespace $(NAME) is reserved for CDS builtins' );
287
295
  const builtin = model.definitions[absolute];
@@ -297,8 +305,6 @@ function define( model ) {
297
305
  }
298
306
  return;
299
307
  }
300
- setLink( art, '_block', block );
301
- initExprAnnoBlock( art, block );
302
308
  // dictAdd might set $duplicates
303
309
  dictAdd( model.definitions, absolute, art );
304
310
  }
@@ -345,7 +351,7 @@ function define( model ) {
345
351
  return;
346
352
  }
347
353
  const path = decl.extern?.path;
348
- if (!path || path.broken || !path[0]) // syntax error
354
+ if (!path || !path[0]) // syntax error
349
355
  return;
350
356
  decl.extern.id = pathName( path );
351
357
  if (!decl.name)
@@ -354,7 +360,7 @@ function define( model ) {
354
360
  // TODO: check name: no "."
355
361
  const found = src.artifacts[name];
356
362
  // a real `using` declaration is “nicer” than a compiler-generated one:
357
- if (found && found.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
363
+ if (found?.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
358
364
  src.artifacts[name] = decl;
359
365
  else
360
366
  dictAddArray( src.artifacts, name, decl );
@@ -377,15 +383,15 @@ function define( model ) {
377
383
  $inferred: 'namespace',
378
384
  };
379
385
  }
380
- function addArtifact( art, block, prefix ) {
386
+ function addBlockArtifact( art, block, prefix ) {
381
387
  if (art.kind === 'using')
382
388
  return;
383
- addDefinition( art, block, prefix );
389
+ addMainArtifact( art, block, prefix );
384
390
  if (art.artifacts) {
385
391
  const p = `${ art.name.id }.`;
386
392
  // path prefixes (usings) must be added before extensions in artifacts:
387
393
  addPathPrefixes( art.artifacts, p );
388
- dictForEach( art.artifacts, a => addArtifact( a, art, p ) );
394
+ dictForEach( art.artifacts, a => addBlockArtifact( a, art, p ) );
389
395
  }
390
396
  if (art.extensions) { // requires using to be known!
391
397
  art.extensions.forEach( e => e.name && addExtension( e, art ) );
@@ -418,55 +424,8 @@ function define( model ) {
418
424
  // Set block number for debugging (--raw-output):
419
425
  // eslint-disable-next-line no-multi-assign
420
426
  ext.$effectiveSeqNo = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
421
- // add "namespace" for the case that ext.artifacts is empty (TODO: later)
422
- // now add all definitions in ext.artifacts:
423
427
  const prefix = `${ absolute }.`;
424
- dictForEach( ext.artifacts, a => addArtifact( a, ext, prefix ) );
425
- }
426
-
427
- function initExtension( parent ) {
428
- forEachMember( parent, function initExtensionMember( sub, name, prop ) {
429
- if (sub.kind !== 'extend' && sub.kind !== 'annotate')
430
- return; // for defs inside, set somewhere else - TODO: rethink
431
- if (prop === 'params' && name === '') // RETURNS
432
- sub.name = { id: '', location: sub.location };
433
- setLink( sub, '_block', parent._block );
434
- initExprAnnoBlock( sub, parent._block );
435
- setLink( sub, '_parent', parent );
436
- setLink( sub, '_main', parent._main || parent );
437
- initExtension( sub );
438
- } );
439
- if (parent.kind !== 'extend')
440
- return;
441
- // TODO: sub queries? expand/inline?
442
- parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
443
- if (parent.scale && !parent.precision) {
444
- // TODO: where could we store the location of the name?
445
- error( 'syntax-missing-type-property', [ parent.scale.location ],
446
- { prop: 'scale', otherprop: 'precision' },
447
- 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
448
- parent.scale = undefined; // no consequential error
449
- }
450
- }
451
-
452
- function initExprAnnoBlock( art, block ) {
453
- // remark: `art` could also be the extension
454
- for (const prop in art) {
455
- if (prop.charAt(0) !== '@')
456
- continue;
457
- const anno = art[prop];
458
- // _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
459
- if (anno.literal === 'array') {
460
- anno.kind = '$annotation';
461
- for (const item of anno.val)
462
- setLink( item, '_block', block );
463
- }
464
- else if (anno.$tokenTexts) {
465
- // remark: it wouldn't hurt to set it always...
466
- anno.kind = '$annotation';
467
- setLink( anno, '_block', block );
468
- }
469
- }
428
+ dictForEach( ext.artifacts, a => addBlockArtifact( a, ext, prefix ) );
470
429
  }
471
430
 
472
431
  function addVocabulary( vocab, block, prefix ) {
@@ -476,6 +435,8 @@ function define( model ) {
476
435
  dictAdd( model.vocabularies, name.id, vocab );
477
436
  }
478
437
 
438
+ // I18n code (eventually part of extend.js) -----------------------------------
439
+
479
440
  /**
480
441
  * Add (optional) translations into the XSN model.
481
442
  */
@@ -522,32 +483,9 @@ function define( model ) {
522
483
  }
523
484
 
524
485
  // Phase 2 ("init"), top-level & main -----------------------------------------
525
- // Functions called from top-level: initNamespaceAndUsing(), initArtifact(),
486
+ // Functions called from top-level: initNamespaceAndUsing(), initMainArtifact(),
526
487
  // initVocabulary(), initExtension()
527
488
 
528
- // TODO: message ids
529
- function checkRedefinition( art ) {
530
- if (!art.$duplicates || !art.name.id ||
531
- art.$errorReported === 'syntax-duplicate-extend')
532
- return;
533
- if (art.kind === 'annotate' || art.kind === 'extend')
534
- return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
535
- if (art._main) {
536
- error( 'duplicate-definition', [ art.name.location, art ], {
537
- name: art.name.id,
538
- '#': kindProperties[art.kind].normalized || art.kind,
539
- } );
540
- }
541
- else if (!art.builtin) {
542
- // TODO: better messages with definitions with the same name as builtin,
543
- // especially if there is just one
544
- error( 'duplicate-definition', [ art.name.location, art ], {
545
- name: art.name.id,
546
- '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
547
- } );
548
- }
549
- }
550
-
551
489
  function initNamespaceAndUsing( src ) {
552
490
  if (src.$frontend && src.$frontend !== 'cdl')
553
491
  return;
@@ -586,13 +524,12 @@ function define( model ) {
586
524
  }
587
525
  }
588
526
 
589
- function initArtifact( art, reInit = false ) {
590
- if (!reInit)
527
+ function initMainArtifact( art, reInit = false ) {
528
+ if (!reInit) // not for auto-exposed entity
591
529
  initArtifactParentLink( art, model.definitions );
592
- const block = art._block;
593
530
  checkRedefinition( art );
594
531
  initDollarSelf( art ); // $self
595
- initMembers( art, art, block );
532
+ initMembers( art );
596
533
  if (art.params)
597
534
  initDollarParameters( art ); // $parameters
598
535
  if (art.query) {
@@ -601,9 +538,105 @@ function define( model ) {
601
538
  }
602
539
  }
603
540
 
541
+ function initVocabulary( art ) {
542
+ initArtifactParentLink( art, model.vocabularies );
543
+ checkRedefinition( art );
544
+ initMembers( art );
545
+
546
+ if (art.query) {
547
+ initArtifactQuery( art );
548
+ error( 'def-unsupported-projection', [ art.location, art ], null,
549
+ 'Projections for annotation definitions are not supported' );
550
+ }
551
+ }
552
+
553
+ function initExtension( parent ) {
554
+ // TODO: re-think
555
+ forEachMember( parent, function initExtensionMember( sub, name, prop ) {
556
+ if (sub.kind !== 'extend' && sub.kind !== 'annotate')
557
+ return; // for defs inside, set somewhere else - TODO: rethink
558
+ if (prop === 'params' && name === '') // RETURNS
559
+ sub.name = { id: '', location: sub.location };
560
+ setLink( sub, '_block', parent._block );
561
+ initExprAnnoBlock( sub, parent._block );
562
+ setLink( sub, '_parent', parent );
563
+ setLink( sub, '_main', parent._main || parent );
564
+ initExtension( sub );
565
+ } );
566
+ if (parent.kind !== 'extend')
567
+ return;
568
+ // TODO: sub queries? expand/inline?
569
+ parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
570
+ if (parent.scale && !parent.precision) {
571
+ // TODO: where could we store the location of the name?
572
+ error( 'syntax-missing-type-property', [ parent.scale.location ],
573
+ { prop: 'scale', otherprop: 'precision' },
574
+ 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
575
+ parent.scale = undefined; // no consequential error
576
+ }
577
+ }
578
+
579
+ /**
580
+ * Make the `_parent` pointer of artifact `A.B.C` point to `A.B`. If no `A.B`
581
+ * exists in XSN.definitions, create an artifact with kind `namespace` (better
582
+ * would be `$gap`). Add `A.B.C` to the `_subArtifacts` dictionary of `A.B`.
583
+ * Do recursively if necessary, `_parent` of `A.B` is `A` in this example.
584
+ */
585
+ function initArtifactParentLink( art, definitions, path, pathIndex ) {
586
+ setLink( art, '_parent', null );
587
+ const { id } = art.name;
588
+ const dot = id.lastIndexOf( '.' );
589
+ if (dot < 0)
590
+ return;
591
+ const prefix = id.substring( 0, dot );
592
+ let parent = definitions[prefix];
593
+ if (!parent) {
594
+ path ??= art.name.path;
595
+ pathIndex ??= path?.length - 1;
596
+ const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
597
+ const location = weakLocation( pathItemOrName.location );
598
+ parent = { kind: 'namespace', name: { id: prefix, location }, location };
599
+ definitions[prefix] = parent;
600
+ initArtifactParentLink( parent, definitions, path, pathIndex );
601
+ }
602
+ setLink( art, '_parent', parent );
603
+ if (!parent._subArtifacts)
604
+ setLink( parent, '_subArtifacts', Object.create( null ) );
605
+ if (art.$duplicates !== true) // no redef or "first def"
606
+ parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
607
+ }
608
+
609
+ // TODO: message ids
610
+ // Called by initMainArtifact() and initVocabulary() and for members
611
+ function checkRedefinition( art ) {
612
+ if (art.kind === 'annotate' || art.kind === 'extend') // move this check to call in extend.js?
613
+ return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
614
+ if (!art.$duplicates || !art.name.id ||
615
+ art.$errorReported === 'syntax-duplicate-extend')
616
+ return;
617
+ if (art._main) {
618
+ error( 'duplicate-definition', [ art.name.location, art ], {
619
+ name: art.name.id,
620
+ '#': kindProperties[art.kind].normalized || art.kind,
621
+ } );
622
+ }
623
+ else if (!art.builtin) {
624
+ // TODO: better messages with definitions with the same name as builtin,
625
+ // especially if there is just one
626
+ error( 'duplicate-definition', [ art.name.location, art ], {
627
+ name: art.name.id,
628
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
629
+ } );
630
+ }
631
+ }
632
+
633
+ // Phase 2, init queries ------------------------------------------------------
634
+
604
635
  /**
605
636
  * Restrict the query of `art` to only simple projections, i.e. those without 'group by', etc.
606
637
  *
638
+ * TODO v8 and for `type`: do in parsers
639
+ *
607
640
  * @param {XSN.Artifact} art
608
641
  */
609
642
  function restrictToSimpleProjection( art ) {
@@ -659,88 +692,6 @@ function define( model ) {
659
692
  }
660
693
  }
661
694
 
662
- function initVocabulary( art ) {
663
- initArtifactParentLink( art, model.vocabularies );
664
- checkRedefinition( art );
665
- const block = art._block;
666
- initMembers( art, art, block );
667
-
668
- if (art.query) {
669
- initArtifactQuery( art );
670
- error( 'def-unsupported-projection', [ art.location, art ], null,
671
- 'Projections for annotation definitions are not supported' );
672
- }
673
- }
674
-
675
- function initArtifactParentLink( art, definitions, path, pathIndex ) {
676
- setLink( art, '_parent', null );
677
- const { id } = art.name;
678
- const dot = id.lastIndexOf( '.' );
679
- if (dot < 0)
680
- return;
681
- const prefix = id.substring( 0, dot );
682
- let parent = definitions[prefix];
683
- if (!parent) {
684
- path ??= art.name.path;
685
- pathIndex ??= path?.length - 1;
686
- const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
687
- const location = weakLocation( pathItemOrName.location );
688
- parent = { kind: 'namespace', name: { id: prefix, location }, location };
689
- definitions[prefix] = parent;
690
- initArtifactParentLink( parent, definitions, path, pathIndex );
691
- }
692
- setLink( art, '_parent', parent );
693
- if (!parent._subArtifacts)
694
- setLink( parent, '_subArtifacts', Object.create( null ) );
695
- if (art.$duplicates !== true) // no redef or "first def"
696
- parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
697
- }
698
-
699
- // Init special things: -------------------------------------------------------
700
-
701
- function initDollarSelf( art ) {
702
- // TODO: use setMemberParent() ?
703
- const self = {
704
- name: { id: '$self', location: art.location },
705
- kind: '$self',
706
- location: art.location,
707
- };
708
- setLink( self, '_parent', art );
709
- setLink( self, '_main', art ); // used on main artifact
710
- setLink( self, '_origin', art );
711
- art.$tableAliases = Object.create( null );
712
- art.$tableAliases.$self = self;
713
- }
714
-
715
- function initDollarParameters( art ) {
716
- // TODO: use setMemberParent() ?
717
- const parameters = {
718
- name: { id: '$parameters' },
719
- kind: '$parameters',
720
- location: art.location,
721
- deprecated: true, // hide in code completion
722
- };
723
- setLink( parameters, '_parent', art );
724
- setLink( parameters, '_main', art );
725
- // Search for :const after :param. If there will be a possibility in the
726
- // future that we can extend <query>.columns, we must be sure to use
727
- // _block of that new column after :param (or just allow $parameters there).
728
- setLink( parameters, '_block', art._block );
729
- if (art.params) {
730
- parameters.elements = art.params;
731
- parameters.$tableAliases = art.params; // TODO: find better name - $lexical?
732
- }
733
- art.$tableAliases.$parameters = parameters;
734
- }
735
-
736
- // From here til EOF, reexamine code ---------------------------------------
737
- // See populate:
738
- // - userQuery() or _query property?
739
- // - initFromColumns()
740
- // - ensureColumnName()
741
-
742
- // Init queries: --------------------------------------------------------------
743
-
744
695
  // art is:
745
696
  // - entity/event/type for top-level queries (including UNION args)
746
697
  // - $tableAlias for sub query in FROM - TODO: what about UNION there?
@@ -749,7 +700,7 @@ function define( model ) {
749
700
  if (!query) // parse error
750
701
  return query;
751
702
  if (query.from) { // select
752
- initQuery();
703
+ initQuery( query, art );
753
704
  initTableExpression( query.from, query, [] );
754
705
  if (query.mixin)
755
706
  initMixins( query, art );
@@ -794,21 +745,21 @@ function define( model ) {
794
745
  return undefined;
795
746
  }
796
747
  return query._leadingQuery || query;
748
+ }
797
749
 
798
- function initQuery() {
799
- const main = art._main || art;
800
- setLink( query, '_$next',
801
- (art.kind === '$tableAlias' ? art._parent._$next : art ) );
802
- setLink( query, '_block', art._block );
803
- query.kind = 'select';
804
- query.name = { location: query.location, id: main.$queries.length + 1 };
805
- setMemberParent( query, null, main );
806
- // console.log(art.kind,art.name,query.name,query._$next.name)
807
- main.$queries.push( query );
808
- setLink( query, '_parent', art ); // _parent should point to alias/main/query
809
- query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
810
- dependsOnSilent( main, query );
811
- }
750
+ function initQuery( query, art ) {
751
+ setLink( query, '_block', art._block );
752
+ const main = art._main || art;
753
+ setLink( query, '_$next',
754
+ (art.kind === '$tableAlias' ? art._parent._$next : art ) );
755
+ query.kind = 'select';
756
+ query.name = { location: query.location, id: main.$queries.length + 1 };
757
+ setMemberParent( query, null, main );
758
+ // console.log(art.kind,art.name,query.name,query._$next.name)
759
+ main.$queries.push( query );
760
+ setLink( query, '_parent', art ); // _parent should point to alias/main/query
761
+ query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
762
+ dependsOnSilent( main, query );
812
763
  }
813
764
 
814
765
  // table is table expression in FROM, becomes an alias
@@ -816,7 +767,7 @@ function define( model ) {
816
767
  if (!table) // parse error
817
768
  return;
818
769
  if (table.path) { // path in FROM
819
- if (!table.path.length || table.path.broken)
770
+ if (!table.path.length)
820
771
  // parse error (e.g. final ',' in FROM), projection on <eof>
821
772
  return;
822
773
  if (!table.name) {
@@ -826,7 +777,7 @@ function define( model ) {
826
777
  // TODO: if we have too much time, we can calculate the real location with '.'
827
778
  table.name = { $inferred: 'as', id, location: last.location };
828
779
  }
829
- addAsAlias();
780
+ addAsAlias( table, query, joinParents );
830
781
  // _origin is set when we resolve the ref
831
782
  if (query._parent.kind !== 'select')
832
783
  query._main._from.push( table ); // store tabref if outside "real" subquery
@@ -838,7 +789,7 @@ function define( model ) {
838
789
  const id = `$_select_${ query._main.$queries.length + 1 }__`;
839
790
  table.name = { id, location: table.location, $inferred: '$internal' };
840
791
  }
841
- addAsAlias();
792
+ addAsAlias( table, query, joinParents );
842
793
  // Store _origin to leading query of table.query for name resolution
843
794
  setLink( table, '_origin', initQueryExpression( table.query, table ) );
844
795
  }
@@ -878,37 +829,36 @@ function define( model ) {
878
829
  // TODO: probably set this to query if we switch to name restriction in JOIN
879
830
  }
880
831
  }
881
- return;
882
-
883
- function addAsAlias() {
884
- table.kind = '$tableAlias';
885
- setMemberParent( table, table.name.id, query );
886
- setLink( table, '_block', query._block );
887
- dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
888
- if (tableAlias.name.$inferred === '$internal') {
889
- const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
890
- // TODO: the semanticLoc query is not initialized yet, and thus cannot
891
- // be used here
892
- error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
893
- { '#': 'duplicate', code: 'as ‹alias›' } );
894
- }
895
- else {
896
- error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
897
- }
898
- } );
899
- // also add to JOIN nodes for name restrictions:
900
- for (const p of joinParents) {
901
- // for JOIN alias restriction, we cannot use $duplicates, as it is
902
- // already used for duplicate aliases of queries:
903
- dictAddArray( p.$tableAliases, table.name.id, table );
832
+ }
833
+
834
+ function addAsAlias( table, query, joinParents ) {
835
+ table.kind = '$tableAlias';
836
+ setLink( table, '_block', query._block );
837
+ setMemberParent( table, table.name.id, query );
838
+ dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
839
+ if (tableAlias.name.$inferred === '$internal') {
840
+ const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
841
+ // TODO: the semanticLoc query is not initialized yet, and thus cannot
842
+ // be used here
843
+ error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
844
+ { '#': 'duplicate', code: 'as ‹alias›' } );
904
845
  }
905
- if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
906
- message( 'name-invalid-dollar-alias', [ table.name.location, table ], {
907
- '#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
908
- name: '$',
909
- keyword: 'as',
910
- } );
846
+ else {
847
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
911
848
  }
849
+ } );
850
+ // also add to JOIN nodes for name restrictions:
851
+ for (const p of joinParents) {
852
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
853
+ // already used for duplicate aliases of queries:
854
+ dictAddArray( p.$tableAliases, table.name.id, table );
855
+ }
856
+ if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
857
+ message( 'name-invalid-dollar-alias', [ table.name.location, table ], {
858
+ '#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
859
+ name: '$',
860
+ keyword: 'as',
861
+ } );
912
862
  }
913
863
  }
914
864
 
@@ -921,7 +871,7 @@ function define( model ) {
921
871
  initExprForQuery( query.where, query );
922
872
  if (query.having)
923
873
  initExprForQuery( query.having, query );
924
- initMembers( query, query, query._block );
874
+ initMembers( query );
925
875
  }
926
876
 
927
877
  function initExprForQuery( expr, query ) {
@@ -939,11 +889,6 @@ function define( model ) {
939
889
  const args = Array.isArray( expr.args ) ? expr.args : Object.values( expr.args );
940
890
  args.forEach( e => initExprForQuery( e, query ) );
941
891
  }
942
- else if (expr.path && expr.$expected === 'exists') {
943
- // TODO: does really the parser has to set $expected?
944
- expr.$expected = 'approved-exists';
945
- approveExistsInChildren( expr );
946
- }
947
892
  }
948
893
 
949
894
  function initMixins( query, art ) {
@@ -972,15 +917,18 @@ function define( model ) {
972
917
  let wildcard = !!inExtension; // no `extend … with columns { * }`
973
918
  // TODO: forbid expand/inline in ref-where, outside queries (CSN), ...
974
919
  let hasItems = false;
920
+ let colIndex = 0;
975
921
  for (const col of columns || parent.expand || parent.inline || []) {
922
+ ++colIndex;
976
923
  if (!col) // parse error
977
924
  continue;
978
925
  hasItems = true;
979
926
  if (!columns) { // expand or inline
980
927
  if (parent.value)
981
- setLink( col, '_columnParent', parent ); // also set for '*' in expand/inline
928
+ setLink( col, '_columnParent', parent ); // TODO?: also set for '*' in expand/inline
982
929
  else if (parent._columnParent)
983
930
  setLink( col, '_columnParent', parent._columnParent );
931
+ // colParent not set for `{ col } as struct`, except if that is in expand/inline
984
932
  }
985
933
  if (col.val === '*') {
986
934
  if (!wildcard) {
@@ -1009,10 +957,13 @@ function define( model ) {
1009
957
  }
1010
958
  }
1011
959
  // Either expression (value), expand, new virtual or new association
1012
- else if (col.value || col.name) {
960
+ else {
1013
961
  col.kind = 'element';
1014
962
  if (!col._block)
1015
963
  setLink( col, '_block', parent._block );
964
+ setMemberParent( col, null, parent );
965
+ // remark: _parent might be change if col is in expand of to-many assoc
966
+ ensureColumnName( col, colIndex, parent, !columns );
1016
967
  if (col.inline) { // `@anno elem.{ * }` does not work
1017
968
  if (col.doc) {
1018
969
  message( 'syntax-unexpected-anno', [ col.doc.location, col ],
@@ -1029,10 +980,12 @@ function define( model ) {
1029
980
  // TODO: allow sub queries? at least in top-level expand without parallel ref
1030
981
  if (columns && !inExtension) // not (yet) in `extend … with columns {…}`
1031
982
  initExprForQuery( col.value, parent );
1032
- initSelectItems( col, null, user ); // TODO: use col as user (i.e. remove param)
1033
- }
983
+ if (col.expand || col.inline)
984
+ initSelectItems( col, null, user ); // TODO: use col, remove 3rd param?
1034
985
 
1035
- initCdlTypeCast( col, parent );
986
+ initMembers( col ); // with #13933, TODO: only for enums
987
+ checkCdlTypeCast( col );
988
+ }
1036
989
  }
1037
990
 
1038
991
  if (hasItems && !wildcard && parent.excludingDict && !options.$recompile) {
@@ -1044,76 +997,75 @@ function define( model ) {
1044
997
  }
1045
998
  }
1046
999
 
1047
- function initCdlTypeCast( col, parent ) {
1048
- if (col.val)
1049
- return; // e.g. '*' column
1050
-
1051
- setMemberParent( col, col.name, parent );
1052
- initMembers( col, col, col._block );
1000
+ /**
1001
+ * Ensure that the column has a name.
1002
+ *
1003
+ * @param col
1004
+ * @param {number} colIndex
1005
+ * @param query
1006
+ * @param {boolean} insideExpand
1007
+ * Whether the column is inside 'expand'.
1008
+ * Anonymous 'expands' don't have a column parent, hence why we need to know this explicitly.
1009
+ */
1010
+ function ensureColumnName( col, colIndex, query, insideExpand ) {
1011
+ if (col.name)
1012
+ return col.name.id;
1013
+ if (col.inline || col.val === '*' || col.val === '**') // '**' = duplicate '*'
1014
+ return '';
1015
+ // TODO: should we give technical name (colIndex) for error messages?
1016
+ const path = col.value &&
1017
+ (col.value.path || !col.value.args && col.value.func?.path);
1018
+ const last = path?.length && path[path.length - 1];
1019
+ if (last) {
1020
+ col.name = { id: last.id || '', location: last.location, $inferred: 'as' };
1021
+ return col.name.id;
1022
+ }
1023
+ else if ((col.expand || col.value) &&
1024
+ !path && // no parse error (path without last item)
1025
+ (insideExpand || query._parent.kind !== 'select')) { // not sub-selects
1026
+ error( 'query-req-name',
1027
+ // TODO: message function: `query` should work directly
1028
+ [ (col.value || col).location, (query.name ? query : query._parent ) ],
1029
+ {},
1030
+ 'Alias name is required for this select item' );
1031
+ }
1032
+ col.name = {
1033
+ // NOTE: If the alias is changed, corresponding name-clash tests must be updated as well!
1034
+ id: `$_column_${ colIndex }`,
1035
+ location: (col.value || col).location,
1036
+ $inferred: '$internal',
1037
+ };
1038
+ return col.name.id;
1039
+ }
1053
1040
 
1041
+ function checkCdlTypeCast( col ) { // with #13933:
1054
1042
  // We don't allow CDL-style casts to anonymous structures. We reject it already here
1055
1043
  // and not in checks.js to ensure that it's rejected in parseCdl.
1044
+ // TODO: via <guard> in CdlGrammar.g4
1056
1045
  if (col.elements) {
1057
- error('type-invalid-cast', [ col.elements[$location] ?? col.location, col ],
1058
- { '#': 'to-inline-structure' });
1046
+ error( 'type-invalid-cast', [ (col.elements[$location] ?? col.location), col ],
1047
+ { '#': 'to-inline-structure' } );
1059
1048
  }
1060
- else if (col.expand && (col.type || col.elements || col.items)) {
1049
+ else if (col.expand && (col.type || col.items)) {
1061
1050
  const loc = (col.type?.location || col.elements?.[$location] ||
1062
- col.items?.location || col.location);
1063
- error('type-invalid-cast', [ loc, col ], { '#': 'expand' });
1051
+ col.items?.location || col.location);
1052
+ error( 'type-invalid-cast', [ loc, col ], { '#': 'expand' } );
1064
1053
  }
1054
+ // Remark: there are quite a few consequential error, TODO: avoid them
1065
1055
  }
1066
1056
 
1067
- /**
1068
- * If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
1069
- * since we will have a top-level subquery after exists-processing in the forRelationalDB.
1070
- *
1071
- * Recursively drill down into:
1072
- * - the .path
1073
- * - the .args
1074
- * - the .where.args
1075
- *
1076
- * Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
1077
- *
1078
- * working: exists toE[exists toE] -> select from E where exists toE
1079
- * not working: toE[exists toE] -> we don't support subqueries in filters
1080
- *
1081
- * @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
1082
- */
1083
- function approveExistsInChildren( exprOrPathElement ) {
1084
- if (!exprOrPathElement) // may be null in case of parse error
1085
- return;
1086
- if (exprOrPathElement.$expected === 'exists')
1087
- exprOrPathElement.$expected = 'approved-exists';
1088
- // Drill down
1089
- if (Array.isArray(exprOrPathElement.args))
1090
- exprOrPathElement.args.forEach( elem => approveExistsInChildren( elem ) );
1091
- else if (exprOrPathElement.where?.args)
1092
- exprOrPathElement.where.args.forEach( elem => approveExistsInChildren( elem ) );
1093
- else if (exprOrPathElement.path)
1094
- exprOrPathElement.path.forEach( elem => approveExistsInChildren( elem ) );
1095
- }
1096
- // TODO: we might issue 'expr-unexpected-exists' and 'expr-no-subquery' already in
1097
- // define.js (using a to-be-written expression traversal function in utils.js)
1098
-
1099
1057
  // Members (elements, enum, actions, params): ---------------------------------
1100
1058
 
1101
1059
  /**
1102
1060
  * Set property `_parent` for all elements in `parent` to `parent` and do so
1103
1061
  * recursively for all sub elements.
1104
1062
  *
1105
- * If not for extensions: construct === parent
1106
- *
1107
1063
  * Param `initExtensions` is for parse.cdl - TODO delete
1108
- *
1109
- * TODO: separate extension!
1110
1064
  */
1111
- function initMembers( construct, parent, block, initExtensions = false ) {
1112
- // TODO: split extend from init
1113
- const main = parent._main || parent;
1114
- const isQueryExtension = construct.kind === 'extend' && main.query;
1115
- let obj = initItemsLinks( construct, block );
1116
- initExprAnnoBlock( construct, block );
1065
+ function initMembers( parent ) {
1066
+ const block = parent._block;
1067
+ let obj = initItemsLinks( parent, block );
1068
+ initExprAnnoBlock( parent, block );
1117
1069
  if (obj.target && targetIsTargetAspect( obj )) {
1118
1070
  obj.targetAspect = obj.target;
1119
1071
  delete obj.target;
@@ -1121,290 +1073,110 @@ function define( model ) {
1121
1073
  const { targetAspect } = obj;
1122
1074
  if (targetAspect) {
1123
1075
  if (obj.foreignKeys) {
1124
- error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], construct ] );
1076
+ error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], parent ] );
1125
1077
  delete obj.foreignKeys; // continuation semantics: not specified
1126
1078
  }
1127
1079
  if (obj.on && !obj.target) {
1128
- error( 'type-unexpected-on-condition', [ obj.on.location, construct ] );
1080
+ error( 'type-unexpected-on-condition', [ obj.on.location, parent ] );
1129
1081
  delete obj.on; // continuation semantics: not specified
1130
1082
  }
1131
- if (targetAspect.elements)
1132
- initAnonymousAspect();
1133
- }
1134
- if (obj !== parent && obj.elements && parent.enum) { // applying the extension
1135
- initElementsAsEnum();
1136
- }
1137
- else {
1138
- if (checkDefinitions( construct, parent, 'elements', obj.elements || false ))
1139
- forEachInOrder( obj, 'elements', init );
1140
- if (checkDefinitions( construct, parent, 'enum', obj.enum || false ))
1141
- forEachGeneric( obj, 'enum', init );
1083
+ if (targetAspect.elements) // eslint-disable-next-line no-multi-assign
1084
+ parent = obj = initAnonymousAspect( parent, obj, targetAspect );
1142
1085
  }
1086
+ forEachInOrder( obj, 'elements', (...args) => initArtifact( parent, ...args ) );
1087
+ forEachGeneric( obj, 'enum', (...args) => initArtifact( parent, ...args ) );
1088
+ forEachInOrder( obj, 'foreignKeys', (...args) => initArtifact( parent, ...args ) );
1089
+ forEachGeneric( parent, 'actions', (...args) => initArtifact( parent, ...args ) );
1090
+ forEachInOrder( parent, 'params', (...args) => initArtifact( parent, ...args ) );
1143
1091
 
1144
- if (obj.foreignKeys)
1145
- forEachInOrder( obj, 'foreignKeys', init );
1146
- if (checkDefinitions( construct, parent, 'actions' ))
1147
- forEachGeneric( construct, 'actions', init );
1148
- if (checkDefinitions( construct, parent, 'params' ))
1149
- forEachInOrder( construct, 'params', init );
1150
- const { returns } = construct;
1092
+ const { returns } = parent;
1151
1093
  if (returns) {
1152
- const { kind } = construct;
1094
+ const { kind } = parent;
1153
1095
  returns.kind = (kind === 'extend' || kind === 'annotate') ? kind : 'param';
1154
- init( returns, '' ); // '' is special name for returns parameter
1096
+ initArtifact( parent, returns, '' ); // '' is special name for returns parameter
1155
1097
  }
1156
- return;
1157
-
1158
- function initElementsAsEnum() {
1159
- // in extensions, extended enums are represented as elements
1160
- let hasElement = false;
1161
- for (const n in obj.elements) {
1162
- const e = obj.elements[n];
1163
- if (e.kind === 'extend')
1164
- continue;
1165
- const noVal = e.value?.val === undefined && e.value?.sym === undefined;
1166
- // TODO: forbid #symbol as enum value
1167
- if (e.$syntax === 'element' || // `extend … with elements` or `extend with { element … }`
1168
- noVal && e.$syntax !== 'enum' || // no value in CDL input
1169
- e.virtual || e.key || e.masked || e.type || e.elements || e.items || e.stored) {
1170
- // We do not want to complain separately about all element properties:
1171
- error( 'ext-unexpected-element', [ e.location, construct ],
1172
- { name: e.name.id, code: 'extend … with enum' },
1173
- // eslint-disable-next-line @stylistic/max-len
1174
- 'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
1175
- // Don't emit 'ext-expecting-enum' if this error is emitted.
1176
- return;
1177
- }
1178
- e.kind = 'enum';
1179
- if (noVal || e.$syntax !== 'enum')
1180
- hasElement = true; // warning with CDL input or `name: {}` in CSN input
1181
- }
1182
- if (hasElement) {
1183
- // This message is similar to the one above. In v6, we could probably
1184
- // turn this warning into an error, remove `$syntax: 'element',
1185
- // and use the above `ext-unexpected-element` only for CSN input.
1186
- warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
1187
- { code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
1188
- }
1189
- forEachGeneric( { enum: obj.elements }, 'enum', init );
1190
- }
1191
-
1192
- function initAnonymousAspect() {
1193
- // TODO: main?
1194
- const inEntity = parent._main?.kind === 'entity';
1195
- // TODO: also allow indirectly (component in component in entity)?
1196
- setLink( targetAspect, '_outer', obj );
1197
- setLink( targetAspect, '_parent', parent._parent );
1198
- setLink( targetAspect, '_main', null ); // for name resolution
1199
-
1200
- parent = targetAspect;
1201
- construct = parent; // avoid extension behavior
1202
- targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1203
- setLink( targetAspect, '_block', block );
1204
- initDollarSelf( targetAspect );
1205
- // allow ref of up_ in anonymous aspect inside entity
1206
- // (TODO: complain if used and the managed composition is included into
1207
- // another entity - might induce auto-redirection):
1208
- if (inEntity && !targetAspect.elements.up_) {
1209
- const up = {
1210
- name: { id: 'up_' },
1211
- kind: '$navElement',
1212
- location: obj.location,
1213
- };
1214
- setLink( up, '_parent', targetAspect );
1215
- setLink( up, '_main', targetAspect ); // used on main artifact
1216
- // recompilation case: both target and targetAspect → allow up_ in that case, too:
1217
- const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
1218
- const entity = name && model.definitions[name];
1219
- if (entity && entity.elements)
1220
- setLink( up, '_origin', entity.elements.up_ );
1221
- // processAspectComposition/expand() sets _origin to element of
1222
- // generated target entity
1223
- targetAspect.$tableAliases.up_ = up;
1224
- }
1225
- obj = targetAspect;
1226
- }
1227
-
1228
- function init( elem, name, prop ) {
1229
- if (!elem.kind) // wrong CSN input
1230
- elem.kind = dictKinds[prop];
1231
- if (!elem.name && !elem._outer) {
1232
- const ref = elem.targetElement || elem.kind === 'element' && elem.value;
1233
- if (ref && ref.path) {
1234
- elem.name = Object.assign( { $inferred: 'as' },
1235
- ref.path[ref.path.length - 1] );
1236
- }
1237
- else { // RETURNS, parser robustness
1238
- elem.name = { id: name, location: elem.location };
1239
- }
1240
- }
1241
- // if (!kindProperties[ elem.kind ]) console.log(elem.kind,elem.name)
1242
- if ((elem.kind === 'extend' || elem.kind === 'annotate') && !initExtensions) {
1243
- storeExtension( elem, name, prop, parent, block );
1244
- return;
1245
- }
1246
- if (isQueryExtension && elem.kind === 'element') {
1247
- error( 'extend-query', [ elem.location, construct ], // TODO: searchName ?
1248
- { code: 'extend projection' },
1249
- 'Use $(CODE) to add select items to the query entity' );
1250
- return;
1251
- }
1098
+ }
1252
1099
 
1253
- const bl = elem._block || block;
1254
- setLink( elem, '_block', bl );
1255
- const existing = parent[prop]?.[name];
1256
- const add = construct !== parent && (!existing || elem.$inferred !== 'include');
1257
- // don't dump with `entity T {}; extend T with { extend e {}; e {}; e {} };`:
1258
- if (elem.$duplicates === true && add)
1259
- elem.$duplicates = null;
1260
- setMemberParent( elem, name, parent, add && prop );
1261
- checkRedefinition( elem );
1262
- initMembers( elem, elem, bl, initExtensions );
1263
- if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
1264
- initBoundSelfParam( elem.params, elem._main );
1265
-
1266
- // for a correct home path, setMemberParent needed to be called
1267
-
1268
- if (!elem.value || elem.kind !== 'element' ||
1269
- elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1270
- return;
1271
- // -> it's a calculated element
1272
- if (!elem.type && elem.value?.type) { // top-level CAST( expr AS type )
1273
- if (!elem.target)
1274
- elem.type = { ...elem.value.type, $inferred: 'cast' };
1275
- }
1276
- elem.$syntax = 'calc';
1277
- // TODO: it is not just "syntax" - maybe better test for `$calcDepElement`?
1278
- createAndLinkCalcDepElement( elem );
1279
-
1280
- // Special case (hack) for calculated elements that use composition+filter:
1281
- // See "Notes on `$enclosed`" in `ExposingAssocWithFilter.md` for details.
1282
- if (elem.target && elem.value.path?.[elem.value.path.length - 1]?.where) {
1283
- delete elem.type;
1284
- delete elem.on;
1285
- delete elem.target;
1286
- }
1287
- }
1100
+ function initAnonymousAspect( parent, obj, targetAspect ) {
1101
+ // TODO: main?
1102
+ const inEntity = parent._main?.kind === 'entity';
1103
+ // TODO: also allow indirectly (component in component in entity)?
1104
+ setLink( targetAspect, '_outer', obj );
1105
+ setLink( targetAspect, '_parent', parent._parent );
1106
+ setLink( targetAspect, '_main', null ); // for name resolution
1107
+
1108
+ targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1109
+ setLink( targetAspect, '_block', parent._block );
1110
+ initDollarSelf( targetAspect );
1111
+ // allow ref of up_ in anonymous aspect inside entity
1112
+ // (TODO: complain if used and the managed composition is included into
1113
+ // another entity - might induce auto-redirection):
1114
+ if (inEntity && !targetAspect.elements.up_) {
1115
+ const up = {
1116
+ name: { id: 'up_' },
1117
+ kind: '$navElement',
1118
+ location: obj.location,
1119
+ };
1120
+ setLink( up, '_parent', targetAspect );
1121
+ setLink( up, '_main', targetAspect ); // used on main artifact
1122
+ // recompilation case: both target and targetAspect → allow up_ in that case, too:
1123
+ const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
1124
+ const entity = name && model.definitions[name];
1125
+ if (entity && entity.elements)
1126
+ setLink( up, '_origin', entity.elements.up_ );
1127
+ // processAspectComposition/expand() sets _origin to element of
1128
+ // generated target entity
1129
+ targetAspect.$tableAliases.up_ = up;
1130
+ }
1131
+ return targetAspect;
1288
1132
  }
1289
1133
 
1290
- function initBoundSelfParam( params, main ) {
1291
- if (!params)
1292
- return;
1293
- if (boundSelfParamType === true) { // first try
1294
- const def = model.definitions.$self;
1295
- if (def) {
1296
- boundSelfParamType = false;
1297
- return;
1134
+ function initArtifact( parent, elem, name, prop ) {
1135
+ if (!elem.kind) // wrong CSN input
1136
+ elem.kind = dictKinds[prop];
1137
+ if (!elem.name && !elem._outer) {
1138
+ const ref = elem.targetElement || elem.kind === 'element' && elem.value;
1139
+ if (ref && ref.path) {
1140
+ elem.name = Object.assign( { $inferred: 'as' },
1141
+ ref.path[ref.path.length - 1] );
1142
+ }
1143
+ else { // RETURNS, parser robustness
1144
+ elem.name = { id: name, location: elem.location };
1298
1145
  }
1299
- boundSelfParamType = '$self';
1300
- }
1301
- const first = params[Object.keys( params )[0] || ''];
1302
- const type = first?.type || first?.items?.type; // this sequence = no derived type
1303
- const path = type?.path;
1304
- if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
1305
- const $self = main.$tableAliases?.$self ||
1306
- main.kind === 'extend' && { name: { id: '$self' } };
1307
- // remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
1308
- setLink( type, '_artifact', $self );
1309
- setLink( path[0], '_artifact', $self );
1310
1146
  }
1311
- }
1312
1147
 
1313
- /**
1314
- * Initialize artifact links inside `obj.items` (for nested ones as well).
1315
- * Does nothing, it `obj.items` does not exist.
1316
- *
1317
- * @param {XSN.Artifact} obj
1318
- * @param {object} block
1319
- * @return {XSN.Artifact}
1320
- */
1321
- function initItemsLinks( obj, block ) {
1322
- let { items } = obj;
1323
- while (items) {
1324
- setLink( items, '_outer', obj );
1325
- setLink( items, '_parent', obj._parent );
1326
- setLink( items, '_block', block );
1327
- obj = items;
1328
- items = obj.items;
1329
- }
1330
- return obj;
1331
- }
1148
+ if (!elem._block)
1149
+ setLink( elem, '_block', parent._block );
1332
1150
 
1333
- // To be reworked -------------------------------------------------------------
1151
+ // don't dump with `entity T {}; extend T with { extend e {}; e {}; e {} };`:
1152
+ setMemberParent( elem, name, parent );
1153
+ checkRedefinition( elem );
1154
+ initMembers( elem );
1155
+ if (elem.kind === 'action' || elem.kind === 'function')
1156
+ initBoundSelfParam( elem.params, elem._main );
1334
1157
 
1335
- // TODO: is only necessary for extensions - make special for extend/annotate
1336
- function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
1337
- // TODO: do differently, see also annotateMembers() in resolver
1338
- // To have been checked by parsers:
1339
- // - artifacts (CDL-only anyway) only inside [extend] context|service
1340
- if (!dict)
1341
- return false;
1342
- const feature = kindProperties[parent.kind ?? 'element'][prop];
1343
- if (feature &&
1344
- (feature === true || construct.kind !== 'extend' || feature( prop, parent )))
1345
- return true;
1346
- const location = dict[$location];
1158
+ // for a correct home path, setMemberParent needed to be called
1347
1159
 
1348
- // TODO: a bit inconsistent = not a simple switch on `prop`…
1349
- if (prop === 'actions') {
1350
- if (Object.keys( dict ).length) {
1351
- error( 'def-unexpected-actions', [ location, construct ], {}, // TODO: ext-
1352
- 'Actions and functions only exist top-level and for entities' ); // or aspects
1353
- }
1354
- else {
1355
- warning( 'ext-ignoring-actions', [ location, construct ], {},
1356
- 'Actions and functions only exist top-level and for entities' );
1357
- return false;
1358
- }
1359
- }
1360
- else if (parent.kind === 'action' || parent.kind === 'function') {
1361
- error( 'ext-unexpected-action', [ construct.location, construct ], { '#': parent.kind }, {
1362
- std: 'Actions and functions can\'t be extended, only annotated', // TODO: ext-unsupported
1363
- action: 'Actions can\'t be extended, only annotated',
1364
- function: 'Functions can\'t be extended, only annotated',
1365
- } );
1366
- }
1367
- else if (prop === 'params') {
1368
- if (!feature) {
1369
- // Note: This error can't be triggered at the moment. But as we likely want to
1370
- // allow extensions with params in the future, we keep the code.
1371
- error( 'def-unexpected-params', [ location, construct ], {},
1372
- 'Parameters only exist for entities, actions or functions' );
1373
- }
1374
- else {
1375
- // remark: we could allow this
1376
- error( 'extend-with-params', [ location, construct ], {},
1377
- 'Extending artifacts with parameters is not supported' );
1378
- }
1379
- }
1380
- else if (feature) { // allowed in principle, but not with extend
1381
- if (!Object.keys( dict ).length) {
1382
- warning( 'ext-ignoring-elements', [ location, construct ], {},
1383
- 'Only structures with directly specified elements can be extended by elements' );
1384
- return false;
1385
- }
1386
- else if (parent.$inferred === 'include') { // special case for better error message
1387
- const variant = (construct.enum || construct.elements) ? 'elements' : 'std';
1388
- error( 'ref-expected-direct-structure', [ location, construct ],
1389
- { '#': variant, art: parent } );
1390
- }
1391
- else {
1392
- error( 'extend-type', [ location, construct ], {},
1393
- 'Only structures or enum types can be extended with elements/enums' );
1394
- }
1395
- }
1396
- else if (prop === 'elements') {
1397
- error( 'def-unexpected-elements', [ location, construct ], {},
1398
- 'Elements only exist in entities, types or typed constructs' );
1399
- }
1400
- else if (prop === 'columns') {
1401
- error( 'extend-columns', [ location, construct ], { art: construct } );
1402
- }
1403
- else { // if (prop === 'enum') {
1404
- error( 'def-unexpected-enum', [ location, construct ], {},
1405
- 'Enum symbols can only be defined for types or typed constructs' );
1160
+ if (!elem.value || elem.kind !== 'element' ||
1161
+ elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1162
+ return;
1163
+ // -> it's a calculated element
1164
+ if (!elem.type && elem.value.type) { // top-level CAST( expr AS type )
1165
+ if (!elem.target)
1166
+ elem.type = { ...elem.value.type, $inferred: 'cast' };
1167
+ }
1168
+ elem.$syntax = 'calc';
1169
+ // TODO: it is not just "syntax" - maybe better test for `$calcDepElement`?
1170
+ createAndLinkCalcDepElement( elem );
1171
+
1172
+ // Special case (hack) for calculated elements that use composition+filter:
1173
+ // See "Notes on `$enclosed`" in `ExposingAssocWithFilter.md` for details.
1174
+ // TODO: hm, only for inferred type - then just do not infer it in that case
1175
+ if (elem.target && elem.value.path?.[elem.value.path.length - 1]?.where) {
1176
+ delete elem.type;
1177
+ delete elem.on;
1178
+ delete elem.target;
1406
1179
  }
1407
- return construct === parent;
1408
1180
  }
1409
1181
 
1410
1182
  /**