@sap/cds-compiler 3.6.0 → 3.7.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 (70) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +9 -5
  4. package/doc/CHANGELOG_BETA.md +20 -2
  5. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  6. package/lib/api/main.js +2 -1
  7. package/lib/api/options.js +3 -2
  8. package/lib/base/dictionaries.js +10 -0
  9. package/lib/base/message-registry.js +56 -12
  10. package/lib/base/messages.js +39 -20
  11. package/lib/base/model.js +1 -0
  12. package/lib/base/shuffle.js +2 -1
  13. package/lib/checks/elements.js +29 -1
  14. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
  15. package/lib/checks/nonexpandableStructured.js +1 -1
  16. package/lib/checks/onConditions.js +8 -5
  17. package/lib/checks/types.js +6 -1
  18. package/lib/checks/validator.js +7 -3
  19. package/lib/compiler/assert-consistency.js +20 -23
  20. package/lib/compiler/base.js +1 -2
  21. package/lib/compiler/builtins.js +2 -2
  22. package/lib/compiler/checks.js +237 -242
  23. package/lib/compiler/define.js +63 -75
  24. package/lib/compiler/extend.js +325 -22
  25. package/lib/compiler/finalize-parse-cdl.js +1 -55
  26. package/lib/compiler/kick-start.js +6 -7
  27. package/lib/compiler/populate.js +284 -288
  28. package/lib/compiler/propagator.js +15 -13
  29. package/lib/compiler/resolve.js +136 -306
  30. package/lib/compiler/shared.js +42 -44
  31. package/lib/compiler/tweak-assocs.js +29 -27
  32. package/lib/compiler/utils.js +29 -3
  33. package/lib/edm/annotations/genericTranslation.js +7 -13
  34. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  35. package/lib/edm/csn2edm.js +0 -4
  36. package/lib/edm/edm.js +6 -4
  37. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  38. package/lib/edm/edmPreprocessor.js +1 -5
  39. package/lib/gen/Dictionary.json +34 -2
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +2429 -2401
  43. package/lib/inspect/inspectPropagation.js +2 -0
  44. package/lib/json/from-csn.js +87 -41
  45. package/lib/json/to-csn.js +47 -16
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +109 -28
  48. package/lib/language/language.g4 +20 -4
  49. package/lib/model/csnRefs.js +30 -2
  50. package/lib/model/csnUtils.js +1 -0
  51. package/lib/model/revealInternalProperties.js +1 -2
  52. package/lib/modelCompare/compare.js +2 -1
  53. package/lib/optionProcessor.js +3 -0
  54. package/lib/render/manageConstraints.js +5 -2
  55. package/lib/render/toCdl.js +20 -7
  56. package/lib/render/toHdbcds.js +2 -8
  57. package/lib/render/toSql.js +6 -5
  58. package/lib/render/utils/common.js +9 -5
  59. package/lib/transform/db/assertUnique.js +2 -1
  60. package/lib/transform/db/expansion.js +2 -0
  61. package/lib/transform/db/flattening.js +37 -36
  62. package/lib/transform/db/rewriteCalculatedElements.js +559 -0
  63. package/lib/transform/db/transformExists.js +15 -6
  64. package/lib/transform/db/views.js +40 -37
  65. package/lib/transform/forRelationalDB.js +44 -30
  66. package/lib/transform/odata/typesExposure.js +50 -15
  67. package/lib/transform/parseExpr.js +14 -8
  68. package/lib/transform/transformUtilsNew.js +6 -5
  69. package/lib/transform/translateAssocsToJoins.js +49 -33
  70. package/package.json +1 -1
@@ -14,7 +14,12 @@ const {
14
14
  forEachGeneric,
15
15
  isDeprecatedEnabled,
16
16
  } = require( '../base/model');
