@sap/cds-compiler 5.7.2 → 5.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 +62 -2
- package/bin/cdsse.js +13 -1
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/options.js +2 -1
- package/lib/api/validate.js +9 -0
- package/lib/base/location.js +1 -1
- package/lib/base/message-registry.js +55 -20
- package/lib/base/messages.js +5 -2
- package/lib/base/model.js +8 -6
- package/lib/checks/assocOutsideService.js +40 -0
- package/lib/checks/featureFlags.js +4 -1
- package/lib/checks/types.js +7 -4
- package/lib/checks/validator.js +3 -0
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/checks.js +79 -17
- package/lib/compiler/define.js +60 -3
- package/lib/compiler/extend.js +1 -2
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/populate.js +17 -6
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +181 -150
- package/lib/compiler/shared.js +276 -22
- package/lib/compiler/tweak-assocs.js +15 -4
- package/lib/compiler/xpr-rewrite.js +76 -50
- package/lib/edm/annotations/edmJson.js +1 -1
- package/lib/edm/annotations/genericTranslation.js +2 -2
- package/lib/edm/csn2edm.js +2 -2
- package/lib/edm/edmPreprocessor.js +15 -9
- package/lib/edm/edmUtils.js +12 -5
- package/lib/gen/CdlGrammar.checksum +1 -0
- package/lib/gen/CdlParser.js +2239 -2229
- package/lib/gen/Dictionary.json +55 -8
- package/lib/json/from-csn.js +37 -17
- package/lib/json/to-csn.js +4 -0
- package/lib/language/genericAntlrParser.js +7 -0
- package/lib/main.d.ts +5 -1
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +0 -5
- package/lib/modelCompare/utils/filter.js +2 -2
- package/lib/optionProcessor.js +2 -0
- package/lib/parsers/AstBuildingParser.js +72 -34
- package/lib/parsers/CdlGrammar.g4 +20 -19
- package/lib/parsers/XprTree.js +206 -0
- package/lib/parsers/index.js +1 -1
- package/lib/render/toCdl.js +61 -89
- package/lib/render/toSql.js +59 -29
- package/lib/render/utils/standardDatabaseFunctions.js +252 -15
- package/lib/transform/addTenantFields.js +9 -3
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -0
- package/lib/transform/db/assocsToQueries/utils.js +10 -3
- package/lib/transform/db/expansion.js +3 -1
- package/lib/transform/db/flattening.js +7 -3
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +70 -17
- package/lib/transform/draft/db.js +8 -3
- package/lib/transform/draft/odata.js +27 -4
- package/lib/transform/effective/main.js +37 -10
- package/lib/transform/effective/misc.js +4 -9
- package/lib/transform/effective/service.js +34 -0
- package/lib/transform/effective/types.js +28 -17
- package/lib/transform/forOdata.js +36 -10
- package/lib/transform/forRelationalDB.js +30 -18
- package/lib/transform/odata/adaptAnnotationRefs.js +37 -21
- package/lib/transform/odata/createForeignKeys.js +120 -116
- package/lib/transform/odata/flattening.js +10 -8
- package/lib/transform/transformUtils.js +58 -25
- package/lib/transform/translateAssocsToJoins.js +10 -6
- package/lib/transform/universalCsn/coreComputed.js +5 -1
- package/package.json +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/rewrite-not-supported.md +5 -0
- package/share/messages/rewrite-undefined-key.md +94 -0
package/lib/compiler/resolve.js
CHANGED
|
@@ -46,7 +46,7 @@ const {
|
|
|
46
46
|
isDeprecatedEnabled,
|
|
47
47
|
} = require('../base/model');
|
|
48
48
|
const { dictAdd } = require('../base/dictionaries');
|
|
49
|
-
const {
|
|
49
|
+
const { weakLocation } = require('../base/location');
|
|
50
50
|
const { combinedLocation } = require('../base/location');
|
|
51
51
|
const { typeParameters } = require('./builtins');
|
|
52
52
|
|
|
@@ -92,15 +92,16 @@ function resolve( model ) {
|
|
|
92
92
|
resolveDefinitionName,
|
|
93
93
|
attachAndEmitValidNames,
|
|
94
94
|
traverseExpr,
|
|
95
|
+
traverseTypedExpr,
|
|
95
96
|
effectiveType,
|
|
96
97
|
getOrigin,
|
|
97
98
|
getInheritedProp,
|
|
98
99
|
resolveTypeArgumentsUnchecked,
|
|
99
100
|
} = model.$functions;
|
|
100
101
|
Object.assign( model.$functions, {
|
|
101
|
-
resolveExpr,
|
|
102
102
|
addForeignKeyNavigations,
|
|
103
103
|
redirectionChain,
|
|
104
|
+
resolveExprInAnnotations,
|
|
104
105
|
} );
|
|
105
106
|
|
|
106
107
|
const ignoreSpecifiedElements
|
|
@@ -260,6 +261,10 @@ function resolve( model ) {
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
function propagateKeyProps( view ) {
|
|
264
|
+
if (view.kind === 'type') {
|
|
265
|
+
// we don't propagate keys to type projections, see #13575
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
263
268
|
// Second argument true ensure that `key` is only propagated along simple
|
|
264
269
|
// view, i.e. ref or subquery in FROM, not UNION or JOIN.
|
|
265
270
|
traverseQueryPost( view.query, true, ( query ) => {
|
|
@@ -552,11 +557,11 @@ function resolve( model ) {
|
|
|
552
557
|
addForeignKeyNavigations( art );
|
|
553
558
|
}
|
|
554
559
|
|
|
555
|
-
|
|
560
|
+
resolveExpr( art.default, 'default', art, art );
|
|
556
561
|
|
|
557
562
|
// TODO: distinguish not by $syntax (it is semantics), but whether in query
|
|
558
563
|
const valueCtx = (art.$syntax === 'calc') ? 'calc' : 'column';
|
|
559
|
-
|
|
564
|
+
resolveExpr( art.value, valueCtx, art, art );
|
|
560
565
|
if (art.type?.$inferred === 'cast')
|
|
561
566
|
inferTypePropertiesFromCast( art );
|
|
562
567
|
if (art.value) {
|
|
@@ -598,7 +603,7 @@ function resolve( model ) {
|
|
|
598
603
|
|
|
599
604
|
// Check explicit types: If either side has one, so must the other.
|
|
600
605
|
const sType = specifiedElement.type?._artifact;
|
|
601
|
-
const iTypeArt =
|
|
606
|
+
const iTypeArt = getInheritedProp( inferredElement, 'type' )?._artifact;
|
|
602
607
|
const iType = iTypeArt || inferredElement;
|
|
603
608
|
// FIXME: The coding above returns incorrect iType for expand on associations
|
|
604
609
|
|
|
@@ -678,7 +683,7 @@ function resolve( model ) {
|
|
|
678
683
|
}
|
|
679
684
|
|
|
680
685
|
if (specifiedElement.virtual) {
|
|
681
|
-
const iVirtual =
|
|
686
|
+
const iVirtual = getInheritedProp( inferredElement, 'virtual' )?.val || false;
|
|
682
687
|
if (!specifiedElement.virtual.val !== !iVirtual) {
|
|
683
688
|
error( 'query-mismatched-element', [
|
|
684
689
|
specifiedElement.virtual.location || specifiedElement.location, user,
|
|
@@ -737,7 +742,7 @@ function resolve( model ) {
|
|
|
737
742
|
|
|
738
743
|
if (specifiedElement.key) { // TODO: `|| inferredElement.key?.val`, once to.sql is fixed
|
|
739
744
|
// TODO: Do not use _origin chain for key; has been propagated in propagateKeyProps().
|
|
740
|
-
const iKey =
|
|
745
|
+
const iKey = getInheritedProp( inferredElement, 'key' )?.val;
|
|
741
746
|
// If "key" is specified or truthy in the inferred element, the values must match.
|
|
742
747
|
if (!iKey !== !specifiedElement.key?.val) {
|
|
743
748
|
error( 'query-mismatched-element', [
|
|
@@ -793,16 +798,6 @@ function resolve( model ) {
|
|
|
793
798
|
return false;
|
|
794
799
|
}
|
|
795
800
|
|
|
796
|
-
function getInferredPropFromOrigin( prop ) {
|
|
797
|
-
// Inferred property via _origin chain (0 === circular).
|
|
798
|
-
let element = inferredElement;
|
|
799
|
-
if (element._effectiveType !== 0) {
|
|
800
|
-
while (getOrigin( element ) && !element[prop])
|
|
801
|
-
element = getOrigin( element );
|
|
802
|
-
}
|
|
803
|
-
return element[prop];
|
|
804
|
-
}
|
|
805
|
-
|
|
806
801
|
function getInferredCardinality() {
|
|
807
802
|
let element = inferredElement;
|
|
808
803
|
if (element._effectiveType !== 0) {
|
|
@@ -822,11 +817,19 @@ function resolve( model ) {
|
|
|
822
817
|
// Set '@Core.Computed' in the Core Compiler to have it propagated...
|
|
823
818
|
if (art.kind !== 'element' || art['@Core.Computed'])
|
|
824
819
|
return;
|
|
820
|
+
|
|
821
|
+
// For events and types, elements can't be @Core.Computed, as values are only used
|
|
822
|
+
// to infer the element signature. For virtual, we keep @Core.Computed, as it's
|
|
823
|
+
// always been that way, even before type projections.
|
|
824
|
+
const elementsCanBeComputed = art._main?.kind !== 'type' && art._main?.kind !== 'event';
|
|
825
|
+
|
|
825
826
|
if (art.virtual?.val ||
|
|
826
|
-
|
|
827
|
+
elementsCanBeComputed && art.value &&
|
|
827
828
|
(!art.value._artifact || !art.value.path || // in localization view: _artifact, but no path
|
|
828
829
|
art.value.stored?.val || // calculated elements on-write are always computed
|
|
829
|
-
art.value._artifact.kind === 'builtin' ||
|
|
830
|
+
art.value._artifact.kind === 'builtin' ||
|
|
831
|
+
art.value._artifact.kind === 'param' ||
|
|
832
|
+
art.value.scope === 'param' )) {
|
|
830
833
|
art['@Core.Computed'] = {
|
|
831
834
|
name: {
|
|
832
835
|
path: [ { id: 'Core.Computed', location: art.location } ],
|
|
@@ -1067,10 +1070,11 @@ function resolve( model ) {
|
|
|
1067
1070
|
traverseExpr( art.on, 'on-check', art, (expr) => {
|
|
1068
1071
|
const { path } = expr;
|
|
1069
1072
|
if (!expr?._artifact || path?.length < 2 || issue['#'])
|
|
1070
|
-
return;
|
|
1073
|
+
return traverseExpr.SKIP; // no path or with error or already found issue
|
|
1071
1074
|
const head = (path[0]._navigation?.kind === '$self') ? 1 : 0;
|
|
1072
1075
|
if (path[head]._artifact === art)
|
|
1073
1076
|
checkAutoRedirectedPathItem( path[head + 1], modelTarget, issue );
|
|
1077
|
+
return traverseExpr.SKIP;
|
|
1074
1078
|
} );
|
|
1075
1079
|
// Check explicit+implicit foreign keys: no renamed target element
|
|
1076
1080
|
const implicit = art.foreignKeys?.[$inferred];
|
|
@@ -1390,6 +1394,7 @@ function resolve( model ) {
|
|
|
1390
1394
|
const typeArt = resolvePath( art.type, 'type', user );
|
|
1391
1395
|
if (typeArt)
|
|
1392
1396
|
resolveTypeArgumentsUnchecked( art, typeArt, user );
|
|
1397
|
+
return typeArt;
|
|
1393
1398
|
}
|
|
1394
1399
|
|
|
1395
1400
|
function resolveExprInAnnotations( art ) {
|
|
@@ -1408,7 +1413,10 @@ function resolve( model ) {
|
|
|
1408
1413
|
if (expr.$tokenTexts) {
|
|
1409
1414
|
if (!anno.kind)
|
|
1410
1415
|
initAnnotationForExpression( anno, art );
|
|
1411
|
-
|
|
1416
|
+
const type = anno === expr && anno.name;
|
|
1417
|
+
// TODO: it might be best to set an _artifact link also for property values
|
|
1418
|
+
// like in `@Anno: [ { foo: #EnumSymbol }]
|
|
1419
|
+
resolveExpr( expr, 'annotation', anno, type );
|
|
1412
1420
|
}
|
|
1413
1421
|
else if (expr.literal === 'array') {
|
|
1414
1422
|
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
@@ -1428,7 +1436,7 @@ function resolve( model ) {
|
|
|
1428
1436
|
anno.kind = '$annotation';
|
|
1429
1437
|
setLink( anno, '_outer', art );
|
|
1430
1438
|
art.$contains ??= {};
|
|
1431
|
-
art.$contains.$annotation = { // set in
|
|
1439
|
+
art.$contains.$annotation = { // set in resolveExprNode
|
|
1432
1440
|
$path: false,
|
|
1433
1441
|
$self: false,
|
|
1434
1442
|
};
|
|
@@ -1436,157 +1444,180 @@ function resolve( model ) {
|
|
|
1436
1444
|
// Might be useful for future recursive types.
|
|
1437
1445
|
}
|
|
1438
1446
|
|
|
1439
|
-
function
|
|
1440
|
-
|
|
1441
|
-
const sym = expr?.sym;
|
|
1442
|
-
const assignment = art;
|
|
1443
|
-
// Check enum as top-level annotation value:
|
|
1444
|
-
if (art?.kind === '$annotation')
|
|
1445
|
-
art = art === expr && art.name._artifact;
|
|
1446
|
-
if (!art)
|
|
1447
|
-
return;
|
|
1448
|
-
const symbols = effectiveType( art )?.enum;
|
|
1449
|
-
if (!sym || !symbols)
|
|
1450
|
-
return;
|
|
1451
|
-
// TODO: or warning if enum symbol but non-enum type?
|
|
1452
|
-
if (symbols[sym.id]) {
|
|
1453
|
-
setLink( sym, '_artifact', symbols[sym.id] );
|
|
1454
|
-
}
|
|
1455
|
-
else {
|
|
1456
|
-
let type = art._effectiveType;
|
|
1457
|
-
// inferred enums can't be extended (yet): show underlying enum
|
|
1458
|
-
while (type.enum[$inferred])
|
|
1459
|
-
type = getOrigin( type );
|
|
1460
|
-
const err = message( 'ref-undefined-enum', [ sym.location, assignment ],
|
|
1461
|
-
{ id: sym.id, type } );
|
|
1462
|
-
if (options.newParser)
|
|
1463
|
-
attachAndEmitValidNames( err, symbols );
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
function resolveExpr( expr, exprCtx, user ) {
|
|
1468
|
-
traverseExpr( expr, exprCtx, user, resolveExprItem );
|
|
1447
|
+
function resolveExpr( expr, exprCtx, art, type = null ) {
|
|
1448
|
+
traverseTypedExpr( expr, exprCtx, art, type, resolveExprNode );
|
|
1469
1449
|
}
|
|
1470
1450
|
|
|
1471
|
-
function
|
|
1451
|
+
function resolveExprNode( expr, expected, user, type ) {
|
|
1472
1452
|
if (expr.type) // e.g. cast( a as Integer )
|
|
1473
|
-
resolveTypeExpr( expr, user._user || user );
|
|
1453
|
+
type = resolveTypeExpr( expr, user._user || user );
|
|
1474
1454
|
|
|
1475
1455
|
if (expr.path) {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
// TODO: location of EXISTS, TODO: really do this in define.js
|
|
1484
|
-
expr.$expected = 'approved-exists'; // only complain once
|
|
1485
|
-
}
|
|
1486
|
-
resolvePath( expr, expected, user );
|
|
1487
|
-
|
|
1488
|
-
const last = expr.$expected !== 'approved-exists' && expr.path[expr.path.length - 1];
|
|
1489
|
-
for (const step of expr.path) {
|
|
1490
|
-
if (step && (step.args || step.where || step.cardinality) &&
|
|
1491
|
-
step._artifact && !Array.isArray( step._artifact ) )
|
|
1492
|
-
resolveParamsAndWhere( step, expected, user, step === last );
|
|
1493
|
-
// TODO: delete 4th arg
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
if (expected === 'annotation') {
|
|
1497
|
-
user._outer.$contains.$annotation.$path = true;
|
|
1498
|
-
user._outer.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
|
|
1499
|
-
}
|
|
1456
|
+
type = resolveExprPath( expr, expected, user );
|
|
1457
|
+
}
|
|
1458
|
+
else if (expr.id) {
|
|
1459
|
+
type = resolvePathItem( expr, expected, user );
|
|
1460
|
+
}
|
|
1461
|
+
else if (expr.sym) {
|
|
1462
|
+
resolveEnumSymbol( expr, expected, user, type );
|
|
1500
1463
|
}
|
|
1501
1464
|
else if (expr.query) {
|
|
1465
|
+
// No traversal into query, art.$queries set in define.js
|
|
1466
|
+
// No subqueries for type projections, nor in annotation expressions.
|
|
1502
1467
|
const { query } = expr;
|
|
1503
|
-
if (query.kind
|
|
1504
|
-
|
|
1468
|
+
if (query._main?.kind === 'type' ||
|
|
1469
|
+
(!query.kind && !query._leadingQuery)) { // UNION has _leadingQuery
|
|
1470
|
+
error( 'expr-no-subquery', [ expr.location, user ], {
|
|
1471
|
+
'#': query._main?.kind === 'type' ? 'type' : 'std',
|
|
1472
|
+
}, {
|
|
1473
|
+
std: 'Subqueries are not supported here',
|
|
1474
|
+
type: 'Subqueries are not supported in type projections',
|
|
1475
|
+
} );
|
|
1505
1476
|
}
|
|
1506
|
-
else {
|
|
1507
|
-
|
|
1508
|
-
|
|
1477
|
+
else if (query._main?.kind === 'event') {
|
|
1478
|
+
warning( 'expr-no-event-subquery', [ expr.location, user ],
|
|
1479
|
+
'Subqueries are not supported in event projections' );
|
|
1509
1480
|
}
|
|
1510
1481
|
}
|
|
1482
|
+
return type;
|
|
1511
1483
|
}
|
|
1512
1484
|
|
|
1513
|
-
function
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
const entity = art.kind === 'entity' &&
|
|
1520
|
-
(!isLast || user.expand || user.inline || expWithFilter.includes( expected )) &&
|
|
1521
|
-
art;
|
|
1522
|
-
if (step.args)
|
|
1523
|
-
resolveParams( step.args, art, entity, expected, user, step.location );
|
|
1524
|
-
|
|
1525
|
-
// Publishing an association with filters is ok in columns and also ok in calculated elements.
|
|
1526
|
-
const publishAssoc = art.kind === 'entity' && (expected === 'column' || expected === 'calc');
|
|
1527
|
-
|
|
1528
|
-
if (entity || publishAssoc) {
|
|
1529
|
-
if (step.where) {
|
|
1530
|
-
setLink( step, '_user', user._user || user );
|
|
1531
|
-
const inCalc = (expected === 'calc' || expected === 'calc-filter');
|
|
1532
|
-
resolveExpr( step.where, (inCalc ? 'calc-filter' : 'filter'), step );
|
|
1485
|
+
function resolveExprPath( expr, expected, user ) {
|
|
1486
|
+
// TODO: re-think this $expected: 'exists' thing
|
|
1487
|
+
if (expr.$expected === 'exists') {
|
|
1488
|
+
if (expected !== 'annotation') { // `exists e[…]` allowed in annotation expressions
|
|
1489
|
+
error( 'expr-unexpected-exists', [ expr.location, user ], {},
|
|
1490
|
+
'An EXISTS predicate is not expected here' );
|
|
1533
1491
|
}
|
|
1492
|
+
// We complain about the EXISTS before, as EXISTS subquery is also not supported
|
|
1493
|
+
// TODO: location of EXISTS, TODO: really do this in define.js
|
|
1494
|
+
expr.$expected = 'approved-exists'; // only complain once
|
|
1495
|
+
}
|
|
1496
|
+
const ref = resolvePath( expr, expected, user );
|
|
1497
|
+
|
|
1498
|
+
if (expected === 'annotation') {
|
|
1499
|
+
user._outer.$contains.$annotation.$path = true;
|
|
1500
|
+
user._outer.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// check whether arguments and filters are allowed on last path item;
|
|
1504
|
+
// references in those are to be resolved below even if not allowed
|
|
1505
|
+
// (for code completion)
|
|
1506
|
+
const last = expr.path[expr.path.length - 1];
|
|
1507
|
+
if (!last || !(last.args || last.where || last.cardinality) ||
|
|
1508
|
+
expr.$expected === 'approved-exists' ||
|
|
1509
|
+
user.expand || user.inline ||
|
|
1510
|
+
expWithFilter.includes( expected ) || // `from`, …
|
|
1511
|
+
last._navigation?.kind === '$tableAlias') // error already reported
|
|
1512
|
+
return ref;
|
|
1513
|
+
|
|
1514
|
+
const type = effectiveType( last._artifact );
|
|
1515
|
+
const art = type && (type.kind === 'entity' ? type : type.target?._artifact);
|
|
1516
|
+
if (!art)
|
|
1517
|
+
return ref; // error already reported via resolvePathItem()
|
|
1518
|
+
const unexpectedFilter = expected !== 'column' && expected !== 'calc';
|
|
1519
|
+
if (last.args || last.where || last.cardinality)
|
|
1520
|
+
reportUnexpectedArgsAndFilter( last, expected, user, art, unexpectedFilter && 'std' );
|
|
1521
|
+
// TODO: we should have different message-ids for the "last" stuff: adding
|
|
1522
|
+
// `.item` likely corrects the ref, probably with location at end of ref
|
|
1523
|
+
return ref;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
function resolvePathItem( step, expected, user ) {
|
|
1527
|
+
// In FROM ref, first item artifact might be array (duplicate in same
|
|
1528
|
+
// `artifacts` dictionary)
|
|
1529
|
+
if (!step._artifact || Array.isArray( step._artifact ))
|
|
1530
|
+
return traverseExpr.SKIP; // cannot resolve filter refs for undef'd / duplicates
|
|
1531
|
+
|
|
1532
|
+
const isAlias = step._navigation?.kind === '$tableAlias';
|
|
1533
|
+
if (isAlias)
|
|
1534
|
+
return reportUnexpectedArgsAndFilter( step, expected, user, { params: {} }, 'tableAlias' );
|
|
1535
|
+
const type = effectiveType( step._artifact );
|
|
1536
|
+
const art = type && (type.kind === 'entity' ? type : type.target?._artifact);
|
|
1537
|
+
if (!art) {
|
|
1538
|
+
const exp = (expected === 'from') ? 'from' : 'std';
|
|
1539
|
+
return (type && type.target)
|
|
1540
|
+
? traverseExpr.SKIP // something wrong with the association
|
|
1541
|
+
: reportUnexpectedArgsAndFilter( step, expected, user, { params: {} }, exp );
|
|
1542
|
+
}
|
|
1543
|
+
setLink( step, '_user', user._user || user );
|
|
1544
|
+
const { args } = step;
|
|
1545
|
+
if (!args)
|
|
1546
|
+
return null;
|
|
1547
|
+
if (Array.isArray( args )) {
|
|
1548
|
+
const loc = [ args[0]?.location || step.location, user ];
|
|
1549
|
+
error( 'expr-expected-named-argument', loc, {},
|
|
1550
|
+
'Expected named parameters for the entity' );
|
|
1551
|
+
// only via CSN input - TODO: or do in CSN parser?
|
|
1552
|
+
return null;
|
|
1534
1553
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1554
|
+
if (!art.params) {
|
|
1555
|
+
// remark: from the location, this should be 'expr-unexpected-arguments',
|
|
1556
|
+
// or probably better: use a text variant of expr-undefined-param
|
|
1557
|
+
error( 'expr-unexpected-argument', [ args[$location] || step.location, user ],
|
|
1558
|
+
{ art, '#': 'no-params' } ); // TODO: own message-id ?
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
for (const id in args) {
|
|
1563
|
+
const param = art.params[id];
|
|
1564
|
+
const { name } = args[id];
|
|
1565
|
+
setArtifactLink( name, param );
|
|
1566
|
+
if (!param) {
|
|
1567
|
+
error( 'expr-undefined-param', [ name.location, user ], { art, id },
|
|
1568
|
+
'Entity $(ART) has no parameter $(ID)' );
|
|
1569
|
+
}
|
|
1570
|
+
// no need to do s/th special for duplicate arguments in args[id].$duplicates
|
|
1548
1571
|
}
|
|
1572
|
+
return null;
|
|
1549
1573
|
}
|
|
1550
1574
|
|
|
1551
|
-
function
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
first = first[0];
|
|
1556
|
-
error( 'expr-unexpected-argument',
|
|
1557
|
-
[ dict[$location] || dictLocation( dict, first?.name?.location || stepLocation ),
|
|
1558
|
-
user ],
|
|
1559
|
-
{ art, '#': (entity ? 'entity' : expected ) },
|
|
1560
|
-
{
|
|
1561
|
-
std: 'Parameters can only be provided when navigating along associations',
|
|
1562
|
-
from: 'Parameters can only be provided for the source entity or associations',
|
|
1563
|
-
// or extra message id for entity?
|
|
1564
|
-
entity: 'Unexpected arguments for entity $(ART) without parameters',
|
|
1565
|
-
} );
|
|
1575
|
+
function resolveEnumSymbol( expr, expected, user, type ) {
|
|
1576
|
+
const { sym } = expr;
|
|
1577
|
+
// CSN input with both '#'+val (recompilation) → do not resolve
|
|
1578
|
+
if (!sym || expr.val !== undefined || !type)
|
|
1566
1579
|
return;
|
|
1580
|
+
// The parameter def for an argument, and the annotation def for an assignment
|
|
1581
|
+
// is in `type._artifact`, it is not `type` itself:
|
|
1582
|
+
type = effectiveType( type.id ? type._artifact : type );
|
|
1583
|
+
// Remark: do not use `?.`, type could be 0 for cyclic parameter type
|
|
1584
|
+
if (type && type.foreignKeys) {
|
|
1585
|
+
const keys = Object.values( type.foreignKeys );
|
|
1586
|
+
if (keys.length === 1) {
|
|
1587
|
+
const elem = resolvePath( keys[0].targetElement, 'targetElement', keys[0] );
|
|
1588
|
+
type = effectiveType( elem );
|
|
1589
|
+
}
|
|
1567
1590
|
}
|
|
1568
|
-
const
|
|
1569
|
-
if (
|
|
1570
|
-
const loc = [ dict[0] && dict[0].location || stepLocation, user ];
|
|
1571
|
-
error( 'expr-expected-named-argument', loc, {},
|
|
1572
|
-
'Expected named parameters for the entity' );
|
|
1573
|
-
for (const a of dict)
|
|
1574
|
-
resolveExpr( a, exp, user );
|
|
1591
|
+
const symbols = type && type.enum;
|
|
1592
|
+
if (!symbols)
|
|
1575
1593
|
return;
|
|
1594
|
+
|
|
1595
|
+
// TODO: or warning if enum symbol but non-enum type?
|
|
1596
|
+
if (symbols[sym.id]) {
|
|
1597
|
+
setLink( sym, '_artifact', symbols[sym.id] );
|
|
1576
1598
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1599
|
+
else {
|
|
1600
|
+
// inferred enums can't be extended (yet): show underlying enum
|
|
1601
|
+
while (type.enum[$inferred])
|
|
1602
|
+
type = getOrigin( type );
|
|
1603
|
+
const err = message( 'ref-undefined-enum', [ sym.location, user ],
|
|
1604
|
+
{ id: sym.id, type } );
|
|
1605
|
+
if (options.newParser || options.newparser)
|
|
1606
|
+
attachAndEmitValidNames( err, symbols );
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function reportUnexpectedArgsAndFilter( step, expected, user, art, variant ) {
|
|
1611
|
+
if (step.args && art?.params) {
|
|
1612
|
+
const loc = [ step.args[$location] || step.location, user ];
|
|
1613
|
+
error( 'expr-unexpected-argument', loc, { '#': variant } );
|
|
1614
|
+
}
|
|
1615
|
+
if ((step.where || step.cardinality) && variant) {
|
|
1616
|
+
const location = combinedLocation( step.where, step.cardinality );
|
|
1617
|
+
// XSN TODO: filter$location including […]
|
|
1618
|
+
message( 'expr-unexpected-filter', [ location, user ], { '#': variant } );
|
|
1589
1619
|
}
|
|
1620
|
+
return variant && traverseExpr.SKIP;
|
|
1590
1621
|
}
|
|
1591
1622
|
}
|
|
1592
1623
|
|