@sap/cds-compiler 3.7.2 → 3.8.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 +71 -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 +25 -6
- package/lib/compiler/base.js +51 -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 +717 -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 +20 -58
- 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 +4844 -4508
- package/lib/inspect/inspectPropagation.js +20 -36
- package/lib/json/from-csn.js +56 -7
- package/lib/json/to-csn.js +71 -110
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +49 -9
- package/lib/language/language.g4 +106 -83
- 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 +19 -4
- 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
|
@@ -57,7 +57,8 @@ function fns( model ) {
|
|
|
57
57
|
artItemsCount: Number.MAX_SAFE_INTEGER,
|
|
58
58
|
undefinedDef: 'anno-undefined-def',
|
|
59
59
|
undefinedArt: 'anno-undefined-art',
|
|
60
|
-
allowAutoexposed: true,
|
|
60
|
+
allowAutoexposed: true, // TODO: think about Info/Warning
|
|
61
|
+
noMessageForLocalized: true, // TODO: should we issue a Debug message for code completion?
|
|
61
62
|
},
|
|
62
63
|
type: { // TODO: more detailed later (e.g. for enum base type?)
|
|
63
64
|
envFn: artifactsEnv,
|
|
@@ -182,7 +183,6 @@ function fns( model ) {
|
|
|
182
183
|
resolveUncheckedPath,
|
|
183
184
|
resolveTypeArgumentsUnchecked,
|
|
184
185
|
resolvePath,
|
|
185
|
-
checkAnnotate,
|
|
186
186
|
attachAndEmitValidNames,
|
|
187
187
|
} );
|
|
188
188
|
return;
|
|
@@ -301,7 +301,7 @@ function fns( model ) {
|
|
|
301
301
|
function resolvePath( ref, expected, user, extDict, msgArt ) {
|
|
302
302
|
if (ref == null) // no references -> nothing to do
|
|
303
303
|
return undefined;
|
|
304
|
-
if (
|
|
304
|
+
if (ref._artifact !== undefined)
|
|
305
305
|
return ref._artifact;
|
|
306
306
|
if (!ref.path || ref.path.broken || !ref.path.length) {
|
|
307
307
|
// incomplete type AST or empty env (already reported)
|
|
@@ -598,7 +598,7 @@ function fns( model ) {
|
|
|
598
598
|
// if (head.id === 'k') {console.log(Object.keys(user));
|
|
599
599
|
// throw new CompilerAssertion(JSON.stringify(user.name))}
|
|
600
600
|
// if head._artifact is set or is null then it was already computed once
|
|
601
|
-
if (
|
|
601
|
+
if (head._artifact !== undefined)
|
|
602
602
|
return Array.isArray(head._artifact) ? false : head._artifact;
|
|
603
603
|
// console.log(pathName(path), !spec.next && !extDict &&
|
|
604
604
|
// (spec.useDefinitions || env.$frontend === 'json' || env))
|
|
@@ -616,7 +616,7 @@ function fns( model ) {
|
|
|
616
616
|
break; // TODO: probably remove _$next link
|
|
617
617
|
const e = art.artifacts || art.$tableAliases || Object.create(null);
|
|
618
618
|
const r = e[head.id];
|
|
619
|
-
if (r) {
|
|
619
|
+
if (r && r.$inferred !== '$internal') {
|
|
620
620
|
if (Array.isArray(r)) { // redefinitions
|
|
621
621
|
setArtifactLink( head, r );
|
|
622
622
|
return false;
|
|
@@ -650,11 +650,14 @@ function fns( model ) {
|
|
|
650
650
|
if (r[0].kind === '$navElement' && r.every( e => !e._parent.$duplicates )) {
|
|
651
651
|
// only complain about ambiguous source elements if we do not have
|
|
652
652
|
// duplicate table aliases, only mention non-ambiguous source elems
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
653
|
+
const uniqueNames = r.filter( e => !e.$duplicates);
|
|
654
|
+
if (uniqueNames.length) {
|
|
655
|
+
const names = uniqueNames.filter( e => e._parent.$inferred !== '$internal' )
|
|
656
|
+
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
657
|
+
let variant = names.length === uniqueNames.length ? 'std' : 'few';
|
|
658
|
+
if (names.length === 0)
|
|
659
|
+
variant = 'none';
|
|
660
|
+
error( 'ref-ambiguous', [ head.location, user ], { '#': variant, id: head.id, names });
|
|
658
661
|
}
|
|
659
662
|
}
|
|
660
663
|
setArtifactLink( head, r );
|
|
@@ -733,10 +736,12 @@ function fns( model ) {
|
|
|
733
736
|
}
|
|
734
737
|
else if (env.$frontend && env.$frontend !== 'cdl' || spec.global) {
|
|
735
738
|
// IDE can inspect <model>.definitions - provide null for valid
|
|
736
|
-
|
|
737
|
-
|
|
739
|
+
if (!spec.noMessageForLocalized || !head.id.startsWith( 'localized.' )) {
|
|
740
|
+
signalNotFound( spec.undefinedDef || 'ref-undefined-def', [ head.location, user ],
|
|
741
|
+
valid, { art: head.id } );
|
|
742
|
+
}
|
|
738
743
|
}
|
|
739
|
-
else {
|
|
744
|
+
else if (!spec.noMessageForLocalized || head.id !== 'localized') {
|
|
740
745
|
signalNotFound( spec.undefinedArt || 'ref-undefined-art', [ head.location, user ],
|
|
741
746
|
valid, { name: head.id } );
|
|
742
747
|
}
|
|
@@ -815,7 +820,7 @@ function fns( model ) {
|
|
|
815
820
|
setTargetReferenceKey( orig.name.id, item );
|
|
816
821
|
}
|
|
817
822
|
art = sub;
|
|
818
|
-
if (spec.envFn && (!artItemsCount || item === last) &&
|
|
823
|
+
if (spec.envFn && !spec.allowAutoexposed && (!artItemsCount || item === last) &&
|
|
819
824
|
art && art.$inferred === 'autoexposed' && !user.$inferred) {
|
|
820
825
|
// Depending on the processing sequence, the following could be a
|
|
821
826
|
// simple 'ref-undefined-art'/'ref-undefined-def' - TODO: which we
|
|
@@ -935,7 +940,7 @@ function fns( model ) {
|
|
|
935
940
|
msg.validNames = Object.create( null );
|
|
936
941
|
for (const name of Object.keys( valid )) {
|
|
937
942
|
// ignore internal types such as cds.Association
|
|
938
|
-
if (valid[name].internal || valid[name].deprecated)
|
|
943
|
+
if (valid[name].internal || valid[name].deprecated || valid[name].$inferred === '$internal')
|
|
939
944
|
continue;
|
|
940
945
|
msg.validNames[name] = valid[name];
|
|
941
946
|
}
|
|
@@ -949,49 +954,6 @@ function fns( model ) {
|
|
|
949
954
|
{ std: `Valid: ${ names.sort().join(', ') }`, zero: 'No valid names' });
|
|
950
955
|
}
|
|
951
956
|
}
|
|
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
957
|
}
|
|
996
958
|
|
|
997
959
|
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,
|