17
- const { setLink, linkToOrigin, withAssociation } = require('./utils');
17
+ const {
18
+ setLink,
19
+ linkToOrigin,
20
+ withAssociation,
21
+ viewFromPrimary,
22
+ } = require('./utils');
18
23
  const $inferred = Symbol.for('cds.$inferred');
19
24
  // const { refString } = require( '../base/messages')
20
25
 
@@ -81,7 +86,7 @@ function propagate( model ) {
81
86
  function run( art ) {
82
87
  if (!art)
83
88
  return;
84
- if (!checkAndSetStatus( art )) {
89
+ if (!checkAndSetStatus( art ) || art.kind === 'select') {
85
90
  runMembers( art );
86
91
  return;
87
92
  }
@@ -254,7 +259,8 @@ function propagate( model ) {
254
259
  }
255
260
 
256
261
  function onlyViaArtifact( prop, target, source ) {
257
- const from = target._from && target._from[0].path;
262
+ const from = viewFromPrimary( target )?.path;
263
+ // do not propagate from member / if follow assoc in from:
258
264
  if (!(from ? from[from.length - 1]._artifact : source)._main)
259
265
  annotation( prop, target, source );
260
266
  }
@@ -280,7 +286,7 @@ function propagate( model ) {
280
286
  if (!viaType)
281
287
  always( prop, target, source );
282
288
  else if (!oldVirtualNotNullPropagation) // NULL would block strange propagation to sub element
283
- target[prop] = { $inferred: 'NULL', val: undefined }; // set null value in Univeral CSN
289
+ target[prop] = { $inferred: 'NULL', val: undefined }; // set null value in Universal CSN
284
290
  }
285
291
 
286
292
  function checkVirtual( view ) {
@@ -331,18 +337,14 @@ function targetMinZero( art ) {
331
337
  }
332
338
 
333
339
  function getOrigin( art ) {
334
- if (art._origin)
340
+ let origin = art._origin;
341
+ while (origin?.kind === 'select')
342
+ origin = origin._origin;
343
+ if (origin)
335
344
  // Do not consider _origin if due to expand of table alias ref
336
- return (!art.expand || art._origin.kind === 'element') && art._origin;
345
+ return (!art.expand || origin.kind === 'element') && origin;
337
346
  // Remark: a column with an 'inline' is never an element -> no need to check
338
347
  // art.inline
339
- if (art._from && art._from.length) { // query
340
- const tabref = art._from[0]._artifact;
341
- return (tabref && tabref.kind === 'element')
342
- ? tabref._effectiveType && tabref._effectiveType.target &&
343
- tabref._effectiveType.target._artifact
344
- : tabref;
345
- }
346
348
 
347
349
  return (art.type && (!art.type.$inferred || art.type.$inferred === 'cast'))
348
350
  ? art.type._artifact
@@ -52,11 +52,9 @@ const { typeParameters } = require('./builtins');
52
52
 
53
53
  const { kindProperties } = require('./base');
54
54
  const {
55
+ pushLink,
55
56
  setLink,
56
57
  setArtifactLink,
57
- annotationHasEllipsis,
58
- pathName,
59
- linkToOrigin,
60
58
  setMemberParent,
61
59
  withAssociation,
62
60
  storeExtension,
@@ -69,7 +67,6 @@ const {
69
67
  } = require('./utils');
70
68
 
71
69
  const detectCycles = require('./cycle-detector');
72
- const { CompilerAssertion } = require('../base/error');
73
70
 
74
71
  const $location = Symbol.for('cds.$location');
75
72
 
@@ -94,12 +91,11 @@ function resolve( model ) {
94
91
  lateExtensions,
95
92
  applyTypeExtensions,
96
93
  effectiveType,
97
- directType,
94
+ getOrigin,
95
+ chooseAnnotationsInArtifact,
96
+ extensionFor,
98
97
  resolveType,
99
98
  resolveTypeArgumentsUnchecked,
100
- populateQuery,
101
- layeredAssignments,
102
- assignmentsOfHighestLayers,
103
99
  } = model.$functions;
104
100
  const { environment } = model.$volatileFunctions;
105
101
  Object.assign( model.$functions, {
@@ -117,7 +113,11 @@ function resolve( model ) {
117
113
  // addImplicitForeignKeys() in populate.js, as we might need to know the
118
114
  // foreign keys in populate.js (foreign key access w/o JOINs).
119
115
 
120
- // Phase 3: calculate keys along simple queries in collected views:
116
+ // Phase 2+3: calculate keys along simple queries in collected views:
117
+ model._entities = Object.values( model.definitions )
118
+ .filter( art => art.$effectiveSeqNo )
119
+ .sort( (x, y) => x.$effectiveSeqNo - y.$effectiveSeqNo );
120
+ model._entities.forEach( setNavigationProjections );
121
121
  model._entities.forEach( propagateKeyProps );
122
122
  // While most dependencies leading have been added at this point, new
123
123
  // cycles could be added later (e.g. via assocs in where conditions),
@@ -146,9 +146,38 @@ function resolve( model ) {
146
146
  }
147
147
 
148
148
  //--------------------------------------------------------------------------
149
- // Phase 3: calculate propagated KEYs
149
+ // Phase 2+3: calculate propagated KEYs
150
150
  //--------------------------------------------------------------------------
151
151
 
152
+ function setNavigationProjections( view ) {
153
+ if (!view.$queries)
154
+ return;
155
+ for (const query of view.$queries) {
156
+ forEachGeneric( query, 'elements', ( elem ) => {
157
+ if (!elem._origin || elem.expand || !elem.value)
158
+ return;
159
+ // TODO: what about elements where _origin is set without value?
160
+ // TODO: or should we push elems with `expand` sibling to extra list for
161
+ // better messages? (Whatever that means exaclty.)
162
+ const nav = pathNavigation( elem.value );
163
+ const { path } = elem.value;
164
+ const item = path[path.length - 1];
165
+ if (nav.navigation && nav.item === item) {
166
+ // sourceElem, alias.sourceElem, mixin:
167
+ // redirectImplicitly( elem, origin );
168
+ pushLink( nav.navigation, '_projections', elem );
169
+ }
170
+ else if (elem._pathHead?.kind === '$inline' && path.length === 1) {
171
+ const hpath = elem._pathHead.value?.path;
172
+ const head = hpath?.length === 1 && hpath[0]._navigation;
173
+ // Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
174
+ if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
175
+ pushLink( head.elements[item.id], '_projections', elem );
176
+ }
177
+ } );
178
+ }
179
+ }
180
+
152
181
  function propagateKeyProps( view ) {
153
182
  // Second argument true ensure that `key` is only propagated along simple
154
183
  // view, i.e. ref or subquery in FROM, not UNION or JOIN.
@@ -293,16 +322,17 @@ function resolve( model ) {
293
322
  function resolveRefs( art ) {
294
323
  if (art.builtin)
295
324
  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())
325
+ // console.log(info( null, [ art.location, art ], {}, 'REFS').toString());
326
+ // console.log(info( null, [ art.location, art ], { art: art.target || 'none' },
327
+ // 'RR: $(ART)').toString());
298
328
  const parent = art._parent;
299
329
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
300
330
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
301
- if (art.key && art.key.val && !art.key.$inferred && !(allowedInMain && isTopLevelElement)) {
331
+ if (art.key?.val && !art.key.$inferred && !(allowedInMain && isTopLevelElement)) {
302
332
  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',
333
+ { '#': allowedInMain ? 'sub' : 'std', keyword: 'key' }, {
334
+ std: '$(KEYWORD) is only supported for elements in an entity or an aspect',
335
+ sub: '$(KEYWORD) is only supported for top-level elements',
306
336
  });
307
337
  }
308
338
  if (art.targetAspect && !(allowedInMain && isTopLevelElement)) {
@@ -348,7 +378,7 @@ function resolve( model ) {
348
378
  if (elemtype && effectiveType( elemtype )) {
349
379
  const assocType = getAssocSpec( elemtype ) || {};
350
380
  if (assocType.on && !obj.on)
351
- obj.on = { $inferred: 'rewrite' };
381
+ obj.on = { $inferred: 'rewrite' }; // TODO: no extra rewrite here
352
382
  if (assocType.targetAspect) {
353
383
  error( 'composition-as-type-of', [ obj.type.location, art ], {},
354
384
  'A managed aspect composition element can\'t be used as type' );
@@ -406,9 +436,7 @@ function resolve( model ) {
406
436
  if (art.$queries)
407
437
  art.$queries.forEach( resolveQuery );
408
438
 
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
-
439
+ // TODO: or should we set silent dependencies in init()?
412
440
  if (obj.elements) { // silent dependencies
413
441
  forEachGeneric( obj, 'elements', elem => dependsOnSilent( art, elem ) );
414
442
  }
@@ -424,11 +452,14 @@ function resolve( model ) {
424
452
 
425
453
  resolveExpr( art.default, 'default', art );
426
454
  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 );
455
+ if (art.type?.$inferred === 'cast')
456
+ inferTypePropertiesFromCast( art );
457
+ if (art.value) {
458
+ if (art.$syntax === 'calc')
459
+ checkCalculatedElement(art);
460
+ else if (art.type && !art.$inferred )
461
+ checkStructureCast(art);
462
+ }
432
463
 
433
464
  annotateMembers( art ); // TODO recheck - recursively, but also forEachMember below
434
465
  chooseAnnotationsInArtifact( art );
@@ -452,36 +483,86 @@ function resolve( model ) {
452
483
  }
453
484
  }
454
485
 
486
+ function checkCalculatedElement( art ) {
487
+ const loc = [ art.value.location, art ];
488
+ if (art._parent.kind === 'element') {
489
+ // TODO: Support calculated elements in structures.
490
+ // The checks below are already aware of those.
491
+ message( 'def-unsupported-calc-elem', loc, { '#': 'nested' } );
492
+ }
493
+
494
+ const allowedInKind = [ 'entity', 'aspect', 'element' ];
495
+ let parent = art._parent;
496
+ while (parent.kind === 'element')
497
+ parent = parent._parent;
498
+
499
+ if (!allowedInKind.includes(art._main.kind)) {
500
+ error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
501
+ }
502
+ else if (!allowedInKind.includes(parent.kind)) {
503
+ error( 'def-invalid-calc-elem', loc, { '#': parent.kind } );
504
+ }
505
+ else if (effectiveType(art)?.elements) {
506
+ if (art.type)
507
+ error( 'type-unexpected-structure', [ art.type.location, art ], { '#': 'calc' } );
508
+ else
509
+ error( 'ref-unexpected-structured', [ art.value.location, art ], { '#': 'expr' } );
510
+ }
511
+ else {
512
+ const noTruthyAllowed = [ 'localized', 'key', 'virtual' ];
513
+ for (const prop of noTruthyAllowed) {
514
+ if (art[prop]?.val) {
515
+ // probably better than a parse error (which is good for DEFAULT vs calc),
516
+ // also appears with parse-cdl:
517
+ error('def-invalid-calc-elem', loc, { '#': prop });
518
+ return; // one error is enough
519
+ }
520
+ }
521
+ }
522
+ }
523
+
524
+ function checkStructureCast( art ) {
525
+ const elem = (art.value.op?.val === 'cast')
526
+ ? art.value.args[0]?._artifact
527
+ : art.value._artifact;
528
+ if (elem && art.type) { // has explicit type
529
+ if (art.type._artifact?.elements) {
530
+ error('type-cast-to-structured', [ art.type.location, art ], {},
531
+ 'Can\'t cast to structured element');
532
+ }
533
+ else if (elem.elements) { // TODO: calc elements
534
+ error('type-cast-structured', [ art.type.location, art ], {},
535
+ 'Structured elements can\'t be cast to a different type');
536
+ }
537
+ }
538
+ }
539
+
455
540
  // Return type containing the assoc spec (keys, on); note that no
456
541
  // propagation/rewrite has been done yet, cyclic dependency must have been
457
542
  // checked before!
458
543
  function getAssocSpec( type ) {
459
- const cyclic = new Set(); // TODO(#8942): May not be necessary if effectiveType() is adapted.
460
544
  // only to be called without cycles
461
- while (type && !cyclic.has(type)) {
462
- cyclic.add(type);
463
- if (type.on || type.foreignKeys || type.targetAspect)
545
+ let unmanaged = null;
546
+ while (type) {
547
+ if (type.on) // if unmanaged, continue trying to find targetAspect
548
+ unmanaged = type;
549
+ else if (type.foreignKeys || type.targetAspect)
464
550
  return type;
465
- type = directType( type );
551
+ type = getOrigin( type );
466
552
  }
467
- return null;
553
+ return unmanaged;
468
554
  }
469
555
 
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' };
480
- }
556
+ function inferTypePropertiesFromCast( elem ) {
557
+ for (const prop of typeParameters.list) {
558
+ if (elem.value[prop])
559
+ elem[prop] = { ...elem.value[prop], $inferred: 'cast' };
481
560
  }
482
561
  }
483
562
 
484
563
  // Phase 4 - annotations ---------------------------------------------------
564
+ // Some functions remain here for the moment, as resolve functionality is called
565
+ // from annotate functions, which should not be the case (TODO!)
485
566
 
486
567
  function annotateUnknown( ext ) {
487
568
  // extensions may have annotations for elements/actions/... which may
@@ -577,16 +658,10 @@ function resolve( model ) {
577
658
  }
578
659
  }
579
660
  if (art?._annotate) {
580
- if (art.kind === 'action' || art.kind === 'function') {
581
- expandParameters( art );
582
- if (art.returns)
583
- effectiveType( art.returns );
584
- }
585
661
  const aor = art.returns || art;
586
662
  const obj = aor.items || aor.targetAspect || aor;
587
663
  // Currently(?), effectiveType() does not calculate the effective type of
588
664
  // its line item:
589
- effectiveType( obj );
590
665
  if (art._annotate.elements) // explicit $expand on aor needed
591
666
  setExpandStatusAnnotate( aor, 'annotate' );
592
667
  annotate( obj, 'element', 'elements', 'enum', art );
@@ -628,259 +703,13 @@ function resolve( model ) {
628
703
  }
629
704
  }
630
705
 
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
- }
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
- }
667
- }
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);
691
- else
692
- model.extensions = [ ext ];
693
- }
694
- 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;
749
- }
750
- }
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
- }
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
- }
774
-
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: '...' } );
784
- }
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
- }
815
- }
816
- }
817
- // console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
818
- return { val: result, literal: 'array' };
819
- }
820
- // function se(a) { return a.upTo ? [a.val,a.upTo.val] : a.val ; }
821
-
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;
833
- }
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;
845
- }
846
-
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;
865
- }
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
- }
877
-
878
706
  // Phase 4 - queries and associations --------------------------------------
