@sap/cds-compiler 3.0.0 → 3.1.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 (79) hide show
  1. package/CHANGELOG.md +104 -9
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +28 -16
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +24 -2
  7. package/doc/CHANGELOG_DEPRECATED.md +21 -1
  8. package/lib/api/main.js +92 -40
  9. package/lib/api/options.js +2 -3
  10. package/lib/base/keywords.js +64 -1
  11. package/lib/base/message-registry.js +33 -5
  12. package/lib/base/messages.js +54 -65
  13. package/lib/base/model.js +2 -0
  14. package/lib/base/optionProcessorHelper.js +53 -21
  15. package/lib/checks/actionsFunctions.js +8 -7
  16. package/lib/checks/selectItems.js +96 -14
  17. package/lib/checks/types.js +5 -8
  18. package/lib/checks/validator.js +1 -2
  19. package/lib/compiler/assert-consistency.js +65 -13
  20. package/lib/compiler/base.js +6 -4
  21. package/lib/compiler/builtins.js +93 -4
  22. package/lib/compiler/checks.js +1 -1
  23. package/lib/compiler/define.js +28 -23
  24. package/lib/compiler/extend.js +20 -11
  25. package/lib/compiler/finalize-parse-cdl.js +5 -9
  26. package/lib/compiler/index.js +2 -0
  27. package/lib/compiler/populate.js +37 -32
  28. package/lib/compiler/propagator.js +11 -6
  29. package/lib/compiler/resolve.js +15 -19
  30. package/lib/compiler/shared.js +54 -18
  31. package/lib/compiler/tweak-assocs.js +5 -11
  32. package/lib/compiler/utils.js +15 -6
  33. package/lib/edm/annotations/genericTranslation.js +12 -2
  34. package/lib/edm/annotations/preprocessAnnotations.js +18 -15
  35. package/lib/edm/csn2edm.js +18 -17
  36. package/lib/edm/edm.js +22 -13
  37. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  38. package/lib/edm/edmInboundChecks.js +85 -0
  39. package/lib/edm/edmPreprocessor.js +336 -665
  40. package/lib/edm/edmUtils.js +86 -45
  41. package/lib/gen/Dictionary.json +29 -9
  42. package/lib/gen/language.checksum +1 -1
  43. package/lib/gen/language.interp +1 -2
  44. package/lib/gen/languageLexer.js +3 -0
  45. package/lib/gen/languageParser.js +4332 -4496
  46. package/lib/inspect/.eslintrc.json +4 -0
  47. package/lib/inspect/index.js +14 -0
  48. package/lib/inspect/inspectModelStatistics.js +81 -0
  49. package/lib/inspect/inspectPropagation.js +189 -0
  50. package/lib/inspect/inspectUtils.js +44 -0
  51. package/lib/json/from-csn.js +19 -20
  52. package/lib/json/to-csn.js +11 -8
  53. package/lib/language/genericAntlrParser.js +150 -92
  54. package/lib/language/language.g4 +47 -74
  55. package/lib/main.d.ts +1 -0
  56. package/lib/model/api.js +1 -1
  57. package/lib/model/csnRefs.js +56 -29
  58. package/lib/model/csnUtils.js +29 -14
  59. package/lib/model/revealInternalProperties.js +6 -4
  60. package/lib/modelCompare/compare.js +3 -0
  61. package/lib/optionProcessor.js +81 -38
  62. package/lib/render/toCdl.js +57 -32
  63. package/lib/render/toHdbcds.js +1 -1
  64. package/lib/render/toSql.js +31 -11
  65. package/lib/render/utils/common.js +3 -4
  66. package/lib/transform/db/associations.js +43 -35
  67. package/lib/transform/db/cdsPersistence.js +0 -1
  68. package/lib/transform/db/flattening.js +3 -4
  69. package/lib/transform/db/transformExists.js +7 -5
  70. package/lib/transform/draft/db.js +1 -1
  71. package/lib/transform/forHanaNew.js +11 -2
  72. package/lib/transform/forOdataNew.js +4 -4
  73. package/lib/transform/localized.js +15 -11
  74. package/lib/transform/odata/typesExposure.js +14 -5
  75. package/lib/utils/file.js +28 -18
  76. package/lib/utils/moduleResolve.js +0 -1
  77. package/package.json +3 -4
  78. package/share/messages/syntax-expected-integer.md +9 -8
  79. package/lib/checks/unknownMagic.js +0 -41
@@ -109,7 +109,7 @@
109
109
 
110
110
  'use strict';
111
111
 
