@sap/cds-compiler 6.3.6 → 6.4.6
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 -3
- package/LICENSE +32 -0
- package/README.md +14 -2
- package/bin/cdsse.js +0 -3
- package/doc/CHANGELOG_BETA.md +1 -1
- package/doc/CHANGELOG_DEPRECATED.md +1 -1
- package/lib/base/message-registry.js +9 -2
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +2 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
- package/lib/checks/existsMustEndInAssoc.js +1 -1
- package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
- package/lib/checks/validator.js +4 -2
- package/lib/compiler/assert-consistency.js +3 -2
- package/lib/compiler/builtins.js +5 -6
- package/lib/compiler/checks.js +37 -26
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/extend.js +39 -50
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/lsp-api.js +1 -1
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +29 -6
- package/lib/compiler/resolve.js +13 -3
- package/lib/compiler/shared.js +157 -133
- package/lib/compiler/tweak-assocs.js +87 -29
- package/lib/compiler/xpr-rewrite.js +164 -160
- package/lib/edm/annotations/edmJson.js +206 -37
- package/lib/edm/csn2edm.js +13 -0
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +106 -72
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1501 -1509
- package/lib/json/to-csn.js +8 -5
- package/lib/language/genericAntlrParser.js +0 -0
- package/lib/main.js +19 -16
- package/lib/model/csnRefs.js +589 -521
- package/lib/model/csnUtils.js +8 -5
- package/lib/model/enrichCsn.js +1 -0
- package/lib/parsers/AstBuildingParser.js +73 -28
- package/lib/render/toCdl.js +2 -1
- package/lib/render/toHdbcds.js +6 -3
- package/lib/render/toSql.js +5 -0
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +4 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
- package/lib/transform/db/assocsToQueries/utils.js +0 -5
- package/lib/transform/db/cdsPersistence.js +17 -18
- package/lib/transform/db/expansion.js +179 -3
- package/lib/transform/db/flattening.js +16 -5
- package/lib/transform/db/rewriteCalculatedElements.js +79 -283
- package/lib/transform/effective/main.js +8 -1
- package/lib/transform/forOdata.js +1 -1
- package/lib/transform/forRelationalDB.js +21 -80
- package/lib/transform/localized.js +75 -127
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
- package/lib/transform/transformUtils.js +23 -21
- package/lib/transform/translateAssocsToJoins.js +7 -5
- package/lib/transform/tupleExpansion.js +16 -3
- package/package.json +3 -3
- package/doc/DeprecatedOptions_v2.md +0 -150
- package/doc/NameResolution.md +0 -837
- package/lib/transform/parseExpr.js +0 -415
package/lib/compiler/shared.js
CHANGED
|
@@ -40,6 +40,7 @@ function fns( model ) {
|
|
|
40
40
|
} = model.$messageFunctions;
|
|
41
41
|
const Functions = model.$functions;
|
|
42
42
|
|
|
43
|
+
// Map `exprCtx` (is a param of traversal functions) to reference semantics
|
|
43
44
|
const referenceSemantics = {
|
|
44
45
|
// global: ------------------------------------------------------------------
|
|
45
46
|
using: { // only used to produce error message
|
|
@@ -48,6 +49,13 @@ function fns( model ) {
|
|
|
48
49
|
dynamic: modelDefinitions,
|
|
49
50
|
notFound: undefinedDefinition,
|
|
50
51
|
},
|
|
52
|
+
// scope:'global': for cds.Association and auto-redirected targets
|
|
53
|
+
$global: {
|
|
54
|
+
isMainRef: 'all',
|
|
55
|
+
lexical: null,
|
|
56
|
+
dynamic: modelDefinitions,
|
|
57
|
+
notFound: undefinedDefinition,
|
|
58
|
+
},
|
|
51
59
|
// only used for the main annotate/extend statements, not inner ones:
|
|
52
60
|
annotate: {
|
|
53
61
|
isMainRef: 'all',
|
|
@@ -60,10 +68,10 @@ function fns( model ) {
|
|
|
60
68
|
isMainRef: 'no-generated',
|
|
61
69
|
lexical: userBlock,
|
|
62
70
|
dynamic: modelDefinitions,
|
|
63
|
-
notFound:
|
|
71
|
+
notFound: undefinedForExtend,
|
|
64
72
|
accept: extendableArtifact,
|
|
65
73
|
},
|
|
66
|
-
|
|
74
|
+
_uncheckedExtension: { // to be used only with resolveUncheckedPath()
|
|
67
75
|
isMainRef: 'all',
|
|
68
76
|
lexical: userBlock,
|
|
69
77
|
dynamic: modelDefinitions,
|
|
@@ -76,12 +84,6 @@ function fns( model ) {
|
|
|
76
84
|
notFound: undefinedDefinition,
|
|
77
85
|
accept: acceptStructOrBare,
|
|
78
86
|
},
|
|
79
|
-
_include: { // cyclic include: no accept
|
|
80
|
-
isMainRef: 'no-generated',
|
|
81
|
-
lexical: userBlock,
|
|
82
|
-
dynamic: modelBuiltinsOrDefinitions,
|
|
83
|
-
notFound: undefinedDefinition,
|
|
84
|
-
},
|
|
85
87
|
target: {
|
|
86
88
|
isMainRef: 'no-autoexposed',
|
|
87
89
|
lexical: userBlock,
|
|
@@ -90,7 +92,7 @@ function fns( model ) {
|
|
|
90
92
|
accept: acceptEntity,
|
|
91
93
|
noDep: true,
|
|
92
94
|
// special `scope`s for auto-redirections:
|
|
93
|
-
global: () =>
|
|
95
|
+
global: () => '$global',
|
|
94
96
|
},
|
|
95
97
|
targetAspect: {
|
|
96
98
|
isMainRef: 'no-autoexposed',
|
|
@@ -107,6 +109,7 @@ function fns( model ) {
|
|
|
107
109
|
notFound: undefinedDefinition,
|
|
108
110
|
accept: acceptQuerySource,
|
|
109
111
|
noDep: '', // dependency special for from
|
|
112
|
+
args: () => 'from-args',
|
|
110
113
|
},
|
|
111
114
|
type: {
|
|
112
115
|
isMainRef: 'no-autoexposed',
|
|
@@ -117,11 +120,11 @@ function fns( model ) {
|
|
|
117
120
|
accept: acceptTypeOrElement,
|
|
118
121
|
// special `scope`s for CDL parser - TYPE OF (TODO generated?), cds.Association:
|
|
119
122
|
typeOf: typeOfSemantics,
|
|
120
|
-
global: () =>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
global: () => '$global', // TODO: do we need `navigation: staticTarget`?
|
|
124
|
+
},
|
|
125
|
+
$typeOf: {
|
|
126
|
+
dynamic: typeOfParentDict,
|
|
127
|
+
navigation: staticTarget,
|
|
125
128
|
},
|
|
126
129
|
// element references without lexical scope (except $self/$projection): -----
|
|
127
130
|
targetElement: {
|
|
@@ -130,22 +133,24 @@ function fns( model ) {
|
|
|
130
133
|
dynamic: targetElements,
|
|
131
134
|
navigation: targetNavigation,
|
|
132
135
|
notFound: undefinedTargetElement,
|
|
133
|
-
param:
|
|
136
|
+
param: () => '$scopePar',
|
|
134
137
|
},
|
|
135
138
|
filter: {
|
|
136
139
|
lexical: justDollarAliases,
|
|
137
140
|
dollar: true,
|
|
138
141
|
dynamic: targetElements,
|
|
139
142
|
notFound: undefinedTargetElement,
|
|
140
|
-
param:
|
|
143
|
+
param: () => '$scopePar',
|
|
144
|
+
nestedColumn: () => 'filter',
|
|
141
145
|
},
|
|
142
|
-
'calc-filter': {
|
|
146
|
+
'calc-filter': { // TODO: what is so special about this?
|
|
143
147
|
lexical: justDollarAliases,
|
|
144
148
|
dollar: true,
|
|
145
149
|
dynamic: targetElements,
|
|
146
150
|
navigation: calcElemNavigation,
|
|
147
151
|
notFound: undefinedTargetElement,
|
|
148
152
|
param: paramUnsupported,
|
|
153
|
+
filter: () => 'calc-filter',
|
|
149
154
|
},
|
|
150
155
|
default: {
|
|
151
156
|
lexical: null,
|
|
@@ -159,7 +164,7 @@ function fns( model ) {
|
|
|
159
164
|
dollar: true,
|
|
160
165
|
dynamic: () => Object.create( null ),
|
|
161
166
|
notFound: undefinedVariable,
|
|
162
|
-
param:
|
|
167
|
+
param: () => '$scopePar',
|
|
163
168
|
},
|
|
164
169
|
'limit-offset': 'limit-rows',
|
|
165
170
|
// general element / variable references --------------------------------------
|
|
@@ -169,7 +174,7 @@ function fns( model ) {
|
|
|
169
174
|
dynamic: combinedSourcesOrParentElements,
|
|
170
175
|
notFound: undefinedSourceElement,
|
|
171
176
|
check: checkRefInQuery,
|
|
172
|
-
param:
|
|
177
|
+
param: () => '$scopePar',
|
|
173
178
|
},
|
|
174
179
|
having: 'where',
|
|
175
180
|
groupBy: 'where',
|
|
@@ -179,22 +184,15 @@ function fns( model ) {
|
|
|
179
184
|
dynamic: combinedSourcesOrParentElements,
|
|
180
185
|
notFound: undefinedSourceElement,
|
|
181
186
|
check: checkColumnRef,
|
|
182
|
-
param:
|
|
183
|
-
nestedColumn: () =>
|
|
184
|
-
lexical: justDollarAliases,
|
|
185
|
-
dollar: true,
|
|
186
|
-
dynamic: nestedElements,
|
|
187
|
-
notFound: undefinedNestedElement,
|
|
188
|
-
check: checkColumnRef,
|
|
189
|
-
param: paramSemantics,
|
|
190
|
-
}),
|
|
187
|
+
param: () => '$scopePar',
|
|
188
|
+
nestedColumn: () => '$srcRefInNestedColumn',
|
|
191
189
|
},
|
|
192
190
|
'from-args': {
|
|
193
191
|
lexical: null,
|
|
194
192
|
dollar: true,
|
|
195
193
|
dynamic: () => Object.create( null ),
|
|
196
194
|
notFound: undefinedVariable,
|
|
197
|
-
param:
|
|
195
|
+
param: () => '$scopePar',
|
|
198
196
|
},
|
|
199
197
|
calc: {
|
|
200
198
|
lexical: justDollarAliases,
|
|
@@ -203,6 +201,7 @@ function fns( model ) {
|
|
|
203
201
|
navigation: calcElemNavigation,
|
|
204
202
|
notFound: undefinedParentElement,
|
|
205
203
|
param: paramUnsupported,
|
|
204
|
+
filter: () => 'calc-filter',
|
|
206
205
|
},
|
|
207
206
|
'join-on': {
|
|
208
207
|
lexical: tableAliasesAndSelf,
|
|
@@ -210,7 +209,7 @@ function fns( model ) {
|
|
|
210
209
|
dynamic: combinedSourcesOrParentElements,
|
|
211
210
|
rejectRoot: rejectOwnExceptVisibleAliases,
|
|
212
211
|
notFound: undefinedSourceElement,
|
|
213
|
-
param:
|
|
212
|
+
param: () => '$scopePar',
|
|
214
213
|
},
|
|
215
214
|
on: { // unmanaged assoc: outside query, redirected or new assoc in column
|
|
216
215
|
lexical: justDollarAliases,
|
|
@@ -221,15 +220,8 @@ function fns( model ) {
|
|
|
221
220
|
accept: acceptElemOrVarOrSelf,
|
|
222
221
|
check: checkAssocOn,
|
|
223
222
|
param: paramUnsupported,
|
|
224
|
-
nestedColumn: () => ({ // in expand and inline
|
|
225
|
-
lexical: justDollarAliases,
|
|
226
|
-
dollar: true,
|
|
227
|
-
dynamic: parentElements,
|
|
228
|
-
navigation: assocOnNavigation,
|
|
229
|
-
notFound: undefinedParentElement,
|
|
230
|
-
rewriteProjectionToSelf: true,
|
|
231
|
-
}),
|
|
232
223
|
rewriteProjectionToSelf: true,
|
|
224
|
+
nestedColumn: () => '$projRefInNestedColumn',
|
|
233
225
|
},
|
|
234
226
|
'mixin-on': {
|
|
235
227
|
lexical: tableAliasesAndSelf,
|
|
@@ -239,7 +231,7 @@ function fns( model ) {
|
|
|
239
231
|
notFound: undefinedSourceElement,
|
|
240
232
|
accept: acceptElemOrVarOrSelf,
|
|
241
233
|
check: checkAssocOn,
|
|
242
|
-
param:
|
|
234
|
+
param: () => '$scopePar', // TODO: check that assocs containing param in ON is not published
|
|
243
235
|
},
|
|
244
236
|
'orderBy-ref': {
|
|
245
237
|
lexical: tableAliasesAndSelf,
|
|
@@ -247,7 +239,7 @@ function fns( model ) {
|
|
|
247
239
|
dynamic: parentElements,
|
|
248
240
|
notFound: undefinedOrderByElement,
|
|
249
241
|
check: checkOrderByRef,
|
|
250
|
-
param:
|
|
242
|
+
param: () => '$scopePar',
|
|
251
243
|
},
|
|
252
244
|
'orderBy-expr': {
|
|
253
245
|
lexical: tableAliasesAndSelf,
|
|
@@ -255,7 +247,7 @@ function fns( model ) {
|
|
|
255
247
|
dynamic: combinedSourcesOrParentElements,
|
|
256
248
|
notFound: undefinedSourceElement,
|
|
257
249
|
check: checkRefInQuery,
|
|
258
|
-
param:
|
|
250
|
+
param: () => '$scopePar',
|
|
259
251
|
},
|
|
260
252
|
'orderBy-set-ref': {
|
|
261
253
|
lexical: tableAliasesAndSelf, // TODO: reject own tab aliases
|
|
@@ -264,7 +256,7 @@ function fns( model ) {
|
|
|
264
256
|
rejectRoot: rejectOwnAliasesAndMixins,
|
|
265
257
|
notFound: undefinedParentElement,
|
|
266
258
|
check: checkOrderByRef,
|
|
267
|
-
param:
|
|
259
|
+
param: () => '$scopePar',
|
|
268
260
|
},
|
|
269
261
|
'orderBy-set-expr': {
|
|
270
262
|
lexical: tableAliasesAndSelf, // TODO: reject own tab aliases
|
|
@@ -273,7 +265,7 @@ function fns( model ) {
|
|
|
273
265
|
rejectRoot: rejectAllOwn,
|
|
274
266
|
notFound: undefinedVariable,
|
|
275
267
|
check: checkRefInQuery,
|
|
276
|
-
param:
|
|
268
|
+
param: () => '$scopePar',
|
|
277
269
|
},
|
|
278
270
|
annotation: { // annotation assignments
|
|
279
271
|
lexical: justDollarAliases,
|
|
@@ -282,19 +274,14 @@ function fns( model ) {
|
|
|
282
274
|
navigation: assocOnNavigation,
|
|
283
275
|
noDep: true,
|
|
284
276
|
notFound: undefinedParentElement,
|
|
277
|
+
accept: acceptElemOrAnyVar,
|
|
278
|
+
variableFilter: (dict => dict),
|
|
285
279
|
messageMap: {
|
|
286
280
|
'ref-undefined-element': 'anno-undefined-element',
|
|
287
281
|
'ref-undefined-param': 'anno-undefined-param',
|
|
288
282
|
},
|
|
289
|
-
param:
|
|
290
|
-
nestedColumn: () =>
|
|
291
|
-
lexical: justDollarAliases,
|
|
292
|
-
dollar: true,
|
|
293
|
-
dynamic: parentElements,
|
|
294
|
-
navigation: assocOnNavigation,
|
|
295
|
-
notFound: undefinedParentElement,
|
|
296
|
-
rewriteProjectionToSelf: true,
|
|
297
|
-
}),
|
|
283
|
+
param: () => '$annotationScopePar',
|
|
284
|
+
nestedColumn: () => '$projRefInNestedColumn',
|
|
298
285
|
},
|
|
299
286
|
// TODO: introduce some kind of inheritance
|
|
300
287
|
// used by xpr-rewrite.js to resolve rewritten path roots.
|
|
@@ -305,15 +292,35 @@ function fns( model ) {
|
|
|
305
292
|
navigation: assocOnNavigation,
|
|
306
293
|
noDep: true,
|
|
307
294
|
notFound: null, // no error, just falsy links
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
295
|
+
accept: acceptElemOrAnyVar,
|
|
296
|
+
param: () => '$scopePar',
|
|
297
|
+
nestedColumn: () => '$projRefInNestedColumn',
|
|
298
|
+
},
|
|
299
|
+
$scopePar: {
|
|
300
|
+
dynamic: artifactParams,
|
|
301
|
+
notFound: undefinedParam,
|
|
302
|
+
},
|
|
303
|
+
$annotationScopePar: {
|
|
304
|
+
messageMap: {
|
|
305
|
+
'ref-undefined-element': 'anno-undefined-element',
|
|
306
|
+
'ref-undefined-param': 'anno-undefined-param',
|
|
307
|
+
},
|
|
308
|
+
dynamic: artifactParams,
|
|
309
|
+
notFound: undefinedParam,
|
|
310
|
+
},
|
|
311
|
+
// for `nestedColumn`, these two will be merged with base semantics:
|
|
312
|
+
$projRefInNestedColumn: { // for assoc-`on` and annotations
|
|
313
|
+
lexical: justDollarAliases,
|
|
314
|
+
dynamic: parentElements,
|
|
315
|
+
navigation: assocOnNavigation, // like std `environment`, but no dependency
|
|
316
|
+
rewriteProjectionToSelf: true,
|
|
317
|
+
},
|
|
318
|
+
$srcRefInNestedColumn: { // for column refs
|
|
319
|
+
lexical: justDollarAliases,
|
|
320
|
+
dollar: true,
|
|
321
|
+
dynamic: nestedElements,
|
|
322
|
+
navigation: environment,
|
|
323
|
+
notFound: undefinedNestedElement,
|
|
317
324
|
},
|
|
318
325
|
};
|
|
319
326
|
|
|
@@ -393,16 +400,13 @@ function fns( model ) {
|
|
|
393
400
|
const exit = callback( step, exprCtx, user );
|
|
394
401
|
if (exit === traverseExpr.STOP)
|
|
395
402
|
return true;
|
|
396
|
-
if (step.where && exit !== traverseExpr.SKIP
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
: 'filter' ),
|
|
402
|
-
step, callback ) === traverseExpr.STOP)
|
|
403
|
-
return true;
|
|
403
|
+
if (step.where && exit !== traverseExpr.SKIP) {
|
|
404
|
+
const ctx = referenceSemantics[exprCtx].filter?.() || 'filter';
|
|
405
|
+
if (traverseExpr( step.where, ctx, step, callback ) === traverseExpr.STOP)
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
404
408
|
if (step.args) {
|
|
405
|
-
const ctx = (
|
|
409
|
+
const ctx = referenceSemantics[exprCtx].args?.() || exprCtx;
|
|
406
410
|
const args = Array.isArray( step.args ) ? step.args : Object.values( step.args );
|
|
407
411
|
// TODO: there should be no array `args` on path item
|
|
408
412
|
for (const arg of args) {
|
|
@@ -416,7 +420,7 @@ function fns( model ) {
|
|
|
416
420
|
// Special expression traversal function for `resolveExpr`. Let's see
|
|
417
421
|
// later whether we can use this version as the general one.
|
|
418
422
|
// If we continue to have separate ones, remove the STOP stuff – it is not
|
|
419
|
-
// needed for `resolveExpr
|
|
423
|
+
// needed for `resolveExpr`; SKIP is used, though.
|
|
420
424
|
|
|
421
425
|
function traverseTypedExpr( expr, exprCtx, user, type, callback ) {
|
|
422
426
|
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
@@ -569,16 +573,13 @@ function fns( model ) {
|
|
|
569
573
|
const exit = callback( step, exprCtx, user, null );
|
|
570
574
|
if (exit === traverseExpr.STOP)
|
|
571
575
|
return true;
|
|
572
|
-
if (step.where && exit !== traverseExpr.SKIP
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
: 'filter' ),
|
|
578
|
-
step, null, callback ) === traverseExpr.STOP)
|
|
579
|
-
return true;
|
|
576
|
+
if (step.where && exit !== traverseExpr.SKIP) {
|
|
577
|
+
const ctx = referenceSemantics[exprCtx].filter?.() || 'filter';
|
|
578
|
+
if (traverseTypedExpr( step.where, ctx, step, null, callback ) === traverseExpr.STOP)
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
580
581
|
if (step.args) {
|
|
581
|
-
const ctx = (
|
|
582
|
+
const ctx = referenceSemantics[exprCtx].args?.() || exprCtx;
|
|
582
583
|
const args = Array.isArray( step.args ) ? step.args : Object.values( step.args );
|
|
583
584
|
// TODO: there should be no array `args` on path item
|
|
584
585
|
for (const arg of args) {
|
|
@@ -641,7 +642,6 @@ function fns( model ) {
|
|
|
641
642
|
|
|
642
643
|
const s = referenceSemantics[expected];
|
|
643
644
|
const semantics = (typeof s === 'string') ? referenceSemantics[s] : s;
|
|
644
|
-
semantics.name = expected;
|
|
645
645
|
|
|
646
646
|
const r = getPathRoot( ref, semantics, origUser );
|
|
647
647
|
const root = r && acceptPathRoot( r, ref, semantics, origUser );
|
|
@@ -782,19 +782,26 @@ function fns( model ) {
|
|
|
782
782
|
ruser = ruser._outer._outer;
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
// Handle expand/inline
|
|
785
|
+
// Handle expand/inline before `type of`, :param, global (internally for CDL):
|
|
786
786
|
if (user._columnParent && !semantics.isMainRef) { // in expand/inline
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
787
|
+
const func = semantics.nestedColumn;
|
|
788
|
+
if (!func)
|
|
789
|
+
throw new CompilerAssertion( 'Unexpected ref context in nested column' );
|
|
790
|
+
const ctx = func();
|
|
791
|
+
semantics = (typeof ctx === 'string')
|
|
792
|
+
? ({ ...semantics, ...referenceSemantics[ctx] })
|
|
793
|
+
: ctx;
|
|
790
794
|
}
|
|
791
795
|
if (typeof scope === 'string') { // typeOf, param, global
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
+
const func = semantics[scope] || scope === 'param' && paramUnsupported;
|
|
797
|
+
// 'param' is a user scope → useful default (error msg ref-unexpected-param)
|
|
798
|
+
// 'global' and 'typeOf' are internal scopes of the compiler → dump if not provided
|
|
799
|
+
if (!func)
|
|
800
|
+
throw new CompilerAssertion( `Unexpected scope ${ scope }, no handler defined in context` );
|
|
801
|
+
const ctx = func( ruser, path, location, semantics );
|
|
802
|
+
semantics = (typeof ctx === 'string') ? referenceSemantics[ctx] : ctx;
|
|
803
|
+
if (!semantics)
|
|
796
804
|
return setArtifactLink( head, null );
|
|
797
|
-
}
|
|
798
805
|
}
|
|
799
806
|
const valid = [];
|
|
800
807
|
|
|
@@ -829,7 +836,8 @@ function fns( model ) {
|
|
|
829
836
|
valid.push( dynamicDict );
|
|
830
837
|
}
|
|
831
838
|
else {
|
|
832
|
-
|
|
839
|
+
const filterFn = semantics.variableFilter || removeRestrictedVariables;
|
|
840
|
+
valid.push( filterFn( model.$magicVariables.elements ),
|
|
833
841
|
removeDollarNames( dynamicDict ) );
|
|
834
842
|
}
|
|
835
843
|
// TODO: streamline function arguments (probably: user, path, semantics )
|
|
@@ -853,7 +861,7 @@ function fns( model ) {
|
|
|
853
861
|
const { isMainRef } = semantics;
|
|
854
862
|
if (isMainRef) {
|
|
855
863
|
artItemsCount = (typeof ref.scope === 'number' && ref.scope) ||
|
|
856
|
-
|
|
864
|
+
(ref.scope ? 1 : path.length);
|
|
857
865
|
}
|
|
858
866
|
let art = null;
|
|
859
867
|
const elementsEnv = semantics.navigation || environment;
|
|
@@ -1024,11 +1032,17 @@ function fns( model ) {
|
|
|
1024
1032
|
return art;
|
|
1025
1033
|
}
|
|
1026
1034
|
case 'builtin': {
|
|
1035
|
+
// TODO: use properties in builtins
|
|
1027
1036
|
if (art.name.id === '$at') {
|
|
1028
1037
|
message( 'ref-deprecated-variable', [ head.location, user ],
|
|
1029
1038
|
{ code: '$at', newcode: '$valid' },
|
|
1030
1039
|
'$(CODE) is deprecated; use $(NEWCODE) instead' );
|
|
1031
1040
|
}
|
|
1041
|
+
else if (art.$restricted && semantics.accept !== acceptElemOrAnyVar) {
|
|
1042
|
+
error( 'ref-unexpected-var', [ head.location, user ],
|
|
1043
|
+
{ '#': 'annotation', name: head.id } );
|
|
1044
|
+
return null; // no further error on `unknown` for $draft.unknown
|
|
1045
|
+
}
|
|
1032
1046
|
return art;
|
|
1033
1047
|
}
|
|
1034
1048
|
default:
|
|
@@ -1063,20 +1077,12 @@ function fns( model ) {
|
|
|
1063
1077
|
while (struct.kind === 'element')
|
|
1064
1078
|
struct = struct._parent;
|
|
1065
1079
|
if (struct === user._main && struct.kind !== 'annotation')
|
|
1066
|
-
return
|
|
1080
|
+
return '$typeOf';
|
|
1067
1081
|
error( 'type-unexpected-typeof', [ head.location, user ],
|
|
1068
1082
|
{ keyword: 'type of', '#': struct.kind } );
|
|
1069
1083
|
return false;
|
|
1070
1084
|
}
|
|
1071
1085
|
|
|
1072
|
-
function paramSemantics( _user, _path, _loction, semantics ) {
|
|
1073
|
-
return {
|
|
1074
|
-
messageMap: semantics.messageMap,
|
|
1075
|
-
dynamic: artifactParams,
|
|
1076
|
-
notFound: undefinedParam,
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
1086
|
function paramUnsupported( user, _path, location ) {
|
|
1081
1087
|
error( 'ref-unexpected-scope', [ location, user ], // TODO: ref-unexpected-param
|
|
1082
1088
|
// why an extra text for calculated elements? or separate for all?
|
|
@@ -1287,26 +1293,36 @@ function fns( model ) {
|
|
|
1287
1293
|
|
|
1288
1294
|
function undefinedForAnnotate( user, item, valid, _dict, prev, path ) {
|
|
1289
1295
|
// in a CSN source, only one env was tested (valid.length 1):
|
|
1290
|
-
const
|
|
1296
|
+
const name = (prev) ? `${ prev.name.id }.${ item.id }` : item.id;
|
|
1291
1297
|
if (!user.elements && !user.actions && !user.enum && !user.params &&
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1298
|
+
endsWithSuffix( name, '.drafts', art => art?._service && art.kind === 'entity' ) ||
|
|
1299
|
+
endsWithSuffix( name, '.DraftAdministrativeData',
|
|
1300
|
+
( art, prefix ) => (art?.kind === 'service' || prefix === 'DRAFT') ) ||
|
|
1301
|
+
name === 'DRAFT' && path?.length === 2 && path[1].id === 'DraftAdministrativeData' ||
|
|
1302
|
+
name.startsWith( 'localized.' )) // TODO: only if suffix is defined
|
|
1295
1303
|
return;
|
|
1296
1304
|
signalNotFound( (valid.length > 1 ? 'ext-undefined-art' : 'ext-undefined-def'),
|
|
1297
1305
|
// TODO: ext-undefined-xyz
|
|
1298
|
-
[ item.location, user ], valid, { art } );
|
|
1306
|
+
[ item.location, user ], valid, { art: name } );
|
|
1299
1307
|
}
|
|
1300
1308
|
|
|
1301
|
-
function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1309
|
+
function undefinedForExtend( user, item, valid, _dict, prev ) {
|
|
1310
|
+
// in a CSN source, only one env was tested (valid.length 1):
|
|
1311
|
+
const name = (prev) ? `${ prev.name.id }.${ item.id }` : item.id;
|
|
1312
|
+
if (name.startsWith( 'localized.' )) {
|
|
1313
|
+
error( 'ref-undefined-art', [ user.name.location || user.location, user ],
|
|
1314
|
+
{ '#': 'localized', keyword: 'annotate' } );
|
|
1315
|
+
}
|
|
1316
|
+
else {
|
|
1317
|
+
undefinedDefinition( user, item, valid, _dict, prev );
|
|
1318
|
+
}
|
|
1306
1319
|
}
|
|
1307
1320
|
|
|
1308
|
-
function
|
|
1309
|
-
|
|
1321
|
+
function endsWithSuffix( name, suffix, cond ) {
|
|
1322
|
+
if (!name.endsWith( suffix ))
|
|
1323
|
+
return false;
|
|
1324
|
+
const prefix = name.slice( 0, -suffix.length );
|
|
1325
|
+
return cond( model.definitions[prefix], prefix, name );
|
|
1310
1326
|
}
|
|
1311
1327
|
|
|
1312
1328
|
function undefinedParam( user, head, valid, _dict, _art, _path, semantics ) {
|
|
@@ -1424,8 +1440,7 @@ function fns( model ) {
|
|
|
1424
1440
|
while ((head = head?._parent) && head.kind === 'builtin')
|
|
1425
1441
|
id = `${ head.name.id }.${ id }`;
|
|
1426
1442
|
const msgId = (art.$uncheckedElements) ? 'ref-unknown-var' : 'ref-undefined-var';
|
|
1427
|
-
signalNotFound( msgId, [ item.location, user ],
|
|
1428
|
-
removeInvalidMagicVariables( valid, semantics ), { id }, semantics );
|
|
1443
|
+
signalNotFound( msgId, [ item.location, user ], valid, { id }, semantics );
|
|
1429
1444
|
}
|
|
1430
1445
|
else if (art.kind === 'aspect' && !art.name) { // anonymous target aspect - TODO: still?
|
|
1431
1446
|
signalNotFound( 'ref-undefined-element', [ item.location, user ], valid,
|
|
@@ -1507,16 +1522,17 @@ function fns( model ) {
|
|
|
1507
1522
|
: acceptElemOrVar( art, user, ref );
|
|
1508
1523
|
}
|
|
1509
1524
|
|
|
1510
|
-
function acceptElemOrVar( art, user, ref
|
|
1525
|
+
function acceptElemOrVar( art, user, ref ) {
|
|
1526
|
+
if (art.kind !== 'builtin' || !art.$restricted)
|
|
1527
|
+
return acceptElemOrAnyVar( art, user, ref );
|
|
1528
|
+
error( 'ref-unexpected-var', [ ref.location, user ],
|
|
1529
|
+
{ '#': 'annotation', name: pathName( ref.path ) } );
|
|
1530
|
+
return null;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
function acceptElemOrAnyVar( art, user, ref ) {
|
|
1511
1534
|
const { path } = ref;
|
|
1512
1535
|
if (art.kind === 'builtin') {
|
|
1513
|
-
if (art.$onlyInExprCtx && !art.$onlyInExprCtx.includes(semantics.name)) {
|
|
1514
|
-
error( 'ref-unexpected-var', [ ref.location, user ], {
|
|
1515
|
-
'#': art.$onlyInExprCtx[0], name: pathName( path ),
|
|
1516
|
-
});
|
|
1517
|
-
return null;
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
1536
|
if (user.expand || user.inline) {
|
|
1521
1537
|
const location = (user.expand || user.inline)[$location];
|
|
1522
1538
|
const code = (user.expand) ? '{ ‹expand› }' : '.{ ‹inline› }';
|
|
@@ -1537,7 +1553,7 @@ function fns( model ) {
|
|
|
1537
1553
|
}
|
|
1538
1554
|
// TODO: combine $requireElementAccess/$autoElement to $bareRoot ?
|
|
1539
1555
|
else if (!user.expand && !user.inline && // $self._artifact to main artifact
|
|
1540
|
-
|
|
1556
|
+
!(art._main && art.kind !== 'select') && ref.path[0]._navigation?.kind === '$self') {
|
|
1541
1557
|
// TODO: better ref-invalid-self
|
|
1542
1558
|
const { location, id } = path[0];
|
|
1543
1559
|
error( 'ref-unexpected-self', [ location, user ], { id } );
|
|
@@ -1655,7 +1671,7 @@ function fns( model ) {
|
|
|
1655
1671
|
}
|
|
1656
1672
|
}
|
|
1657
1673
|
else if (source.kind !== 'entity' &&
|
|
1658
|
-
|
|
1674
|
+
!acceptEventProjectionSource( source, user )) {
|
|
1659
1675
|
signalNotFound( 'ref-invalid-source', [ ref.location, user ], null,
|
|
1660
1676
|
{ '#': user._main.kind } );
|
|
1661
1677
|
return (source === art) ? art : false; // art to show cyclic issues
|
|
@@ -1937,8 +1953,8 @@ function fns( model ) {
|
|
|
1937
1953
|
// has to be run after foreign-key rewrite
|
|
1938
1954
|
const outer = user._columnParent?._origin;
|
|
1939
1955
|
let assoc = outer?.foreignKeys &&
|
|
1940
|
-
|
|
1941
|
-
|
|
1956
|
+
pathStartsWithSelf( { path } ) == null && // not $self or CDS var like $now
|
|
1957
|
+
outer;
|
|
1942
1958
|
for (let index = startIndex; index < path.length; ++index) {
|
|
1943
1959
|
if (assoc?.target) {
|
|
1944
1960
|
if (!assoc.foreignKeys) {
|
|
@@ -2029,7 +2045,8 @@ function fns( model ) {
|
|
|
2029
2045
|
const err = message( semantics?.messageMap?.[msgId] || msgId, location, textParams );
|
|
2030
2046
|
if (valid) {
|
|
2031
2047
|
const user = Array.isArray( location ) && location[1];
|
|
2032
|
-
|
|
2048
|
+
// TODO: see TODO missing param `viaCdl` in attachAndEmitValidNames:
|
|
2049
|
+
err.validNames = (user && definedViaCdl( user ));
|
|
2033
2050
|
valid.reverse();
|
|
2034
2051
|
attachAndEmitValidNames( err, ...valid );
|
|
2035
2052
|
}
|
|
@@ -2070,6 +2087,8 @@ function fns( model ) {
|
|
|
2070
2087
|
const valid = Object.assign( Object.create( null ), ...validDicts );
|
|
2071
2088
|
msg.validNames = Object.create( null );
|
|
2072
2089
|
for (const name of Object.keys( valid )) {
|
|
2090
|
+
if (!name)
|
|
2091
|
+
continue;
|
|
2073
2092
|
const art = valid[name];
|
|
2074
2093
|
// ignore internal types such as cds.Association, ignore names with dot for
|
|
2075
2094
|
// CDL references to main artifacts:
|
|
@@ -2094,14 +2113,19 @@ function fns( model ) {
|
|
|
2094
2113
|
}
|
|
2095
2114
|
}
|
|
2096
2115
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2116
|
+
/**
|
|
2117
|
+
* Filter out restricted variables from a dictionary of variables.
|
|
2118
|
+
* Variables with the `$restricted` property set to true are excluded.
|
|
2119
|
+
*
|
|
2120
|
+
* @param {object} variables - Dictionary of variable objects
|
|
2121
|
+
* @returns {object} New dictionary with restricted variables removed
|
|
2122
|
+
*/
|
|
2123
|
+
function removeRestrictedVariables( variables ) {
|
|
2124
|
+
// TODO: we could also do something with `deprecated`, $requiresBetaFlag, …
|
|
2101
2125
|
const valid = Object.create(null);
|
|
2102
2126
|
for (const name in variables) {
|
|
2103
2127
|
const variable = variables[name];
|
|
2104
|
-
if (!variable.$
|
|
2128
|
+
if (!variable.$restricted)
|
|
2105
2129
|
valid[name] = variable;
|
|
2106
2130
|
}
|
|
2107
2131
|
return valid;
|