879
707
 
880
708
  function resolveQuery( query ) {
881
- if (!query._main) // parse error
709
+ if (!query._main || !query._effectiveType) // parse error
882
710
  return;
883
- traverseQueryPost( query, null, populateQuery ); // TODO: still necessary?
711
+ // TODO: or set silent dependencies in init?
712
+ forEachGeneric( query, 'elements', elem => dependsOnSilent( query, elem ) );
884
713
  forEachGeneric( query, '$tableAliases', ( alias ) => {
885
714
  // console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
886
715
  if (alias.kind === 'mixin')
@@ -889,6 +718,7 @@ function resolve( model ) {
889
718
  // pure path has been resolved, resolve args and filter now:
890
719
  resolveExpr( alias, 'from', query._parent );
891
720
  } );
721
+ // if (!query.$inlines) console.log('RQ:',query)
892
722
  for (const col of query.$inlines)
893
723
  resolveExpr( col.value, 'expr', col, undefined, true );
894
724
  // for (const col of query.$inlines)
@@ -1002,13 +832,9 @@ function resolve( model ) {
1002
832
  return; // avoid subsequent errors
1003
833
  }
1004
834
  else if (target && !obj.foreignKeys && target.kind === 'entity') {
1005
- if (obj.$inferred === 'REDIRECTED') {
1006
- addImplicitForeignKeys( art, obj, target );
1007
- }
1008
- else if (obj.type && obj.type._artifact && obj.type._artifact.internal) {
1009
- // cds.Association, ...
835
+ // redirected or explicit type cds.Association, ...
836
+ if (obj.$inferred === 'REDIRECTED' || obj.type?._artifact?.internal)
1010
837
  addImplicitForeignKeys( art, obj, target );
1011
- }
1012
838
  }
1013
839
 
1014
840
  if (target && !target.$inferred) {
@@ -1072,8 +898,10 @@ function resolve( model ) {
1072
898
  // TODO: add this somehow to tweak-assocs.js ?
1073
899
  function resolveRedirected( elem, target ) {
1074
900
  setLink( elem, '_redirected', null ); // null = do not touch path steps after assoc
1075
- const assoc = directType( elem );
901
+ const assoc = getOrigin( elem );
1076
902
  const origType = assoc && effectiveType( assoc );
903
+ if (origType === 0)
904
+ return;
1077
905
  if (!origType || !origType.target) {
1078
906
  const path = (elem.value && elem.value.path);
1079
907
  const loc = (path && path[path.length - 1] || elem.value || elem).location;
@@ -1240,11 +1068,12 @@ function resolve( model ) {
1240
1068
  // Also: For enums, it points to the enum type, which is why this trick is needed.
1241
1069
  // TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
1242
1070
  // trick may be removed if effectiveType() does not stop at enums.
1071
+ // TODO: this is wrong - we must check typeArt.enum, not its effectiveType
1243
1072
  const cyclic = new Set();
1244
1073
  let effectiveTypeArt = effectiveType( typeArt );
1245
1074
  while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
1246
1075
  cyclic.add(effectiveTypeArt);
1247
- const underlyingEnumType = directType(effectiveTypeArt);
1076
+ const underlyingEnumType = getOrigin(effectiveTypeArt);
1248
1077
  if (underlyingEnumType)
1249
1078
  effectiveTypeArt = effectiveType(underlyingEnumType);
1250
1079
  else
@@ -1261,7 +1090,7 @@ function resolve( model ) {
1261
1090
  if (artWithType[param] !== undefined) {
1262
1091
  if (!params.includes(param)) {
1263
1092
  // Whether the type ref itself is a builtin or a custom type with a builtin as base.
1264
- const type = directType(artWithType);
1093
+ const type = getOrigin(artWithType);
1265
1094
 
1266
1095
  let variant;
1267
1096
  if (type.builtin)
@@ -1274,6 +1103,7 @@ function resolve( model ) {
1274
1103
  // effectiveType is not a builtin -> array or structured
1275
1104
  variant = 'non-scalar';
1276
1105
 
1106
+ // console.log(typeArt.name,artWithType.name,effectiveTypeArt.name)
1277
1107
  error('type-unexpected-argument', [ artWithType[param].location, artWithType ], {
1278
1108
  '#': variant, prop: param, art: artWithType.type, type: effectiveTypeArt,
1279
1109
  });