@sap/cds-compiler 6.4.2 → 6.5.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 (71) hide show
  1. package/CHANGELOG.md +87 -1159
  2. package/README.md +1 -10
  3. package/doc/IncompatibleChanges_v5.md +436 -0
  4. package/doc/IncompatibleChanges_v6.md +659 -0
  5. package/doc/Versioning.md +3 -7
  6. package/lib/api/main.js +1 -0
  7. package/lib/api/options.js +5 -0
  8. package/lib/api/validate.js +3 -0
  9. package/lib/base/message-registry.js +25 -2
  10. package/lib/base/messages.js +1 -1
  11. package/lib/base/model.js +3 -2
  12. package/lib/checks/actionsFunctions.js +6 -3
  13. package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
  14. package/lib/checks/existsInForbiddenPlaces.js +32 -0
  15. package/lib/checks/existsMustEndInAssoc.js +1 -1
  16. package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
  17. package/lib/checks/validator.js +6 -2
  18. package/lib/compiler/assert-consistency.js +5 -7
  19. package/lib/compiler/builtins.js +5 -6
  20. package/lib/compiler/checks.js +4 -8
  21. package/lib/compiler/define.js +244 -459
  22. package/lib/compiler/extend.js +297 -11
  23. package/lib/compiler/finalize-parse-cdl.js +2 -10
  24. package/lib/compiler/generate.js +29 -1
  25. package/lib/compiler/populate.js +21 -63
  26. package/lib/compiler/propagator.js +1 -2
  27. package/lib/compiler/resolve.js +2 -12
  28. package/lib/compiler/shared.js +145 -114
  29. package/lib/compiler/tweak-assocs.js +14 -10
  30. package/lib/compiler/utils.js +97 -0
  31. package/lib/compiler/xpr-rewrite.js +113 -140
  32. package/lib/edm/annotations/edmJson.js +9 -6
  33. package/lib/edm/annotations/genericTranslation.js +8 -4
  34. package/lib/edm/csn2edm.js +3 -4
  35. package/lib/edm/edmInboundChecks.js +1 -2
  36. package/lib/edm/edmPreprocessor.js +3 -3
  37. package/lib/gen/CdlGrammar.checksum +1 -1
  38. package/lib/gen/CdlParser.js +4 -3
  39. package/lib/gen/Dictionary.json +16 -1
  40. package/lib/json/from-csn.js +4 -6
  41. package/lib/json/to-csn.js +3 -3
  42. package/lib/model/csnRefs.js +13 -4
  43. package/lib/model/enrichCsn.js +4 -2
  44. package/lib/optionProcessor.js +8 -4
  45. package/lib/parsers/AstBuildingParser.js +1 -1
  46. package/lib/render/utils/sql.js +3 -2
  47. package/lib/transform/db/applyTransformations.js +1 -1
  48. package/lib/transform/db/assertUnique.js +3 -3
  49. package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
  50. package/lib/transform/db/assocsToQueries/transformExists.js +17 -13
  51. package/lib/transform/db/assocsToQueries/utils.js +1 -6
  52. package/lib/transform/db/backlinks.js +4 -4
  53. package/lib/transform/db/cdsPersistence.js +4 -4
  54. package/lib/transform/db/constraints.js +4 -4
  55. package/lib/transform/db/expansion.js +5 -5
  56. package/lib/transform/db/flattening.js +4 -5
  57. package/lib/transform/db/rewriteCalculatedElements.js +3 -3
  58. package/lib/transform/db/temporal.js +11 -11
  59. package/lib/transform/draft/db.js +2 -0
  60. package/lib/transform/draft/odata.js +5 -7
  61. package/lib/transform/effective/flattening.js +1 -2
  62. package/lib/transform/forOdata.js +3 -3
  63. package/lib/transform/forRelationalDB.js +1 -1
  64. package/lib/transform/localized.js +13 -20
  65. package/lib/transform/odata/createForeignKeys.js +1 -2
  66. package/lib/transform/odata/flattening.js +1 -2
  67. package/lib/transform/odata/toFinalBaseType.js +52 -55
  68. package/lib/transform/transformUtils.js +3 -4
  69. package/package.json +3 -3
  70. package/doc/CHANGELOG_BETA.md +0 -464
  71. package/doc/CHANGELOG_DEPRECATED.md +0 -235
@@ -134,6 +134,28 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
134
134
  }
135
135
  }
136
136
 
