@sap/cds-compiler 2.10.2 → 2.11.4

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 (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
@@ -98,6 +98,8 @@ function assertConsistency( model, stage ) {
98
98
  '$blocks',
99
99
  '$newfeatures',
100
100
  '$messageFunctions',
101
+ '$functions',
102
+ '$volatileFunctions',
101
103
  ],
102
104
  },
103
105
  ':parser': { // top-level from parser
@@ -247,7 +249,7 @@ function assertConsistency( model, stage ) {
247
249
  'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
248
250
  'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
249
251
  '_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
250
- '$tableAliases', 'kind', '_$next', '_combined', '$inlines',
252
+ '$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
251
253
  ],
252
254
  },
253
255
  none: { optional: () => true }, // parse error
@@ -479,6 +481,7 @@ function assertConsistency( model, stage ) {
479
481
  },
480
482
  items: {
481
483
  kind: true,
484
+ also: [ 0 ], // 0 for cyclic expansions
482
485
  requires: [ 'location' ],
483
486
  optional: [
484
487
  'enum',
@@ -539,7 +542,7 @@ function assertConsistency( model, stage ) {
539
542
  // query specific
540
543
  'where', 'columns', 'mixin', 'quantifier', 'offset',
541
544
  'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
542
- 'limit',
545
+ 'limit', '_status',
543
546
  ],
544
547
  },
545
548
  _leadingQuery: { kind: true, test: TODO },
@@ -588,6 +591,8 @@ function assertConsistency( model, stage ) {
588
591
  $sources: { parser: true, test: isArray( isString ) },
589
592
  $expected: { parser: true, test: isString },
590
593
  $messageFunctions: { test: TODO },
594
+ $functions: { test: TODO },
595
+ $volatileFunctions: { test: TODO },
591
596
  };
592
597
  let _noSyntaxErrors = null;
593
598
  assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
@@ -659,6 +664,8 @@ function assertConsistency( model, stage ) {
659
664
  }
660
665
 
661
666
  function standard( node, parent, prop, spec, name ) {
667
+ if (spec.also && spec.also.includes( node ))
668
+ return;
662
669
  isObject( node, parent, prop, spec, name );
663
670
 
664
671
  const names = Object.getOwnPropertyNames( node );
@@ -0,0 +1,65 @@
1
+ // Base Definitions for the Core Compiler
2
+
3
+
4
+ 'use strict';
5
+
6
+ const dictKinds = {
7
+ definitions: 'absolute',
8
+ elements: 'element',
9
+ enum: 'enum',
10
+ foreignKeys: 'key',
11
+ actions: 'action',
12
+ params: 'param',
13
+ };
14
+
15
+ const kindProperties = {
16
+ // TODO: also foreignKeys ?
17
+ namespace: { artifacts: true }, // on-the-fly context
18
+ context: { artifacts: true, normalized: 'namespace' },
19
+ service: { artifacts: true, normalized: 'namespace' },
20
+ entity: { elements: true, actions: true, params: () => false },
21
+ select: { normalized: 'select', elements: true },
22
+ $join: { normalized: 'select' },
23
+ $tableAlias: { normalized: 'alias' }, // table alias in select
24
+ $self: { normalized: 'alias' }, // table alias in select
25
+ $navElement: { normalized: 'element' },
26
+ $inline: { normalized: 'element' }, // column with inline property
27
+ event: { elements: true },
28
+ type: { elements: propExists, enum: propExists },
29
+ aspect: { elements: propExists },
30
+ annotation: { elements: propExists, enum: propExists },
31
+ enum: { normalized: 'element' },
32
+ element: { elements: propExists, enum: propExists, dict: 'elements' },
33
+ mixin: { normalized: 'alias' },
34
+ action: {
35
+ params: () => false, elements: () => false, enum: () => false, dict: 'actions',
36
+ }, // no extend params, only annotate
37
+ function: {
38
+ params: () => false, elements: () => false, enum: () => false, normalized: 'action',
39
+ }, // no extend params, only annotate
40
+ key: { normalized: 'element' },
41
+ param: { elements: () => false, enum: () => false, dict: 'params' },
42
+ source: { artifacts: true }, // TODO -> $source
43
+ using: {},
44
+ extend: {
45
+ isExtension: true,
46
+ noDep: 'special',
47
+ elements: true, /* only for parse-cdl */
48
+ actions: true, /* only for parse-cdl */
49
+ },
50
+ annotate: {
51
+ isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
52
+ },
53
+ builtin: {}, // = CURRENT_DATE, TODO: improve
54
+ $parameters: {}, // $parameters in query entities
55
+ };
56
+
57
+ function propExists( prop, parent ) {
58
+ const obj = parent.returns || parent;
59
+ return (obj.items || obj.targetAspect || obj)[prop];
60
+ }
61
+
62
+ module.exports = {
63
+ dictKinds,
64
+ kindProperties,
65
+ };
@@ -2,9 +2,8 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const { forEachInDict } = require('../base/dictionaries');
6
5
  const { builtinLocation } = require('../base/location');
7
- const { setProp } = require('../base/model');
6
+ const { setProp } = require('./utils');
8
7
 
9
8
  const core = {
10
9
  String: { parameters: [ 'length' ], category: 'string' },
@@ -82,18 +81,19 @@ const specialFunctions = {
82
81
  */
83
82
  const magicVariables = {
84
83
  $user: {
84
+ // id and locale are always available
85
85
  elements: { id: {}, locale: {} },
86
86
  // Allow $user.<any>
87
87
  $uncheckedElements: true,
88
88
  // Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
89
89
  $autoElement: 'id',
90
- }, // CDS-specific, not part of SQL
91
- $at: {
90
+ },
91
+ $at: { // CDS-specific, not part of SQL
92
92
  elements: {
93
93
  from: {}, to: {},
94
94
  },
95
95
  },
96
- $now: {}, // Dito
96
+ $now: {}, // Dito
97
97
  $session: {
98
98
  // In ABAP CDS session variables are accessed in a generic way via
99
99
  // the pseudo variable $session.
@@ -167,6 +167,31 @@ function isRelationTypeName(typeName) {
167
167
  return typeCategories.relation.includes(typeName);
168
168
  }
169
169
 
170
+ /**
171
+ * Checks whether the given absolute path is inside a reserved namespace.
172
+ *
173
+ * @param {string} absolute
174
+ * @returns {boolean}
175
+ */
176
+ function isInReservedNamespace(absolute) {
177
+ return absolute.startsWith( 'cds.') &&
178
+ !absolute.match(/^cds\.foundation(\.|$)/) &&
179
+ !absolute.match(/^cds\.outbox(\.|$)/); // Requested by Node runtime
180
+ }
181
+
182
+ /**
183
+ * Tell if a type is (directly) a builtin type
184
+ * Note that in CSN builtins are not in the definition of the model, so we can only
185
+ * check against their absolute names. Builtin types are "cds.<something>", i.e. they
186
+ * are directly in 'cds', but not for example in 'cds.foundation'.
187
+ *
188
+ * @param {string} type
189
+ * @returns {boolean}
190
+ */
191
+ function isBuiltinType(type) {
192
+ return typeof type === 'string' && isInReservedNamespace(type);
193
+ }
194
+
170
195
  /**
171
196
  * Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
172
197
  * `definitions` of the XSN model as well as to `$builtins`.
@@ -174,6 +199,7 @@ function isRelationTypeName(typeName) {
174
199
  * @param {XSN.Model} model XSN model without CDS builtins
175
200
  */
176
201
  function initBuiltins( model ) {
202
+ const { options } = model;
177
203
  setMagicVariables( magicVariables );
178
204
  // namespace:"cds" stores the builtins ---
179
205
  const cds = createNamespace( 'cds', 'reserved' );
@@ -241,27 +267,45 @@ function initBuiltins( model ) {
241
267
  for (const name in builtins) {
242
268
  const magic = builtins[name];
243
269
  // TODO: rename to $builtinFunction
244
- const art = { kind: 'builtin', name: { id: name, element: name } };
270
+ const art = { kind: 'builtin', name: { element: name, id: name } };
245
271
  artifacts[name] = art;
246
- if (magic.elements)
247
- art.elements = forEachInDict( magic.elements, (e, n) => magicElement( e, n, art ));
272
+
248
273
  if (magic.$autoElement)
249
274
  art.$autoElement = magic.$autoElement;
250
275
  if (magic.$uncheckedElements)
251
276
  art.$uncheckedElements = magic.$uncheckedElements;
277
+
278
+ createMagicElements( art, magic.elements );
279
+ if (options.variableReplacements)
280
+ createMagicElements( art, options.variableReplacements[name] );
252
281
  // setProp( art, '_effectiveType', art );
253
282
  }
254
283
  model.$magicVariables = { kind: '$magicVariables', artifacts };
255
284
  }
256
285
 
257
- function magicElement( spec, name, parent ) {
258
- const magic = {
259
- kind: 'builtin',
260
- name: { id: name, element: `${ parent.name.element }.${ name }` },
261
- };
262
- setProp( magic, '_parent', parent );
263
- // setProp( magic, '_effectiveType', magic );
264
- return magic;
286
+ function createMagicElements( art, elements ) {
287
+ if (!elements)
288
+ return;
289
+
290
+ const names = Object.keys(elements);
291
+ if (names.length > 0 && !art.elements)
292
+ art.elements = Object.create(null);
293
+
294
+ for (const n of names) {
295
+ const magic = {
296
+ kind: 'builtin',
297
+ name: { id: n, element: `${ art.name.element }.${ n }` },
298
+ };
299
+ // Propagate this property so that it is available for sub-elements.
300
+ if (art.$uncheckedElements)
301
+ magic.$uncheckedElements = art.$uncheckedElements;
302
+ setProp( magic, '_parent', art );
303
+ // setProp( magic, '_effectiveType', magic );
304
+ if (elements[n] && typeof elements[n] === 'object')
305
+ createMagicElements(magic, elements[n]);
306
+
307
+ art.elements[n] = magic;
308
+ }
265
309
  }
266
310
  }
267
311
 
@@ -269,6 +313,8 @@ module.exports = {
269
313
  functionsWithoutParens,
270
314
  specialFunctions,
271
315
  initBuiltins,
316
+ isInReservedNamespace,
317
+ isBuiltinType,
272
318
  isIntegerTypeName,
273
319
  isDecimalTypeName,
274
320
  isNumericTypeName,
@@ -632,8 +632,9 @@ function check( model ) { // = XSN
632
632
  * @returns {void}
633
633
  */
634
634
  function checkTokenStreamExpression(xpr, allowAssocTail) {
635
+ const args = Array.isArray(xpr.args) ? xpr.args : Object.values(xpr.args || {});
635
636
  // Check for illegal argument usage within the expression
636
- for (const arg of xpr.args || []) {
637
+ for (const arg of args) {
637
638
  if (isVirtualElement(arg))
638
639
  error(null, arg.location, 'Virtual elements can\'t be used in an expression');
639
640
 
@@ -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,9 +101,11 @@
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
 
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
111
113
  const {
112
114
  isDeprecatedEnabled, isBetaEnabled,
113
115
  setProp, forEachGeneric, forEachInOrder,
114
- forEachMember, forEachDefinition,
116
+ forEachDefinition,
115
117
  forEachMemberRecursivelyWithQuery,
116
118
  } = require('../base/model');
117
119
  const {
@@ -120,20 +122,21 @@ const {
120
122
  const {
121
123
  dictLocation,
122
124
  } = require('../base/location');
125
+ const { kindProperties, dictKinds } = require('./base');
123
126
  const {
124
- annotationVal, annotationIsFalse, annotateWith,
125
- } = require('./utils');
126
- const {
127
- dictKinds,
128
- kindProperties,
129
- fns,
127
+ annotationVal,
128
+ annotationIsFalse,
129
+ annotateWith,
130
130
  linkToOrigin,
131
131
  setMemberParent,
132
132
  storeExtension,
133
133
  dependsOnSilent,
134
- } = require('./shared');
134
+ pathName,
135
+ augmentPath,
136
+ splitIntoPath,
137
+ } = require('./utils');
135
138
  const { compareLayer, layer } = require('./moduleLayers');
136
- const { initBuiltins } = require('./builtins');
139
+ const { initBuiltins, isInReservedNamespace } = require('./builtins');
137
140
  const setLink = setProp;
138
141
 
139
142
  /**
@@ -148,7 +151,7 @@ const setLink = setProp;
148
151
  *
149
152
  * @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
150
153
  */
151
- function getDefinerFunctions( model ) {
154
+ function define( model ) {
152
155
  const { options } = model;
153
156
  // Get simplified "resolve" functionality and the message function:
154
157
  const {
@@ -160,24 +163,29 @@ function getDefinerFunctions( model ) {
160
163
  resolveTypeArguments,
161
164
  defineAnnotations,
162
165
  attachAndEmitValidNames,
163
- } = fns( model );
164
- const extensionsDict = Object.create(null);
165
- let addTextsLanguageAssoc = false;
166
-
167
- return {
168
- define,
166
+ } = model.$functions;
167
+ Object.assign( model.$functions, {
169
168
  initArtifact,
170
169
  lateExtensions,
171
170
  projectionAncestor,
172
171
  hasTruthyProp,
172
+ } );
173
+ // During the definer, we can only resolve artifact references, i.e,
174
+ // after a `.`, we only search in the `_subArtifacts` dictionary:
175
+ model.$volatileFunctions.environment = function artifactsEnv( art ) {
176
+ return art._subArtifacts || Object.create(null);
173
177
  };
174
178
 
179
+ const extensionsDict = Object.create(null);
180
+ let addTextsLanguageAssoc = false;
181
+ return doDefine();
182
+
175
183
  /**
176
184
  * Main function of the definer.
177
185
  *
178
186
  * @returns {XSN.Model}
179
187
  */
180
- function define() {
188
+ function doDefine() {
181
189
  if (options.deprecated &&
182
190
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
183
191
  warning( 'api-deprecated-option', {},
@@ -206,8 +214,6 @@ function getDefinerFunctions( model ) {
206
214
 
207
215
  if (options.parseCdl) {
208
216
  initExtensionsWithoutApplying();
209
- // Check for redefinitions
210
- Object.keys( model.definitions ).forEach( preProcessArtifact );
211
217
  // If no extensions shall be applied then we can skip further
212
218
  // artifact processing and return the model with an `extensions` property.
213
219
  return model;
@@ -215,7 +221,6 @@ function getDefinerFunctions( model ) {
215
221
 
216
222
  applyExtensions();
217
223
 
218
- Object.keys( model.definitions ).forEach( preProcessArtifact );
219
224
  const commonLanguagesEntity // TODO: remove beta after a grace period
220
225
  = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
221
226
  model.definitions['sap.common.Languages'];
@@ -238,13 +243,13 @@ function getDefinerFunctions( model ) {
238
243
  * @param {XSN.AST} src
239
244
  */
240
245
  function addSource( src ) {
241
- // handle sub model from CSN parser
246
+ // handle sub model from parser
242
247
  if (!src.kind)
243
248
  src.kind = 'source';
244
249
 
245
250
  let namespace = src.namespace && src.namespace.path;
246
251
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
247
- if (prefix.startsWith( 'cds.') && !prefix.match(/^cds\.foundation(\.|$)/)) {
252
+ if (isInReservedNamespace(prefix)) {
248
253
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
249
254
  // TODO: use $(NAME)
250
255
  'The namespace "cds" is reserved for CDS builtins' );
@@ -280,8 +285,7 @@ function getDefinerFunctions( model ) {
280
285
  function addDefinition( art, block ) {
281
286
  const { absolute } = art.name;
282
287
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
283
- if (absolute === 'cds' ||
284
- absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
288
+ if (absolute === 'cds' || isInReservedNamespace(absolute)) {
285
289
  error( 'reserved-namespace-cds', [ art.name.location, art ], {},
286
290
  // TODO: use $(NAME)
287
291
  'The namespace "cds" is reserved for CDS builtins' );
@@ -305,7 +309,7 @@ function getDefinerFunctions( model ) {
305
309
  }
306
310
  else {
307
311
  setLink( art, '_block', block );
308
- // dictAdd might set $duplicates to true if def in other source
312
+ // dictAdd might set $duplicates to true
309
313
  dictAdd( model.definitions, absolute, art );
310
314
  return true;
311
315
  }
@@ -444,6 +448,23 @@ function getDefinerFunctions( model ) {
444
448
 
445
449
  // Phase 2 ("init") --------------------------------------------------------
446
450
 
451
+ function checkRedefinition( art ) {
452
+ if (!art.$duplicates)
453
+ return;
454
+ if (art._main) {
455
+ error( 'duplicate-definition', [ art.name.location, art ], {
456
+ name: art.name.id,
457
+ '#': kindProperties[art.kind].normalized || art.kind,
458
+ } );
459
+ }
460
+ else {
461
+ error( 'duplicate-definition', [ art.name.location, art ], {
462
+ name: art.name.absolute,
463
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
464
+ } );
465
+ }
466
+ }
467
+
447
468
  function initNamespaceAndUsing( src ) {
448
469
  if (src.namespace) {
449
470
  const decl = src.namespace;
@@ -488,6 +509,7 @@ function getDefinerFunctions( model ) {
488
509
  if (!reInit)
489
510
  initParentLink( art, model.definitions );
490
511
  const block = art._block;
512
+ checkRedefinition( art );
491
513
  defineAnnotations( art, art, block );
492
514
  initMembers( art, art, block );
493
515
  initDollarSelf( art ); // $self
@@ -513,6 +535,7 @@ function getDefinerFunctions( model ) {
513
535
 
514
536
  function initVocabulary( art ) {
515
537
  initParentLink( art, model.vocabularies );
538
+ checkRedefinition( art );
516
539
  const block = art._block;
517
540
  defineAnnotations( art, art, block );
518
541
  initMembers( art, art, block );
@@ -554,20 +577,6 @@ function getDefinerFunctions( model ) {
554
577
 
555
578
  // From here til EOF, reexamine code ---------------------------------------
556
579
 
557
- // currently called from preProcessArtifact(), do be called in "init"
558
- function checkRedefinitions( obj, name, prop ) {
559
- forEachMember( obj, checkRedefinitions, obj.targetAspect );
560
- if (!obj.$duplicates)
561
- return;
562
- if (obj.name.location.file === '<built-in>') {
563
- // builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
564
- // The error shall only be printed for the user-defined conflicting artifact.
565
- return;
566
- }
567
- error( 'duplicate-definition', [ obj.name.location, obj ],
568
- { name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
569
- }
570
-
571
580
  function initDollarSelf( art ) {
572
581
  const selfname = '$self';
573
582
  // TODO: use setMemberParent() ?
@@ -774,12 +783,13 @@ function getDefinerFunctions( model ) {
774
783
  setMemberParent( table, table.name.id, query );
775
784
  setProp( table, '_block', query._block );
776
785
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
777
- error( 'duplicate-definition', [ loc, table ], { name, '#': '$tableAlias' } );
786
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
778
787
  } );
779
788
  // also add to JOIN nodes for name restrictions:
780
789
  for (const p of joinParents) {
781
- // console.log('ADD:', query.name.id, parents.length, p)
782
- dictAdd( p.$tableAliases, table.name.id, table );
790
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
791
+ // already used for duplicate aliases of queries:
792
+ dictAddArray( p.$tableAliases, table.name.id, table );
783
793
  }
784
794
  if (table.name.id[0] === '$') {
785
795
  warning( 'syntax-dollar-ident', [ table.name.location, table ], {
@@ -793,8 +803,8 @@ function getDefinerFunctions( model ) {
793
803
 
794
804
  // art is:
795
805
  // - entity for top-level queries (including UNION args)
796
- // - $tableAlias for sub query in FROM
797
- // - $query for real sub query (in columns, WHERE, ...)
806
+ // - $tableAlias for sub query in FROM - TODO: what about UNION there?
807
+ // - $query for real sub query (in columns, WHERE, ...), again: what about UNION there?
798
808
  function initQueryExpression( query, art ) {
799
809
  if (!query) // parse error
800
810
  return query;
@@ -846,7 +856,7 @@ function getDefinerFunctions( model ) {
846
856
  query.kind = 'select';
847
857
  query.name = { location: query.location };
848
858
  setMemberParent( query, main.$queries.length + 1, main );
849
- // console.log(JSON.stringify(query.name))
859
+ // console.log(art.kind,art.name,query.name,query._$next.name)
850
860
  // if (query.name.query === 1 && query.name.absolute === 'S') throw Error();
851
861
  main.$queries.push( query );
852
862
  setProp( query, '_parent', art ); // _parent should point to alias/main/query
@@ -866,7 +876,7 @@ function getDefinerFunctions( model ) {
866
876
  // assignments on the mixin... (also for future mixin definitions
867
877
  // with generated values)
868
878
  dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
869
- error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '$tableAlias' } );
879
+ error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
870
880
  } );
871
881
  if (mixin.name.id[0] === '$') {
872
882
  warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
@@ -993,6 +1003,7 @@ function getDefinerFunctions( model ) {
993
1003
  setProp( elem, '_block', bl );
994
1004
  setMemberParent( elem, name, parent, construct !== parent && prop );
995
1005
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1006
+ checkRedefinition( elem );
996
1007
  defineAnnotations( elem, elem, bl );
997
1008
  initMembers( elem, elem, bl, initExtensions );
998
1009
 
@@ -1330,7 +1341,7 @@ function getDefinerFunctions( model ) {
1330
1341
  name: { path: splitIntoPath( location, entityName ), absolute: entityName, location },
1331
1342
  location,
1332
1343
  elements,
1333
- $inferred: 'aspect-composition',
1344
+ $inferred: 'composition-entity',
1334
1345
  };
1335
1346
  if (target.name) { // named target aspect
1336
1347
  setLink( art, '_origin', target );
@@ -1958,24 +1969,6 @@ function getDefinerFunctions( model ) {
1958
1969
  }
1959
1970
  }
1960
1971
 
1961
- // TODO: move to "init" phase
1962
- /**
1963
- * Check whether redefinitions of the given artifact name exist and
1964
- * adapt to `targetAspect`.
1965
- *
1966
- * @param {string} name
1967
- */
1968
- function preProcessArtifact( name ) {
1969
- const art = model.definitions[name];
1970
- if (Array.isArray(art.$duplicates)) {
1971
- // A definition name containing a `.` is not invalid (TODO: starting or
1972
- // ending with a dot is invalid and could be checked here)
1973
- for (const a of art.$duplicates)
1974
- checkRedefinitions( a, name, 'definitions' );
1975
- }
1976
- checkRedefinitions( art, name, 'definitions' );
1977
- }
1978
-
1979
1972
  /**
1980
1973
  * Process "composition of" artifacts.
1981
1974
  *
@@ -2111,7 +2104,7 @@ function getDefinerFunctions( model ) {
2111
2104
  name: { path: splitIntoPath( location, absolute ), absolute, location },
2112
2105
  location: base.location,
2113
2106
  elements,
2114
- $inferred: 'localized',
2107
+ $inferred: 'localized-entity',
2115
2108
  };
2116
2109
  const locale = {
2117
2110
  name: { location, id: 'locale' },
@@ -2212,7 +2205,7 @@ function getDefinerFunctions( model ) {
2212
2205
  name: { location, id: 'texts' },
2213
2206
  kind: 'element',
2214
2207
  location,
2215
- $inferred: 'localized-texts',
2208
+ $inferred: 'localized',
2216
2209
  type: augmentPath( location, 'cds.Composition' ),
2217
2210
  cardinality: { targetMax: { literal: 'string', val: '*', location }, location },
2218
2211
  target: augmentPath( location, textsName ),
@@ -2227,7 +2220,7 @@ function getDefinerFunctions( model ) {
2227
2220
  name: { location, id: 'localized' },
2228
2221
  kind: 'element',
2229
2222
  location,
2230
- $inferred: 'localized-texts',
2223
+ $inferred: 'localized',
2231
2224
  type: augmentPath( location, 'cds.Association' ),
2232
2225
  target: augmentPath( location, textsName ),
2233
2226
  on: augmentEqual( location, 'localized', keys ),
@@ -2316,36 +2309,6 @@ function mergeI18nBlocks( model ) {
2316
2309
  }
2317
2310
  }
2318
2311
 
2319
- /**
2320
- * Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
2321
- * locations).
2322
- *
2323
- * @param {XSN.Path} path
2324
- */
2325
- function pathName(path) {
2326
- return path.map( id => id.id ).join('.');
2327
- }
2328
-
2329
- /**
2330
- * Generates an XSN path out of the given name. Path segments are delimited by a dot.
2331
- * Each segment will have the given location assigned.
2332
- *
2333
- * @param {CSN.Location} location
2334
- * @param {string} name
2335
- * @returns {XSN.Path}
2336
- */
2337
- function splitIntoPath( location, name ) {
2338
- return name.split('.').map( id => ({ id, location }) );
2339
- }
2340
-
2341
- /**
2342
- * @param {CSN.Location} location
2343
- * @param {...any} args
2344
- */
2345
- function augmentPath( location, ...args ) {
2346
- return { path: args.map( id => ({ id, location }) ), location };
2347
- }
2348
-
2349
2312
  function augmentEqual( location, assocname, relations, prefix = '' ) {
2350
2313
  const args = relations.map( eq );
2351
2314
  return (args.length === 1)
@@ -2374,9 +2337,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
2374
2337
  // these function could be used to a future lib/compiler/utils.js, but DO NOT
2375
2338
  // SHARE with utility functions for CSN processors
2376
2339
 
2377
- module.exports = {
2378
- define: model => getDefinerFunctions( model ).define(),
2379
- getDefinerFunctions,
2380
- augmentPath,
2381
- splitIntoPath,
2382
- };
2340
+ module.exports = { define };