@sap/cds-compiler 2.5.2 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
@@ -88,7 +88,7 @@
88
88
  // Sub Phase 2 (initXYZ)
89
89
  // - set _parent, _main (later: _service?) links, and _block links of members
90
90
  // - add _subArtifacts dictionary and "namespace artifacts" for name resolution
91
- // - duplicate checks (TODO - currently via preProcessArtifact in definer)
91
+ // - duplicate checks
92
92
  // - structure checks ?
93
93
  // - annotation assignments
94
94
  // - POST: resolvePath() can be called for artifact references (if complete model)
@@ -101,19 +101,19 @@
101
101
  // to avoid consequential or repeated errors.
102
102
  // - But: The same artifact is added to multiple dictionaries.
103
103
  // - Solution part 1: $duplicates as property of the artifact or member
104
- // for 'definitions', '_artifacts' and member dictionaries.
105
- // - Solution part 2: array value in dictionary for duplicates in CDL 'artifacts'
106
- // dictionary, also used for `_combined` in query search dictionary.
104
+ // for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
105
+ // dictionary of the whole model, `$tableAliases` dictionary of queries.
106
+ // - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
107
+ // dictionary, `_combined` dictionary for query search, `$tableAliases`
108
+ // of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
107
109
 
108
110
  'use strict';
109
111
 
110
- const {
111
- makeMessageFunction, searchName, weakLocation,
112
- } = require('../base/messages');
112
+ const { searchName, weakLocation } = require('../base/messages');
113
113
  const {
114
114
  isDeprecatedEnabled, isBetaEnabled,
115
115
  setProp, forEachGeneric, forEachInOrder,
116
- forEachMember, forEachDefinition,
116
+ forEachDefinition,
117
117
  forEachMemberRecursivelyWithQuery,
118
118
  } = require('../base/model');
