@sap/cds-compiler 3.6.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +12 -5
  4. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  5. package/doc/CHANGELOG_BETA.md +35 -2
  6. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  7. package/doc/DeprecatedOptions_v2.md +1 -1
  8. package/doc/NameResolution.md +1 -1
  9. package/lib/api/main.js +63 -23
  10. package/lib/api/options.js +1 -0
  11. package/lib/api/validate.js +5 -0
  12. package/lib/base/dictionaries.js +15 -3
  13. package/lib/base/keywords.js +2 -0
  14. package/lib/base/message-registry.js +120 -34
  15. package/lib/base/messages.js +51 -27
  16. package/lib/base/model.js +4 -2
  17. package/lib/base/shuffle.js +2 -1
  18. package/lib/checks/arrayOfs.js +1 -1
  19. package/lib/checks/defaultValues.js +1 -1
  20. package/lib/checks/elements.js +29 -1
  21. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/onConditions.js +15 -9
  25. package/lib/checks/sql-snippets.js +2 -2
  26. package/lib/checks/types.js +5 -1
  27. package/lib/checks/validator.js +7 -3
  28. package/lib/compiler/assert-consistency.js +42 -26
  29. package/lib/compiler/base.js +50 -4
  30. package/lib/compiler/builtins.js +17 -8
  31. package/lib/compiler/checks.js +241 -246
  32. package/lib/compiler/define.js +113 -146
  33. package/lib/compiler/extend.js +889 -383
  34. package/lib/compiler/finalize-parse-cdl.js +5 -58
  35. package/lib/compiler/index.js +1 -1
  36. package/lib/compiler/kick-start.js +7 -8
  37. package/lib/compiler/populate.js +297 -293
  38. package/lib/compiler/propagator.js +27 -18
  39. package/lib/compiler/resolve.js +146 -463
  40. package/lib/compiler/shared.js +36 -79
  41. package/lib/compiler/tweak-assocs.js +30 -28
  42. package/lib/compiler/utils.js +31 -5
  43. package/lib/edm/annotations/genericTranslation.js +131 -59
  44. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  45. package/lib/edm/csn2edm.js +22 -5
  46. package/lib/edm/edm.js +6 -4
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  48. package/lib/edm/edmPreprocessor.js +42 -26
  49. package/lib/gen/Dictionary.json +38 -2
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -1
  52. package/lib/gen/languageLexer.js +1 -1
  53. package/lib/gen/languageParser.js +4828 -4472
  54. package/lib/inspect/inspectPropagation.js +20 -34
  55. package/lib/json/from-csn.js +140 -44
  56. package/lib/json/to-csn.js +114 -122
  57. package/lib/language/errorStrategy.js +2 -0
  58. package/lib/language/genericAntlrParser.js +156 -36
  59. package/lib/language/language.g4 +100 -58
  60. package/lib/language/textUtils.js +13 -0
  61. package/lib/main.d.ts +43 -3
  62. package/lib/main.js +4 -2
  63. package/lib/model/csnRefs.js +15 -3
  64. package/lib/model/csnUtils.js +12 -74
  65. package/lib/model/revealInternalProperties.js +4 -2
  66. package/lib/modelCompare/compare.js +2 -1
  67. package/lib/optionProcessor.js +3 -0
  68. package/lib/render/manageConstraints.js +5 -2
  69. package/lib/render/toCdl.js +216 -104
  70. package/lib/render/toHdbcds.js +2 -9
  71. package/lib/render/toRename.js +14 -51
  72. package/lib/render/toSql.js +4 -3
  73. package/lib/render/utils/common.js +9 -5
  74. package/lib/transform/braceExpression.js +6 -0
  75. package/lib/transform/db/assertUnique.js +2 -1
  76. package/lib/transform/db/expansion.js +2 -0
  77. package/lib/transform/db/flattening.js +37 -36
  78. package/lib/transform/db/rewriteCalculatedElements.js +600 -0
  79. package/lib/transform/db/transformExists.js +4 -0
  80. package/lib/transform/db/views.js +40 -37
  81. package/lib/transform/forOdataNew.js +20 -15
  82. package/lib/transform/forRelationalDB.js +58 -41
  83. package/lib/transform/odata/typesExposure.js +50 -15
  84. package/lib/transform/parseExpr.js +16 -8
  85. package/lib/transform/transformUtilsNew.js +42 -14
  86. package/lib/transform/translateAssocsToJoins.js +60 -37
  87. package/lib/transform/universalCsn/coreComputed.js +15 -7
  88. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  89. package/package.json +2 -1
@@ -46,30 +46,24 @@ const {
46
46
  } = require('../base/model');
47
47
  const { dictAdd } = require('../base/dictionaries');
48
48
  const { dictLocation } = require('../base/location');
49
- const { searchName, weakLocation } = require('../base/messages');
49
+ const { weakLocation } = require('../base/messages');
50
50
  const { combinedLocation } = require('../base/location');
51
51
  const { typeParameters } = require('./builtins');
52
52
 
