@sap/cds-compiler 5.1.2 → 5.3.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 (68) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/bin/cdsc.js +7 -2
  3. package/bin/cdshi.js +24 -17
  4. package/bin/cdsse.js +17 -18
  5. package/doc/CHANGELOG_BETA.md +9 -4
  6. package/lib/api/main.js +19 -2
  7. package/lib/api/options.js +4 -1
  8. package/lib/api/validate.js +5 -0
  9. package/lib/base/builtins.js +1 -0
  10. package/lib/base/message-registry.js +40 -3
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +0 -11
  13. package/lib/checks/actionsFunctions.js +0 -12
  14. package/lib/checks/structuredAnnoExpressions.js +10 -14
  15. package/lib/compiler/assert-consistency.js +21 -13
  16. package/lib/compiler/builtins.js +2 -2
  17. package/lib/compiler/checks.js +25 -6
  18. package/lib/compiler/define.js +27 -31
  19. package/lib/compiler/extend.js +16 -18
  20. package/lib/compiler/generate.js +3 -3
  21. package/lib/compiler/populate.js +22 -16
  22. package/lib/compiler/propagator.js +3 -2
  23. package/lib/compiler/resolve.js +87 -94
  24. package/lib/compiler/shared.js +12 -13
  25. package/lib/compiler/tweak-assocs.js +390 -86
  26. package/lib/compiler/utils.js +41 -33
  27. package/lib/compiler/xpr-rewrite.js +45 -58
  28. package/lib/edm/annotations/genericTranslation.js +17 -13
  29. package/lib/edm/csn2edm.js +28 -4
  30. package/lib/edm/edm.js +68 -28
  31. package/lib/edm/edmInboundChecks.js +5 -8
  32. package/lib/edm/edmPreprocessor.js +66 -40
  33. package/lib/edm/edmUtils.js +1 -1
  34. package/lib/gen/BaseParser.js +778 -0
  35. package/lib/gen/CdlParser.js +4477 -0
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +1 -1
  38. package/lib/gen/languageParser.js +4072 -4024
  39. package/lib/inspect/inspectPropagation.js +1 -1
  40. package/lib/json/from-csn.js +5 -3
  41. package/lib/json/to-csn.js +7 -10
  42. package/lib/language/antlrParser.js +96 -0
  43. package/lib/language/errorStrategy.js +1 -1
  44. package/lib/language/genericAntlrParser.js +32 -4
  45. package/lib/language/multiLineStringParser.js +1 -1
  46. package/lib/main.d.ts +23 -0
  47. package/lib/model/cloneCsn.js +22 -13
  48. package/lib/model/csnUtils.js +2 -0
  49. package/lib/model/revealInternalProperties.js +2 -0
  50. package/lib/modelCompare/utils/filter.js +70 -42
  51. package/lib/optionProcessor.js +16 -10
  52. package/lib/parsers/AstBuildingParser.js +1290 -0
  53. package/lib/parsers/CdlGrammar.g4 +2013 -0
  54. package/lib/parsers/Lexer.js +249 -0
  55. package/lib/render/toCdl.js +46 -45
  56. package/lib/render/toSql.js +5 -5
  57. package/lib/transform/addTenantFields.js +4 -4
  58. package/lib/transform/db/applyTransformations.js +54 -16
  59. package/lib/transform/draft/odata.js +10 -11
  60. package/lib/transform/effective/flattening.js +10 -14
  61. package/lib/transform/forRelationalDB.js +7 -6
  62. package/lib/transform/odata/flattening.js +42 -31
  63. package/lib/transform/odata/toFinalBaseType.js +7 -6
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  65. package/lib/utils/moduleResolve.js +1 -1
  66. package/package.json +2 -2
  67. package/share/messages/redirected-to-ambiguous.md +5 -4
  68. package/share/messages/redirected-to-complex.md +6 -3
