@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
@@ -9,14 +9,17 @@ const { searchName, weakLocation } = require('../base/messages');
9
9
  const {
10
10
  isDeprecatedEnabled,
11
11
  forEachGeneric, forEachInOrder, forEachDefinition,
12
+ forEachMember,
13
+ isBetaEnabled,
12
14
  } = require('../base/model');
13
- const { dictAdd, dictAddArray } = require('../base/dictionaries');
15
+ const { dictAdd, pushToDict } = require('../base/dictionaries');
14
16
  const { kindProperties, dictKinds } = require('./base');
15
17
  const {
16
18
  setLink,
17
19
  setArtifactLink,
18
20
  copyExpr,
19
- annotateWith,
21
+ setAnnotation,
22
+ setExpandStatusAnnotate,
20
23
  linkToOrigin,
21
24
  setMemberParent,
22
25
  dependsOnSilent,
@@ -27,8 +30,10 @@ const {
27
30
  annotationHasEllipsis,
28
31
  } = require('./utils');
29
32
  const layers = require('./moduleLayers');
30
- const { typeParameters } = require('./builtins');
31
- const { CompilerAssertion } = require('../base/error');
33
+
34
+ const $location = Symbol.for('cds.$location');
35
+
36
+ const genLocation = { file: '' }; // attach stupid location - TODO: remove in v4
32
37
 
33
38
  function extend( model ) {
34
39
  const { options } = model;
@@ -39,23 +44,22 @@ function extend( model ) {
39
44
  const {
40
45
  resolvePath,
41
46
  resolveUncheckedPath,
42
- initAnnotations,
43
- checkAnnotate,
47
+ resolveTypeArgumentsUnchecked,
44
48
  attachAndEmitValidNames,
45
- checkDefinitions,
46
49
  initArtifact,
47
50
  initMembers,
48
- extensionsDict, // not a function - TODO
49
51
  } = model.$functions;
50
52
 
51
53
  Object.assign( model.$functions, {
52
54
  lateExtensions,
53
- applyTypeExtensions,
54
55
  chooseAnnotationsInArtifact,
55
- extensionFor,
56
- copyAnnotationsForExtensions,
56
+ extendArtifactAfter,
57
57
  } );
58
58
 
59
+ const extensionsDict = Object.create(null);
60
+ forEachDefinition( model, tagIncludes ); // TODO TMP
61
+
62
+ forEachDefinition( model, chooseAnnotationsInArtifact );
59
63
  applyExtensions();
60
64
 
61
65
  const addTextsLanguageAssoc = checkTextsLanguageAssocOption(model, options);
@@ -63,9 +67,8 @@ function extend( model ) {
63
67
 
64
68
  Object.keys( model.definitions ).forEach( processArtifact );
65
69
 
66
- lateExtensions( false );
67
-
68
70
  compositionChildPersistence();
71
+ return;
69
72
 
70
73
  /**
71
74
  * Process "composition of" artifacts.
@@ -115,90 +118,458 @@ function extend( model ) {
115
118
  if (def.$inferred === 'composition-entity' && !processed.has(def)) {
116
119
  if (def._parent)
117
120
  processCompositionPersistence(def._parent);
118
- copyPersistenceAnnotations(def, def._parent, options);
121
+ copyPersistenceAnnotations( def, def._parent );
119
122
  processed.add(def);
120
123
  }
121
124
  }
122
125
  }
123
126
 
127
+ // TMP:
128
+ function tagIncludes( art ) {
129
+ if (art.includes)
130
+ extensionsDict[art.name.absolute] = [];
131
+ }
132
+
133
+ //-----------------------------------------------------------------------------
134
+ // Extensions: general algorithm
135
+ //-----------------------------------------------------------------------------
136
+ // extendArtifactBefore, extendArtifactAfter, createRemainingAnnotateStatements
137
+
138
+ // TODO: assert that we have not yet transformed/used _extensions on sub elements
139
+ // TODO necessary(?): transformArtifactExtensions must ensure that each annotate
140
+ // is in either returns,items,elements,enum
141
+ function extendArtifactAfter( art ) {
142
+ const extensionsMap = art._extensions;
143
+ if (!extensionsMap || art.builtin) // builtin members handled via "super annotate"
144
+ return;
145
+ // type extensions after having “populated” the artifact ($typeArgs -> length,
146
+ // …, TODO: do that there) and setting an _effectiveType:
147
+ if (art.$typeExts) {
148
+ const { type } = art; // if the type is not inferred, it is the origin...
149
+ if (type?._artifact && !type.$inferred) // ...and thus is resolved
150
+ resolveTypeArgumentsUnchecked( art, type._artifact, art );
151
+ const exts = art.$typeExts;
152
+ applyTypeExtensions( art, exts.length, 'length' );
153
+ const scaleDiff = applyTypeExtensions( art, exts.scale, 'scale' );
154
+ applyTypeExtensions( art, exts.precision, 'precision', scaleDiff );
155
+ applyTypeExtensions( art, exts.srid, 'srid' );
156
+ delete art.$typeExts;
157
+ }
158
+ // TODO tmp: no proper XSN representation yet for annotate … with returns:
159
+ if (art.kind === 'annotate' && !art.returns &&
160
+ (extensionsMap.elements?.some( e => e.$syntax === 'returns' ) ||
161
+ extensionsMap.enum?.some( e => e.$syntax === 'returns' )))
162
+ annotateCreate( art, '', art, 'returns' );
163
+
164
+ moveDictExtensions( art, extensionsMap, 'params' );
165
+ // moveReturnsExtensions( art, extensionsMap );
166
+ const sub = art.returns || art.items || art.targetAspect?.elements && art.targetAspect;
167
+ if (sub) {
168
+ if (art.returns) { // after having applied params!
169
+ extendHandleReturns( extensionsMap.elements, art );
170
+ extendHandleReturns( extensionsMap.enum, art );
171
+ }
172
+ // care about 'ext-unexpected-returns' in a later change if we have XSN returns
173
+ pushToDict( sub, '_extensions', ...extensionsMap.elements || [] );
174
+ pushToDict( sub, '_extensions', ...extensionsMap.enum || [] );
175
+ }
176
+ else {
177
+ moveDictExtensions( art, extensionsMap,
178
+ (art.enum && art.kind !== 'annotate' ? 'enum' : 'elements'), 'elements' );
179
+ moveDictExtensions( art, extensionsMap, 'enum' );
180
+ }
181
+ moveDictExtensions( art, extensionsMap, 'actions' );
182
+ }
183
+
184
+ /**
185
+ * Create super annotate statements for remaining extensions
186
+ */
187
+ function lateExtensions() { // -> createRemainingAnnotateStatements
188
+ model.extensions = Object.values( model.$lateExtensions );
189
+ // TODO: testMode sort?
190
+ model.extensions.forEach( createSuperAnnotate );
191
+ // set _artifact links for “main extensions” late as it would disturb the
192
+ // still existing old extend mechanism, see chooseAnnotationsInArtifact(),
193
+ // needed for LSP and friends:
194
+ Object.values( model.sources ).forEach( setArtifactLinkForExtensions );
195
+ Object.values( model.definitions ).forEach( setArtifactLinkForExtensions );
196
+ }
197
+
198
+ // TODO: delete again
199
+ function setArtifactLinkForExtensions( source ) {
200
+ if (!source.extensions)
201
+ return;
202
+ for (const ext of source.extensions ) {
203
+ const { name } = ext;
204
+ if (name?.absolute && name._artifact === undefined) // no link set yet
205
+ resolvePath( name, ext.kind, ext ); // for LSP
206
+ }
207
+ }
208
+
209
+ // For extendArtifactAfter(): -------------------------------------------------
210
+
211
+ // Remarks on messages: we allow the type extensions only if the artifact
212
+ // originally had that property → any check of the kind “type prop can only be
213
+ // used with FooBar” is independent from `extend … with type`. Function
214
+ // checkTypeArguments() in resolve.js reports 'type-unexpected-argument', but
215
+ // that is currently incomplete.
216
+ //
217
+ // We then report (in the future), use the first message of:
218
+ // - the usual messages if a type argument is wrong, independently from `extend`
219
+ // - 'ext-unexpected-type-argument' (TODO) if the artifact does not have the prop
220
+ // - 'ext-invalid-type-argument' if the value is wrong for extend (no overwrite)
221
+ //
222
+ // TODO v4: do not allow `extend … with (precision: …)` alone if original def also has `scale`
223
+ function applyTypeExtensions( art, ext, prop, scaleDiff ) {
224
+ // console.log('ATE:',art?.[prop],ext?.[prop],scaleDiff)
225
+ if (!ext?.[prop])
226
+ return 0;
227
+ if (!art[prop]) {
228
+ const isBuiltin = art._effectiveType?.builtin;
229
+ if (isBuiltin && !allowsTypeArgument( art, prop )) {
230
+ // Let checkTypeArguments() in resolve.js report a message, is incomplete
231
+ // though, i.e. can only safely be used for scalars at the moment. But we
232
+ // will improve that function and not try to do extra things here.
233
+ art[prop] = ext[prop]; // enable checkTypeArguments() doing its job
234
+ return 0;
235
+ }
236
+ // TODO: think about 'ext-unexpected-type-argument'
237
+ error( 'ext-invalid-type-property', [ ext[prop].location, ext ],
238
+ { '#': (isBuiltin ? 'indirect' : 'new-prop'), prop } );
239
+ return 0;
240
+ }
241
+ const artVal = art[prop].val;
242
+ const extVal = ext[prop].val;
243
+ if (prop === 'srid') {
244
+ error( 'ext-invalid-type-property', [ ext[prop].location, ext ], { '#': 'prop', prop } );
245
+ }
246
+ else if (typeof artVal !== 'number' || typeof extVal !== 'number' ) {
247
+ // Users can't change from/to string value for property,
248
+ // e.g. `variable`/`floating` for Decimal
249
+ // TODO: Shouldn't the text distinguish between orig string and extension string?
250
+ // Not sure whether to talk about strings if we have a keyword in CDL
251
+ error( 'ext-invalid-type-property', [ ext[prop].location, ext ], { '#': 'string', prop } );
252
+ }
253
+ else if (extVal < artVal + (scaleDiff || 0)) {
254
+ const number = artVal + (scaleDiff || 0);
255
+ error( 'ext-invalid-type-property', [ ext[prop].location, ext ],
256
+ // eslint-disable-next-line object-curly-newline
257
+ { '#': (scaleDiff ? 'scale' : 'number'), prop, number, otherprop: 'scale' } );
258
+ }
259
+ else {
260
+ art[prop] = ext[prop];
261
+ return extVal - artVal;
262
+ }
263
+ return 0;
264
+ }
265
+
266
+ function allowsTypeArgument( art, prop ) {
267
+ const { parameters } = art._effectiveType;
268
+ if (!parameters)
269
+ return false;
270
+ return parameters.includes( prop ) || parameters[0]?.name === prop;
271
+ }
272
+
273
+ function moveDictExtensions( art, extensionsMap, artProp, extProp = artProp ) {
274
+ // TODO: setExpandStatusAnnotate
275
+ const extensions = extensionsMap[extProp];
276
+ if (!extensions)
277
+ return;
278
+ const artDict = art[artProp] || annotateFor( art, extProp ); // no auto-correction in annotate
279
+
280
+ for (const ext of extensions) {
281
+ const extDict = ext[extProp];
282
+ for (const name in extDict) {
283
+ let dictCheck = (art.kind !== 'annotate'); // no check in super annotate statement
284
+ const elemExt = extDict[name];
285
+ if (elemExt.kind !== 'annotate' && elemExt.kind !== 'extend') // TODO: specified elems
286
+ continue; // definitions inside extend, already handled
287
+ dictCheck = dictCheck && checkRemainingMemberExtensions( art, elemExt, artProp, name );
288
+ const elem = artDict[name] || annotateFor( art, extProp, name );
289
+ setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null) );
290
+ pushToDict( elem, '_extensions', elemExt );
291
+ }
292
+ }
293
+ }
294
+
295
+ // function moveReturnsExtensions( art, extensionsMap ) {
296
+ // const artReturns = art.returns;
297
+ // const extensions = extensionsMap.returns;
298
+ // // TODO: artItem is null
299
+ // for (const ext of extensions)
300
+ // pushToDict( artReturns, '_extensions', ext.returns );
301
+ // }
302
+
303
+ function annotateFor( art, prop, name ) {
304
+ const base = annotateBase( art );
305
+ if (name === '' && prop === 'params')
306
+ return base.returns || annotateCreate( base, name, base, 'returns' );
307
+ const dict = base[prop] || (base[prop] = Object.create( null ));
308
+ if (name == null)
309
+ return dict;
310
+ return dict[name] || annotateCreate( dict, name, base );
311
+ }
312
+
313
+ function annotateBase( art ) {
314
+ while (art._outer) // TOOD: think about anonymous target aspect
315
+ art = art._outer;
316
+ // if (art._annotateS)
317
+ // return art._annotateS;
318
+ if (art.kind === 'annotate')
319
+ return art;
320
+
321
+ // TODO: more to do if annotate can have `returns` property
322
+ if (art.kind === 'select')
323
+ art = art._parent;
324
+ if (art._main)
325
+ return annotateFor( art._parent, kindProperties[art.kind].dict, art.name.id );
326
+
327
+ const { absolute } = art.name;
328
+ return model.$lateExtensions[absolute] || annotateCreate( model.$lateExtensions, absolute );
329
+ }
330
+
331
+ function annotateCreate( dict, id, parent, prop ) {
332
+ const annotate = {
333
+ kind: 'annotate',
334
+ name: { id, location: genLocation },
335
+ $inferred: '',
336
+ location: genLocation,
337
+ };
338
+ if (parent) {
339
+ setLink( annotate, '_parent', parent );
340
+ setLink( annotate, '_main', parent._main || parent );
341
+ }
342
+ else {
343
+ annotate.name.absolute = id; // TODO later (if all names are sparse): delete absolute
344
+ }
345
+ dict[prop || id] = annotate;
346
+ return annotate;
347
+ }
348
+
349
+ function extendHandleReturns( extensions, art ) {
350
+ for (const ext of extensions || []) {
351
+ if (ext.$syntax === 'returns') { // TODO tmp: no proper XSN representation
352
+ ext.$syntax = '$inside-returns';
353
+ delete ext.params;
354
+ }
355
+ else {
356
+ warning( 'ext-expected-returns', [ ext.name.location, ext ], {
357
+ '#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
358
+ }, {
359
+ std: 'Expected $(CODE)', // unused variant
360
+ action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
361
+ function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
362
+ } );
363
+ }
364
+ }
365
+ }
366
+
367
+ // const unexpected_props = {
368
+ // elements: 'anno-unexpected-elements',
369
+ // enum: 'anno-unexpected-elements', // TODO
370
+ // params: 'anno-unexpected-params',
371
+ // actions: 'anno-unexpected-actions',
372
+ // };
373
+ // const undefined_props = {
374
+ // elements: 'anno-undefined-element',
375
+ // enum: 'anno-undefined-element', // TODO
376
+ // params: 'anno-undefined-param',
377
+ // actions: 'anno-undefined-action',
378
+ // };
379
+
380
+ function checkRemainingMemberExtensions( parent, ext, prop, name ) {
381
+ // console.log('CRME:',prop,name,parent,ext)
382
+ const dict = parent[prop];
383
+ if (!dict) {
384
+ // TODO: check - for each name? - better locations
385
+ const location = ext._parent[prop][$location] || ext.name.location;
386
+ // Remark: no `elements` dict location with `annotate Main:elem`
387
+ switch (prop) {
388
+ // TODO: change texts, somehow similar to checkDefinitions() ?
389
+ case 'elements':
390
+ case 'enum': // TODO: extra?
391
+ warning( 'anno-unexpected-elements', [ location, ext._parent ],
392
+ { '#': (parent._effectiveType?.kind === 'entity') ? 'entity' : 'std' }, {
393
+ std: 'Elements only exist in entities, types or typed constructs',
394
+ entity: 'Elements of entity types can\'t be annotated',
395
+ });
396
+ break;
397
+ case 'params':
398
+ warning( 'anno-unexpected-params', [ location, ext._parent ], {},
399
+ 'Parameters only exist for actions or functions' );
400
+ break;
401
+ case 'actions':
402
+ warning( 'anno-unexpected-actions', [ location, ext._parent ], {},
403
+ 'Actions and functions only exist top-level and for entities' );
404
+ break;
405
+ default:
406
+ // assert
407
+ }
408
+ return false;
409
+ }
410
+ else if (!dict[name]) {
411
+ // TODO: make variant `returns` an auto-variant for ($ART) ?
412
+ const inReturns = parent._parent?.returns && parent._parent;
413
+ const art = inReturns || parent;
414
+ switch (prop) {
415
+ case 'elements':
416
+ notFound( 'anno-undefined-element', ext.name.location, ext,
417
+ { '#': (inReturns ? 'returns' : 'element'), art, name },
418
+ parent.elements );
419
+ break;
420
+ case 'enum': // TODO: extra msg id?
421
+ notFound( 'anno-undefined-element', ext.name.location, ext,
422
+ { '#': (inReturns ? 'enum-returns' : 'enum'), art, name },
423
+ parent.enum );
424
+ break;
425
+ case 'params':
426
+ notFound( 'anno-undefined-param', ext.name.location, ext,
427
+ { '#': 'param', art: parent, name },
428
+ parent.params );
429
+ break;
430
+ case 'actions':
431
+ notFound( 'anno-undefined-action', ext.name.location, ext,
432
+ { '#': 'action', art: parent, name },
433
+ parent.actions );
434
+ break;
435
+ default:
436
+ // assert
437
+ }
438
+ }
439
+ return true;
440
+ }
441
+
442
+ function notFound( msgId, location, address, args, validDict ) {
443
+ const msg = message( msgId, [ location, address ], args );
444
+ attachAndEmitValidNames( msg, validDict );
445
+ }
446
+
447
+ // For createRemainingAnnotateStatements(): -----------------------------------
448
+
449
+ function createSuperAnnotate( annotate ) {
450
+ const extensions = annotate._extensions;
451
+ if (extensions && !annotate._main) {
452
+ const { absolute } = annotate.name;
453
+ const isLocalized = absolute.startsWith( 'localized.' ); // TODO: && anno
454
+ const art = model.definitions[absolute];
455
+ for (const ext of extensions)
456
+ checkRemainingMainExtensions( art, ext, isLocalized );
457
+ if (art?.builtin && art.kind !== 'namespace') { // TODO: do not set `builtin` on cds, cds.hana
458
+ setLink( annotate, '_extensions', art._extensions ); // for messages and member extensions
459
+ // direct annotations on builtins or on the builtins for propagation, and
460
+ // also shallow-copied to $collectedExtensions for to-csn
461
+ for (const prop in art) {
462
+ if (prop.charAt(0) === '@' || prop === 'doc')
463
+ annotate[prop] = art[prop];
464
+ }
465
+ }
466
+ if (extensions.length === 1) { // i.e. no proper location if from more than one extensions
467
+ annotate.location = extensions[0].location;
468
+ annotate.name.location = extensions[0].name.location;
469
+ }
470
+ }
471
+ chooseAnnotationsInArtifact( annotate );
472
+ extendArtifactAfter( annotate );
473
+ forEachMember( annotate, createSuperAnnotate );
474
+ }
475
+
476
+ function checkRemainingMainExtensions( art, ext, localized ) {
477
+ if (localized) // TODO v4: ignore only for annotate
478
+ return;
479
+ if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
480
+ return;
481
+ // else if (ext.kind === 'extend') { // TODO v4 - add error
482
+ // }
483
+ if (art?.kind === 'namespace') {
484
+ // TODO: not at all different to having no definition
485
+ info( 'anno-namespace', [ ext.name.location, ext ], {}, // TODO: better location?
486
+ 'Namespaces can\'t be annotated' );
487
+ }
488
+ else if (art?.builtin) {
489
+ info( 'anno-builtin', [ ext.name.location, ext ], {}, // TODO: better location?
490
+ 'Builtin types should not be annotated. Use custom type instead' );
491
+ }
492
+ }
493
+
494
+ // Issue messages for annotations on namespaces and builtins
495
+ // (TODO: really here?, probably split main artifacts vs returns)
496
+ // see also lateExtensions() where similar messages are reported
497
+ function checkAnnotate( construct, art ) {
498
+ // TODO: Handle extend statements properly: Different message for empty extend?
499
+
500
+ // --> without art._block, art not found
501
+ if (construct.kind === 'annotate' && art._block?.$frontend === 'cdl') {
502
+ if (construct.$syntax === 'returns' && art.kind !== 'action' && art.kind !== 'function' ) {
503
+ // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
504
+ // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
505
+ // `art._block` ensures that `art` is a defined def.
506
+ return;
507
+ // warning('ext-unexpected-returns', [ construct.name.location, construct ],
508
+ // { keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
509
+ }
510
+ else if (construct.$syntax !== 'returns' &&
511
+ (art.kind === 'action' || art.kind === 'function') && construct.elements) {
512
+ warning('ext-expected-returns', [ construct.name.location, construct ], {
513
+ '#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
514
+ }, {
515
+ std: 'Expected $(CODE)', // unused variant
516
+ action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
517
+ function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
518
+ });
519
+ }
520
+ }
521
+ }
522
+
124
523
  // extend ------------------------------------------------------------------
125
524
 
126
525
  /**
127
526
  * Apply the extensions inside the extensionsDict on the model.
128
527
  *
129
- * Phase 1: context extends, 2: extends with structure includes, 3: extends
130
- * without structure includes (in the case of cyclic includes)
131
- *
132
- * Before phase 1: all artifact extensions have been collected (even those
133
- * inside extend context), only "empty" ones from structure includes are still unknown.
134
- * After phase 1, all main artifacts are known, also "empty" extensions are known.
528
+ * First try normally: extends with structure includes; with remaining cyclic
529
+ * includes, do so without includes.
135
530
  */
136
531
  function applyExtensions() {
137
- let phase = 1; // TODO: basically remove phase 1
532
+ let noIncludes = false;
138
533
  let extNames = Object.keys( extensionsDict ).sort();
139
- // Remark: The sort() makes sure that an extend for artifact C.E is applied
140
- // after the extend for C has been applied (which could have defined C.E).
141
- // Looping over model.definitions in Phase 1 would miss the `extend
142
- // context` for a context C.C defined in an `extend context C`.
143
- //
144
- // TODO: no need to sort anymore
534
+
145
535
  while (extNames.length) {
146
536
  const { length } = extNames;
147
537
  for (const name of extNames) {
148
538
  const art = model.definitions[name];
149
- if (!art || art.kind === 'namespace') {
150
- model.$lateExtensions[name] = extensionsDict[name];
539
+ if (art && art.kind !== 'namespace' &&
540
+ extendArtifact( extensionsDict[name], art, noIncludes ))
151
541
  delete extensionsDict[name];
152
- }
153
- else if (art.$duplicates) { // cannot extend redefinitions
154
- delete extensionsDict[name];
155
- }
156
- else if (phase === 1
157
- ? extendContext( name, art )
158
- : extendArtifact( extensionsDict[name], art, Array.isArray( phase ) && phase )) {
159
- delete extensionsDict[name];
160
- }
161
542
  }
162
543
  extNames = Object.keys( extensionsDict ); // no sort() required anymore
163
- if (phase === 1)
164
- phase = 2;
165
- else if (extNames.length >= length)
166
- phase = Object.keys( extensionsDict ); // = no includes
544
+ if (extNames.length >= length)
545
+ noIncludes = Object.keys( extensionsDict ); // = no includes
167
546
  }
168
547
  }
169
548
 
170
- function extendContext( name, art ) {
171
- // TODO: remove this function - add remains to define.js
172
- // (ext.expectedKind == art.kind) already checked by parser except for context/service
173
- if (!kindProperties[art.kind].artifacts) {
174
- // no context or service => warn about context extensions
175
- for (const ext of extensionsDict[name]) {
176
- if (ext.expectedKind === 'context' || ext.expectedKind === 'service') {
177
- const loc = ext.name.location;
178
- // TODO: warning is enough
179
- error( 'extend-with-artifacts', [ loc, ext ], { name, '#': ext.expectedKind, keyword: `EXTEND ${ ext.expectedKind }` }, {
180
- std: 'Can\'t extend non-context / non-service $(NAME) with $(KEYWORD)',
181
- service: 'Can\'t extend non-service $(NAME) with $(KEYWORD)',
182
- context: 'Can\'t extend non-context $(NAME) with $(KEYWORD)',
549
+ function checkExtensionsKind( extensions, art ) {
550
+ for (const ext of extensions) {
551
+ const kind = ext.expectedKind?.val;
552
+ if (kind && kind !== art.kind) {
553
+ const loc = ext.expectedKind.location;
554
+ if (kind === 'context' || kind === 'service') {
555
+ // We have no real artifact during the construction of a super-annotate statement:
556
+ const msgArgs = {
557
+ '#': (art.kind === 'service' || art.kind === 'annotate') ? art.kind : 'std',
558
+ art,
559
+ kind,
560
+ code: 'extend with definitions',
561
+ keyword: 'extend service',
562
+ };
563
+ warning( 'ext-invalid-kind', [ loc, ext ], msgArgs, {
564
+ std: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) instead',
565
+ annotate: 'There is no artifact $(ART), use $(CODE) instead',
566
+ // do not mention 'extend context', that is not in CAPire
567
+ service: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) or $(KEYWORD) instead',
183
568
  });
184
569
  }
570
+ // TODO: Use similar checks for EXTEND ENTITY etc - 'ext-ignoring-kind'
185
571
  }
186
- return false;
187
572
  }
188
-
189
- for (const ext of extensionsDict[name]) {
190
- setArtifactLink( ext.name, art );
191
- checkDefinitions( ext, art, 'elements'); // error for elements etc
192
- checkDefinitions( ext, art, 'enum');
193
- checkDefinitions( ext, art, 'actions');
194
- checkDefinitions( ext, art, 'params');
195
- checkDefinitions( ext, art, 'columns');
196
- if (ext.includes)
197
- applyIncludes( ext, art ); // emits error if `includes` is set
198
- checkAnnotate( ext, art );
199
- copyAnnotationsForExtensions( ext, art );
200
- }
201
- return true;
202
573
  }
203
574
 
204
575
  /**
@@ -231,6 +602,7 @@ function extend( model ) {
231
602
  }
232
603
  if (!noIncludes && art.includes)
233
604
  applyIncludes( art, art );
605
+ // checkExtensionsKind( extensions, art );
234
606
  extendMembers( extensions, art, noIncludes === 'gen' );
235
607
  if (!noIncludes && art.includes) {
236
608
  // early propagation of specific annotation assignments
@@ -244,12 +616,14 @@ function extend( model ) {
244
616
  function extendMembers( extensions, art, noExtend ) {
245
617
  // TODO: do the whole extension stuff lazily if the elements are requested
246
618
  const elemExtensions = [];
247
- extensions.sort( layers.compareLayer );
619
+ if (art._main) // extensions already sorted for main artifacts
620
+ extensions.sort( layers.compareLayer );
248
621
  // TODO: use same sequence as in chooseAssignment() - better: use common code with that fn
249
- for (const ext of extensions) {
622
+ // console.log('EM:',art.name,extensions,art._extensions)
623
+ for (const ext of extensions) { // those in extMap.includes
250
624
  // console.log(message( 'id', [ext.location, ext], { art: ext.name._artifact },
251
625
  // 'Info', 'EXT').toString())
252
- if (!('_artifact' in ext.name)) { // not already applied
626
+ if (ext.name._artifact === undefined) { // not already applied
253
627
  setArtifactLink( ext.name, art );
254
628
  if (noExtend && ext.kind === 'extend') {
255
629
  error( 'extend-for-generated', [ ext.name.location, ext ], { art },
@@ -267,12 +641,10 @@ function extend( model ) {
267
641
  art.includes = [ ...ext.includes ];
268
642
  applyIncludes( ext, art );
269
643
  }
644
+ // console.log(ext,art)
270
645
  checkAnnotate( ext, art );
271
- initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
272
- copyAnnotationsForExtensions( ext, art );
273
646
  // TODO: do we allow to add elements with array of {...}? If yes, adapt
274
647
  initMembers( ext, art, ext._block ); // might set _extend, _annotate
275
- storeTypeExtension( ext, art );
276
648
  dependsOnSilent(art, ext); // art depends silently on ext (inverse to normal dep!)
277
649
  }
278
650
  for (const name in ext.elements) {
@@ -282,17 +654,24 @@ function extend( model ) {
282
654
  break; // more than one elem in same EXTEND is fine
283
655
  }
284
656
  }
285
-
286
- if (ext.columns) // extend projection
287
- extendColumns( ext, art );
288
657
  }
289
658
  if (elemExtensions.length > 1)
290
659
  reportUnstableExtensions( elemExtensions );
291
- if (art._extendType && art._extendType.length > 0)
292
- reportTypeExtensionsInSameLayer( art._extendType );
293
660
 
661
+ // This whole function will be removed with a next change - no need to have nice code here:
662
+ const extsTmp = { elements: Object.create(null), actions: Object.create(null) };
663
+ for (const e of extensions) {
664
+ for (const n in e.elements || []) {
665
+ if (e.elements[n].kind === 'extend')
666
+ pushToDict( extsTmp.elements, n, e.elements[n] );
667
+ }
668
+ for (const n in extensions.actions || []) {
669
+ if (e.actions[n].kind === 'extend')
670
+ pushToDict( extsTmp.actions, n, e.actions[n] );
671
+ }
672
+ }
294
673
  [ 'elements', 'actions' ].forEach( (prop) => {
295
- const dict = art._extend && art._extend[prop];
674
+ const dict = extsTmp[prop];
296
675
  for (const name in dict) {
297
676
  let obj = art;
298
677
  if (obj.targetAspect)
@@ -386,129 +765,6 @@ function extend( model ) {
386
765
  return !hasError;
387
766
  }
388
767
 
389
-
390
- /**
391
- * Copy columns for EXTEND PROJECTION
392
- *
393
- * @param {XSN.Extension} ext
394
- * @param {XSN.Artifact} art
395
- */
396
- function extendColumns( ext, art ) {
397
- // TODO: consider reportUnstableExtensions
398
-
399
- const { location } = ext.name;
400
- const { query } = art;
401
- if (!query) {
402
- if (art.kind !== 'annotate')
403
- error( 'extend-columns', [ location, ext ], { art } );
404
- return;
405
- }
406
- if (!query.from || !query.from.path) {
407
- error( 'extend-columns', [ location, ext ], { art } );
408
- }
409
- else {
410
- if (!query.columns)
411
- query.columns = [ { location, val: '*' } ];
412
-
413
- for (const column of ext.columns) {
414
- setLink( column, '_block', ext._block );
415
- query.columns.push(column);
416
- }
417
- }
418
- }
419
-
420
- /**
421
- * Similar to chooseAssignment for annotations, this function applies type extensions in order,
422
- * such that hierarchies are respected.
423
- * Order already set in `extendMembers()` using `compareLayers()`.
424
- *
425
- * @param art
426
- */
427
- function applyTypeExtensions( art ) {
428
- /**
429
- * Contains the previous extension for each property that was applied
430
- * successfully.
431
- */
432
- const previousSuccess = Object.create(null);
433
- const allowedBuiltinCategories = [ 'string', 'decimal', 'integer', 'binary' ];
434
- const artType = art.type?._artifact;
435
-
436
- for (const ext of art._extendType) {
437
- if (art.$inferred) {
438
- // Only report first extension for $inferred artifact. Reduces noise.
439
- error('ref-expected-scalar-type', [ ext.name.location, ext ], { '#': 'inferred' } );
440
- break;
441
- }
442
-
443
- const baseType = art._effectiveType; // may be an ENUM -- TODO: not here!
444
- if (!artType || (art.kind !== 'type' && art.kind !== 'element') || !baseType?.builtin) {
445
- // Only report first extension for non-scalar base type. Reduces noise.
446
- error('ref-expected-scalar-type', [ ext.name.location, ext ], { } );
447
- break;
448
- }
449
- else if (!allowedBuiltinCategories.includes(baseType.category)) {
450
- // Only report first extension for non-scalar type. Reduces noise.
451
- error('ref-expected-scalar-type', [ ext.name.location, ext ],
452
- { '#': 'unsupported', prop: baseType.category });
453
- continue;
454
- }
455
-
456
- for (const prop of typeParameters.list) {
457
- if (!ext[prop])
458
- continue;
459
-
460
- if (!baseType.parameters?.includes(prop)) {
461
- // For `extend T with type (length:10)` where T does not expect a length.
462
- error( 'type-unexpected-argument', [ ext[prop].location, ext ], {
463
- '#': 'type', prop, art, type: baseType,
464
- });
465
- break; // one error for first property is enough
466
- }
467
- else if (!art[prop]) {
468
- // Can only extend properties that exist directly on the artifact.
469
- error('ext-invalid-type-property', [ ext[prop].location, ext ],
470
- { prop, '#': 'new-prop' });
471
- break; // one error for first property is enough
472
- }
473
- else if (art[prop].val === ext[prop].val) {
474
- // Ignore extensions with same value
475
- }
476
- else if (art[prop].literal === 'string' || ext[prop].literal === 'string' ) {
477
- // Users can't change from/to string value for property,
478
- // e.g. `variable`/`floating` for Decimal
479
- error( 'ext-invalid-type-property', [ ext[prop].location, ext ],
480
- { '#': 'string', prop } );
481
- }
482
- else if (art[prop].val > ext[prop].val) {
483
- // TODO: Future: Sub-message that points to previous extension?
484
- error( 'ext-invalid-type-property', [ ext[prop].location, ext ], {
485
- prop,
486
- value: ext[prop].val,
487
- number: art[prop].val,
488
- '#': previousSuccess[prop] ? 'ext-smaller' : 'smaller',
489
- } );
490
- break; // one error for first property is enough
491
- }
492
- else if (art[prop].val < ext[prop].val) {
493
- art[prop] = ext[prop];
494
- previousSuccess[prop] = ext;
495
- }
496
- }
497
-
498
- // If `scale` is increased, but `precision` is not, data may be lost in a migration.
499
- // e.g. `Decimal(4,2)` allows `12.34`. Increasing scale to 3 would make this value
500
- // invalid and only allow `1.234`.
501
- // TODO: Should we actually check that the increase is correct?
502
- if (ext.scale && !ext.precision) {
503
- error('ext-invalid-type-property', [ ext.scale.location, ext ], {
504
- prop: 'scale',
505
- otherprop: 'precision',
506
- '#': 'scale',
507
- });
508
- }
509
- }
510
- }
511
-
512
768
  /**
513
769
  * Report 'Warning: Unstable element order due to repeated extensions'
514
770
  * except if all extensions are in the same file.
@@ -550,26 +806,6 @@ function extend( model ) {
550
806
  }
551
807
  }
552
808
 
553
- /**
554
- * Report type extensions in same layer, similar mechanism to chooseAssignment()
555
- * for annotations.
556
- *
557
- * @param {XSN.Extension[]} extensions
558
- */
559
- function reportTypeExtensionsInSameLayer( extensions ) {
560
- // Group assignments by layer
561
- const extLayers = layeredAssignments( extensions );
562
- // We only care about the highest layer
563
- const { assignments, issue } = assignmentsOfHighestLayers( extLayers );
564
-
565
- if (issue || assignments.length > 1) { // TODO: allow in same file?
566
- const id = (issue === 'unrelated')
567
- ? 'ext-duplicate-extend-type-unrelated-layer'
568
- : 'ext-duplicate-extend-type';
569
- for (const ext of assignments)
570
- message( id, [ ext.name.location, ext ], { type: ext.name._artifact } );
571
- }
572
- }
573
809
 
574
810
  /**
575
811
  * @param {XSN.Extension[]} extensions
@@ -597,77 +833,6 @@ function extend( model ) {
597
833
  }
598
834
  }
599
835
 
600
- /**
601
- * @param {Function|false} [veryLate]
602
- */
603
- function lateExtensions( veryLate ) {
604
- for (const name in model.$lateExtensions) {
605
- const art = model.definitions[name];
606
- const exts = model.$lateExtensions[name];
607
- if (art && art.kind !== 'namespace') {
608
- if (art.builtin) {
609
- for (const ext of exts)
610
- info( 'anno-builtin', [ ext.name.location, ext ] );
611
- }
612
- // created texts entity, auto-exposed entity
613
- if (exts) {
614
- extendArtifact( exts, art, 'gen' );
615
- if (veryLate)
616
- veryLate( art );
617
- model.$lateExtensions[name] = null; // done
618
- }
619
- }
620
- else if (veryLate) {
621
- // Complain about unused extensions, i.e. those
622
- // which do not point to a valid artifact
623
- for (const ext of exts) {
624
- delete ext.name.path[0]._artifact; // get message for root
625
- // TODO: make resolvePath('extend'/'annotate') ignore namespaces
626
- // Don't try to apply annotations in the `localized.` namespace.
627
- // That's done in `localized.js`.
628
- if (!name.startsWith('localized.') &&
629
- resolvePath( ext.name, ext.kind, ext )) { // should issue error/info
630
- // should issue error for cds extensions (annotate ok)
631
- if (art.kind === 'namespace') {
632
- // TODO: Emit error if namespace is extended by non-definitions.
633
- info( 'anno-namespace', [ ext.name.location, ext ], {},
634
- 'Namespaces can\'t be annotated' );
635
- }
636
- // Builtin annotations would be represented as annotations in to-csn.js
637
- else if (art.builtin) {
638
- info( 'anno-builtin', [ ext.name.location, ext ] );
639
- }
640
- }
641
- // TODO: warning for context/service extension on non-correct
642
- if (ext.kind === 'annotate')
643
- delete ext.name._artifact; // make it be considered by extendArtifact()
644
- }
645
- // create "super" ANNOTATE containing all non-applied ones
646
- const first = exts[0];
647
- const { location } = first.name;
648
-
649
- /** @type {XSN.Definition} */
650
- const annotationArtifact = {
651
- kind: 'annotate',
652
- name: { path: [ { id: name, location } ], absolute: name, location },
653
- location: first.location,
654
- };
655
-
656
- if (!model.extensions)
657
- model.extensions = [];
658
-
659
- model.extensions.push(annotationArtifact);
660
- extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
661
- // if one of the annotate statement mentions 'returns', assume it
662
- // TODO: with warning/info?
663
- for (const ext of exts) {
664
- if (ext.$syntax === 'returns')
665
- annotationArtifact.$syntax = 'returns';
666
- }
667
- }
668
- }
669
- }
670
-
671
836
  // includes ----------------------------------------------------------------
672
837
 
673
838
  /**
@@ -693,6 +858,7 @@ function extend( model ) {
693
858
  delete ref._artifact;
694
859
  }
695
860
  else if (name && name in extensionsDict) {
861
+ // one of the includes has itself extensions that need to be applied first
696
862
  return false;
697
863
  }
698
864
  else if (ref._artifact) {
@@ -775,6 +941,7 @@ function extend( model ) {
775
941
  // all usages in the expressions? Possibly just the first one?
776
942
  elem.value = Object.assign( { $inferred: 'include' }, copyExpr( origin.value ));
777
943
  elem.$syntax = 'calc';
944
+ setLink( elem, '_calcOrigin', origin._calcOrigin || origin );
778
945
  }
779
946
  // TODO: also complain if elem is just defined in art
780
947
  });
@@ -801,8 +968,15 @@ function extend( model ) {
801
968
  forEachInOrder(parent, prop, ( member, name ) => {
802
969
  if (member.$inferred === 'include' && Array.isArray(member.$duplicates)) {
803
970
  const includes = [ member, ...member.$duplicates ].map(dup => dup._origin._main);
804
- warning( 'ref-duplicate-include-member', [ parent.name.location, member ],
805
- { '#': prop, name, sorted_arts: includes } );
971
+ if (isBetaEnabled(options, 'v4preview')) {
972
+ error( 'duplicate-definition', [ parent.name.location, member ],
973
+ { '#': `include-${ prop }`, name, sorted_arts: includes } );
974
+ }
975
+ else {
976
+ // Error accidentally removed in v2/v3, therefore only a warning.
977
+ warning( 'ref-duplicate-include-member', [ parent.name.location, member ],
978
+ { '#': prop, name, sorted_arts: includes } );
979
+ }
806
980
  }
807
981
  });
808
982
  }
@@ -933,6 +1107,7 @@ function extend( model ) {
933
1107
  const art = useTextsAspect
934
1108
  ? createTextsEntityWithInclude( base, absolute, fioriEnabled )
935
1109
  : createTextsEntityWithDefaultElements( base, absolute, fioriEnabled );
1110
+ // both functions are rather similar...
936
1111
 
937
1112
  const { location } = base.name;
938
1113
 
@@ -968,7 +1143,7 @@ function extend( model ) {
968
1143
  elem.key = { val: true, $inferred: 'localized', location };
969
1144
  // If the propagated elements remain key (that is not fiori.draft.enabled)
970
1145
  // they should be omitted from OData containment EDM
971
- annotateWith( elem, '@odata.containment.ignore', location );
1146
+ setAnnotation( elem, '@odata.containment.ignore', location );
972
1147
  }
973
1148
  else {
974
1149
  // add the former key paths to the unique constraint
@@ -1005,10 +1180,10 @@ function extend( model ) {
1005
1180
  path: [ { id: locale.name.id, location: locale.location } ],
1006
1181
  location: locale.location,
1007
1182
  });
1008
- annotateWith( art, '@assert.unique.locale', art.location, assertUniqueValue, 'array' );
1183
+ setAnnotation( art, '@assert.unique.locale', art.location, assertUniqueValue, 'array' );
1009
1184
  }
1010
1185
 
1011
- copyPersistenceAnnotations(art, base, options);
1186
+ copyPersistenceAnnotations( art, base );
1012
1187
  return art;
1013
1188
  }
1014
1189
 
@@ -1040,7 +1215,7 @@ function extend( model ) {
1040
1215
  if (!fioriEnabled) {
1041
1216
  // To be compatible, we switch off draft without @fiori.draft.enabled
1042
1217
  // TODO (next major version): remove?
1043
- annotateWith( art, '@odata.draft.enabled', art.location, false );
1218
+ setAnnotation( art, '@odata.draft.enabled', art.location, false );
1044
1219
  }
1045
1220
  else {
1046
1221
  // @fiori.draft.enabled artifacts need default elements ID_texts and locale.
@@ -1061,9 +1236,11 @@ function extend( model ) {
1061
1236
 
1062
1237
  if (addTextsLanguageAssoc && art.elements.language)
1063
1238
  art.elements.language = undefined; // TODO: Message? Ignore?
1239
+ // TODO: what is this necessary? We do not create a text entity in this case
1064
1240
 
1065
1241
  setLink( art, '_block', model.$internal );
1066
1242
  model.definitions[absolute] = art;
1243
+ chooseAnnotationsInArtifact( art ); // having extensions here would be wrong
1067
1244
  return art;
1068
1245
  }
1069
1246
 
@@ -1098,7 +1275,7 @@ function extend( model ) {
1098
1275
  locale.key = { val: true, location };
1099
1276
  // To be compatible, we switch off draft without @fiori.draft.enabled
1100
1277
  // TODO (next major version): remove?
1101
- annotateWith( art, '@odata.draft.enabled', art.location, false );
1278
+ setAnnotation( art, '@odata.draft.enabled', art.location, false );
1102
1279
  }
1103
1280
  else {
1104
1281
  const textId = {
@@ -1110,11 +1287,11 @@ function extend( model ) {
1110
1287
  };
1111
1288
  dictAdd( art.elements, 'ID_texts', textId );
1112
1289
  }
1113
-
1114
1290
  dictAdd( art.elements, 'locale', locale );
1291
+
1115
1292
  setLink( art, '_block', model.$internal );
1116
1293
  model.definitions[absolute] = art;
1117
-
1294
+ chooseAnnotationsInArtifact( art ); // having extensions here would be wrong
1118
1295
  return art;
1119
1296
  }
1120
1297
 
@@ -1398,9 +1575,9 @@ function extend( model ) {
1398
1575
  model.definitions[entityName] = art;
1399
1576
  initArtifact( art );
1400
1577
 
1578
+ chooseAnnotationsInArtifact( art ); // having extensions here would be wrong
1401
1579
  // Copy persistence annotations from aspect.
1402
- copyPersistenceAnnotations(art, target, options);
1403
-
1580
+ copyPersistenceAnnotations( art, target ); // after chooseAnnotation()
1404
1581
  return art;
1405
1582
  }
1406
1583
 
@@ -1416,138 +1593,204 @@ function extend( model ) {
1416
1593
  if (origin.key)
1417
1594
  proxy.key = Object.assign( { $inferred: 'include' }, origin.key );
1418
1595
  if (anno)
1419
- annotateWith( proxy, anno );
1596
+ setAnnotation( proxy, anno );
1420
1597
  dictAdd( proxyDict.elements, pname, proxy );
1421
1598
  }
1422
1599
  }
1423
1600
 
1424
1601
 
1425
1602
  // Phase 4 - annotations ---------------------------------------------------
1426
-
1427
- function extensionFor( art ) {
1428
- if (art.kind === 'annotate')
1429
- return art;
1430
- if (art._extension)
1431
- return art._extension;
1432
-
1433
- // $extension means: already applied
1434
- const ext = {
1435
- kind: art.kind, // set kind for setMemberParent()
1436
- $extension: 'exists',
1437
- location: art.location, // location( extension to existing art ) = location(art)
1438
- };
1439
- const { location } = art.name;
1440
- if (!art._main) {
1441
- ext.name = {
1442
- path: [ { id: art.name.absolute, location } ],
1443
- location,
1444
- absolute: art.name.absolute,
1445
- };
1446
- if (model.extensions)
1447
- model.extensions.push(ext);
1448
- else
1449
- model.extensions = [ ext ];
1450
- }
1451
- else {
1452
- ext.name = { id: art.name.id, location };
1453
- const parent = extensionFor( art._parent );
1454
- const kind = kindProperties[art.kind].normalized || art.kind;
1455
- // enums would be first in elements
1456
- if ( parent[kindProperties[kind].dict]?.[art.name.id] )
1457
- throw new CompilerAssertion(art.name.id);
1458
- setMemberParent( ext, art.name.id, parent, kindProperties[kind].dict );
1459
- }
1460
- ext.kind = 'annotate'; // after setMemberParent()!
1461
- setLink( art, '_extension', ext );
1462
- setArtifactLink( ext.name, art );
1463
- if (art.returns)
1464
- ext.$syntax = 'returns';
1465
- return ext;
1466
- }
1603
+ // move to top
1467
1604
 
1468
1605
  /**
1469
1606
  * Goes through all (applied) annotations in the given artifact and chooses one
1470
1607
  * if multiple exist according to the module layer.
1608
+ * TODO: rename to extendArtifactBefore
1471
1609
  *
1472
1610
  * @param {XSN.Artifact} art
1473
1611
  */
1474
1612
  function chooseAnnotationsInArtifact( art ) {
1475
- for (const prop in art) {
1476
- if (prop.charAt(0) === '@')
1477
- chooseAssignment( prop, art );
1613
+ // for main artifacts, move extensions from `$lateExtensions` model dictionary:
1614
+ if (!art._main && !art._outer && art._extensions === undefined &&
1615
+ art.kind !== 'namespace') {
1616
+ // if (!art.name) console.log(art)
1617
+ const { absolute } = art.name;
1618
+ setLink( art, '_extensions', model.$lateExtensions[absolute]?._extensions || null );
1619
+ if (art._extensions && !art.builtin) { // keep extensions for builtin in $lateExtensions
1620
+ delete model.$lateExtensions[absolute];
1621
+ // TODO: if the extension mechanism has been completed, we could uncomment:
1622
+ // art._extensions.forEach( ext => resolvePath( ext.name, ext.kind, ext )); // for LSP
1623
+ // for now, we do that at the end of lateExtensions()
1624
+ }
1625
+ }
1626
+ if (art._extensions) {
1627
+ // TODO: the following function can now be simplified
1628
+ // if (art.$inferred) console.log('CAI:', art.name, art.$inferred,art._extensions)
1629
+ // With extensions, member appears in CSN, affects directly the rendering of
1630
+ // elements etc. TODO: do that more specifically on the dicts (via symbol)
1631
+ // Probably better: we could use the _extensions dict prop directly in to-csn
1632
+ if (art.$inferred)
1633
+ setExpandStatusAnnotate( art, 'annotate' );
1634
+ if (Array.isArray( art._extensions )) {
1635
+ checkExtensionsKind( art._extensions, art ); // TODO: check with builtins
1636
+ transformArtifactExtensions( art );
1637
+ }
1638
+ applyAllExtensions( art );
1478
1639
  }
1479
- if (art.doc)
1480
- chooseAssignment( 'doc', art );
1481
1640
  }
1482
1641
 
1483
- function chooseAssignment( annoName, art ) {
1484
- let anno = art[annoName];
1485
- if (!Array.isArray( anno )) { // just one assignment -> use it
1486
- if (!annotationHasEllipsis( anno ))
1487
- return;
1488
- anno = [ anno ];
1489
- }
1490
- // console.log('ASSIGN:',art.name.absolute,annoName)
1491
- const scheduledAssignments = [];
1492
- // sort assignment according to layer (define is bottom layer):
1493
- const layeredAnnos = layeredAssignments( anno );
1494
- let cont = true;
1495
- while (cont) {
1496
- const { assignments, issue } = assignmentsOfHighestLayers( layeredAnnos );
1497
- // console.log( 'CA:', annoName, issue, assignments)
1498
- let index = assignments.length;
1499
- cont = !!index; // safety
1500
- while (--index >= 0) {
1501
- const a = assignments[index];
1502
- scheduledAssignments.push( a );
1503
- if (!annotationHasEllipsis( a )) {
1504
- cont = false;
1505
- break;
1642
+ // TODO: if extensions has more than one of returns,items,elements,enum, delete all those props
1643
+ function transformArtifactExtensions( art ) {
1644
+ const hasOnlySubExtensions = art._outer; // items, anonymous aspects
1645
+ const dict = Object.create(null);
1646
+ for (const ext of art._extensions) {
1647
+ for (const prop in ext) {
1648
+ if (ext[prop] === undefined) // deleted propery
1649
+ continue;
1650
+ // TODO: do this check nicer (after complete move to new extensions mechanism)
1651
+ if (prop.charAt(0) === '@' || prop === 'doc' ||
1652
+ prop === 'includes' || prop === 'columns' ||
1653
+ prop === 'length' || prop === 'scale' || prop === 'precision' || prop === 'srid') {
1654
+ if (!hasOnlySubExtensions)
1655
+ pushToDict( dict, prop, ext );
1656
+ }
1657
+ else if (prop === 'elements' || prop === 'enum' || prop === 'actions' ||
1658
+ prop === 'params' || prop === 'returns') {
1659
+ if (ext.kind === 'extend')
1660
+ pushToDict( dict, 'includes', ext );
1661
+ pushToDict( dict, prop, ext );
1506
1662
  }
1507
1663
  }
1508
- if (issue) {
1509
- // eslint-disable-next-line no-nested-ternary
1510
- const msg = (index < 0)
1511
- ? 'anno-unstable-array'
1512
- : (issue === true)
1513
- ? 'anno-duplicate'
1514
- : 'anno-duplicate-unrelated-layer';
1515
- const variant = annoName === 'doc' ? 'doc' : 'std';
1516
- for (const a of assignments) {
1517
- if (!a.$errorReported) {
1518
- message( msg, [ a.name?.location || a.location, art ],
1519
- { '#': variant, anno: annoName } );
1664
+ }
1665
+ art._extensions = dict;
1666
+ }
1667
+
1668
+ function applyAllExtensions( art ) {
1669
+ const extensions = art._extensions;
1670
+ for (const prop in extensions) {
1671
+ // TODO: do the following `if` in a nicer way
1672
+ if ([ 'elements', 'enum', 'actions', 'params', 'returns' ].includes( prop ))
1673
+ continue; // currently just annotates on sub elements - TODO: error here
1674
+ // annotations, `doc`, `includes`, `columns`, `length`, ...
1675
+ const scheduled = [];
1676
+ // sort extensions according to layer (specified elements are bottom layer):
1677
+ const layered = layeredExtensions( extensions[prop] );
1678
+
1679
+ let cont = true;
1680
+ while (cont) {
1681
+ const { highest, issue } = extensionsOfHighestLayers( layered );
1682
+ // console.log( 'CA:', annoName, issue, extensions)
1683
+ let index = highest.length;
1684
+ cont = !!index; // safety
1685
+ while (--index >= 0) {
1686
+ const ext = highest[index];
1687
+ scheduled.push( ext );
1688
+ if (extensionOverwrites( ext, prop )) {
1689
+ cont = false;
1690
+ break;
1520
1691
  }
1521
1692
  }
1693
+ if (issue || index > 0)
1694
+ reportDuplicateExtensions( highest, prop, issue, index, art );
1695
+ }
1696
+ // Now apply the relevant extensions
1697
+ scheduled.reverse();
1698
+ for (const ext of scheduled)
1699
+ applySingleExtension( art, ext, prop );
1700
+ delete extensions[prop];
1701
+ }
1702
+ }
1703
+
1704
+ function extensionOverwrites( ext, prop ) {
1705
+ return (prop.charAt(0) !== '@')
1706
+ ? [ 'doc', 'length', 'precision', 'scale', 'srid' ].includes( prop )
1707
+ : !annotationHasEllipsis( ext[prop] );
1708
+ }
1709
+
1710
+ // TODO: still a bit annotation assignment specific
1711
+ function reportDuplicateExtensions( extensions, prop, issue, index, art ) {
1712
+ // TODO: think about messages for these
1713
+ if (prop === 'elements' || prop === 'enum' || prop === 'actions' || prop === 'columns' ||
1714
+ prop === 'params' || prop === 'returns' || prop === 'includes' )
1715
+ return; // extensions currently handled extra
1716
+ if (issue) {
1717
+ // eslint-disable-next-line no-nested-ternary
1718
+ let msg = (index < 0)
1719
+ ? 'anno-unstable-array'
1720
+ : (issue === true)
1721
+ ? 'anno-duplicate'
1722
+ : 'anno-duplicate-unrelated-layer';
1723
+ if (prop.charAt(0) !== '@' && prop !== 'doc') {
1724
+ msg = (issue === true)
1725
+ ? 'ext-duplicate-extend-type'
1726
+ : 'ext-duplicate-extend-type-unrelated-layer';
1727
+ // not sure whether to repeat the extended artifact in the message (we
1728
+ // have the semantic location, after all)
1522
1729
  }
1523
- else if (index > 0) { // more than one set (not just ...)
1524
- const variant = annoName === 'doc' ? 'doc' : 'std';
1525
- while (index >= 0) { // do not report for trailing [...]
1526
- const a = assignments[index--];
1527
- warning( 'anno-duplicate-same-file', [ a.name?.location || a.location, art ],
1528
- { '#': variant, anno: annoName } );
1730
+ const variant = prop === 'doc' ? 'doc' : 'std';
1731
+ for (const ext of extensions) {
1732
+ const anno = ext[prop];
1733
+ if (anno && !anno.$errorReported) {
1734
+ message( msg, [ anno.name?.location || anno.location, ext ],
1735
+ { '#': variant, anno: prop, type: art } );
1529
1736
  }
1530
1737
  }
1531
1738
  }
1532
- // Now apply the assignments - all but the first have a '...'
1533
- let result = null;
1534
- scheduledAssignments.reverse();
1535
- for (const a of scheduledAssignments)
1536
- result = applyAssignment( result, a, art, annoName );
1537
- art[annoName] = result.name ? result
1538
- : Object.assign( {}, scheduledAssignments[scheduledAssignments.length - 1], result );
1739
+ else if (index > 0) { // more than one set (not just ...)
1740
+ const variant = prop === 'doc' ? 'doc' : 'std';
1741
+ const msgid = (prop.charAt(0) === '@' || prop === 'doc')
1742
+ ? 'anno-duplicate-same-file' // TODO: always ext-duplicate-…
1743
+ : 'ext-duplicate-same-file';
1744
+ while (index >= 0) { // do not report for trailing [...]
1745
+ const ext = extensions[index--];
1746
+ const anno = ext[prop];
1747
+ warning( msgid, [ anno.name?.location || anno.location, ext ],
1748
+ { '#': variant, prop, anno: prop } );
1749
+ }
1750
+ }
1751
+ }
1752
+
1753
+ function applySingleExtension( art, ext, prop ) {
1754
+ if (prop === 'includes') {
1755
+ if (ext.kind === 'extend' && art.$inferred) {
1756
+ error( 'extend-for-generated', [ ext.name.location, ext ], { art },
1757
+ 'You can\'t use EXTEND on the generated $(ART)' );
1758
+ }
1759
+ else if (art.kind !== 'annotate' && !art._outer) { // not with elem extension in targetAspect
1760
+ const { absolute } = art.name;
1761
+ const dict = extensionsDict[absolute] || (extensionsDict[absolute] = []);
1762
+ dict.push( ext ); // TODO: change
1763
+ // console.log( 'ASI:',prop,art.name,ext,extensionsDict[absolute])
1764
+ }
1765
+ // art[prop] = (art[prop]) ? art[prop].concat( ext[prop] ) : ext[prop];
1766
+ }
1767
+ else if (prop === 'columns') {
1768
+ const { query } = art;
1769
+ if (!query?.from?.path)
1770
+ error( 'extend-columns', [ ext.columns[$location], ext ], { art } );
1771
+ else if (!query.columns)
1772
+ query.columns = [ { location: query.from.location, val: '*' }, ...ext.columns ];
1773
+ else
1774
+ query.columns.push( ...ext.columns );
1775
+ }
1776
+ else if ([ 'length', 'precision', 'scale', 'srid' ].includes( prop )) {
1777
+ const typeExts = art.$typeExts || (art.$typeExts = {});
1778
+ typeExts[prop] = ext;
1779
+ }
1780
+ else {
1781
+ const result = applyAssignment( art[prop], ext[prop], ext, prop );
1782
+ art[prop] = (result.name) ? result : Object.assign( {}, art[prop], result );
1783
+ }
1539
1784
  }
1540
1785
 
1541
1786
  function applyAssignment( previousAnno, anno, art, annoName ) {
1787
+ const firstEllipsis = annotationHasEllipsis( anno );
1788
+ if (!firstEllipsis)
1789
+ return anno;
1542
1790
  const hasBase = previousAnno?.literal === 'array';
1543
1791
  if (!previousAnno) {
1544
- const firstEllipsis = annotationHasEllipsis( anno );
1545
- if (!firstEllipsis)
1546
- return anno;
1547
- if (anno.$priority) { // already complained about with Define
1548
- const loc = firstEllipsis.location || anno.name.location;
1549
- message( 'anno-unexpected-ellipsis-layers', [ loc, art ], { code: '...' } );
1550
- }
1792
+ const loc = firstEllipsis.location || anno.name.location;
1793
+ message( 'anno-unexpected-ellipsis', [ loc, art ], { code: '...' } );
1551
1794
  previousAnno = { val: [] };
1552
1795
  }
1553
1796
  else if (previousAnno.literal !== 'array') {
@@ -1641,69 +1884,70 @@ function extend( model ) {
1641
1884
  return node.variant ? `${ ref }#${ node.variant.id }` : ref;
1642
1885
  }
1643
1886
 
1644
- // formerly in shared.js: -----------------------------------------------------
1645
-
1646
- // Copy annotations from `ext` to `art`, overwriting inferred ones.
1647
- // TODO: move to extend.js if not used anymore in define.js
1648
- function copyAnnotationsForExtensions( ext, art ) {
1649
- for (const annoProp in ext) {
1650
- if (annoProp.charAt(0) === '@' || annoProp === 'doc') {
1651
- const extAnno = ext[annoProp];
1652
- if (art[annoProp]?.$inferred)
1653
- art[annoProp] = extAnno; // overwrite $inferred annos
1654
- else
1655
- dictAddArray( art, annoProp, extAnno );
1656
- }
1887
+ /**
1888
+ * Copy the annotations `@cds.persistence.skip`/`@cds.persistence.exists` from
1889
+ * source to target if present on source but not target.
1890
+ *
1891
+ * @param {object} target
1892
+ * @param {object} source
1893
+ */
1894
+ function copyPersistenceAnnotations( target, source ) {
1895
+ if (!source)
1896
+ return;
1897
+
1898
+ const copyExists = !isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
1899
+ if (copyExists)
1900
+ copy( '@cds.persistence.exists' );
1901
+ copy( '@cds.persistence.skip' );
1902
+
1903
+ function copy( anno ) {
1904
+ if ( source[anno] && !target[anno] )
1905
+ target[anno] = { ...source[anno], $inferred: 'parent-origin' };
1657
1906
  }
1658
1907
  }
1659
1908
  }
1660
1909
 
1661
1910
  /**
1662
- * Group assignments by their layers. An assignment provided with a definition
1911
+ * Group extensions by their layers. A definition (for specified elements)
1663
1912
  * is considered to be provided in a layer named '', the lowest layer.
1664
1913
  *
1665
- * TODO: make this usable for extend (elements), too =
1666
- * do not use $priority, make assignments on define do not have own _block
1667
- *
1668
- * @param {object[]} assignments Array of assignments, e.g. extensions.
1669
- * @returns {Record<string, object>} key: layer name, value: {name, layer, assignments[]}`
1914
+ * @param {object[]} extensions Array of extensions.
1915
+ * @returns {Record<string, object>} key: layer name, value: {name, layer, extensions[]}`
1670
1916
  */
1671
- function layeredAssignments( assignments ) {
1917
+ function layeredExtensions( extensions ) {
1672
1918
  const layered = Object.create(null);
1673
- for (const a of assignments) {
1674
- const layer = a.$priority !== false && layers.layer( a );
1919
+ for (const ext of extensions) {
1920
+ const layer = (ext.kind === 'annotate' || ext.kind === 'extend') && layers.layer( ext );
1675
1921
  // just consider layer if Extend/Annotate, not Define
1676
1922
  const name = (layer) ? layer.realname : '';
1677
1923
  const done = layered[name];
1678
1924
  if (done)
1679
- done.assignments.push( a );
1925
+ done.extensions.push( ext );
1680
1926
  else
1681
- layered[name] = { name, layer, assignments: [ a ] };
1682
- // TODO: file - if set: unique in layer
1927
+ layered[name] = { name, layer, extensions: [ ext ] };
1683
1928
  }
1684
1929
  return layered;
1685
1930
  }
1686
1931
 
1687
1932
  /**
1688
- * Return assignments of the highest layers.
1933
+ * Return extensions of the highest layers.
1689
1934
  * Also returns whether there could be an issue:
1690
- * - false: there are just assignments in one file,
1691
- * - 'unrelated': there is just one assignment per layer
1692
- * - true: there is at least one layer with two or more assignments, and
1935
+ * - false: there are just extensions in one file,
1936
+ * - 'unrelated': there is just one extension per layer
1937
+ * - true: there is at least one layer with two or more extensions, and
1693
1938
  * at least two files are involved
1694
- * TODO: make this usable for extend (elements), too.
1695
1939
  *
1696
- * @param {Record<string, object>} layeredAnnos Structure as returned by layeredAssignments()
1940
+ * @param {Record<string, object>} layered Structure as returned by layeredExtensions()
1697
1941
  * @returns {{assignments, issue: boolean|string}}
1698
1942
  */
1699
- function assignmentsOfHighestLayers( layeredAnnos ) {
1700
- const layerNames = Object.keys( layeredAnnos );
1943
+ function extensionsOfHighestLayers( layered ) {
1944
+ const layerNames = Object.keys( layered );
1701
1945
  // console.log('HIB:',layerNames)
1702
1946
  if (layerNames.length <= 1) {
1703
1947
  const name = layerNames[0];
1704
- const { assignments } = layeredAnnos[name] || { assignments: [] };
1705
- delete layeredAnnos[name];
1706
- return { assignments, issue: inMoreThanOneFile( assignments ) };
1948
+ const highest = layered[name]?.extensions || [];
1949
+ delete layered[name];
1950
+ return { highest, issue: inMoreThanOneFile( highest ) };
1707
1951
  }
1708
1952
 
1709
1953
  // collect all layers which are lower than another layer
@@ -1711,42 +1955,41 @@ function assignmentsOfHighestLayers( layeredAnnos ) {
1711
1955
  allExtends[''] = {}; // the "Define" layer
1712
1956
  for (const name of layerNames) {
1713
1957
  if (name) // not the "Define" layer
1714
- Object.assign( allExtends, layeredAnnos[name].layer._layerExtends );
1958
+ Object.assign( allExtends, layered[name].layer._layerExtends );
1715
1959
  }
1716
1960
  // console.log('HIE:',Object.keys(allExtends))
1717
- const assignments = [];
1718
- const highest = [];
1961
+ const highest = []; // extensions
1962
+ const highestLayers = [];
1719
1963
  for (const name of layerNames) {
1720
1964
  if (!(name in allExtends)) {
1721
- const layer = layeredAnnos[name];
1722
- delete layeredAnnos[name];
1723
- highest.push( layer );
1724
- assignments.push( ...layer.assignments );
1965
+ const layer = layered[name];
1966
+ delete layered[name];
1967
+ highestLayers.push( layer );
1968
+ highest.push( ...layer.extensions );
1725
1969
  }
1726
1970
  }
1727
- assignments.sort( compareAssignments );
1728
- const good = highest.every( layer => !inMoreThanOneFile( layer.assignments ));
1971
+ highest.sort( compareExtensions );
1972
+ const good = highestLayers.every( layer => !inMoreThanOneFile( layer.extensions ));
1729
1973
  // TODO: use layer.file instead
1730
- const issue = !good || highest.length > 1 && 'unrelated';
1731
- // console.log('HI:',highest.map(l=>l.name),issue,issue&&assignments)
1732
- return { assignments, issue };
1974
+ const issue = !good || highestLayers.length > 1 && 'unrelated';
1975
+ // console.log('HI:',highest.map(l=>l.name),issue,issue&&extensions)
1976
+ return { highest, issue };
1733
1977
  }
1734
1978
 
1735
- function inMoreThanOneFile( assignments ) {
1736
- if (assignments.length <= 1)
1979
+ function inMoreThanOneFile( extensions ) {
1980
+ if (extensions.length <= 1)
1737
1981
  return false;
1738
- // annotations have no location with implicit `true`, doc have no name:
1739
- const file = (assignments[0].name || assignments[0]).location?.file;
1740
- return !file || assignments.slice(1).some( a => (a.name || a).location?.file !== file );
1982
+ const file = extensions[0].location?.file;
1983
+ return !file || extensions.slice(1).some( e => e.location?.file !== file );
1741
1984
  }
1742
1985
 
1743
1986
  /**
1744
- * Compare two assignments which are not comparable via layering:
1745
- * - via the fs.realpath of the file (not layer!) of the assignments, then
1746
- * - via the line, then column of the assignments.
1987
+ * Compare two extensions which are not comparable via layering:
1988
+ * - via the fs.realpath of the file (not layer!) of the extensions, then
1989
+ * - via the line, then column of the extensions.
1747
1990
  * Returns <0 if `a`<`b`, >1 if `a`>`b`, i.e. can be used for ascending sort.
1748
1991
  */
1749
- function compareAssignments( a, b ) {
1992
+ function compareExtensions( a, b ) {
1750
1993
  const fileA = layers.realname( a._block );
1751
1994
  const fileB = layers.realname( b._block );
1752
1995
  if (fileA !== fileB)
@@ -1755,29 +1998,6 @@ function compareAssignments( a, b ) {
1755
1998
  (a?.location?.col || 0) - (b?.location?.col || 0);
1756
1999
  }
1757
2000
 
1758
- /**
1759
- * Copy the annotations `@cds.persistence.skip`/`@cds.persistence.exists` from
1760
- * source to target if present on source but not target.
1761
- *
1762
- * @param {object} target
1763
- * @param {object} source
1764
- * @param {CSN.Options} options
1765
- */
1766
- function copyPersistenceAnnotations( target, source, options ) {
1767
- if (!source)
1768
- return;
1769
-
1770
- const copyExists = !isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
1771
- if (copyExists)
1772
- copy( '@cds.persistence.exists' );
1773
- copy( '@cds.persistence.skip' );
1774
-
1775
- function copy( anno ) {
1776
- if ( source[anno] && !target[anno] )
1777
- target[anno] = { ...source[anno], $inferred: 'parent-origin' };
1778
- }
1779
- }
1780
-
1781
2001
  function augmentEqual( location, assocname, relations, prefix = '' ) {
1782
2002
  const args = relations.map( eq );
1783
2003
  return (args.length === 1)
@@ -1811,15 +2031,14 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
1811
2031
  * @param {XSN.Extension} ext
1812
2032
  * @param {object} art
1813
2033
  */
1814
- function storeTypeExtension( ext, art ) {
1815
- // If there are no parameters to apply, don't store the extension.
1816
- if (!typeParameters.list.some( prop => ext[prop] !== undefined ))
1817
- return;
1818
- else if (!art._extendType)
1819
- setLink( art, '_extendType', [] );
1820
- art._extendType.push( ext );
1821
- }
1822
-
2034
+ // function storeTypeExtension( ext, art ) {
2035
+ // // If there are no parameters to apply, don't store the extension.
2036
+ // if (!typeParameters.list.some( prop => ext[prop] !== undefined ))
2037
+ // return;
2038
+ // else if (!art._extendType)
2039
+ // setLink( art, '_extendType', [] );
2040
+ // art._extendType.push( ext );
2041
+ // }
1823
2042
 
1824
2043
  function checkTextsLanguageAssocOption( model, options ) {
1825
2044
  const languages = model.definitions['sap.common.Languages'];