53
- const { kindProperties } = require('./base');
54
53
  const {
54
+ pushLink,
55
55
  setLink,
56
56
  setArtifactLink,
57
- annotationHasEllipsis,
58
- pathName,
59
- linkToOrigin,
60
57
  setMemberParent,
61
58
  withAssociation,
62
- storeExtension,
63
59
  dependsOn,
64
60
  dependsOnSilent,
65
- setExpandStatusAnnotate,
66
61
  testExpr,
67
62
  targetMaxNotOne,
68
63
  traverseQueryPost,
69
64
  } = require('./utils');
70
65
 
71
66
  const detectCycles = require('./cycle-detector');
72
- const { CompilerAssertion } = require('../base/error');
73
67
 
74
68
  const $location = Symbol.for('cds.$location');
75
69
 
@@ -87,19 +81,11 @@ function resolve( model ) {
87
81
  } = model.$messageFunctions;
88
82
  const {
89
83
  resolvePath,
90
- checkAnnotate,
91
- initAnnotations,
92
- copyAnnotationsForExtensions,
93
- attachAndEmitValidNames,
94
84
  lateExtensions,
95
- applyTypeExtensions,
96
85
  effectiveType,
97
- directType,
86
+ getOrigin,
98
87
  resolveType,
99
88
  resolveTypeArgumentsUnchecked,
100
- populateQuery,
101
- layeredAssignments,
102
- assignmentsOfHighestLayers,
103
89
  } = model.$functions;
104
90
  const { environment } = model.$volatileFunctions;