@@ -99,6 +99,7 @@ function resolve( model ) {
99
99
  Object.assign( model.$functions, {
100
100
  resolveExpr,
101
101
  addForeignKeyNavigations,
102
+ redirectionChain,
102
103
  } );
103
104
 
104
105
  const ignoreSpecifiedElements
@@ -174,9 +175,10 @@ function resolve( model ) {
174
175
  // TODO: or should we push elems with `expand` sibling to extra list for
175
176
  // better messages? (Whatever that means exactly.)
176
177
 
177
- if (elem._pathHead) {
178
- if (elem._pathHead?.kind !== '$inline')
179
- // we're traversing top-level elements of the query; other _pathHead kinds can't happen
178
+ if (elem._columnParent) {
179
+ if (elem._columnParent?.kind !== '$inline')
180
+ // we're traversing top-level elements of the query;
181
+ // other _columnParent kinds can't happen
180
182
  throw new CompilerAssertion('found unexpected "expand", but expected "inline"');
181
183
 
182
184
  if (!isPathBreakout( elem.value )) {
@@ -205,13 +207,17 @@ function resolve( model ) {
205
207
  if (!nav.item._navigation) // first non-table-alias
206
208
  setLink( nav.item, '_navigation', navItem );
207
209
 
208
- if (path[index].where || path[index].args)
209
- return;
210
+ // We consider an element only projected if the path doesn't have
211
+ // either arguments or filters; but we build up the navigation env
212
+ // nonetheless, as it makes rewriting paths later on easier.
213
+ let isComplexPath = !!(path[index].where || path[index].args);
214
+
210
215
  ++index;
211
216
  while (navItem && index < path.length) {
212
217
  const step = path[index];
213
- if (!step?.id || step.where || step.args)
218
+ if (!step?.id)
214
219
  break;
220
+ isComplexPath ||= !!(step.where || step.args);
215
221
  if (!navItem.elements?.[step.id]) {
216
222
  const elements = navItem._origin?.elements ||
217
223
  navItem._origin?.target?._artifact?.elements;
@@ -228,26 +234,26 @@ function resolve( model ) {
228
234
  setLink( step, '_navigation', navItem );
229
235
  ++index;
230
236
  }
231
- // Last path step, if found, is a simple projection
237
+ // Last path step, if found, is a projected, either complex or simple.
232
238
  if (index === path.length && navItem)
233
- pushLink( navItem, '_projections', elem );
239
+ pushLink( navItem, isComplexPath ? '_complexProjections' : '_projections', elem );
234
240
  }
235
241
  }
236
242
 
237
243
  function columnParentPath( elem ) {
238
- if (!elem._pathHead || !elem.value?.path || isPathBreakout( elem.value ))
244
+ if (!elem._columnParent || !elem.value?.path || isPathBreakout( elem.value ))
239
245
  return elem.value?.path;
240
246
 
241
247
  const fullPath = [ ...elem.value.path ];
242
- let pathHead = elem._pathHead;
243
- while (pathHead) {
244
- if (pathHead.kind !== '$inline' || !pathHead.value?.path ||
245
- isPathBreakout( pathHead.value )) {
248
+ let columnParent = elem._columnParent;
249
+ while (columnParent) {
250
+ if (columnParent.kind !== '$inline' || !columnParent.value?.path ||
251
+ isPathBreakout( columnParent.value )) {
246
252
  // path breakout for e.g. `$self.{ foo }`, `1 as a .{ foo }`
247
253
  return null;
248
254
  }
249
- fullPath.unshift(...pathHead.value.path);
250
- pathHead = pathHead._pathHead;
255
+ fullPath.unshift(...columnParent.value.path);
256
+ columnParent = columnParent._columnParent;
251
257
  }
252
258
  return fullPath;
253
259
  }
@@ -285,16 +291,16 @@ function resolve( model ) {
285
291
  return false;
286
292
  }
287
293
 
288
- function inheritedSourceKeyProp( { value, _pathHead } ) {
294
+ function inheritedSourceKeyProp( { value, _columnParent } ) {
289
295
  if (!value || !value.path)
290
296
  return null;
291
- const nav = !_pathHead && pathNavigation( value );
297
+ const nav = !_columnParent && pathNavigation( value );
292
298
  const item = value.path[value.path.length - 1];
293
299
  if (nav?.navigation && nav.item === item)
294
300
  return item._artifact?.key;
295
- if (value.path.length !== 1 || _pathHead?.kind !== '$inline')
301
+ if (value.path.length !== 1 || _columnParent?.kind !== '$inline')
296
302
  return null;
297
- const hpath = _pathHead.value?.path;
303
+ const hpath = _columnParent.value?.path;
298
304
  const head = hpath?.length === 1 && hpath[0]._navigation;
299
305
  return head?.kind === '$tableAlias' && item._artifact?.key;
300
306
  }
@@ -316,7 +322,7 @@ function resolve( model ) {
316
322
  propagateKeys = false;
317
323
  info( 'query-from-many', [ toMany.location, query ], { art: toMany }, {
318
324
  std: 'Key properties are not propagated because a to-many association $(ART) is selected',
319
- // eslint-disable-next-line max-len
325
+ // eslint-disable-next-line @stylistic/js/max-len
320
326
  element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
321
327
  } );
322
328
  }
@@ -344,7 +350,7 @@ function resolve( model ) {
344
350
  const elem = query.elements[name];
345
351
 
346
352
  if (!elem.$inferred && elem.value?.path) {
347
- const path = elem._pathHead ? columnParentPath( elem ) : elem.value.path;
353
+ const path = elem._columnParent ? columnParentPath( elem ) : elem.value.path;
348
354
  if (testExpr({ path }, selectTest, () => false, elem))
349
355
  propagateKeys = false;
350
356
  }
@@ -357,9 +363,9 @@ function resolve( model ) {
357
363
  // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
358
364
  info( 'query-navigate-many', [ art.location, user || query ], { art }, {
359
365
  std: 'Navigating along to-many association $(ART) - key properties are not propagated',
360
- // eslint-disable-next-line max-len
366
+ // eslint-disable-next-line @stylistic/js/max-len
361
367
  element: 'Navigating along to-many association $(MEMBER) of $(ART) - key properties are not propagated',
362
- // eslint-disable-next-line max-len
368
+ // eslint-disable-next-line @stylistic/js/max-len
363
369
  alias: 'Navigating along to-many mixin association $(MEMBER) - key properties are not propagated',
364
370
  } );
365
371
  }
@@ -395,9 +401,6 @@ function resolve( model ) {
395
401
  function resolveRefs( art ) {
396
402
  if (art.builtin)
397
403
  return;
398
- // console.log(info( null, [ art.location, art ], {}, 'REFS').toString());
399
- // console.log(info( null, [ art.location, art ], { art: art.target || 'none' },
400
- // 'RR: $(ART)').toString());
401
404
  const parent = art._parent;
402
405
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
403
406
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
@@ -513,8 +516,6 @@ function resolve( model ) {
513
516
  }
514
517
  }
515
518
  if (obj.target) {
516
- // console.log(error( 'test-target', [ obj.location, obj ],
517
- // { target: obj.target, kind: obj.kind }, 'Target: $(TARGET), Kind $(KIND)'));
518
519
  if (!obj.target.$inferred || obj.target.$inferred === 'aspect-composition')
519
520
  resolveTarget( art, obj );
520
521
  else
@@ -530,7 +531,6 @@ function resolve( model ) {
530
531
  resolvePath( art.targetElement, 'targetElement', art );
531
532
 
532
533
  // Resolve projections/views
533
- // if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
534
534
 
535
535
  if (art.$queries)
536
536
  art.$queries.forEach( resolveQuery );
@@ -923,14 +923,12 @@ function resolve( model ) {
923
923
  // TODO: or set silent dependencies in init?
924
924
  forEachGeneric( query, 'elements', elem => dependsOnSilent( query, elem ) );
925
925
  forEachGeneric( query, '$tableAliases', ( alias ) => {
926
- // console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
927
926
  if (alias.kind === 'mixin')
928
927
  resolveRefs( alias ); // mixin element
929
928
  else if (alias.kind !== '$self')
930
929
  // pure path has been resolved, resolve args and filter now:
931
930
  resolveExpr( alias, 'from', query._parent );
932
931
  } );
933
- // if (!query.$inlines) console.log('RQ:',query)
934
932
  for (const col of query.$inlines)
935
933
  resolveExpr( col.value, 'column', col );
936
934
  // for (const col of query.$inlines)
@@ -1008,7 +1006,7 @@ function resolve( model ) {
1008
1006
  }
1009
1007
  const target = resolvePath( obj.target, 'target', art );
1010
1008
 
1011
- if (obj._pathHead && obj.type && !obj.type.$inferred && art._main && art._main.query) {
1009
+ if (obj._columnParent && obj.type && !obj.type.$inferred && art._main && art._main.query) {
1012
1010
  // New association inside expand/inline: The on-condition can't be properly checked,
1013
1011
  // so abort early. See #8797
1014
1012
  error( 'query-unexpected-assoc', [ obj.name.location, art ], {},
@@ -1090,17 +1088,8 @@ function resolve( model ) {
1090
1088
  issue['#'] = 'order';
1091
1089
  }
1092
1090
  }
1093
- if (issue['#']) {
1094
- /* eslint-disable max-len */
1095
- message( 'type-expecting-service-target', [ art.target.location, art ], issue, {
1096
- std: 'Expecting service entity $(TARGET)',
1097
- ref: 'Expecting service entity $(TARGET); its element $(ID) referred to at line $(LINE), column $(COL) is not from an element with the same name in the provided model target',
1098
- key: 'Expecting service entity $(TARGET); its key element $(ID) is not from a key element with the same name in the provided model target',
1099
- missing: 'Expecting service entity $(TARGET); it does not have the key element $(ID) of the provided model target',
1100
- order: 'Expecting service entity $(TARGET); its key elements are in a different order than those of the provided model target',
1101
- } );
1102
- /* eslint-enable max-len */
1103
- }
1091
+ if (issue['#'])
1092
+ message( 'type-expecting-service-target', [ art.target.location, art ], issue );
1104
1093
  }
1105
1094
 
1106
1095
  function keyElementNames( elements ) {
@@ -1138,7 +1127,7 @@ function resolve( model ) {
1138
1127
  function addImplicitForeignKeys( art, obj, target ) {
1139
1128
  obj.foreignKeys = Object.create( null );
1140
1129
  forEachInOrder( target, 'elements', ( elem, name ) => {
1141
- if (elem.key && elem.key.val) {
1130
+ if (elem.key?.val) {
1142
1131
  const location = weakLocation( obj.target.location );
1143
1132
  const key = {
1144
1133
  name: { location, id: elem.name.id, $inferred: 'keys' },
@@ -1223,7 +1212,7 @@ function resolve( model ) {
1223
1212
  const text = (item !== last) ? 'sub' : 'std';
1224
1213
  error( 'duplicate-key-ref', [ item.location, key ], { '#': text, name }, {
1225
1214
  std: 'Foreign key $(NAME) already refers to the same target element',
1226
- // eslint-disable-next-line max-len
1215
+ // eslint-disable-next-line @stylistic/js/max-len
1227
1216
  sub: 'Foreign key $(NAME) already refers to the target element whose sub element is again referred to here',
1228
1217
  // TODO: please add ideas for a better text, e.g. to (closed) PR #11325
1229
1218
  } );
@@ -1241,9 +1230,8 @@ function resolve( model ) {
1241
1230
  const origType = assoc && effectiveType( assoc );
1242
1231
  if (origType === 0)
1243
1232
  return;
1244
- if (!origType || !origType.target) {
1245
- const path = (elem.value && elem.value.path);
1246
- const loc = (path && path[path.length - 1] || elem.value || elem).location;
1233
+ if (!origType?.target) {
1234
+ const loc = (elem.value?.path?.at(-1) || elem.value || elem).location;
1247
1235
  error( 'redirected-no-assoc', [ loc, elem ], {},
1248
1236
  'Only an association can be redirected' );
1249
1237
  return;
@@ -1253,63 +1241,68 @@ function resolve( model ) {
1253
1241
  error( 'type-invalid-cast', [ elem.type.location, elem ], { '#': 'assoc' } );
1254
1242
  return;
1255
1243
  }
1256
- // console.log(message( null, elem.location, elem, {target,art:assoc}, 'Info','RE')
1257
- // .toString(), elem.value)
1258
- const nav = elem._main && elem._main.query && elem.value && pathNavigation( elem.value );
1259
- if (nav && nav.item !== elem.value.path[elem.value.path.length - 1]) {
1260
- if (!elem.on && origType.on) {
1261
- error( 'rewrite-not-supported', [ elem.target.location, elem ] );
1262
- return;
1263
- }
1264
- }
1244
+
1265
1245
  const origTarget = origType.target._artifact;
1266
- // console.log(require('../model/revealInternalProperties').ref(elem),
1267
- // !!origTarget,!!origType._effectiveType,!!origType.target)
1268
- if (!origTarget || !target)
1269
- return;
1270
1246
 
1271
- const chain = [];
1272
- if (target === origTarget) {
1273
- if (!elem.target.$inferred && !elem.on && !elem.foreignKeys) {
1274
- // Only a managed redirection gets this info message. Because otherwise
1275
- // we'd have to check whether on-condition/foreignKeys are the same.
1276
- // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
1277
- info( 'redirected-to-same', [ elem.target.location, elem ], { art: target },
1278
- 'The redirected target is the original $(ART)' );
1279
- }
1280
- setLink( elem, '_redirected', chain ); // store the chain
1281
- return;
1247
+ if (target === origTarget && !elem.target.$inferred && !elem.on && !elem.foreignKeys) {
1248
+ // Only a managed redirection gets this info message. Because otherwise
1249
+ // we'd have to check whether on-condition/foreignKeys are the same.
1250
+ // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
1251
+ info( 'redirected-to-same', [ elem.target.location, elem ], { art: target },
1252
+ 'The redirected target is the original $(ART)' );
1282
1253
  }
1254
+
1283
1255
  if (elem.foreignKeys || elem.on)
1284
1256
  return; // TODO: or should we still bring an msg if nothing in common?
1257
+
1258
+ const chain = redirectionChain( elem, target, origTarget );
1259
+ setLink( elem, '_redirected', chain );
1260
+ }
1261
+
1262
+ /**
1263
+ * Get the redirection chain between a target and the original target.
1264
+ *
1265
+ * @param {XSN.Artifact} elem
1266
+ * @param {XSN.Artifact} target
1267
+ * @param {XSN.Artifact} origTarget
1268
+ * @param {boolean} [silent] Whether to report error and other messages.
1269
+ * @returns {XSN.Artifact[]|null}
1270
+ */
1271
+ function redirectionChain( elem, target, origTarget, silent = false ) {
1272
+ if (!origTarget || !target)
1273
+ return null;
1274
+ if (target === origTarget)
1275
+ return []; // e.g. explicit ON-condition/foreign keys or original target
1276
+
1277
+ const chain = [];
1285
1278
  // now check whether target and origTarget are "related"
1279
+ // first: check via simple projections
1286
1280
  while (target.query) {
1287
1281
  const from = target.query.args ? {} : target.query.from;
1288
1282
  if (!from)
1289
- return; // parse error - TODO: or UNION?
1283
+ return null; // parse error - TODO: or UNION?
1290
1284
  if (!from.path) {
1285
+ if (silent)
1286
+ break;
1291
1287
  const isTarget = target === elem.target._artifact;
1292
1288
  const op = from.op?.val || target.query.op?.val;
1293
1289
  const variant = (!isTarget && 'std') || (op && 'targetOp') || 'target';
1294
- // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
1295
1290
  info( 'redirected-to-complex', [ elem.target.location, elem ],
1296
- { art: target, '#': variant, keyword: op || '' }, {
1297
- std: 'Redirection involves the complex view $(ART)',
1298
- target: 'The redirected target $(ART) is a complex view',
1299
- targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD)',
1300
- } );
1291
+ { art: target, '#': variant, keyword: op || '' } );
1301
1292
  break;
1302
1293
  }
1303
1294
  target = from._artifact;
1304
1295
  if (!target)
1305
- return;
1296
+ return null;
1306
1297
  chain.push( from );
1307
1298
  if (target === origTarget) {
1299
+ // found in simple projection chain
1308
1300
  chain.reverse();
1309
- setLink( elem, '_redirected', chain );
1310
- return;
1301
+ return chain;
1311
1302
  }
1312
1303
  }
1304
+
1305
+ // there is a complex view in-between; search through table aliases
1313
1306
  let redirected = null;
1314
1307
  chain.reverse();
1315
1308
  let news = [ { chain, sources: [ target ] } ];
@@ -1327,25 +1320,25 @@ function resolve( model ) {
1327
1320
  else if (!redirected) {
1328
1321
  redirected = (s.kind === '$tableAlias') ? [ s, ...o.chain ] : o.chain;
1329
1322
  }
1330
- else {
1323
+ else if (!silent) {
1331
1324
  error( 'redirected-to-ambiguous', [ elem.target.location, elem ], { art: origTarget },
1332
1325
  'The redirected target originates more than once from $(ART)' );
1333
- return;
1326
+ return null;
1327
+ }
1328
+ else {
1329
+ return null;
1334
1330
  }
1335
1331
  }
1336
1332
  }
1337
1333
  }
1338
- if (redirected) {
1339
- setLink( elem, '_redirected', redirected );
1340
- }
1341
- else if (redirected == null) {
1334
+ if (!silent && redirected == null) {
1342
1335
  error( 'redirected-to-unrelated', [ elem.target.location, elem ], { art: origTarget },
1343
1336
  'The redirected target does not originate from $(ART)' );
1344
1337
  }
1345
- return;
1338
+ return redirected;
1346
1339
 
1347
1340
  // B = proj on A, C = A x B, X = { a: assoc to A on a.Q1 = ...}, Y = X.{ a: redirected to C }
1348
- // what does a: redirected to C means?
1341
+ // what does 'a: redirected to C' mean?
1349
1342
  // -> collect all elements Qi used in ON (corr: foreign keys)
1350
1343
  // -> only use an tableAlias which has propagation for all elements
1351
1344
  // no - error if the original target can be reached twice
@@ -1377,7 +1370,7 @@ function resolve( model ) {
1377
1370
  if (a.path && a.kind !== '$self' && a.kind !== 'mixin')
1378
1371
  sources.push( a );
1379
1372
  }
1380
- if (alias.kind === '$tablealias')
1373
+ if (alias.kind === '$tableAlias')
1381
1374
  news.push( { chain: [ alias, ...chain ], sources } );
1382
1375
  else
1383
1376
  news.push( { chain, sources } );
@@ -1436,7 +1429,6 @@ function resolve( model ) {
1436
1429
  }
1437
1430
 
1438
1431
  function resolveExpr( expr, exprCtx, user ) {
1439
- // console.log(expr?.location.line,exprCtx)
1440
1432
  traverseExpr( expr, exprCtx, user, resolveExprItem );
1441
1433
  }
1442
1434
 
@@ -1513,7 +1505,7 @@ function resolve( model ) {
1513
1505
  message( 'expr-unexpected-filter', [ location, user ], { '#': variant }, {
1514
1506
  std: 'A filter can only be provided when navigating along associations',
1515
1507
  // to help users for `… from E:toF { toF[…].x }`
1516
- // eslint-disable-next-line max-len
1508
+ // eslint-disable-next-line @stylistic/js/max-len
1517
1509
  tableAlias: 'A filter can only be provided when navigating along associations, but found table alias',
1518
1510
  from: 'A filter can only be provided for the source entity or associations',
1519
1511
  } );
@@ -1593,7 +1585,8 @@ function pathNavigation( ref ) {
1593
1585
  if (root.kind === '$self')
1594
1586
  return { item, tableAlias: root };
1595
1587
  if (root.kind !== '$tableAlias' || ref.path.length < 2)
1596
- return {}; // should not happen
1588
+ return {}; // should not happen
1589
+ // table alias
1597
1590
  return { navigation: root.elements?.[item.id], item, tableAlias: root };
1598
1591
  }
1599
1592
 
@@ -1,5 +1,4 @@
1
1
  // Compiler functions and utilities shared across all phases
2
- // TODO: rename to paths.js and move non resolve-paths functions to somewhere else
3
2
 
4
3
  'use strict';
5
4
 
@@ -546,7 +545,7 @@ function fns( model ) {
546
545
  ruser = ruser._outer;
547
546
 
548
547
  // Handle expand/inline, `type of`, :param, global (internally for CDL):
549
- if (user._pathHead && !semantics.isMainRef) { // in expand/inline
548
+ if (user._columnParent && !semantics.isMainRef) { // in expand/inline
550
549
  const { name } = semantics;
551
550
  semantics = semantics.nestedColumn();
552
551
  semantics.name = name;
@@ -563,7 +562,7 @@ function fns( model ) {
563
562
 
564
563
  // Search in lexical environments, including $self/$projection:
565
564
  const { isMainRef } = semantics;
566
- const lexical = semantics.lexical?.( ruser ); // TODO: _pathHead?
565
+ const lexical = semantics.lexical?.( ruser ); // TODO: _columnParent?
567
566
  if (lexical) {
568
567
  const [ nextProp, dictProp ] = (isMainRef)
569
568
  ? [ '_block', 'artifacts' ]
@@ -608,7 +607,7 @@ function fns( model ) {
608
607
  // element item in the path)
609
608
  // TODO - think about setting _navigation for all $navElement – the
610
609
  // "ref: ['tabAlias']: inline: […]" handling might be easier
611
- // (no _pathHead consultation for key prop and renaming support)
610
+ // (no _columnParent consultation for key prop and renaming support)
612
611
  function getPathItem( ref, semantics, user ) {
613
612
  // let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
614
613
  const { path } = ref;
@@ -918,7 +917,7 @@ function fns( model ) {
918
917
  }
919
918
 
920
919
  function nestedElements( user ) {
921
- const colParent = user._pathHead;
920
+ const colParent = user._columnParent;
922
921
  Functions.effectiveType( colParent ); // set _origin
923
922
  const path = colParent?.value?.path;
924
923
  if (!path?.length)
@@ -1129,7 +1128,7 @@ function fns( model ) {
1129
1128
  }
1130
1129
 
1131
1130
  function undefinedNestedElement( user, head, valid, _dict, _art, path, semantics ) {
1132
- const art = user._pathHead._origin;
1131
+ const art = user._columnParent._origin;
1133
1132
  if (!art)
1134
1133
  return null; // no consequential error
1135
1134
  return undefinedItemElement( user, head, valid, null, art, path, semantics );
@@ -1226,7 +1225,7 @@ function fns( model ) {
1226
1225
  // of invisible table aliases; at least one stakeholder uses this,
1227
1226
  // so it can't be an error (yet).
1228
1227
  message( 'ref-deprecated-self-element', [ ref.path[0].location, user._user ], {},
1229
- // eslint-disable-next-line max-len
1228
+ // eslint-disable-next-line @stylistic/js/max-len
1230
1229
  'Referring to the query\'s own elements here might lead to invalid SQL references; use source elements only' );
1231
1230
  return false;
1232
1231
  default:
@@ -1551,7 +1550,7 @@ function fns( model ) {
1551
1550
  const location = (user.expand || user.inline)[$location];
1552
1551
  // mention `table alias` in text only with initial single path item ref,
1553
1552
  // but do not mention that $self { … } is allowed, shouldn't be advertised:
1554
- const txt = (path.length > 1 || user._pathHead) ? 'struct' : 'init';
1553
+ const txt = (path.length > 1 || user._columnParent) ? 'struct' : 'init';
1555
1554
  const code = (user.expand) ? '{ ‹expand› }' : '.{ ‹inline› }';
1556
1555
  message( 'def-unexpected-nested-proj', [ location, user ], { '#': txt, code } );
1557
1556
  }
@@ -1618,7 +1617,7 @@ function fns( model ) {
1618
1617
  else if (target._artifact && target._artifact !== user._main && user._main.kind === 'entity') {
1619
1618
  const last = path[path.length - 1];
1620
1619
  warning( 'ref-invalid-backlink', [ last.location, user ], { art: target, id: '$self' },
1621
- // eslint-disable-next-line max-len
1620
+ // eslint-disable-next-line @stylistic/js/max-len
1622
1621
  'The target $(ART) of the association is not the current entity represented by $(ID)' );
1623
1622
  }
1624
1623
  }
@@ -1655,7 +1654,7 @@ function fns( model ) {
1655
1654
 
1656
1655
  function checkOnlyForeignKeyNavigation( user, path, startIndex = 0, msgPrefix = '' ) {
1657
1656
  // has to be run after foreign-key rewrite
1658
- const outer = user._pathHead?._origin;
1657
+ const outer = user._columnParent?._origin;
1659
1658
  let assoc = outer?.foreignKeys &&
1660
1659
  pathStartsWithSelf( { path } ) == null && // not $self or CDS var like $now
1661
1660
  outer;
@@ -1708,11 +1707,11 @@ function fns( model ) {
1708
1707
  std: 'Can follow association $(ART) only to its foreign key references, not to $(NAME)',
1709
1708
  keys: 'Can follow managed association $(ART) only to the keys of its target, not to $(NAME)',
1710
1709
  complete: 'The reference must cover a full foreign key reference of association $(ART)',
1711
- // eslint-disable-next-line max-len
1710
+ // eslint-disable-next-line @stylistic/js/max-len
1712
1711
  'self-std': 'In column ref starting with $(ALIAS), we can follow association $(ART) only to its foreign key references, not to $(NAME)',
1713
- // eslint-disable-next-line max-len
1712
+ // eslint-disable-next-line @stylistic/js/max-len
1714
1713
  'self-keys': 'In column ref starting with $(ALIAS), we can follow managed association $(ART) only to the keys of its target, not to $(NAME)',
1715
- // eslint-disable-next-line max-len
1714
+ // eslint-disable-next-line @stylistic/js/max-len
1716
1715
  'self-complete': 'The column reference starting with $(ALIAS) must cover a full foreign key reference of association $(ART)',
1717
1716
  } );
1718
1717
  // TODO later: mention allowed ones