137
+ // Initialization (define.js), shared with extend.js: ---------------------------
138
+
139
+ /**
140
+ * Initialize artifact links inside `obj.items` (for nested ones as well).
141
+ * Does nothing, it `obj.items` does not exist.
142
+ *
143
+ * @param {XSN.Artifact} obj
144
+ * @param {object} block
145
+ * @return {XSN.Artifact}
146
+ */
147
+ function initItemsLinks( obj, block ) {
148
+ let { items } = obj;
149
+ while (items) {
150
+ setLink( items, '_outer', obj );
151
+ setLink( items, '_parent', obj._parent );
152
+ setLink( items, '_block', block );
153
+ obj = items;
154
+ items = obj.items;
155
+ }
156
+ return obj;
157
+ }
158
+
137
159
  /**
138
160
  * Set the member `elem` to have a _parent link to `parent` and a corresponding
139
161
  * _main link. Also set the member's name accordingly, where argument `name`
@@ -162,6 +184,76 @@ function createAndLinkCalcDepElement( elem ) {
162
184
  setLink( r, '_outer', elem );
163
185
  }
164
186
 
187
+ function initExprAnnoBlock( art, block ) {
188
+ // remark: `art` could also be the extension
189
+ for (const prop in art) {
190
+ if (prop.charAt(0) !== '@')
191
+ continue;
192
+ const anno = art[prop];
193
+ // _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
194
+ if (anno.literal === 'array') {
195
+ anno.kind = '$annotation';
196
+ for (const item of anno.val)
197
+ setLink( item, '_block', block );
198
+ }
199
+ else if (anno.$tokenTexts) {
200
+ // remark: it wouldn't hurt to set it always...
201
+ anno.kind = '$annotation';
202
+ setLink( anno, '_block', block );
203
+ }
204
+ }
205
+ }
206
+
207
+ function initDollarSelf( art ) {
208
+ // TODO: use setMemberParent() ?
209
+ const self = {
210
+ name: { id: '$self', location: art.location },
211
+ kind: '$self',
212
+ location: art.location,
213
+ };
214
+ setLink( self, '_parent', art );
215
+ setLink( self, '_main', art ); // used on main artifact
216
+ setLink( self, '_origin', art );
217
+ art.$tableAliases = Object.create( null );
218
+ art.$tableAliases.$self = self;
219
+ }
220
+
221
+ function initDollarParameters( art ) {
222
+ // TODO: use setMemberParent() ?
223
+ const parameters = {
224
+ name: { id: '$parameters' },
225
+ kind: '$parameters',
226
+ location: art.location,
227
+ deprecated: true, // hide in code completion
228
+ };
229
+ setLink( parameters, '_parent', art );
230
+ setLink( parameters, '_main', art );
231
+ // Search for :const after :param. If there will be a possibility in the
232
+ // future that we can extend <query>.columns, we must be sure to use
233
+ // _block of that new column after :param (or just allow $parameters there).
234
+ setLink( parameters, '_block', art._block );
235
+ if (art.params) {
236
+ parameters.elements = art.params;
237
+ parameters.$tableAliases = art.params; // TODO: find better name - $lexical?
238
+ }
239
+ art.$tableAliases.$parameters = parameters;
240
+ }
241
+
242
+ function initBoundSelfParam( params, main ) {
243
+ if (!params)
244
+ return;
245
+ const first = params[Object.keys( params )[0] || ''];
246
+ const type = first?.type || first?.items?.type; // this sequence = no derived type
247
+ const path = type?.path;
248
+ if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
249
+ const $self = main.$tableAliases?.$self ||
250
+ main.kind === 'extend' && { name: { id: '$self' } };
251
+ // remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
252
+ setLink( type, '_artifact', $self );
253
+ setLink( path[0], '_artifact', $self );
254
+ }
255
+ }
256
+
165
257
  /**
166
258
  * Adds a dependency user -> art with the given location.
167
259
  *
@@ -702,8 +794,13 @@ module.exports = {
702
794
  proxyCopyMembers,
703
795
  dependsOn,
704
796
  dependsOnSilent,
797
+ initItemsLinks,
705
798
  setMemberParent,
706
799
  createAndLinkCalcDepElement,
800
+ initExprAnnoBlock,
801
+ initDollarSelf,
802
+ initDollarParameters,
803
+ initBoundSelfParam,
707
804
  storeExtension,
708
805
  withAssociation,
709
806
  pathName,
@@ -72,10 +72,10 @@
72
72
  // the path up to the last path step in the type-of.
73
73
  // - an element definition, `$self.sub.elem` needs to be replaced by the element name.
74
74
  // Paths without `$self` on the "type-of element" itself need to have
75
- // the first path step be replaced by the target element name.
75
+ // the first path step be replaced by the destination element name.
76
76
  // - a parameter, `$self.sub.elem` needs to be replaced by the parameter name.
77
77
  // Paths without `$self` on the "type-of element" itself need to have
78
- // the first path step be replaced by the target parameter name.
78
+ // the first path step be replaced by the destination parameter name.
79
79
  // - a return parameter, `$self` needs to be rejected, because there is no way
80
80
  // to refer to `returns`. Paths without `$self` on the "type-of element" itself
81
81
  // (not sub-elements), need to be rejected as well.
@@ -108,7 +108,7 @@
108
108
  // was projected or if a prefix was projected (e.g. for structures or associations).
109
109
  // The same rules as for ON-condition rewriting apply.
110
110
  //
111
- // Furthermore, as the target is a select item, and this select item belongs to a table
111
+ // Furthermore, as the destination is a select item, and this select item belongs to a table
112
112
  // alias, we should rewrite all annotation paths only to projected elements of that
113
113
  // table alias. Cross-rewriting between table aliases should not be done.
114
114
  // This is the same we do for association rewriting.
@@ -157,14 +157,13 @@ const { isSimpleCdlIdentifier } = require('../parsers/identifiers');
157
157
  // Config object passed around all "rewrite" functions.
158
158
  class AnnoRewriteConfig {
159
159
  anno;
160
- target;
161
- targetRoot;
160
+ destination;
161
+ destinationRoot;
162
162
  origin;
163
- fromTargetType;
163
+ fromDestinationType;
164
164
  fromCalcElement;
165
165
  expandedRoot;
166
166
  expandedRootType;
167
- isInFilter;
168
167
  tokenExpr;
169
168
  }
170
169
 
@@ -185,45 +184,50 @@ function xprRewriteFns( model ) {
185
184
  };
186
185
 
187
186
  /**
187
+ * Report anno-missing-rewrite if rewrite for annotations.
188
+ * Returns traverseExpr.STOP.
189
+ *
188
190
  * @param expr
189
191
  * @param {AnnoRewriteConfig} config
190
192
  * @param {string} [variant]
191
193
  */
