@sap/cds-compiler 5.2.0 → 5.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 +39 -0
- package/bin/cdsc.js +5 -0
- package/bin/cdshi.js +8 -8
- package/doc/CHANGELOG_BETA.md +9 -4
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +25 -1
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +0 -1
- package/lib/compiler/assert-consistency.js +2 -2
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +25 -6
- package/lib/compiler/define.js +24 -28
- package/lib/compiler/extend.js +11 -13
- package/lib/compiler/generate.js +3 -3
- package/lib/compiler/populate.js +13 -7
- package/lib/compiler/propagator.js +2 -2
- package/lib/compiler/resolve.js +58 -60
- package/lib/compiler/shared.js +5 -5
- package/lib/compiler/tweak-assocs.js +247 -34
- package/lib/compiler/utils.js +40 -32
- package/lib/compiler/xpr-rewrite.js +44 -58
- package/lib/edm/annotations/genericTranslation.js +4 -4
- package/lib/edm/csn2edm.js +2 -2
- package/lib/edm/edm.js +46 -21
- package/lib/edm/edmInboundChecks.js +0 -1
- package/lib/edm/edmPreprocessor.js +40 -27
- package/lib/edm/edmUtils.js +1 -1
- package/lib/gen/BaseParser.js +180 -122
- package/lib/gen/CdlParser.js +2226 -2170
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3820 -3777
- package/lib/inspect/inspectPropagation.js +1 -1
- package/lib/json/from-csn.js +5 -3
- package/lib/json/to-csn.js +7 -10
- package/lib/language/antlrParser.js +38 -4
- package/lib/language/errorStrategy.js +1 -1
- package/lib/language/genericAntlrParser.js +4 -4
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/model/cloneCsn.js +22 -13
- package/lib/optionProcessor.js +7 -7
- package/lib/parsers/AstBuildingParser.js +155 -37
- package/lib/parsers/CdlGrammar.g4 +154 -81
- package/lib/parsers/Lexer.js +20 -10
- package/lib/render/toCdl.js +23 -18
- package/lib/transform/addTenantFields.js +4 -4
- package/lib/transform/db/rewriteCalculatedElements.js +11 -5
- package/lib/transform/db/transformExists.js +43 -18
- package/lib/transform/effective/main.js +1 -1
- package/lib/transform/forRelationalDB.js +8 -7
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +1 -1
- package/share/messages/redirected-to-complex.md +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 5.3.2 - 2024-10-08
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- to.sql|hdi|hdbcds|effective: Handle subexpressions in conjunction with exists predicate.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Version 5.3.0 - 2024-09-25
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- compiler:
|
|
22
|
+
- A warning is emitted if a string enum's values are longer than the specified length.
|
|
23
|
+
- ON-condition rewriting has been improved and now supports secondary associations.
|
|
24
|
+
- to.edm(x): Support optional action and function parameters in OData V4. The following rules apply:
|
|
25
|
+
+ A parameter declared `not null` without default value is mandatory.
|
|
26
|
+
+ A **function** parameter declared `null` without default value is mandatory.
|
|
27
|
+
+ An **action** parameter declared `null` without default value is optional as it is equivalent to
|
|
28
|
+
`@Core.OptionalParameter { DefaultValue: null }`.
|
|
29
|
+
+ A parameter with a default value is optional and the default value is rendered as
|
|
30
|
+
`@Core.OptionalParameter { DefaultValue: <value> }` regardless of its nullability.
|
|
31
|
+
+ `@Core.OptionalParameter: true` can be used to turn a mandatory parameter into an optional parameter
|
|
32
|
+
(especially function parameters) and to signal an unspecified default value in the API if the parameter
|
|
33
|
+
has no default clause.
|
|
34
|
+
+ `@Core.OptionalParameter: false` turns the creation of a `@Core.OptionalParameter: { Default: ... }`
|
|
35
|
+
annotation off.
|
|
36
|
+
+ A default clause or `@Core.OptionalParameter: <bool>` have no effect on an explicit binding parameter.
|
|
37
|
+
+ Mandatory and optional **action** parameters may appear in any order.
|
|
38
|
+
+ Optional **function** parameters must not be followed by mandatory parameters.
|
|
39
|
+
- to.edm: Forward `@OpenAPI {...}` into EDM Json with option `--odata-openapi-hints`.
|
|
40
|
+
- cdsc: Option `--transitive-localized-views` was added.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- CDL parser: Issue warning if annotation assignments have been written
|
|
45
|
+
at an invalid position inside a type expressions.
|
|
46
|
+
- CDL parser: Issue warning for arrayed parameter with default value.
|
|
47
|
+
- to.cdl: Arrayed parameters with default values were not rendered correctly.
|
|
48
|
+
|
|
10
49
|
## Version 5.2.0 - 2024-08-27
|
|
11
50
|
|
|
12
51
|
### Added
|
package/bin/cdsc.js
CHANGED
|
@@ -217,6 +217,11 @@ function cdsc_main() {
|
|
|
217
217
|
if (cmdLine.options.stdin)
|
|
218
218
|
cmdLine.options.fallbackParser ??= 'auto!';
|
|
219
219
|
|
|
220
|
+
if (cmdLine.options[cmdLine.command]?.transitiveLocalizedViews) {
|
|
221
|
+
cmdLine.options.fewerLocalizedViews = !cmdLine.options[cmdLine.command].transitiveLocalizedViews
|
|
222
|
+
delete cmdLine.options[cmdLine.command].transitiveLocalizedViews;
|
|
223
|
+
}
|
|
224
|
+
|
|
220
225
|
parseSeverityOptions(cmdLine);
|
|
221
226
|
|
|
222
227
|
// Do the work for the selected command
|
package/bin/cdshi.js
CHANGED
|
@@ -47,20 +47,20 @@ function highlight( err, buf ) {
|
|
|
47
47
|
console.error( 'ERROR:', err.toString() );
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const { tokens, lexer } = parser;
|
|
50
|
+
const { tokenStream } = compiler.parseX( buf, 'hi.cds', options );
|
|
51
|
+
const { tokens, lexer } = tokenStream;
|
|
53
52
|
if (!buf.length || !tokens || !tokens.length)
|
|
54
53
|
return;
|
|
54
|
+
|
|
55
55
|
const chars = [ ...buf ];
|
|
56
56
|
for (const tok of tokens) {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
if (tok.type === 'Comment') // but interpret DocComment!
|
|
58
|
+
continue;
|
|
59
|
+
const { location, start } = tok;
|
|
59
60
|
if (start < 0)
|
|
60
61
|
continue;
|
|
61
62
|
const stop = lexer.characterPos( location.endLine, location.endCol ) - 1;
|
|
62
|
-
const cat = tok.
|
|
63
|
-
// console.log(tok.location.toString(),tok.text,tok.parsed,stop > start)
|
|
63
|
+
const cat = tok.parsedAs;
|
|
64
64
|
if (!cat) {
|
|
65
65
|
if (stop > start) {
|
|
66
66
|
chars[start] = (cat !== 0 ? '\x0f' : '\x16'); // ^O / ^V (ERROR)
|
|
@@ -73,7 +73,7 @@ function highlight( err, buf ) {
|
|
|
73
73
|
else if (cat !== 'keyword' && cat !== 'token') {
|
|
74
74
|
if (cat !== 'ref' || chars[start] !== '$')
|
|
75
75
|
chars[start] = categoryChars[cat] || cat.charAt(0);
|
|
76
|
-
if (stop > start)
|
|
76
|
+
if (stop > start)
|
|
77
77
|
chars[start + 1] = '_';
|
|
78
78
|
}
|
|
79
79
|
}
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,11 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 5.3.0 - 2024-09-25
|
|
12
|
+
|
|
13
|
+
### Removed `optionalActionFunctionParameters`
|
|
14
|
+
|
|
15
|
+
It is now enabled by default.
|
|
11
16
|
|
|
12
17
|
## Version 5.0.0 - 2024-05-29
|
|
13
18
|
|
|
@@ -17,19 +22,19 @@ It is now enabled by default.
|
|
|
17
22
|
|
|
18
23
|
## Version 4.9.0 - 2024-04-25
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### Removed `odataAnnotationExpressions`
|
|
21
26
|
|
|
22
27
|
It is now enabled by default.
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
### Removed `odataPathsInAnnotationExpressions`
|
|
25
30
|
|
|
26
31
|
It is now enabled by default.
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
### Removed `annotationExpressions`
|
|
29
34
|
|
|
30
35
|
It is now enabled by default.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
### Added `temporalRawProjection`
|
|
33
38
|
|
|
34
39
|
Enables revocation of temporal where clause in projections of temporal entities if the
|
|
35
40
|
`@cds.valid { from, to }` annotations on the projection elements have falsy values.
|
package/lib/api/validate.js
CHANGED
|
@@ -118,6 +118,11 @@ const validators = {
|
|
|
118
118
|
expected: () => 'type boolean|number',
|
|
119
119
|
found: val => `type ${ typeof val }`,
|
|
120
120
|
},
|
|
121
|
+
withLocations: {
|
|
122
|
+
validate: val => typeof val === 'boolean' || val === 'withEndPosition',
|
|
123
|
+
expected: () => 'type boolean|"withEndPosition"',
|
|
124
|
+
found: val => `type ${ typeof val }`,
|
|
125
|
+
},
|
|
121
126
|
dictionaryPrototype: {
|
|
122
127
|
validate: () => true,
|
|
123
128
|
},
|
|
@@ -158,6 +158,7 @@ const centralMessages = {
|
|
|
158
158
|
'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
|
|
159
159
|
'syntax-missing-as': { severity: 'Error', configurableFor: true },
|
|
160
160
|
'syntax-missing-proj-semicolon': { severity: 'Warning', errorFor: [ 'v6' ] },
|
|
161
|
+
'syntax-unexpected-after': { severity: 'Warning', errorFor: [ 'v6' ] },
|
|
161
162
|
'syntax-unexpected-many-one': { severity: 'Error', configurableFor: true }, // TODO: remove `configurableFor` soon, latest v6
|
|
162
163
|
'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
|
|
163
164
|
'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
|
|
@@ -359,6 +360,11 @@ const centralMessageTexts = {
|
|
|
359
360
|
std: 'Annotations can\'t be used in a column with $(CODE)',
|
|
360
361
|
doc: 'Doc comments can\'t be used in a column with $(CODE)',
|
|
361
362
|
},
|
|
363
|
+
'syntax-unexpected-after': {
|
|
364
|
+
std: 'Unexpected $(KEYWORD) after annotation assignment',
|
|
365
|
+
many: 'Unexpected $(KEYWORD) after array type',
|
|
366
|
+
enum: 'Unexpected annotation assignment after enum type',
|
|
367
|
+
},
|
|
362
368
|
'syntax-unexpected-many-one': 'Replace $(CODE) with $(DELIMITED) to avoid an ambiguity with managed compositions of anonymous aspects',
|
|
363
369
|
'syntax-invalid-name': {
|
|
364
370
|
std: 'Identifier must consist of at least one character', // only via delimited id
|
|
@@ -704,6 +710,10 @@ const centralMessageTexts = {
|
|
|
704
710
|
'inline-expand': 'The ON-condition is not rewritten in nested projections - provide an explicit ON-condition',
|
|
705
711
|
'secondary': 'The ON-condition is not rewritten due to multiple associations in this path - provide an explicit ON-condition',
|
|
706
712
|
},
|
|
713
|
+
'rewrite-not-projected': {
|
|
714
|
+
std: 'Projected association $(NAME) uses non-projected element $(ELEMREF)',
|
|
715
|
+
element: 'Projected association $(NAME) uses non-projected element $(ELEMREF) of $(ART)',
|
|
716
|
+
},
|
|
707
717
|
'type-unsupported-rewrite': {
|
|
708
718
|
std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
|
|
709
719
|
'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported'
|
|
@@ -753,6 +763,13 @@ const centralMessageTexts = {
|
|
|
753
763
|
'onCond': 'Unexpected $(KEYWORD) on an association/composition with ON-condition; $(KEYWORD) requires exactly one foreign key',
|
|
754
764
|
'targetAspect': 'Unexpected $(KEYWORD) on composition of aspect'
|
|
755
765
|
},
|
|
766
|
+
'type-expecting-service-target': {
|
|
767
|
+
std: 'Expecting service entity $(TARGET)',
|
|
768
|
+
ref: 'Expecting service entity $(TARGET); its element $(ID) referred to at line $(LINE), column $(COL) is not from an element with the same name in the provided model target',
|
|
769
|
+
key: 'Expecting service entity $(TARGET); its key element $(ID) is not from a key element with the same name in the provided model target',
|
|
770
|
+
missing: 'Expecting service entity $(TARGET); it does not have the key element $(ID) of the provided model target',
|
|
771
|
+
order: 'Expecting service entity $(TARGET); its key elements are in a different order than those of the provided model target',
|
|
772
|
+
},
|
|
756
773
|
|
|
757
774
|
'anno-builtin': 'Builtin types should not be annotated nor extended. Use custom type instead',
|
|
758
775
|
'ext-undefined-def': 'Artifact $(ART) has not been found',
|
|
@@ -988,6 +1005,13 @@ const centralMessageTexts = {
|
|
|
988
1005
|
foreignKeys: 'Foreign key $(ID) does not result from the query',
|
|
989
1006
|
},
|
|
990
1007
|
|
|
1008
|
+
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
1009
|
+
'redirected-to-complex': {
|
|
1010
|
+
std: 'Redirection involves the complex view $(ART); add an explicit ON-condition/foreign keys to redirection',
|
|
1011
|
+
target: 'The redirected target $(ART) is a complex view; add an explicit ON-condition/foreign keys to redirection',
|
|
1012
|
+
targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD); add an explicit ON-condition/foreign keys to redirection',
|
|
1013
|
+
},
|
|
1014
|
+
|
|
991
1015
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
992
1016
|
|
|
993
1017
|
'ref-ambiguous': {
|
|
@@ -1118,7 +1142,7 @@ const centralMessageTexts = {
|
|
|
1118
1142
|
'odata-key-recursive': 'Unexpected recursive key $(NAME)',
|
|
1119
1143
|
'odata-key-uuid-default-anno': 'Expected element of type $(TYPE) to be annotated with $(ANNO) when used as primary key in $(ID)',
|
|
1120
1144
|
'odata-ignoring-param-default': {
|
|
1121
|
-
'std': 'Ignoring default value
|
|
1145
|
+
'std': 'Ignoring default value',
|
|
1122
1146
|
'xpr': 'Ignoring unexpected expression as default value',
|
|
1123
1147
|
'colitem': 'Ignoring unexpected default value for a structured or collection like parameter',
|
|
1124
1148
|
},
|
package/lib/base/messages.js
CHANGED
|
@@ -908,7 +908,7 @@ function transformArg( arg, r, args, texts ) {
|
|
|
908
908
|
return quoted( arg );
|
|
909
909
|
if (arg._artifact)
|
|
910
910
|
arg = arg._artifact;
|
|
911
|
-
|
|
911
|
+
while (arg._outer) // nested 'items'
|
|
912
912
|
arg = arg._outer;
|
|
913
913
|
if (args['#'] || args.member )
|
|
914
914
|
return shortArtName( arg );
|
package/lib/base/model.js
CHANGED
|
@@ -21,7 +21,6 @@ const availableBetaFlags = {
|
|
|
21
21
|
mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
|
|
22
22
|
enableUniversalCsn: true,
|
|
23
23
|
odataTerms: true,
|
|
24
|
-
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
25
24
|
effectiveCsn: true,
|
|
26
25
|
tenantVariable: true,
|
|
27
26
|
calcAssoc: true,
|
|
@@ -122,7 +122,6 @@ function assertConsistency( model, stage ) {
|
|
|
122
122
|
'dependencies', // for USING..FROM
|
|
123
123
|
'kind', // TODO: remove from parser
|
|
124
124
|
'meta',
|
|
125
|
-
'@sql_mapping', // TODO: it is time that a 'header' attribute replaces 'version'
|
|
126
125
|
'$withLocalized',
|
|
127
126
|
'$sources',
|
|
128
127
|
'tokenStream',
|
|
@@ -578,6 +577,7 @@ function assertConsistency( model, stage ) {
|
|
|
578
577
|
'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
|
|
579
578
|
'_outer', '_effectiveType', '$effectiveSeqNo', 'notNull', '_parent',
|
|
580
579
|
'_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
|
|
580
|
+
'localized', // really? see #13135
|
|
581
581
|
'$calcDepElement',
|
|
582
582
|
'$syntax', '_extensions',
|
|
583
583
|
'_status', '_redirected',
|
|
@@ -1041,7 +1041,7 @@ function assertConsistency( model, stage ) {
|
|
|
1041
1041
|
// TODO
|
|
1042
1042
|
// else if (spec.instanceOf && spec.instanceOf !== 'ignore' &&
|
|
1043
1043
|
// Object.getPrototypeOf( node ) !== spec.instanceOf.prototype)
|
|
1044
|
-
// eslint-disable-next-line max-len
|
|
1044
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1045
1045
|
// throw new InternalConsistencyError( `Expected object of class ${ spec.instanceOf.name } but found ${ found }${ at( [ null, parent ], prop, name ) }` );
|
|
1046
1046
|
}
|
|
1047
1047
|
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -219,7 +219,7 @@ const dateRegEx = /^(-?\d{4})-(\d{1,2})-(\d{1,2})$/;
|
|
|
219
219
|
// YYYY - MM - dd
|
|
220
220
|
const timeRegEx = /^T?(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?(?:Z|[+-]\d{2}(?::\d{2})?)?$/;
|
|
221
221
|
// T HH : mm : ss TZD
|
|
222
|
-
// eslint-disable-next-line max-len
|
|
222
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
223
223
|
const timestampRegEx = /^(-?\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(\.\d{1,7})?)?(?:Z|[+-]\d{2}(?::\d{2})?)?$/;
|
|
224
224
|
// YYYY - MM - dd T HH : mm : ss . fraction TZD
|
|
225
225
|
const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?[ \t]*$/i;
|
package/lib/compiler/checks.js
CHANGED
|
@@ -33,6 +33,8 @@ function check( model ) {
|
|
|
33
33
|
error, warning, info, message,
|
|
34
34
|
} = model.$messageFunctions;
|
|
35
35
|
|
|
36
|
+
const { getOrigin } = model.$functions;
|
|
37
|
+
|
|
36
38
|
checkSapCommonLocale( model );
|
|
37
39
|
checkSapCommonTextsAspects( model );
|
|
38
40
|
|
|
@@ -400,13 +402,18 @@ function check( model ) {
|
|
|
400
402
|
|
|
401
403
|
const expectedType = isNumeric ? 'number' : 'string';
|
|
402
404
|
|
|
405
|
+
let art = enumNode;
|
|
406
|
+
while (art?._effectiveType && art.length === undefined)
|
|
407
|
+
art = getOrigin( art );
|
|
408
|
+
const maxLength = art.length?.val ?? model.options.defaultStringLength;
|
|
409
|
+
|
|
403
410
|
// Do not check elements that don't have a value at all or are
|
|
404
411
|
// references to other enum elements. There are other checks for that.
|
|
405
412
|
const hasWrongType = element => element.value &&
|
|
406
413
|
(element.value.literal !== expectedType) &&
|
|
407
414
|
(element.value.literal !== 'enum');
|
|
408
415
|
|
|
409
|
-
for (const key
|
|
416
|
+
for (const key in enumNode.enum) {
|
|
410
417
|
const element = enumNode.enum[key];
|
|
411
418
|
if (hasWrongType( element )) {
|
|
412
419
|
const actualType = element.value.literal;
|
|
@@ -418,6 +425,18 @@ function check( model ) {
|
|
|
418
425
|
string: 'Expected string value for enum element $(NAME) but was $(PROP)',
|
|
419
426
|
} );
|
|
420
427
|
}
|
|
428
|
+
else if (isString && maxLength !== undefined) {
|
|
429
|
+
const value = element.value?.val ?? element.name.id;
|
|
430
|
+
if (value.length > maxLength) {
|
|
431
|
+
const loc = element.value?.location ?? element.name.location;
|
|
432
|
+
warning( 'def-invalid-value', [ loc, element ], {
|
|
433
|
+
'#': element.value ? 'std' : 'implicit', name: element.name.id, value: maxLength,
|
|
434
|
+
}, {
|
|
435
|
+
std: 'Enum value $(NAME) exceeds specified length $(VALUE)',
|
|
436
|
+
implicit: 'Implicit enum value $(NAME) exceeds specified length $(VALUE)',
|
|
437
|
+
} );
|
|
438
|
+
}
|
|
439
|
+
}
|
|
421
440
|
}
|
|
422
441
|
}
|
|
423
442
|
|
|
@@ -850,16 +869,16 @@ function check( model ) {
|
|
|
850
869
|
// One argument must be "$self" and the other an assoc
|
|
851
870
|
if (xpr.op.val === '=' && xpr.args.length === 2) {
|
|
852
871
|
// Tree-ish expression from the compiler (not augmented)
|
|
853
|
-
// eslint-disable-next-line max-len
|
|
872
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
854
873
|
return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[1] ) ||
|
|
855
|
-
// eslint-disable-next-line max-len
|
|
874
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
856
875
|
isAssociationOperand( xpr.args[1] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
|
|
857
876
|
}
|
|
858
877
|
else if (xpr.args.length === 3 && xpr.args[1].val === '=') {
|
|
859
878
|
// Tree-ish expression from the compiler (not augmented)
|
|
860
|
-
// eslint-disable-next-line max-len
|
|
879
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
861
880
|
return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[2] ) ||
|
|
862
|
-
// eslint-disable-next-line max-len
|
|
881
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
863
882
|
isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
|
|
864
883
|
}
|
|
865
884
|
|
|
@@ -1054,7 +1073,7 @@ function check( model ) {
|
|
|
1054
1073
|
value.literal !== 'timestamp' && value.literal !== 'string') {
|
|
1055
1074
|
// Hm, actually date and time cannot be mixed
|
|
1056
1075
|
warning( null, loc, { type, anno },
|
|
1057
|
-
// eslint-disable-next-line max-len
|
|
1076
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1058
1077
|
'A date/time value or a string is required for type $(TYPE) for annotation $(ANNO)' );
|
|
1059
1078
|
}
|
|
1060
1079
|
}
|
package/lib/compiler/define.js
CHANGED
|
@@ -174,7 +174,6 @@ function define( model ) {
|
|
|
174
174
|
shuffleArray,
|
|
175
175
|
initArtifact,
|
|
176
176
|
initMembers,
|
|
177
|
-
checkDefinitions, // TODO: remove
|
|
178
177
|
initSelectItems,
|
|
179
178
|
} );
|
|
180
179
|
|
|
@@ -188,13 +187,12 @@ function define( model ) {
|
|
|
188
187
|
function doDefine() {
|
|
189
188
|
if (options.deprecated &&
|
|
190
189
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
191
|
-
warning( 'api-deprecated-option', {},
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
} );
|
|
190
|
+
warning( 'api-deprecated-option', {}, {
|
|
191
|
+
prop: 'deprecated', '#': (options.beta ? 'beta' : 'std'),
|
|
192
|
+
}, {
|
|
193
|
+
std: 'With option $(PROP), recent features are disabled',
|
|
194
|
+
beta: 'With option $(PROP), beta features and other recent features are disabled',
|
|
195
|
+
} );
|
|
198
196
|
}
|
|
199
197
|
model.definitions = Object.create( null );
|
|
200
198
|
setLink( model, '_entities', [] ); // for entities with includes
|
|
@@ -317,7 +315,7 @@ function define( model ) {
|
|
|
317
315
|
if (artifacts[using])
|
|
318
316
|
continue;
|
|
319
317
|
// TODO: enable optional locations
|
|
320
|
-
const location = a.name.path
|
|
318
|
+
const location = a.name.path?.[0]?.location || a.location;
|
|
321
319
|
const absolute = prefix + using;
|
|
322
320
|
artifacts[using] = {
|
|
323
321
|
kind: 'using', // !, not namespace - we do not know artifact yet
|
|
@@ -350,7 +348,7 @@ function define( model ) {
|
|
|
350
348
|
return;
|
|
351
349
|
decl.extern.id = pathName( path );
|
|
352
350
|
if (!decl.name)
|
|
353
|
-
decl.name = { ...path
|
|
351
|
+
decl.name = { ...path.at(-1), $inferred: 'as' };
|
|
354
352
|
const name = decl.name.id;
|
|
355
353
|
// TODO: check name: no "."
|
|
356
354
|
const found = src.artifacts[name];
|
|
@@ -365,7 +363,7 @@ function define( model ) {
|
|
|
365
363
|
function addNamespace( namespace, src ) {
|
|
366
364
|
// create using for own namespace:
|
|
367
365
|
// TODO: should we really do that (in v6)? See also initNamespaceAndUsing().
|
|
368
|
-
const last = namespace.path
|
|
366
|
+
const last = namespace.path.at(-1);
|
|
369
367
|
const { id } = last;
|
|
370
368
|
if (src.artifacts[id] || last.id.includes( '.' ))
|
|
371
369
|
// not used as we have a definition/using with that name, or dotted last path id
|
|
@@ -437,8 +435,8 @@ function define( model ) {
|
|
|
437
435
|
} );
|
|
438
436
|
if (parent.kind !== 'extend')
|
|
439
437
|
return;
|
|
440
|
-
|
|
441
|
-
|
|
438
|
+
// TODO: sub queries? expand/inline?
|
|
439
|
+
parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
|
|
442
440
|
if (parent.scale && !parent.precision) {
|
|
443
441
|
// TODO: where could we store the location of the name?
|
|
444
442
|
error( 'syntax-missing-type-property', [ parent.scale.location ],
|
|
@@ -794,7 +792,7 @@ function define( model ) {
|
|
|
794
792
|
setMemberParent( table, query.name.id, query ); // sets _parent,_main
|
|
795
793
|
initSubQuery( table ); // init sub queries in ON
|
|
796
794
|
const aliases = Object.keys( table.$tableAliases || {} );
|
|
797
|
-
// Use first
|
|
795
|
+
// Use first table alias name on the right side of the join to name the
|
|
798
796
|
// (internal) query, should only be relevant for --raw-output, not for
|
|
799
797
|
// user messages or references - TODO: correct if join on left?
|
|
800
798
|
table.name.id = aliases[1] || aliases[0] || '<unknown>';
|
|
@@ -919,16 +917,15 @@ function define( model ) {
|
|
|
919
917
|
else {
|
|
920
918
|
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
921
919
|
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
922
|
-
error( 'syntax-duplicate-wildcard', [ col.location, null ],
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
} );
|
|
920
|
+
error( 'syntax-duplicate-wildcard', [ col.location, null ], {
|
|
921
|
+
'#': (wildcard.location.col ? 'col' : 'std'),
|
|
922
|
+
prop: '*',
|
|
923
|
+
line: wildcard.location.line,
|
|
924
|
+
col: wildcard.location.col,
|
|
925
|
+
}, {
|
|
926
|
+
std: 'You have provided a $(PROP) already in line $(LINE)',
|
|
927
|
+
col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
|
|
928
|
+
} );
|
|
932
929
|
// TODO: extra text variants for expand/inline? - probably not
|
|
933
930
|
col.val = null; // do not consider it for expandWildcard()
|
|
934
931
|
}
|
|
@@ -1073,7 +1070,7 @@ function define( model ) {
|
|
|
1073
1070
|
// We do not want to complain separately about all element properties:
|
|
1074
1071
|
error( 'ext-unexpected-element', [ e.location, construct ],
|
|
1075
1072
|
{ name: e.name.id, code: 'extend … with enum' },
|
|
1076
|
-
// eslint-disable-next-line max-len
|
|
1073
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1077
1074
|
'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
|
|
1078
1075
|
// Don't emit 'ext-expecting-enum' if this error is emitted.
|
|
1079
1076
|
return;
|
|
@@ -1094,7 +1091,7 @@ function define( model ) {
|
|
|
1094
1091
|
|
|
1095
1092
|
function initAnonymousAspect() {
|
|
1096
1093
|
// TODO: main?
|
|
1097
|
-
const inEntity = parent._main
|
|
1094
|
+
const inEntity = parent._main?.kind === 'entity';
|
|
1098
1095
|
// TODO: also allow indirectly (component in component in entity)?
|
|
1099
1096
|
setLink( targetAspect, '_outer', obj );
|
|
1100
1097
|
setLink( targetAspect, '_parent', parent._parent );
|
|
@@ -1205,7 +1202,7 @@ function define( model ) {
|
|
|
1205
1202
|
if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
|
|
1206
1203
|
const $self = main.$tableAliases?.$self ||
|
|
1207
1204
|
main.kind === 'extend' && { name: { id: '$self' } };
|
|
1208
|
-
// remark: an extend has no "table alias" `$self` (relevant for parse-cdl)
|
|
1205
|
+
// remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
|
|
1209
1206
|
setLink( type, '_artifact', $self );
|
|
1210
1207
|
setLink( path[0], '_artifact', $self );
|
|
1211
1208
|
}
|
|
@@ -1258,7 +1255,6 @@ function define( model ) {
|
|
|
1258
1255
|
return false;
|
|
1259
1256
|
}
|
|
1260
1257
|
}
|
|
1261
|
-
//
|
|
1262
1258
|
else if (parent.kind === 'action' || parent.kind === 'function') {
|
|
1263
1259
|
error( 'ext-unexpected-action', [ construct.location, construct ], { '#': parent.kind }, {
|
|
1264
1260
|
std: 'Actions and functions can\'t be extended, only annotated', // TODO: → ext-unsupported
|
package/lib/compiler/extend.js
CHANGED
|
@@ -543,7 +543,7 @@ function extend( model ) {
|
|
|
543
543
|
{
|
|
544
544
|
std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
|
|
545
545
|
array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
|
|
546
|
-
// eslint-disable-next-line max-len
|
|
546
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
547
547
|
struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
|
|
548
548
|
boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
|
|
549
549
|
null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
|
|
@@ -640,9 +640,9 @@ function extend( model ) {
|
|
|
640
640
|
}
|
|
641
641
|
else if (extVal < artVal + (scaleDiff || 0)) {
|
|
642
642
|
const number = artVal + (scaleDiff || 0);
|
|
643
|
-
error( 'ext-invalid-type-property', [ ext[prop].location, ext ],
|
|
644
|
-
|
|
645
|
-
|
|
643
|
+
error( 'ext-invalid-type-property', [ ext[prop].location, ext ], {
|
|
644
|
+
'#': (scaleDiff ? 'scale' : 'number'), prop, number, otherprop: 'scale',
|
|
645
|
+
} );
|
|
646
646
|
}
|
|
647
647
|
else {
|
|
648
648
|
art[prop] = ext[prop];
|
|
@@ -1170,14 +1170,11 @@ function extend( model ) {
|
|
|
1170
1170
|
// TODO: use shared functionality with notFound in resolver.js
|
|
1171
1171
|
const { location } = ext.name;
|
|
1172
1172
|
extMain.kind = ext.kind;
|
|
1173
|
-
const msg
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
|
|
1179
|
-
action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
|
|
1180
|
-
} );
|
|
1173
|
+
const msg = error( 'extend-undefined', [ location, artName ], { art: artName }, {
|
|
1174
|
+
std: 'Unknown $(ART) - nothing to extend',
|
|
1175
|
+
element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
|
|
1176
|
+
action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
|
|
1177
|
+
} );
|
|
1181
1178
|
attachAndEmitValidNames( msg, validDict );
|
|
1182
1179
|
}
|
|
1183
1180
|
}
|
|
@@ -1192,6 +1189,7 @@ function extend( model ) {
|
|
|
1192
1189
|
*
|
|
1193
1190
|
* @param {XSN.Definition} art
|
|
1194
1191
|
* @param {XSN.Artifact} target
|
|
1192
|
+
* @param {string[]} [justResolveCyclic]
|
|
1195
1193
|
* @returns {boolean}
|
|
1196
1194
|
*/
|
|
1197
1195
|
function canApplyIncludes( art, target, justResolveCyclic ) {
|
|
@@ -1272,7 +1270,7 @@ function extend( model ) {
|
|
|
1272
1270
|
*
|
|
1273
1271
|
* @param {XSN.Extension} ext
|
|
1274
1272
|
* @param {XSN.Artifact} art
|
|
1275
|
-
* @param {string} prop
|
|
1273
|
+
* @param {string} prop 'elements' or 'actions'
|
|
1276
1274
|
*/
|
|
1277
1275
|
function includeMembers( ext, art, prop ) {
|
|
1278
1276
|
// TODO two kind of messages:
|
package/lib/compiler/generate.js
CHANGED
|
@@ -103,7 +103,7 @@ function generate( model ) {
|
|
|
103
103
|
const lang = textsAspect.elements.language;
|
|
104
104
|
error( 'def-unexpected-element', [ lang.name.location, lang ],
|
|
105
105
|
{ option: 'addTextsLanguageAssoc', art: textsAspect, name: 'language' },
|
|
106
|
-
// eslint-disable-next-line max-len
|
|
106
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
107
107
|
'$(ART) is not used because option $(OPTION) conflicts with existing element $(NAME); remove either option or element' );
|
|
108
108
|
hasError = true;
|
|
109
109
|
}
|
|
@@ -244,7 +244,7 @@ function generate( model ) {
|
|
|
244
244
|
(fioriEnabled && art.elements.ID_texts)) {
|
|
245
245
|
// TODO if we have too much time: check all elements of texts entity for safety
|
|
246
246
|
warning( null, [ art.name.location, art ], { art: textsEntity },
|
|
247
|
-
// eslint-disable-next-line max-len
|
|
247
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
248
248
|
'Texts entity $(ART) can\'t be created as there is another definition with that name' );
|
|
249
249
|
info( null, [ textsEntity.name.location, textsEntity ], { art },
|
|
250
250
|
'Texts entity for $(ART) can\'t be created with this definition' );
|
|
@@ -639,7 +639,7 @@ function generate( model ) {
|
|
|
639
639
|
}
|
|
640
640
|
if (model.definitions[entityName]) {
|
|
641
641
|
error( null, [ location, elem ], { art: entityName },
|
|
642
|
-
// eslint-disable-next-line max-len
|
|
642
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
643
643
|
'Target entity $(ART) can\'t be created as there is another definition with this name' );
|
|
644
644
|
return false;
|
|
645
645
|
}
|
package/lib/compiler/populate.js
CHANGED
|
@@ -632,6 +632,12 @@ function populate( model ) {
|
|
|
632
632
|
}
|
|
633
633
|
}
|
|
634
634
|
|
|
635
|
+
/**
|
|
636
|
+
* Set type properties of specified elements on the inferred artifact, but only
|
|
637
|
+
* assign them if their values differs from the inferred ones (for better locations).
|
|
638
|
+
*
|
|
639
|
+
* @param {XSN.Artifact} art
|
|
640
|
+
*/
|
|
635
641
|
function setSpecifiedElementTypeProperties( art ) {
|
|
636
642
|
for (const prop in art.typeProps$) {
|
|
637
643
|
let o = art;
|
|
@@ -972,14 +978,14 @@ function populate( model ) {
|
|
|
972
978
|
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
973
979
|
info( 'wildcard-excluding-many', [ sibling.name.location, query ],
|
|
974
980
|
{ id, keyword: 'excluding' },
|
|
975
|
-
// eslint-disable-next-line max-len
|
|
981
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
976
982
|
'This select item replaces $(ID) from two or more sources. Add $(ID) to $(KEYWORD) to silence this message' );
|
|
977
983
|
}
|
|
978
984
|
else {
|
|
979
985
|
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
980
986
|
info( 'wildcard-excluding-one', [ sibling.name.location, query ],
|
|
981
987
|
{ id, alias: navElem._parent.name.id, keyword: 'excluding' },
|
|
982
|
-
// eslint-disable-next-line max-len
|
|
988
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
983
989
|
'This select item replaces $(ID) from table alias $(ALIAS). Add $(ID) to $(KEYWORD) to silence this message' );
|
|
984
990
|
}
|
|
985
991
|
}
|
|
@@ -1106,9 +1112,9 @@ function populate( model ) {
|
|
|
1106
1112
|
// art: definitionScope( target ), - TODO extra debug info in message
|
|
1107
1113
|
sorted_arts: exposed,
|
|
1108
1114
|
}, {
|
|
1109
|
-
// eslint-disable-next-line max-len
|
|
1115
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1110
1116
|
std: 'Replace target $(TARGET) by one of $(SORTED_ARTS); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
1111
|
-
// eslint-disable-next-line max-len
|
|
1117
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1112
1118
|
two: 'Replace target $(TARGET) by $(SORTED_ARTS) or $(SECOND); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
1113
1119
|
} );
|
|
1114
1120
|
// continuation semantics: no auto-redirection
|
|
@@ -1130,11 +1136,11 @@ function populate( model ) {
|
|
|
1130
1136
|
anno: 'cds.redirection.target',
|
|
1131
1137
|
sorted_arts: exposed,
|
|
1132
1138
|
}, {
|
|
1133
|
-
// eslint-disable-next-line max-len
|
|
1139
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1134
1140
|
std: 'Add $(ANNO) to one of $(SORTED_ARTS) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
1135
|
-
// eslint-disable-next-line max-len
|
|
1141
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1136
1142
|
two: 'Add $(ANNO) to either $(SORTED_ARTS) or $(SECOND) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
1137
|
-
// eslint-disable-next-line max-len
|
|
1143
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
1138
1144
|
justOne: 'Remove $(ANNO) from all but one of $(SORTED_ARTS) to have a unique redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
1139
1145
|
} );
|
|
1140
1146
|
}
|
|
@@ -74,7 +74,7 @@ function propagate( model ) {
|
|
|
74
74
|
|
|
75
75
|
const { options } = model;
|
|
76
76
|
const { rewriteAnnotationsRefs } = xprRewriteFns( model );
|
|
77
|
-
// eslint-disable-next-line max-len
|
|
77
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
78
78
|
const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
|
|
79
79
|
const { warning, throwWithError } = model.$messageFunctions;
|
|
80
80
|
|
|
@@ -368,7 +368,7 @@ function propagate( model ) {
|
|
|
368
368
|
const art = item && item._artifact;
|
|
369
369
|
if (art && art.virtual && art.virtual.val) {
|
|
370
370
|
warning( 'def-missing-virtual', [ item.location, elem ], { art, keyword: 'virtual' },
|
|
371
|
-
// eslint-disable-next-line max-len
|
|
371
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
372
372
|
'Prepend $(KEYWORD) to current select item - referred element $(ART) is virtual which is not inherited' );
|
|
373
373
|
return;
|
|
374
374
|
}
|