@sap/cds-compiler 4.1.2 → 4.2.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 +101 -1
- package/bin/cdsc.js +6 -3
- package/doc/CHANGELOG_BETA.md +5 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +2 -2
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +24 -24
- package/lib/base/message-registry.js +41 -6
- package/lib/base/messages.js +7 -0
- package/lib/base/model.js +37 -8
- package/lib/checks/elements.js +11 -10
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -3
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/utils.js +3 -2
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +27 -24
- package/lib/compiler/base.js +6 -2
- package/lib/compiler/builtins.js +34 -34
- package/lib/compiler/checks.js +179 -208
- package/lib/compiler/classes.js +2 -2
- package/lib/compiler/cycle-detector.js +6 -6
- package/lib/compiler/define.js +66 -45
- package/lib/compiler/extend.js +81 -72
- package/lib/compiler/finalize-parse-cdl.js +26 -26
- package/lib/compiler/generate.js +61 -45
- package/lib/compiler/index.js +47 -49
- package/lib/compiler/kick-start.js +8 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +42 -35
- package/lib/compiler/propagator.js +6 -6
- package/lib/compiler/resolve.js +170 -126
- package/lib/compiler/shared.js +122 -45
- package/lib/compiler/tweak-assocs.js +93 -40
- package/lib/compiler/utils.js +15 -12
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +678 -772
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +686 -646
- package/lib/edm/edmUtils.js +277 -296
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1253 -1276
- package/lib/json/from-csn.js +34 -4
- package/lib/json/to-csn.js +4 -4
- package/lib/language/language.g4 +2 -5
- package/lib/main.d.ts +61 -1
- package/lib/model/csnUtils.js +31 -2
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/modelCompare/compare.js +37 -2
- package/lib/modelCompare/utils/filter.js +1 -1
- package/lib/optionProcessor.js +15 -3
- package/lib/render/toCdl.js +30 -4
- package/lib/render/toSql.js +5 -9
- package/lib/render/utils/common.js +8 -6
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +121 -47
- package/lib/transform/db/flattening.js +75 -7
- package/lib/transform/forOdata.js +4 -1
- package/lib/transform/forRelationalDB.js +80 -62
- package/lib/transform/localized.js +91 -54
- package/lib/transform/transformUtils.js +9 -10
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
package/lib/compiler/extend.js
CHANGED
|
@@ -25,7 +25,7 @@ const layers = require('./moduleLayers');
|
|
|
25
25
|
const { CompilerAssertion } = require('../base/error');
|
|
26
26
|
const { CsnLocation } = require('./classes');
|
|
27
27
|
|
|
28
|
-
const $location = Symbol.for('cds.$location');
|
|
28
|
+
const $location = Symbol.for( 'cds.$location' );
|
|
29
29
|
|
|
30
30
|
// attach stupid location - TODO: remove in v5
|
|
31
31
|
const genLocation = new CsnLocation( '' );
|
|
@@ -53,10 +53,10 @@ function extend( model ) {
|
|
|
53
53
|
applyIncludes, // TODO: re-check
|
|
54
54
|
} );
|
|
55
55
|
|
|
56
|
-
const includesNonShadowedFirst = isDeprecatedEnabled(model.options, 'includesNonShadowedFirst');
|
|
56
|
+
const includesNonShadowedFirst = isDeprecatedEnabled( model.options, 'includesNonShadowedFirst' );
|
|
57
57
|
|
|
58
58
|
sortModelSources();
|
|
59
|
-
const extensionsDict = Object.create(null); // TODO TMP
|
|
59
|
+
const extensionsDict = Object.create( null ); // TODO TMP
|
|
60
60
|
forEachDefinition( model, tagIncludes ); // TODO TMP
|
|
61
61
|
|
|
62
62
|
forEachDefinition( model, extendArtifactBefore );
|
|
@@ -134,46 +134,32 @@ function extend( model ) {
|
|
|
134
134
|
delete art.$typeExts;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
if (art.kind === 'annotate' && !art.returns && extensionsMap.returns)
|
|
137
|
+
if (art.kind === 'annotate' && !art.returns && extensionsMap.returns && !art._parent?.returns)
|
|
138
138
|
annotateCreate( art, '', art, 'returns' );
|
|
139
139
|
|
|
140
|
+
moveDictExtensions( art, extensionsMap, 'actions' );
|
|
140
141
|
moveDictExtensions( art, extensionsMap, 'params' );
|
|
141
142
|
moveReturnsExtensions( art, extensionsMap );
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
143
|
+
|
|
144
|
+
if (art.returns) {
|
|
145
|
+
pushToDict( art.returns, '_extensions', ...extensionsMap.elements || [] );
|
|
146
|
+
pushToDict( art.returns, '_extensions', ...extensionsMap.enum || [] );
|
|
147
|
+
if (art.kind !== 'annotate') {
|
|
145
148
|
extendHandleReturns( extensionsMap.elements, art );
|
|
146
149
|
extendHandleReturns( extensionsMap.enum, art );
|
|
150
|
+
return;
|
|
147
151
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
}
|
|
153
|
+
const sub = art.items || art.targetAspect?.elements && art.targetAspect;
|
|
154
|
+
if (sub) {
|
|
155
|
+
pushToDict( sub, '_extensions', ...extensionsMap.elements || [] );
|
|
156
|
+
pushToDict( sub, '_extensions', ...extensionsMap.enum || [] );
|
|
151
157
|
}
|
|
152
158
|
else {
|
|
153
159
|
moveDictExtensions( art, extensionsMap,
|
|
154
160
|
(art.enum && art.kind !== 'annotate' ? 'enum' : 'elements'), 'elements' );
|
|
155
161
|
moveDictExtensions( art, extensionsMap, 'enum' );
|
|
156
162
|
}
|
|
157
|
-
moveDictExtensions( art, extensionsMap, 'actions' );
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* FIXME: Remove this workaround. This workaround avoids endless recursion for annotate statements
|
|
162
|
-
* that have both "returns" and "elements". The endless recursion happens due to the
|
|
163
|
-
* pushDict on `sub`. The `elements` extensions will point to the same extension on which
|
|
164
|
-
* `returns` exist.
|
|
165
|
-
*
|
|
166
|
-
* @param extensionsMap
|
|
167
|
-
* @param {string} prop
|
|
168
|
-
* @return {XSN.Extension[]}
|
|
169
|
-
*/
|
|
170
|
-
function avoidRecursiveReturns( extensionsMap, prop ) {
|
|
171
|
-
if (!extensionsMap[prop])
|
|
172
|
-
return [];
|
|
173
|
-
const exts = [];
|
|
174
|
-
for (const ext of extensionsMap[prop])
|
|
175
|
-
exts.push({ ...ext, returns: undefined });
|
|
176
|
-
return exts;
|
|
177
163
|
}
|
|
178
164
|
|
|
179
165
|
/**
|
|
@@ -224,7 +210,7 @@ function extend( model ) {
|
|
|
224
210
|
annotate: 'There is no artifact $(ART), use $(CODE) instead',
|
|
225
211
|
// do not mention 'extend context', that is not in CAPire
|
|
226
212
|
service: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) or $(KEYWORD) instead',
|
|
227
|
-
});
|
|
213
|
+
} );
|
|
228
214
|
}
|
|
229
215
|
// TODO: Use similar checks for EXTEND ENTITY etc - 'ext-ignoring-kind'
|
|
230
216
|
}
|
|
@@ -234,7 +220,7 @@ function extend( model ) {
|
|
|
234
220
|
// TODO: if extensions has more than one of returns,items,elements,enum, delete all those props
|
|
235
221
|
function transformArtifactExtensions( art ) {
|
|
236
222
|
const hasOnlySubExtensions = art._outer; // items, anonymous aspects
|
|
237
|
-
const dict = Object.create(null);
|
|
223
|
+
const dict = Object.create( null );
|
|
238
224
|
for (const ext of art._extensions) {
|
|
239
225
|
for (const prop in ext) {
|
|
240
226
|
if (ext[prop] === undefined) // deleted property
|
|
@@ -435,7 +421,7 @@ function extend( model ) {
|
|
|
435
421
|
while (prevPos < previousValue.length) {
|
|
436
422
|
const prevItem = previousValue[prevPos++];
|
|
437
423
|
result.push( prevItem );
|
|
438
|
-
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo)) {
|
|
424
|
+
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo )) {
|
|
439
425
|
upToSpec = false;
|
|
440
426
|
break;
|
|
441
427
|
}
|
|
@@ -606,32 +592,41 @@ function extend( model ) {
|
|
|
606
592
|
continue; // definitions inside extend, already handled
|
|
607
593
|
dictCheck = dictCheck && checkRemainingMemberExtensions( art, elemExt, artProp, name );
|
|
608
594
|
const elem = artDict[name] || annotateFor( art, extProp, name );
|
|
609
|
-
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null) );
|
|
595
|
+
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null ) );
|
|
610
596
|
pushToDict( elem, '_extensions', elemExt );
|
|
611
597
|
}
|
|
612
598
|
}
|
|
613
599
|
}
|
|
614
600
|
|
|
615
601
|
function moveReturnsExtensions( art, extensionsMap ) {
|
|
616
|
-
|
|
602
|
+
const extensions = extensionsMap.returns;
|
|
603
|
+
if (!extensions)
|
|
617
604
|
return;
|
|
605
|
+
const artReturns = art.returns;
|
|
606
|
+
let extReturns = artReturns;
|
|
607
|
+
const isAction = art.kind === 'action' || art.kind === 'function';
|
|
618
608
|
|
|
619
|
-
for (const ext of
|
|
620
|
-
if (!
|
|
621
|
-
const variant = art.kind === 'action' || art.kind === 'function' ? art.kind : 'std';
|
|
609
|
+
for (const ext of extensions) {
|
|
610
|
+
if (!artReturns && art.kind !== 'annotate') {
|
|
622
611
|
warning( 'ext-unexpected-returns', [ ext.returns.location, ext ], {
|
|
623
|
-
'#':
|
|
612
|
+
'#': (isAction ? art.kind : 'std'), keyword: 'returns',
|
|
624
613
|
}, {
|
|
625
614
|
std: 'Unexpected $(KEYWORD); only actions and functions have return parameters',
|
|
626
615
|
action: 'Unexpected $(KEYWORD) for action without return parameter',
|
|
627
616
|
// function without `returns` can happen via CSN input!
|
|
628
617
|
function: 'Unexpected $(KEYWORD) for function without return parameter',
|
|
629
|
-
});
|
|
618
|
+
} );
|
|
619
|
+
// Do not put completely wrong returns into a “super annotate” statement;
|
|
620
|
+
// this could induce consequential errors with [..., …]:
|
|
621
|
+
if (!isAction)
|
|
622
|
+
continue; // do not put into 'extensions'
|
|
623
|
+
// add to 'extensions' for action/function without returns:
|
|
624
|
+
extReturns ??= annotateFor( art, 'params', '' );
|
|
625
|
+
}
|
|
626
|
+
if (extReturns) {
|
|
627
|
+
setLink( ext.name, '_artifact', (isAction ? artReturns : null ) );
|
|
628
|
+
pushToDict( extReturns, '_extensions', ext.returns );
|
|
630
629
|
}
|
|
631
|
-
// If `!art.returns`, then it could be CSN from SQL, where actions are replaced by dummies.
|
|
632
|
-
const elem = art.returns || annotateFor( art, 'params', '' );
|
|
633
|
-
setLink( ext.name, '_artifact', (elem.kind !== 'annotate' ? elem : null) );
|
|
634
|
-
pushToDict(elem, '_extensions', ext.returns);
|
|
635
630
|
}
|
|
636
631
|
}
|
|
637
632
|
|
|
@@ -720,7 +715,7 @@ function extend( model ) {
|
|
|
720
715
|
{ '#': (parent._effectiveType?.kind === 'entity') ? 'entity' : 'std' }, {
|
|
721
716
|
std: 'Elements only exist in entities, types or typed constructs',
|
|
722
717
|
entity: 'Elements of entity types can\'t be annotated',
|
|
723
|
-
});
|
|
718
|
+
} );
|
|
724
719
|
break;
|
|
725
720
|
case 'params':
|
|
726
721
|
warning( 'anno-unexpected-params', [ location, ext._parent ], {},
|
|
@@ -808,8 +803,6 @@ function extend( model ) {
|
|
|
808
803
|
return;
|
|
809
804
|
if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
|
|
810
805
|
return;
|
|
811
|
-
// else if (ext.kind === 'extend') { // TODO v4 - add error
|
|
812
|
-
// }
|
|
813
806
|
if (art?.kind === 'namespace') {
|
|
814
807
|
// TODO: not at all different to having no definition
|
|
815
808
|
info( 'anno-namespace', [ ext.name.location, ext ], {}, // TODO: better location?
|
|
@@ -834,13 +827,13 @@ function extend( model ) {
|
|
|
834
827
|
}
|
|
835
828
|
else if (!construct.returns &&
|
|
836
829
|
(art.kind === 'action' || art.kind === 'function') && construct.elements) {
|
|
837
|
-
warning('ext-expecting-returns', [ construct.name.location, construct ], {
|
|
830
|
+
warning( 'ext-expecting-returns', [ construct.name.location, construct ], {
|
|
838
831
|
'#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
|
|
839
832
|
}, {
|
|
840
833
|
std: 'Expected $(CODE)', // unused variant
|
|
841
834
|
action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
|
|
842
835
|
function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
|
|
843
|
-
});
|
|
836
|
+
} );
|
|
844
837
|
}
|
|
845
838
|
}
|
|
846
839
|
}
|
|
@@ -883,7 +876,7 @@ function extend( model ) {
|
|
|
883
876
|
*/
|
|
884
877
|
function extendArtifact( extensions, art, noIncludes = false ) {
|
|
885
878
|
if (!noIncludes && !(canApplyIncludes( art, art ) &&
|
|
886
|
-
extensions.every( ext => canApplyIncludes(ext, art) )))
|
|
879
|
+
extensions.every( ext => canApplyIncludes( ext, art ) )))
|
|
887
880
|
return false;
|
|
888
881
|
if (Array.isArray( noIncludes )) {
|
|
889
882
|
canApplyIncludes( art, art, noIncludes );
|
|
@@ -891,7 +884,7 @@ function extend( model ) {
|
|
|
891
884
|
}
|
|
892
885
|
else if (!noIncludes &&
|
|
893
886
|
!(canApplyIncludes( art, art ) &&
|
|
894
|
-
extensions.every( ext => canApplyIncludes( ext, art) ))) {
|
|
887
|
+
extensions.every( ext => canApplyIncludes( ext, art ) ))) {
|
|
895
888
|
// console.log( 'FALSE:',art.name, extensions.map( e => e.name ) )
|
|
896
889
|
return false;
|
|
897
890
|
}
|
|
@@ -942,7 +935,7 @@ function extend( model ) {
|
|
|
942
935
|
// includes = ['Base1',3,'Base2']
|
|
943
936
|
// where 3 means adding the next 3 elements before applying include 'Base2'
|
|
944
937
|
if (art.includes)
|
|
945
|
-
art.includes.push(...ext.includes);
|
|
938
|
+
art.includes.push( ...ext.includes );
|
|
946
939
|
else
|
|
947
940
|
art.includes = [ ...ext.includes ];
|
|
948
941
|
applyIncludes( ext, art );
|
|
@@ -951,7 +944,7 @@ function extend( model ) {
|
|
|
951
944
|
checkAnnotate( ext, art );
|
|
952
945
|
// TODO: do we allow to add elements with array of {...}? If yes, adapt
|
|
953
946
|
initMembers( ext, art, ext._block ); // might set _extend, _annotate
|
|
954
|
-
dependsOnSilent(art, ext); // art depends silently on ext (inverse to normal dep!)
|
|
947
|
+
dependsOnSilent( art, ext ); // art depends silently on ext (inverse to normal dep!)
|
|
955
948
|
}
|
|
956
949
|
for (const name in ext.elements) {
|
|
957
950
|
const elem = ext.elements[name];
|
|
@@ -965,7 +958,7 @@ function extend( model ) {
|
|
|
965
958
|
reportUnstableExtensions( elemExtensions );
|
|
966
959
|
|
|
967
960
|
// This whole function will be removed with a next change - no need to have nice code here:
|
|
968
|
-
const dict = Object.create(null);
|
|
961
|
+
const dict = Object.create( null );
|
|
969
962
|
// actions cannot be extended anyway. TODO: there should be a message
|
|
970
963
|
// (possible with CSN input), but that was missing before this change, too.
|
|
971
964
|
for (const e of extensions) {
|
|
@@ -1007,7 +1000,8 @@ function extend( model ) {
|
|
|
1007
1000
|
let lastExt = null;
|
|
1008
1001
|
let open = []; // the "highest" layers
|
|
1009
1002
|
for (const ext of extensions) {
|
|
1010
|
-
const extLayer = layers.layer( ext ) ||
|
|
1003
|
+
const extLayer = layers.layer( ext ) ||
|
|
1004
|
+
{ realname: '', _layerExtends: Object.create( null ) };
|
|
1011
1005
|
if (!open.length) {
|
|
1012
1006
|
lastExt = ext;
|
|
1013
1007
|
open = [ extLayer.realname ];
|
|
@@ -1055,7 +1049,7 @@ function extend( model ) {
|
|
|
1055
1049
|
element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
|
|
1056
1050
|
action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
|
|
1057
1051
|
} );
|
|
1058
|
-
attachAndEmitValidNames(msg, validDict);
|
|
1052
|
+
attachAndEmitValidNames( msg, validDict );
|
|
1059
1053
|
}
|
|
1060
1054
|
}
|
|
1061
1055
|
|
|
@@ -1097,7 +1091,10 @@ function extend( model ) {
|
|
|
1097
1091
|
* Apply all includes of `ext` on `ext`. Checks that `art` allows includes.
|
|
1098
1092
|
* If `ext === art`, then includes of the artifact itself are applied.
|
|
1099
1093
|
* If `ext !== art`, applies includes on the extensions, not artifact.
|
|
1100
|
-
* Sets `
|
|
1094
|
+
* Sets `_ancestors` links on `art`.
|
|
1095
|
+
*
|
|
1096
|
+
* TODO: try to set `_ancestors` only to entities (but beware “intermediate”
|
|
1097
|
+
* non-entities).
|
|
1101
1098
|
*
|
|
1102
1099
|
* Examples:
|
|
1103
1100
|
* ext === art: `entity E : F {}` => add elements of F to E
|
|
@@ -1126,7 +1123,18 @@ function extend( model ) {
|
|
|
1126
1123
|
}
|
|
1127
1124
|
if (!art.query && art.elements) // do not set art.elements and art.enums with query entity!
|
|
1128
1125
|
includeMembers( ext, art, 'elements' );
|
|
1129
|
-
|
|
1126
|
+
if (art.kind !== 'type') {
|
|
1127
|
+
includeMembers( ext, art, 'actions' );
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
for (const ref of ext.includes) {
|
|
1131
|
+
const template = ref._artifact; // already resolved
|
|
1132
|
+
if (template?.actions && Object.keys( template.actions ).length) {
|
|
1133
|
+
warning( 'ref-ignoring-actions', [ ref.location, ext ], { art: template },
|
|
1134
|
+
'The actions of $(ART) are not added to the type' );
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1130
1138
|
}
|
|
1131
1139
|
|
|
1132
1140
|
/**
|
|
@@ -1143,15 +1151,15 @@ function extend( model ) {
|
|
|
1143
1151
|
// Warning 'Overwrites definition from include "I" (at elem def)
|
|
1144
1152
|
const parent = ext === art && art;
|
|
1145
1153
|
const members = ext[prop];
|
|
1146
|
-
if (members
|
|
1147
|
-
ext[prop] = Object.create(null);
|
|
1154
|
+
if (members)
|
|
1155
|
+
ext[prop] = Object.create( null );
|
|
1148
1156
|
let hasNewElement = false;
|
|
1149
1157
|
|
|
1150
1158
|
for (const ref of ext.includes) {
|
|
1151
1159
|
const template = ref._artifact; // already resolved
|
|
1152
1160
|
if (template) { // be robust
|
|
1153
1161
|
if (template[prop] && !ext[prop])
|
|
1154
|
-
ext[prop] = Object.create(null);
|
|
1162
|
+
ext[prop] = Object.create( null );
|
|
1155
1163
|
// eslint-disable-next-line no-loop-func
|
|
1156
1164
|
forEachInOrder( template, prop, ( origin, name ) => {
|
|
1157
1165
|
if (members && members[name]) {
|
|
@@ -1165,7 +1173,7 @@ function extend( model ) {
|
|
|
1165
1173
|
if (!parent) // not yet set for EXTEND foo WITH bar => linkToOrigin() did not add it
|
|
1166
1174
|
dictAdd( ext[prop], name, elem );
|
|
1167
1175
|
elem.$inferred = 'include';
|
|
1168
|
-
if (origin.masked)
|
|
1176
|
+
if (origin.masked) // TODO(v5): remove 'masked'
|
|
1169
1177
|
elem.masked = Object.assign( { $inferred: 'include' }, origin.masked );
|
|
1170
1178
|
if (origin.key)
|
|
1171
1179
|
elem.key = Object.assign( { $inferred: 'include' }, origin.key );
|
|
@@ -1178,7 +1186,7 @@ function extend( model ) {
|
|
|
1178
1186
|
setLink( elem, '_calcOrigin', origin._calcOrigin || origin );
|
|
1179
1187
|
}
|
|
1180
1188
|
// TODO: also complain if elem is just defined in art
|
|
1181
|
-
});
|
|
1189
|
+
} );
|
|
1182
1190
|
}
|
|
1183
1191
|
}
|
|
1184
1192
|
|
|
@@ -1194,7 +1202,7 @@ function extend( model ) {
|
|
|
1194
1202
|
// the element order.
|
|
1195
1203
|
if (ext[prop][name] !== elem )
|
|
1196
1204
|
dictAdd( ext[prop], name, elem );
|
|
1197
|
-
});
|
|
1205
|
+
} );
|
|
1198
1206
|
}
|
|
1199
1207
|
}
|
|
1200
1208
|
|
|
@@ -1207,13 +1215,13 @@ function extend( model ) {
|
|
|
1207
1215
|
function checkRedefinitionThroughIncludes( parent, prop ) {
|
|
1208
1216
|
if (!parent[prop])
|
|
1209
1217
|
return;
|
|
1210
|
-
forEachInOrder(parent, prop, ( member, name ) => {
|
|
1211
|
-
if (member.$inferred === 'include' && Array.isArray(member.$duplicates)) {
|
|
1212
|
-
const includes = [ member, ...member.$duplicates ].map(dup => dup._origin._main);
|
|
1218
|
+
forEachInOrder( parent, prop, ( member, name ) => {
|
|
1219
|
+
if (member.$inferred === 'include' && Array.isArray( member.$duplicates )) {
|
|
1220
|
+
const includes = [ member, ...member.$duplicates ].map( dup => dup._origin._main );
|
|
1213
1221
|
error( 'duplicate-definition', [ parent.name.location, member ],
|
|
1214
1222
|
{ '#': `include-${ prop }`, name, sorted_arts: includes } );
|
|
1215
1223
|
}
|
|
1216
|
-
});
|
|
1224
|
+
} );
|
|
1217
1225
|
}
|
|
1218
1226
|
}
|
|
1219
1227
|
|
|
@@ -1225,7 +1233,7 @@ function extend( model ) {
|
|
|
1225
1233
|
* @returns {Record<string, object>} key: layer name, value: {name, layer, extensions[]}`
|
|
1226
1234
|
*/
|
|
1227
1235
|
function layeredExtensions( extensions ) {
|
|
1228
|
-
const layered = Object.create(null);
|
|
1236
|
+
const layered = Object.create( null );
|
|
1229
1237
|
for (const ext of extensions) {
|
|
1230
1238
|
const layer = (ext.kind === 'annotate' || ext.kind === 'extend' || ext.kind === 'source') &&
|
|
1231
1239
|
layers.layer( ext );
|
|
@@ -1257,12 +1265,13 @@ function extensionsOfHighestLayers( layered ) {
|
|
|
1257
1265
|
if (layerNames.length <= 1) {
|
|
1258
1266
|
const name = layerNames[0];
|
|
1259
1267
|
const highest = layered[name]?.extensions || [];
|
|
1268
|
+
highest.sort( compareExtensions );
|
|
1260
1269
|
delete layered[name];
|
|
1261
1270
|
return { highest, issue: inMoreThanOneFile( highest ) };
|
|
1262
1271
|
}
|
|
1263
1272
|
|
|
1264
1273
|
// collect all layers which are lower than another layer
|
|
1265
|
-
const allExtends = Object.create(null);
|
|
1274
|
+
const allExtends = Object.create( null );
|
|
1266
1275
|
allExtends[''] = {}; // the "Define" layer
|
|
1267
1276
|
for (const name of layerNames) {
|
|
1268
1277
|
if (name) // not the "Define" layer
|
|
@@ -1280,7 +1289,7 @@ function extensionsOfHighestLayers( layered ) {
|
|
|
1280
1289
|
}
|
|
1281
1290
|
}
|
|
1282
1291
|
highest.sort( compareExtensions );
|
|
1283
|
-
const good = highestLayers.every( layer => !inMoreThanOneFile( layer.extensions ));
|
|
1292
|
+
const good = highestLayers.every( layer => !inMoreThanOneFile( layer.extensions ) );
|
|
1284
1293
|
// TODO: use layer.file instead
|
|
1285
1294
|
const issue = !good || highestLayers.length > 1 && 'unrelated';
|
|
1286
1295
|
// console.log('HI:',highest.map(l=>l.name),issue,issue&&extensions)
|
|
@@ -1291,7 +1300,7 @@ function inMoreThanOneFile( extensions ) {
|
|
|
1291
1300
|
if (extensions.length <= 1)
|
|
1292
1301
|
return false;
|
|
1293
1302
|
const file = extensions[0].location?.file;
|
|
1294
|
-
return !file || extensions.
|
|
1303
|
+
return !file || extensions.some( e => e.location?.file !== file );
|
|
1295
1304
|
}
|
|
1296
1305
|
|
|
1297
1306
|
/**
|
|
@@ -47,14 +47,14 @@ function finalizeParseCdl( model ) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
forEachGeneric(model, 'definitions', art => resolveTypesForParseCdl(art, art));
|
|
51
|
-
forEachGeneric(model, 'vocabularies', art => resolveTypesForParseCdl(art, art));
|
|
50
|
+
forEachGeneric( model, 'definitions', art => resolveTypesForParseCdl( art, art ) );
|
|
51
|
+
forEachGeneric( model, 'vocabularies', art => resolveTypesForParseCdl( art, art ) );
|
|
52
52
|
|
|
53
53
|
if (extensions.length > 0) {
|
|
54
54
|
// TODO: do a sort based on the location in case there were two extensions
|
|
55
55
|
// on the same definition? Yes: anno first outside, then inside service def
|
|
56
56
|
model.extensions = extensions;
|
|
57
|
-
model.extensions.forEach(ext => resolveTypesForParseCdl(ext, ext));
|
|
57
|
+
model.extensions.forEach( ext => resolveTypesForParseCdl( ext, ext ) );
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -69,9 +69,9 @@ function finalizeParseCdl( model ) {
|
|
|
69
69
|
if (!artifact || typeof artifact !== 'object')
|
|
70
70
|
return;
|
|
71
71
|
|
|
72
|
-
if (Array.isArray(artifact)) {
|
|
72
|
+
if (Array.isArray( artifact )) {
|
|
73
73
|
// e.g. `args` array
|
|
74
|
-
artifact.forEach(art => resolveTypesForParseCdl(art, main));
|
|
74
|
+
artifact.forEach( art => resolveTypesForParseCdl( art, main ) );
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -80,66 +80,66 @@ function finalizeParseCdl( model ) {
|
|
|
80
80
|
return;
|
|
81
81
|
|
|
82
82
|
if (artifact.type)
|
|
83
|
-
resolveTypeUnchecked(artifact, main);
|
|
83
|
+
resolveTypeUnchecked( artifact, main );
|
|
84
84
|
|
|
85
85
|
for (const include of artifact.includes || [])
|
|
86
|
-
resolveUncheckedPath(include, 'include', main);
|
|
86
|
+
resolveUncheckedPath( include, 'include', main );
|
|
87
87
|
|
|
88
88
|
// define.js takes care that `target` is a ref and
|
|
89
89
|
// `targetAspect` is a structure.
|
|
90
90
|
if (artifact.target)
|
|
91
|
-
resolveUncheckedPath(artifact.target, 'target', main);
|
|
91
|
+
resolveUncheckedPath( artifact.target, 'target', main );
|
|
92
92
|
if (artifact.targetAspect)
|
|
93
|
-
resolveTypesForParseCdl(artifact.targetAspect, main);
|
|
93
|
+
resolveTypesForParseCdl( artifact.targetAspect, main );
|
|
94
94
|
|
|
95
95
|
if (artifact.from) {
|
|
96
96
|
const { from } = artifact;
|
|
97
|
-
resolveUncheckedPath(from, 'from', main);
|
|
98
|
-
resolveTypesForParseCdl(from, main);
|
|
97
|
+
resolveUncheckedPath( from, 'from', main );
|
|
98
|
+
resolveTypesForParseCdl( from, main );
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// Recursively go through all XSN properties. There are a few that need to be
|
|
102
102
|
// handled specifically. Refer to the code below this loop for details.
|
|
103
103
|
for (const prop in artifact) {
|
|
104
|
-
if (artifact[prop] === undefined || parseCdlSpeciallyHandledXsnProps.includes(prop) ||
|
|
105
|
-
parseCdlIgnoredXsnProps.includes(prop))
|
|
104
|
+
if (artifact[prop] === undefined || parseCdlSpeciallyHandledXsnProps.includes( prop ) ||
|
|
105
|
+
parseCdlIgnoredXsnProps.includes( prop ))
|
|
106
106
|
continue;
|
|
107
107
|
|
|
108
|
-
if (artifact[prop] && Object.getPrototypeOf(artifact[prop]) === null)
|
|
108
|
+
if (artifact[prop] && Object.getPrototypeOf( artifact[prop] ) === null)
|
|
109
109
|
// Dictionary in XSN
|
|
110
|
-
forEachGeneric(artifact, prop, art => resolveTypesForParseCdl(art, art));
|
|
110
|
+
forEachGeneric( artifact, prop, art => resolveTypesForParseCdl( art, art ) );
|
|
111
111
|
else
|
|
112
|
-
resolveTypesForParseCdl(artifact[prop], main);
|
|
112
|
+
resolveTypesForParseCdl( artifact[prop], main );
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// `$queries` has a flat structure that we can use instead of going through all `query`.
|
|
116
116
|
// For these query-related properties, we need to keep the reference to the artifact
|
|
117
117
|
// containing it. Otherwise some type's aren't properly resolved.
|
|
118
118
|
// TODO: If resolveTypeUnchecked is reworked, we may be able to simplify this coding.
|
|
119
|
-
(artifact.$queries || []).forEach( art => resolveTypesForParseCdl(art, artifact) );
|
|
120
|
-
(artifact.columns || []).forEach( art => resolveTypesForParseCdl(art, artifact) );
|
|
121
|
-
forEachGeneric( artifact, 'mixin', art => resolveTypesForParseCdl(art, artifact) );
|
|
119
|
+
(artifact.$queries || []).forEach( art => resolveTypesForParseCdl( art, artifact ) );
|
|
120
|
+
(artifact.columns || []).forEach( art => resolveTypesForParseCdl( art, artifact ) );
|
|
121
|
+
forEachGeneric( artifact, 'mixin', art => resolveTypesForParseCdl( art, artifact ) );
|
|
122
122
|
|
|
123
123
|
// For better error messages for `type of`s in `returns`, we pass the object as the new main.
|
|
124
|
-
resolveTypesForParseCdl(artifact.returns, artifact.returns);
|
|
124
|
+
resolveTypesForParseCdl( artifact.returns, artifact.returns );
|
|
125
125
|
|
|
126
126
|
// Because `args` can be used in different contexts with different semantics,
|
|
127
127
|
// it needs to be handled specifically.
|
|
128
128
|
if (artifact.args && typeof artifact.args === 'object') {
|
|
129
|
-
if (Array.isArray(artifact.args)) {
|
|
129
|
+
if (Array.isArray( artifact.args )) {
|
|
130
130
|
// `args` may either be an array (e.g. query 'from' args) ...
|
|
131
|
-
artifact.args.forEach((from) => {
|
|
131
|
+
artifact.args.forEach( (from) => {
|
|
132
132
|
// ... and could be either inside a `from` ...
|
|
133
133
|
if (from && from.kind === '$tableAlias')
|
|
134
|
-
resolveUncheckedPath(from, 'from', from._main);
|
|
134
|
+
resolveUncheckedPath( from, 'from', from._main );
|
|
135
135
|
|
|
136
136
|
// ... or only params ...
|
|
137
|
-
resolveTypesForParseCdl(from, main);
|
|
138
|
-
});
|
|
137
|
+
resolveTypesForParseCdl( from, main );
|
|
138
|
+
} );
|
|
139
139
|
}
|
|
140
140
|
else {
|
|
141
141
|
// ... or dictionary (e.g. params)
|
|
142
|
-
forEachGeneric(artifact, 'args', obj => resolveTypesForParseCdl(obj, obj));
|
|
142
|
+
forEachGeneric( artifact, 'args', obj => resolveTypesForParseCdl( obj, obj ) );
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
}
|