105
91
  Object.assign( model.$functions, {
@@ -117,7 +103,11 @@ function resolve( model ) {
117
103
  // addImplicitForeignKeys() in populate.js, as we might need to know the
118
104
  // foreign keys in populate.js (foreign key access w/o JOINs).
119
105
 
120
- // Phase 3: calculate keys along simple queries in collected views:
106
+ // Phase 2+3: calculate keys along simple queries in collected views:
107
+ model._entities = Object.values( model.definitions )
108
+ .filter( art => art.$effectiveSeqNo )
109
+ .sort( (x, y) => x.$effectiveSeqNo - y.$effectiveSeqNo );
110
+ model._entities.forEach( setNavigationProjections );
121
111
  model._entities.forEach( propagateKeyProps );
122
112
  // While most dependencies leading have been added at this point, new
123
113
  // cycles could be added later (e.g. via assocs in where conditions),
@@ -126,14 +116,9 @@ function resolve( model ) {
126
116
  // Phase 4: resolve all artifacts:
127
117
  forEachDefinition( model, resolveRefs );
128
118
  forEachGeneric( model, 'vocabularies', resolveRefs );
129
- // for builtin types
130
- forEachGeneric( model.definitions.cds, '_subArtifacts', chooseAnnotationsInArtifact);
131
- forEachGeneric( model.definitions['cds.hana'], '_subArtifacts', chooseAnnotationsInArtifact);
132
- // Phase 6: apply ANNOTATE on auto-exposed entities and unknown artifacts:
133
- lateExtensions( annotateMembers );
134
- if (model.extensions)
135
- model.extensions.map( annotateUnknown );
136
- // Phase 7: report cyclic dependencies:
119
+ // create “super” ANNOTATE statements for annotations on unknown artifacts:
120
+ lateExtensions();
121
+ // report cyclic dependencies:
137
122
  detectCycles( model.definitions, ( user, art, location ) => {
138
123
  if (location) {
139
124
  error( 'ref-cyclic', [ location, user ], { art }, {
@@ -146,9 +131,38 @@ function resolve( model ) {
146
131
  }
147
132
 
148
133
  //--------------------------------------------------------------------------
149
- // Phase 3: calculate propagated KEYs
134
+ // Phase 2+3: calculate propagated KEYs
150
135
  //--------------------------------------------------------------------------
151
136
 
137
+ function setNavigationProjections( view ) {
138
+ if (!view.$queries)
139
+ return;
140
+ for (const query of view.$queries) {
141
+ forEachGeneric( query, 'elements', ( elem ) => {
142
+ if (!elem._origin || elem.expand || !elem.value)
143
+ return;
144
+ // TODO: what about elements where _origin is set without value?
145
+ // TODO: or should we push elems with `expand` sibling to extra list for
146
+ // better messages? (Whatever that means exaclty.)
147
+ const nav = pathNavigation( elem.value );
148
+ const { path } = elem.value;
149
+ const item = path[path.length - 1];
150
+ if (nav.navigation && nav.item === item) {
151
+ // sourceElem, alias.sourceElem, mixin:
152
+ // redirectImplicitly( elem, origin );
153
+ pushLink( nav.navigation, '_projections', elem );
154
+ }
155
+ else if (elem._pathHead?.kind === '$inline' && path.length === 1) {
156
+ const hpath = elem._pathHead.value?.path;
157
+ const head = hpath?.length === 1 && hpath[0]._navigation;
158
+ // Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
159
+ if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
160
+ pushLink( head.elements[item.id], '_projections', elem );
161
+ }
162
+ } );
163
+ }
164
+ }
165
+
152
166
  function propagateKeyProps( view ) {
153
167
  // Second argument true ensure that `key` is only propagated along simple
154
168
  // view, i.e. ref or subquery in FROM, not UNION or JOIN.
@@ -293,16 +307,17 @@ function resolve( model ) {
293
307
  function resolveRefs( art ) {
294
308
  if (art.builtin)
295
309
  return;
296
- // console.log(message( null, art.location, art, {}, 'Info','REFS').toString())
297
- // console.log(message( null, art.location, art, {target:art.target}, 'Info','RR').toString())
310
+ // console.log(info( null, [ art.location, art ], {}, 'REFS').toString());
311
+ // console.log(info( null, [ art.location, art ], { art: art.target || 'none' },
312
+ // 'RR: $(ART)').toString());
298
313
  const parent = art._parent;
299
314
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
300
315
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
301
- if (art.key && art.key.val && !art.key.$inferred && !(allowedInMain && isTopLevelElement)) {
316
+ if (art.key?.val && !art.key.$inferred && !(allowedInMain && isTopLevelElement)) {
302
317
  warning( 'unexpected-key', [ art.key.location, art ],
303
- { '#': allowedInMain ? 'sub' : 'std' }, {
304
- std: 'KEY is only supported for elements in an entity or an aspect',
305
- sub: 'KEY is only supported for top-level elements',
318
+ { '#': allowedInMain ? 'sub' : 'std', keyword: 'key' }, {
319
+ std: '$(KEYWORD) is only supported for elements in an entity or an aspect',
320
+ sub: '$(KEYWORD) is only supported for top-level elements',
306
321
  });
307
322
  }
308
323
  if (art.targetAspect && !(allowedInMain && isTopLevelElement)) {
@@ -348,7 +363,7 @@ function resolve( model ) {
348
363
  if (elemtype && effectiveType( elemtype )) {
349
364
  const assocType = getAssocSpec( elemtype ) || {};
350
365
  if (assocType.on && !obj.on)
351
- obj.on = { $inferred: 'rewrite' };
366
+ obj.on = { $inferred: 'rewrite' }; // TODO: no extra rewrite here
352
367
  if (assocType.targetAspect) {
353
368
  error( 'composition-as-type-of', [ obj.type.location, art ], {},
354
369
  'A managed aspect composition element can\'t be used as type' );
@@ -406,9 +421,7 @@ function resolve( model ) {
406
421
  if (art.$queries)
407
422
  art.$queries.forEach( resolveQuery );
408
423
 
409
- if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
410
- effectiveType(obj); // set _effectiveType if appropriate, (future?): copy elems if extended
411
-
424
+ // TODO: or should we set silent dependencies in init()?
412
425
  if (obj.elements) { // silent dependencies
413
426
  forEachGeneric( obj, 'elements', elem => dependsOnSilent( art, elem ) );
414
427
  }
@@ -424,14 +437,14 @@ function resolve( model ) {
424
437
 
425
438
  resolveExpr( art.default, 'default', art );
426
439
  resolveExpr( art.value, 'expr', art, undefined, art.expand || art.inline );
427
- if (art.value && !art.type && !art.target && !art.elements)
428
- inferTypeFromCast( art );
429
-
430
- if (art.kind === 'element' || art.kind === 'mixin')
431
- effectiveType( art );
432
-
433
- annotateMembers( art ); // TODO recheck - recursively, but also forEachMember below
434
- chooseAnnotationsInArtifact( art );
440
+ if (art.type?.$inferred === 'cast')
441
+ inferTypePropertiesFromCast( art );
442
+ if (art.value) {
443
+ if (art.$syntax === 'calc')
444
+ checkCalculatedElement(art);
445
+ else if (art.type && !art.$inferred )
446
+ checkStructureCast(art);
447
+ }
435
448
 
436
449
  forEachMember( art, resolveRefs, art.targetAspect );
437
450
 
@@ -452,435 +465,98 @@ function resolve( model ) {
452
465
  }
453
466
  }
454
467
 
455
- // Return type containing the assoc spec (keys, on); note that no
456
- // propagation/rewrite has been done yet, cyclic dependency must have been
457
- // checked before!
458
- function getAssocSpec( type ) {
459
- const cyclic = new Set(); // TODO(#8942): May not be necessary if effectiveType() is adapted.
460
- // only to be called without cycles
461
- while (type && !cyclic.has(type)) {
462
- cyclic.add(type);
463
- if (type.on || type.foreignKeys || type.targetAspect)
464
- return type;
465
- type = directType( type );
466
- }
467
- return null;
468
- }
469
-
470
- function inferTypeFromCast( elem ) {
471
- // TODO: think about CAST checks in checks.js
472
- const { op, type } = elem.value;
473
- if (op && op.val === 'cast' && type && type._artifact) {
474
- // op.val is also correctly set with CSN input
475
- elem.type = { ...type, $inferred: 'cast' };
476
- setArtifactLink( elem.type, type._artifact );
477
- for (const prop of typeParameters.list) {
478
- if (elem.value[prop])
479
- elem[prop] = { ...elem.value[prop], $inferred: 'cast' };
468
+ function checkCalculatedElement( art ) {
469
+ const loc = [ art.value.location, art ];
470
+ if (art._parent.kind === 'element') {
471
+ // TODO: Support calculated elements in structures.
472
+ // The checks below are already aware of those.
473
+ message( 'def-unsupported-calc-elem', loc, { '#': 'nested' } );
474
+ }
475
+
476
+ const allowedInKind = [ 'entity', 'aspect', 'element' ];
477
+ let parent = art._parent;
478
+ while (parent.kind === 'element')
479
+ parent = parent._parent;
480
+
481
+ if (!allowedInKind.includes(art._main.kind)) {
482
+ if (art.$inferred === 'include') {
483
+ // even for include-chains, we find the correct ref due to element-expansion.
484
+ const include = art._main.includes.find(i => i._artifact === art._origin._main);
485
+ error('ref-invalid-calc-elem', [ include.location || art.value.location, art ],
486
+ { '#': art._main.kind });
480
487
  }
481
- }
482
- }
483
-
484
- // Phase 4 - annotations ---------------------------------------------------
485
-
486
- function annotateUnknown( ext ) {
487
- // extensions may have annotations for elements/actions/... which may
488
- // themselves may be unknown
489
- forEachMember(ext, annotateUnknown);
490
-
491
- if (ext.$extension) // extension for known artifact -> already applied
492
- return;
493
- annotateMembers( ext );
494
- chooseAnnotationsInArtifact( ext );
495
- }
496
-
497
- /**
498
- * @param {XSN.Artifact} art
499
- * @param {XSN.Extension[]} [extensions]
500
- * @param {string} [prop]
501
- * @param {string} [name]
502
- * @param {object} [parent]
503
- * @param {string} [kind]
504
- */
505
- function annotateMembers( art, extensions, prop, name, parent, kind ) {
506
- const showMsg = !art && parent && parent.kind !== 'annotate';
507
- if (!art && extensions && extensions.length) {
508
- if (Array.isArray( parent ))
509
- return;
510
- const parentExt = extensionFor(parent);
511
- art = parentExt[prop] && parentExt[prop][name];
512
- if (!art) {
513
- art = {
514
- kind, // for setMemberParent()
515
- name: { id: name, location: extensions[0].name.location },
516
- location: extensions[0].location,
517
- };
518
- setMemberParent( art, name, parentExt, prop );
519
- art.kind = 'annotate'; // after setMemberParent()!
520
- }
521
- }
522
-
523
- for (const ext of extensions || []) {
524
- if ('_artifact' in ext.name) // already applied
525
- continue;
526
- setArtifactLink( ext.name, art );
527
-
528
- if (art) {
529
- checkAnnotate( ext, art );
530
- initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
531
- copyAnnotationsForExtensions( ext, art );
532
- // eslint-disable-next-line no-shadow
533
- forEachMember( ext, ( elem, name, prop ) => {
534
- storeExtension( elem, name, prop, art, ext._block );
535
- });
536
- }
537
- if (showMsg) {
538
- // somehow similar to checkDefinitions():
539
- const feature = kindProperties[parent.kind][prop];
540
- if (prop === 'elements' || prop === 'enum') {
541
- if (!feature) {
542
- warning( 'anno-unexpected-elements', [ ext.name.location, art ], {},
543
- 'Elements only exist in entities, types or typed constructs' );
544
- }
545
- else {
546
- const isEntity = (parent.returns?.type || parent.type)?._artifact?.kind === 'entity';
547
- let variant = parent.enum ? 'enum' : 'element';
548
- if (isEntity)
549
- variant = 'entity-element';
550
- else if (parent.returns)
551
- variant = 'returns';
552
- notFound( 'anno-undefined-element', ext.name.location, art,
553
- { '#': variant, art: parent, name },
554
- parent.elements || parent.enum );
555
- }
556
- }
557
- else if (prop === 'actions') {
558
- if (!feature) {
559
- warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
560
- 'Actions and functions only exist top-level and for entities' );
561
- }
562
- else {
563
- notFound( 'anno-undefined-action', ext.name.location, art,
564
- { art: searchName( parent, name, 'action' ) },
565
- parent.actions );
566
- }
567
- }
568
- else if (!feature) {
569
- warning( 'anno-unexpected-params', [ ext.name.location, art ], {},
570
- 'Parameters only exist for actions or functions' );
571
- } // TODO: entities betaMod
572
- else {
573
- notFound( 'anno-undefined-param', ext.name.location, art,
574
- { art: searchName( parent, name, 'param' ) },
575
- parent.params );
576
- }
577
- }
578
- }
579
- if (art?._annotate) {
580
- if (art.kind === 'action' || art.kind === 'function') {
581
- expandParameters( art );
582
- if (art.returns)
583
- effectiveType( art.returns );
488
+ else {
489
+ error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
584
490
  }
585
- const aor = art.returns || art;
586
- const obj = aor.items || aor.targetAspect || aor;
587
- // Currently(?), effectiveType() does not calculate the effective type of
588
- // its line item:
589
- effectiveType( obj );
590
- if (art._annotate.elements) // explicit $expand on aor needed
591
- setExpandStatusAnnotate( aor, 'annotate' );
592
- annotate( obj, 'element', 'elements', 'enum', art );
593
- annotate( art, 'action', 'actions' );
594
- annotate( art, 'param', 'params' );
595
- // const { returns } = art._annotate;
596
- // if (returns) {
597
- // const dict = returns.elements;
598
- // const env = obj.returns && obj.returns.elements || null;
599
- // for (const n in dict)
600
- // annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
601
- // }
602
- }
603
-
604
- if (art?._extendType) {
605
- // Only works because annotateMembers is called in resolveRefs() _after_ `.type` was resolved.
606
- // TODO: If we allow extending included elements, we may need custom $expand,
607
- // similar to annotate above.
608
- art._extendType.forEach(resolveRefs);
609
- applyTypeExtensions(art);
610
- }
611
-
612
- return;
613
-
614
- function notFound( msgId, location, address, args, validDict ) {
615
- // TODO: probably move this to shared.js and use for EXTEND, too
616
- const msg = message( msgId, [ location, address ], args );
617
- attachAndEmitValidNames(msg, validDict);
618
- }
619
-
620
- // eslint-disable-next-line no-shadow
621
- function annotate( obj, kind, prop, altProp, parent = obj ) {
622
- const dict = art._annotate[prop];
623
- if (dict && art._annotate[prop])
624
- setExpandStatusAnnotate( art, 'annotate' );
625
- const env = obj[prop] || altProp && obj[altProp] || null;
626
- for (const n in dict)
627
- annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
628
- }
629
- }
630
-
631
- function expandParameters( action ) {
632
- // see also expandElements()
633
- if (!effectiveType( action ))
634
- return;
635
- const chain = [];
636
- // Should we be able to consider params and returns separately?
637
- // Probably not, let to-csn omit unchanged params/returns.
638
- while (action._origin && !action.params) {
639
- chain.push( action );
640
- action = action._origin;
641
491
  }
642
- chain.reverse();
643
- for (const art of chain) {
644
- const origin = art._origin;
645
- if (!art.params && origin.params) {
646
- for (const name in origin.params) {
647
- // TODO: we could check _annotate here to decide whether we really
648
- // not to create proxies
649
- const orig = origin.params[name];
650
- linkToOrigin( orig, name, art, 'params', weakLocation( orig.location ), true )
651
- .$inferred = 'expanded';
652
- }
653
- }
654
- if (!art.returns && origin.returns) {
655
- // TODO: make linkToOrigin() work for returns, kind/name?
656
- const location = weakLocation( origin.returns.location );
657
- art.returns = {
658
- name: Object.assign( {}, art.name, { id: '', param: '', location } ),
659
- kind: 'param',
660
- location,
661
- $inferred: 'expanded',
662
- };
663
- setLink( art.returns, '_parent', art );
664
- setLink( art.returns, '_main', art._main || art );
665
- setLink( art.returns, '_origin', origin.returns );
666
- }
492
+ else if (!allowedInKind.includes(parent.kind)) {
493
+ error( 'def-invalid-calc-elem', loc, { '#': parent.kind } );
667
494
  }
668
- }
669
-
670
- function extensionFor( art ) {
671
- if (art.kind === 'annotate')
672
- return art;
673
- if (art._extension)
674
- return art._extension;
675
-
676
- // $extension means: already applied
677
- const ext = {
678
- kind: art.kind, // set kind for setMemberParent()
679
- $extension: 'exists',
680
- location: art.location, // location( extension to existing art ) = location(art)
681
- };
682
- const { location } = art.name;
683
- if (!art._main) {
684
- ext.name = {
685
- path: [ { id: art.name.absolute, location } ],
686
- location,
687
- absolute: art.name.absolute,
688
- };
689
- if (model.extensions)
690
- model.extensions.push(ext);
495
+ else if (effectiveType(art)?.elements) {
496
+ if (art.type)
497
+ error( 'type-unexpected-structure', [ art.type.location, art ], { '#': 'calc' } );
691
498
  else
692
- model.extensions = [ ext ];
499
+ error( 'ref-unexpected-structured', [ art.value.location, art ], { '#': 'expr' } );
693
500
  }
694
501
  else {
695
- ext.name = { id: art.name.id, location };
696
- const parent = extensionFor( art._parent );
697
- const kind = kindProperties[art.kind].normalized || art.kind;
698
- // enums would be first in elements
699
- if ( parent[kindProperties[kind].dict] &&
700
- parent[kindProperties[kind].dict][art.name.id] )
701
- throw new CompilerAssertion(art.name.id);
702
- setMemberParent( ext, art.name.id, parent, kindProperties[kind].dict );
703
- }
704
- ext.kind = 'annotate'; // after setMemberParent()!
705
- setLink( art, '_extension', ext );
706
- setArtifactLink( ext.name, art );
707
- if (art.returns)
708
- ext.$syntax = 'returns';
709
- return ext;
710
- }
711
-
712
- /**
713
- * Goes through all (applied) annotations in the given artifact and chooses one
714
- * if multiple exist according to the module layer.
715
- *
716
- * @param {XSN.Artifact} art
717
- */
718
- function chooseAnnotationsInArtifact( art ) {
719
- for (const prop in art) {
720
- if (prop.charAt(0) === '@')
721
- chooseAssignment( prop, art );
722
- }
723
- if (art.doc)
724
- chooseAssignment( 'doc', art );
725
- }
726
-
727
- function chooseAssignment( annoName, art ) {
728
- let anno = art[annoName];
729
- if (!Array.isArray( anno )) { // just one assignment -> use it
730
- if (!annotationHasEllipsis( anno ))
731
- return;
732
- anno = [ anno ];
733
- }
734
- // console.log('ASSIGN:',art.name.absolute,annoName)
735
- const scheduledAssignments = [];
736
- // sort assignment according to layer (define is bottom layer):
737
- const layeredAnnos = layeredAssignments( anno );
738
- let cont = true;
739
- while (cont) {
740
- const { assignments, issue } = assignmentsOfHighestLayers( layeredAnnos );
741
- let index = assignments.length;
742
- cont = !!index; // safety
743
- while (--index >= 0) {
744
- const a = assignments[index];
745
- scheduledAssignments.push( a );
746
- if (!annotationHasEllipsis( a )) {
747
- cont = false;
748
- break;
502
+ const noTruthyAllowed = [ 'localized', 'key', 'virtual' ];
503
+ for (const prop of noTruthyAllowed) {
504
+ if (art[prop]?.val) {
505
+ // probably better than a parse error (which is good for DEFAULT vs calc),
506
+ // also appears with parse-cdl:
507
+ error('def-invalid-calc-elem', loc, { '#': prop });
508
+ return; // one error is enough
749
509
  }
750
510
  }
751
- if (issue) {
752
- // eslint-disable-next-line no-nested-ternary
753
- const msg = (issue === true)
754
- ? 'anno-duplicate'
755
- : (index >= 0) ? 'anno-duplicate-unrelated-layer' : 'anno-unstable-array';
756
- const variant = annoName === 'doc' ? 'doc' : 'std';
757
- for (const a of assignments) {
758
- if (!a.$errorReported) {
759
- message( msg, [ a.name?.location || a.location, art ],
760
- { '#': variant, anno: annoName } );
761
- }
762
- }
763
- }
764
- // else if (index > 0) -- if we allow multiple assignments in one file - the last wins
765
511
  }
766
- // Now apply the assignments - all but the first have a '...'
767
- let result = null;
768
- scheduledAssignments.reverse();
769
- for (const a of scheduledAssignments)
770
- result = applyAssignment( result, a, art, annoName );
771
- art[annoName] = result.name ? result
772
- : Object.assign( {}, scheduledAssignments[scheduledAssignments.length - 1], result );
773
512
  }
774
513
 
775
- function applyAssignment( previousAnno, anno, art, annoName ) {
776
- const hasBase = previousAnno?.literal === 'array';
777
- if (!previousAnno) {
778
- const firstEllipsis = annotationHasEllipsis( anno );
779
- if (!firstEllipsis)
780
- return anno;
781
- if (anno.$priority) { // already complained about with Define
782
- const loc = firstEllipsis.location || anno.name.location;
783
- message( 'anno-unexpected-ellipsis-layers', [ loc, art ], { code: '...' } );
514
+ function checkStructureCast( art ) {
515
+ const elem = (art.value.op?.val === 'cast')
516
+ ? art.value.args[0]?._artifact
517
+ : art.value._artifact;
518
+ if (elem && art.type) { // has explicit type
519
+ if (art.type._artifact?.elements) {
520
+ error('type-cast-to-structured', [ art.type.location, art ], {},
521
+ 'Can\'t cast to structured element');
784
522
  }
785
- previousAnno = { val: [] };
786
- }
787
- else if (previousAnno.literal !== 'array') {
788
- // TODO: If we introduce sub-messages, point to the non-array base value.
789
- error( 'anno-mismatched-ellipsis', [ anno.name.location, art ], { code: '...' } );
790
- previousAnno = { val: [] };
791
- }
792
- const previousValue = previousAnno.val;
793
- let prevPos = 0;
794
- const result = [];
795
- for (const item of anno.val) {
796
- const ell = item && item.literal === 'token' && item.val === '...';
797
- if (!ell) {
798
- result.push( item );
799
- }
800
- else {
801
- let upToSpec = item.upTo && checkUpToSpec( item.upTo, art, annoName, true );
802
- while (prevPos < previousValue.length) {
803
- const prevItem = previousValue[prevPos++];
804
- result.push( prevItem );
805
- if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo)) {
806
- upToSpec = false;
807
- break;
808
- }
809
- }
810
- if (upToSpec && hasBase) {
811
- // non-matched UP TO; if there is no base to apply to, there is already an error.
812
- warning( null, [ item.upTo.location, art ], { anno: annoName, code: '... up to' },
813
- 'The $(CODE) value does not match any item in the base annotation $(ANNO)' );
814
- }
523
+ else if (elem.elements) { // TODO: calc elements
524
+ error('type-cast-structured', [ art.type.location, art ], {},
525
+ 'Structured elements can\'t be cast to a different type');
815
526
  }
816
527
  }
817
- // console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
818
- return { val: result, literal: 'array' };
819
528
  }
820
- // function se(a) { return a.upTo ? [a.val,a.upTo.val] : a.val ; }
821
529
 
822
- function checkUpToSpec( upToSpec, art, annoName, isFullUpTo ) {
823
- const { literal } = upToSpec;
824
- if (!isFullUpTo) { // inside struct of UP TO
825
- if (literal !== 'struct' && literal !== 'array' )
826
- return true;
827
- }
828
- else if (literal === 'struct') {
829
- return Object.values( upToSpec.struct ).every( v => checkUpToSpec( v, art, annoName ) );
830
- }
831
- else if (literal !== 'array' && literal !== 'boolean' && literal !== 'null') {
832
- return true;
530
+ // Return type containing the assoc spec (keys, on); note that no
531
+ // propagation/rewrite has been done yet, cyclic dependency must have been
532
+ // checked before!
533
+ function getAssocSpec( type ) {
534
+ // only to be called without cycles
535
+ let unmanaged = null;
536
+ while (type) {
537
+ if (type.on) // if unmanaged, continue trying to find targetAspect
538
+ unmanaged = type;
539
+ else if (type.foreignKeys || type.targetAspect)
540
+ return type;
541
+ type = getOrigin( type );
833
542
  }
834
- error( null, [ upToSpec.location, art ],
835
- { anno: annoName, code: '... up to', '#': literal },
836
- {
837
- std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
838
- array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
839
- // eslint-disable-next-line max-len
840
- struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
841
- boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
842
- null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
843
- } );
844
- return false;
543
+ return unmanaged;
845
544
  }
846
545
 
847
- function equalUpTo( previousItem, upToSpec ) {
848
- if (!previousItem)
849
- return false;
850
- if ('val' in upToSpec) {
851
- if (previousItem.val === upToSpec.val) // enum, struct and ref have no val
852
- return true;
853
- const typeUpTo = typeof upToSpec.val;
854
- const typePrev = typeof previousItem.val;
855
- if (typeUpTo === 'number')
856
- return typePrev === 'string' && previousItem.val === upToSpec.val.toString();
857
- if (typePrev === 'number')
858
- return typeUpTo === 'string' && upToSpec.val === previousItem.val.toString();
859
- }
860
- else if (upToSpec.path) {
861
- return previousItem.path && normalizeRef( previousItem ) === normalizeRef( upToSpec );
862
- }
863
- else if (upToSpec.sym) {
864
- return previousItem.sym && previousItem.sym.id === upToSpec.sym.id;
546
+ function inferTypePropertiesFromCast( elem ) {
547
+ for (const prop of typeParameters.list) {
548
+ if (elem.value[prop])
549
+ elem[prop] = { ...elem.value[prop], $inferred: 'cast' };
865
550
  }
866
- else if (upToSpec.struct && previousItem.struct) {
867
- return Object.entries( upToSpec.struct )
868
- .every( ([ n, v ]) => equalUpTo( previousItem.struct[n], v ) );
869
- }
870
- return false;
871
- }
872
-
873
- function normalizeRef( node ) { // see to-csn.js
874
- const ref = pathName( node.path );
875
- return node.variant ? `${ ref }#${ node.variant.id }` : ref;
876
551
  }
877
552
 
878
553
  // Phase 4 - queries and associations --------------------------------------
879
554
 
880
555
  function resolveQuery( query ) {
881
- if (!query._main) // parse error
556
+ if (!query._main || !query._effectiveType) // parse error
882
557
  return;
883
- traverseQueryPost( query, null, populateQuery ); // TODO: still necessary?
558
+ // TODO: or set silent dependencies in init?
559
+ forEachGeneric( query, 'elements', elem => dependsOnSilent( query, elem ) );
884
560
  forEachGeneric( query, '$tableAliases', ( alias ) => {
885
561
  // console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
886
562
  if (alias.kind === 'mixin')
@@ -889,6 +565,7 @@ function resolve( model ) {
889
565
  // pure path has been resolved, resolve args and filter now:
890
566
  resolveExpr( alias, 'from', query._parent );
891
567
  } );
568
+ // if (!query.$inlines) console.log('RQ:',query)
892
569
  for (const col of query.$inlines)
893
570
  resolveExpr( col.value, 'expr', col, undefined, true );
894
571
  // for (const col of query.$inlines)
@@ -1002,13 +679,9 @@ function resolve( model ) {
1002
679
  return; // avoid subsequent errors
1003
680
  }
1004
681
  else if (target && !obj.foreignKeys && target.kind === 'entity') {
1005
- if (obj.$inferred === 'REDIRECTED') {
682
+ // redirected or explicit type cds.Association, ...
683
+ if (obj.$inferred === 'REDIRECTED' || obj.type?._artifact?.internal)
1006
684
  addImplicitForeignKeys( art, obj, target );
1007
- }
1008
- else if (obj.type && obj.type._artifact && obj.type._artifact.internal) {
1009
- // cds.Association, ...
1010
- addImplicitForeignKeys( art, obj, target );
1011
- }
1012
685
  }
1013
686
 
1014
687
  if (target && !target.$inferred) {
@@ -1072,8 +745,10 @@ function resolve( model ) {
1072
745
  // TODO: add this somehow to tweak-assocs.js ?
1073
746
  function resolveRedirected( elem, target ) {
1074
747
  setLink( elem, '_redirected', null ); // null = do not touch path steps after assoc
1075
- const assoc = directType( elem );
748
+ const assoc = getOrigin( elem );
1076
749
  const origType = assoc && effectiveType( assoc );
750
+ if (origType === 0)
751
+ return;
1077
752
  if (!origType || !origType.target) {
1078
753
  const path = (elem.value && elem.value.path);
1079
754
  const loc = (path && path[path.length - 1] || elem.value || elem).location;
@@ -1240,11 +915,13 @@ function resolve( model ) {
1240
915
  // Also: For enums, it points to the enum type, which is why this trick is needed.
1241
916
  // TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
1242
917
  // trick may be removed if effectiveType() does not stop at enums.
918
+ // TODO: this is wrong - we must check typeArt.enum, not its effectiveType
919
+ // TODO: this function is not complete(!): parallel `elements` and `length`, … - rework function
1243
920
  const cyclic = new Set();
1244
921
  let effectiveTypeArt = effectiveType( typeArt );
1245
922
  while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
1246
923
  cyclic.add(effectiveTypeArt);
1247
- const underlyingEnumType = directType(effectiveTypeArt);
924
+ const underlyingEnumType = getOrigin(effectiveTypeArt);
1248
925
  if (underlyingEnumType)
1249
926
  effectiveTypeArt = effectiveType(underlyingEnumType);
1250
927
  else
@@ -1261,7 +938,7 @@ function resolve( model ) {
1261
938
  if (artWithType[param] !== undefined) {
1262
939
  if (!params.includes(param)) {
1263
940
  // Whether the type ref itself is a builtin or a custom type with a builtin as base.
1264
- const type = directType(artWithType);
941
+ const type = getOrigin(artWithType);
1265
942
 
1266
943
  let variant;
1267
944
  if (type.builtin)
@@ -1274,6 +951,7 @@ function resolve( model ) {
1274
951
  // effectiveType is not a builtin -> array or structured
1275
952
  variant = 'non-scalar';
1276
953
 
954
+ // console.log(typeArt.name,artWithType.name,effectiveTypeArt.name)
1277
955
  error('type-unexpected-argument', [ artWithType[param].location, artWithType ], {
1278
956
  '#': variant, prop: param, art: artWithType.type, type: effectiveTypeArt,
1279
957
  });
@@ -1347,7 +1025,7 @@ function resolve( model ) {
1347
1025
  }
1348
1026
 
1349
1027
  function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
1350
- const alias = step._navigation && step._navigation.kind === '$tableAlias' && step._navigation;
1028
+ const alias = (step._navigation?.kind === '$tableAlias') ? step._navigation : null;
1351
1029
  const type = alias || effectiveType( step._artifact );
1352
1030
  const art = (type && type.target) ? type.target._artifact : type;
1353
1031
  if (!art)
@@ -1360,14 +1038,19 @@ function resolve( model ) {
1360
1038
  if (step.where)
1361
1039
  resolveExpr( step.where, 'filter', user, environment( type ) );
1362
1040
  }
1363
- else if (step.where && step.where.location || step.cardinality ) {
1041
+ else if (step.where?.location || step.cardinality ) {
1364
1042
  const location = combinedLocation( step.where, step.cardinality );
1043
+ let variant = alias ? 'tableAlias' : 'std';
1044
+ if (expected === 'from')
1045
+ variant = 'from';
1365
1046
  // XSN TODO: filter$location including […]
1366
- message( 'expr-no-filter', [ location, user ], { '#': expected },
1367
- {
1368
- std: 'A filter can only be provided when navigating along associations',
1369
- from: 'A filter can only be provided for the source entity or associations',
1370
- } );
1047
+ message( 'expr-no-filter', [ location, user ], { '#': variant }, {
1048
+ std: 'A filter can only be provided when navigating along associations',
1049
+ // to help users for `… from E:toF { toF[…].x }`
1050
+ // eslint-disable-next-line max-len
1051
+ tableAlias: 'A filter can only be provided when navigating along associations, but found table alias',
1052
+ from: 'A filter can only be provided for the source entity or associations',
1053
+ } );
1371
1054
  }
1372
1055
  }
1373
1056