@sap/cds-compiler 4.8.0 → 4.9.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 +38 -4
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +30 -17
- package/doc/CHANGELOG_BETA.md +19 -0
- package/lib/api/main.js +59 -24
- package/lib/api/options.js +12 -1
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +27 -0
- package/lib/base/message-registry.js +38 -21
- package/lib/base/messages.js +51 -20
- package/lib/base/model.js +4 -5
- package/lib/checks/actionsFunctions.js +2 -2
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +3 -2
- package/lib/checks/validator.js +2 -34
- package/lib/compiler/assert-consistency.js +10 -3
- package/lib/compiler/checks.js +44 -18
- package/lib/compiler/define.js +38 -30
- package/lib/compiler/extend.js +33 -10
- package/lib/compiler/index.js +0 -1
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +0 -2
- package/lib/compiler/propagator.js +23 -19
- package/lib/compiler/resolve.js +48 -29
- package/lib/compiler/shared.js +60 -20
- package/lib/compiler/tweak-assocs.js +72 -116
- package/lib/compiler/xpr-rewrite.js +762 -0
- package/lib/edm/annotations/edmJson.js +24 -7
- package/lib/edm/annotations/genericTranslation.js +81 -61
- package/lib/edm/edm.js +4 -4
- package/lib/edm/edmInboundChecks.js +33 -0
- package/lib/edm/edmPreprocessor.js +9 -6
- package/lib/gen/Dictionary.json +129 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1523 -1518
- package/lib/json/from-csn.js +13 -4
- package/lib/json/to-csn.js +12 -12
- package/lib/language/genericAntlrParser.js +14 -6
- package/lib/main.d.ts +67 -14
- package/lib/main.js +1 -0
- package/lib/model/cloneCsn.js +6 -3
- package/lib/model/csnRefs.js +23 -11
- package/lib/model/csnUtils.js +13 -7
- package/lib/model/enrichCsn.js +3 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +33 -34
- package/lib/optionProcessor.js +27 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +3 -1
- package/lib/transform/db/applyTransformations.js +33 -0
- package/lib/transform/db/constraints.js +75 -28
- package/lib/transform/db/expansion.js +8 -3
- package/lib/transform/db/flattening.js +2 -2
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +6 -8
- package/lib/transform/effective/misc.js +31 -10
- package/lib/transform/forOdata.js +23 -7
- package/lib/transform/forRelationalDB.js +3 -3
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +221 -124
- package/lib/transform/odata/toFinalBaseType.js +1 -1
- package/lib/transform/odata/typesExposure.js +15 -12
- package/lib/transform/parseExpr.js +4 -4
- package/lib/transform/transformUtils.js +47 -42
- package/lib/transform/translateAssocsToJoins.js +47 -47
- package/lib/transform/universalCsn/universalCsnEnricher.js +16 -19
- package/package.json +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/message-explanations.json +1 -0
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
|
@@ -20,30 +20,16 @@ const {
|
|
|
20
20
|
linkToOrigin,
|
|
21
21
|
withAssociation,
|
|
22
22
|
viewFromPrimary,
|
|
23
|
+
copyExpr,
|
|
23
24
|
} = require('./utils');
|
|
25
|
+
const { propagationRules } = require('../base/builtins');
|
|
24
26
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
27
|
+
const { xprRewriteFns } = require('./xpr-rewrite');
|
|
25
28
|
// const { ref } = require( '../model/revealInternalProperties' )
|
|
26
29
|
|
|
27
30
|
// Note that propagation here is also used for deep-copying (function `onlyViaParent`)
|
|
28
31
|
function propagate( model ) {
|
|
29
32
|
const props = {
|
|
30
|
-
'@com.sap.gtt.core.CoreModel.Indexable': never,
|
|
31
|
-
'@cds.persistence.exists': never, // also copied in generate.js
|
|
32
|
-
'@cds.persistence.table': never,
|
|
33
|
-
'@cds.persistence.calcview': never,
|
|
34
|
-
'@cds.persistence.udf': never,
|
|
35
|
-
'@cds.persistence.skip': notWithPersistenceTable, // also copied in generate.js
|
|
36
|
-
// '@cds.tenant.independent' is propagated as normal, but also copied in generate.js
|
|
37
|
-
'@sql.append': never,
|
|
38
|
-
'@sql.prepend': never,
|
|
39
|
-
'@sql.replace': never,
|
|
40
|
-
'@Analytics.hidden': never,
|
|
41
|
-
'@Analytics.visible': never,
|
|
42
|
-
'@cds.autoexpose': onlyViaArtifact,
|
|
43
|
-
'@cds.autoexposed': never, // in case people set it themselves
|
|
44
|
-
'@cds.external': never,
|
|
45
|
-
'@cds.redirection.target': never,
|
|
46
|
-
'@fiori.draft.enabled': onlyViaArtifact,
|
|
47
33
|
'@': annotation, // always except in 'items' (and parameters for entity return types)
|
|
48
34
|
doc: annotation, // always except in 'items' (and parameters for entity return types)
|
|
49
35
|
default: withKind, // always except in 'items'
|
|
@@ -70,9 +56,20 @@ function propagate( model ) {
|
|
|
70
56
|
enum: expensive,
|
|
71
57
|
params: expensive, // actually only with parent action
|
|
72
58
|
returns,
|
|
73
|
-
$filtered: annotation,
|
|
59
|
+
$filtered: annotation, // TODO(v5): Remove
|
|
60
|
+
$enclosed: annotation,
|
|
74
61
|
};
|
|
62
|
+
const ruleToFunction = {
|
|
63
|
+
__proto__: null,
|
|
64
|
+
never,
|
|
65
|
+
onlyViaArtifact,
|
|
66
|
+
notWithPersistenceTable,
|
|
67
|
+
};
|
|
68
|
+
for (const rule in propagationRules)
|
|
69
|
+
props[rule] = ruleToFunction[propagationRules[rule]];
|
|
70
|
+
|
|
75
71
|
const { options } = model;
|
|
72
|
+
const { rewriteAnnotationsRefs } = xprRewriteFns( model );
|
|
76
73
|
// eslint-disable-next-line max-len
|
|
77
74
|
const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
|
|
78
75
|
const { warning, throwWithError } = model.$messageFunctions;
|
|
@@ -103,7 +100,7 @@ function propagate( model ) {
|
|
|
103
100
|
const news = [];
|
|
104
101
|
for (const target of targets) {
|
|
105
102
|
const origin = getOrigin( target );
|
|
106
|
-
if (origin) {
|
|
103
|
+
if (origin && origin.kind !== '$self') {
|
|
107
104
|
// Calculated elements that are simple references: `calc = field;`.
|
|
108
105
|
// Respect sibling properties in inheritance.
|
|
109
106
|
if (target._calcOrigin?._origin && target.value?._artifact) {
|
|
@@ -202,6 +199,10 @@ function propagate( model ) {
|
|
|
202
199
|
target[prop] = [ ...val ];
|
|
203
200
|
target[prop].$inferred = 'prop';
|
|
204
201
|
}
|
|
202
|
+
else if (prop.charAt(0) === '@' && val?.kind === '$annotation') {
|
|
203
|
+
target[prop] = Object.assign( copyExpr( val ), { $inferred: 'prop' } );
|
|
204
|
+
rewriteAnnotationsRefs( target, source, prop );
|
|
205
|
+
}
|
|
205
206
|
else {
|
|
206
207
|
target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );
|
|
207
208
|
if (val._artifact !== undefined)
|
|
@@ -270,6 +271,7 @@ function propagate( model ) {
|
|
|
270
271
|
target[prop][$inferred] = 'prop';
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
// Only propagate if parent object (which is not necessarily `_parent`) was propagated.
|
|
273
275
|
function onlyViaParent( prop, target, source ) {
|
|
274
276
|
if (target.$inferred === 'proxy' || target.$inferred === 'expanded')
|
|
275
277
|
// assocs and enums do not have 'include'
|
|
@@ -279,6 +281,8 @@ function propagate( model ) {
|
|
|
279
281
|
function targetAspect( prop, target, source ) {
|
|
280
282
|
if (target.targetAspect)
|
|
281
283
|
return;
|
|
284
|
+
if (target.type?._artifact === model.definitions['cds.Association'])
|
|
285
|
+
return; // don't propagate targetAspect to associations (e.g. via $enclosed)
|
|
282
286
|
const ta = source.targetAspect;
|
|
283
287
|
if (!ta.elements && !ta._origin) { // _origin set for elements in source
|
|
284
288
|
notWithExpand( prop, target, source );
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -44,7 +44,6 @@ const {
|
|
|
44
44
|
forEachGeneric,
|
|
45
45
|
forEachInOrder,
|
|
46
46
|
isDeprecatedEnabled,
|
|
47
|
-
isBetaEnabled,
|
|
48
47
|
} = require('../base/model');
|
|
49
48
|
const { dictAdd } = require('../base/dictionaries');
|
|
50
49
|
const { dictLocation, weakLocation } = require('../base/location');
|
|
@@ -126,6 +125,11 @@ function resolve( model ) {
|
|
|
126
125
|
// Phase 4: resolve all artifacts:
|
|
127
126
|
forEachDefinition( model, resolveRefs );
|
|
128
127
|
forEachGeneric( model, 'vocabularies', resolveRefs );
|
|
128
|
+
if (model.options.lspMode) {
|
|
129
|
+
for (const name in model.sources)
|
|
130
|
+
resolveDefinitionName( model.sources[name].namespace );
|
|
131
|
+
}
|
|
132
|
+
|
|
129
133
|
// create “super” ANNOTATE statements for annotations on unknown artifacts:
|
|
130
134
|
createRemainingAnnotateStatements();
|
|
131
135
|
// report cyclic dependencies:
|
|
@@ -178,9 +182,12 @@ function resolve( model ) {
|
|
|
178
182
|
// Path could start with table alias; get start index
|
|
179
183
|
let index = path.indexOf(nav.item);
|
|
180
184
|
if (index === -1)
|
|
181
|
-
return;
|
|
185
|
+
return; // should not happen
|
|
182
186
|
|
|
183
187
|
let navItem = nav.navigation;
|
|
188
|
+
if (!nav.item._navigation) // first non-table-alias
|
|
189
|
+
setLink( nav.item, '_navigation', navItem );
|
|
190
|
+
|
|
184
191
|
if (path[index].where || path[index].args)
|
|
185
192
|
return;
|
|
186
193
|
++index;
|
|
@@ -363,6 +370,9 @@ function resolve( model ) {
|
|
|
363
370
|
const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
|
|
364
371
|
const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
|
|
365
372
|
|
|
373
|
+
if (model.options.lspMode && art.name && !art._main)
|
|
374
|
+
resolveDefinitionName( art );
|
|
375
|
+
|
|
366
376
|
// Check KEY (TODO: make this an extra function)
|
|
367
377
|
const { key } = art;
|
|
368
378
|
if (key?.val && !key.$inferred) {
|
|
@@ -557,10 +567,12 @@ function resolve( model ) {
|
|
|
557
567
|
const sType = specifiedElement.type?._artifact;
|
|
558
568
|
const iTypeArt = getInferredPropFromOrigin( 'type' )?._artifact;
|
|
559
569
|
const iType = iTypeArt || inferredElement;
|
|
560
|
-
|
|
561
|
-
// xor: could be missing a type;
|
|
562
570
|
// FIXME: The coding above returns incorrect iType for expand on associations
|
|
563
571
|
|
|
572
|
+
// $enclosed: maybe composition was changed to association; we allow that change here.
|
|
573
|
+
const compToAssoc = sType === model.definitions['cds.Association'] && inferredElement.target;
|
|
574
|
+
|
|
575
|
+
// xor: could be missing a type;
|
|
564
576
|
if (!specifiedElement.type && inferredElement.type) {
|
|
565
577
|
error( 'query-mismatched-element', [ specifiedElement.location, user ], {
|
|
566
578
|
'#': !specifiedElement.type ? 'missing' : 'extra', name: user.name.id, prop: 'type',
|
|
@@ -568,7 +580,7 @@ function resolve( model ) {
|
|
|
568
580
|
return;
|
|
569
581
|
}
|
|
570
582
|
// If specified type is `null`, type could not be resolved.
|
|
571
|
-
else if (sType && sType !== iType &&
|
|
583
|
+
else if (!compToAssoc && sType && sType !== iType &&
|
|
572
584
|
// Special case for $recompilation: allow one level of type indirection. See #12113.
|
|
573
585
|
(!model.options.$recompile || sType !== iType.type?._artifact)) {
|
|
574
586
|
const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
|
|
@@ -1348,6 +1360,19 @@ function resolve( model ) {
|
|
|
1348
1360
|
// General resolver functions
|
|
1349
1361
|
//--------------------------------------------------------------------------
|
|
1350
1362
|
|
|
1363
|
+
// Resolve the n-1 path steps before the definition name for LSP.
|
|
1364
|
+
function resolveDefinitionName( art ) {
|
|
1365
|
+
const path = art?.name?.path;
|
|
1366
|
+
if (!art || art._main || !path || path.length <= 1)
|
|
1367
|
+
return;
|
|
1368
|
+
|
|
1369
|
+
let name = art.name.id;
|
|
1370
|
+
for (let i = path.length - 1; i > 0; --i) {
|
|
1371
|
+
name = name.substring(0, name.length - path[i].id.length - 1);
|
|
1372
|
+
setArtifactLink( path[i - 1], model.definitions[name] || false );
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1351
1376
|
// Resolve the type and its arguments if applicable.
|
|
1352
1377
|
function resolveTypeExpr( art, user ) {
|
|
1353
1378
|
const typeArt = resolvePath( art.type, 'type', user );
|
|
@@ -1367,7 +1392,6 @@ function resolve( model ) {
|
|
|
1367
1392
|
if (!anno.kind)
|
|
1368
1393
|
initAnnotationForExpression( anno, art );
|
|
1369
1394
|
resolveExpr( expr, 'annotation', anno );
|
|
1370
|
-
reportUnsupportedAnnoExpr( expr );
|
|
1371
1395
|
}
|
|
1372
1396
|
else if (expr.literal === 'array') {
|
|
1373
1397
|
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
@@ -1377,32 +1401,22 @@ function resolve( model ) {
|
|
|
1377
1401
|
}
|
|
1378
1402
|
}
|
|
1379
1403
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
// due to test mode and shuffling, we would get a different location on each compilation.
|
|
1387
|
-
const loc = model.options.testMode ? null : [ expr.location ];
|
|
1388
|
-
info( 'anno-experimental-expressions', loc, {
|
|
1389
|
-
option: 'annotationExpressions',
|
|
1390
|
-
// eslint-disable-next-line max-len
|
|
1391
|
-
}, 'Expressions in annotation values are a beta feature. Use at your own risk. (This message can be suppressed with beta flag $(OPTION))' );
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
// for faster processing, mark artifacts and annotations which contain anno expressions
|
|
1404
|
+
/**
|
|
1405
|
+
* For faster processing, mark artifacts and annotations which contain anno expressions
|
|
1406
|
+
*
|
|
1407
|
+
* @param {object} anno
|
|
1408
|
+
* @param {XSN.Artifact} art
|
|
1409
|
+
*/
|
|
1396
1410
|
function initAnnotationForExpression( anno, art ) {
|
|
1397
1411
|
anno.kind = '$annotation';
|
|
1398
1412
|
setLink( anno, '_outer', art );
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1413
|
+
art.$contains ??= {};
|
|
1414
|
+
art.$contains.$annotation = { // set in resolveExprItem
|
|
1415
|
+
$path: false,
|
|
1416
|
+
$self: false,
|
|
1417
|
+
};
|
|
1418
|
+
// Think about tagging parents too (like before #12636).
|
|
1419
|
+
// Might be useful for future recursive types.
|
|
1406
1420
|
}
|
|
1407
1421
|
|
|
1408
1422
|
function resolveExpr( expr, exprCtx, user ) {
|
|
@@ -1434,6 +1448,11 @@ function resolve( model ) {
|
|
|
1434
1448
|
resolveParamsAndWhere( step, expected, user, step === last );
|
|
1435
1449
|
// TODO: delete 4th arg
|
|
1436
1450
|
}
|
|
1451
|
+
|
|
1452
|
+
if (expected === 'annotation') {
|
|
1453
|
+
user._outer.$contains.$annotation.$path = true;
|
|
1454
|
+
user._outer.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
|
|
1455
|
+
}
|
|
1437
1456
|
}
|
|
1438
1457
|
else if (expr.query) {
|
|
1439
1458
|
const { query } = expr;
|
package/lib/compiler/shared.js
CHANGED
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
isAssocToPrimaryKeys,
|
|
20
20
|
artifactRefLocation,
|
|
21
21
|
} = require('./utils');
|
|
22
|
+
const { isBetaEnabled } = require('../base/model');
|
|
22
23
|
|
|
23
24
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
24
25
|
const $location = Symbol.for( 'cds.$location' );
|
|
@@ -133,14 +134,14 @@ function fns( model ) {
|
|
|
133
134
|
param: paramSemantics,
|
|
134
135
|
},
|
|
135
136
|
filter: {
|
|
136
|
-
lexical:
|
|
137
|
+
lexical: justDollarAliases,
|
|
137
138
|
dollar: true,
|
|
138
139
|
dynamic: targetElements,
|
|
139
140
|
notFound: undefinedTargetElement,
|
|
140
141
|
param: paramSemantics,
|
|
141
142
|
},
|
|
142
143
|
'calc-filter': {
|
|
143
|
-
lexical:
|
|
144
|
+
lexical: justDollarAliases,
|
|
144
145
|
dollar: true,
|
|
145
146
|
dynamic: targetElements,
|
|
146
147
|
navigation: calcElemNavigation,
|
|
@@ -181,7 +182,7 @@ function fns( model ) {
|
|
|
181
182
|
check: checkColumnRef,
|
|
182
183
|
param: paramSemantics,
|
|
183
184
|
nestedColumn: () => ({ // in expand and inline
|
|
184
|
-
lexical:
|
|
185
|
+
lexical: justDollarAliases,
|
|
185
186
|
dollar: true,
|
|
186
187
|
dynamic: nestedElements,
|
|
187
188
|
notFound: undefinedNestedElement,
|
|
@@ -197,7 +198,7 @@ function fns( model ) {
|
|
|
197
198
|
param: paramSemantics,
|
|
198
199
|
},
|
|
199
200
|
calc: {
|
|
200
|
-
lexical:
|
|
201
|
+
lexical: justDollarAliases,
|
|
201
202
|
dollar: true,
|
|
202
203
|
dynamic: parentElements,
|
|
203
204
|
navigation: calcElemNavigation,
|
|
@@ -213,7 +214,7 @@ function fns( model ) {
|
|
|
213
214
|
param: paramSemantics,
|
|
214
215
|
},
|
|
215
216
|
on: { // unmanaged assoc: outside query, redirected or new assoc in column
|
|
216
|
-
lexical:
|
|
217
|
+
lexical: justDollarAliases,
|
|
217
218
|
dollar: true,
|
|
218
219
|
dynamic: parentElements,
|
|
219
220
|
navigation: assocOnNavigation,
|
|
@@ -222,7 +223,7 @@ function fns( model ) {
|
|
|
222
223
|
check: checkAssocOn,
|
|
223
224
|
param: paramUnsupported,
|
|
224
225
|
nestedColumn: () => ({ // in expand and inline
|
|
225
|
-
lexical:
|
|
226
|
+
lexical: justDollarAliases,
|
|
226
227
|
dollar: true,
|
|
227
228
|
dynamic: parentElements,
|
|
228
229
|
navigation: assocOnNavigation,
|
|
@@ -276,7 +277,7 @@ function fns( model ) {
|
|
|
276
277
|
param: paramSemantics,
|
|
277
278
|
},
|
|
278
279
|
annotation: { // annotation assignments
|
|
279
|
-
lexical:
|
|
280
|
+
lexical: justDollarAliases,
|
|
280
281
|
dollar: true,
|
|
281
282
|
dynamic: parentElements,
|
|
282
283
|
navigation: assocOnNavigation,
|
|
@@ -287,8 +288,8 @@ function fns( model ) {
|
|
|
287
288
|
'ref-undefined-param': 'anno-undefined-param',
|
|
288
289
|
},
|
|
289
290
|
param: paramSemantics,
|
|
290
|
-
nestedColumn: () => ({
|
|
291
|
-
lexical:
|
|
291
|
+
nestedColumn: () => ({
|
|
292
|
+
lexical: justDollarAliases,
|
|
292
293
|
dollar: true,
|
|
293
294
|
dynamic: parentElements,
|
|
294
295
|
navigation: assocOnNavigation,
|
|
@@ -298,14 +299,21 @@ function fns( model ) {
|
|
|
298
299
|
},
|
|
299
300
|
// TODO: introduce some kind of inheritance
|
|
300
301
|
annoRewrite: { // annotation assignments
|
|
301
|
-
lexical:
|
|
302
|
+
lexical: justDollarAliases,
|
|
302
303
|
dollar: true,
|
|
303
304
|
dynamic: parentElements,
|
|
304
305
|
navigation: assocOnNavigation,
|
|
305
306
|
noDep: true,
|
|
306
307
|
notFound: null, // no error, just falsy links
|
|
307
308
|
param: paramSemantics,
|
|
308
|
-
|
|
309
|
+
nestedColumn: () => ({
|
|
310
|
+
lexical: justDollarAliases,
|
|
311
|
+
dollar: true,
|
|
312
|
+
dynamic: parentElements,
|
|
313
|
+
navigation: assocOnNavigation,
|
|
314
|
+
notFound: undefinedParentElement,
|
|
315
|
+
rewriteProjectionToSelf: true,
|
|
316
|
+
}),
|
|
309
317
|
},
|
|
310
318
|
};
|
|
311
319
|
|
|
@@ -313,9 +321,11 @@ function fns( model ) {
|
|
|
313
321
|
traverseExpr,
|
|
314
322
|
resolveUncheckedPath,
|
|
315
323
|
resolveTypeArgumentsUnchecked, // TODO: move to some other file
|
|
324
|
+
resolvePathRoot,
|
|
316
325
|
resolvePath,
|
|
317
326
|
checkExpr,
|
|
318
327
|
checkOnCondition,
|
|
328
|
+
navigationEnv,
|
|
319
329
|
nestedElements,
|
|
320
330
|
attachAndEmitValidNames,
|
|
321
331
|
} );
|
|
@@ -354,7 +364,6 @@ function fns( model ) {
|
|
|
354
364
|
const { path } = ref;
|
|
355
365
|
if (!path || path.broken) // incomplete type AST
|
|
356
366
|
return undefined;
|
|
357
|
-
|
|
358
367
|
const semantics = referenceSemantics[refCtx];
|
|
359
368
|
if (!semantics.isMainRef)
|
|
360
369
|
throw new CompilerAssertion( `resolveUncheckedPath() called for reference ctx '${ refCtx }'` );
|
|
@@ -628,6 +637,22 @@ function fns( model ) {
|
|
|
628
637
|
return art;
|
|
629
638
|
}
|
|
630
639
|
|
|
640
|
+
/**
|
|
641
|
+
* Resolve the _path-root_ only. Used for rewriting annotation paths.
|
|
642
|
+
*
|
|
643
|
+
* @param ref
|
|
644
|
+
* @param {string} expected
|
|
645
|
+
* @param user
|
|
646
|
+
*/
|
|
647
|
+
function resolvePathRoot( ref, expected, user ) {
|
|
648
|
+
if (ref == null || !ref.path) // no references -> nothing to do
|
|
649
|
+
return undefined;
|
|
650
|
+
const s = referenceSemantics[expected];
|
|
651
|
+
const semantics = (typeof s === 'string') ? referenceSemantics[s] : s;
|
|
652
|
+
const r = getPathRoot( ref, semantics, user );
|
|
653
|
+
return r && acceptPathRoot( r, ref, semantics, user );
|
|
654
|
+
}
|
|
655
|
+
|
|
631
656
|
// Helper functions for resolve[Unchecked]Path, getPath{Root,Item}: -----------
|
|
632
657
|
|
|
633
658
|
function acceptLexical( art, path, semantics, user ) {
|
|
@@ -687,9 +712,14 @@ function fns( model ) {
|
|
|
687
712
|
// TODO: remove again, should be easy enough in to-csn without.
|
|
688
713
|
if (path.length === 1 && art.kind === '$tableAlias')
|
|
689
714
|
(user._user || user).$noOrigin = true;
|
|
715
|
+
if (head.id === '$projection' && user.kind === '$annotation') {
|
|
716
|
+
error( 'ref-unsupported-projection', [ head.location, user ],
|
|
717
|
+
{ code: '$projection', newcode: '$self' },
|
|
718
|
+
'$(CODE) is not supported in annotations; replace by $(NEWCODE)' );
|
|
719
|
+
}
|
|
690
720
|
return art;
|
|
691
721
|
}
|
|
692
|
-
case '$parameters': {
|
|
722
|
+
case '$parameters': {
|
|
693
723
|
// TODO: if ref.scope='param' is handled, test that here, too ?
|
|
694
724
|
const { id } = path[1];
|
|
695
725
|
message( 'ref-obsolete-parameters', [ head.location, user ],
|
|
@@ -756,10 +786,10 @@ function fns( model ) {
|
|
|
756
786
|
function userBlock( user ) {
|
|
757
787
|
return definedViaCdl( user ) && user._block;
|
|
758
788
|
}
|
|
759
|
-
function
|
|
789
|
+
function justDollarAliases( user ) {
|
|
760
790
|
const query = userQuery( user );
|
|
761
791
|
if (!query)
|
|
762
|
-
return user._main || user;
|
|
792
|
+
return user._main || user; // TODO: also contains `up_` for aspects; remove
|
|
763
793
|
// query.$tableAliases contains both aliases and $self/$projection
|
|
764
794
|
const aliases = query.$tableAliases;
|
|
765
795
|
const r = Object.create( null );
|
|
@@ -783,7 +813,7 @@ function fns( model ) {
|
|
|
783
813
|
const aliases = userQuery( user ).$tableAliases;
|
|
784
814
|
user.$extended = Object.keys( aliases )[0];
|
|
785
815
|
}
|
|
786
|
-
return
|
|
816
|
+
return justDollarAliases( user );
|
|
787
817
|
}
|
|
788
818
|
|
|
789
819
|
// Functions called via semantics.dynamic: ------------------------------------
|
|
@@ -829,7 +859,13 @@ function fns( model ) {
|
|
|
829
859
|
return query._combined; // TODO: do we need query._parent._combined ?
|
|
830
860
|
}
|
|
831
861
|
function parentElements( user ) {
|
|
832
|
-
|
|
862
|
+
// Note: We could have `$self` in bound actions refer to its entity, but reject it now.
|
|
863
|
+
// If users request it, we can either allow it later or point them to binding parameters.
|
|
864
|
+
const useParent = user._main &&
|
|
865
|
+
user.kind !== 'select' &&
|
|
866
|
+
user.kind !== 'action' &&
|
|
867
|
+
user.kind !== 'function';
|
|
868
|
+
return environment( useParent ? user._parent : user );
|
|
833
869
|
}
|
|
834
870
|
|
|
835
871
|
function queryElements( user ) {
|
|
@@ -1144,7 +1180,7 @@ function fns( model ) {
|
|
|
1144
1180
|
// corresponding column expression; this might have references to elements
|
|
1145
1181
|
// of invisible table aliases; at least one stakeholder uses this,
|
|
1146
1182
|
// so it can't be an error (yet).
|
|
1147
|
-
|
|
1183
|
+
message( 'ref-deprecated-self-element', [ ref.path[0].location, user._user ], {},
|
|
1148
1184
|
// eslint-disable-next-line max-len
|
|
1149
1185
|
'Referring to the query\'s own elements here might lead to invalid SQL references; use source elements only' );
|
|
1150
1186
|
return false;
|
|
@@ -1199,8 +1235,12 @@ function fns( model ) {
|
|
|
1199
1235
|
}
|
|
1200
1236
|
|
|
1201
1237
|
function acceptRealArtifact( art, user, ref ) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1238
|
+
if (art.kind !== 'namespace')
|
|
1239
|
+
return art;
|
|
1240
|
+
// For compatibility (≤v4), we accept `extend Unknown` without elements/actions/includes
|
|
1241
|
+
// In v5, only allow `extend with definitions`.
|
|
1242
|
+
if (!isBetaEnabled( model.options, 'v5preview' ) &&
|
|
1243
|
+
!(user.elements || user.actions || user.includes))
|
|
1204
1244
|
return art;
|
|
1205
1245
|
const { location } = ref.path[ref.path.length - 1];
|
|
1206
1246
|
signalNotFound( 'ref-undefined-def', [ location, user ], null, { art } );
|