@sap/cds-compiler 4.0.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- 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 +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- 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 +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- 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} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- 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/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/extend.js
CHANGED
|
@@ -23,10 +23,12 @@ const {
|
|
|
23
23
|
} = require('./utils');
|
|
24
24
|
const layers = require('./moduleLayers');
|
|
25
25
|
const { CompilerAssertion } = require('../base/error');
|
|
26
|
+
const { CsnLocation } = require('./classes');
|
|
26
27
|
|
|
27
|
-
const $location = Symbol.for('cds.$location');
|
|
28
|
+
const $location = Symbol.for( 'cds.$location' );
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// attach stupid location - TODO: remove in v5
|
|
31
|
+
const genLocation = new CsnLocation( '' );
|
|
30
32
|
|
|
31
33
|
// Array.prototype.spread = 42; // prototype-polluted JS classes
|
|
32
34
|
|
|
@@ -41,6 +43,7 @@ function extend( model ) {
|
|
|
41
43
|
resolveTypeArgumentsUnchecked,
|
|
42
44
|
attachAndEmitValidNames,
|
|
43
45
|
initMembers,
|
|
46
|
+
initSelectItems,
|
|
44
47
|
} = model.$functions;
|
|
45
48
|
|
|
46
49
|
Object.assign( model.$functions, {
|
|
@@ -50,10 +53,10 @@ function extend( model ) {
|
|
|
50
53
|
applyIncludes, // TODO: re-check
|
|
51
54
|
} );
|
|
52
55
|
|
|
53
|
-
const includesNonShadowedFirst = isDeprecatedEnabled(model.options, 'includesNonShadowedFirst');
|
|
56
|
+
const includesNonShadowedFirst = isDeprecatedEnabled( model.options, 'includesNonShadowedFirst' );
|
|
54
57
|
|
|
55
58
|
sortModelSources();
|
|
56
|
-
const extensionsDict = Object.create(null); // TODO TMP
|
|
59
|
+
const extensionsDict = Object.create( null ); // TODO TMP
|
|
57
60
|
forEachDefinition( model, tagIncludes ); // TODO TMP
|
|
58
61
|
|
|
59
62
|
forEachDefinition( model, extendArtifactBefore );
|
|
@@ -131,46 +134,32 @@ function extend( model ) {
|
|
|
131
134
|
delete art.$typeExts;
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
if (art.kind === 'annotate' && !art.returns && extensionsMap.returns)
|
|
137
|
+
if (art.kind === 'annotate' && !art.returns && extensionsMap.returns && !art._parent?.returns)
|
|
135
138
|
annotateCreate( art, '', art, 'returns' );
|
|
136
139
|
|
|
140
|
+
moveDictExtensions( art, extensionsMap, 'actions' );
|
|
137
141
|
moveDictExtensions( art, extensionsMap, 'params' );
|
|
138
142
|
moveReturnsExtensions( art, extensionsMap );
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
|
|
143
|
+
|
|
144
|
+
if (art.returns) {
|
|
145
|
+
pushToDict( art.returns, '_extensions', ...extensionsMap.elements || [] );
|
|
146
|
+
pushToDict( art.returns, '_extensions', ...extensionsMap.enum || [] );
|
|
147
|
+
if (art.kind !== 'annotate') {
|
|
142
148
|
extendHandleReturns( extensionsMap.elements, art );
|
|
143
149
|
extendHandleReturns( extensionsMap.enum, art );
|
|
150
|
+
return;
|
|
144
151
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 || [] );
|
|
148
157
|
}
|
|
149
158
|
else {
|
|
150
159
|
moveDictExtensions( art, extensionsMap,
|
|
151
160
|
(art.enum && art.kind !== 'annotate' ? 'enum' : 'elements'), 'elements' );
|
|
152
161
|
moveDictExtensions( art, extensionsMap, 'enum' );
|
|
153
162
|
}
|
|
154
|
-
moveDictExtensions( art, extensionsMap, 'actions' );
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* FIXME: Remove this workaround. This workaround avoids endless recursion for annotate statements
|
|
159
|
-
* that have both "returns" and "elements". The endless recursion happens due to the
|
|
160
|
-
* pushDict on `sub`. The `elements` extensions will point to the same extension on which
|
|
161
|
-
* `returns` exist.
|
|
162
|
-
*
|
|
163
|
-
* @param extensionsMap
|
|
164
|
-
* @param {string} prop
|
|
165
|
-
* @return {XSN.Extension[]}
|
|
166
|
-
*/
|
|
167
|
-
function avoidRecursiveReturns( extensionsMap, prop ) {
|
|
168
|
-
if (!extensionsMap[prop])
|
|
169
|
-
return [];
|
|
170
|
-
const exts = [];
|
|
171
|
-
for (const ext of extensionsMap[prop])
|
|
172
|
-
exts.push({ ...ext, returns: undefined });
|
|
173
|
-
return exts;
|
|
174
163
|
}
|
|
175
164
|
|
|
176
165
|
/**
|
|
@@ -221,7 +210,7 @@ function extend( model ) {
|
|
|
221
210
|
annotate: 'There is no artifact $(ART), use $(CODE) instead',
|
|
222
211
|
// do not mention 'extend context', that is not in CAPire
|
|
223
212
|
service: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) or $(KEYWORD) instead',
|
|
224
|
-
});
|
|
213
|
+
} );
|
|
225
214
|
}
|
|
226
215
|
// TODO: Use similar checks for EXTEND ENTITY etc - 'ext-ignoring-kind'
|
|
227
216
|
}
|
|
@@ -231,7 +220,7 @@ function extend( model ) {
|
|
|
231
220
|
// TODO: if extensions has more than one of returns,items,elements,enum, delete all those props
|
|
232
221
|
function transformArtifactExtensions( art ) {
|
|
233
222
|
const hasOnlySubExtensions = art._outer; // items, anonymous aspects
|
|
234
|
-
const dict = Object.create(null);
|
|
223
|
+
const dict = Object.create( null );
|
|
235
224
|
for (const ext of art._extensions) {
|
|
236
225
|
for (const prop in ext) {
|
|
237
226
|
if (ext[prop] === undefined) // deleted property
|
|
@@ -383,12 +372,16 @@ function extend( model ) {
|
|
|
383
372
|
const { query } = art;
|
|
384
373
|
for (const col of ext.columns)
|
|
385
374
|
col.$extended = true;
|
|
386
|
-
|
|
375
|
+
|
|
376
|
+
if (!query?.from?.path) {
|
|
387
377
|
error( 'extend-columns', [ ext.columns[$location], ext ], { art } );
|
|
388
|
-
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (!query.columns)
|
|
389
381
|
query.columns = [ { location: query.from.location, val: '*' }, ...ext.columns ];
|
|
390
382
|
else
|
|
391
383
|
query.columns.push( ...ext.columns );
|
|
384
|
+
initSelectItems( query, ext.columns, query, true );
|
|
392
385
|
}
|
|
393
386
|
else if ([ 'length', 'precision', 'scale', 'srid' ].includes( prop )) {
|
|
394
387
|
const typeExts = art.$typeExts || (art.$typeExts = {});
|
|
@@ -428,7 +421,7 @@ function extend( model ) {
|
|
|
428
421
|
while (prevPos < previousValue.length) {
|
|
429
422
|
const prevItem = previousValue[prevPos++];
|
|
430
423
|
result.push( prevItem );
|
|
431
|
-
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo)) {
|
|
424
|
+
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo )) {
|
|
432
425
|
upToSpec = false;
|
|
433
426
|
break;
|
|
434
427
|
}
|
|
@@ -599,32 +592,41 @@ function extend( model ) {
|
|
|
599
592
|
continue; // definitions inside extend, already handled
|
|
600
593
|
dictCheck = dictCheck && checkRemainingMemberExtensions( art, elemExt, artProp, name );
|
|
601
594
|
const elem = artDict[name] || annotateFor( art, extProp, name );
|
|
602
|
-
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null) );
|
|
595
|
+
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null ) );
|
|
603
596
|
pushToDict( elem, '_extensions', elemExt );
|
|
604
597
|
}
|
|
605
598
|
}
|
|
606
599
|
}
|
|
607
600
|
|
|
608
601
|
function moveReturnsExtensions( art, extensionsMap ) {
|
|
609
|
-
|
|
602
|
+
const extensions = extensionsMap.returns;
|
|
603
|
+
if (!extensions)
|
|
610
604
|
return;
|
|
605
|
+
const artReturns = art.returns;
|
|
606
|
+
let extReturns = artReturns;
|
|
607
|
+
const isAction = art.kind === 'action' || art.kind === 'function';
|
|
611
608
|
|
|
612
|
-
for (const ext of
|
|
613
|
-
if (!
|
|
614
|
-
const variant = art.kind === 'action' || art.kind === 'function' ? art.kind : 'std';
|
|
609
|
+
for (const ext of extensions) {
|
|
610
|
+
if (!artReturns && art.kind !== 'annotate') {
|
|
615
611
|
warning( 'ext-unexpected-returns', [ ext.returns.location, ext ], {
|
|
616
|
-
'#':
|
|
612
|
+
'#': (isAction ? art.kind : 'std'), keyword: 'returns',
|
|
617
613
|
}, {
|
|
618
614
|
std: 'Unexpected $(KEYWORD); only actions and functions have return parameters',
|
|
619
615
|
action: 'Unexpected $(KEYWORD) for action without return parameter',
|
|
620
616
|
// function without `returns` can happen via CSN input!
|
|
621
617
|
function: 'Unexpected $(KEYWORD) for function without return parameter',
|
|
622
|
-
});
|
|
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 );
|
|
623
629
|
}
|
|
624
|
-
// If `!art.returns`, then it could be CSN from SQL, where actions are replaced by dummies.
|
|
625
|
-
const elem = art.returns || annotateFor( art, 'params', '' );
|
|
626
|
-
setLink( ext.name, '_artifact', (elem.kind !== 'annotate' ? elem : null) );
|
|
627
|
-
pushToDict(elem, '_extensions', ext.returns);
|
|
628
630
|
}
|
|
629
631
|
}
|
|
630
632
|
|
|
@@ -713,12 +715,17 @@ function extend( model ) {
|
|
|
713
715
|
{ '#': (parent._effectiveType?.kind === 'entity') ? 'entity' : 'std' }, {
|
|
714
716
|
std: 'Elements only exist in entities, types or typed constructs',
|
|
715
717
|
entity: 'Elements of entity types can\'t be annotated',
|
|
716
|
-
});
|
|
718
|
+
} );
|
|
717
719
|
break;
|
|
718
720
|
case 'params':
|
|
719
721
|
warning( 'anno-unexpected-params', [ location, ext._parent ], {},
|
|
720
722
|
'Parameters only exist for actions or functions' );
|
|
721
723
|
break;
|
|
724
|
+
case 'actions':
|
|
725
|
+
// TODO: check if artifact can have actions, similar to `anno-unexpected-actions`
|
|
726
|
+
notFound( 'anno-undefined-action', ext.name.location, ext,
|
|
727
|
+
{ '#': 'action', art: parent, name } );
|
|
728
|
+
break;
|
|
722
729
|
default:
|
|
723
730
|
if (model.options.testMode)
|
|
724
731
|
throw new CompilerAssertion(`Missing case for prop: ${ prop }`);
|
|
@@ -796,8 +803,6 @@ function extend( model ) {
|
|
|
796
803
|
return;
|
|
797
804
|
if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
|
|
798
805
|
return;
|
|
799
|
-
// else if (ext.kind === 'extend') { // TODO v4 - add error
|
|
800
|
-
// }
|
|
801
806
|
if (art?.kind === 'namespace') {
|
|
802
807
|
// TODO: not at all different to having no definition
|
|
803
808
|
info( 'anno-namespace', [ ext.name.location, ext ], {}, // TODO: better location?
|
|
@@ -822,13 +827,13 @@ function extend( model ) {
|
|
|
822
827
|
}
|
|
823
828
|
else if (!construct.returns &&
|
|
824
829
|
(art.kind === 'action' || art.kind === 'function') && construct.elements) {
|
|
825
|
-
warning('ext-expecting-returns', [ construct.name.location, construct ], {
|
|
830
|
+
warning( 'ext-expecting-returns', [ construct.name.location, construct ], {
|
|
826
831
|
'#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
|
|
827
832
|
}, {
|
|
828
833
|
std: 'Expected $(CODE)', // unused variant
|
|
829
834
|
action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
|
|
830
835
|
function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
|
|
831
|
-
});
|
|
836
|
+
} );
|
|
832
837
|
}
|
|
833
838
|
}
|
|
834
839
|
}
|
|
@@ -871,7 +876,7 @@ function extend( model ) {
|
|
|
871
876
|
*/
|
|
872
877
|
function extendArtifact( extensions, art, noIncludes = false ) {
|
|
873
878
|
if (!noIncludes && !(canApplyIncludes( art, art ) &&
|
|
874
|
-
extensions.every( ext => canApplyIncludes(ext, art) )))
|
|
879
|
+
extensions.every( ext => canApplyIncludes( ext, art ) )))
|
|
875
880
|
return false;
|
|
876
881
|
if (Array.isArray( noIncludes )) {
|
|
877
882
|
canApplyIncludes( art, art, noIncludes );
|
|
@@ -879,7 +884,7 @@ function extend( model ) {
|
|
|
879
884
|
}
|
|
880
885
|
else if (!noIncludes &&
|
|
881
886
|
!(canApplyIncludes( art, art ) &&
|
|
882
|
-
extensions.every( ext => canApplyIncludes( ext, art) ))) {
|
|
887
|
+
extensions.every( ext => canApplyIncludes( ext, art ) ))) {
|
|
883
888
|
// console.log( 'FALSE:',art.name, extensions.map( e => e.name ) )
|
|
884
889
|
return false;
|
|
885
890
|
}
|
|
@@ -887,8 +892,15 @@ function extend( model ) {
|
|
|
887
892
|
model._entities.push( art ); // add structure with includes in dep order
|
|
888
893
|
art.$entity = ++model.$entity;
|
|
889
894
|
}
|
|
890
|
-
if (
|
|
891
|
-
|
|
895
|
+
if (art.includes) {
|
|
896
|
+
if (!noIncludes) {
|
|
897
|
+
applyIncludes( art, art );
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
for (const ref of art.includes)
|
|
901
|
+
resolvePath( ref, 'include', art );
|
|
902
|
+
}
|
|
903
|
+
}
|
|
892
904
|
// checkExtensionsKind( extensions, art );
|
|
893
905
|
extendMembers( extensions, art, noIncludes === 'gen' );
|
|
894
906
|
if (!noIncludes && art.includes) {
|
|
@@ -923,7 +935,7 @@ function extend( model ) {
|
|
|
923
935
|
// includes = ['Base1',3,'Base2']
|
|
924
936
|
// where 3 means adding the next 3 elements before applying include 'Base2'
|
|
925
937
|
if (art.includes)
|
|
926
|
-
art.includes.push(...ext.includes);
|
|
938
|
+
art.includes.push( ...ext.includes );
|
|
927
939
|
else
|
|
928
940
|
art.includes = [ ...ext.includes ];
|
|
929
941
|
applyIncludes( ext, art );
|
|
@@ -932,7 +944,7 @@ function extend( model ) {
|
|
|
932
944
|
checkAnnotate( ext, art );
|
|
933
945
|
// TODO: do we allow to add elements with array of {...}? If yes, adapt
|
|
934
946
|
initMembers( ext, art, ext._block ); // might set _extend, _annotate
|
|
935
|
-
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!)
|
|
936
948
|
}
|
|
937
949
|
for (const name in ext.elements) {
|
|
938
950
|
const elem = ext.elements[name];
|
|
@@ -946,7 +958,7 @@ function extend( model ) {
|
|
|
946
958
|
reportUnstableExtensions( elemExtensions );
|
|
947
959
|
|
|
948
960
|
// This whole function will be removed with a next change - no need to have nice code here:
|
|
949
|
-
const dict = Object.create(null);
|
|
961
|
+
const dict = Object.create( null );
|
|
950
962
|
// actions cannot be extended anyway. TODO: there should be a message
|
|
951
963
|
// (possible with CSN input), but that was missing before this change, too.
|
|
952
964
|
for (const e of extensions) {
|
|
@@ -988,7 +1000,8 @@ function extend( model ) {
|
|
|
988
1000
|
let lastExt = null;
|
|
989
1001
|
let open = []; // the "highest" layers
|
|
990
1002
|
for (const ext of extensions) {
|
|
991
|
-
const extLayer = layers.layer( ext ) ||
|
|
1003
|
+
const extLayer = layers.layer( ext ) ||
|
|
1004
|
+
{ realname: '', _layerExtends: Object.create( null ) };
|
|
992
1005
|
if (!open.length) {
|
|
993
1006
|
lastExt = ext;
|
|
994
1007
|
open = [ extLayer.realname ];
|
|
@@ -1036,7 +1049,7 @@ function extend( model ) {
|
|
|
1036
1049
|
element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
|
|
1037
1050
|
action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
|
|
1038
1051
|
} );
|
|
1039
|
-
attachAndEmitValidNames(msg, validDict);
|
|
1052
|
+
attachAndEmitValidNames( msg, validDict );
|
|
1040
1053
|
}
|
|
1041
1054
|
}
|
|
1042
1055
|
|
|
@@ -1055,7 +1068,6 @@ function extend( model ) {
|
|
|
1055
1068
|
function canApplyIncludes( art, target, justResolveCyclic ) {
|
|
1056
1069
|
if (!art.includes)
|
|
1057
1070
|
return true;
|
|
1058
|
-
const isView = !!target.query;
|
|
1059
1071
|
for (const ref of art.includes) {
|
|
1060
1072
|
const name = resolveUncheckedPath( ref, 'include', art );
|
|
1061
1073
|
// console.log('CAI:',justResolveCyclic, name, ref.path, Object.keys(extensionsDict))
|
|
@@ -1071,7 +1083,6 @@ function extend( model ) {
|
|
|
1071
1083
|
else if (ref._artifact) {
|
|
1072
1084
|
delete ref._artifact;
|
|
1073
1085
|
}
|
|
1074
|
-
resolvePath( ref, isView ? 'viewInclude' : 'include', art );
|
|
1075
1086
|
}
|
|
1076
1087
|
return true;
|
|
1077
1088
|
}
|
|
@@ -1080,7 +1091,10 @@ function extend( model ) {
|
|
|
1080
1091
|
* Apply all includes of `ext` on `ext`. Checks that `art` allows includes.
|
|
1081
1092
|
* If `ext === art`, then includes of the artifact itself are applied.
|
|
1082
1093
|
* If `ext !== art`, applies includes on the extensions, not artifact.
|
|
1083
|
-
* Sets `
|
|
1094
|
+
* Sets `_ancestors` links on `art`.
|
|
1095
|
+
*
|
|
1096
|
+
* TODO: try to set `_ancestors` only to entities (but beware “intermediate”
|
|
1097
|
+
* non-entities).
|
|
1084
1098
|
*
|
|
1085
1099
|
* Examples:
|
|
1086
1100
|
* ext === art: `entity E : F {}` => add elements of F to E
|
|
@@ -1096,22 +1110,31 @@ function extend( model ) {
|
|
|
1096
1110
|
return;
|
|
1097
1111
|
}
|
|
1098
1112
|
|
|
1099
|
-
if (!art.query)
|
|
1100
|
-
|
|
1101
|
-
|
|
1113
|
+
if (!art._ancestors && !art.query)
|
|
1114
|
+
setLink( art, '_ancestors', [] ); // recursive array of includes
|
|
1115
|
+
for (const ref of ext.includes) {
|
|
1116
|
+
const template = resolvePath( ref, 'include', art );
|
|
1117
|
+
// !template -> non-includable, e.g. scalar type, or cyclic
|
|
1118
|
+
if (template && !art.query) {
|
|
1119
|
+
if (template._ancestors)
|
|
1120
|
+
art._ancestors.push( ...template._ancestors );
|
|
1121
|
+
art._ancestors.push( template );
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (!art.query && art.elements) // do not set art.elements and art.enums with query entity!
|
|
1125
|
+
includeMembers( ext, art, 'elements' );
|
|
1126
|
+
if (art.kind !== 'type') {
|
|
1127
|
+
includeMembers( ext, art, 'actions' );
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1102
1130
|
for (const ref of ext.includes) {
|
|
1103
|
-
const template = ref._artifact;
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
art._ancestors.push( ...template._ancestors );
|
|
1108
|
-
art._ancestors.push( template );
|
|
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' );
|
|
1109
1135
|
}
|
|
1110
1136
|
}
|
|
1111
1137
|
}
|
|
1112
|
-
if (!art.query) // do not set art.elements and art.enums with query entity!
|
|
1113
|
-
includeMembers( ext, art, 'elements' );
|
|
1114
|
-
includeMembers( ext, art, 'actions' );
|
|
1115
1138
|
}
|
|
1116
1139
|
|
|
1117
1140
|
/**
|
|
@@ -1128,12 +1151,15 @@ function extend( model ) {
|
|
|
1128
1151
|
// Warning 'Overwrites definition from include "I" (at elem def)
|
|
1129
1152
|
const parent = ext === art && art;
|
|
1130
1153
|
const members = ext[prop];
|
|
1131
|
-
|
|
1154
|
+
if (members)
|
|
1155
|
+
ext[prop] = Object.create( null );
|
|
1132
1156
|
let hasNewElement = false;
|
|
1133
1157
|
|
|
1134
1158
|
for (const ref of ext.includes) {
|
|
1135
1159
|
const template = ref._artifact; // already resolved
|
|
1136
1160
|
if (template) { // be robust
|
|
1161
|
+
if (template[prop] && !ext[prop])
|
|
1162
|
+
ext[prop] = Object.create( null );
|
|
1137
1163
|
// eslint-disable-next-line no-loop-func
|
|
1138
1164
|
forEachInOrder( template, prop, ( origin, name ) => {
|
|
1139
1165
|
if (members && members[name]) {
|
|
@@ -1147,7 +1173,7 @@ function extend( model ) {
|
|
|
1147
1173
|
if (!parent) // not yet set for EXTEND foo WITH bar => linkToOrigin() did not add it
|
|
1148
1174
|
dictAdd( ext[prop], name, elem );
|
|
1149
1175
|
elem.$inferred = 'include';
|
|
1150
|
-
if (origin.masked)
|
|
1176
|
+
if (origin.masked) // TODO(v5): remove 'masked'
|
|
1151
1177
|
elem.masked = Object.assign( { $inferred: 'include' }, origin.masked );
|
|
1152
1178
|
if (origin.key)
|
|
1153
1179
|
elem.key = Object.assign( { $inferred: 'include' }, origin.key );
|
|
@@ -1160,7 +1186,7 @@ function extend( model ) {
|
|
|
1160
1186
|
setLink( elem, '_calcOrigin', origin._calcOrigin || origin );
|
|
1161
1187
|
}
|
|
1162
1188
|
// TODO: also complain if elem is just defined in art
|
|
1163
|
-
});
|
|
1189
|
+
} );
|
|
1164
1190
|
}
|
|
1165
1191
|
}
|
|
1166
1192
|
|
|
@@ -1176,7 +1202,7 @@ function extend( model ) {
|
|
|
1176
1202
|
// the element order.
|
|
1177
1203
|
if (ext[prop][name] !== elem )
|
|
1178
1204
|
dictAdd( ext[prop], name, elem );
|
|
1179
|
-
});
|
|
1205
|
+
} );
|
|
1180
1206
|
}
|
|
1181
1207
|
}
|
|
1182
1208
|
|
|
@@ -1189,13 +1215,13 @@ function extend( model ) {
|
|
|
1189
1215
|
function checkRedefinitionThroughIncludes( parent, prop ) {
|
|
1190
1216
|
if (!parent[prop])
|
|
1191
1217
|
return;
|
|
1192
|
-
forEachInOrder(parent, prop, ( member, name ) => {
|
|
1193
|
-
if (member.$inferred === 'include' && Array.isArray(member.$duplicates)) {
|
|
1194
|
-
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 );
|
|
1195
1221
|
error( 'duplicate-definition', [ parent.name.location, member ],
|
|
1196
1222
|
{ '#': `include-${ prop }`, name, sorted_arts: includes } );
|
|
1197
1223
|
}
|
|
1198
|
-
});
|
|
1224
|
+
} );
|
|
1199
1225
|
}
|
|
1200
1226
|
}
|
|
1201
1227
|
|
|
@@ -1207,7 +1233,7 @@ function extend( model ) {
|
|
|
1207
1233
|
* @returns {Record<string, object>} key: layer name, value: {name, layer, extensions[]}`
|
|
1208
1234
|
*/
|
|
1209
1235
|
function layeredExtensions( extensions ) {
|
|
1210
|
-
const layered = Object.create(null);
|
|
1236
|
+
const layered = Object.create( null );
|
|
1211
1237
|
for (const ext of extensions) {
|
|
1212
1238
|
const layer = (ext.kind === 'annotate' || ext.kind === 'extend' || ext.kind === 'source') &&
|
|
1213
1239
|
layers.layer( ext );
|
|
@@ -1239,12 +1265,13 @@ function extensionsOfHighestLayers( layered ) {
|
|
|
1239
1265
|
if (layerNames.length <= 1) {
|
|
1240
1266
|
const name = layerNames[0];
|
|
1241
1267
|
const highest = layered[name]?.extensions || [];
|
|
1268
|
+
highest.sort( compareExtensions );
|
|
1242
1269
|
delete layered[name];
|
|
1243
1270
|
return { highest, issue: inMoreThanOneFile( highest ) };
|
|
1244
1271
|
}
|
|
1245
1272
|
|
|
1246
1273
|
// collect all layers which are lower than another layer
|
|
1247
|
-
const allExtends = Object.create(null);
|
|
1274
|
+
const allExtends = Object.create( null );
|
|
1248
1275
|
allExtends[''] = {}; // the "Define" layer
|
|
1249
1276
|
for (const name of layerNames) {
|
|
1250
1277
|
if (name) // not the "Define" layer
|
|
@@ -1262,7 +1289,7 @@ function extensionsOfHighestLayers( layered ) {
|
|
|
1262
1289
|
}
|
|
1263
1290
|
}
|
|
1264
1291
|
highest.sort( compareExtensions );
|
|
1265
|
-
const good = highestLayers.every( layer => !inMoreThanOneFile( layer.extensions ));
|
|
1292
|
+
const good = highestLayers.every( layer => !inMoreThanOneFile( layer.extensions ) );
|
|
1266
1293
|
// TODO: use layer.file instead
|
|
1267
1294
|
const issue = !good || highestLayers.length > 1 && 'unrelated';
|
|
1268
1295
|
// console.log('HI:',highest.map(l=>l.name),issue,issue&&extensions)
|
|
@@ -1273,7 +1300,7 @@ function inMoreThanOneFile( extensions ) {
|
|
|
1273
1300
|
if (extensions.length <= 1)
|
|
1274
1301
|
return false;
|
|
1275
1302
|
const file = extensions[0].location?.file;
|
|
1276
|
-
return !file || extensions.
|
|
1303
|
+
return !file || extensions.some( e => e.location?.file !== file );
|
|
1277
1304
|
}
|
|
1278
1305
|
|
|
1279
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,65 +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 (
|
|
104
|
+
if (artifact[prop] === undefined || parseCdlSpeciallyHandledXsnProps.includes( prop ) ||
|
|
105
|
+
parseCdlIgnoredXsnProps.includes( prop ))
|
|
105
106
|
continue;
|
|
106
107
|
|
|
107
|
-
if (artifact[prop] && Object.getPrototypeOf(artifact[prop]) === null)
|
|
108
|
+
if (artifact[prop] && Object.getPrototypeOf( artifact[prop] ) === null)
|
|
108
109
|
// Dictionary in XSN
|
|
109
|
-
forEachGeneric(artifact, prop, art => resolveTypesForParseCdl(art, art));
|
|
110
|
+
forEachGeneric( artifact, prop, art => resolveTypesForParseCdl( art, art ) );
|
|
110
111
|
else
|
|
111
|
-
resolveTypesForParseCdl(artifact[prop], main);
|
|
112
|
+
resolveTypesForParseCdl( artifact[prop], main );
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// `$queries` has a flat structure that we can use instead of going through all `query`.
|
|
115
116
|
// For these query-related properties, we need to keep the reference to the artifact
|
|
116
117
|
// containing it. Otherwise some type's aren't properly resolved.
|
|
117
118
|
// TODO: If resolveTypeUnchecked is reworked, we may be able to simplify this coding.
|
|
118
|
-
(artifact.$queries || []).forEach( art => resolveTypesForParseCdl(art, artifact) );
|
|
119
|
-
(artifact.columns || []).forEach( art => resolveTypesForParseCdl(art, artifact) );
|
|
120
|
-
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 ) );
|
|
121
122
|
|
|
122
123
|
// For better error messages for `type of`s in `returns`, we pass the object as the new main.
|
|
123
|
-
resolveTypesForParseCdl(artifact.returns, artifact.returns);
|
|
124
|
+
resolveTypesForParseCdl( artifact.returns, artifact.returns );
|
|
124
125
|
|
|
125
126
|
// Because `args` can be used in different contexts with different semantics,
|
|
126
127
|
// it needs to be handled specifically.
|
|
127
128
|
if (artifact.args && typeof artifact.args === 'object') {
|
|
128
|
-
if (Array.isArray(artifact.args)) {
|
|
129
|
+
if (Array.isArray( artifact.args )) {
|
|
129
130
|
// `args` may either be an array (e.g. query 'from' args) ...
|
|
130
|
-
artifact.args.forEach((from) => {
|
|
131
|
+
artifact.args.forEach( (from) => {
|
|
131
132
|
// ... and could be either inside a `from` ...
|
|
132
133
|
if (from && from.kind === '$tableAlias')
|
|
133
|
-
resolveUncheckedPath(from, 'from', from._main);
|
|
134
|
+
resolveUncheckedPath( from, 'from', from._main );
|
|
134
135
|
|
|
135
136
|
// ... or only params ...
|
|
136
|
-
resolveTypesForParseCdl(from, main);
|
|
137
|
-
});
|
|
137
|
+
resolveTypesForParseCdl( from, main );
|
|
138
|
+
} );
|
|
138
139
|
}
|
|
139
140
|
else {
|
|
140
141
|
// ... or dictionary (e.g. params)
|
|
141
|
-
forEachGeneric(artifact, 'args', obj => resolveTypesForParseCdl(obj, obj));
|
|
142
|
+
forEachGeneric( artifact, 'args', obj => resolveTypesForParseCdl( obj, obj ) );
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
}
|