192
194
  function reportAnnoRewriteError( expr, config, variant = 'std' ) {
193
- return !config.tokenExpr || // TODO: some info message ?
195
+ if (config.tokenExpr) {
194
196
  error( 'anno-missing-rewrite', [
195
- weakLocation( config.target.location ), config.target,
197
+ weakLocation( config.destination.location ), config.destination,
196
198
  ], {
197
199
  '#': variant,
198
200
  anno: config.anno,
199
201
  art: config.origin,
200
202
  elemref: expr,
201
203
  });
204
+ }
205
+ return traverseExpr.STOP;
202
206
  }
203
207
 
204
208
  /**
205
- * Rewrite the propagated annotation relative to the target.
209
+ * Rewrite the propagated annotation relative to the destination.
206
210
  *
207
- * @param {XSN.Artifact} target
211
+ * @param {XSN.Artifact} destination
208
212
  * @param {XSN.Artifact} origin
209
213
  * @param {string} annoName
210
214
  */
211
- function rewriteAnnotationsRefs( target, origin, annoName ) {
215
+ function rewriteAnnotationsRefs( destination, origin, annoName ) {
212
216
  // Make sure not to waste time if no inherited annotation has references:
213
217
  if (!origin?.$contains?.$annotation?.$path)
214
218
  return;
215
219
 
216
- const anno = target[annoName];
220
+ const anno = destination[annoName];
217
221
  // only annotations with expressions have a kind
218
222
  // also, don't report errors twice
219
223
  if (!anno.kind || anno.$invalidPaths)
220
224
  return;
221
225
 
222
- const hasError = rewriteRefsInExpression( target, origin, annoName );
223
- target.$contains ??= {};
224
- target.$contains.$annotation ??= {};
225
- target.$contains.$annotation.$path ||= origin.$contains.$annotation.$path;
226
- target.$contains.$annotation.$self ||= origin.$contains.$annotation.$self;
226
+ const hasError = rewriteRefsInExpression( destination, origin, annoName );
227
+ destination.$contains ??= {};
228
+ destination.$contains.$annotation ??= {};
229
+ destination.$contains.$annotation.$path ||= origin.$contains.$annotation.$path;
230
+ destination.$contains.$annotation.$self ||= origin.$contains.$annotation.$self;
227
231
  if (hasError)
228
232
  anno.$invalidPaths = true; // avoid subsequent errors
229
233
  }
@@ -231,23 +235,23 @@ function xprRewriteFns( model ) {
231
235
  function rewriteRefsInExpression( destination, origin, propName ) {
232
236
  // Annotation comes from the destination's type. That's important to know, because
233
237
  // path prefixes need to be adapted.
234
- const fromTargetType = destination.type?._artifact === origin;
238
+ const fromDestinationType = destination.type?._artifact === origin;
235
239
  // Annotation comes from the destination's calculated element.
236
240
  // A special case propagation rule, e.g
237
241
  // for `calcString: String = str;`. We also need to adapt path prefixes.
238
- const fromCalcElement = !fromTargetType && destination.$calcDepElement &&
242
+ const fromCalcElement = !fromDestinationType && destination.$calcDepElement &&
239
243
  destination.value?._artifact === origin;
240
244
 
241
245
  const { expandedRoot, expandedRootType }
242
- = !fromTargetType && getExpandRoot( destination ) || {};
246
+ = !fromDestinationType && getExpandRoot( destination ) || {};
243
247
 
244
248
  const config = {
245
249
  __proto__: AnnoRewriteConfig.prototype,
246
250
  anno: propName,
247
- target: destination,
248
- targetRoot: annoRootArt( destination ),
251
+ destination,
252
+ destinationRoot: annoRootArt( destination ),
249
253
  origin,
250
- fromTargetType,
254
+ fromDestinationType,
251
255
  fromCalcElement,
252
256
  expandedRoot,
253
257
  expandedRootType,
@@ -255,10 +259,8 @@ function xprRewriteFns( model ) {
255
259
 
256
260
  if (propName.charAt( 0 ) === '@')
257
261
  return rewriteAnnotationExpr( destination[propName], config );
258
- return traverseExpr.STOP === traverseExpr(
259
- destination[propName], 'annoRewrite', destination,
260
- // eslint-disable-next-line @stylistic/max-len, @stylistic/function-paren-newline
261
- (e, refCtx) => (rewriteAnnoExpr( e, config, refCtx ) ? traverseExpr.STOP : traverseExpr.SKIP) );
262
+ return traverseExpr( destination[propName], 'annoRewrite', destination,
263
+ rewriteAnnoExpr.bind( config ) );
262
264
  }
263
265
 
264
266
  /**
@@ -277,37 +279,36 @@ function xprRewriteFns( model ) {
277
279
  else if (expr.$tokenTexts) {
278
280
  // used to set `$tokenText` to true in case of rewritten annotation
279
281
  config.tokenExpr = expr;
280
- return traverseExpr.STOP === traverseExpr(
281
- expr, 'annoRewrite', config.target,
282
- // eslint-disable-next-line @stylistic/max-len, @stylistic/function-paren-newline
283
- (e, refCtx) => (rewriteAnnoExpr( e, config, refCtx ) ? traverseExpr.STOP : traverseExpr.SKIP) );
282
+ return traverseExpr( expr, 'annoRewrite', config.destination,
283
+ rewriteAnnoExpr.bind( config ) );
284
284
  }
285
285
  return false;
286
286
  }
287
287
 
288
288
  /**
289
- * @param {XSN.Expression} expr
290
- * @param {AnnoRewriteConfig} config
291
- * @param {string} refCtx
292
- * @returns {null|true} Returns true if the expression couldn't be rewritten.
289
+ * Rewrite the annotation expression `expr`.
290
+ * Returns traverseExpr.STOP if the expression couldn't be rewritten.
293
291
  */
294
- function rewriteAnnoExpr( expr, config, refCtx ) {
292
+ function rewriteAnnoExpr( expr, refCtx, user ) {
293
+ const config = this; // we use rewriteAnnoExpr.bind( config )
295
294
  const root = expr.path && (expr.path[0]?._navigation || expr.path[0]?._artifact);
296
295
  if (!root || !expr._artifact)
297
296
  return null; // invalid path
298
297
 
299
- const { target } = config;
298
+ if (refCtx === 'filter')
299
+ return rewriteGenericAnnoPath( expr, refCtx, config, user );
300
300
 
301
301
  // Report obsolete $parameters; parameters on non-actions not supported, yet.
302
- if (root.kind === '$parameters' || (root.kind === 'param' && root._parent.kind !== 'action' &&
303
- root._parent.kind !== 'function'))
302
+ if (root.kind === '$parameters' ||
303
+ (root.kind === 'param' && root._parent.kind !== 'action' &&
304
+ root._parent.kind !== 'function'))
304
305
  return reportAnnoRewriteError( expr, config, 'unsupported' );
305
306
 
306
307
  if (root.kind === 'key') {
307
308
  // References starting with a foreign key name (only possible in an
308
309
  // annotation on a foreign key) have length 1 + the foreign key cannot be
309
310
  // renamed → nothing to do. Otherwise, foreign keys cannot be directly
310
- // addressed, except as `annotate` target. (Even as annotation value for
311
+ // addressed, except via `annotate`. (Even as annotation value for
311
312
  // foreign keys, `$self.assoc.target_id` always refers to the target side.)
312
313
  return null;
313
314
  }
@@ -318,37 +319,15 @@ function xprRewriteFns( model ) {
318
319
  return null;
319
320
 
320
321
  let hasError = false;
321
- if (config.fromTargetType || config.fromCalcElement)
322
- hasError = adaptPathPrefixViaType( expr, config );
322
+ if (config.fromDestinationType || config.fromCalcElement)
323
+ hasError = adaptPathPrefixViaType( expr, config, user );
323
324
  else if (config.expandedRoot)
324
- hasError = adaptPathPrefixViaTypeExpansion( expr, config );
325
+ hasError = adaptPathPrefixViaTypeExpansion( expr, config, user );
325
326
 
326
- hasError ||= rewriteGenericAnnoPath( expr, config, refCtx );
327
+ hasError ||= rewriteGenericAnnoPath( expr, refCtx, config, user );
327
328
 
328
329
  if (hasError)
329
- return true;
330
-
331
- // TODO: Remove extra loop once filter traversal is added to traverseExpr (#12068)
332
- for (const step of expr.path) {
333
- if (step?._artifact && step.where && !Array.isArray( step._artifact ) ) {
334
- // We must not prefix `$`-renamed variables with `$self`, as it would
335
- // change meaning, see (#11775). Also, the path's target changes.
336
- const assocTarget = step._artifact.target._artifact;
337
- if (target) {
338
- const filterConfig = { ...config, target: assocTarget, isInFilter: true };
339
- if (traverseExpr.STOP === traverseExpr(
340
- step.where, 'filter', step,
341
- // eslint-disable-next-line @stylistic/max-len
342
- (e, ctx) => expr.path && (rewriteGenericAnnoPath( e, filterConfig, ctx ) ? traverseExpr.STOP : traverseExpr.SKIP)
343
- ))
344
- return true;
345
- }
346
- else {
347
- // can't happen: rejected earlier by compiler
348
- return reportAnnoRewriteError( expr, config, 'unsupported' );
349
- }
350
- }
351
- }
330
+ return traverseExpr.STOP;
352
331
 
353
332
  if (expr.$tokenTexts === true) {
354
333
  // TODO: do not do with Universal-CSN (and gensrc, but that does not matter)
@@ -365,46 +344,44 @@ function xprRewriteFns( model ) {
365
344
  if (model.options.testMode) {
366
345
  // re-resolve the modified path; all paths steps must match what we rewrote
367
346
  const ref = { ...expr, path: [ ...expr.path.map(item => ({ ...item })) ] };
368
- if (!resolvePath( ref, refCtx, target ))
347
+ if (!resolvePath( ref, refCtx, user ))
369
348
  throw new CompilerAssertion(`rewritten anno path must be resolvable: ${ JSON.stringify(ref.path) }`);
370
-
371
- for (let i = 0; i < ref.path.length; ++i) {
372
- const actual = ref.path[i];
373
- const expected = ref.path[i];
374
- if (actual._artifact !== expected._artifact) {
375
- throw new CompilerAssertion(`rewritten anno path contains incorrect artifact links: ${
376
- JSON.stringify(ref.path) }; step ${ i }`);
377
- }
378
- else if (actual._navigation !== undefined && actual._navigation !== expected._navigation) {
379
- throw new CompilerAssertion(`rewritten anno path contains incorrect navigation links: ${
380
- JSON.stringify(ref.path) }; step ${ i }`);
381
- }
382
- }
383
349
  }
384
-
385
350
  return false;
386
351
  }
387
352
 
388
353
  /**
389
- * @param {XSN.Expression} expr
390
- * @param {AnnoRewriteConfig} config
391
- * @returns {*}
354
+ * Return the destination node of the expr rewrite (stored in param `config`), or
355
+ * if we are in a filter expression, the association target of the corresponding
356
+ * path item (provided via param `user`).
357
+ *
358
+ * TODO: in the end, we should directly use the standard expression parameter
359
+ * `user` in all functions below. Probably if we unify this module with
360
+ * rewriteCondition() of tweak-assocs.js.
392
361
  */
393
- function getRootEnv( expr, config ) {
394
- const { target } = config;
362
+ function destinationOrAssocTarget( config, user ) {
363
+ return (user.id && user.where)
364
+ ? user._artifact.target._artifact
365
+ : config.destination;
366
+ }
395
367
 
368
+ /**
369
+ * TODO: what exactly is a root environment?
370
+ */
371
+ function getRootEnv( expr, config, destination ) {
396
372
  if (expr.scope === 'param') // path is absolute
397
- return navigationEnv( config.targetRoot, null, null, 'nav' );
373
+ return navigationEnv( config.destinationRoot, null, null, 'nav' );
398
374
 
399
375
  // On select items, use navigation elements or table alias
400
376
  // TODO: Expand/inline paths don't have a `_navigation` property on their last
401
377
  // path step, yet. We need to implement expand/inline.
402
- const isSimpleSelectItem = target.value?.path && target._main?.query && !target._columnParent;
378
+ const isSimpleSelectItem = destination.value?.path && destination._main?.query &&
379
+ !destination._columnParent;
403
380
  if (isSimpleSelectItem) {
404
381
  const isSelfPath = (expr.path[0]?._navigation?.kind === '$self');
405
382
  if (isSelfPath) {
406
383
  // Path is absolute, use table alias to resolve it.
407
- let tableAlias = target.value.path[0]._navigation;
384
+ let tableAlias = destination.value.path[0]._navigation;
408
385
  while (tableAlias && tableAlias.kind === '$navElement')
409
386
  tableAlias = tableAlias._parent;
410
387
  if (tableAlias)
@@ -412,37 +389,36 @@ function xprRewriteFns( model ) {
412
389
  }
413
390
  else {
414
391
  // Path is relative
415
- const nav = target.value.path[target.value.path.length - 1]._navigation?._parent;
392
+ const nav = destination.value.path[destination.value.path.length - 1]._navigation?._parent;
416
393
  if (nav)
417
394
  return nav;
418
395
  }
419
396
  }
420
397
 
421
- if (isSimpleSelectItem && model.options.testMode)
422
- throw new CompilerAssertion(`select item has no table alias: ${ JSON.stringify(target.value.path) }`);
398
+ if (isSimpleSelectItem && model.options.testMode &&
399
+ destination.value?.path[0]._navigation?.kind !== 'mixin')
400
+ throw new CompilerAssertion(`select item has no table alias: ${ JSON.stringify(destination.value.path) }`);
423
401
 
424
402
  if (isAnnoPathAbsolute( expr ))
425
- return navigationEnv( config.targetRoot, null, null, 'nav' );
403
+ return navigationEnv( config.destinationRoot, null, null, 'nav' );
426
404
 
427
405
  // anno path is relative / element reference (others were already rejected)
428
- // if the target is a root artifact, use it. Otherwise, use its parent.
429
- return navigationEnv( isAnnoRootArt( target ) ? target : target._parent, null, null, 'nav' );
406
+ // if the destination is a root artifact, use it. Otherwise, use its parent.
407
+ return navigationEnv( isAnnoRootArt( destination ) ? destination : destination._parent,
408
+ null, null, 'nav' );
430
409
  }
431
410
 
432
- /**
433
- * @param {XSN.Expression} expr
434
- * @param {AnnoRewriteConfig} config
435
- * @param {string} refCtx
436
- * @returns {boolean}
437
- */
438
- function rewriteGenericAnnoPath( expr, config, refCtx ) {
411
+ function rewriteGenericAnnoPath( expr, refCtx, config, user ) {
412
+ if (expr._artifact?.kind === 'builtin')
413
+ return false;
414
+ const destination = destinationOrAssocTarget( config, user );
439
415
  const isAbsolute = isAnnoPathAbsolute( expr );
440
416
  const startIndex = isAbsolute ? 1 : 0;
441
417
 
442
418
  // We get the root environment now, even though below we resolve the root item
443
419
  // again if it was absolute (e.g. $self). We do so, because for queries, we
444
420
  // want to respect the select item's corresponding table alias.
445
- const rootEnv = getRootEnv( expr, config );
421
+ const rootEnv = getRootEnv( expr, config, destination );
446
422
 
447
423
  // reset artifact link; we'll set it again if there are no errors
448
424
  setArtifactLink( expr, null );
@@ -454,7 +430,7 @@ function xprRewriteFns( model ) {
454
430
  delete expr.path[0]._navigation;
455
431
  // TODO: What about `up_`? Shouldn't we set `_navigation` as well?
456
432
  // TODO: Can we handle `$self` of anonymous-composition-of-aspect?
457
- const root = resolvePathRoot( expr, refCtx, config.target );
433
+ const root = resolvePathRoot( expr, refCtx, destination );
458
434
  if (!root)
459
435
  return reportAnnoRewriteError( expr, config );
460
436
  }
@@ -495,13 +471,13 @@ function xprRewriteFns( model ) {
495
471
  setArtifactLink( expr, art );
496
472
 
497
473
  if (startIndex === 0 && expr.path[0].id.startsWith('$')) {
498
- if (config.isInFilter) {
474
+ if (refCtx === 'filter') {
499
475
  // In filters, we must not prepend `$self`, as that would change its meaning.
500
476
  // We must reject it. See #11775
501
477
  return reportAnnoRewriteError( expr, config );
502
478
  }
503
479
  // After rewriting, if an element starts with `$` -> add root prefix
504
- prependRootPath( config.origin, config.targetRoot, expr );
480
+ prependRootPath( config.origin, config.destinationRoot, expr );
505
481
  }
506
482
 
507
483
  return false;
@@ -513,12 +489,12 @@ function xprRewriteFns( model ) {
513
489
  *
514
490
  * @returns {boolean} Returns true if it couldn't be rewritten.
515
491
  */
516
- function adaptPathPrefixViaType( expr, config ) {
517
- const { target, origin } = config;
518
- if (!target._main && !origin._main)
492
+ function adaptPathPrefixViaType( expr, config, user ) {
493
+ const { destination, origin } = config;
494
+ if (!destination._main && !origin._main)
519
495
  return false; // no need to rewrite; both are top-level
520
496
 
521
- if (rejectOuterReference( expr, origin, config ))
497
+ if (rejectOuterReference( expr, origin, config, user ))
522
498
  return true;
523
499
 
524
500
  // $self-paths via types from/to non-main artifacts always need to be rewritten.
@@ -529,16 +505,16 @@ function xprRewriteFns( model ) {
529
505
  stripAbsolutePathPrefix( expr, origin );
530
506
 
531
507
  if (wasAbsolute) {
532
- prependRootPath( origin, target, expr );
508
+ prependRootPath( origin, destination, expr );
533
509
  }
534
- else if (!isAnnoRootArt( target )) { // target is element
535
- const item = { id: target.name.id };
536
- setArtifactLink( item, target );
510
+ else if (!isAnnoRootArt( destination )) { // destination is element
511
+ const item = { id: destination.name.id };
512
+ setArtifactLink( item, destination );
537
513
  prependToStrippedPath( origin, expr, [ item ] );
538
514
  }
539
- else if (target.kind === 'param') {
515
+ else if (destination.kind === 'param') {
540
516
  // annotations on parameters need a `:prefix`
541
- prependRootPath( origin, target, expr );
517
+ prependRootPath( origin, destination, expr );
542
518
  }
543
519
  else {
544
520
  prependToStrippedPath( origin, expr, [ ] );
@@ -556,7 +532,7 @@ function xprRewriteFns( model ) {
556
532
  *
557
533
  * @returns {boolean} Returns true if it couldn't be rewritten.
558
534
  */
559
- function adaptPathPrefixViaTypeExpansion( expr, config ) {
535
+ function adaptPathPrefixViaTypeExpansion( expr, config, user ) {
560
536
  const root = expr.path[0]?._navigation;
561
537
  if (root?.kind !== '$self') {
562
538
  // non-self paths are always valid in expanded artifacts
@@ -569,14 +545,14 @@ function xprRewriteFns( model ) {
569
545
  if (!isBetaEnabled( model.options, 'rewriteAnnotationExpressionsViaType' ))
570
546
  return reportAnnoRewriteError( expr, config, 'unsupported' );
571
547
 
572
- if (rejectOuterReference( expr, config.expandedRootType, config ))
548
+ if (rejectOuterReference( expr, config.expandedRootType, config, user ))
573
549
  return true;
574
550
 
575
551
  stripAbsolutePathPrefix( expr, config.expandedRootType );
576
552
  prependRootPath( config.expandedRootType, config.expandedRoot, expr );
577
- setExpandStatusAnnotate( config.target, 'annotate' );
553
+ setExpandStatusAnnotate( config.destination, 'annotate' );
578
554
 
579
- config.target[config.anno].$inferred = 'anno-rewrite';
555
+ config.destination[config.anno].$inferred = 'anno-rewrite';
580
556
  // $self-paths via type expansion always need to be rewritten.
581
557
  if (config.tokenExpr)
582
558
  config.tokenExpr.$tokenTexts = true;
@@ -621,32 +597,29 @@ function xprRewriteFns( model ) {
621
597
  }
622
598
 
623
599
  /**
624
- * Returns false if the path can be propagated to the target without referring
625
- * to any "outer" elements. It differentiates between the target being a main
600
+ * Returns false if the path can be propagated to the destination without referring
601
+ * to any "outer" elements. It differentiates between the destination being a main
626
602
  * artifact and elements, because an element annotation referring to itself can't
627
603
  * be propagated to a type:
628
604
  *
629
605
  * type T1 : { @a: (elem) elem: String; };
630
606
  * type T2 : T1:elem; // invalid
631
607
  *
632
- * Also considers other targets such as `returns`, etc.
633
- *
634
- * @param {XSN.Expression} expr
635
- * @param {XSN.Artifact} origin
636
- * @param {AnnoRewriteConfig} config
637
- * @returns {boolean}
608
+ * Also considers other destinations such as `returns`, etc.
638
609
  */
639
- function rejectOuterReference( expr, origin, config ) {
610
+ function rejectOuterReference( expr, origin, config, user ) {
640
611
  if (!isAnnoPathAbsolute( expr ) && !origin._main)
641
612
  return false;
642
613
 
643
614
  const root = expr.path[0]?._navigation;
644
615
  const found = findRelativeRoot( expr, origin );
616
+ const destination = destinationOrAssocTarget( config, user );
645
617
  const isInvalid = (found === -1) ||
646
618
  // Can't use paths with `$self` in `returns`.
647
- (root?.kind === '$self' && isReturnParam( config.targetRoot )) ||
619
+ (root?.kind === '$self' && isReturnParam( config.destinationRoot )) ||
648
620
  // siblings are allowed for non-main artifacts, except for 'returns'
649
- (!config.target._main || isReturnParam( config.target )) && (expr.path.length - found) <= 1;
621
+ (!destination._main || isReturnParam( destination )) &&
622
+ (expr.path.length - found) <= 1;
650
623
 
651
624
  if (isInvalid)
652
625
  return reportAnnoRewriteError( expr, config );
@@ -720,7 +693,7 @@ function xprRewriteFns( model ) {
720
693
  * @returns {*|null}
721
694
  */
722
695
  function rewriteItem( expr, config, env, index ) {
723
- const rewriteTarget = findRewriteTarget( expr, index, env, config.target );
696
+ const rewriteTarget = findRewriteTarget( expr, index, env, config.destination );
724
697
  const found = setArtifactLink( expr.path[index], rewriteTarget[0] );
725
698
  if (!found)
726
699
  return null;
@@ -784,20 +757,20 @@ function isReturnParam( art ) {
784
757
  }
785
758
 
786
759
  /**
787
- * Gets the artifact (e.g. element) that was expanded. `target` is a sub-artifact of that root and
788
- * is an expanded element.
760
+ * Gets the artifact (e.g. element) that was expanded.
761
+ * `destination` is a sub-artifact of that root and is an expanded element.
789
762
  *
790
763
  * - expandedRoot: Top-most structure that was expanded.
791
764
  * - expandedRootType: The type of expandedRoot.
792
765
  *
793
- * @param {XSN.Artifact} target
766
+ * @param {XSN.Artifact} destination
794
767
  * @returns { {expandedRoot: XSN.Artifact, expandedRootType: XSN.Artifact}}
795
768
  */
796
- function getExpandRoot( target ) {
797
- if (target.$inferred !== 'expanded' && target.$inferred !== 'rewrite')
769
+ function getExpandRoot( destination ) {
770
+ if (destination.$inferred !== 'expanded' && destination.$inferred !== 'rewrite')
798
771
  return { expandedRoot: null, expandedRootType: null };
799
772
 
800
- let expandedRoot = target;
773
+ let expandedRoot = destination;
801
774
 
802
775
  // 'expanded' for structures, 'rewrite' for foreign keys
803
776
  while (expandedRoot.$inferred === 'expanded' || expandedRoot.$inferred === 'rewrite')