@sap/cds-compiler 3.7.2 → 3.8.0
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 +63 -4
- package/bin/cdsc.js +3 -0
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +15 -0
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +61 -22
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +5 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +64 -22
- package/lib/base/messages.js +12 -7
- package/lib/base/model.js +3 -2
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/hasPersistedElements.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/onConditions.js +9 -6
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +1 -2
- package/lib/compiler/assert-consistency.js +24 -5
- package/lib/compiler/base.js +49 -2
- package/lib/compiler/builtins.js +15 -6
- package/lib/compiler/checks.js +4 -4
- package/lib/compiler/define.js +59 -80
- package/lib/compiler/extend.js +701 -498
- package/lib/compiler/finalize-parse-cdl.js +4 -3
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +2 -2
- package/lib/compiler/populate.js +17 -9
- package/lib/compiler/propagator.js +12 -5
- package/lib/compiler/resolve.js +26 -173
- package/lib/compiler/shared.js +12 -53
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +2 -2
- package/lib/edm/annotations/genericTranslation.js +124 -46
- package/lib/edm/csn2edm.js +22 -1
- package/lib/edm/edmPreprocessor.js +41 -21
- package/lib/gen/Dictionary.json +4 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4810 -4482
- package/lib/inspect/inspectPropagation.js +20 -36
- package/lib/json/from-csn.js +55 -5
- package/lib/json/to-csn.js +71 -110
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +47 -8
- package/lib/language/language.g4 +88 -62
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +14 -2
- package/lib/model/csnUtils.js +11 -74
- package/lib/model/revealInternalProperties.js +3 -0
- package/lib/optionProcessor.js +3 -0
- package/lib/render/toCdl.js +203 -104
- package/lib/render/toHdbcds.js +0 -1
- package/lib/render/toRename.js +14 -51
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/rewriteCalculatedElements.js +55 -14
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +21 -14
- package/lib/transform/parseExpr.js +2 -0
- package/lib/transform/transformUtilsNew.js +36 -9
- package/lib/transform/translateAssocsToJoins.js +11 -4
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
|
@@ -29,17 +29,18 @@ function finalizeParseCdl( model ) {
|
|
|
29
29
|
resolveUncheckedPath,
|
|
30
30
|
resolveTypeArgumentsUnchecked,
|
|
31
31
|
initMembers,
|
|
32
|
-
extensionsDict,
|
|
33
32
|
} = model.$functions;
|
|
34
33
|
|
|
35
34
|
resolveTypesAndExtensionsForParseCdl();
|
|
36
35
|
return;
|
|
37
36
|
|
|
38
37
|
function resolveTypesAndExtensionsForParseCdl() {
|
|
38
|
+
const late = model.$lateExtensions;
|
|
39
39
|
const extensions = [];
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// TODO: why not just use the extensions as they are from the first source?
|
|
42
|
+
for (const name in late) {
|
|
43
|
+
for (const ext of late[name]._extensions) {
|
|
43
44
|
ext.name.absolute = resolveUncheckedPath( ext.name, 'extend', ext );
|
|
44
45
|
// Initialize members and define annotations in sub-elements.
|
|
45
46
|
initMembers( ext, ext, ext._block, true );
|
package/lib/compiler/index.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
|
|
18
|
-
const parseLanguage = require('../language/antlrParser');
|
|
18
|
+
const parseLanguage = require('../language/antlrParser'); // TODO: should we do some lazyload here?
|
|
19
19
|
const parseCsn = require('../json/from-csn');
|
|
20
20
|
|
|
21
21
|
const assertConsistency = require('./assert-consistency');
|
|
@@ -31,9 +31,9 @@ function kickStart( model ) {
|
|
|
31
31
|
*/
|
|
32
32
|
function setAncestorsAndService( name ) {
|
|
33
33
|
const art = model.definitions[name];
|
|
34
|
-
if (
|
|
34
|
+
if (art._parent === undefined)
|
|
35
35
|
return; // nothing to do for builtins and redefinitions
|
|
36
|
-
if (art.query &&
|
|
36
|
+
if (art.query && art._ancestors === undefined)
|
|
37
37
|
setProjectionAncestors( art );
|
|
38
38
|
|
|
39
39
|
let parent = art._parent;
|
package/lib/compiler/populate.js
CHANGED
|
@@ -26,7 +26,6 @@ const {
|
|
|
26
26
|
const {
|
|
27
27
|
dictAdd, dictAddArray, dictFirst, dictForEach,
|
|
28
28
|
} = require('../base/dictionaries');
|
|
29
|
-
const { dictLocation } = require('../base/location');
|
|
30
29
|
const { weakLocation } = require('../base/messages');
|
|
31
30
|
const { CompilerAssertion } = require('../base/error');
|
|
32
31
|
|
|
@@ -48,6 +47,7 @@ const {
|
|
|
48
47
|
} = require('./utils');
|
|
49
48
|
|
|
50
49
|
const $inferred = Symbol.for('cds.$inferred');
|
|
50
|
+
const $location = Symbol.for('cds.$location');
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
// Export function of this file.
|
|
@@ -61,6 +61,8 @@ function populate( model ) {
|
|
|
61
61
|
resolvePath,
|
|
62
62
|
attachAndEmitValidNames,
|
|
63
63
|
initArtifact,
|
|
64
|
+
chooseAnnotationsInArtifact,
|
|
65
|
+
extendArtifactAfter,
|
|
64
66
|
} = model.$functions;
|
|
65
67
|
model.$volatileFunctions.environment = environment;
|
|
66
68
|
Object.assign( model.$functions, {
|
|
@@ -191,13 +193,13 @@ function populate( model ) {
|
|
|
191
193
|
if (!art)
|
|
192
194
|
return art;
|
|
193
195
|
// if (--depth) throw Error(`ET: ${ Object.keys(art) }`)
|
|
194
|
-
if (
|
|
196
|
+
if (art._effectiveType !== undefined)
|
|
195
197
|
return art._effectiveType;
|
|
196
198
|
|
|
197
199
|
// console.log(message( null, art.location, art, {}, 'Info','FT').toString())
|
|
198
200
|
const chain = [];
|
|
199
201
|
// console.log( 'ET-START:', art.kind, art.name )
|
|
200
|
-
while (art &&
|
|
202
|
+
while (art && art._effectiveType === undefined) {
|
|
201
203
|
setLink( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
202
204
|
chain.push( art );
|
|
203
205
|
art = getOrigin( art );
|
|
@@ -210,13 +212,15 @@ function populate( model ) {
|
|
|
210
212
|
|
|
211
213
|
chain.reverse();
|
|
212
214
|
for (const a of chain) {
|
|
213
|
-
// console.log( 'ET-DO:', a.name, art?.kind )
|
|
214
215
|
// Without type, value.path or _origin at beginning, link to itself:
|
|
216
|
+
chooseAnnotationsInArtifact( a );
|
|
215
217
|
art = populateArtifact( a, art ) || a;
|
|
216
218
|
if (a.elements$ || a.enum$)
|
|
217
219
|
mergeSpecifiedElementsOrEnum( a );
|
|
218
220
|
setLink( a, '_effectiveType', art );
|
|
219
221
|
a.$effectiveSeqNo = ++effectiveSeqNo;
|
|
222
|
+
// console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
|
|
223
|
+
extendArtifactAfter( a ); // after setting _effectiveType (for messages)
|
|
220
224
|
}
|
|
221
225
|
// console.log( 'ET-END:', art?.kind, art?.name )
|
|
222
226
|
return art;
|
|
@@ -226,6 +230,8 @@ function populate( model ) {
|
|
|
226
230
|
// Name-resolution relevant properties directly at artifact:
|
|
227
231
|
// ‹view›.elements of input must have been moved (to elements$) before!
|
|
228
232
|
// console.log('Q:',art.elements,art.enum,art.items,!!art.query)
|
|
233
|
+
if (art.includes) // first version of includes via effectiveTpe()
|
|
234
|
+
art.includes.forEach( i => effectiveType( i._artifact ) );
|
|
229
235
|
if (art.elements != null || art.enum != null || art.items != null)
|
|
230
236
|
return art;
|
|
231
237
|
if (art.target) {
|
|
@@ -294,7 +300,7 @@ function populate( model ) {
|
|
|
294
300
|
if (!art)
|
|
295
301
|
return undefined; // TODO: null?
|
|
296
302
|
// if (--depth) throw Error(`GOR: ${ Object.keys(art) }`)
|
|
297
|
-
if (
|
|
303
|
+
if (art._origin !== undefined)
|
|
298
304
|
return art._origin;
|
|
299
305
|
if (art.type) // not stored in _origin
|
|
300
306
|
return resolveType( art.type, art );
|
|
@@ -328,7 +334,7 @@ function populate( model ) {
|
|
|
328
334
|
}
|
|
329
335
|
|
|
330
336
|
function resolveType( ref, user ) {
|
|
331
|
-
if (
|
|
337
|
+
if (ref._artifact !== undefined)
|
|
332
338
|
return ref._artifact;
|
|
333
339
|
while (user._outer) // in items
|
|
334
340
|
user = user._outer;
|
|
@@ -631,8 +637,10 @@ function populate( model ) {
|
|
|
631
637
|
if (!effectiveType( assoc )?.target)
|
|
632
638
|
return initFromColumns( elem, elem.expand );
|
|
633
639
|
const { targetMax } = path[path.length - 1].cardinality || getCardinality( assoc );
|
|
634
|
-
if (targetMax && (targetMax.val === '*' || targetMax.val > 1))
|
|
635
|
-
elem.items = { location:
|
|
640
|
+
if (targetMax && (targetMax.val === '*' || targetMax.val > 1)) {
|
|
641
|
+
elem.items = { location: elem.expand[$location] };
|
|
642
|
+
setLink( elem.items, '_outer', elem );
|
|
643
|
+
}
|
|
636
644
|
return initFromColumns( elem, elem.expand );
|
|
637
645
|
}
|
|
638
646
|
|
|
@@ -1286,7 +1294,7 @@ function populate( model ) {
|
|
|
1286
1294
|
setLink( art, '_service', service );
|
|
1287
1295
|
setLink( art, '_block', model.$internal );
|
|
1288
1296
|
initArtifact( art, !!autoexposed );
|
|
1289
|
-
effectiveType( art );
|
|
1297
|
+
effectiveType( art );
|
|
1290
1298
|
// TODO: try to set locations of elements locations of orig target elements
|
|
1291
1299
|
newAutoExposed.push( art );
|
|
1292
1300
|
return art;
|
|
@@ -99,6 +99,13 @@ function propagate( model ) {
|
|
|
99
99
|
for (const target of targets) {
|
|
100
100
|
const origin = getOrigin( target );
|
|
101
101
|
if (origin) {
|
|
102
|
+
// Calculated elements that are simple references: `calc = field;`.
|
|
103
|
+
// Respect sibling properties in inheritance.
|
|
104
|
+
if (target._calcOrigin?._origin && target.value?._artifact) {
|
|
105
|
+
chain.push({ target, source: target.value._artifact });
|
|
106
|
+
if (checkAndSetStatus( target.value._artifact ))
|
|
107
|
+
news.push(target.value._artifact);
|
|
108
|
+
}
|
|
102
109
|
chain.push( { target, source: origin } );
|
|
103
110
|
if (checkAndSetStatus( origin ))
|
|
104
111
|
news.push( origin );
|
|
@@ -178,13 +185,13 @@ function propagate( model ) {
|
|
|
178
185
|
}
|
|
179
186
|
else {
|
|
180
187
|
target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );
|
|
181
|
-
if (
|
|
188
|
+
if (val._artifact !== undefined)
|
|
182
189
|
setLink( target[prop], '_artifact', val._artifact );
|
|
183
|
-
if (
|
|
190
|
+
if (val._outer !== undefined)
|
|
184
191
|
setLink( target[prop], '_outer', val._outer );
|
|
185
|
-
if (
|
|
192
|
+
if (val._parent !== undefined)
|
|
186
193
|
setLink( target[prop], '_parent', val._parent );
|
|
187
|
-
if (
|
|
194
|
+
if (val._main !== undefined)
|
|
188
195
|
setLink( target[prop], '_main', val._main );
|
|
189
196
|
}
|
|
190
197
|
}
|
|
@@ -359,7 +366,7 @@ function checkAndSetStatus( art ) {
|
|
|
359
366
|
}
|
|
360
367
|
|
|
361
368
|
function setEffectiveType( target, source ) {
|
|
362
|
-
if (
|
|
369
|
+
if (source._effectiveType !== undefined)
|
|
363
370
|
setLink( target, '_effectiveType', source._effectiveType);
|
|
364
371
|
}
|
|
365
372
|
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -46,21 +46,18 @@ const {
|
|
|
46
46
|
} = require('../base/model');
|
|
47
47
|
const { dictAdd } = require('../base/dictionaries');
|
|
48
48
|
const { dictLocation } = require('../base/location');
|
|
49
|
-
const {
|
|
49
|
+
const { weakLocation } = require('../base/messages');
|
|
50
50
|
const { combinedLocation } = require('../base/location');
|
|
51
51
|
const { typeParameters } = require('./builtins');
|
|
52
52
|
|
|
53
|
-
const { kindProperties } = require('./base');
|
|
54
53
|
const {
|
|
55
54
|
pushLink,
|
|
56
55
|
setLink,
|
|
57
56
|
setArtifactLink,
|
|
58
57
|
setMemberParent,
|
|
59
58
|
withAssociation,
|
|
60
|
-
storeExtension,
|
|
61
59
|
dependsOn,
|
|
62
60
|
dependsOnSilent,
|
|
63
|
-
setExpandStatusAnnotate,
|
|
64
61
|
testExpr,
|
|
65
62
|
targetMaxNotOne,
|
|
66
63
|
traverseQueryPost,
|
|
@@ -84,16 +81,9 @@ function resolve( model ) {
|
|
|
84
81
|
} = model.$messageFunctions;
|
|
85
82
|
const {
|
|
86
83
|
resolvePath,
|
|
87
|
-
checkAnnotate,
|
|
88
|
-
initAnnotations,
|
|
89
|
-
copyAnnotationsForExtensions,
|
|
90
|
-
attachAndEmitValidNames,
|
|
91
84
|
lateExtensions,
|
|
92
|
-
applyTypeExtensions,
|
|
93
85
|
effectiveType,
|
|
94
86
|
getOrigin,
|
|
95
|
-
chooseAnnotationsInArtifact,
|
|
96
|
-
extensionFor,
|
|
97
87
|
resolveType,
|
|
98
88
|
resolveTypeArgumentsUnchecked,
|
|
99
89
|
} = model.$functions;
|
|
@@ -126,14 +116,9 @@ function resolve( model ) {
|
|
|
126
116
|
// Phase 4: resolve all artifacts:
|
|
127
117
|
forEachDefinition( model, resolveRefs );
|
|
128
118
|
forEachGeneric( model, 'vocabularies', resolveRefs );
|
|
129
|
-
// for
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Phase 6: apply ANNOTATE on auto-exposed entities and unknown artifacts:
|
|
133
|
-
lateExtensions( annotateMembers );
|
|
134
|
-
if (model.extensions)
|
|
135
|
-
model.extensions.map( annotateUnknown );
|
|
136
|
-
// Phase 7: report cyclic dependencies:
|
|
119
|
+
// create “super” ANNOTATE statements for annotations on unknown artifacts:
|
|
120
|
+
lateExtensions();
|
|
121
|
+
// report cyclic dependencies:
|
|
137
122
|
detectCycles( model.definitions, ( user, art, location ) => {
|
|
138
123
|
if (location) {
|
|
139
124
|
error( 'ref-cyclic', [ location, user ], { art }, {
|
|
@@ -461,9 +446,6 @@ function resolve( model ) {
|
|
|
461
446
|
checkStructureCast(art);
|
|
462
447
|
}
|
|
463
448
|
|
|
464
|
-
annotateMembers( art ); // TODO recheck - recursively, but also forEachMember below
|
|
465
|
-
chooseAnnotationsInArtifact( art );
|
|
466
|
-
|
|
467
449
|
forEachMember( art, resolveRefs, art.targetAspect );
|
|
468
450
|
|
|
469
451
|
// Set '@Core.Computed' in the Core Compiler to have it propagated...
|
|
@@ -497,7 +479,15 @@ function resolve( model ) {
|
|
|
497
479
|
parent = parent._parent;
|
|
498
480
|
|
|
499
481
|
if (!allowedInKind.includes(art._main.kind)) {
|
|
500
|
-
|
|
482
|
+
if (art.$inferred === 'include') {
|
|
483
|
+
// even for include-chains, we find the correct ref due to element-expansion.
|
|
484
|
+
const include = art._main.includes.find(i => i._artifact === art._origin._main);
|
|
485
|
+
error('ref-invalid-calc-elem', [ include.location || art.value.location, art ],
|
|
486
|
+
{ '#': art._main.kind });
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
|
|
490
|
+
}
|
|
501
491
|
}
|
|
502
492
|
else if (!allowedInKind.includes(parent.kind)) {
|
|
503
493
|
error( 'def-invalid-calc-elem', loc, { '#': parent.kind } );
|
|
@@ -560,149 +550,6 @@ function resolve( model ) {
|
|
|
560
550
|
}
|
|
561
551
|
}
|
|
562
552
|
|
|
563
|
-
// Phase 4 - annotations ---------------------------------------------------
|
|
564
|
-
// Some functions remain here for the moment, as resolve functionality is called
|
|
565
|
-
// from annotate functions, which should not be the case (TODO!)
|
|
566
|
-
|
|
567
|
-
function annotateUnknown( ext ) {
|
|
568
|
-
// extensions may have annotations for elements/actions/... which may
|
|
569
|
-
// themselves may be unknown
|
|
570
|
-
forEachMember(ext, annotateUnknown);
|
|
571
|
-
|
|
572
|
-
if (ext.$extension) // extension for known artifact -> already applied
|
|
573
|
-
return;
|
|
574
|
-
annotateMembers( ext );
|
|
575
|
-
chooseAnnotationsInArtifact( ext );
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* @param {XSN.Artifact} art
|
|
580
|
-
* @param {XSN.Extension[]} [extensions]
|
|
581
|
-
* @param {string} [prop]
|
|
582
|
-
* @param {string} [name]
|
|
583
|
-
* @param {object} [parent]
|
|
584
|
-
* @param {string} [kind]
|
|
585
|
-
*/
|
|
586
|
-
function annotateMembers( art, extensions, prop, name, parent, kind ) {
|
|
587
|
-
const showMsg = !art && parent && parent.kind !== 'annotate';
|
|
588
|
-
if (!art && extensions && extensions.length) {
|
|
589
|
-
if (Array.isArray( parent ))
|
|
590
|
-
return;
|
|
591
|
-
const parentExt = extensionFor(parent);
|
|
592
|
-
art = parentExt[prop] && parentExt[prop][name];
|
|
593
|
-
if (!art) {
|
|
594
|
-
art = {
|
|
595
|
-
kind, // for setMemberParent()
|
|
596
|
-
name: { id: name, location: extensions[0].name.location },
|
|
597
|
-
location: extensions[0].location,
|
|
598
|
-
};
|
|
599
|
-
setMemberParent( art, name, parentExt, prop );
|
|
600
|
-
art.kind = 'annotate'; // after setMemberParent()!
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
for (const ext of extensions || []) {
|
|
605
|
-
if ('_artifact' in ext.name) // already applied
|
|
606
|
-
continue;
|
|
607
|
-
setArtifactLink( ext.name, art );
|
|
608
|
-
|
|
609
|
-
if (art) {
|
|
610
|
-
checkAnnotate( ext, art );
|
|
611
|
-
initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
|
|
612
|
-
copyAnnotationsForExtensions( ext, art );
|
|
613
|
-
// eslint-disable-next-line no-shadow
|
|
614
|
-
forEachMember( ext, ( elem, name, prop ) => {
|
|
615
|
-
storeExtension( elem, name, prop, art, ext._block );
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
if (showMsg) {
|
|
619
|
-
// somehow similar to checkDefinitions():
|
|
620
|
-
const feature = kindProperties[parent.kind][prop];
|
|
621
|
-
if (prop === 'elements' || prop === 'enum') {
|
|
622
|
-
if (!feature) {
|
|
623
|
-
warning( 'anno-unexpected-elements', [ ext.name.location, art ], {},
|
|
624
|
-
'Elements only exist in entities, types or typed constructs' );
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
const isEntity = (parent.returns?.type || parent.type)?._artifact?.kind === 'entity';
|
|
628
|
-
let variant = parent.enum ? 'enum' : 'element';
|
|
629
|
-
if (isEntity)
|
|
630
|
-
variant = 'entity-element';
|
|
631
|
-
else if (parent.returns)
|
|
632
|
-
variant = 'returns';
|
|
633
|
-
notFound( 'anno-undefined-element', ext.name.location, art,
|
|
634
|
-
{ '#': variant, art: parent, name },
|
|
635
|
-
parent.elements || parent.enum );
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
else if (prop === 'actions') {
|
|
639
|
-
if (!feature) {
|
|
640
|
-
warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
|
|
641
|
-
'Actions and functions only exist top-level and for entities' );
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
notFound( 'anno-undefined-action', ext.name.location, art,
|
|
645
|
-
{ art: searchName( parent, name, 'action' ) },
|
|
646
|
-
parent.actions );
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
else if (!feature) {
|
|
650
|
-
warning( 'anno-unexpected-params', [ ext.name.location, art ], {},
|
|
651
|
-
'Parameters only exist for actions or functions' );
|
|
652
|
-
} // TODO: entities betaMod
|
|
653
|
-
else {
|
|
654
|
-
notFound( 'anno-undefined-param', ext.name.location, art,
|
|
655
|
-
{ art: searchName( parent, name, 'param' ) },
|
|
656
|
-
parent.params );
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (art?._annotate) {
|
|
661
|
-
const aor = art.returns || art;
|
|
662
|
-
const obj = aor.items || aor.targetAspect || aor;
|
|
663
|
-
// Currently(?), effectiveType() does not calculate the effective type of
|
|
664
|
-
// its line item:
|
|
665
|
-
if (art._annotate.elements) // explicit $expand on aor needed
|
|
666
|
-
setExpandStatusAnnotate( aor, 'annotate' );
|
|
667
|
-
annotate( obj, 'element', 'elements', 'enum', art );
|
|
668
|
-
annotate( art, 'action', 'actions' );
|
|
669
|
-
annotate( art, 'param', 'params' );
|
|
670
|
-
// const { returns } = art._annotate;
|
|
671
|
-
// if (returns) {
|
|
672
|
-
// const dict = returns.elements;
|
|
673
|
-
// const env = obj.returns && obj.returns.elements || null;
|
|
674
|
-
// for (const n in dict)
|
|
675
|
-
// annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
|
|
676
|
-
// }
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
if (art?._extendType) {
|
|
680
|
-
// Only works because annotateMembers is called in resolveRefs() _after_ `.type` was resolved.
|
|
681
|
-
// TODO: If we allow extending included elements, we may need custom $expand,
|
|
682
|
-
// similar to annotate above.
|
|
683
|
-
art._extendType.forEach(resolveRefs);
|
|
684
|
-
applyTypeExtensions(art);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
return;
|
|
688
|
-
|
|
689
|
-
function notFound( msgId, location, address, args, validDict ) {
|
|
690
|
-
// TODO: probably move this to shared.js and use for EXTEND, too
|
|
691
|
-
const msg = message( msgId, [ location, address ], args );
|
|
692
|
-
attachAndEmitValidNames(msg, validDict);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// eslint-disable-next-line no-shadow
|
|
696
|
-
function annotate( obj, kind, prop, altProp, parent = obj ) {
|
|
697
|
-
const dict = art._annotate[prop];
|
|
698
|
-
if (dict && art._annotate[prop])
|
|
699
|
-
setExpandStatusAnnotate( art, 'annotate' );
|
|
700
|
-
const env = obj[prop] || altProp && obj[altProp] || null;
|
|
701
|
-
for (const n in dict)
|
|
702
|
-
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
553
|
// Phase 4 - queries and associations --------------------------------------
|
|
707
554
|
|
|
708
555
|
function resolveQuery( query ) {
|
|
@@ -1069,6 +916,7 @@ function resolve( model ) {
|
|
|
1069
916
|
// TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
|
|
1070
917
|
// trick may be removed if effectiveType() does not stop at enums.
|
|
1071
918
|
// TODO: this is wrong - we must check typeArt.enum, not its effectiveType
|
|
919
|
+
// TODO: this function is not complete(!): parallel `elements` and `length`, … - rework function
|
|
1072
920
|
const cyclic = new Set();
|
|
1073
921
|
let effectiveTypeArt = effectiveType( typeArt );
|
|
1074
922
|
while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
|
|
@@ -1177,7 +1025,7 @@ function resolve( model ) {
|
|
|
1177
1025
|
}
|
|
1178
1026
|
|
|
1179
1027
|
function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
|
|
1180
|
-
const alias = step._navigation
|
|
1028
|
+
const alias = (step._navigation?.kind === '$tableAlias') ? step._navigation : null;
|
|
1181
1029
|
const type = alias || effectiveType( step._artifact );
|
|
1182
1030
|
const art = (type && type.target) ? type.target._artifact : type;
|
|
1183
1031
|
if (!art)
|
|
@@ -1190,14 +1038,19 @@ function resolve( model ) {
|
|
|
1190
1038
|
if (step.where)
|
|
1191
1039
|
resolveExpr( step.where, 'filter', user, environment( type ) );
|
|
1192
1040
|
}
|
|
1193
|
-
else if (step.where
|
|
1041
|
+
else if (step.where?.location || step.cardinality ) {
|
|
1194
1042
|
const location = combinedLocation( step.where, step.cardinality );
|
|
1043
|
+
let variant = alias ? 'tableAlias' : 'std';
|
|
1044
|
+
if (expected === 'from')
|
|
1045
|
+
variant = 'from';
|
|
1195
1046
|
// XSN TODO: filter$location including […]
|
|
1196
|
-
message( 'expr-no-filter', [ location, user ], { '#':
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1047
|
+
message( 'expr-no-filter', [ location, user ], { '#': variant }, {
|
|
1048
|
+
std: 'A filter can only be provided when navigating along associations',
|
|
1049
|
+
// to help users for `… from E:toF { toF[…].x }`
|
|
1050
|
+
// eslint-disable-next-line max-len
|
|
1051
|
+
tableAlias: 'A filter can only be provided when navigating along associations, but found table alias',
|
|
1052
|
+
from: 'A filter can only be provided for the source entity or associations',
|
|
1053
|
+
} );
|
|
1201
1054
|
}
|
|
1202
1055
|
}
|
|
1203
1056
|
|
package/lib/compiler/shared.js
CHANGED
|
@@ -182,7 +182,6 @@ function fns( model ) {
|
|
|
182
182
|
resolveUncheckedPath,
|
|
183
183
|
resolveTypeArgumentsUnchecked,
|
|
184
184
|
resolvePath,
|
|
185
|
-
checkAnnotate,
|
|
186
185
|
attachAndEmitValidNames,
|
|
187
186
|
} );
|
|
188
187
|
return;
|
|
@@ -301,7 +300,7 @@ function fns( model ) {
|
|
|
301
300
|
function resolvePath( ref, expected, user, extDict, msgArt ) {
|
|
302
301
|
if (ref == null) // no references -> nothing to do
|
|
303
302
|
return undefined;
|
|
304
|
-
if (
|
|
303
|
+
if (ref._artifact !== undefined)
|
|
305
304
|
return ref._artifact;
|
|
306
305
|
if (!ref.path || ref.path.broken || !ref.path.length) {
|
|
307
306
|
// incomplete type AST or empty env (already reported)
|
|
@@ -598,7 +597,7 @@ function fns( model ) {
|
|
|
598
597
|
// if (head.id === 'k') {console.log(Object.keys(user));
|
|
599
598
|
// throw new CompilerAssertion(JSON.stringify(user.name))}
|
|
600
599
|
// if head._artifact is set or is null then it was already computed once
|
|
601
|
-
if (
|
|
600
|
+
if (head._artifact !== undefined)
|
|
602
601
|
return Array.isArray(head._artifact) ? false : head._artifact;
|
|
603
602
|
// console.log(pathName(path), !spec.next && !extDict &&
|
|
604
603
|
// (spec.useDefinitions || env.$frontend === 'json' || env))
|
|
@@ -616,7 +615,7 @@ function fns( model ) {
|
|
|
616
615
|
break; // TODO: probably remove _$next link
|
|
617
616
|
const e = art.artifacts || art.$tableAliases || Object.create(null);
|
|
618
617
|
const r = e[head.id];
|
|
619
|
-
if (r) {
|
|
618
|
+
if (r && r.$inferred !== '$internal') {
|
|
620
619
|
if (Array.isArray(r)) { // redefinitions
|
|
621
620
|
setArtifactLink( head, r );
|
|
622
621
|
return false;
|
|
@@ -650,11 +649,14 @@ function fns( model ) {
|
|
|
650
649
|
if (r[0].kind === '$navElement' && r.every( e => !e._parent.$duplicates )) {
|
|
651
650
|
// only complain about ambiguous source elements if we do not have
|
|
652
651
|
// duplicate table aliases, only mention non-ambiguous source elems
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
652
|
+
const uniqueNames = r.filter( e => !e.$duplicates);
|
|
653
|
+
if (uniqueNames.length) {
|
|
654
|
+
const names = uniqueNames.filter( e => e._parent.$inferred !== '$internal' )
|
|
655
|
+
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
656
|
+
let variant = names.length === uniqueNames.length ? 'std' : 'few';
|
|
657
|
+
if (names.length === 0)
|
|
658
|
+
variant = 'none';
|
|
659
|
+
error( 'ref-ambiguous', [ head.location, user ], { '#': variant, id: head.id, names });
|
|
658
660
|
}
|
|
659
661
|
}
|
|
660
662
|
setArtifactLink( head, r );
|
|
@@ -935,7 +937,7 @@ function fns( model ) {
|
|
|
935
937
|
msg.validNames = Object.create( null );
|
|
936
938
|
for (const name of Object.keys( valid )) {
|
|
937
939
|
// ignore internal types such as cds.Association
|
|
938
|
-
if (valid[name].internal || valid[name].deprecated)
|
|
940
|
+
if (valid[name].internal || valid[name].deprecated || valid[name].$inferred === '$internal')
|
|
939
941
|
continue;
|
|
940
942
|
msg.validNames[name] = valid[name];
|
|
941
943
|
}
|
|
@@ -949,49 +951,6 @@ function fns( model ) {
|
|
|
949
951
|
{ std: `Valid: ${ names.sort().join(', ') }`, zero: 'No valid names' });
|
|
950
952
|
}
|
|
951
953
|
}
|
|
952
|
-
|
|
953
|
-
// Issue messages for annotations on namespaces and builtins
|
|
954
|
-
// (TODO: really here?, probably split main artifacts vs returns)
|
|
955
|
-
// see also lateExtensions() where similar messages are reported
|
|
956
|
-
function checkAnnotate( construct, art ) {
|
|
957
|
-
// TODO: Handle extend statements properly: Different message for empty extend?
|
|
958
|
-
|
|
959
|
-
// Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
|
|
960
|
-
// they can still be applied. Namespace annotations are extracted in to-csn.js
|
|
961
|
-
// In parseCdl mode USINGs and other unknown references are generated as
|
|
962
|
-
// namespaces which would lead to false positives.
|
|
963
|
-
// TODO: should this really be different to annotate-unknown?
|
|
964
|
-
if (art.kind === 'namespace') {
|
|
965
|
-
info( 'anno-namespace', [ construct.name.location, construct ], {},
|
|
966
|
-
'Namespaces can\'t be annotated' );
|
|
967
|
-
}
|
|
968
|
-
// Builtin annotations would also get lost. Same as for namespaces:
|
|
969
|
-
// extracted in to-csn.js
|
|
970
|
-
else if (art.builtin === true) {
|
|
971
|
-
info( 'anno-builtin', [ construct.name.location, construct ], {},
|
|
972
|
-
'Builtin types should not be annotated. Use custom type instead' );
|
|
973
|
-
}
|
|
974
|
-
// --> without art._block, art not found
|
|
975
|
-
else if (construct.kind === 'annotate' && art._block?.$frontend === 'cdl') {
|
|
976
|
-
if (construct.$syntax === 'returns' && art.kind !== 'action' && art.kind !== 'function' ) {
|
|
977
|
-
// `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
|
|
978
|
-
// for non-actions. We can't only check for !art.returns, because `action A();` is valid.
|
|
979
|
-
// `art._block` ensures that `art` is a defined def.
|
|
980
|
-
warning('ext-unexpected-returns', [ construct.name.location, construct ],
|
|
981
|
-
{ keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
|
|
982
|
-
}
|
|
983
|
-
else if (construct.$syntax !== 'returns' &&
|
|
984
|
-
(art.kind === 'action' || art.kind === 'function') && construct.elements) {
|
|
985
|
-
warning('ext-expected-returns', [ construct.name.location, construct ], {
|
|
986
|
-
'#': art.kind, keyword: 'returns', code: 'annotate ‹name› with returns { … }',
|
|
987
|
-
}, {
|
|
988
|
-
std: 'Expected $(CODE)', // unused variant
|
|
989
|
-
action: 'Expected $(KEYWORD) when annotating action return structure, i.e. $(CODE)',
|
|
990
|
-
function: 'Expected $(KEYWORD) when annotating function return structure, i.e. $(CODE)',
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
954
|
}
|
|
996
955
|
|
|
997
956
|
module.exports = {
|
|
@@ -256,7 +256,7 @@ function tweakAssocs( model ) {
|
|
|
256
256
|
const fk = linkToOrigin( orig, name, elem, 'foreignKeys', elem.location );
|
|
257
257
|
fk.$inferred = 'rewrite'; // Override existing value; TODO: other $inferred value?
|
|
258
258
|
// TODO: re-check for case that foreign key is managed association
|
|
259
|
-
if (
|
|
259
|
+
if (orig._effectiveType !== undefined)
|
|
260
260
|
setLink( fk, '_effectiveType', orig._effectiveType);
|
|
261
261
|
const te = copyExpr( orig.targetElement, elem.location );
|
|
262
262
|
if (elem._redirected) {
|
package/lib/compiler/utils.js
CHANGED
|
@@ -55,7 +55,7 @@ function annotationLocation( anno ) {
|
|
|
55
55
|
* @param {*} [val]
|
|
56
56
|
* @param {string} [literal]
|
|
57
57
|
*/
|
|
58
|
-
function
|
|
58
|
+
function setAnnotation( art, anno, location = art.location, val = true, literal = 'boolean' ) {
|
|
59
59
|
if (art[anno]) // do not overwrite user-defined including null
|
|
60
60
|
return;
|
|
61
61
|
art[anno] = {
|
|
@@ -448,7 +448,7 @@ module.exports = {
|
|
|
448
448
|
annotationIsFalse,
|
|
449
449
|
annotationHasEllipsis,
|
|
450
450
|
annotationLocation,
|
|
451
|
-
|
|
451
|
+
setAnnotation,
|
|
452
452
|
setLink,
|
|
453
453
|
setArtifactLink,
|
|
454
454
|
linkToOrigin,
|