@sap/cds-compiler 6.4.2 → 6.5.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 (71) hide show
  1. package/CHANGELOG.md +87 -1159
  2. package/README.md +1 -10
  3. package/doc/IncompatibleChanges_v5.md +436 -0
  4. package/doc/IncompatibleChanges_v6.md +659 -0
  5. package/doc/Versioning.md +3 -7
  6. package/lib/api/main.js +1 -0
  7. package/lib/api/options.js +5 -0
  8. package/lib/api/validate.js +3 -0
  9. package/lib/base/message-registry.js +25 -2
  10. package/lib/base/messages.js +1 -1
  11. package/lib/base/model.js +3 -2
  12. package/lib/checks/actionsFunctions.js +6 -3
  13. package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
  14. package/lib/checks/existsInForbiddenPlaces.js +32 -0
  15. package/lib/checks/existsMustEndInAssoc.js +1 -1
  16. package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
  17. package/lib/checks/validator.js +6 -2
  18. package/lib/compiler/assert-consistency.js +5 -7
  19. package/lib/compiler/builtins.js +5 -6
  20. package/lib/compiler/checks.js +4 -8
  21. package/lib/compiler/define.js +244 -459
  22. package/lib/compiler/extend.js +297 -11
  23. package/lib/compiler/finalize-parse-cdl.js +2 -10
  24. package/lib/compiler/generate.js +29 -1
  25. package/lib/compiler/populate.js +21 -63
  26. package/lib/compiler/propagator.js +1 -2
  27. package/lib/compiler/resolve.js +2 -12
  28. package/lib/compiler/shared.js +145 -114
  29. package/lib/compiler/tweak-assocs.js +14 -10
  30. package/lib/compiler/utils.js +97 -0
  31. package/lib/compiler/xpr-rewrite.js +113 -140
  32. package/lib/edm/annotations/edmJson.js +9 -6
  33. package/lib/edm/annotations/genericTranslation.js +8 -4
  34. package/lib/edm/csn2edm.js +3 -4
  35. package/lib/edm/edmInboundChecks.js +1 -2
  36. package/lib/edm/edmPreprocessor.js +3 -3
  37. package/lib/gen/CdlGrammar.checksum +1 -1
  38. package/lib/gen/CdlParser.js +4 -3
  39. package/lib/gen/Dictionary.json +16 -1
  40. package/lib/json/from-csn.js +4 -6
  41. package/lib/json/to-csn.js +3 -3
  42. package/lib/model/csnRefs.js +13 -4
  43. package/lib/model/enrichCsn.js +4 -2
  44. package/lib/optionProcessor.js +8 -4
  45. package/lib/parsers/AstBuildingParser.js +1 -1
  46. package/lib/render/utils/sql.js +3 -2
  47. package/lib/transform/db/applyTransformations.js +1 -1
  48. package/lib/transform/db/assertUnique.js +3 -3
  49. package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
  50. package/lib/transform/db/assocsToQueries/transformExists.js +17 -13
  51. package/lib/transform/db/assocsToQueries/utils.js +1 -6
  52. package/lib/transform/db/backlinks.js +4 -4
  53. package/lib/transform/db/cdsPersistence.js +4 -4
  54. package/lib/transform/db/constraints.js +4 -4
  55. package/lib/transform/db/expansion.js +5 -5
  56. package/lib/transform/db/flattening.js +4 -5
  57. package/lib/transform/db/rewriteCalculatedElements.js +3 -3
  58. package/lib/transform/db/temporal.js +11 -11
  59. package/lib/transform/draft/db.js +2 -0
  60. package/lib/transform/draft/odata.js +5 -7
  61. package/lib/transform/effective/flattening.js +1 -2
  62. package/lib/transform/forOdata.js +3 -3
  63. package/lib/transform/forRelationalDB.js +1 -1
  64. package/lib/transform/localized.js +13 -20
  65. package/lib/transform/odata/createForeignKeys.js +1 -2
  66. package/lib/transform/odata/flattening.js +1 -2
  67. package/lib/transform/odata/toFinalBaseType.js +52 -55
  68. package/lib/transform/transformUtils.js +3 -4
  69. package/package.json +3 -3
  70. package/doc/CHANGELOG_BETA.md +0 -464
  71. 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,8 +133,13 @@ const {
132
133
  const { kindProperties, dictKinds } = require('./base');
133
134
  const {
134
135
  setLink,
136
+ initItemsLinks,
135
137
  setMemberParent,
136
138
  createAndLinkCalcDepElement,
139
+ initExprAnnoBlock,
140
+ initDollarSelf,
141
+ initDollarParameters,
142
+ initBoundSelfParam,
137
143
  storeExtension,
138
144
  dependsOnSilent,
139
145
  pathName,
@@ -173,12 +179,11 @@ function define( model ) {
173
179
  shuffleDict,
174
180
  shuffleArray,
175
181
  initArtifact,
176
- initMembers,
182
+ initMembers, // for finalize-parser-cdl.js
183
+ targetIsTargetAspect,
184
+ checkRedefinition,
177
185
  initSelectItems,
178
186
  } );
179
-
180
- let boundSelfParamType = true; // special `$self` for binding param must still be initialised
181
-
182
187
  return doDefine();
183
188
 
184
189
  /**
@@ -204,16 +209,18 @@ function define( model ) {
204
209
  const sourceNames = shuffleArray( Object.keys( model.sources ) );
205
210
  for (const name of sourceNames)
206
211
  addSource( model.sources[name] );
212
+
213
+ // Phase 2:
207
214
  for (const name of sourceNames)
208
215
  initNamespaceAndUsing( model.sources[name] );
209
216
  dictForEach( model.definitions, initArtifact );
210
217
  dictForEach( model.vocabularies, initVocabulary );
211
218
  dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
212
219
 
213
- addI18nBlocks();
220
+ addI18nBlocks(); // TODO: part of extend.js?
214
221
 
215
222
  const { $self } = model.definitions;
216
- if ($self) {
223
+ if ($self && $self.kind !== 'namespace') { // TODO v7: non-config error
217
224
  message( 'name-deprecated-$self', [ $self.name.location, $self ], { name: '$self' },
218
225
  'Do not use $(NAME) as name for an artifact definition' );
219
226
  }
@@ -234,7 +241,7 @@ function define( model ) {
234
241
 
235
242
  let namespace = src.namespace?.name;
236
243
  let prefix = '';
237
- if (namespace?.path && !namespace.path.broken) {
244
+ if (namespace?.path) {
238
245
  namespace.id = pathName( namespace.path );
239
246
  prefix = `${ namespace.id }.`;
240
247
  }
@@ -242,7 +249,7 @@ function define( model ) {
242
249
  error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
243
250
  { name: 'cds' },
244
251
  'The namespace $(NAME) is reserved for CDS builtins' );
245
- namespace = null;
252
+ namespace = null; // TODO: reconsider
246
253
  }
247
254
  if (src.$frontend !== 'json') { // CDL input
248
255
  // TODO: set _block to builtin
@@ -278,10 +285,12 @@ function define( model ) {
278
285
  }
279
286
 
280
287
  function addDefinition( art, block, prefix ) {
288
+ setLink( art, '_block', block );
289
+ initExprAnnoBlock( art, block );
281
290
  art.name.id ??= prefix + pathName( art.name.path );
282
291
  const absolute = art.name.id;
283
292
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
284
- if (absolute === 'cds' || isInReservedNamespace( absolute )) {
293
+ if (isInReservedNamespace( absolute )) {
285
294
  error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
286
295
  'The namespace $(NAME) is reserved for CDS builtins' );
287
296
  const builtin = model.definitions[absolute];
@@ -297,8 +306,6 @@ function define( model ) {
297
306
  }
298
307
  return;
299
308
  }
300
- setLink( art, '_block', block );
301
- initExprAnnoBlock( art, block );
302
309
  // dictAdd might set $duplicates
303
310
  dictAdd( model.definitions, absolute, art );
304
311
  }
@@ -345,7 +352,7 @@ function define( model ) {
345
352
  return;
346
353
  }
347
354
  const path = decl.extern?.path;
348
- if (!path || path.broken || !path[0]) // syntax error
355
+ if (!path || !path[0]) // syntax error
349
356
  return;
350
357
  decl.extern.id = pathName( path );
351
358
  if (!decl.name)
@@ -354,7 +361,7 @@ function define( model ) {
354
361
  // TODO: check name: no "."
355
362
  const found = src.artifacts[name];
356
363
  // a real `using` declaration is “nicer” than a compiler-generated one:
357
- if (found && found.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
364
+ if (found?.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
358
365
  src.artifacts[name] = decl;
359
366
  else
360
367
  dictAddArray( src.artifacts, name, decl );
@@ -418,57 +425,10 @@ function define( model ) {
418
425
  // Set block number for debugging (--raw-output):
419
426
  // eslint-disable-next-line no-multi-assign
420
427
  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
428
  const prefix = `${ absolute }.`;
424
429
  dictForEach( ext.artifacts, a => addArtifact( a, ext, prefix ) );
425
430
  }
426
431
 
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
- }
470
- }
471
-
472
432
  function addVocabulary( vocab, block, prefix ) {
473
433
  setLink( vocab, '_block', block );
474
434
  const { name } = vocab;
@@ -476,6 +436,8 @@ function define( model ) {
476
436
  dictAdd( model.vocabularies, name.id, vocab );
477
437
  }
478
438
 
439
+ // I18n code (eventually part of extend.js) -----------------------------------
440
+
479
441
  /**
480
442
  * Add (optional) translations into the XSN model.
481
443
  */
@@ -525,29 +487,6 @@ function define( model ) {
525
487
  // Functions called from top-level: initNamespaceAndUsing(), initArtifact(),
526
488
  // initVocabulary(), initExtension()
527
489
 
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
490
  function initNamespaceAndUsing( src ) {
552
491
  if (src.$frontend && src.$frontend !== 'cdl')
553
492
  return;
@@ -587,12 +526,11 @@ function define( model ) {
587
526
  }
588
527
 
589
528
  function initArtifact( art, reInit = false ) {
590
- if (!reInit)
529
+ if (!reInit) // not for auto-exposed entity
591
530
  initArtifactParentLink( art, model.definitions );
592
- const block = art._block;
593
531
  checkRedefinition( art );
594
532
  initDollarSelf( art ); // $self
595
- initMembers( art, art, block );
533
+ initMembers( art );
596
534
  if (art.params)
597
535
  initDollarParameters( art ); // $parameters
598
536
  if (art.query) {
@@ -601,9 +539,105 @@ function define( model ) {
601
539
  }
602
540
  }
603
541
 
542
+ function initVocabulary( art ) {
543
+ initArtifactParentLink( art, model.vocabularies );
544
+ checkRedefinition( art );
545
+ initMembers( art );
546
+
547
+ if (art.query) {
548
+ initArtifactQuery( art );
549
+ error( 'def-unsupported-projection', [ art.location, art ], null,
550
+ 'Projections for annotation definitions are not supported' );
551
+ }
552
+ }
553
+
554
+ function initExtension( parent ) {
555
+ // TODO: re-think
556
+ forEachMember( parent, function initExtensionMember( sub, name, prop ) {
557
+ if (sub.kind !== 'extend' && sub.kind !== 'annotate')
558
+ return; // for defs inside, set somewhere else - TODO: rethink
559
+ if (prop === 'params' && name === '') // RETURNS
560
+ sub.name = { id: '', location: sub.location };
561
+ setLink( sub, '_block', parent._block );
562
+ initExprAnnoBlock( sub, parent._block );
563
+ setLink( sub, '_parent', parent );
564
+ setLink( sub, '_main', parent._main || parent );
565
+ initExtension( sub );
566
+ } );
567
+ if (parent.kind !== 'extend')
568
+ return;
569
+ // TODO: sub queries? expand/inline?
570
+ parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
571
+ if (parent.scale && !parent.precision) {
572
+ // TODO: where could we store the location of the name?
573
+ error( 'syntax-missing-type-property', [ parent.scale.location ],
574
+ { prop: 'scale', otherprop: 'precision' },
575
+ 'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
576
+ parent.scale = undefined; // no consequential error
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Make the `_parent` pointer of artifact `A.B.C` point to `A.B`. If no `A.B`
582
+ * exists in XSN.definitions, create an artifact with kind `namespace` (better
583
+ * would be `$gap`). Add `A.B.C` to the `_subArtifacts` dictionary of `A.B`.
584
+ * Do recursively if necessary, `_parent` of `A.B` is `A` in this example.
585
+ */
586
+ function initArtifactParentLink( art, definitions, path, pathIndex ) {
587
+ setLink( art, '_parent', null );
588
+ const { id } = art.name;
589
+ const dot = id.lastIndexOf( '.' );
590
+ if (dot < 0)
591
+ return;
592
+ const prefix = id.substring( 0, dot );
593
+ let parent = definitions[prefix];
594
+ if (!parent) {
595
+ path ??= art.name.path;
596
+ pathIndex ??= path?.length - 1;
597
+ const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
598
+ const location = weakLocation( pathItemOrName.location );
599
+ parent = { kind: 'namespace', name: { id: prefix, location }, location };
600
+ definitions[prefix] = parent;
601
+ initArtifactParentLink( parent, definitions, path, pathIndex );
602
+ }
603
+ setLink( art, '_parent', parent );
604
+ if (!parent._subArtifacts)
605
+ setLink( parent, '_subArtifacts', Object.create( null ) );
606
+ if (art.$duplicates !== true) // no redef or "first def"
607
+ parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
608
+ }
609
+
610
+ // TODO: message ids
611
+ // Called by initArtifact() and initVocabulary() and for members
612
+ function checkRedefinition( art ) {
613
+ if (art.kind === 'annotate' || art.kind === 'extend') // move this check to call in extend.js?
614
+ return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
615
+ if (!art.$duplicates || !art.name.id ||
616
+ art.$errorReported === 'syntax-duplicate-extend')
617
+ return;
618
+ if (art._main) {
619
+ error( 'duplicate-definition', [ art.name.location, art ], {
620
+ name: art.name.id,
621
+ '#': kindProperties[art.kind].normalized || art.kind,
622
+ } );
623
+ }
624
+ else if (!art.builtin) {
625
+ // TODO: better messages with definitions with the same name as builtin,
626
+ // especially if there is just one
627
+ error( 'duplicate-definition', [ art.name.location, art ], {
628
+ name: art.name.id,
629
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
630
+ } );
631
+ }
632
+ }
633
+
634
+ // Phase 2, init queries ------------------------------------------------------
635
+
604
636
  /**
605
637
  * Restrict the query of `art` to only simple projections, i.e. those without 'group by', etc.
606
638
  *
639
+ * TODO v8 and for `type`: do in parsers
640
+ *
607
641
  * @param {XSN.Artifact} art
608
642
  */
609
643
  function restrictToSimpleProjection( art ) {
@@ -659,88 +693,6 @@ function define( model ) {
659
693
  }
660
694
  }
661
695
 
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
696
  // art is:
745
697
  // - entity/event/type for top-level queries (including UNION args)
746
698
  // - $tableAlias for sub query in FROM - TODO: what about UNION there?
@@ -749,7 +701,7 @@ function define( model ) {
749
701
  if (!query) // parse error
750
702
  return query;
751
703
  if (query.from) { // select
752
- initQuery();
704
+ initQuery( query, art );
753
705
  initTableExpression( query.from, query, [] );
754
706
  if (query.mixin)
755
707
  initMixins( query, art );
@@ -794,21 +746,21 @@ function define( model ) {
794
746
  return undefined;
795
747
  }
796
748
  return query._leadingQuery || query;
749
+ }
797
750
 
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
- }
751
+ function initQuery( query, art ) {
752
+ setLink( query, '_block', art._block );
753
+ const main = art._main || art;
754
+ setLink( query, '_$next',
755
+ (art.kind === '$tableAlias' ? art._parent._$next : art ) );
756
+ query.kind = 'select';
757
+ query.name = { location: query.location, id: main.$queries.length + 1 };
758
+ setMemberParent( query, null, main );
759
+ // console.log(art.kind,art.name,query.name,query._$next.name)
760
+ main.$queries.push( query );
761
+ setLink( query, '_parent', art ); // _parent should point to alias/main/query
762
+ query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
763
+ dependsOnSilent( main, query );
812
764
  }
813
765
 
814
766
  // table is table expression in FROM, becomes an alias
@@ -816,7 +768,7 @@ function define( model ) {
816
768
  if (!table) // parse error
817
769
  return;
818
770
  if (table.path) { // path in FROM
819
- if (!table.path.length || table.path.broken)
771
+ if (!table.path.length)
820
772
  // parse error (e.g. final ',' in FROM), projection on <eof>
821
773
  return;
822
774
  if (!table.name) {
@@ -826,7 +778,7 @@ function define( model ) {
826
778
  // TODO: if we have too much time, we can calculate the real location with '.'
827
779
  table.name = { $inferred: 'as', id, location: last.location };
828
780
  }
829
- addAsAlias();
781
+ addAsAlias( table, query, joinParents );
830
782
  // _origin is set when we resolve the ref
831
783
  if (query._parent.kind !== 'select')
832
784
  query._main._from.push( table ); // store tabref if outside "real" subquery
@@ -838,7 +790,7 @@ function define( model ) {
838
790
  const id = `$_select_${ query._main.$queries.length + 1 }__`;
839
791
  table.name = { id, location: table.location, $inferred: '$internal' };
840
792
  }
841
- addAsAlias();
793
+ addAsAlias( table, query, joinParents );
842
794
  // Store _origin to leading query of table.query for name resolution
843
795
  setLink( table, '_origin', initQueryExpression( table.query, table ) );
844
796
  }
@@ -878,37 +830,36 @@ function define( model ) {
878
830
  // TODO: probably set this to query if we switch to name restriction in JOIN
879
831
  }
880
832
  }
881
- return;
833
+ }
882
834
 
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 );
835
+ function addAsAlias( table, query, joinParents ) {
836
+ table.kind = '$tableAlias';
837
+ setLink( table, '_block', query._block );
838
+ setMemberParent( table, table.name.id, query );
839
+ dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
840
+ if (tableAlias.name.$inferred === '$internal') {
841
+ const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
842
+ // TODO: the semanticLoc query is not initialized yet, and thus cannot
843
+ // be used here
844
+ error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
845
+ { '#': 'duplicate', code: 'as ‹alias›' } );
904
846
  }
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
- } );
847
+ else {
848
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
911
849
  }
850
+ } );
851
+ // also add to JOIN nodes for name restrictions:
852
+ for (const p of joinParents) {
853
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
854
+ // already used for duplicate aliases of queries:
855
+ dictAddArray( p.$tableAliases, table.name.id, table );
856
+ }
857
+ if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
858
+ message( 'name-invalid-dollar-alias', [ table.name.location, table ], {
859
+ '#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
860
+ name: '$',
861
+ keyword: 'as',
862
+ } );
912
863
  }
913
864
  }
