@sap/cds-compiler 4.0.2 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +100 -5
  2. package/bin/cdsc.js +12 -12
  3. package/doc/CHANGELOG_BETA.md +11 -0
  4. package/lib/api/main.js +31 -11
  5. package/lib/api/validate.js +1 -1
  6. package/lib/base/location.js +6 -7
  7. package/lib/base/message-registry.js +84 -38
  8. package/lib/base/messages.js +11 -10
  9. package/lib/base/model.js +6 -2
  10. package/lib/checks/defaultValues.js +6 -6
  11. package/lib/checks/foreignKeys.js +0 -5
  12. package/lib/checks/onConditions.js +17 -12
  13. package/lib/checks/queryNoDbArtifacts.js +132 -72
  14. package/lib/checks/sql-snippets.js +15 -4
  15. package/lib/checks/types.js +3 -3
  16. package/lib/checks/utils.js +1 -1
  17. package/lib/compiler/assert-consistency.js +44 -16
  18. package/lib/compiler/base.js +1 -0
  19. package/lib/compiler/builtins.js +7 -8
  20. package/lib/compiler/checks.js +274 -197
  21. package/lib/compiler/classes.js +62 -0
  22. package/lib/compiler/cycle-detector.js +3 -3
  23. package/lib/compiler/define.js +63 -50
  24. package/lib/compiler/extend.js +38 -20
  25. package/lib/compiler/finalize-parse-cdl.js +2 -1
  26. package/lib/compiler/generate.js +0 -8
  27. package/lib/compiler/index.js +9 -7
  28. package/lib/compiler/kick-start.js +2 -0
  29. package/lib/compiler/populate.js +139 -110
  30. package/lib/compiler/propagator.js +4 -3
  31. package/lib/compiler/resolve.js +157 -126
  32. package/lib/compiler/shared.js +706 -404
  33. package/lib/compiler/tweak-assocs.js +21 -10
  34. package/lib/compiler/utils.js +228 -36
  35. package/lib/edm/annotations/genericTranslation.js +1 -1
  36. package/lib/edm/edm.js +4 -1
  37. package/lib/edm/edmPreprocessor.js +5 -4
  38. package/lib/edm/edmUtils.js +2 -4
  39. package/lib/gen/Dictionary.json +34 -10
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +3987 -3963
  43. package/lib/json/from-csn.js +43 -47
  44. package/lib/json/to-csn.js +11 -11
  45. package/lib/language/antlrParser.js +2 -1
  46. package/lib/language/genericAntlrParser.js +52 -43
  47. package/lib/language/language.g4 +59 -59
  48. package/lib/language/multiLineStringParser.js +2 -0
  49. package/lib/main.d.ts +5 -0
  50. package/lib/model/csnRefs.js +37 -19
  51. package/lib/model/csnUtils.js +20 -16
  52. package/lib/model/revealInternalProperties.js +29 -21
  53. package/lib/modelCompare/compare.js +112 -39
  54. package/lib/modelCompare/utils/filter.js +54 -24
  55. package/lib/optionProcessor.js +6 -6
  56. package/lib/render/manageConstraints.js +20 -17
  57. package/lib/render/toCdl.js +34 -20
  58. package/lib/render/toHdbcds.js +2 -2
  59. package/lib/render/toRename.js +4 -9
  60. package/lib/render/toSql.js +77 -26
  61. package/lib/render/utils/common.js +3 -3
  62. package/lib/render/utils/unique.js +52 -0
  63. package/lib/transform/db/applyTransformations.js +61 -20
  64. package/lib/transform/db/assertUnique.js +7 -8
  65. package/lib/transform/db/associations.js +2 -2
  66. package/lib/transform/db/cdsPersistence.js +8 -8
  67. package/lib/transform/db/expansion.js +17 -21
  68. package/lib/transform/db/flattening.js +23 -23
  69. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  70. package/lib/transform/db/temporal.js +1 -1
  71. package/lib/transform/db/transformExists.js +8 -7
  72. package/lib/transform/db/views.js +73 -33
  73. package/lib/transform/draft/db.js +11 -9
  74. package/lib/transform/draft/odata.js +1 -1
  75. package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
  76. package/lib/transform/forRelationalDB.js +69 -75
  77. package/lib/transform/localized.js +6 -5
  78. package/lib/transform/odata/toFinalBaseType.js +3 -3
  79. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
  80. package/lib/transform/translateAssocsToJoins.js +14 -28
  81. package/package.json +1 -1
  82. package/share/messages/check-proper-type-of.md +1 -1
  83. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  84. package/share/messages/message-explanations.json +1 -1
