@sap/cds-compiler 4.0.0 → 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.
- package/CHANGELOG.md +115 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +60 -12
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +30 -2
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +12 -5
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/model/sortViews.js +4 -2
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +56 -42
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
package/lib/compiler/resolve.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
'#': '
|
|
619
|
-
name: user.name.id,
|
|
620
|
-
prop: 'enum',
|
|
657
|
+
'#': 'enumExtra', name: user.name.id, id: name,
|
|
621
658
|
});
|
|
659
|
+
break;
|
|
622
660
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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, '
|
|
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, '
|
|
844
|
+
resolveExpr( query.where, 'where', query );
|
|
805
845
|
if (query.groupBy)
|
|
806
|
-
resolveBy( query.groupBy, '
|
|
807
|
-
resolveExpr( query.having, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1017
|
-
|
|
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
|
|
1020
|
-
|
|
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
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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 =
|
|
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-
|
|
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
|
-
|
|
1336
|
-
|
|
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
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
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') ? '
|
|
1379
|
+
const exp = (expected === 'from') ? 'from-args' : expected;
|
|
1349
1380
|
if (Array.isArray(dict)) {
|
|
1350
|
-
|
|
1351
|
-
|
|
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
|
-
|
|
1364
|
-
|
|
1395
|
+
error( 'expr-undefined-param', [ a.name.location, user ], { art, id: name },
|
|
1396
|
+
'Entity $(ART) has no parameter $(ID)' );
|
|
1365
1397
|
}
|
|
1366
|
-
|
|
1367
|
-
resolveExpr( a, (expected === 'from') ? 'param-only' : exp, user );
|
|
1398
|
+
resolveExpr( a, exp, user );
|
|
1368
1399
|
}
|
|
1369
1400
|
}
|
|
1370
1401
|
}
|