@sap/cds-compiler 3.7.2 → 3.8.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 +71 -4
  2. package/bin/cdsc.js +3 -0
  3. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  4. package/doc/CHANGELOG_BETA.md +15 -0
  5. package/doc/DeprecatedOptions_v2.md +1 -1
  6. package/doc/NameResolution.md +1 -1
  7. package/lib/api/main.js +61 -22
  8. package/lib/api/options.js +1 -0
  9. package/lib/api/validate.js +5 -0
  10. package/lib/base/dictionaries.js +5 -3
  11. package/lib/base/keywords.js +2 -0
  12. package/lib/base/message-registry.js +64 -22
  13. package/lib/base/messages.js +12 -7
  14. package/lib/base/model.js +3 -2
  15. package/lib/checks/arrayOfs.js +1 -1
  16. package/lib/checks/defaultValues.js +1 -1
  17. package/lib/checks/hasPersistedElements.js +1 -1
  18. package/lib/checks/invalidTarget.js +1 -1
  19. package/lib/checks/onConditions.js +9 -6
  20. package/lib/checks/sql-snippets.js +2 -2
  21. package/lib/checks/types.js +1 -2
  22. package/lib/compiler/assert-consistency.js +25 -6
  23. package/lib/compiler/base.js +51 -2
  24. package/lib/compiler/builtins.js +15 -6
  25. package/lib/compiler/checks.js +4 -4
  26. package/lib/compiler/define.js +59 -80
  27. package/lib/compiler/extend.js +717 -498
  28. package/lib/compiler/finalize-parse-cdl.js +4 -3
  29. package/lib/compiler/index.js +1 -1
  30. package/lib/compiler/kick-start.js +2 -2
  31. package/lib/compiler/populate.js +17 -9
  32. package/lib/compiler/propagator.js +12 -5
  33. package/lib/compiler/resolve.js +26 -173
  34. package/lib/compiler/shared.js +20 -58
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +2 -2
  37. package/lib/edm/annotations/genericTranslation.js +124 -46
  38. package/lib/edm/csn2edm.js +22 -1
  39. package/lib/edm/edmPreprocessor.js +41 -21
  40. package/lib/gen/Dictionary.json +4 -0
  41. package/lib/gen/language.checksum +1 -1
  42. package/lib/gen/language.interp +3 -1
  43. package/lib/gen/languageLexer.js +1 -1
  44. package/lib/gen/languageParser.js +4844 -4508
  45. package/lib/inspect/inspectPropagation.js +20 -36
  46. package/lib/json/from-csn.js +56 -7
  47. package/lib/json/to-csn.js +71 -110
  48. package/lib/language/errorStrategy.js +1 -0
  49. package/lib/language/genericAntlrParser.js +49 -9
  50. package/lib/language/language.g4 +106 -83
  51. package/lib/language/textUtils.js +13 -0
  52. package/lib/main.d.ts +43 -3
  53. package/lib/main.js +4 -2
  54. package/lib/model/csnRefs.js +19 -4
  55. package/lib/model/csnUtils.js +11 -74
  56. package/lib/model/revealInternalProperties.js +3 -0
  57. package/lib/optionProcessor.js +3 -0
  58. package/lib/render/toCdl.js +203 -104
  59. package/lib/render/toHdbcds.js +0 -1
  60. package/lib/render/toRename.js +14 -51
  61. package/lib/transform/braceExpression.js +6 -0
  62. package/lib/transform/db/rewriteCalculatedElements.js +55 -14
  63. package/lib/transform/forOdataNew.js +20 -15
  64. package/lib/transform/forRelationalDB.js +21 -14
  65. package/lib/transform/parseExpr.js +2 -0
  66. package/lib/transform/transformUtilsNew.js +36 -9
  67. package/lib/transform/translateAssocsToJoins.js +11 -4
  68. package/lib/transform/universalCsn/coreComputed.js +15 -7
  69. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  70. package/package.json +2 -1