112
- const { isDeprecatedEnabled, forEachGeneric, forEachInOrder } = require('../base/model');
112
+ const { forEachGeneric, forEachInOrder } = require('../base/model');
113
113
  const {
114
114
  dictAdd, dictAddArray, dictForEach, pushToDict,
115
115
  } = require('../base/dictionaries');
@@ -147,6 +147,7 @@ function define( model ) {
147
147
  } = model.$messageFunctions;
148
148
  const {
149
149
  resolveUncheckedPath,
150
+ checkAnnotate,
150
151
  defineAnnotations,
151
152
  } = model.$functions;
152
153
 
@@ -197,11 +198,12 @@ function define( model ) {
197
198
  }
198
199
 
199
200
  // Phase 1: ----------------------------------------------------------------
201
+ // Functions called from top-level: addSource()
200
202
 
201
203
  /**
202
204
  * Add definitions of the given source AST, both CDL and CSN
203
205
  *
204
- * @param {XSN.AST} src
206
+ * @param {XSN.SourceAst} src
205
207
  */
206
208
  function addSource( src ) {
207
209
  // handle sub model from parser
@@ -266,7 +268,7 @@ function define( model ) {
266
268
  return;
267
269
  }
268
270
  setLink( art, '_block', block );
269
- // dictAdd might set $duplicates to true
271
+ // dictAdd might set $duplicates
270
272
  dictAdd( model.definitions, absolute, art );
271
273
  }
272
274
 
