@sap/cds-compiler 3.1.0 → 3.3.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 +90 -3
- package/bin/cdsc.js +1 -1
- package/doc/CHANGELOG_BETA.md +18 -0
- package/lib/api/main.js +8 -13
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +2 -24
- package/lib/base/message-registry.js +43 -14
- package/lib/base/messages.js +20 -10
- package/lib/base/model.js +1 -1
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/arrayOfs.js +15 -7
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +48 -0
- package/lib/checks/defaultValues.js +2 -2
- package/lib/checks/elements.js +81 -6
- package/lib/checks/foreignKeys.js +12 -13
- package/lib/checks/invalidTarget.js +10 -11
- package/lib/checks/managedInType.js +21 -15
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +9 -9
- package/lib/checks/parameters.js +21 -0
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +26 -14
- package/lib/compiler/assert-consistency.js +13 -6
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +40 -33
- package/lib/compiler/define.js +50 -44
- package/lib/compiler/extend.js +303 -37
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +83 -62
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +61 -104
- package/lib/compiler/shared.js +16 -6
- package/lib/compiler/tweak-assocs.js +25 -12
- package/lib/compiler/utils.js +2 -2
- package/lib/edm/annotations/genericTranslation.js +15 -5
- package/lib/edm/csn2edm.js +10 -10
- package/lib/edm/edm.js +17 -9
- package/lib/edm/edmPreprocessor.js +82 -42
- package/lib/edm/edmUtils.js +18 -16
- package/lib/gen/Dictionary.json +14 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -2
- package/lib/gen/languageParser.js +4205 -4100
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +1 -1
- package/lib/json/from-csn.js +26 -19
- package/lib/json/to-csn.js +47 -5
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +29 -13
- package/lib/language/language.g4 +28 -8
- package/lib/main.d.ts +3 -6
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +74 -47
- package/lib/model/csnUtils.js +236 -218
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +31 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +5 -0
- package/lib/render/manageConstraints.js +2 -2
- package/lib/render/toCdl.js +31 -44
- package/lib/render/toHdbcds.js +7 -5
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +11 -5
- package/lib/render/utils/common.js +20 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/expansion.js +81 -37
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +1 -1
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
- package/lib/transform/localized.js +28 -19
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +1 -1
- package/lib/transform/transformUtilsNew.js +101 -39
- package/lib/transform/translateAssocsToJoins.js +5 -4
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/package.json +2 -2
- package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
- package/share/messages/check-proper-type-of.md +4 -4
- package/share/messages/check-proper-type.md +2 -2
- package/share/messages/duplicate-autoexposed.md +4 -4
- package/share/messages/extend-repeated-intralayer.md +4 -5
- package/share/messages/extend-unrelated-layer.md +4 -4
- package/share/messages/message-explanations.json +3 -1
- package/share/messages/redirected-to-ambiguous.md +7 -6
- package/share/messages/redirected-to-complex.md +63 -0
- package/share/messages/redirected-to-unrelated.md +6 -5
- package/share/messages/rewrite-not-supported.md +4 -4
- package/share/messages/syntax-expected-integer.md +3 -3
- package/share/messages/wildcard-excluding-one.md +37 -0
|
@@ -229,11 +229,13 @@ function assertConsistency( model, stage ) {
|
|
|
229
229
|
elements: { kind: true, inherits: 'definitions', also: [ 0 ] }, // 0 for cyclic expansions
|
|
230
230
|
// specified elements in query entities (TODO: introduce real "specified elements" instead):
|
|
231
231
|
elements$: { kind: true, enumerable: false, test: TODO },
|
|
232
|
+
enum$: { kind: true, enumerable: false, test: TODO },
|
|
232
233
|
actions: { kind: true, inherits: 'definitions' },
|
|
233
234
|
enum: { kind: true, inherits: 'definitions' },
|
|
234
235
|
foreignKeys: { kind: true, inherits: 'definitions' },
|
|
235
236
|
$keysNavigation: { kind: true, test: TODO },
|
|
236
237
|
params: { kind: true, inherits: 'definitions' },
|
|
238
|
+
_extendType: { kind: true, test: TODO },
|
|
237
239
|
mixin: { inherits: 'definitions' },
|
|
238
240
|
query: {
|
|
239
241
|
kind: true,
|
|
@@ -307,6 +309,7 @@ function assertConsistency( model, stage ) {
|
|
|
307
309
|
kind: 'element',
|
|
308
310
|
test: isDictionary( definition ), // definition since redef
|
|
309
311
|
requires: [ 'location', 'name' ],
|
|
312
|
+
optional: [ '$duplicates' ],
|
|
310
313
|
},
|
|
311
314
|
orderBy: { inherits: 'value', test: isArray( expression ) },
|
|
312
315
|
sort: { test: locationVal( isString ), enum: [ 'asc', 'desc' ] },
|
|
@@ -330,7 +333,7 @@ function assertConsistency( model, stage ) {
|
|
|
330
333
|
requires: [ 'location' ],
|
|
331
334
|
optional: [
|
|
332
335
|
'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
|
|
333
|
-
'scope', '_artifact', '$inferred', '$expand', '$tableAliases', '_$next',
|
|
336
|
+
'scope', '_artifact', '$inferred', '$expand', '$inCycle', '$tableAliases', '_$next',
|
|
334
337
|
'_effectiveType', // by propagation
|
|
335
338
|
],
|
|
336
339
|
},
|
|
@@ -504,8 +507,8 @@ function assertConsistency( model, stage ) {
|
|
|
504
507
|
optional: [
|
|
505
508
|
'enum',
|
|
506
509
|
'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
|
|
507
|
-
'_outer', '_effectiveType', 'notNull',
|
|
508
|
-
'_origin', '_block', '$inferred', '$expand', '_deps',
|
|
510
|
+
'_outer', '_effectiveType', 'notNull', '_parent',
|
|
511
|
+
'_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
|
|
509
512
|
'$syntax',
|
|
510
513
|
'_status', '_redirected',
|
|
511
514
|
...typeProperties,
|
|
@@ -602,11 +605,13 @@ function assertConsistency( model, stage ) {
|
|
|
602
605
|
kind: true,
|
|
603
606
|
test: isOneOf([
|
|
604
607
|
// Uppercase values are used in logic, lowercase value are "just for us", i.e.
|
|
605
|
-
// debugging or to add
|
|
608
|
+
// debugging or to add properties such as $generated in Universal CSN.
|
|
606
609
|
// However, that is no longer true. For example, `autoexposed` is used in populate.js
|
|
607
610
|
// as well.
|
|
608
611
|
'IMPLICIT',
|
|
609
612
|
'REDIRECTED',
|
|
613
|
+
'NULL', // from propagator
|
|
614
|
+
'prop', // from propagator
|
|
610
615
|
|
|
611
616
|
'$autoElement', // for magicVars: $user is automatically changed to $user.id
|
|
612
617
|
'$generated', // compiler generated annotations, e.g. @Core.Computed
|
|
@@ -618,8 +623,7 @@ function assertConsistency( model, stage ) {
|
|
|
618
623
|
'composition-entity',
|
|
619
624
|
'copy', // only used in rewriteCondition(): On-condition is copied
|
|
620
625
|
'duplicate-autoexposed', // just like `autoexposed`, but with `duplicate` error.
|
|
621
|
-
'
|
|
622
|
-
'expand-param', // expanded params (difference to expand-element only for debugging)
|
|
626
|
+
'expanded', // expanded elements, items, params
|
|
623
627
|
'include', // through includes, e.g. `entity E : F {}`
|
|
624
628
|
'keys',
|
|
625
629
|
'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
|
|
@@ -628,6 +632,8 @@ function assertConsistency( model, stage ) {
|
|
|
628
632
|
'none', // only used in ensureColumnName(): Used in object representing empty alias
|
|
629
633
|
'query', // inferred query properties, e.g. `key`
|
|
630
634
|
'rewrite', // on-conditions or FKeys are rewritten
|
|
635
|
+
'parent-origin', // annotation/property copied from parent that does not come through
|
|
636
|
+
// $origin and is not a direct annotation
|
|
631
637
|
]),
|
|
632
638
|
},
|
|
633
639
|
|
|
@@ -639,6 +645,7 @@ function assertConsistency( model, stage ) {
|
|
|
639
645
|
// See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
|
|
640
646
|
test: isOneOf([ 'origin', 'annotate', 'target' ]),
|
|
641
647
|
},
|
|
648
|
+
$inCycle: { kind: true, test: isBoolean },
|
|
642
649
|
|
|
643
650
|
$autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
|
|
644
651
|
$extra: { parser: true, test: TODO }, // for unexpected properties in CSN
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -19,6 +19,10 @@ const core = {
|
|
|
19
19
|
DecimalFloat: { category: 'decimal', deprecated: true },
|
|
20
20
|
Integer64: { category: 'integer' },
|
|
21
21
|
Integer: { category: 'integer' },
|
|
22
|
+
UInt8: { category: 'integer' },
|
|
23
|
+
Int16: { category: 'integer' },
|
|
24
|
+
Int32: { category: 'integer' },
|
|
25
|
+
Int64: { category: 'integer' },
|
|
22
26
|
Double: { category: 'decimal' },
|
|
23
27
|
Date: { category: 'dateTime' },
|
|
24
28
|
Time: { category: 'dateTime' },
|
|
@@ -54,6 +58,7 @@ const typeParameters = {
|
|
|
54
58
|
srid: [ 'number' ],
|
|
55
59
|
},
|
|
56
60
|
};
|
|
61
|
+
// a.k.a "typeProperties"
|
|
57
62
|
typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
|
|
58
63
|
|
|
59
64
|
// const hana = {
|
|
@@ -137,6 +142,7 @@ const specialFunctions = compileFunctions( {
|
|
|
137
142
|
separator: [ 'FLAG', 'IN', 'FROM', 'OCCURRENCE', 'GROUP' ],
|
|
138
143
|
},
|
|
139
144
|
],
|
|
145
|
+
SUBSTR_REGEXPR: 'SUBSTRING_REGEXPR',
|
|
140
146
|
} );
|
|
141
147
|
|
|
142
148
|
function compileFunctions( special ) {
|
|
@@ -364,6 +370,8 @@ function isInReservedNamespace(absolute) {
|
|
|
364
370
|
* check against their absolute names. Builtin types are "cds.<something>", i.e. they
|
|
365
371
|
* are directly in 'cds', but not for example in 'cds.foundation'.
|
|
366
372
|
*
|
|
373
|
+
* TODO: Handle `{ ref: [ "cds.Integer" ] }`
|
|
374
|
+
*
|
|
367
375
|
* @param {string} type
|
|
368
376
|
* @returns {boolean}
|
|
369
377
|
*/
|
package/lib/compiler/checks.js
CHANGED
|
@@ -52,7 +52,7 @@ function check( model ) { // = XSN
|
|
|
52
52
|
checkLocalizedSubElement(elem);
|
|
53
53
|
if (elem.key && elem.key.val) {
|
|
54
54
|
if (elem.virtual && elem.virtual.val)
|
|
55
|
-
error(null, [ elem.location, elem ], 'Element can\'t be virtual and key');
|
|
55
|
+
error(null, [ elem.location, elem ], {}, 'Element can\'t be virtual and key');
|
|
56
56
|
|
|
57
57
|
checkForUnmanagedAssociations( elem, elem.key );
|
|
58
58
|
}
|
|
@@ -73,7 +73,7 @@ function check( model ) { // = XSN
|
|
|
73
73
|
// on it!): The code below misses to consider CSN input.
|
|
74
74
|
if (construct.name.id && construct.name.id.indexOf('.') !== -1) {
|
|
75
75
|
// TODO: No, we should not forbid this
|
|
76
|
-
error(null, [ construct.name.location, construct ],
|
|
76
|
+
error(null, [ construct.name.location, construct ], {},
|
|
77
77
|
'The character \'.\' is not allowed in identifiers');
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -84,8 +84,8 @@ function check( model ) { // = XSN
|
|
|
84
84
|
if (elem.localized && elem.localized.val) {
|
|
85
85
|
const type = elem._effectiveType;
|
|
86
86
|
if (!type || !type.builtin || type.category !== 'string') {
|
|
87
|
-
warning(null, [ elem.
|
|
88
|
-
'Keyword
|
|
87
|
+
warning(null, [ elem.type?.location, elem ], { keyword: 'localized' },
|
|
88
|
+
'Keyword $(KEYWORD) should only be used in combination with string types');
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
// "key" keyword at localized element in SELECT list.
|
|
@@ -159,7 +159,7 @@ function check( model ) { // = XSN
|
|
|
159
159
|
// Special handling to print a more detailed error message.
|
|
160
160
|
// Other cases like `null` as enum value are handled in `checkEnumValueType()`
|
|
161
161
|
if (type === 'enum') {
|
|
162
|
-
warning('enum-value-ref', [ loc, enumNode ],
|
|
162
|
+
warning('enum-value-ref', [ loc, enumNode ], {},
|
|
163
163
|
'References to other values are not allowed as enum values');
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
@@ -387,7 +387,7 @@ function check( model ) { // = XSN
|
|
|
387
387
|
if (elem.cardinality[p[0]] && elem.cardinality[p[1]] &&
|
|
388
388
|
elem.cardinality[p[1]].literal === 'number' &&
|
|
389
389
|
elem.cardinality[p[0]].val > elem.cardinality[p[1]].val) {
|
|
390
|
-
error(null, [ elem.cardinality.location, elem ],
|
|
390
|
+
error(null, [ elem.cardinality.location, elem ], {},
|
|
391
391
|
`${ p[2] } minimum cardinality must not be greater than ${ p[2].toLowerCase() } maximum cardinality`);
|
|
392
392
|
}
|
|
393
393
|
});
|
|
@@ -422,7 +422,7 @@ function check( model ) { // = XSN
|
|
|
422
422
|
if (groupByEntry._artifact && groupByEntry._artifact._effectiveType &&
|
|
423
423
|
groupByEntry._artifact._effectiveType.on) {
|
|
424
424
|
// Unmanaged association - complain
|
|
425
|
-
error(null, [ groupByEntry.location, art ],
|
|
425
|
+
error(null, [ groupByEntry.location, art ], {},
|
|
426
426
|
'Unmanaged associations are not allowed in GROUP BY');
|
|
427
427
|
}
|
|
428
428
|
}
|
|
@@ -430,7 +430,7 @@ function check( model ) { // = XSN
|
|
|
430
430
|
if (orderByEntry._artifact && orderByEntry._artifact._effectiveType &&
|
|
431
431
|
orderByEntry._artifact._effectiveType.on) {
|
|
432
432
|
// Unmanaged association - complain
|
|
433
|
-
error(null, [ orderByEntry.location, art ],
|
|
433
|
+
error(null, [ orderByEntry.location, art ], {},
|
|
434
434
|
'Unmanaged associations are not allowed in ORDER BY');
|
|
435
435
|
}
|
|
436
436
|
}
|
|
@@ -459,7 +459,7 @@ function check( model ) { // = XSN
|
|
|
459
459
|
for (const k in elem.foreignKeys) {
|
|
460
460
|
const key = elem.foreignKeys[k].targetElement;
|
|
461
461
|
if (key && key._artifact && key._artifact.virtual && key._artifact.virtual.val) {
|
|
462
|
-
error(null, [ key.location, elem ],
|
|
462
|
+
error(null, [ key.location, elem ], {},
|
|
463
463
|
'Virtual elements can\'t be used as a foreign key for a managed association');
|
|
464
464
|
}
|
|
465
465
|
}
|
|
@@ -517,7 +517,7 @@ function check( model ) { // = XSN
|
|
|
517
517
|
if (argTarget.on) {
|
|
518
518
|
const same = path0._artifact === elem;
|
|
519
519
|
if (!same) {
|
|
520
|
-
error(null, [ path0.location, elem ],
|
|
520
|
+
error(null, [ path0.location, elem ], {},
|
|
521
521
|
'Unmanaged association condition can\'t follow another unmanaged association');
|
|
522
522
|
}
|
|
523
523
|
}
|
|
@@ -527,7 +527,7 @@ function check( model ) { // = XSN
|
|
|
527
527
|
if (op && op.val === 'xpr') // no check for xpr
|
|
528
528
|
return;
|
|
529
529
|
if (op && op.val !== '=')
|
|
530
|
-
error(null, [ op.location, elem ], '$self comparison is only allowed with \'=\'');
|
|
530
|
+
error(null, [ op.location, elem ], {}, '$self comparison is only allowed with \'=\'');
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
// A function like this could be part of the compiler
|
|
@@ -601,7 +601,7 @@ function check( model ) { // = XSN
|
|
|
601
601
|
// Check for illegal argument usage within the expression
|
|
602
602
|
for (const arg of args) {
|
|
603
603
|
if (isVirtualElement(arg))
|
|
604
|
-
error(null, arg.location, 'Virtual elements can\'t be used in an expression');
|
|
604
|
+
error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
|
|
605
605
|
|
|
606
606
|
|
|
607
607
|
// Recursively traverse the argument expression
|
|
@@ -624,20 +624,23 @@ function check( model ) { // = XSN
|
|
|
624
624
|
// Check for illegal argument usage within the expression
|
|
625
625
|
for (const arg of Array.isArray(xpr.args) && xpr.args || []) { // TODO named args?
|
|
626
626
|
if (isVirtualElement(arg))
|
|
627
|
-
error(null, arg.location, 'Virtual elements can\'t be used in an expression');
|
|
627
|
+
error(null, arg.location, {}, 'Virtual elements can\'t be used in an expression');
|
|
628
628
|
|
|
629
629
|
// Arg must not be an association and not $self
|
|
630
630
|
// Only if path is not approved exists path (that is non-query position)
|
|
631
631
|
if (arg.path && arg.$expected !== undefined) { // not 'approved-exists'
|
|
632
|
-
if (arg.$expected === 'exists')
|
|
633
|
-
error(null, arg.location,
|
|
632
|
+
if (arg.$expected === 'exists') {
|
|
633
|
+
error(null, arg.location, {},
|
|
634
|
+
'An association can\'t be used as a value in an expression');
|
|
635
|
+
}
|
|
634
636
|
}
|
|
635
637
|
else if (!allowAssocTail && isAssociationOperand(arg)) {
|
|
636
|
-
error(null, arg.location,
|
|
638
|
+
error(null, arg.location, {},
|
|
639
|
+
'An association can\'t be used as a value in an expression');
|
|
637
640
|
}
|
|
638
641
|
|
|
639
642
|
if (isDollarSelfOrProjectionOperand(arg))
|
|
640
|
-
error(null, arg.location, `"${ arg.path[0].id }" can only be used as a value in a comparison to an association`);
|
|
643
|
+
error(null, arg.location, {}, `"${ arg.path[0].id }" can only be used as a value in a comparison to an association`);
|
|
641
644
|
|
|
642
645
|
// Recursively traverse the argument expression
|
|
643
646
|
checkTreeLikeExpression(arg, allowAssocTail);
|
|
@@ -736,7 +739,7 @@ function check( model ) { // = XSN
|
|
|
736
739
|
|
|
737
740
|
// Element must exist in annotation
|
|
738
741
|
if (!elementDecl) {
|
|
739
|
-
warning(null, anno.location || anno.name.location, `Element "${ anno.name.path.map(step => step.id).join('.') }" not found for annotation "${ annoDecl.name.absolute }"`);
|
|
742
|
+
warning(null, anno.location || anno.name.location, {}, `Element "${ anno.name.path.map(step => step.id).join('.') }" not found for annotation "${ annoDecl.name.absolute }"`);
|
|
740
743
|
return;
|
|
741
744
|
}
|
|
742
745
|
|
|
@@ -747,10 +750,14 @@ function check( model ) { // = XSN
|
|
|
747
750
|
|
|
748
751
|
// Must have literal or path unless it is a boolean
|
|
749
752
|
if (!anno.literal && !anno.path && getFinalTypeNameOf(elementDecl) !== 'cds.Boolean') {
|
|
750
|
-
if (elementDecl.type && elementDecl.type._artifact.name.absolute)
|
|
751
|
-
warning(null, anno.location || anno.name.location,
|
|
752
|
-
|
|
753
|
-
|
|
753
|
+
if (elementDecl.type && elementDecl.type._artifact.name.absolute) {
|
|
754
|
+
warning(null, anno.location || anno.name.location, {},
|
|
755
|
+
`Expecting a value of type "${ elementDecl.type._artifact.name.absolute }" for the annotation`);
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
warning(null, anno.location || anno.name.location, {},
|
|
759
|
+
'Expecting a value for the annotation');
|
|
760
|
+
}
|
|
754
761
|
|
|
755
762
|
return;
|
|
756
763
|
}
|
|
@@ -775,7 +782,7 @@ function check( model ) { // = XSN
|
|
|
775
782
|
if (elementDecl._effectiveType.items) {
|
|
776
783
|
// Make sure we have an array value
|
|
777
784
|
if (value.literal !== 'array') {
|
|
778
|
-
warning(null, loc, 'An array value is required here');
|
|
785
|
+
warning(null, loc, {}, 'An array value is required here');
|
|
779
786
|
return;
|
|
780
787
|
}
|
|
781
788
|
// Check each element
|
|
@@ -788,7 +795,7 @@ function check( model ) { // = XSN
|
|
|
788
795
|
// Struct expected (can only happen within arrays)?
|
|
789
796
|
if (elementDecl._effectiveType.elements) {
|
|
790
797
|
if (value.literal !== 'struct') {
|
|
791
|
-
warning(null, loc, 'A struct value is required here');
|
|
798
|
+
warning(null, loc, {}, 'A struct value is required here');
|
|
792
799
|
return;
|
|
793
800
|
}
|
|
794
801
|
// FIXME: Should check each element
|
|
@@ -800,28 +807,28 @@ function check( model ) { // = XSN
|
|
|
800
807
|
if (builtins.isStringTypeName(type)) {
|
|
801
808
|
if (value.literal !== 'string' && value.literal !== 'enum' &&
|
|
802
809
|
!elementDecl._effectiveType.enum)
|
|
803
|
-
warning(null, loc, `A string value is required for type "${ type }"`);
|
|
810
|
+
warning(null, loc, {}, `A string value is required for type "${ type }"`);
|
|
804
811
|
}
|
|
805
812
|
else if (builtins.isBinaryTypeName(type)) {
|
|
806
813
|
if (value.literal !== 'string' && value.literal !== 'x')
|
|
807
|
-
warning(null, loc, `A hexadecimal string value is required for type "${ type }"`);
|
|
814
|
+
warning(null, loc, {}, `A hexadecimal string value is required for type "${ type }"`);
|
|
808
815
|
}
|
|
809
816
|
else if (builtins.isNumericTypeName(type)) {
|
|
810
817
|
if (value.literal !== 'number' && value.literal !== 'enum' &&
|
|
811
818
|
!elementDecl._effectiveType.enum)
|
|
812
|
-
warning(null, loc, `A numerical value is required for type "${ type }"`);
|
|
819
|
+
warning(null, loc, {}, `A numerical value is required for type "${ type }"`);
|
|
813
820
|
}
|
|
814
821
|
else if (builtins.isDateOrTimeTypeName(type)) {
|
|
815
822
|
if (value.literal !== 'date' && value.literal !== 'time' &&
|
|
816
823
|
value.literal !== 'timestamp' && value.literal !== 'string')
|
|
817
|
-
warning(null, loc, `A date/time value or a string is required for type "${ type }"`);
|
|
824
|
+
warning(null, loc, {}, `A date/time value or a string is required for type "${ type }"`);
|
|
818
825
|
}
|
|
819
826
|
else if (builtins.isBooleanTypeName(type)) {
|
|
820
827
|
if (value.literal && value.literal !== 'boolean')
|
|
821
|
-
warning(null, loc, `A boolean value is required for type "${ type }"`);
|
|
828
|
+
warning(null, loc, {}, `A boolean value is required for type "${ type }"`);
|
|
822
829
|
}
|
|
823
830
|
else if (builtins.isRelationTypeName(type) || builtins.isGeoTypeName(type)) {
|
|
824
|
-
warning(null, loc, `Type "${ type }" can't be assigned a value`);
|
|
831
|
+
warning(null, loc, {}, `Type "${ type }" can't be assigned a value`);
|
|
825
832
|
}
|
|
826
833
|
else {
|
|
827
834
|
throw new Error(`Unknown primitive type name: ${ type }`);
|
|
@@ -834,19 +841,19 @@ function check( model ) { // = XSN
|
|
|
834
841
|
// Enum symbol provided and expected
|
|
835
842
|
if (!expectedEnum[value.sym.id]) {
|
|
836
843
|
// .. but no such constant
|
|
837
|
-
warning(null, loc, `Enum symbol "#${ value.sym.id }" not found in enum`);
|
|
844
|
+
warning(null, loc, {}, `Enum symbol "#${ value.sym.id }" not found in enum`);
|
|
838
845
|
}
|
|
839
846
|
}
|
|
840
847
|
else {
|
|
841
848
|
// Enum symbol provided but not expected
|
|
842
|
-
warning(null, loc, `
|
|
849
|
+
warning(null, loc, {}, `Can't use enum symbol "#${ value.sym.id }" for non-enum type "${ type }"`);
|
|
843
850
|
}
|
|
844
851
|
}
|
|
845
852
|
else if (expectedEnum) {
|
|
846
853
|
// Enum symbol not provided but expected
|
|
847
854
|
if (!Object.keys(expectedEnum).some(symbol => expectedEnum[symbol].value.val === value.val)) {
|
|
848
855
|
// ... and none of the valid enum symbols matches the value
|
|
849
|
-
warning(null, loc, 'An enum value is required here');
|
|
856
|
+
warning(null, loc, {}, 'An enum value is required here');
|
|
850
857
|
}
|
|
851
858
|
}
|
|
852
859
|
}
|
package/lib/compiler/define.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// Definitions inside a context are not listed here (as opposed to
|
|
8
8
|
// `definitions`, see below), but inside the property `artifacts` of that context.
|
|
9
9
|
|
|
10
|
-
// The define phase (function 'define' below) enriches a dictionary of
|
|
10
|
+
// The 'define' phase (function 'define' below) enriches a dictionary of
|
|
11
11
|
// (file names to) AST-like CSNs and restructure them a little bit, the result
|
|
12
12
|
// is called "augmented CSN":
|
|
13
13
|
// { sources: <dictionary of ASTs>, definitions: <dictionary of artifact defs> }
|
|
@@ -194,7 +194,7 @@ function define( model ) {
|
|
|
194
194
|
dictForEach( model.definitions, initArtifact );
|
|
195
195
|
dictForEach( model.vocabularies, initVocabulary );
|
|
196
196
|
|
|
197
|
-
mergeI18nBlocks(
|
|
197
|
+
mergeI18nBlocks();
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
// Phase 1: ----------------------------------------------------------------
|
|
@@ -213,9 +213,8 @@ function define( model ) {
|
|
|
213
213
|
let namespace = src.namespace && src.namespace.path;
|
|
214
214
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
215
215
|
if (isInReservedNamespace(prefix)) {
|
|
216
|
-
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
217
|
-
|
|
218
|
-
'The namespace "cds" is reserved for CDS builtins' );
|
|
216
|
+
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
|
|
217
|
+
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
219
218
|
namespace = null;
|
|
220
219
|
}
|
|
221
220
|
if (src.$frontend !== 'json') { // CDL input
|
|
@@ -249,9 +248,8 @@ function define( model ) {
|
|
|
249
248
|
const { absolute } = art.name;
|
|
250
249
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
251
250
|
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
252
|
-
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
253
|
-
|
|
254
|
-
'The namespace "cds" is reserved for CDS builtins' );
|
|
251
|
+
error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
|
|
252
|
+
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
255
253
|
const builtin = model.definitions[absolute];
|
|
256
254
|
if (builtin && builtin.builtin) // if already a builtin...
|
|
257
255
|
return;
|
|
@@ -259,8 +257,6 @@ function define( model ) {
|
|
|
259
257
|
}
|
|
260
258
|
else if (art.query && (absolute === 'localized' || absolute.startsWith( 'localized.' ))) {
|
|
261
259
|
// Due to recompilation, we don't emit this info message for JSON frontend.
|
|
262
|
-
// TODO: generalize this for $generated (definitions starting with
|
|
263
|
-
// "localized" just have `$generated: true` as default)
|
|
264
260
|
if (block.$frontend !== 'json') {
|
|
265
261
|
info( 'ignored-localized-definition', [ art.name.location, art ], {},
|
|
266
262
|
'This definition in the namespace "localized" is ignored' );
|
|
@@ -419,6 +415,8 @@ function define( model ) {
|
|
|
419
415
|
}
|
|
420
416
|
|
|
421
417
|
function initNamespaceAndUsing( src ) {
|
|
418
|
+
if (src.$frontend && src.$frontend !== 'cdl')
|
|
419
|
+
return;
|
|
422
420
|
if (src.namespace) {
|
|
423
421
|
const decl = src.namespace;
|
|
424
422
|
const { path } = decl;
|
|
@@ -478,12 +476,16 @@ function define( model ) {
|
|
|
478
476
|
if (!setLink( art, '_leadingQuery', initQueryExpression( art.query, art ) ) )
|
|
479
477
|
return; // null or undefined in case of parse error
|
|
480
478
|
setLink( art._leadingQuery, '_$next', art );
|
|
481
|
-
// the following
|
|
479
|
+
// the following may be removed soon if we have:
|
|
482
480
|
// view elements as proxies to elements of leading query
|
|
483
481
|
if (art.elements) { // specified element via compilation of client-style CSN
|
|
484
482
|
setLink( art, 'elements$', art.elements );
|
|
485
483
|
delete art.elements;
|
|
486
484
|
}
|
|
485
|
+
if (art.enum) { // specified enum via compilation of client-style CSN
|
|
486
|
+
setLink( art, 'enum$', art.enum );
|
|
487
|
+
delete art.enum;
|
|
488
|
+
}
|
|
487
489
|
}
|
|
488
490
|
|
|
489
491
|
function initVocabulary( art ) {
|
|
@@ -607,13 +609,13 @@ function define( model ) {
|
|
|
607
609
|
defineAnnotations( col, col, parent._block );
|
|
608
610
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
609
611
|
if (col.doc)
|
|
610
|
-
warning( 'syntax-anno
|
|
612
|
+
warning( 'syntax-ignoring-anno', [ col.doc.location, col ], { '#': 'doc' } );
|
|
611
613
|
|
|
612
614
|
// col.$annotations no available for CSN input, have to search.
|
|
613
615
|
// Warning about first annotation should be enough to avoid spam.
|
|
614
616
|
const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
|
|
615
617
|
if (firstAnno)
|
|
616
|
-
warning( 'syntax-anno
|
|
618
|
+
warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ] );
|
|
617
619
|
}
|
|
618
620
|
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
619
621
|
if (columns)
|
|
@@ -658,7 +660,7 @@ function define( model ) {
|
|
|
658
660
|
|
|
659
661
|
/**
|
|
660
662
|
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
661
|
-
* since we will have a top-level subquery after exists-processing in the
|
|
663
|
+
* since we will have a top-level subquery after exists-processing in the forRelationalDB.
|
|
662
664
|
*
|
|
663
665
|
* Recursively drill down into:
|
|
664
666
|
* - the .path
|
|
@@ -886,17 +888,21 @@ function define( model ) {
|
|
|
886
888
|
/**
|
|
887
889
|
* Set property `_parent` for all elements in `parent` to `parent` and do so
|
|
888
890
|
* recursively for all sub elements.
|
|
891
|
+
*
|
|
892
|
+
* If not for extensions: construct === parent
|
|
889
893
|
*/
|
|
890
|
-
// If not for extensions: construct === parent
|
|
891
894
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
892
895
|
// TODO: split extend from init
|
|
893
896
|
const isQueryExtension = kindProperties[construct.kind].isExtension &&
|
|
894
897
|
(parent._main || parent).query;
|
|
895
898
|
let obj = construct;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
setLink(
|
|
899
|
+
let { items } = obj;
|
|
900
|
+
while (items) {
|
|
901
|
+
setLink( items, '_outer', obj );
|
|
902
|
+
setLink( items, '_parent', obj._parent );
|
|
903
|
+
setLink( items, '_block', block );
|
|
904
|
+
obj = items;
|
|
905
|
+
items = obj.items;
|
|
900
906
|
}
|
|
901
907
|
if (obj.target && targetIsTargetAspect( obj )) {
|
|
902
908
|
obj.targetAspect = obj.target;
|
|
@@ -913,7 +919,7 @@ function define( model ) {
|
|
|
913
919
|
if (obj.on && !obj.target) {
|
|
914
920
|
error( 'unexpected-on-for-composition', [ obj.on.location, construct ],
|
|
915
921
|
{},
|
|
916
|
-
'A managed aspect composition can\'t have a specified ON
|
|
922
|
+
'A managed aspect composition can\'t have a specified ON-condition' );
|
|
917
923
|
delete obj.on; // continuation semantics: not specified
|
|
918
924
|
}
|
|
919
925
|
if (targetAspect.elements) {
|
|
@@ -1017,7 +1023,7 @@ function define( model ) {
|
|
|
1017
1023
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
1018
1024
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1019
1025
|
checkRedefinition( elem );
|
|
1020
|
-
if (elem.kind === 'annotate')
|
|
1026
|
+
if (elem.kind === 'annotate' || elem.kind === 'extend')
|
|
1021
1027
|
checkAnnotate( elem, elem );
|
|
1022
1028
|
defineAnnotations( elem, elem, bl );
|
|
1023
1029
|
initMembers( elem, elem, bl, initExtensions );
|
|
@@ -1039,7 +1045,7 @@ function define( model ) {
|
|
|
1039
1045
|
std: 'Elements can\'t have a value',
|
|
1040
1046
|
entity: 'Entity elements can\'t have a value',
|
|
1041
1047
|
type: 'Type elements can\'t have a value',
|
|
1042
|
-
extend: '
|
|
1048
|
+
extend: 'Can\'t extend type/entity elements with values',
|
|
1043
1049
|
});
|
|
1044
1050
|
return;
|
|
1045
1051
|
}
|
|
@@ -1072,8 +1078,11 @@ function define( model ) {
|
|
|
1072
1078
|
'Actions and functions only exist top-level and for entities' );
|
|
1073
1079
|
}
|
|
1074
1080
|
else if (parent.kind === 'action' || parent.kind === 'function') {
|
|
1075
|
-
error( 'extend-action', [ construct.location, construct ], {},
|
|
1076
|
-
|
|
1081
|
+
error( 'extend-action', [ construct.location, construct ], { '#': parent.kind }, {
|
|
1082
|
+
std: 'Actions and functions can\'t be extended, only annotated',
|
|
1083
|
+
action: 'Actions can\'t be extended, only annotated',
|
|
1084
|
+
function: 'Functions can\'t be extended, only annotated',
|
|
1085
|
+
} );
|
|
1077
1086
|
}
|
|
1078
1087
|
else if (prop === 'params') {
|
|
1079
1088
|
if (!feature) {
|
|
@@ -1105,29 +1114,27 @@ function define( model ) {
|
|
|
1105
1114
|
}
|
|
1106
1115
|
return construct === parent;
|
|
1107
1116
|
}
|
|
1108
|
-
}
|
|
1109
1117
|
|
|
1110
|
-
/**
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
.filter(name => !!model.sources[name].i18n)
|
|
1118
|
-
.sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
|
|
1118
|
+
/**
|
|
1119
|
+
* Merge (optional) translations into the XSN model.
|
|
1120
|
+
*/
|
|
1121
|
+
function mergeI18nBlocks() {
|
|
1122
|
+
const sortedSources = Object.keys(model.sources)
|
|
1123
|
+
.filter(name => !!model.sources[name].i18n)
|
|
1124
|
+
.sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
|
|
1119
1125
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1126
|
+
if (sortedSources.length === 0)
|
|
1127
|
+
return;
|
|
1122
1128
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1129
|
+
if (!model.i18n)
|
|
1130
|
+
model.i18n = Object.create( null );
|
|
1125
1131
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1132
|
+
for (const name of sortedSources)
|
|
1133
|
+
initI18nFromSource( model.sources[name] );
|
|
1134
|
+
}
|
|
1128
1135
|
|
|
1129
1136
|
/**
|
|
1130
|
-
* Add the source's translations to the model. Warns if the
|
|
1137
|
+
* Add the source's translations to the model. Warns if the source's translations
|
|
1131
1138
|
* do not match the ones from previous sources.
|
|
1132
1139
|
*
|
|
1133
1140
|
* @param {XSN.SourceAst} src
|
|
@@ -1144,9 +1151,8 @@ function mergeI18nBlocks( model ) {
|
|
|
1144
1151
|
model.i18n[langKey][textKey] = sourceVal;
|
|
1145
1152
|
}
|
|
1146
1153
|
else if (modelVal.val !== sourceVal.val) {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
{ prop: textKey, otherprop: langKey } );
|
|
1154
|
+
warning( 'i18n-different-value', sourceVal.location,
|
|
1155
|
+
{ prop: textKey, otherprop: langKey } );
|
|
1150
1156
|
}
|
|
1151
1157
|
}
|
|
1152
1158
|
}
|