@@ -29,17 +29,18 @@ function finalizeParseCdl( model ) {
29
29
  resolveUncheckedPath,
30
30
  resolveTypeArgumentsUnchecked,
31
31
  initMembers,
32
- extensionsDict,
33
32
  } = model.$functions;
34
33
 
35
34
  resolveTypesAndExtensionsForParseCdl();
36
35
  return;
37
36
 
38
37
  function resolveTypesAndExtensionsForParseCdl() {
38
+ const late = model.$lateExtensions;
39
39
  const extensions = [];
40
40
 
41
- for (const name in extensionsDict) {
42
- for (const ext of extensionsDict[name]) {
41
+ // TODO: why not just use the extensions as they are from the first source?
42
+ for (const name in late) {
43
+ for (const ext of late[name]._extensions) {
43
44
  ext.name.absolute = resolveUncheckedPath( ext.name, 'extend', ext );
44
45
  // Initialize members and define annotations in sub-elements.
45
46
  initMembers( ext, ext, ext._block, true );
@@ -15,7 +15,7 @@
15
15
  'use strict';
16
16
 
17
17
  const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
18
- const parseLanguage = require('../language/antlrParser');
18
+ const parseLanguage = require('../language/antlrParser'); // TODO: should we do some lazyload here?
19
19
  const parseCsn = require('../json/from-csn');
20
20
 
21
21
  const assertConsistency = require('./assert-consistency');
@@ -31,9 +31,9 @@ function kickStart( model ) {
31
31
  */
32
32
  function setAncestorsAndService( name ) {
33
33
  const art = model.definitions[name];
34
- if (!('_parent' in art))
34
+ if (art._parent === undefined)
35
35
  return; // nothing to do for builtins and redefinitions
36
- if (art.query && !('_ancestors' in art))
36
+ if (art.query && art._ancestors === undefined)
37
37
  setProjectionAncestors( art );
38
38
 
39
39
  let parent = art._parent;
@@ -26,7 +26,6 @@ const {
26
26
  const {
27
27
  dictAdd, dictAddArray, dictFirst, dictForEach,
28
28
  } = require('../base/dictionaries');
29
- const { dictLocation } = require('../base/location');
30
29
  const { weakLocation } = require('../base/messages');
31
30
  const { CompilerAssertion } = require('../base/error');
32
31
 
@@ -48,6 +47,7 @@ const {
48
47
  } = require('./utils');
49
48
 
50
49
  const $inferred = Symbol.for('cds.$inferred');
50
+ const $location = Symbol.for('cds.$location');
51
51
 
52
52
 
53
53
  // Export function of this file.
@@ -61,6 +61,8 @@ function populate( model ) {
61
61
  resolvePath,
62
62
  attachAndEmitValidNames,
63
63
  initArtifact,
64
+ chooseAnnotationsInArtifact,
65
+ extendArtifactAfter,
64
66
  } = model.$functions;
65
67
  model.$volatileFunctions.environment = environment;
66
68
  Object.assign( model.$functions, {
@@ -191,13 +193,13 @@ function populate( model ) {
191
193
  if (!art)
192
194
  return art;
193
195
  // if (--depth) throw Error(`ET: ${ Object.keys(art) }`)
194
- if ('_effectiveType' in art)
196
+ if (art._effectiveType !== undefined)
195
197
  return art._effectiveType;
196
198
 
197
199
  // console.log(message( null, art.location, art, {}, 'Info','FT').toString())
198
200
  const chain = [];
199
201
  // console.log( 'ET-START:', art.kind, art.name )
200
- while (art && !('_effectiveType' in art)) {
202
+ while (art && art._effectiveType === undefined) {
201
203
  setLink( art, '_effectiveType', 0 ); // initial setting in case of cycles
202
204
  chain.push( art );
203
205
  art = getOrigin( art );
@@ -210,13 +212,15 @@ function populate( model ) {
210
212
 
211
213
  chain.reverse();
212
214
  for (const a of chain) {
213
- // console.log( 'ET-DO:', a.name, art?.kind )
214
215
  // Without type, value.path or _origin at beginning, link to itself:
216
+ chooseAnnotationsInArtifact( a );
215
217
  art = populateArtifact( a, art ) || a;
216
218
  if (a.elements$ || a.enum$)
217
219
  mergeSpecifiedElementsOrEnum( a );
218
220
  setLink( a, '_effectiveType', art );
219
221
  a.$effectiveSeqNo = ++effectiveSeqNo;
222
+ // console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
223
+ extendArtifactAfter( a ); // after setting _effectiveType (for messages)
220
224
  }
221
225
  // console.log( 'ET-END:', art?.kind, art?.name )
222
226
  return art;
@@ -226,6 +230,8 @@ function populate( model ) {
226
230
  // Name-resolution relevant properties directly at artifact:
227
231
  // ‹view›.elements of input must have been moved (to elements$) before!
228
232
  // console.log('Q:',art.elements,art.enum,art.items,!!art.query)
233
+ if (art.includes) // first version of includes via effectiveTpe()
234
+ art.includes.forEach( i => effectiveType( i._artifact ) );
229
235
  if (art.elements != null || art.enum != null || art.items != null)
230
236
  return art;
231
237
  if (art.target) {
@@ -294,7 +300,7 @@ function populate( model ) {
294
300
  if (!art)
295
301
  return undefined; // TODO: null?
296
302
  // if (--depth) throw Error(`GOR: ${ Object.keys(art) }`)
297
- if ('_origin' in art)
303
+ if (art._origin !== undefined)
298
304
  return art._origin;
299
305
  if (art.type) // not stored in _origin
300
306
  return resolveType( art.type, art );
@@ -328,7 +334,7 @@ function populate( model ) {
328
334
  }
329
335
 
330
336
  function resolveType( ref, user ) {
331
- if ('_artifact' in ref)
337
+ if (ref._artifact !== undefined)
332
338
  return ref._artifact;
333
339
  while (user._outer) // in items
334
340
  user = user._outer;
@@ -631,8 +637,10 @@ function populate( model ) {
631
637
  if (!effectiveType( assoc )?.target)
632
638
  return initFromColumns( elem, elem.expand );
633
639
  const { targetMax } = path[path.length - 1].cardinality || getCardinality( assoc );
634
- if (targetMax && (targetMax.val === '*' || targetMax.val > 1))
635
- elem.items = { location: dictLocation( elem.expand ) }; // TODO: array location
640
+ if (targetMax && (targetMax.val === '*' || targetMax.val > 1)) {
641
+ elem.items = { location: elem.expand[$location] };
642
+ setLink( elem.items, '_outer', elem );
643
+ }
636
644
  return initFromColumns( elem, elem.expand );
637
645
  }
638
646
 
@@ -1286,7 +1294,7 @@ function populate( model ) {
1286
1294
  setLink( art, '_service', service );
1287
1295
  setLink( art, '_block', model.$internal );
1288
1296
  initArtifact( art, !!autoexposed );
1289
- effectiveType( art ); // TODO: necessary? see traverseElementEnvironments()
1297
+ effectiveType( art );
1290
1298
  // TODO: try to set locations of elements locations of orig target elements
1291
1299
  newAutoExposed.push( art );
1292
1300
  return art;
@@ -99,6 +99,13 @@ function propagate( model ) {
99
99
  for (const target of targets) {
100
100
  const origin = getOrigin( target );
101
101
  if (origin) {
102
+ // Calculated elements that are simple references: `calc = field;`.
103
+ // Respect sibling properties in inheritance.
104
+ if (target._calcOrigin?._origin && target.value?._artifact) {
105
+ chain.push({ target, source: target.value._artifact });
106
+ if (checkAndSetStatus( target.value._artifact ))
107
+ news.push(target.value._artifact);
108
+ }
102
109
  chain.push( { target, source: origin } );
103
110
  if (checkAndSetStatus( origin ))
104
111
  news.push( origin );
@@ -178,13 +185,13 @@ function propagate( model ) {
178
185
  }
179
186
  else {
180
187
  target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );
181
- if ('_artifact' in val)
188
+ if (val._artifact !== undefined)
182
189
  setLink( target[prop], '_artifact', val._artifact );
183
- if ('_outer' in val)
190
+ if (val._outer !== undefined)
184
191
  setLink( target[prop], '_outer', val._outer );
185
- if ('_parent' in val)
192
+ if (val._parent !== undefined)
186
193
  setLink( target[prop], '_parent', val._parent );
187
- if ('_main' in val)
194
+ if (val._main !== undefined)
188
195
  setLink( target[prop], '_main', val._main );
189
196
  }
190
197
  }
@@ -359,7 +366,7 @@ function checkAndSetStatus( art ) {
359
366
  }
360
367
 
361
368
  function setEffectiveType( target, source ) {
362
- if ('_effectiveType' in source)
369
+ if (source._effectiveType !== undefined)
363
370
  setLink( target, '_effectiveType', source._effectiveType);
364
371
  }
365
372
 
@@ -46,21 +46,18 @@ 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 {
55
54
  pushLink,
56
55
  setLink,
57
56
  setArtifactLink,
58
57
  setMemberParent,
59
58
  withAssociation,
60
- storeExtension,
61
59
  dependsOn,
62
60
  dependsOnSilent,
63
- setExpandStatusAnnotate,
64
61
  testExpr,
65
62
  targetMaxNotOne,
66
63
  traverseQueryPost,
@@ -84,16 +81,9 @@ function resolve( model ) {
84
81
  } = model.$messageFunctions;
85
82
  const {
86
83
  resolvePath,
87
- checkAnnotate,
88
- initAnnotations,
89
- copyAnnotationsForExtensions,
90
- attachAndEmitValidNames,
91
84
  lateExtensions,
92
- applyTypeExtensions,
93
85
  effectiveType,
94
86
  getOrigin,
95
- chooseAnnotationsInArtifact,
96
- extensionFor,
97
87
  resolveType,
98
88
  resolveTypeArgumentsUnchecked,
99
89
  } = model.$functions;
@@ -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 }, {
@@ -461,9 +446,6 @@ function resolve( model ) {
461
446
  checkStructureCast(art);
462
447
  }
463
448
 
464
- annotateMembers( art ); // TODO recheck - recursively, but also forEachMember below
465
- chooseAnnotationsInArtifact( art );
466
-
467
449
  forEachMember( art, resolveRefs, art.targetAspect );
468
450
 
469
451
  // Set '@Core.Computed' in the Core Compiler to have it propagated...
@@ -497,7 +479,15 @@ function resolve( model ) {
497
479
  parent = parent._parent;
498
480
 
499
481
  if (!allowedInKind.includes(art._main.kind)) {
500
- error( 'def-invalid-calc-elem', loc, { '#': 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 });
487
+ }
488
+ else {
489
+ error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
490
+ }
501
491
  }
502
492
  else if (!allowedInKind.includes(parent.kind)) {
503
493
  error( 'def-invalid-calc-elem', loc, { '#': parent.kind } );
@@ -560,149 +550,6 @@ function resolve( model ) {
560
550
  }
561
551
  }
562
552
 
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!)
566
-
567
- function annotateUnknown( ext ) {
568
- // extensions may have annotations for elements/actions/... which may
569
- // themselves may be unknown
570
- forEachMember(ext, annotateUnknown);
571
-
572
- if (ext.$extension) // extension for known artifact -> already applied
573
- return;
574
- annotateMembers( ext );
575
- chooseAnnotationsInArtifact( ext );
576
- }
577
-
578
- /**
579
- * @param {XSN.Artifact} art
580
- * @param {XSN.Extension[]} [extensions]
581
- * @param {string} [prop]
582
- * @param {string} [name]
583
- * @param {object} [parent]
584
- * @param {string} [kind]
585
- */
586
- function annotateMembers( art, extensions, prop, name, parent, kind ) {
587
- const showMsg = !art && parent && parent.kind !== 'annotate';
588
- if (!art && extensions && extensions.length) {
589
- if (Array.isArray( parent ))
590
- return;
591
- const parentExt = extensionFor(parent);
592
- art = parentExt[prop] && parentExt[prop][name];
593
- if (!art) {
594
- art = {
595
- kind, // for setMemberParent()
596
- name: { id: name, location: extensions[0].name.location },
597
- location: extensions[0].location,
598
- };
599
- setMemberParent( art, name, parentExt, prop );
600
- art.kind = 'annotate'; // after setMemberParent()!
601
- }
602
- }
603
-
604
- for (const ext of extensions || []) {
605
- if ('_artifact' in ext.name) // already applied
606
- continue;
607
- setArtifactLink( ext.name, art );
608
-
609
- if (art) {
610
- checkAnnotate( ext, art );
611
- initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
612
- copyAnnotationsForExtensions( ext, art );
613
- // eslint-disable-next-line no-shadow
614
- forEachMember( ext, ( elem, name, prop ) => {
615
- storeExtension( elem, name, prop, art, ext._block );
616
- });
617
- }
618
- if (showMsg) {
619
- // somehow similar to checkDefinitions():
620
- const feature = kindProperties[parent.kind][prop];
621
- if (prop === 'elements' || prop === 'enum') {
622
- if (!feature) {
623
- warning( 'anno-unexpected-elements', [ ext.name.location, art ], {},
624
- 'Elements only exist in entities, types or typed constructs' );
625
- }
626
- else {
627
- const isEntity = (parent.returns?.type || parent.type)?._artifact?.kind === 'entity';
628
- let variant = parent.enum ? 'enum' : 'element';
629
- if (isEntity)
630
- variant = 'entity-element';
631
- else if (parent.returns)
632
- variant = 'returns';
633
- notFound( 'anno-undefined-element', ext.name.location, art,
634
- { '#': variant, art: parent, name },
635
- parent.elements || parent.enum );
636
- }
637
- }
638
- else if (prop === 'actions') {
639
- if (!feature) {
640
- warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
641
- 'Actions and functions only exist top-level and for entities' );
642
- }
643
- else {
644
- notFound( 'anno-undefined-action', ext.name.location, art,
645
- { art: searchName( parent, name, 'action' ) },
646
- parent.actions );
647
- }
648
- }
649
- else if (!feature) {
650
- warning( 'anno-unexpected-params', [ ext.name.location, art ], {},
651
- 'Parameters only exist for actions or functions' );
652
- } // TODO: entities betaMod
653
- else {
654
- notFound( 'anno-undefined-param', ext.name.location, art,
655
- { art: searchName( parent, name, 'param' ) },
656
- parent.params );
657
- }
658
- }
659
- }
660
- if (art?._annotate) {
661
- const aor = art.returns || art;
662
- const obj = aor.items || aor.targetAspect || aor;
663
- // Currently(?), effectiveType() does not calculate the effective type of
664
- // its line item:
665
- if (art._annotate.elements) // explicit $expand on aor needed
666
- setExpandStatusAnnotate( aor, 'annotate' );
667
- annotate( obj, 'element', 'elements', 'enum', art );
668
- annotate( art, 'action', 'actions' );
669
- annotate( art, 'param', 'params' );
670
- // const { returns } = art._annotate;
671
- // if (returns) {
672
- // const dict = returns.elements;
673
- // const env = obj.returns && obj.returns.elements || null;
674
- // for (const n in dict)
675
- // annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
676
- // }
677
- }
678
-
679
- if (art?._extendType) {
680
- // Only works because annotateMembers is called in resolveRefs() _after_ `.type` was resolved.
681
- // TODO: If we allow extending included elements, we may need custom $expand,
682
- // similar to annotate above.
683
- art._extendType.forEach(resolveRefs);
684
- applyTypeExtensions(art);
685
- }
686
-
687
- return;
688
-
689
- function notFound( msgId, location, address, args, validDict ) {
690
- // TODO: probably move this to shared.js and use for EXTEND, too
691
- const msg = message( msgId, [ location, address ], args );
692
- attachAndEmitValidNames(msg, validDict);
693
- }
694
-
695
- // eslint-disable-next-line no-shadow
696
- function annotate( obj, kind, prop, altProp, parent = obj ) {
697
- const dict = art._annotate[prop];
698
- if (dict && art._annotate[prop])
699
- setExpandStatusAnnotate( art, 'annotate' );
700
- const env = obj[prop] || altProp && obj[altProp] || null;
701
- for (const n in dict)
702
- annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
703
- }
704
- }
705
-
706
553
  // Phase 4 - queries and associations --------------------------------------
707
554
 
708
555
  function resolveQuery( query ) {
@@ -1069,6 +916,7 @@ function resolve( model ) {
1069
916
  // TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
1070
917
  // trick may be removed if effectiveType() does not stop at enums.
1071
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
1072
920
  const cyclic = new Set();
1073
921
  let effectiveTypeArt = effectiveType( typeArt );
1074
922
  while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
@@ -1177,7 +1025,7 @@ function resolve( model ) {
1177
1025
  }
1178
1026
 
1179
1027
  function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
1180
- const alias = step._navigation && step._navigation.kind === '$tableAlias' && step._navigation;
1028
+ const alias = (step._navigation?.kind === '$tableAlias') ? step._navigation : null;
1181
1029
  const type = alias || effectiveType( step._artifact );
1182
1030
  const art = (type && type.target) ? type.target._artifact : type;
1183
1031
  if (!art)
@@ -1190,14 +1038,19 @@ function resolve( model ) {
1190
1038
  if (step.where)
1191
1039
  resolveExpr( step.where, 'filter', user, environment( type ) );
1192
1040
  }
1193
- else if (step.where && step.where.location || step.cardinality ) {
1041
+ else if (step.where?.location || step.cardinality ) {
1194
1042
  const location = combinedLocation( step.where, step.cardinality );
1043
+ let variant = alias ? 'tableAlias' : 'std';
1044
+ if (expected === 'from')
1045
+ variant = 'from';
1195
1046
  // XSN TODO: filter$location including […]
1196
- message( 'expr-no-filter', [ location, user ], { '#': expected },
1197
- {
1198
- std: 'A filter can only be provided when navigating along associations',
1199
- from: 'A filter can only be provided for the source entity or associations',
1200
- } );
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
+ } );
1201
1054
  }
1202
1055
  }
1203
1056
 
@@ -57,7 +57,8 @@ function fns( model ) {
57
57
  artItemsCount: Number.MAX_SAFE_INTEGER,
58
58
  undefinedDef: 'anno-undefined-def',
59
59
  undefinedArt: 'anno-undefined-art',
60
- allowAutoexposed: true,
60
+ allowAutoexposed: true, // TODO: think about Info/Warning
61
+ noMessageForLocalized: true, // TODO: should we issue a Debug message for code completion?
61
62
  },
62
63
  type: { // TODO: more detailed later (e.g. for enum base type?)
63
64
  envFn: artifactsEnv,
@@ -182,7 +183,6 @@ function fns( model ) {
182
183
  resolveUncheckedPath,
183
184
  resolveTypeArgumentsUnchecked,
184
185
  resolvePath,
185
- checkAnnotate,
186
186
  attachAndEmitValidNames,
187
187
  } );
188
188
  return;
@@ -301,7 +301,7 @@ function fns( model ) {
301
301
  function resolvePath( ref, expected, user, extDict, msgArt ) {
302
302
  if (ref == null) // no references -> nothing to do
303
303
  return undefined;
304
- if ('_artifact' in ref) // also true for _artifact: undefined
304
+ if (ref._artifact !== undefined)
305
305
  return ref._artifact;
306
306
  if (!ref.path || ref.path.broken || !ref.path.length) {
307
307
  // incomplete type AST or empty env (already reported)
@@ -598,7 +598,7 @@ function fns( model ) {
598
598
  // if (head.id === 'k') {console.log(Object.keys(user));
599
599
  // throw new CompilerAssertion(JSON.stringify(user.name))}
600
600
  // if head._artifact is set or is null then it was already computed once
601
- if ('_artifact' in head)
601
+ if (head._artifact !== undefined)
602
602
  return Array.isArray(head._artifact) ? false : head._artifact;
603
603
  // console.log(pathName(path), !spec.next && !extDict &&
604
604
  // (spec.useDefinitions || env.$frontend === 'json' || env))
@@ -616,7 +616,7 @@ function fns( model ) {
616
616
  break; // TODO: probably remove _$next link
617
617
  const e = art.artifacts || art.$tableAliases || Object.create(null);
618
618
  const r = e[head.id];
619
- if (r) {
619
+ if (r && r.$inferred !== '$internal') {
620
620
  if (Array.isArray(r)) { // redefinitions
621
621
  setArtifactLink( head, r );
622
622
  return false;
@@ -650,11 +650,14 @@ function fns( model ) {
650
650
  if (r[0].kind === '$navElement' && r.every( e => !e._parent.$duplicates )) {
651
651
  // only complain about ambiguous source elements if we do not have
652
652
  // duplicate table aliases, only mention non-ambiguous source elems
653
- const names = r.filter( e => !e.$duplicates )
654
- .map( e => `${ e.name.alias }.${ e.name.element }` );
655
- if (names.length) {
656
- error( 'ref-ambiguous', [ head.location, user ], { id: head.id, names },
657
- 'Ambiguous $(ID), replace by $(NAMES)' );
653
+ const uniqueNames = r.filter( e => !e.$duplicates);
654
+ if (uniqueNames.length) {
655
+ const names = uniqueNames.filter( e => e._parent.$inferred !== '$internal' )
656
+ .map( e => `${ e.name.alias }.${ e.name.element }` );
657
+ let variant = names.length === uniqueNames.length ? 'std' : 'few';
658
+ if (names.length === 0)
659
+ variant = 'none';
660
+ error( 'ref-ambiguous', [ head.location, user ], { '#': variant, id: head.id, names });
658
661
  }
659
662
  }
660
663
  setArtifactLink( head, r );
@@ -733,10 +736,12 @@ function fns( model ) {
733
736
  }
734
737
  else if (env.$frontend && env.$frontend !== 'cdl' || spec.global) {
735
738
  // IDE can inspect <model>.definitions - provide null for valid
736
- signalNotFound( spec.undefinedDef || 'ref-undefined-def', [ head.location, user ],
737
- valid, { art: head.id } );
739
+ if (!spec.noMessageForLocalized || !head.id.startsWith( 'localized.' )) {
740
+ signalNotFound( spec.undefinedDef || 'ref-undefined-def', [ head.location, user ],
741
+ valid, { art: head.id } );
742
+ }
738
743
  }
739
- else {
744
+ else if (!spec.noMessageForLocalized || head.id !== 'localized') {
740
745
  signalNotFound( spec.undefinedArt || 'ref-undefined-art', [ head.location, user ],
741
746
  valid, { name: head.id } );
742
747
  }
@@ -815,7 +820,7 @@ function fns( model ) {
815
820
  setTargetReferenceKey( orig.name.id, item );
816
821
  }
817
822
  art = sub;
818
- if (spec.envFn && (!artItemsCount || item === last) &&
823
+ if (spec.envFn && !spec.allowAutoexposed && (!artItemsCount || item === last) &&
819
824
  art && art.$inferred === 'autoexposed' && !user.$inferred) {
820
825
  // Depending on the processing sequence, the following could be a
821
826
  // simple 'ref-undefined-art'/'ref-undefined-def' - TODO: which we
@@ -935,7 +940,7 @@ function fns( model ) {
935
940
  msg.validNames = Object.create( null );
936
941
  for (const name of Object.keys( valid )) {
937
942
  // ignore internal types such as cds.Association
938
- if (valid[name].internal || valid[name].deprecated)
943
+ if (valid[name].internal || valid[name].deprecated || valid[name].$inferred === '$internal')
939
944
  continue;
940
945
  msg.validNames[name] = valid[name];
941
946
  }
@@ -949,49 +954,6 @@ function fns( model ) {
949
954
  { std: `Valid: ${ names.sort().join(', ') }`, zero: 'No valid names' });
950
955
  }
951
956
  }
952
-
953
- // Issue messages for annotations on namespaces and builtins
954
- // (TODO: really here?, probably split main artifacts vs returns)
955
- // see also lateExtensions() where similar messages are reported
956
- function checkAnnotate( construct, art ) {
957
- // TODO: Handle extend statements properly: Different message for empty extend?
958
-
959
- // Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
960
- // they can still be applied. Namespace annotations are extracted in to-csn.js
961
- // In parseCdl mode USINGs and other unknown references are generated as
962
- // namespaces which would lead to false positives.
963
- // TODO: should this really be different to annotate-unknown?
964
- if (art.kind === 'namespace') {
965
- info( 'anno-namespace', [ construct.name.location, construct ], {},
966
- 'Namespaces can\'t be annotated' );
967
- }
968
- // Builtin annotations would also get lost. Same as for namespaces:
969
- // extracted in to-csn.js
970
- else if (art.builtin === true) {
971
- info( 'anno-builtin', [ construct.name.location, construct ], {},
972
- 'Builtin types should not be annotated. Use custom type instead' );
973
- }
974
- // --> without art._block, art not found
975
- else if (construct.kind === 'annotate' && art._block?.$frontend === 'cdl') {
976
- if (construct.$syntax === 'returns' && art.kind !== 'action' && art.kind !== 'function' ) {
977
- // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
978
- // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
979
- // `art._block` ensures that `art` is a defined def.
980
- warning('ext-unexpected-returns', [ construct.name.location, construct ],
981
- { keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
982
- }
983
- else if (construct.$syntax !== 'returns' &&
984
- (art.kind === 'action' || art.kind === 'function') && construct.elements) {
985
- warning('ext-expected-returns', [ construct.name.location, construct ], {
986
- '#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
987
- }, {
988
- std: 'Expected $(CODE)', // unused variant
989
- action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
990
- function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
991
- });
992
- }
993
- }
994
- }
995
957
  }
996
958
 
997
959
  module.exports = {
@@ -256,7 +256,7 @@ function tweakAssocs( model ) {
256
256
  const fk = linkToOrigin( orig, name, elem, 'foreignKeys', elem.location );
257
257
  fk.$inferred = 'rewrite'; // Override existing value; TODO: other $inferred value?
258
258
  // TODO: re-check for case that foreign key is managed association
259
- if ('_effectiveType' in orig)
259
+ if (orig._effectiveType !== undefined)
260
260
  setLink( fk, '_effectiveType', orig._effectiveType);
261
261
  const te = copyExpr( orig.targetElement, elem.location );
262
262
  if (elem._redirected) {
@@ -55,7 +55,7 @@ function annotationLocation( anno ) {
55
55
  * @param {*} [val]
56
56
  * @param {string} [literal]
57
57
  */
58
- function annotateWith( art, anno, location = art.location, val = true, literal = 'boolean' ) {
58
+ function setAnnotation( art, anno, location = art.location, val = true, literal = 'boolean' ) {
59
59
  if (art[anno]) // do not overwrite user-defined including null
60
60
  return;
61
61
  art[anno] = {
@@ -448,7 +448,7 @@ module.exports = {
448
448
  annotationIsFalse,
449
449
  annotationHasEllipsis,
450
450
  annotationLocation,
451
- annotateWith,
451
+ setAnnotation,
452
452
  setLink,
453
453
  setArtifactLink,
454
454
  linkToOrigin,