119
119
  const {
@@ -122,20 +122,20 @@ const {
122
122
  const {
123
123
  dictLocation,
124
124
  } = require('../base/location');
125
+ const { kindProperties, dictKinds } = require('./base');
125
126
  const {
126
- annotationVal, annotationIsFalse, annotateWith,
127
- } = require('./utils');
128
- const {
129
- dictKinds,
130
- kindProperties,
131
- fns,
127
+ annotationVal,
128
+ annotationIsFalse,
129
+ annotateWith,
132
130
  linkToOrigin,
133
131
  setMemberParent,
134
132
  storeExtension,
135
133
  dependsOnSilent,
136
- } = require('./shared');
134
+ augmentPath,
135
+ splitIntoPath,
136
+ } = require('./utils');
137
137
  const { compareLayer, layer } = require('./moduleLayers');
138
- const { initBuiltins } = require('./builtins');
138
+ const { initBuiltins, isInReservedNamespace } = require('./builtins');
139
139
  const setLink = setProp;
140
140
 
141
141
  /**
@@ -150,36 +150,41 @@ const setLink = setProp;
150
150
  *
151
151
  * @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
152
152
  */
153
- function getDefinerFunctions( model ) {
153
+ function define( model ) {
154
154
  const { options } = model;
155
155
  // Get simplified "resolve" functionality and the message function:
156
156
  const {
157
157
  message, error, warning, info, messages,
158
- } = makeMessageFunction( model, model.options, 'compile' );
158
+ } = model.$messageFunctions;
159
159
  const {
160
160
  resolveUncheckedPath,
161
161
  resolvePath,
162
162
  resolveTypeArguments,
163
163
  defineAnnotations,
164
164
  attachAndEmitValidNames,
165
- } = fns( model );
166
- const extensionsDict = Object.create(null);
167
- let addTextsLanguageAssoc = false;
168
-
169
- return {
170
- define,
165
+ } = model.$functions;
166
+ Object.assign( model.$functions, {
171
167
  initArtifact,
172
168
  lateExtensions,
173
169
  projectionAncestor,
174
170
  hasTruthyProp,
171
+ } );
172
+ // During the definer, we can only resolve artifact references, i.e,
173
+ // after a `.`, we only search in the `_subArtifacts` dictionary:
174
+ model.$volatileFunctions.environment = function artifactsEnv( art ) {
175
+ return art._subArtifacts || Object.create(null);
175
176
  };
176
177
 
178
+ const extensionsDict = Object.create(null);
179
+ let addTextsLanguageAssoc = false;
180
+ return doDefine();
181
+
177
182
  /**
178
183
  * Main function of the definer.
179
184
  *
180
185
  * @returns {XSN.Model}
181
186
  */
182
- function define() {
187
+ function doDefine() {
183
188
  if (options.deprecated &&
184
189
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
185
190
  warning( 'api-deprecated-option', {},
@@ -206,10 +211,8 @@ function getDefinerFunctions( model ) {
206
211
 
207
212
  mergeI18nBlocks( model );
208
213
 
209
- if (model.options && model.options.parseCdl) {
214
+ if (options.parseCdl) {
210
215
  initExtensionsWithoutApplying();
211
- // Check for redefinitions
212
- Object.keys( model.definitions ).forEach( preProcessArtifact );
213
216
  // If no extensions shall be applied then we can skip further
214
217
  // artifact processing and return the model with an `extensions` property.
215
218
  return model;
@@ -217,8 +220,8 @@ function getDefinerFunctions( model ) {
217
220
 
218
221
  applyExtensions();
219
222
 
220
- Object.keys( model.definitions ).forEach( preProcessArtifact );
221
- const commonLanguagesEntity = isBetaEnabled( options, 'addTextsLanguageAssoc' ) &&
223
+ const commonLanguagesEntity // TODO: remove beta after a grace period
224
+ = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
222
225
  model.definitions['sap.common.Languages'];
223
226
  addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
224
227
  commonLanguagesEntity.elements.code);
@@ -239,13 +242,13 @@ function getDefinerFunctions( model ) {
239
242
  * @param {XSN.AST} src
240
243
  */
241
244
  function addSource( src ) {
242
- // handle sub model from CSN parser
245
+ // handle sub model from parser
243
246
  if (!src.kind)
244
247
  src.kind = 'source';
245
248
 
246
249
  let namespace = src.namespace && src.namespace.path;
247
250
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
248
- if (prefix.startsWith( 'cds.') && !prefix.match(/^cds\.foundation(\.|$)/)) {
251
+ if (isInReservedNamespace(prefix)) {
249
252
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
250
253
  // TODO: use $(NAME)
251
254
  'The namespace "cds" is reserved for CDS builtins' );
@@ -281,8 +284,7 @@ function getDefinerFunctions( model ) {
281
284
  function addDefinition( art, block ) {
282
285
  const { absolute } = art.name;
283
286
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
284
- if (absolute === 'cds' ||
285
- absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
287
+ if (absolute === 'cds' || isInReservedNamespace(absolute)) {
286
288
  error( 'reserved-namespace-cds', [ art.name.location, art ], {},
287
289
  // TODO: use $(NAME)
288
290
  'The namespace "cds" is reserved for CDS builtins' );
@@ -306,7 +308,7 @@ function getDefinerFunctions( model ) {
306
308
  }
307
309
  else {
308
310
  setLink( art, '_block', block );
309
- // dictAdd might set $duplicates to true if def in other source
311
+ // dictAdd might set $duplicates to true
310
312
  dictAdd( model.definitions, absolute, art );
311
313
  return true;
312
314
  }
@@ -445,6 +447,23 @@ function getDefinerFunctions( model ) {
445
447
 
446
448
  // Phase 2 ("init") --------------------------------------------------------
447
449
 
450
+ function checkRedefinition( art ) {
451
+ if (!art.$duplicates)
452
+ return;
453
+ if (art._main) {
454
+ error( 'duplicate-definition', [ art.name.location, art ], {
455
+ name: art.name.id,
456
+ '#': kindProperties[art.kind].normalized || art.kind,
457
+ } );
458
+ }
459
+ else {
460
+ error( 'duplicate-definition', [ art.name.location, art ], {
461
+ name: art.name.absolute,
462
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
463
+ } );
464
+ }
465
+ }
466
+
448
467
  function initNamespaceAndUsing( src ) {
449
468
  if (src.namespace) {
450
469
  const decl = src.namespace;
@@ -489,12 +508,13 @@ function getDefinerFunctions( model ) {
489
508
  if (!reInit)
490
509
  initParentLink( art, model.definitions );
491
510
  const block = art._block;
511
+ checkRedefinition( art );
492
512
  defineAnnotations( art, art, block );
493
513
  initMembers( art, art, block );
494
514
  initDollarSelf( art ); // $self
495
515
  if (art.params)
496
516
  initParams( art ); // $parameters
497
- if (art.includes && !(art.name.absolute in extensionsDict))
517
+ if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
498
518
  extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
499
519
 
500
520
  if (!art.query)
@@ -514,6 +534,7 @@ function getDefinerFunctions( model ) {
514
534
 
515
535
  function initVocabulary( art ) {
516
536
  initParentLink( art, model.vocabularies );
537
+ checkRedefinition( art );
517
538
  const block = art._block;
518
539
  defineAnnotations( art, art, block );
519
540
  initMembers( art, art, block );
@@ -555,20 +576,6 @@ function getDefinerFunctions( model ) {
555
576
 
556
577
  // From here til EOF, reexamine code ---------------------------------------
557
578
 
558
- // currently called from preProcessArtifact(), do be called in "init"
559
- function checkRedefinitions( obj, name, prop ) {
560
- forEachMember( obj, checkRedefinitions, obj.targetAspect );
561
- if (!obj.$duplicates)
562
- return;
563
- if (obj.name.location.file === '<built-in>') {
564
- // builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
565
- // The error shall only be printed for the user-defined conflicting artifact.
566
- return;
567
- }
568
- error( 'duplicate-definition', [ obj.name.location, obj ],
569
- { name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
570
- }
571
-
572
579
  function initDollarSelf( art ) {
573
580
  const selfname = '$self';
574
581
  // TODO: use setMemberParent() ?
@@ -609,14 +616,7 @@ function getDefinerFunctions( model ) {
609
616
  if (query.on)
610
617
  initExprForQuery( query.on, query );
611
618
  // TODO: MIXIN with name = ...subquery (not yet supported anyway)
612
- for (const elem of query.columns || []) {
613
- if (elem && (elem.value || elem.expand)) {
614
- setProp( elem, '_block', query._block );
615
- defineAnnotations( elem, elem, query._block );
616
- initExprForQuery( elem.value, query );
617
- initExpandInline( elem );
618
- }
619
- }
619
+ initSelectItems( query, query.columns );
620
620
  if (query.where)
621
621
  initExprForQuery( query.where, query );
622
622
  if (query.having)
@@ -624,22 +624,41 @@ function getDefinerFunctions( model ) {
624
624
  initMembers( query, query, query._block );
625
625
  }
626
626
 
627
- function initExpandInline( elem ) {
628
- // TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
629
- for (const sub of elem.expand || elem.inline || []) {
630
- if (!sub) // parse error
627
+ function initSelectItems( parent, columns ) {
628
+ // TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
629
+ let wildcard = null;
630
+ for (const col of columns || parent.expand || parent.inline || []) {
631
+ if (!col) // parse error
631
632
  continue;
632
- if (elem.value)
633
- setProp( sub, '_pathHead', elem ); // also set for '*' in expand/inline
634
- else if (elem._pathHead)
635
- setProp( sub, '_pathHead', elem._pathHead );
636
- if (sub.value || sub.expand) {
637
- setProp( sub, '_block', elem._block );
638
- defineAnnotations( sub, sub, elem._block ); // TODO: complain with inline
639
- initExpandInline( sub );
633
+ if (!columns) {
634
+ if (parent.value)
635
+ setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
636
+ else if (parent._pathHead)
637
+ setProp( col, '_pathHead', parent._pathHead );
638
+ }
639
+ if (col.val === '*') {
640
+ if (!wildcard) {
641
+ wildcard = col;
642
+ }
643
+ else {
644
+ // a late syntax error (this code also runs with parse-cdl), i.e.
645
+ // no semantic loc (wouldn't be available for expand/inline anyway)
646
+ error( 'syntax-duplicate-clause', [ col.location, null ],
647
+ { prop: '*', line: wildcard.location.line, col: wildcard.location.col },
648
+ 'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
649
+ // TODO: extra text variants for expand/inline? - probably not
650
+ col.val = null; // do not consider it for expandWildcard()
651
+ }
652
+ }
653
+ else if (col.value || col.expand) {
654
+ setProp( col, '_block', parent._block );
655
+ defineAnnotations( col, col, parent._block ); // TODO: complain with inline
656
+ // TODO: allow sub queries? at least in top-level expand without parallel ref
657
+ if (columns)
658
+ initExprForQuery( col.value, parent );
659
+ initSelectItems( col );
640
660
  }
641
661
  }
642
- // TODO: allow sub queries in top-level expand without parallel ref
643
662
  }
644
663
 
645
664
  function initExprForQuery( expr, query ) {
@@ -658,9 +677,38 @@ function getDefinerFunctions( model ) {
658
677
  }
659
678
  else if (expr.path && expr.$expected === 'exists') {
660
679
  expr.$expected = 'approved-exists';
680
+ approveExistsInChildren(expr);
661
681
  }
662
682
  }
663
683
 
684
+ /**
685
+ * If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
686
+ * since we will have a top-level subquery after exists-processing in the forHanaNew.
687
+ *
688
+ * Recursively drill down into:
689
+ * - the .path
690
+ * - the .args
691
+ * - the .where.args
692
+ *
693
+ * Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
694
+ *
695
+ * working: exists toE[exists toE] -> select from E where exists toE
696
+ * not working: toE[exists toE] -> we don't support subqueries in filters
697
+ *
698
+ * @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
699
+ */
700
+ function approveExistsInChildren(exprOrPathElement) {
701
+ if (exprOrPathElement.$expected === 'exists')
702
+ exprOrPathElement.$expected = 'approved-exists';
703
+ // Drill down
704
+ if (exprOrPathElement.args)
705
+ exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
706
+ else if (exprOrPathElement.where && exprOrPathElement.where.args)
707
+ exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
708
+ else if (exprOrPathElement.path)
709
+ exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
710
+ }
711
+
664
712
  // table is table expression in FROM, becomes an alias
665
713
  function initTableExpression( table, query, joinParents ) {
666
714
  if (!table) // parse error
@@ -708,6 +756,8 @@ function getDefinerFunctions( model ) {
708
756
  // ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
709
757
  // : ta._parent.from ) // directly in FROM
710
758
  // Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
759
+ if (!tab) // parse error; time for #6241
760
+ return; // (parser method to only add non-null to array)
711
761
  setProp( tab, '_joinParent', table );
712
762
  tab.$joinArgsIndex = index;
713
763
  initTableExpression( tab, query, joinParents );
@@ -732,12 +782,13 @@ function getDefinerFunctions( model ) {
732
782
  setMemberParent( table, table.name.id, query );
733
783
  setProp( table, '_block', query._block );
734
784
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
735
- error( 'duplicate-definition', [ loc, table ], { name, '#': '$tableAlias' } );
785
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
736
786
  } );
737
787
  // also add to JOIN nodes for name restrictions:
738
788
  for (const p of joinParents) {
739
- // console.log('ADD:', query.name.id, parents.length, p)
740
- dictAdd( p.$tableAliases, table.name.id, table );
789
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
790
+ // already used for duplicate aliases of queries:
791
+ dictAddArray( p.$tableAliases, table.name.id, table );
741
792
  }
742
793
  if (table.name.id[0] === '$') {
743
794
  warning( 'syntax-dollar-ident', [ table.name.location, table ], {
@@ -824,7 +875,7 @@ function getDefinerFunctions( model ) {
824
875
  // assignments on the mixin... (also for future mixin definitions
825
876
  // with generated values)
826
877
  dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
827
- error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '$tableAlias' } );
878
+ error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
828
879
  } );
829
880
  if (mixin.name.id[0] === '$') {
830
881
  warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
@@ -847,7 +898,7 @@ function getDefinerFunctions( model ) {
847
898
  // TODO: error if CSN has both target.elements and targetAspect.elements -> delete target
848
899
  return true;
849
900
  }
850
- if (elem.targetAspect || !isDirectComposition( elem ))
901
+ if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
851
902
  return false;
852
903
  const name = resolveUncheckedPath( target, 'compositionTarget', elem );
853
904
  const aspect = name && model.definitions[name];
@@ -861,6 +912,7 @@ function getDefinerFunctions( model ) {
861
912
  * (which is basically the component name of the `parent` element plus a dot).
862
913
  */
863
914
  function initMembers( construct, parent, block, initExtensions = false ) {
915
+ // TODO: split extend from init
864
916
  const isQueryExtension = kindProperties[construct.kind].isExtension &&
865
917
  (parent._main || parent).query;
866
918
  let obj = construct;
@@ -916,7 +968,7 @@ function getDefinerFunctions( model ) {
916
968
  forEachInOrder( construct, 'params', init );
917
969
  const { returns } = construct;
918
970
  if (returns) {
919
- returns.kind = 'param';
971
+ returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
920
972
  init( returns, '' ); // '' is special name for returns parameter
921
973
  }
922
974
  return;
@@ -950,6 +1002,7 @@ function getDefinerFunctions( model ) {
950
1002
  setProp( elem, '_block', bl );
951
1003
  setMemberParent( elem, name, parent, construct !== parent && prop );
952
1004
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1005
+ checkRedefinition( elem );
953
1006
  defineAnnotations( elem, elem, bl );
954
1007
  initMembers( elem, elem, bl, initExtensions );
955
1008
 
@@ -985,6 +1038,7 @@ function getDefinerFunctions( model ) {
985
1038
  }
986
1039
 
987
1040
  function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
1041
+ // TODO: do differently, see also annotateMembers() in resolver
988
1042
  // To have been checked by parsers:
989
1043
  // - artifacts (CDL-only anyway) only inside [extend] context|service
990
1044
  if (!dict)
@@ -1269,7 +1323,7 @@ function getDefinerFunctions( model ) {
1269
1323
  }
1270
1324
 
1271
1325
  function createTargetEntity( target, elem, keys, entityName, base ) {
1272
- const location = elem.target && elem.target.location || elem.location;
1326
+ const { location } = elem.targetAspect || elem.target || elem;
1273
1327
  elem.on = {
1274
1328
  location,
1275
1329
  op: { val: '=', location },
@@ -1314,7 +1368,7 @@ function getDefinerFunctions( model ) {
1314
1368
  // If 'up_' shall be rendered unmanaged, infer the parent
1315
1369
  // primary keys and add the ON condition
1316
1370
  if (isDeprecatedEnabled( options, 'unmanagedUpInComponent' )) {
1317
- addProxyElements( art, keys, 'aspect-composition', location,
1371
+ addProxyElements( art, keys, 'aspect-composition', target.name && location,
1318
1372
  'up__', '@odata.containment.ignore' );
1319
1373
  up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
1320
1374
  }
@@ -1328,7 +1382,7 @@ function getDefinerFunctions( model ) {
1328
1382
  setLink( art, '_base', base._base || base );
1329
1383
 
1330
1384
  dictAdd( art.elements, 'up_', up);
1331
- addProxyElements( art, target.elements, 'aspect-composition', location );
1385
+ addProxyElements( art, target.elements, 'aspect-composition', target.name && location );
1332
1386
 
1333
1387
  setLink( art, '_block', model.$internal );
1334
1388
  model.definitions[entityName] = art;
@@ -1375,7 +1429,7 @@ function getDefinerFunctions( model ) {
1375
1429
  const exts = model.$lateExtensions[name];
1376
1430
  if (art && art.kind !== 'namespace') {
1377
1431
  if (art.builtin) {
1378
- for (const ext in exts)
1432
+ for (const ext of exts)
1379
1433
  info( 'anno-builtin', [ ext.name.location, ext ] );
1380
1434
  }
1381
1435
  // created texts entity, autoexposed entity
@@ -1423,6 +1477,12 @@ function getDefinerFunctions( model ) {
1423
1477
 
1424
1478
  model.extensions.push(annotationArtifact);
1425
1479
  extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
1480
+ // if one of the annotate statement mentions 'returns', assume it
1481
+ // TODO: with warning/info?
1482
+ for (const ext of exts) {
1483
+ if (ext.$syntax === 'returns')
1484
+ annotationArtifact.$syntax = 'returns';
1485
+ }
1426
1486
  }
1427
1487
  }
1428
1488
  }
@@ -1706,6 +1766,7 @@ function getDefinerFunctions( model ) {
1706
1766
  }
1707
1767
 
1708
1768
  function extendMembers( extensions, art, noExtend ) {
1769
+ // TODO: do the whole extension stuff lazily if the elements are requested
1709
1770
  const elemExtensions = [];
1710
1771
  extensions.sort( compareLayer );
1711
1772
  for (const ext of extensions) {
@@ -1751,7 +1812,12 @@ function getDefinerFunctions( model ) {
1751
1812
  [ 'elements', 'actions' ].forEach( (prop) => {
1752
1813
  const dict = art._extend && art._extend[prop];
1753
1814
  for (const name in dict) {
1754
- const validDict = art[prop] || prop === 'elements' && art.enum;
1815
+ let obj = art;
1816
+ if (obj.targetAspect)
1817
+ obj = obj.targetAspect;
1818
+ while (obj.items)
1819
+ obj = obj.items;
1820
+ const validDict = obj[prop] || prop === 'elements' && obj.enum;
1755
1821
  const member = validDict[name];
1756
1822
  if (!member)
1757
1823
  extendNothing( dict[name], prop, name, art, validDict );
@@ -1902,24 +1968,6 @@ function getDefinerFunctions( model ) {
1902
1968
  }
1903
1969
  }
1904
1970
 
1905
- // TODO: move to "init" phase
1906
- /**
1907
- * Check whether redefinitions of the given artifact name exist and
1908
- * adapt to `targetAspect`.
1909
- *
1910
- * @param {string} name
1911
- */
1912
- function preProcessArtifact( name ) {
1913
- const art = model.definitions[name];
1914
- if (Array.isArray(art.$duplicates)) {
1915
- // A definition name containing a `.` is not invalid (TODO: starting or
1916
- // ending with a dot is invalid and could be checked here)
1917
- for (const a of art.$duplicates)
1918
- checkRedefinitions( a, name, 'definitions' );
1919
- }
1920
- checkRedefinitions( art, name, 'definitions' );
1921
- }
1922
-
1923
1971
  /**
1924
1972
  * Process "composition of" artifacts.
1925
1973
  *
@@ -1989,8 +2037,9 @@ function getDefinerFunctions( model ) {
1989
2037
  }
1990
2038
 
1991
2039
  if (isKey && isLocalized) { // key with localized is wrong - ignore localized
1992
- warning( 'localized-key', [ elem.localized.location, elem ], {},
1993
- 'Keyword "localized" is ignored for primary keys' );
2040
+ const errpos = elem.localized || elem.type || elem.name;
2041
+ warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
2042
+ 'Keyword $(KEYWORD) is ignored for primary keys' );
1994
2043
  }
1995
2044
  }
1996
2045
  if (textElems.length <= keys)
@@ -2132,9 +2181,9 @@ function getDefinerFunctions( model ) {
2132
2181
  });
2133
2182
  }
2134
2183
  }
2135
- else { // use location of LOCALIZED keyword
2184
+ if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
2136
2185
  const localized = orig.localized || orig.type || orig.name;
2137
- elem.localized = { val: false, $inferred: 'localized', location: localized.location };
2186
+ elem.localized = { val: null, $inferred: 'localized', location: localized.location };
2138
2187
  }
2139
2188
  }
2140
2189
  if (fioriEnabled)
@@ -2250,9 +2299,9 @@ function mergeI18nBlocks( model ) {
2250
2299
  model.i18n[langKey][textKey] = sourceVal;
2251
2300
  }
2252
2301
  else if (modelVal.val !== sourceVal.val) {
2253
- const { warning } = makeMessageFunction( model );
2254
- warning('i18n-different-value', [ sourceVal.location, null ],
2255
- { prop: textKey, otherprop: langKey });
2302
+ // TODO: put mergeI18nBlocks() into main function instead
2303
+ model.$messageFunctions.warning( 'i18n-different-value', sourceVal.location,
2304
+ { prop: textKey, otherprop: langKey } );
2256
2305
  }
2257
2306
  }
2258
2307
  }
@@ -2269,26 +2318,6 @@ function pathName(path) {
2269
2318
  return path.map( id => id.id ).join('.');
2270
2319
  }
2271
2320
 
2272
- /**
2273
- * Generates an XSN path out of the given name. Path segments are delimited by a dot.
2274
- * Each segment will have the given location assigned.
2275
- *
2276
- * @param {CSN.Location} location
2277
- * @param {string} name
2278
- * @returns {XSN.Path}
2279
- */
2280
- function splitIntoPath( location, name ) {
2281
- return name.split('.').map( id => ({ id, location }) );
2282
- }
2283
-
2284
- /**
2285
- * @param {CSN.Location} location
2286
- * @param {...any} args
2287
- */
2288
- function augmentPath( location, ...args ) {
2289
- return { path: args.map( id => ({ id, location }) ), location };
2290
- }
2291
-
2292
2321
  function augmentEqual( location, assocname, relations, prefix = '' ) {
2293
2322
  const args = relations.map( eq );
2294
2323
  return (args.length === 1)
@@ -2317,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
2317
2346
  // these function could be used to a future lib/compiler/utils.js, but DO NOT
2318
2347
  // SHARE with utility functions for CSN processors
2319
2348
 
2320
- module.exports = {
2321
- define: model => getDefinerFunctions( model ).define(),
2322
- getDefinerFunctions,
2323
- augmentPath,
2324
- splitIntoPath,
2325
- };
2349
+ module.exports = { define };