914
865
 
@@ -921,7 +872,7 @@ function define( model ) {
921
872
  initExprForQuery( query.where, query );
922
873
  if (query.having)
923
874
  initExprForQuery( query.having, query );
924
- initMembers( query, query, query._block );
875
+ initMembers( query );
925
876
  }
926
877
 
927
878
  function initExprForQuery( expr, query ) {
@@ -939,11 +890,6 @@ function define( model ) {
939
890
  const args = Array.isArray( expr.args ) ? expr.args : Object.values( expr.args );
940
891
  args.forEach( e => initExprForQuery( e, query ) );
941
892
  }
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
893
  }
948
894
 
949
895
  function initMixins( query, art ) {
@@ -972,15 +918,18 @@ function define( model ) {
972
918
  let wildcard = !!inExtension; // no `extend … with columns { * }`
973
919
  // TODO: forbid expand/inline in ref-where, outside queries (CSN), ...
974
920
  let hasItems = false;
921
+ let colIndex = 0;
975
922
  for (const col of columns || parent.expand || parent.inline || []) {
923
+ ++colIndex;
976
924
  if (!col) // parse error
977
925
  continue;
978
926
  hasItems = true;
979
927
  if (!columns) { // expand or inline
980
928
  if (parent.value)
981
- setLink( col, '_columnParent', parent ); // also set for '*' in expand/inline
929
+ setLink( col, '_columnParent', parent ); // TODO?: also set for '*' in expand/inline
982
930
  else if (parent._columnParent)
983
931
  setLink( col, '_columnParent', parent._columnParent );
932
+ // colParent not set for `{ col } as struct`, except if that is in expand/inline
984
933
  }
985
934
  if (col.val === '*') {
986
935
  if (!wildcard) {
@@ -1009,10 +958,13 @@ function define( model ) {
1009
958
  }
1010
959
  }
1011
960
  // Either expression (value), expand, new virtual or new association
1012
- else if (col.value || col.name) {
961
+ else {
1013
962
  col.kind = 'element';
1014
963
  if (!col._block)
1015
964
  setLink( col, '_block', parent._block );
965
+ setMemberParent( col, null, parent );
966
+ // remark: _parent might be change if col is in expand of to-many assoc
967
+ ensureColumnName( col, colIndex, parent, !columns );
1016
968
  if (col.inline) { // `@anno elem.{ * }` does not work
1017
969
  if (col.doc) {
1018
970
  message( 'syntax-unexpected-anno', [ col.doc.location, col ],
@@ -1029,7 +981,8 @@ function define( model ) {
1029
981
  // TODO: allow sub queries? at least in top-level expand without parallel ref
1030
982
  if (columns && !inExtension) // not (yet) in `extend … with columns {…}`
1031
983
  initExprForQuery( col.value, parent );
1032
- initSelectItems( col, null, user ); // TODO: use col as user (i.e. remove param)
984
+ if (col.expand || col.inline)
985
+ initSelectItems( col, null, user ); // TODO: use col, remove 3rd param?
1033
986
  }
1034
987
 
1035
988
  initCdlTypeCast( col, parent );
@@ -1044,76 +997,81 @@ function define( model ) {
1044
997
  }
1045
998
  }
1046
999
 
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
+ }
1040
+
1047
1041
  function initCdlTypeCast( col, parent ) {
1048
1042
  if (col.val)
1049
1043
  return; // e.g. '*' column
1050
1044
 
1051
1045
  setMemberParent( col, col.name, parent );
1052
- initMembers( col, col, col._block );
1046
+ initMembers( col );
1053
1047
 
1054
1048
  // We don't allow CDL-style casts to anonymous structures. We reject it already here
1055
1049
  // and not in checks.js to ensure that it's rejected in parseCdl.
1050
+ // TODO: via <guard> in CdlGrammar.g4
1056
1051
  if (col.elements) {
1057
- error('type-invalid-cast', [ col.elements[$location] ?? col.location, col ],
1058
- { '#': 'to-inline-structure' });
1052
+ error( 'type-invalid-cast', [ (col.elements[$location] ?? col.location), col ],
1053
+ { '#': 'to-inline-structure' } );
1059
1054
  }
1060
1055
  else if (col.expand && (col.type || col.elements || col.items)) {
1061
1056
  const loc = (col.type?.location || col.elements?.[$location] ||
1062
- col.items?.location || col.location);
1063
- error('type-invalid-cast', [ loc, col ], { '#': 'expand' });
1057
+ col.items?.location || col.location);
1058
+ error( 'type-invalid-cast', [ loc, col ], { '#': 'expand' } );
1064
1059
  }
1060
+ // Remark: there are quite a few consequential error, TODO: avoid them
1065
1061
  }
1066
1062
 
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
1063
  // Members (elements, enum, actions, params): ---------------------------------
1100
1064
 
1101
1065
  /**
1102
1066
  * Set property `_parent` for all elements in `parent` to `parent` and do so
1103
1067
  * recursively for all sub elements.
1104
1068
  *
1105
- * If not for extensions: construct === parent
1106
- *
1107
1069
  * Param `initExtensions` is for parse.cdl - TODO delete
1108
- *
1109
- * TODO: separate extension!
1110
1070
  */
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 );
1071
+ function initMembers( parent, initExtensions = false ) {
1072
+ const block = parent._block;
1073
+ let obj = initItemsLinks( parent, block );
1074
+ initExprAnnoBlock( parent, block );
1117
1075
  if (obj.target && targetIsTargetAspect( obj )) {
1118
1076
  obj.targetAspect = obj.target;
1119
1077
  delete obj.target;
@@ -1121,74 +1079,30 @@ function define( model ) {
1121
1079
  const { targetAspect } = obj;
1122
1080
  if (targetAspect) {
1123
1081
  if (obj.foreignKeys) {
1124
- error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], construct ] );
1082
+ error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], parent ] );
1125
1083
  delete obj.foreignKeys; // continuation semantics: not specified
1126
1084
  }
1127
1085
  if (obj.on && !obj.target) {
1128
- error( 'type-unexpected-on-condition', [ obj.on.location, construct ] );
1086
+ error( 'type-unexpected-on-condition', [ obj.on.location, parent ] );
1129
1087
  delete obj.on; // continuation semantics: not specified
1130
1088
  }
1131
1089
  if (targetAspect.elements)
1132
1090
  initAnonymousAspect();
1133
1091
  }
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 );
1142
- }
1092
+ forEachInOrder( obj, 'elements', init );
1093
+ forEachGeneric( obj, 'enum', init );
1094
+ forEachInOrder( obj, 'foreignKeys', init );
1095
+ forEachGeneric( parent, 'actions', init );
1096
+ forEachInOrder( parent, 'params', init );
1143
1097
 
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;
1098
+ const { returns } = parent;
1151
1099
  if (returns) {
1152
- const { kind } = construct;
1100
+ const { kind } = parent;
1153
1101
  returns.kind = (kind === 'extend' || kind === 'annotate') ? kind : 'param';
1154
1102
  init( returns, '' ); // '' is special name for returns parameter
1155
1103
  }