@@ -62,7 +62,6 @@ const {
62
62
  testExpr,
63
63
  targetMaxNotOne,
64
64
  traverseQueryPost,
65
- traverseExpr,
66
65
  } = require('./utils');
67
66
 
68
67
  const detectCycles = require('./cycle-detector');
@@ -87,14 +86,16 @@ function resolve( model ) {
87
86
  } = model.$messageFunctions;
88
87
  const {
89
88
  resolvePath,
89
+ traverseExpr,
90
90
  createRemainingAnnotateStatements,
91
91
  effectiveType,
92
92
  getOrigin,
93
- resolveType,
93
+ getInheritedProp,
94
94
  resolveTypeArgumentsUnchecked,
95
95
  } = model.$functions;
96
96
  Object.assign( model.$functions, {
97
97
  resolveExpr,
98
+ addForeignKeyNavigations,
98
99
  } );
99
100
 
100
101
  const ignoreSpecifiedElements
@@ -125,14 +126,21 @@ function resolve( model ) {
125
126
  // create “super” ANNOTATE statements for annotations on unknown artifacts:
126
127
  createRemainingAnnotateStatements();
127
128
  // report cyclic dependencies:
128
- detectCycles( model.definitions, ( user, art, location ) => {
129
+ detectCycles( model.definitions, ( user, art, location, semanticLoc ) => {
129
130
  if (location) {
130
- error( 'ref-cyclic', [ location, user ], { art }, {
131
+ model.$assert = null;
132
+ const msg = semanticLoc && 'target';
133
+ error( 'ref-cyclic', [ location, semanticLoc || user ], { art, '#': msg }, {
131
134
  std: 'Illegal circular reference to $(ART)',
132
135
  element: 'Illegal circular reference to element $(MEMBER) of $(ART)',
136
+ target: 'Illegal circular reference to target $(ART)',
133
137
  });
134
138
  }
135
139
  });
140
+ if (model.$assert) {
141
+ error( '$internal-expecting-cyclic', null, {},
142
+ 'INTERNAL: the compiler should have issued an Error[ref-cyclic]' );
143
+ }
136
144
  return model;
137
145
  }
138
146
 
@@ -319,13 +327,32 @@ function resolve( model ) {
319
327
  const parent = art._parent;
320
328
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
321
329
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
322
- if (art.key?.val && !art.key.$inferred && !(allowedInMain && isTopLevelElement)) {
323
- warning( 'unexpected-key', [ art.key.location, art ],
324
- { '#': allowedInMain ? 'sub' : 'std', keyword: 'key' }, {
325
- std: '$(KEYWORD) is only supported for elements in an entity or an aspect',
326
- sub: '$(KEYWORD) is only supported for top-level elements',
327
- });
330
+
331
+ // Check KEY (TODO: make this an extra function)
332
+ const { key } = art;
333
+ if (key?.val && !key.$inferred) {
334
+ // With unmanaged/composition as key, we complain at the `key` keyword, not
335
+ // the `on` condition / the aspect, because the easiest fix would be to
336
+ // simply remove the keyword. Text and message-id are accordingly.
337
+ // This fits nicely with exposing unmanaged/composition with explicit `key`.
338
+ // We do not complain about unmanaged/composition inside struct keys.
339
+ // (Actually, aspect compositions are not supported as sub elements anyway.)
340
+ if (getInheritedProp( art, 'targetAspect' )) {
341
+ error( 'def-invalid-key', [ key.location, art ], { '#': 'composition' } );
342
+ // TODO: test with managed composition exposed with explicit KEY
343
+ }
344
+ else if (art.target && getInheritedProp( art, 'on' )) {
345
+ error( 'def-invalid-key', [ key.location, art ], { '#': 'unmanaged' } );
346
+ }
347
+ else if (!allowedInMain || !isTopLevelElement) {
348
+ warning( 'def-unsupported-key', [ art.key.location, art ],
349
+ { '#': allowedInMain ? 'sub' : 'std', keyword: 'key' }, {
350
+ std: '$(KEYWORD) is only supported for elements in an entity or an aspect',
351
+ sub: '$(KEYWORD) is only supported for top-level elements',
352
+ } );
353
+ }
328
354
  }
355
+
329
356
  if (art.targetAspect && !(allowedInMain && isTopLevelElement)) {
330
357
  message( 'type-managed-composition', [ art.targetAspect.location, art ],
331
358
  { '#': allowedInMain ? 'sub' : 'std' } );
@@ -426,14 +453,19 @@ function resolve( model ) {
426
453
  forEachGeneric( obj.targetAspect, 'elements', elem => dependsOnSilent( art, elem ) );
427
454
  }
428
455
  if (obj.foreignKeys) { // silent dependencies
429
- forEachGeneric( obj, 'foreignKeys', (elem) => {
430
- dependsOnSilent( art, elem );
431
- } );
456
+ // Avoid strange ref-cyclic if managed composition is key (check comes later)
457
+ // TODO: the following is already done by addimplicitforeignkeys()!
458
+ if (obj.$inferred !== 'aspect-composition') {
459
+ forEachGeneric( obj, 'foreignKeys', (elem) => {
460
+ dependsOnSilent( art, elem );
461
+ } );
462
+ }
432
463
  addForeignKeyNavigations( art );
433
464
  }
434
465
 
435
466
  resolveExpr( art.default, 'default', art );
436
- resolveExpr( art.value, (art.$syntax === 'calc' ? 'calc' : 'expr'), art );
467
+ // TODO: distinguish not by $syntax (it is semantics), but whether in query
468
+ resolveExpr( art.value, (art.$syntax === 'calc' ? 'calc' : 'column'), art );
437
469
  if (art.type?.$inferred === 'cast')
438
470
  inferTypePropertiesFromCast( art );
439
471
  if (art.value) {
@@ -458,6 +490,8 @@ function resolve( model ) {
458
490
  /**
459
491
  * Check whether the signature of the specified element matches that of the inferred one.
460
492
  *
493
+ * TODO: resolveRefs() is already too long → do not add sub functions
494
+ *
461
495
  * TODO:
462
496
  * - This function has a lot of quite similar code blocks; it should be refactored to
463
497
  * combine them.
@@ -529,7 +563,7 @@ function resolve( model ) {
529
563
  iKeys = iKeys._origin;
530
564
  }
531
565
  iKeys = Object.keys(iKeys.foreignKeys || {});
532
- if (sKeys.length !== iKeys.length || sKeys.some(key => !iKeys.includes(key))) {
566
+ if (sKeys.length !== iKeys.length || sKeys.some( fkey => !iKeys.includes(fkey))) {
533
567
  error('query-mismatched-element', [
534
568
  specifiedElement.foreignKeys.location || specifiedElement.location, user,
535
569
  ], {
@@ -609,27 +643,33 @@ function resolve( model ) {
609
643
  }
610
644
  }
611
645
 
612
- if (specifiedElement.enum) {
613
- const iEnum = inferredElement.enum || inferredElement.value?.enum;
614
- forEachGeneric(specifiedElement, 'enum', (sVal, name) => {
615
- const iVal = iEnum[name];
616
- if (!iVal) {
646
+ if (specifiedElement.enum && !specifiedElement.$expand) {
647
+ // TODO: ".value" is necessary due to recompilation: The compiler does not copy
648
+ // "enum" out of ".value", i.e. casts, only "type", changing the _effectiveType.
649
+ const iEnumValues = inferredElement.enum || inferredElement.value?.enum;
650
+ const sEnumValues = specifiedElement.enum;
651
+ for (const name in specifiedElement.enum) {
652
+ // TODO: See TODO above; issue is cast()
653
+ const sEnumEntry = sEnumValues[name];
654
+ const iEnumEntry = iEnumValues[name]?._effectiveType || iEnumValues[name];
655
+ if (!iEnumEntry) {
617
656
  error('query-mismatched-element', [ specifiedElement.location, user ], {
618
- '#': 'std',
619
- name: user.name.id,
620
- prop: 'enum',
657
+ '#': 'enumExtra', name: user.name.id, id: name,
621
658
  });
659
+ break;
622
660
  }
623
-
624
- // TODO: Get $expanded enum values
625
- /* if (iVal.value?.val !== sVal.value?.val || iVal.value?.['#'] !== sVal.value?.['#']) {
626
- error('query-mismatched-element', [ specifiedElement.location, user ], {
627
- '#': 'std',
628
- name: user.name.id,
629
- prop: 'enum',
630
- });
631
- } */
632
- });
661
+ else {
662
+ // We allow implicit `val: "<name>"`.
663
+ const iVal = iEnumEntry.value?.val || iEnumEntry.value?.['#'] || name;
664
+ const sVal = sEnumEntry.value?.val || sEnumEntry.value?.['#'] || name;
665
+ if (iVal !== sVal) {
666
+ error('query-mismatched-element', [ specifiedElement.location, user ], {
667
+ '#': 'enumVal', name: user.name.id, id: name,
668
+ });
669
+ break;
670
+ }
671
+ }
672
+ }
633
673
  }
634
674
  }
635
675
 
@@ -793,7 +833,7 @@ function resolve( model ) {
793
833
  } );
794
834
  // if (!query.$inlines) console.log('RQ:',query)
795
835
  for (const col of query.$inlines)
796
- resolveExpr( col.value, 'expr', col );
836
+ resolveExpr( col.value, 'column', col );
797
837
  // for (const col of query.$inlines)
798
838
  // if (!col.value.path) throw new CompilerAssertion(col.name.element)
799
839
  if (query !== query._main._leadingQuery) // will be done later
@@ -801,17 +841,17 @@ function resolve( model ) {
801
841
  if (query.from)
802
842
  resolveJoinOn( query.from );
803
843
  if (query.where)
804
- resolveExpr( query.where, 'expr', query ); // TODO: extra 'where'?
844
+ resolveExpr( query.where, 'where', query );
805
845
  if (query.groupBy)
806
- resolveBy( query.groupBy, 'expr', 'expr' ); // TODO: extra 'groupBy'?
807
- resolveExpr( query.having, 'expr', query ); // TODO: extra 'having' or 'where'?
846
+ resolveBy( query.groupBy, 'groupBy', 'groupBy' );
847
+ resolveExpr( query.having, 'having', query );
808
848
  if (query.$orderBy) // ORDER BY from UNION:
809
849
  // TODO clarify: can I access the tab alias of outer queries? If not:
810
850
  // 4th arg query._main instead query._parent.
811
- resolveBy( query.$orderBy, 'order-by-set-ref', 'order-by-set-expr' );
851
+ resolveBy( query.$orderBy, 'orderBy-set-ref', 'orderBy-set-expr' );
812
852
  if (query.orderBy) { // ORDER BY
813
853
  // search in `query.elements` after having checked table aliases of the current query
814
- resolveBy( query.orderBy, 'order-by-ref', 'order-by-expr' );
854
+ resolveBy( query.orderBy, 'orderBy-ref', 'orderBy-expr' );
815
855
  // TODO: disallow resulting element ref if in expression!
816
856
  // Necessary to check it in the compiler as it might work with other semantics on DB!
817
857
  // (we could downgrade it to a warning if name is equal to unique source element name)
@@ -824,7 +864,7 @@ function resolve( model ) {
824
864
  for (const j of join.args)
825
865
  resolveJoinOn( j );
826
866
  if (join.on)
827
- resolveExpr( join.on, 'joinOn', query );
867
+ resolveExpr( join.on, 'join-on', query );
828
868
  // TODO: check restrictions according to join "query"
829
869
  }
830
870
  }
@@ -1004,20 +1044,57 @@ function resolve( model ) {
1004
1044
  setArtifactLink( key.targetElement.path[0], elem );
1005
1045
  setLink( key, '_effectiveType', effectiveType(elem) );
1006
1046
  dependsOn(key, elem, location);
1007
- dependsOnSilent(art, key);
1047
+ // TODO TMP: instead, make managed composition of aspects and unmanaged
1048
+ // assocs not depend on their `on` condition (empty `_deps` after resolve)
1049
+ if (art.$inferred !== 'aspect-composition')
1050
+ dependsOnSilent( art, key );
1008
1051
  }
1009
1052
  });
1010
1053
  obj.foreignKeys[$inferred] = 'keys';
1011
1054
  }
1012
1055
 
1013
- function addForeignKeyNavigations( art ) {
1056
+ /**
1057
+ * Add reference tree from foreign key reference back to foreign key of association.
1058
+ *
1059
+ * For `type T: Association to Target { foo as bar, elem.sub }`, this function adds:
1060
+ *
1061
+ * '$keysNavigation': {
1062
+ * foo: { _artifact: 'type:“T”/key:“bar”' },
1063
+ * elem: {
1064
+ * '$keysNavigation': { sub: { _artifact: 'type:“T”/key:“sub”' } }
1065
+ * }
1066
+ *
1067
+ * This function complains if two foreign keys point to the same target element
1068
+ * (`Association to Target { foo as bar, foo }`) or overlapping target elements
1069
+ * (`Association to Target { elem.sub, elem }`). In `resolvePath`, the compiler
1070
+ * already forbids to follow associations in foreign key refs.
1071
+ *
1072
+ * This ref tree could also be used in a core-compiler check which is now part
1073
+ * of to.sql: refs in the `on` condition of unmanaged associations cannot follow
1074
+ * associations other to foreign key refs.
1075
+ *
1076
+ * This ref tree is only created for originally defined managed associations
1077
+ * (including those created by the compiler, like the `up_` association), not
1078
+ * for derived association like for `type DerivedT: T`, or exposed ones.
1079
+ */
1080
+ function addForeignKeyNavigations( art, silent = false ) {
1014
1081
  art.$keysNavigation = Object.create(null);
1082
+ const keys = [];
1083
+ // Basically sort foreign keys according to length of target element ref.
1084
+ // This way, we complain about ref to sub element (`elem.sub`) even if it
1085
+ // comes earlier than the ref to structure element (`elem`).
1015
1086
  forEachGeneric( art, 'foreignKeys', ( key ) => {
1016
- if (!key.targetElement || !key.targetElement.path)
1017
- return;
1087
+ const path = key.targetElement?.path;
1088
+ if (path) {
1089
+ const arr = keys[path.length] || (keys[path.length] = []);
1090
+ arr.push( key );
1091
+ }
1092
+ });
1093
+ for (const key of keys.flat()) {
1018
1094
  let dict = art.$keysNavigation;
1019
- const last = key.targetElement.path[key.targetElement.path.length - 1];
1020
- for (const item of key.targetElement.path) {
1095
+ const { path } = key.targetElement;
1096
+ const last = path[path.length - 1];
1097
+ for (const item of path) {
1021
1098
  let nav = dict[item.id];
1022
1099
  if (!nav) {
1023
1100
  nav = {};
@@ -1028,13 +1105,21 @@ function resolve( model ) {
1028
1105
  nav.$keysNavigation = Object.create(null);
1029
1106
  }
1030
1107
  else if (item === last || nav._artifact) {
1031
- error( 'duplicate-key-ref', [ item.location, key ], {},
1032
- 'The same target reference has already been used in a key definition' );
1033
- return;
1108
+ if (silent)
1109
+ break;
1110
+ const name = nav._artifact?.name.id;
1111
+ const text = (item !== last) ? 'sub' : 'std';
1112
+ error( 'duplicate-key-ref', [ item.location, key ], { '#': text, name }, {
1113
+ std: 'Foreign key $(NAME) already refers to the same target element',
1114
+ // eslint-disable-next-line max-len
1115
+ sub: 'Foreign key $(NAME) already refers to the target element whose sub element is again referred to here',
1116
+ // TODO: please add ideas for a better text, e.g. to (closed) PR #11325
1117
+ } );
1118
+ break;
1034
1119
  }
1035
1120
  dict = nav.$keysNavigation;
1036
1121
  }
1037
- } );
1122
+ }
1038
1123
  }
1039
1124
 
1040
1125
  // TODO: add this somehow to tweak-assocs.js ?
@@ -1067,6 +1152,8 @@ function resolve( model ) {
1067
1152
  }
1068
1153
  }
1069
1154
  const origTarget = origType.target._artifact;
1155
+ // console.log(require('../model/revealInternalProperties').ref(elem),
1156
+ // !!origTarget,!!origType._effectiveType,!!origType.target)
1070
1157
  if (!origTarget || !target)
1071
1158
  return;
1072
1159
 
@@ -1194,65 +1281,9 @@ function resolve( model ) {
1194
1281
 
1195
1282
  // Resolve the type and its arguments if applicable.
1196
1283
  function resolveTypeExpr( art, user ) {
1197
- const typeArt = resolveType( art.type, user );
1198
- if (typeArt) {
1284
+ const typeArt = resolvePath( art.type, 'type', user );
1285
+ if (typeArt)
1199
1286
  resolveTypeArgumentsUnchecked( art, typeArt, user );
1200
- checkTypeArguments( art, typeArt );
1201
- }
1202
- }
1203
-
1204
- /**
1205
- * Check the type arguments on `artWithType`.
1206
- * If the effective type is an array or structured type, an error is emitted.
1207
- */
1208
- function checkTypeArguments( artWithType, typeArt ) {
1209
- // Note: `_effectiveType` point to `artWithType` itself, if it is an enum type,
1210
- // descend to the origin in this case.
1211
- // TODO: this function is not complete(!): parallel `elements` and `length`, … - rework function
1212
- // TODO: check relationship with resolveTypeArgumentsUnchecked()
1213
- let effectiveTypeArt = effectiveType( typeArt );
1214
- while (effectiveTypeArt?.enum)
1215
- effectiveTypeArt = effectiveType( getOrigin( effectiveTypeArt ) );
1216
- if (!effectiveTypeArt)
1217
- return; // e.g. illegal definition references, cycles, ...
1218
-
1219
- const params = effectiveTypeArt.parameters &&
1220
- effectiveTypeArt.parameters.map(p => p.name || p) || [];
1221
-
1222
- for (const param of typeParameters.list) {
1223
- if (artWithType[param] !== undefined) {
1224
- if (!params.includes(param)) {
1225
- // Whether the type ref itself is a builtin or a custom type with a builtin as base.
1226
- const type = getOrigin(artWithType);
1227
-
1228
- let variant;
1229
- if (type.builtin)
1230
- // `.type` is already a builtin: use a nicer message.
1231
- variant = 'builtin';
1232
- else if (effectiveTypeArt.builtin)
1233
- // base type is a builtin, i.e. a scalar
1234
- variant = 'type';
1235
- else
1236
- // effectiveType is not a builtin -> array or structured
1237
- variant = 'non-scalar';
1238
-
1239
- // console.log(typeArt.name,artWithType.name,effectiveTypeArt.name)
1240
- error('type-unexpected-argument', [ artWithType[param].location, artWithType ], {
1241
- '#': variant, prop: param, art: artWithType.type, type: effectiveTypeArt,
1242
- });
1243
- break; // Avoid spam: Only emit the first error.
1244
- }
1245
- else if (!typeParameters.expectedLiteralsFor[param].includes(artWithType[param].literal)) {
1246
- error('type-unexpected-argument', [ artWithType[param].location, artWithType ], {
1247
- '#': 'incorrect-type',
1248
- prop: param,
1249
- code: artWithType[param].literal,
1250
- names: typeParameters.expectedLiteralsFor[param],
1251
- });
1252
- break; // Avoid spam: Only emit the first error.
1253
- }
1254
- }
1255
- }
1256
1287
  }
1257
1288
 
1258
1289
  function resolveExpr( expr, exprCtx, user ) {
@@ -1264,6 +1295,7 @@ function resolve( model ) {
1264
1295
  resolveTypeExpr( expr, user._user || user );
1265
1296
 
1266
1297
  if (expr.path) {
1298
+ // TODO: re-think this $expected: 'exists' thing
1267
1299
  if (expr.$expected === 'exists') {
1268
1300
  error( 'expr-unexpected-exists', [ expr.location, user ], {},
1269
1301
  'An EXISTS predicate is not expected here' );
@@ -1317,7 +1349,7 @@ function resolve( model ) {
1317
1349
  if (expected === 'from')
1318
1350
  variant = 'from';
1319
1351
  // XSN TODO: filter$location including […]
1320
- message( 'expr-no-filter', [ location, user ], { '#': variant }, {
1352
+ message( 'expr-unexpected-filter', [ location, user ], { '#': variant }, {
1321
1353
  std: 'A filter can only be provided when navigating along associations',
1322
1354
  // to help users for `… from E:toF { toF[…].x }`
1323
1355
  // eslint-disable-next-line max-len
@@ -1332,23 +1364,23 @@ function resolve( model ) {
1332
1364
  let first = dict[Object.keys(dict)[0]];
1333
1365
  if (Array.isArray(first))
1334
1366
  first = first[0];
1335
- message( 'args-no-params',
1336
- [ dict[$location] ||
1337
- dictLocation( dict, first && first.name && first.name.location || stepLocation),
1367
+ error( 'expr-unexpected-argument',
1368
+ [ dict[$location] || dictLocation( dict, first?.name?.location || stepLocation ),
1338
1369
  user ],
1339
- { art, '#': (entity ? 'entity' : expected ) },
1340
- {
1341
- std: 'Parameters can only be provided when navigating along associations',
1342
- from: 'Parameters can only be provided for the source entity or associations',
1343
- // or extra message id for entity?
1344
- entity: 'Entity $(ART) has no parameters',
1345
- } );
1370
+ { art, '#': (entity ? 'entity' : expected ) },
1371
+ {
1372
+ std: 'Parameters can only be provided when navigating along associations',
1373
+ from: 'Parameters can only be provided for the source entity or associations',
1374
+ // or extra message id for entity?
1375
+ entity: 'Unexpected arguments for entity $(ART) without parameters',
1376
+ } );
1346
1377
  return;
1347
1378
  }
1348
- const exp = (expected === 'from') ? 'expr' : expected;
1379
+ const exp = (expected === 'from') ? 'from-args' : expected;
1349
1380
  if (Array.isArray(dict)) {
1350
- message( 'args-expected-named', [ dict[0] && dict[0].location || stepLocation, user ], {},
1351
- 'Named parameters must be provided for the entity' );
1381
+ const loc = [ dict[0] && dict[0].location || stepLocation, user ];
1382
+ error( 'expr-expected-named-argument', loc, {},
1383
+ 'Expected named parameters for the entity' );
1352
1384
  for (const a of dict)
1353
1385
  resolveExpr( a, exp, user );
1354
1386
  return;
@@ -1360,11 +1392,10 @@ function resolve( model ) {
1360
1392
  for (const a of Array.isArray(arg) ? arg : [ arg ]) {
1361
1393
  setArtifactLink( a.name, param );
1362
1394
  if (!param) {
1363
- message( 'args-undefined-param', [ a.name.location, user ], { art, id: name },
1364
- 'Entity $(ART) has no parameter $(ID)' );
1395
+ error( 'expr-undefined-param', [ a.name.location, user ], { art, id: name },
1396
+ 'Entity $(ART) has no parameter $(ID)' );
1365
1397
  }
1366
- // TODO: Also for other parameters?
1367
- resolveExpr( a, (expected === 'from') ? 'param-only' : exp, user );
1398
+ resolveExpr( a, exp, user );
1368
1399
  }
1369
1400
  }
1370
1401
  }