@@ -306,7 +308,7 @@ function define( model ) {
306
308
  * declaration.
307
309
  *
308
310
  * @param {XSN.Using} decl Node to be expanded and added to `src`
309
- * @param {XSN.AST} src
311
+ * @param {XSN.SourceAst} src
310
312
  */
311
313
  function addUsing( decl, src ) {
312
314
  if (decl.usings) {
@@ -390,14 +392,17 @@ function define( model ) {
390
392
  setLink( vocab, '_block', block );
391
393
  const { name } = vocab;
392
394
  if (!name.absolute)
393
- name.absolute = prefix + name.path.map( id => id.id ).join('.');
395
+ name.absolute = prefix + pathName( name.path );
394
396
  dictAdd( model.vocabularies, name.absolute, vocab );
395
397
  }
396
398
 
397
399
  // Phase 2 ("init") --------------------------------------------------------
400
+ // Functions called from top-level: initNamespaceAndUsing(), initArtifact(),
401
+ // initVocabulary()
398
402
 
399
403
  function checkRedefinition( art ) {
400
- if (!art.$duplicates)
404
+ if (!art.$duplicates || art.$errorReported === 'syntax-duplicate-extend' ||
405
+ art.$errorReported === 'syntax-duplicate-annotate')
401
406
  return;
402
407
  if (art._main) {
403
408
  error( 'duplicate-definition', [ art.name.location, art ], {
@@ -504,18 +509,6 @@ function define( model ) {
504
509
  definitions[prefix] = parent;
505
510
  initParentLink( parent, definitions );
506
511
  }
507
- if (art.kind !== 'namespace' &&
508
- isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' )) {
509
- let p = parent;
510
- while (p && kindProperties[p.kind].artifacts)
511
- p = p._parent;
512
- if (p) {
513
- error( 'subartifacts-not-supported', [ art.name.location, art ],
514
- { art: p, prop: 'deprecated._generatedEntityNameWithUnderscore' },
515
- // eslint-disable-next-line max-len
516
- 'With the option $(PROP), no sub artifact can be defined for a non-context/service $(ART)' );
517
- }
518
- }
519
512
  setLink( art, '_parent', parent );
520
513
  if (!parent._subArtifacts)
521
514
  setLink( parent, '_subArtifacts', Object.create(null) );
@@ -611,7 +604,17 @@ function define( model ) {
611
604
  // Either expression (value), expand or new association (target && type)
612
605
  else if (col.value || col.expand || (col.target && col.type)) {
613
606
  setLink( col, '_block', parent._block );
614
- defineAnnotations( col, col, parent._block ); // TODO: complain with inline
607
+ defineAnnotations( col, col, parent._block );
608
+ if (col.inline) { // `@anno elem.{ * }` does not work
609
+ if (col.doc)
610
+ warning( 'syntax-anno-ignored', [ col.doc.location, col ], { '#': 'doc' } );
611
+
612
+ // col.$annotations no available for CSN input, have to search.
613
+ // Warning about first annotation should be enough to avoid spam.
614
+ const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
615
+ if (firstAnno)
616
+ warning( 'syntax-anno-ignored', [ col[firstAnno].name.location, col ] );
617
+ }
615
618
  // TODO: allow sub queries? at least in top-level expand without parallel ref
616
619
  if (columns)
617
620
  initExprForQuery( col.value, parent );
@@ -670,6 +673,8 @@ function define( model ) {
670
673
  * @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
671
674
  */
672
675
  function approveExistsInChildren(exprOrPathElement) {
676
+ if (!exprOrPathElement) // may be null in case of parse error
677
+ return;
673
678
  if (exprOrPathElement.$expected === 'exists')
674
679
  exprOrPathElement.$expected = 'approved-exists';
675
680
  // Drill down
@@ -880,9 +885,7 @@ function define( model ) {
880
885
 
881
886
  /**
882
887
  * Set property `_parent` for all elements in `parent` to `parent` and do so
883
- * recursively for all sub elements. Also set the property
884
- * `name.component` of the element with the help of argument `prefix`
885
- * (which is basically the component name of the `parent` element plus a dot).
888
+ * recursively for all sub elements.
886
889
  */
887
890
  // If not for extensions: construct === parent
888
891
  function initMembers( construct, parent, block, initExtensions = false ) {
@@ -1014,6 +1017,8 @@ function define( model ) {
1014
1017
  setMemberParent( elem, name, parent, construct !== parent && prop );
1015
1018
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1016
1019
  checkRedefinition( elem );
1020
+ if (elem.kind === 'annotate')
1021
+ checkAnnotate( elem, elem );
1017
1022
  defineAnnotations( elem, elem, bl );
1018
1023
  initMembers( elem, elem, bl, initExtensions );
1019
1024
 
@@ -1125,7 +1130,7 @@ function mergeI18nBlocks( model ) {
1125
1130
  * Add the source's translations to the model. Warns if the sources translations
1126
1131
  * do not match the ones from previous sources.
1127
1132
  *
1128
- * @param {XSN.AST} src
1133
+ * @param {XSN.SourceAst} src
1129
1134
  */
1130
1135
  function initI18nFromSource( src ) {
1131
1136
  for (const langKey of Object.keys( src.i18n )) {
@@ -33,6 +33,7 @@ function extend( model ) {
33
33
  const {
34
34
  resolvePath,
35
35
  resolveUncheckedPath,
36
+ checkAnnotate,
36
37
  defineAnnotations,
37
38
  attachAndEmitValidNames,
38
39
  checkDefinitions,
@@ -179,6 +180,10 @@ function extend( model ) {
179
180
  checkDefinitions( ext, art, 'actions');
180
181
  checkDefinitions( ext, art, 'params');
181
182
  checkDefinitions( ext, art, 'columns');
183
+ if (ext.includes)
184
+ applyIncludes( ext, art ); // emits error if `includes` is set
185
+ if (ext.kind === 'annotate')
186
+ checkAnnotate( ext, art );
182
187
  defineAnnotations( ext, art, ext._block, ext.kind );
183
188
  }
184
189
  return true;
@@ -238,6 +243,8 @@ function extend( model ) {
238
243
  art.includes = [ ...ext.includes ];
239
244
  applyIncludes( ext, art );
240
245
  }
246
+ if (ext.kind === 'annotate')
247
+ checkAnnotate( ext, art );
241
248
  defineAnnotations( ext, art, ext._block, ext.kind );
242
249
  // TODO: do we allow to add elements with array of {...}? If yes, adapt
243
250
  initMembers( ext, art, ext._block ); // might set _extend, _annotate
@@ -389,9 +396,13 @@ function extend( model ) {
389
396
  for (const ext of exts) {
390
397
  delete ext.name.path[0]._artifact; // get message for root
391
398
  // TODO: make resolvePath('extend'/'annotate') ignore namespaces
392
- if (resolvePath( ext.name, ext.kind, ext )) { // should issue error/info
399
+ // Don't try to apply annotations in the `localized.` namespace.
400
+ // That's done in `localized.js`.
401
+ if (!name.startsWith('localized.') &&
402
+ resolvePath( ext.name, ext.kind, ext )) { // should issue error/info
393
403
  // should issue error for cds extensions (annotate ok)
394
404
  if (art.kind === 'namespace') {
405
+ // TODO: Emit error if namespace is extended by non-definitions.
395
406
  info( 'anno-namespace', [ ext.name.location, ext ], {},
396
407
  'Namespaces can\'t be annotated' );
397
408
  }
@@ -451,6 +462,12 @@ function extend( model ) {
451
462
  * @param {XSN.Artifact} art
452
463
  */
453
464
  function applyIncludes( ext, art ) {
465
+ if (kindProperties[art.kind].include !== true) {
466
+ error('extend-unexpected-include', [ ext.includes[0]?.location, ext ], { kind: art.kind },
467
+ 'Can\'t extend $(KIND) with includes');
468
+ return;
469
+ }
470
+
454
471
  if (!art._ancestors)
455
472
  setLink( art, '_ancestors', [] ); // recursive array of includes
456
473
  for (const ref of ext.includes) {
@@ -512,9 +529,7 @@ function extend( model ) {
512
529
  const fioriAnno = art['@fiori.draft.enabled'];
513
530
  const fioriEnabled = fioriAnno && (fioriAnno.val === undefined || fioriAnno.val);
514
531
 
515
- const textsName = (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
516
- ? `${ art.name.absolute }_texts`
517
- : `${ art.name.absolute }.texts`;
532
+ const textsName = `${ art.name.absolute }.texts`;
518
533
  const textsEntity = model.definitions[textsName];
519
534
  const localized = localizedData( art, textsEntity, fioriEnabled );
520
535
  if (!localized)
@@ -657,8 +672,6 @@ function extend( model ) {
657
672
  };
658
673
  dictAdd( art.elements, 'ID_texts', textId );
659
674
  }
660
- if (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
661
- setLink( art, '_base', base );
662
675
 
663
676
  dictAdd( art.elements, 'locale', locale );
664
677
  if (addTextsLanguageAssoc) {
@@ -831,9 +844,7 @@ function extend( model ) {
831
844
  target = resolvePath( origin.targetAspect, 'compositionTarget', origin );
832
845
  if (!target || !target.elements)
833
846
  return;
834
- const entityName = (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
835
- ? `${ base.name.absolute }_${ elem.name.id }`
836
- : `${ base.name.absolute }.${ elem.name.id }`;
847
+ const entityName = `${ base.name.absolute }.${ elem.name.id }`;
837
848
  const entity = allowAspectComposition( target, elem, keys, entityName ) &&
838
849
  createTargetEntity( target, elem, keys, entityName, base );
839
850
  elem.target = {
@@ -965,8 +976,6 @@ function extend( model ) {
965
976
  // even if target cardinality is 1..1
966
977
  up.notNull = { location, val: true };
967
978
  }
968
- if (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
969
- setLink( art, '_base', base._base || base );
970
979
 
971
980
  dictAdd( art.elements, 'up_', up);
972
981
  addProxyElements( art, target.elements, 'aspect-composition', target.name && location );
@@ -82,8 +82,12 @@ function finalizeParseCdl( model ) {
82
82
  for (const include of artifact.includes || [])
83
83
  resolveUncheckedPath(include, 'include', main);
84
84
 
85
+ // define.js takes care that `target` is a ref and
86
+ // `targetAspect` is a structure.
85
87
  if (artifact.target)
86
88
  resolveUncheckedPath(artifact.target, 'target', main);
89
+ if (artifact.targetAspect)
90
+ resolveTypesForParseCdl(artifact.targetAspect, main);
87
91
 
88
92
  if (artifact.from) {
89
93
  const { from } = artifact;
@@ -93,12 +97,6 @@ function finalizeParseCdl( model ) {
93
97
  resolveTypesForParseCdl(from, main);
94
98
  }
95
99
 
96
- if (artifact.targetAspect) {
97
- if (artifact.targetAspect.path)
98
- resolveUncheckedPath(artifact.targetAspect, 'target', main);
99
- resolveTypesForParseCdl(artifact.targetAspect, main);
100
- }
101
-
102
100
  // Recursively go through all XSN properties. There are a few that need to be
103
101
  // handled specifically. Refer to the code below this loop for details.
104
102
  for (const prop in artifact) {
@@ -158,8 +156,6 @@ function finalizeParseCdl( model ) {
158
156
  * @param {XSN.Artifact} user
159
157
  */
160
158
  function resolveTypeUnchecked(artWithType, user) {
161
- if (!artWithType.type)
162
- return;
163
159
  const root = artWithType.type.path && artWithType.type.path[0];
164
160
  if (!root) // parse error
165
161
  return;
@@ -187,7 +183,7 @@ function finalizeParseCdl( model ) {
187
183
  let struct = artWithType;
188
184
  while (struct.kind === 'element')
189
185
  struct = struct._parent;
190
- if (struct.kind === 'select' || struct !== user._main) {
186
+ if (struct.kind === 'select' || struct.kind === 'annotation' || struct !== user._main) {
191
187
  message( 'type-unexpected-typeof', [ artWithType.type.location, user ],
192
188
  { keyword: 'type of', '#': struct.kind } );
193
189
  return;
@@ -84,6 +84,8 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
84
84
  return parseCsn.parse( source, filename, options, messageFunctions );
85
85
  if (options.fallbackParser) // any other value: like 'cdl' (historic reasons)
86
86
  return parseLanguage( source, filename, options, messageFunctions );
87
+ if (source.startsWith('{')) // Source may be JSON.
88
+ return parseCsn.parse( source, filename, options, messageFunctions );
87
89
 
88
90
  const model = { location: { file: filename } };
89
91
  messageFunctions.error( 'file-unknown-ext', emptyWeakLocation(filename),
@@ -73,14 +73,8 @@ function populate( model ) {
73
73
  /** @type {any} may also be a boolean */
74
74
  let newAutoExposed = [];
75
75
 
76
- // behavior depending on option `deprecated`:
77
- const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
78
- // TODO: we should get rid of noElementsExpansion soon; both
79
- // beta.nestedProjections and beta.universalCsn do not work with it.
80
76
  const scopedRedirections
81
- = enableExpandElements &&
82
- !isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ) &&
83
- !isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
77
+ = !isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
84
78
  !isDeprecatedEnabled( options, '_longAutoexposed' ) &&
85
79
  !isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ) &&
86
80
  !isDeprecatedEnabled( options, '_noScopedRedirections' );
@@ -109,6 +103,8 @@ function populate( model ) {
109
103
  function traverseElementEnvironments( art ) {
110
104
  populateView( art );
111
105
  environment( art );
106
+ if (art.elements$)
107
+ mergeSpecifiedElements(art);
112
108
  forEachMember( art, traverseElementEnvironments );
113
109
  }
114
110
 
@@ -165,7 +161,7 @@ function populate( model ) {
165
161
  // console.log(message( null, art.location, art, {}, 'Info','FT').toString())
166
162
  const chain = [];
167
163
  while (art && !('_effectiveType' in art) &&
168
- (art.type || art._origin || art.value && art.value.path) &&
164
+ (art.type || art._origin || art.value?.path || art.value?.type) &&
169
165
  // TODO: really stop at art.enum? See #8942
170
166
  !art.target && !art.enum && !art.elements && !art.items) {
171
167
  chain.push( art );
@@ -195,7 +191,7 @@ function populate( model ) {
195
191
  let eType = art;
196
192
  if (eType._outer)
197
193
  eType = effectiveType( eType._outer );
198
- // collect the "latest" cardinality (calculate lazyly if necessary)
194
+ // collect the "latest" cardinality (calculate lazily if necessary)
199
195
  let cardinality = art.cardinality ||
200
196
  art._effectiveType && (() => getCardinality( art._effectiveType ));
201
197
  let prev = art;
@@ -222,10 +218,12 @@ function populate( model ) {
222
218
  return art._origin;
223
219
  if (art.type)
224
220
  return resolveType( art.type, art );
221
+ if (art.value?.type)
222
+ return resolveType( art.value.type, art );
225
223
  // console.log( 'EXPR-IN', art.kind, refString(art.name) )
226
224
  if (!art._main || !art.value || !art.value.path)
227
225
  return undefined;
228
- if (art._pathHead && art.value) {
226
+ if (art._pathHead && art.value.path) {
229
227
  setLink( art, '_origin', resolvePath( art.value, 'expr', art, null ) );
230
228
  return art._origin;
231
229
  }
@@ -246,7 +244,9 @@ function populate( model ) {
246
244
  let struct = user;
247
245
  while (struct.kind === 'element')
248
246
  struct = struct._parent;
249
- if (struct.kind === 'select') {
247
+ if (struct.kind === 'select' || struct.kind === 'annotation') {
248
+ // `type of` in annotation definitions can't work, because csn type refs
249
+ // always refer to definitions.
250
250
  message( 'type-unexpected-typeof', [ ref.location, user ],
251
251
  { keyword: 'type of', '#': struct.kind } );
252
252
  // we actually refer to an element in _combined; TODO: return null if
@@ -293,7 +293,7 @@ function populate( model ) {
293
293
 
294
294
 
295
295
  function expandItems( art, origin, eType ) {
296
- if (!enableExpandElements || art.items)
296
+ if (art.items)
297
297
  return false;
298
298
  if (isInParents( art, eType )) {
299
299
  art.items = 0; // circular
@@ -310,8 +310,6 @@ function populate( model ) {
310
310
  }
311
311
 
312
312
  function expandElements( art, struct, eType ) {
313
- if (!enableExpandElements)
314
- return false;
315
313
  if (art.elements || art.kind === '$tableAlias' ||
316
314
  // no element expansions for "non-proper" types like
317
315
  // entities (as parameter types) etc:
@@ -345,7 +343,7 @@ function populate( model ) {
345
343
  }
346
344
 
347
345
  function expandEnum( art, origin ) {
348
- if (!enableExpandElements || art.enum)
346
+ if (art.enum)
349
347
  return false;
350
348
  const ref = art.type || art.value || art.name;
351
349
  const location = weakLocation( ref && ref.location || art.location );
@@ -425,8 +423,6 @@ function populate( model ) {
425
423
  setLink( view, '_status', '_query' );
426
424
  // must be run in order “sub query in FROM first”:
427
425
  traverseQueryPost( view.query, null, populateQuery );
428
- if (view.elements$) // specified elements
429
- mergeSpecifiedElements( view );
430
426
  if (!view.$entity) {
431
427
  model._entities.push( view );
432
428
  view.$entity = ++model.$entity;
@@ -435,14 +431,25 @@ function populate( model ) {
435
431
  }
436
432
  }
437
433
 
438
- function mergeSpecifiedElements( view ) {
434
+ /**
435
+ * Merge _specified_ elements with _inferred_ elements in the given view/element,
436
+ * where specified elements can appear through CSN.
437
+ *
438
+ * We only copy annotations, since they are not part of `columns`,
439
+ * but only appear in `elements` in CSN.
440
+ *
441
+ * This is important to ensure re-compilability.
442
+ *
443
+ * @param art
444
+ */
445
+ function mergeSpecifiedElements( art ) {
439
446
  // Later we use specified elements as proxies to inferred of leading query
440
447
  // (No, we probably do not.)
441
- for (const id in view.elements) {
442
- const ielem = view.elements[id]; // inferred element
443
- const selem = view.elements$[id]; // specified element
448
+ for (const id in art.elements) {
449
+ const ielem = art.elements[id]; // inferred element
450
+ const selem = art.elements$[id]; // specified element
444
451
  if (!selem) {
445
- info( 'query-missing-element', [ ielem.name.location, view ], { id },
452
+ info( 'query-missing-element', [ ielem.name.location, art ], { id },
446
453
  'Element $(ID) is missing in specified elements' );
447
454
  }
448
455
  else {
@@ -452,10 +459,14 @@ function populate( model ) {
452
459
  ielem[prop] = selem[prop];
453
460
  }
454
461
  selem.$replacement = true;
462
+ if (selem.elements) {
463
+ setLink(ielem, 'elements$', selem.elements);
464
+ delete selem.elements;
465
+ }
455
466
  }
456
467
  }
457
- for (const id in view.elements$) {
458
- const selem = view.elements$[id]; // specified element
468
+ for (const id in art.elements$) {
469
+ const selem = art.elements$[id]; // specified element
459
470
  if (!selem.$replacement) {
460
471
  error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
461
472
  'Element $(ID) does not result from the query' );
@@ -472,8 +483,6 @@ function populate( model ) {
472
483
  forEachGeneric( query, '$tableAliases', resolveTabRef );
473
484
 
474
485
  initFromColumns( query, query.columns );
475
- // TODO: already in definer: complain about EXCLUDING with no wildcard
476
- // (would have been automatically with a good CDL syntax: `* without (...)`)
477
486
  if (query.excludingDict) {
478
487
  for (const name in query.excludingDict)
479
488
  resolveExcluding( name, query._combined, query.excludingDict, query );
@@ -1037,8 +1046,6 @@ function populate( model ) {
1037
1046
  // which is not a context/service/namespace, or the definition itself.
1038
1047
  // If inside service, it is the direct child of the (most inner) service.
1039
1048
  function definitionScope( art ) {
1040
- if (art._base) // with deprecated.generatedEntityNameWithUnderscore
1041
- return art._base;
1042
1049
  let base = art;
1043
1050
  while (art._parent) {
1044
1051
  if (art._parent.kind === 'service')
@@ -1110,10 +1117,8 @@ function populate( model ) {
1110
1117
  // the name for dependent entities have already been created using `_` then
1111
1118
  return `${ service.name.absolute }.${ name }`;
1112
1119
  }
1113
- if (isDeprecatedEnabled( options, '_longAutoexposed' )) {
1114
- const dedot = isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' );
1115
- return `${ service.name.absolute }.${ dedot ? absolute.replace( /\./g, '_' ) : absolute }`;
1116
- }
1120
+ if (isDeprecatedEnabled( options, '_longAutoexposed' ))
1121
+ return `${ service.name.absolute }.${ absolute }`;
1117
1122
  const base = definitionScope( target );
1118
1123
  if (base === target)
1119
1124
  return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf('.') + 1 ) }`;
@@ -1,4 +1,10 @@
1
- //
1
+ // Propagate properties in XSN
2
+
3
+ // See also internalDoc/PropagatedCsn.md.
4
+ // As opposed to that document, the propagator here works on XSN, not CSN.
5
+ // We also do not deep-copy member dictionaries here, but create proxy members
6
+ // which get their properties via propagation: we use function `onlyViaParent`
7
+ // if that property would not be propagated otherwise.
2
8
 
3
9
  'use strict';
4
10
 
@@ -11,6 +17,7 @@ const {
11
17
  const { setLink, linkToOrigin, withAssociation } = require('./utils');
12
18
  // const { refString } = require( '../base/messages')
13
19
 
20
+ // Note that propagation here is also used for deep-copying (function `onlyViaParent`)
14
21
  function propagate( model ) {
15
22
  const props = {
16
23
  '@com.sap.gtt.core.CoreModel.Indexable': never,
@@ -56,7 +63,6 @@ function propagate( model ) {
56
63
  returns,
57
64
  };
58
65
  const { options } = model;
59
- const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
60
66
  // eslint-disable-next-line max-len
61
67
  const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
62
68
 
@@ -189,7 +195,7 @@ function propagate( model ) {
189
195
  if (!type || type._main)
190
196
  return false;
191
197
  // We do not consider the $expand status, as elements are already expanded
192
- // by the resolve(), and if not due to deprecated._noElementsExpansion
198
+ // by the resolve()
193
199
  run( type );
194
200
  return type[prop];
195
201
  }
@@ -313,11 +319,10 @@ function propagate( model ) {
313
319
 
314
320
  function items( prop, target, source ) {
315
321
  // usually considered expensive, except:
316
- // - array of Entity (smooth upgrade: array of String(3), array of DerivedScalar)
322
+ // - array of Entity
317
323
  const line = availableAtType( prop, target, source );
318
324
  if (!line ||
319
- line.type && line.type._artifact && line.type._artifact.kind === 'entity' ||
320
- !line.elements && !line.enum && !line.items && !enableExpandElements)
325
+ line.type && line.type._artifact && line.type._artifact.kind === 'entity')
321
326
  returns( prop, target, source, true );
322
327
  }
323
328
  }
@@ -87,6 +87,7 @@ function resolve( model ) {
87
87
  } = model.$messageFunctions;
88
88
  const {
89
89
  resolvePath,
90
+ checkAnnotate,
90
91
  defineAnnotations,
91
92
  attachAndEmitValidNames,
92
93
  lateExtensions,
@@ -103,11 +104,6 @@ function resolve( model ) {
103
104
 
104
105
  /** @type {any} may also be a boolean */
105
106
 
106
- // behavior depending on option `deprecated`:
107
- const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
108
- // TODO: we should get rid of noElementsExpansion soon; both
109
- // beta.nestedProjections and beta.universalCsn do not work with it.
110
-
111
107
  return doResolve();
112
108
 
113
109
  function doResolve() {
@@ -314,7 +310,7 @@ function resolve( model ) {
314
310
  if (obj.type) // TODO: && !obj.type.$inferred ?
315
311
  resolveTypeExpr( obj, art );
316
312
  const type = effectiveType( obj ); // make sure implicitly redirected target exists
317
- if (!obj.items && type && type.items && enableExpandElements) {
313
+ if (!obj.items && type && type.items) {
318
314
  // TODO: shouldn't be this part of populate.js ?
319
315
  const items = {
320
316
  location: weakLocation( (obj.type || obj).location ),
@@ -327,8 +323,7 @@ function resolve( model ) {
327
323
  }
328
324
  if (obj.items) { // TODO: make this a while in v2 (also items proxy)
329
325
  obj = obj.items || obj; // the object which has type properties
330
- if (enableExpandElements)
331
- effectiveType(obj);
326
+ effectiveType(obj);
332
327
  }
333
328
  if (obj.type) { // TODO: && !obj.type.$inferred ?
334
329
  if (obj !== (art.returns || art)) // not already checked
@@ -481,10 +476,7 @@ function resolve( model ) {
481
476
  if (ext.$extension) // extension for known artifact -> already applied
482
477
  return;
483
478
  annotateMembers( ext );
484
- for (const prop in ext) {
485
- if (prop.charAt(0) === '@')
486
- chooseAssignment( prop, ext );
487
- }
479
+ chooseAnnotationsInArtifact( ext );
488
480
  }
489
481
 
490
482
  /**
@@ -519,6 +511,8 @@ function resolve( model ) {
519
511
  setArtifactLink( ext.name, art );
520
512
 
521
513
  if (art) {
514
+ if (art.kind === 'annotate')
515
+ checkAnnotate( ext, art );
522
516
  defineAnnotations( ext, art, ext._block, ext.kind );
523
517
  // eslint-disable-next-line no-shadow
524
518
  forEachMember( ext, ( elem, name, prop ) => {
@@ -541,7 +535,7 @@ function resolve( model ) {
541
535
  }
542
536
  else if (prop === 'actions') {
543
537
  if (!feature) {
544
- warning( 'anno-unexpected-actions', [ ext.name.location, art ], {},
538
+ warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
545
539
  'Actions and functions only exist top-level and for entities' );
546
540
  }
547
541
  else {
@@ -572,7 +566,7 @@ function resolve( model ) {
572
566
  // Currently(?), effectiveType() does not calculate the effective type of
573
567
  // its line item:
574
568
  effectiveType( obj );
575
- if (art._annotate.elements)
569
+ if (art._annotate.elements) // explicit $expand on aor needed
576
570
  setExpandStatusAnnotate( aor, 'annotate' );
577
571
  annotate( obj, 'element', 'elements', 'enum', art );
578
572
  annotate( art, 'action', 'actions' );
@@ -596,6 +590,8 @@ function resolve( model ) {
596
590
  // eslint-disable-next-line no-shadow
597
591
  function annotate( obj, kind, prop, altProp, parent = obj ) {
598
592
  const dict = art._annotate[prop];
593
+ if (dict && art._annotate[prop])
594
+ setExpandStatusAnnotate( art, 'annotate' );
599
595
  const env = obj[prop] || altProp && obj[altProp] || null;
600
596
  for (const n in dict)
601
597
  annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
@@ -618,7 +614,7 @@ function resolve( model ) {
618
614
 
619
615
  function expandParameters( action ) {
620
616
  // see also expandElements()
621
- if (!enableExpandElements || !effectiveType( action ))
617
+ if (!effectiveType( action ))
622
618
  return;
623
619
  const chain = [];
624
620
  // Should we be able to consider params and returns separately?
@@ -959,7 +955,7 @@ function resolve( model ) {
959
955
  resolveBy( query.$orderBy, 'order-by-union', query.elements, query._parent );
960
956
  if (query.orderBy) { // ORDER BY
961
957
  // search in `query.elements` after having checked table aliases of the current query
962
- resolveBy( query.orderBy, 'expr', query.elements );
958
+ resolveBy( query.orderBy, 'order-by', query.elements );
963
959
  // TODO: disallow resulting element ref if in expression!
964
960
  // Necessary to check it in the compiler as it might work with other semantics on DB!
965
961
  // (we could downgrade it to a warning if name is equal to unique source element name)
@@ -1267,7 +1263,7 @@ function resolve( model ) {
1267
1263
  const typeArt = resolveType( art.type, user );
1268
1264
  if (typeArt) {
1269
1265
  resolveTypeArgumentsUnchecked( art, typeArt, user );
1270
- checkTypeArguments( art );
1266
+ checkTypeArguments( art, typeArt );
1271
1267
  }
1272
1268
  }
1273
1269
 
@@ -1275,13 +1271,13 @@ function resolve( model ) {
1275
1271
  * Check the type arguments on `artWithType`.
1276
1272
  * If the effective type is an array or structured type, an error is emitted.
1277
1273
  */
1278
- function checkTypeArguments( artWithType ) {
1274
+ function checkTypeArguments( artWithType, typeArt ) {
1279
1275
  // Note: `_effectiveType` may point to `artWithType` itself, if the type is structured.
1280
1276
  // Also: For enums, it points to the enum type, which is why this trick is needed.
1281
1277
  // TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
1282
1278
  // trick may be removed if effectiveType() does not stop at enums.
1283
1279
  const cyclic = new Set();
1284
- let effectiveTypeArt = effectiveType( artWithType );
1280
+ let effectiveTypeArt = effectiveType( typeArt );
1285
1281
  while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
1286
1282
  cyclic.add(effectiveTypeArt);
1287
1283
  const underlyingEnumType = directType(effectiveTypeArt);