1156
1104
  return;
1157
1105
 
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
1106
  function initAnonymousAspect() {
1193
1107
  // TODO: main?
1194
1108
  const inEntity = parent._main?.kind === 'entity';
@@ -1198,7 +1112,6 @@ function define( model ) {
1198
1112
  setLink( targetAspect, '_main', null ); // for name resolution
1199
1113
 
1200
1114
  parent = targetAspect;
1201
- construct = parent; // avoid extension behavior
1202
1115
  targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1203
1116
  setLink( targetAspect, '_block', block );
1204
1117
  initDollarSelf( targetAspect );
@@ -1243,24 +1156,15 @@ function define( model ) {
1243
1156
  storeExtension( elem, name, prop, parent, block );
1244
1157
  return;
1245
1158
  }
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
- }
1252
1159
 
1253
1160
  const bl = elem._block || block;
1254
1161
  setLink( elem, '_block', bl );
1255
- const existing = parent[prop]?.[name];
1256
- const add = construct !== parent && (!existing || elem.$inferred !== 'include');
1162
+
1257
1163
  // 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 );
1164
+ setMemberParent( elem, name, parent );
1261
1165
  checkRedefinition( elem );
1262
- initMembers( elem, elem, bl, initExtensions );
1263
- if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
1166
+ initMembers( elem, initExtensions );
1167
+ if (elem.kind === 'action' || elem.kind === 'function')
1264
1168
  initBoundSelfParam( elem.params, elem._main );
1265
1169
 
1266
1170
  // for a correct home path, setMemberParent needed to be called
@@ -1269,7 +1173,7 @@ function define( model ) {
1269
1173
  elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1270
1174
  return;
1271
1175
  // -> it's a calculated element
1272
- if (!elem.type && elem.value?.type) { // top-level CAST( expr AS type )
1176
+ if (!elem.type && elem.value.type) { // top-level CAST( expr AS type )
1273
1177
  if (!elem.target)
1274
1178
  elem.type = { ...elem.value.type, $inferred: 'cast' };
1275
1179
  }
@@ -1279,6 +1183,7 @@ function define( model ) {
1279
1183
 
1280
1184
  // Special case (hack) for calculated elements that use composition+filter:
1281
1185
  // See "Notes on `$enclosed`" in `ExposingAssocWithFilter.md` for details.
1186
+ // TODO: hm, only for inferred type - then just do not infer it in that case
1282
1187
  if (elem.target && elem.value.path?.[elem.value.path.length - 1]?.where) {
1283
1188
  delete elem.type;
1284
1189
  delete elem.on;
@@ -1287,126 +1192,6 @@ function define( model ) {
1287
1192
  }
1288
1193
  }
1289
1194
 
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;
1298
- }
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
- }
1311
- }
1312
-
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
- }
1332
-
1333
- // To be reworked -------------------------------------------------------------
1334
-
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];
1347
-
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' );
1406
- }
1407
- return construct === parent;
1408
- }
1409
-
1410
1195
  /**
1411
1196
  * Return whether the `target` is actually a `targetAspect`
1412
1197
  */