@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/classes.js
CHANGED
|
@@ -30,15 +30,15 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
30
30
|
|
|
31
31
|
for (const name in definitions) {
|
|
32
32
|
const a = definitions[name];
|
|
33
|
-
if (Array.isArray(a))
|
|
33
|
+
if (Array.isArray( a ))
|
|
34
34
|
a.forEach( strongConnectRec );
|
|
35
35
|
else
|
|
36
36
|
strongConnectRec( a );
|
|
37
37
|
}
|
|
38
38
|
// now the cleanup
|
|
39
|
-
let nodes = Object.getOwnPropertyNames(definitions).map( n => definitions[n] );
|
|
39
|
+
let nodes = Object.getOwnPropertyNames( definitions ).map( n => definitions[n] );
|
|
40
40
|
while (nodes.length) { // still nodes to cleaned
|
|
41
|
-
nodes = cleanup(nodes);
|
|
41
|
+
nodes = cleanup( nodes );
|
|
42
42
|
}
|
|
43
43
|
return;
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
46
46
|
if (a._scc) // already processed
|
|
47
47
|
return;
|
|
48
48
|
while (a)
|
|
49
|
-
a = strongConnect(a);
|
|
49
|
+
a = strongConnect( a );
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Try to build a SCC starting from the node `v`.
|
|
@@ -60,7 +60,7 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
60
60
|
onStack: true,
|
|
61
61
|
depIndex: 0,
|
|
62
62
|
} );
|
|
63
|
-
stack.push(v);
|
|
63
|
+
stack.push( v );
|
|
64
64
|
// console.log('PUSH: ', v.kind,v.name)
|
|
65
65
|
}
|
|
66
66
|
if (!v._deps) // builtins, otherwise forgotten (TODO: assert in --test-mode)
|
|
@@ -127,7 +127,7 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
127
127
|
delete v._sccCaller;
|
|
128
128
|
for (const w of v._deps) {
|
|
129
129
|
if (w.art._scc)
|
|
130
|
-
todos.push(w.art);
|
|
130
|
+
todos.push( w.art );
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
package/lib/compiler/define.js
CHANGED
|
@@ -141,8 +141,8 @@ const {
|
|
|
141
141
|
const { compareLayer } = require('./moduleLayers');
|
|
142
142
|
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
143
143
|
|
|
144
|
-
const $location = Symbol.for('cds.$location');
|
|
145
|
-
const $inferred = Symbol.for('cds.$inferred');
|
|
144
|
+
const $location = Symbol.for( 'cds.$location' );
|
|
145
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Export function of this file. Transform argument `sources` = dictionary of
|
|
@@ -194,11 +194,11 @@ function define( model ) {
|
|
|
194
194
|
beta: 'With option $(PROP), beta features and many other newer features are disabled',
|
|
195
195
|
} );
|
|
196
196
|
}
|
|
197
|
-
model.definitions = Object.create(null);
|
|
197
|
+
model.definitions = Object.create( null );
|
|
198
198
|
setLink( model, '_entities', [] ); // for entities with includes
|
|
199
199
|
model.$entity = 0;
|
|
200
|
-
model.$compositionTargets = Object.create(null);
|
|
201
|
-
model.$collectedExtensions = Object.create(null);
|
|
200
|
+
model.$compositionTargets = Object.create( null );
|
|
201
|
+
model.$collectedExtensions = Object.create( null );
|
|
202
202
|
|
|
203
203
|
initBuiltins( model );
|
|
204
204
|
const sourceNames = shuffleArray( Object.keys( model.sources ) );
|
|
@@ -228,7 +228,7 @@ function define( model ) {
|
|
|
228
228
|
|
|
229
229
|
let namespace = src.namespace && src.namespace.path;
|
|
230
230
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
231
|
-
if (isInReservedNamespace(prefix)) {
|
|
231
|
+
if (isInReservedNamespace( prefix )) {
|
|
232
232
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
|
|
233
233
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
234
234
|
namespace = null;
|
|
@@ -241,7 +241,7 @@ function define( model ) {
|
|
|
241
241
|
addPathPrefixes( src.artifacts, prefix ); // before addUsing
|
|
242
242
|
}
|
|
243
243
|
else if (src.usings || src.namespace) {
|
|
244
|
-
src.artifacts = Object.create(null);
|
|
244
|
+
src.artifacts = Object.create( null );
|
|
245
245
|
}
|
|
246
246
|
if (src.usings)
|
|
247
247
|
shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
|
|
@@ -254,11 +254,11 @@ function define( model ) {
|
|
|
254
254
|
}
|
|
255
255
|
else if (src.definitions) { // CSN input
|
|
256
256
|
prefix = '';
|
|
257
|
-
dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ));
|
|
257
|
+
dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ) );
|
|
258
258
|
}
|
|
259
259
|
if (src.vocabularies) {
|
|
260
260
|
if (!model.vocabularies)
|
|
261
|
-
model.vocabularies = Object.create(null);
|
|
261
|
+
model.vocabularies = Object.create( null );
|
|
262
262
|
dictForEach( shuffleDict( src.vocabularies ), v => addVocabulary( v, src, prefix ) );
|
|
263
263
|
}
|
|
264
264
|
if (src.extensions) { // requires using to be known!
|
|
@@ -269,11 +269,11 @@ function define( model ) {
|
|
|
269
269
|
function addDefinition( art, block, prefix ) {
|
|
270
270
|
if (!art.name.absolute) {
|
|
271
271
|
// TODO: art.name.absolute = art.name.id || …
|
|
272
|
-
art.name.absolute = (!art.name.path) ? art.name.id : prefix + pathName(art.name.path);
|
|
272
|
+
art.name.absolute = (!art.name.path) ? art.name.id : prefix + pathName( art.name.path );
|
|
273
273
|
}
|
|
274
274
|
const { absolute } = art.name;
|
|
275
275
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
276
|
-
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
276
|
+
if (absolute === 'cds' || isInReservedNamespace( absolute )) {
|
|
277
277
|
error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
|
|
278
278
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
279
279
|
const builtin = model.definitions[absolute];
|
|
@@ -298,7 +298,7 @@ function define( model ) {
|
|
|
298
298
|
function addPathPrefixes( artifacts, prefix ) {
|
|
299
299
|
for (const name in artifacts) {
|
|
300
300
|
const d = artifacts[name];
|
|
301
|
-
const a = Array.isArray(d) ? d[0] : d;
|
|
301
|
+
const a = Array.isArray( d ) ? d[0] : d;
|
|
302
302
|
if (!a.name.absolute)
|
|
303
303
|
a.name.absolute = prefix + name;
|
|
304
304
|
const index = name.indexOf( '.' );
|
|
@@ -363,7 +363,7 @@ function define( model ) {
|
|
|
363
363
|
// create using for own namespace:
|
|
364
364
|
const last = path[path.length - 1];
|
|
365
365
|
const { id } = last;
|
|
366
|
-
if (src.artifacts[id] || last.id.includes('.'))
|
|
366
|
+
if (src.artifacts[id] || last.id.includes( '.' ))
|
|
367
367
|
// not used as we have a definition/using with that name, or dotted last path id
|
|
368
368
|
return;
|
|
369
369
|
src.artifacts[id] = {
|
|
@@ -451,7 +451,9 @@ function define( model ) {
|
|
|
451
451
|
const { name } = vocab;
|
|
452
452
|
if (!name.absolute) {
|
|
453
453
|
// TODO: art.name.absolute = vocab.name.id || …
|
|
454
|
-
vocab.name.absolute = (!vocab.name.path)
|
|
454
|
+
vocab.name.absolute = (!vocab.name.path)
|
|
455
|
+
? vocab.name.id
|
|
456
|
+
: prefix + pathName( vocab.name.path );
|
|
455
457
|
}
|
|
456
458
|
dictAdd( model.vocabularies, name.absolute, vocab );
|
|
457
459
|
}
|
|
@@ -461,8 +463,8 @@ function define( model ) {
|
|
|
461
463
|
*/
|
|
462
464
|
function addI18nBlocks() {
|
|
463
465
|
// TODO: the sequence should be in sync with extend / annotate / future $sources
|
|
464
|
-
const sortedSources = Object.keys(model.sources)
|
|
465
|
-
.filter(name => !!model.sources[name].i18n)
|
|
466
|
+
const sortedSources = Object.keys( model.sources )
|
|
467
|
+
.filter( name => !!model.sources[name].i18n )
|
|
466
468
|
.sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
|
|
467
469
|
|
|
468
470
|
if (sortedSources.length === 0)
|
|
@@ -557,7 +559,7 @@ function define( model ) {
|
|
|
557
559
|
return;
|
|
558
560
|
for (const name in src.artifacts) {
|
|
559
561
|
const entry = src.artifacts[name];
|
|
560
|
-
if (!Array.isArray(entry)) // no local name duplicate
|
|
562
|
+
if (!Array.isArray( entry )) // no local name duplicate
|
|
561
563
|
continue;
|
|
562
564
|
for (const decl of entry) {
|
|
563
565
|
if (!decl.$duplicates) { // do not have two duplicate messages
|
|
@@ -603,7 +605,7 @@ function define( model ) {
|
|
|
603
605
|
function initArtifactParentLink( art, definitions ) {
|
|
604
606
|
setLink( art, '_parent', null );
|
|
605
607
|
const { absolute } = art.name;
|
|
606
|
-
const dot = absolute.lastIndexOf('.');
|
|
608
|
+
const dot = absolute.lastIndexOf( '.' );
|
|
607
609
|
if (dot < 0)
|
|
608
610
|
return;
|
|
609
611
|
art.name.id = absolute.substring( dot + 1 ); // XSN TODO: remove name.id for artifacts
|
|
@@ -617,7 +619,7 @@ function define( model ) {
|
|
|
617
619
|
}
|
|
618
620
|
setLink( art, '_parent', parent );
|
|
619
621
|
if (!parent._subArtifacts)
|
|
620
|
-
setLink( parent, '_subArtifacts', Object.create(null) );
|
|
622
|
+
setLink( parent, '_subArtifacts', Object.create( null ) );
|
|
621
623
|
if (art.$duplicates !== true) // no redef or "first def"
|
|
622
624
|
parent._subArtifacts[absolute.substring( dot + 1 )] = art; // not dictAdd()
|
|
623
625
|
}
|
|
@@ -638,7 +640,7 @@ function define( model ) {
|
|
|
638
640
|
setLink( self, '_parent', art );
|
|
639
641
|
setLink( self, '_main', art ); // used on main artifact
|
|
640
642
|
setLink( self, '_origin', art );
|
|
641
|
-
art.$tableAliases = Object.create(null);
|
|
643
|
+
art.$tableAliases = Object.create( null );
|
|
642
644
|
art.$tableAliases[selfname] = self;
|
|
643
645
|
}
|
|
644
646
|
|
|
@@ -702,13 +704,17 @@ function define( model ) {
|
|
|
702
704
|
for (const q of query.args.slice(1))
|
|
703
705
|
initQueryExpression( q, art );
|
|
704
706
|
setLink( query, '_leadingQuery', leading );
|
|
705
|
-
if (leading
|
|
706
|
-
if (
|
|
707
|
+
if (leading) {
|
|
708
|
+
if (query.orderBy) {
|
|
709
|
+
leading.$orderBy ??= [ ];
|
|
707
710
|
leading.$orderBy.push( ...query.orderBy );
|
|
708
|
-
|
|
709
|
-
|
|
711
|
+
}
|
|
712
|
+
if (query.limit) {
|
|
713
|
+
leading.$limit ??= [ ];
|
|
714
|
+
leading.$limit.push( query.limit );
|
|
715
|
+
}
|
|
710
716
|
}
|
|
711
|
-
// ORDER BY to be evaluated in leading query
|
|
717
|
+
// ORDER BY and LIMIT to be evaluated in leading query
|
|
712
718
|
}
|
|
713
719
|
else { // with parse error (`select from <EOF>`, `select from E { *, ( select }`)
|
|
714
720
|
return undefined;
|
|
@@ -718,7 +724,7 @@ function define( model ) {
|
|
|
718
724
|
function initQuery() {
|
|
719
725
|
const main = art._main || art;
|
|
720
726
|
setLink( query, '_$next',
|
|
721
|
-
(art.kind === '$tableAlias' ? art._parent._$next : art) );
|
|
727
|
+
(art.kind === '$tableAlias' ? art._parent._$next : art ) );
|
|
722
728
|
setLink( query, '_block', art._block );
|
|
723
729
|
query.kind = 'select';
|
|
724
730
|
query.name = { location: query.location };
|
|
@@ -742,7 +748,7 @@ function define( model ) {
|
|
|
742
748
|
return;
|
|
743
749
|
if (!table.name) {
|
|
744
750
|
const last = table.path[table.path.length - 1];
|
|
745
|
-
const dot = last?.id?.lastIndexOf('.');
|
|
751
|
+
const dot = last?.id?.lastIndexOf( '.' );
|
|
746
752
|
const id = (dot >= 0) ? last.id.substring( dot + 1 ) : last.id || '';
|
|
747
753
|
// TODO: if we have too much time, we can calculate the real location with '.'
|
|
748
754
|
table.name = { $inferred: 'as', id, location: last.location };
|
|
@@ -795,6 +801,7 @@ function define( model ) {
|
|
|
795
801
|
// (internal) query, should only be relevant for --raw-output, not for
|
|
796
802
|
// user messages or references - TODO: correct if join on left?
|
|
797
803
|
table.name.param = aliases[1] || aliases[0] || '<unknown>';
|
|
804
|
+
setLink( table, '_user', query ); // TODO: do not set kind/name
|
|
798
805
|
setLink( table, '_$next', query._$next );
|
|
799
806
|
// TODO: probably set this to query if we switch to name restriction in JOIN
|
|
800
807
|
}
|
|
@@ -809,7 +816,7 @@ function define( model ) {
|
|
|
809
816
|
if (tableAlias.$inferred === '$internal') {
|
|
810
817
|
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
811
818
|
error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
|
|
812
|
-
{ '#': 'duplicate', code: 'as ‹alias›' });
|
|
819
|
+
{ '#': 'duplicate', code: 'as ‹alias›' } );
|
|
813
820
|
}
|
|
814
821
|
else {
|
|
815
822
|
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
@@ -845,7 +852,7 @@ function define( model ) {
|
|
|
845
852
|
|
|
846
853
|
function initExprForQuery( expr, query ) {
|
|
847
854
|
// TODO: use traverseExpr()
|
|
848
|
-
if (Array.isArray(expr)) { // TODO: old-style $parens ?
|
|
855
|
+
if (Array.isArray( expr )) { // TODO: old-style $parens ?
|
|
849
856
|
expr.forEach( e => initExprForQuery( e, query ) );
|
|
850
857
|
}
|
|
851
858
|
else if (!expr) {
|
|
@@ -855,13 +862,13 @@ function define( model ) {
|
|
|
855
862
|
initQueryExpression( expr.query, query );
|
|
856
863
|
}
|
|
857
864
|
else if (expr.args) {
|
|
858
|
-
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
865
|
+
const args = Array.isArray( expr.args ) ? expr.args : Object.values( expr.args );
|
|
859
866
|
args.forEach( e => initExprForQuery( e, query ) );
|
|
860
867
|
}
|
|
861
868
|
else if (expr.path && expr.$expected === 'exists') {
|
|
862
869
|
// TODO: does really the parser has to set $expected?
|
|
863
870
|
expr.$expected = 'approved-exists';
|
|
864
|
-
approveExistsInChildren(expr);
|
|
871
|
+
approveExistsInChildren( expr );
|
|
865
872
|
}
|
|
866
873
|
}
|
|
867
874
|
|
|
@@ -938,7 +945,7 @@ function define( model ) {
|
|
|
938
945
|
}
|
|
939
946
|
// col.$annotations no available for CSN input, have to search.
|
|
940
947
|
// Warning about first annotation should be enough to avoid spam.
|
|
941
|
-
const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
|
|
948
|
+
const firstAnno = Object.keys( col ).find( key => key.startsWith( '@' ) );
|
|
942
949
|
if (firstAnno) {
|
|
943
950
|
warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
|
|
944
951
|
{ code: '.{ ‹inline› }' } );
|
|
@@ -956,7 +963,7 @@ function define( model ) {
|
|
|
956
963
|
// TODO: use `parent` for semantic location, use `-excluding`
|
|
957
964
|
warning( 'query-ignoring-exclude', [ parent.excludingDict[$location], user ],
|
|
958
965
|
{ prop: '*' },
|
|
959
|
-
'Excluding elements without wildcard $(PROP) has no effect');
|
|
966
|
+
'Excluding elements without wildcard $(PROP) has no effect' );
|
|
960
967
|
}
|
|
961
968
|
}
|
|
962
969
|
|
|
@@ -983,11 +990,11 @@ function define( model ) {
|
|
|
983
990
|
exprOrPathElement.$expected = 'approved-exists';
|
|
984
991
|
// Drill down
|
|
985
992
|
if (exprOrPathElement.args)
|
|
986
|
-
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
993
|
+
exprOrPathElement.args.forEach( elem => approveExistsInChildren( elem ) );
|
|
987
994
|
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
988
|
-
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
995
|
+
exprOrPathElement.where.args.forEach( elem => approveExistsInChildren( elem ) );
|
|
989
996
|
else if (exprOrPathElement.path)
|
|
990
|
-
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
997
|
+
exprOrPathElement.path.forEach( elem => approveExistsInChildren( elem ) );
|
|
991
998
|
}
|
|
992
999
|
// TODO: we might issue 'expr-unexpected-exists' and 'expr-no-subquery' already in
|
|
993
1000
|
// define.js (using a to-be-written expression traversal function in utils.js)
|
|
@@ -1219,18 +1226,25 @@ function define( model ) {
|
|
|
1219
1226
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
1220
1227
|
if (!dict)
|
|
1221
1228
|
return false;
|
|
1222
|
-
const names = Object.keys( dict );
|
|
1223
|
-
if (!names.length) // TODO: re-check - really allow empty dict if no other?
|
|
1224
|
-
return false;
|
|
1225
1229
|
const feature = kindProperties[parent.kind][prop];
|
|
1226
1230
|
if (feature &&
|
|
1227
1231
|
(feature === true || construct.kind !== 'extend' || feature( prop, parent )))
|
|
1228
1232
|
return true;
|
|
1229
1233
|
const location = dict[$location];
|
|
1234
|
+
|
|
1235
|
+
// TODO: a bit inconsistent = not a simple switch on `prop`…
|
|
1230
1236
|
if (prop === 'actions') {
|
|
1231
|
-
|
|
1232
|
-
|
|
1237
|
+
if (Object.keys( dict ).length) {
|
|
1238
|
+
error( 'def-unexpected-actions', [ location, construct ], {}, // TODO: ext-
|
|
1239
|
+
'Actions and functions only exist top-level and for entities' ); // or aspects
|
|
1240
|
+
}
|
|
1241
|
+
else {
|
|
1242
|
+
warning( 'ext-ignoring-actions', [ location, construct ], {},
|
|
1243
|
+
'Actions and functions only exist top-level and for entities' );
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1233
1246
|
}
|
|
1247
|
+
//
|
|
1234
1248
|
else if (parent.kind === 'action' || parent.kind === 'function') {
|
|
1235
1249
|
error( 'ext-unexpected-action', [ construct.location, construct ], { '#': parent.kind }, {
|
|
1236
1250
|
std: 'Actions and functions can\'t be extended, only annotated',
|
|
@@ -1252,10 +1266,15 @@ function define( model ) {
|
|
|
1252
1266
|
}
|
|
1253
1267
|
}
|
|
1254
1268
|
else if (feature) { // allowed in principle, but not with extend
|
|
1255
|
-
if (
|
|
1269
|
+
if (!Object.keys( dict ).length) {
|
|
1270
|
+
warning( 'ext-ignoring-elements', [ location, construct ], {},
|
|
1271
|
+
'Only structures with directly specified elements can be extended by elements' );
|
|
1272
|
+
return false;
|
|
1273
|
+
}
|
|
1274
|
+
else if (parent.$inferred === 'include') { // special case for better error message
|
|
1256
1275
|
const variant = (construct.enum || construct.elements) ? 'elements' : 'std';
|
|
1257
1276
|
error( 'ref-expected-direct-structure', [ location, construct ],
|
|
1258
|
-
{ '#': variant, art: parent });
|
|
1277
|
+
{ '#': variant, art: parent } );
|
|
1259
1278
|
}
|
|
1260
1279
|
else {
|
|
1261
1280
|
error( 'extend-type', [ location, construct ], {},
|
|
@@ -1276,8 +1295,10 @@ function define( model ) {
|
|
|
1276
1295
|
return construct === parent;
|
|
1277
1296
|
}
|
|
1278
1297
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1298
|
+
/**
|
|
1299
|
+
* Return whether the `target` is actually a `targetAspect`
|
|
1300
|
+
* TODO: really do that here and not in kick-start.js?
|
|
1301
|
+
*/
|
|
1281
1302
|
function targetIsTargetAspect( elem ) {
|
|
1282
1303
|
const { target } = elem;
|
|
1283
1304
|
if (target.elements) {
|