@sap/cds-compiler 3.4.0 → 3.4.4

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/bin/cdsc.js +3 -4
  3. package/bin/cdshi.js +19 -6
  4. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  5. package/lib/api/main.js +6 -3
  6. package/lib/base/message-registry.js +61 -38
  7. package/lib/base/messages.js +7 -3
  8. package/lib/checks/.eslintrc.json +2 -0
  9. package/lib/compiler/.eslintrc.json +4 -1
  10. package/lib/compiler/assert-consistency.js +8 -6
  11. package/lib/compiler/builtins.js +13 -13
  12. package/lib/compiler/checks.js +50 -33
  13. package/lib/compiler/define.js +9 -6
  14. package/lib/compiler/extend.js +83 -55
  15. package/lib/compiler/finalize-parse-cdl.js +3 -3
  16. package/lib/compiler/populate.js +16 -5
  17. package/lib/compiler/propagator.js +22 -29
  18. package/lib/compiler/resolve.js +2 -15
  19. package/lib/compiler/shared.js +4 -4
  20. package/lib/compiler/utils.js +14 -0
  21. package/lib/edm/annotations/genericTranslation.js +68 -56
  22. package/lib/edm/csn2edm.js +214 -174
  23. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  24. package/lib/edm/edmInboundChecks.js +2 -2
  25. package/lib/edm/edmPreprocessor.js +1 -1
  26. package/lib/edm/edmUtils.js +3 -3
  27. package/lib/gen/Dictionary.json +176 -8
  28. package/lib/gen/language.checksum +1 -1
  29. package/lib/gen/language.interp +2 -1
  30. package/lib/gen/languageParser.js +4776 -4513
  31. package/lib/json/from-csn.js +21 -16
  32. package/lib/json/to-csn.js +37 -41
  33. package/lib/language/.eslintrc.json +4 -1
  34. package/lib/language/antlrParser.js +5 -2
  35. package/lib/language/docCommentParser.js +6 -6
  36. package/lib/language/errorStrategy.js +43 -23
  37. package/lib/language/genericAntlrParser.js +54 -95
  38. package/lib/language/language.g4 +92 -66
  39. package/lib/language/multiLineStringParser.js +2 -2
  40. package/lib/language/textUtils.js +2 -2
  41. package/lib/model/csnRefs.js +5 -0
  42. package/lib/modelCompare/compare.js +2 -2
  43. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  44. package/lib/modelCompare/utils/filter.js +99 -0
  45. package/lib/render/.eslintrc.json +1 -0
  46. package/lib/render/toCdl.js +96 -127
  47. package/lib/render/toHdbcds.js +38 -35
  48. package/lib/render/toSql.js +75 -161
  49. package/lib/render/utils/common.js +133 -83
  50. package/lib/render/utils/delta.js +227 -0
  51. package/lib/transform/db/.eslintrc.json +2 -0
  52. package/lib/transform/draft/.eslintrc.json +1 -35
  53. package/lib/transform/forOdataNew.js +33 -22
  54. package/lib/transform/forRelationalDB.js +1 -1
  55. package/lib/transform/localized.js +9 -8
  56. package/lib/transform/odata/typesExposure.js +26 -4
  57. package/lib/transform/transformUtilsNew.js +15 -8
  58. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  59. package/package.json +2 -3
  60. package/lib/modelCompare/filter.js +0 -83
@@ -33,6 +33,7 @@ function propagate( model ) {
33
33
  '@Analytics.visible': never,
34
34
  '@cds.autoexpose': onlyViaArtifact,
35
35
  '@cds.autoexposed': never, // in case people set it themselves
36
+ '@cds.external': never,
36
37
  '@cds.redirection.target': never,
37
38
  '@fiori.draft.enabled': onlyViaArtifact,
38
39
  '@': annotation, // always except in 'returns' and 'items'
@@ -84,42 +85,34 @@ function propagate( model ) {
84
85
  return;
85
86
  }
86
87
  // console.log('RUN:', art.name, art.elements ? Object.keys(art.elements) : 0)
88
+
87
89
  const chain = [];
88
- let target = art;
89
- let source = getOrigin( target );
90
- while (source && checkAndSetStatus( source )) {
91
- chain.push({ target, source });
92
- target = source;
93
- source = getOrigin( target );
94
- }
95
- if (source) { // the source has fully propagated properties
96
- chain.push({ target, source });
97
- }
98
- else if (target._main) { // source is element, which has not inherited props yet
99
- run( target._main ); // run on main artifact first
100
- }
90
+ let targets = [ art ];
91
+ while (targets.length) {
92
+ const news = [];
93
+ for (const target of targets) {
94
+ const origin = getOrigin( target );
95
+ if (origin) {
96
+ chain.push( { target, source: origin } );
97
+ if (checkAndSetStatus( origin ))
98
+ news.push( origin );
99
+ }
101
100
 
102
- // Even with a query source, go through `includes`. `source` is propagated first, i.e. wins.
103
- if (target.includes) {
104
- let targets = [ target ];
105
- while (targets.length) {
106
- const news = [];
107
- for (const t of targets) {
108
- for (const ref of t.includes || []) {
109
- const s = ref._artifact;
110
- if (!s) // ref error
111
- continue;
112
- chain.push( { target: t, source: s } );
113
- if (checkAndSetStatus( s ))
114
- news.push( s );
115
- }
101
+ for (const ref of target.includes || []) {
102
+ const include = ref._artifact;
103
+ if (!include)
104
+ continue;
105
+ chain.push( { target, source: include } );
106
+ if (checkAndSetStatus( include ))
107
+ news.push( include );
116
108
  }
117
- targets = news;
118
109
  }
110
+ targets = news;
119
111
  }
112
+
120
113
  chain.reverse().forEach( step );
121
114
  runMembers( art );
122
- // console.log('DONE:', art.name, art.elements ? Object.keys(art.elements) : 0)
115
+ // console.log('DONE:', art.name, art.elements ? Object.keys(art.elements) : 0);
123
116
  }
124
117
 
125
118
  function runMembers( art ) {
@@ -63,6 +63,7 @@ const {
63
63
  storeExtension,
64
64
  dependsOn,
65
65
  dependsOnSilent,
66
+ setExpandStatusAnnotate,
66
67
  testExpr,
67
68
  targetMaxNotOne,
68
69
  traverseQueryPost,
@@ -618,20 +619,6 @@ function resolve( model ) {
618
619
  }
619
620
  }
620
621
 
621
- function setExpandStatusAnnotate( elem, status ) {
622
- for (;;) {
623
- if (elem.$expand === status)
624
- return; // already set
625
- elem.$expand = status; // meaning: expanded, containing annos
626
- for (let line = elem.items; line; line = line.items)
627
- line.$expand = status; // to-csn just uses the innermost $expand
628
- if (!elem._main)
629
- return;
630
- elem = elem._parent;
631
- }
632
- }
633
-
634
-
635
622
  function expandParameters( action ) {
636
623
  // see also expandElements()
637
624
  if (!effectiveType( action ))
@@ -1291,7 +1278,7 @@ function resolve( model ) {
1291
1278
  }
1292
1279
  }
1293
1280
 
1294
- function resolveExpr( expr, expected, user, extDict, expandOrInline) {
1281
+ function resolveExpr( expr, expected, user, extDict, expandOrInline ) {
1295
1282
  // TODO: when we have rewritten the resolvePath functions,
1296
1283
  // define a traverseExpr() in ./utils.js
1297
1284
  // TODO: extra "expected" 'expand'/'inline' instead o param `expandOrInline`
@@ -528,7 +528,7 @@ function fns( model ) {
528
528
  * @param {object} typeArtifact
529
529
  * @param {CSN.Artifact} user
530
530
  */
531
- function resolveTypeArgumentsUnchecked(artifact, typeArtifact, user) {
531
+ function resolveTypeArgumentsUnchecked( artifact, typeArtifact, user ) {
532
532
  let args = artifact.$typeArgs || [];
533
533
  const parameters = typeArtifact.parameters || [];
534
534
 
@@ -873,7 +873,7 @@ function fns( model ) {
873
873
  * @param {object[]} valid
874
874
  * @param {object} [textParams]
875
875
  */
876
- function signalNotFound(msgId, location, valid, textParams ) {
876
+ function signalNotFound( msgId, location, valid, textParams ) {
877
877
  if (location.$notFound)
878
878
  return;
879
879
  location.$notFound = true;
@@ -891,7 +891,7 @@ function fns( model ) {
891
891
  * @param {object} art
892
892
  * @param {any} location
893
893
  */
894
- function signalMissingElementAccess(art, location) {
894
+ function signalMissingElementAccess( art, location ) {
895
895
  const err = message( 'ref-expected-element', location,
896
896
  { '#': 'magicVar', id: art.name.id } );
897
897
  // Mapping for better valid names: from -> $at.from
@@ -909,7 +909,7 @@ function fns( model ) {
909
909
  * @param {CompileMessage} msg CDS Compiler message
910
910
  * @param {...object} validDicts One ore more artifact dictionaries such as in `_block`.
911
911
  */
912
- function attachAndEmitValidNames(msg, ...validDicts) {
912
+ function attachAndEmitValidNames( msg, ...validDicts ) {
913
913
  if (!options.testMode && !options.attachValidNames)
914
914
  return;
915
915
 
@@ -400,6 +400,19 @@ function setExpandStatus( elem, status ) {
400
400
  }
401
401
  }
402
402
 
403
+ function setExpandStatusAnnotate( elem, status ) {
404
+ for (;;) {
405
+ if (elem.$expand === status)
406
+ return; // already set
407
+ elem.$expand = status; // meaning: expanded, containing annos
408
+ for (let line = elem.items; line; line = line.items)
409
+ line.$expand = status; // to-csn just uses the innermost $expand
410
+ if (!elem._main)
411
+ return;
412
+ elem = elem._parent;
413
+ }
414
+ }
415
+
403
416
  module.exports = {
404
417
  pushLink,
405
418
  annotationVal,
@@ -424,4 +437,5 @@ module.exports = {
424
437
  traverseQueryPost,
425
438
  traverseQueryExtra,
426
439
  setExpandStatus,
440
+ setExpandStatusAnnotate,
427
441
  };
@@ -6,6 +6,7 @@ const oDataDictionary = require('../../gen/Dictionary.json');
6
6
  const { forEachDefinition } = require('../../model/csnUtils');
7
7
  const { forEach } = require("../../utils/objectUtils");
8
8
 
9
+
9
10
  /*
10
11
  OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/master/vocabularies
11
12
  Aggregation (published)
@@ -171,6 +172,10 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
171
172
  vocabularyDefinitions[n].used = false;
172
173
  });
173
174
 
175
+ // These vocabularies are always added for the runtimes
176
+ vocabularyDefinitions['Common'].used = true;
177
+ vocabularyDefinitions['Core'].used = true;
178
+
174
179
  // provide functions for dictionary lookup
175
180
  // use closure to avoid making "dict" and "experimental" global variables
176
181
  let { getDictTerm, getDictType } = function(){
@@ -257,7 +262,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
257
262
  // this function is called in the translation code to issue an info/warning/error message
258
263
  // messages are reported via the severity function
259
264
  // context contains "semantic location"
260
- function message(severity, context, message) {
265
+ function message(severity, context, message, args={}) {
261
266
  let fullMessage = 'In annotation translation: ' + message;
262
267
  if (context) {
263
268
  let loc = 'target: ' + context.target + ', annotation: ' + context.term;
@@ -266,7 +271,7 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
266
271
  }
267
272
  fullMessage += ', ' + loc;
268
273
  }
269
- severity(null, null, `${fullMessage}`);
274
+ severity(null, null, args, `${fullMessage}`);
270
275
  }
271
276
 
272
277
  /*
@@ -1016,119 +1021,126 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
1016
1021
  // floating point type except Edm.Decimal -> Float
1017
1022
  // Edm.Decimal -> Decimal
1018
1023
  // integer tpye -> Int
1019
- function handleSimpleValue(val, dTypeName, context) {
1024
+ function handleSimpleValue(value, dTypeName, context) {
1020
1025
  // these types must be represented as "String" values in XML:
1021
1026
  const castToXmlString = [ 'Edm.PrimitiveType', 'Edm.Stream', 'Edm.Untyped' ];
1022
1027
  // caller already made sure that val is neither object nor array
1023
- dTypeName = resolveType(dTypeName);
1024
1028
 
1025
- if (isEnumType(dTypeName)) {
1026
- const type = getDictType(dTypeName);
1029
+ // check if type has allowed values
1030
+ const typeDef = getDictType(dTypeName);
1031
+ const Allowed = typeDef?.$Allowed;
1032
+
1033
+ let resolvedType = resolveTypeDefinition(dTypeName);
1034
+
1035
+ if (isEnumType(resolvedType)) {
1036
+ const type = getDictType(resolvedType);
1027
1037
  const expected = type.Members.map(m => `"#${m}"`).join(', ');
1028
- message(warning, context, `found non-enum value "${val}", expected ${expected} for ${dTypeName}`);
1038
+ message(warning, context, `found non-enum value "${value}", expected ${expected} for ${resolvedType}`);
1029
1039
  }
1030
1040
 
1031
1041
  let typeName = 'String';
1042
+ if(Allowed && !Allowed.Values[value])
1043
+ message(warning, context, 'Expected $(VALUE) to be one out of the allowed values $(VALUES)', { value, values: Object.keys(Allowed.Values) });
1032
1044
 
1033
- if (typeof val === 'string') {
1034
- if (dTypeName === 'Edm.Boolean') {
1045
+ if (typeof value === 'string') {
1046
+ if (resolvedType === 'Edm.Boolean') {
1035
1047
  typeName = 'Bool';
1036
- if (val !== 'true' && val !== 'false') {
1037
- message(warning, context, `found String, but expected type ${ dTypeName }`);
1048
+ if (value !== 'true' && value !== 'false') {
1049
+ message(warning, context, `found String, but expected type ${ resolvedType }`);
1038
1050
  }
1039
1051
  }
1040
- else if (dTypeName === 'Edm.Decimal') {
1052
+ else if (resolvedType === 'Edm.Decimal') {
1041
1053
  typeName = 'Decimal';
1042
- if (isNaN(Number(val)) || isNaN(parseFloat(val))) {
1043
- message(warning, context, `found non-numeric string, but expected type ${ dTypeName }`);
1054
+ if (isNaN(Number(value)) || isNaN(parseFloat(value))) {
1055
+ message(warning, context, `found non-numeric string, but expected type ${ resolvedType }`);
1044
1056
  }
1045
1057
  }
1046
- else if (dTypeName === 'Edm.Double' || dTypeName === 'Edm.Single') {
1058
+ else if (resolvedType === 'Edm.Double' || resolvedType === 'Edm.Single') {
1047
1059
  typeName = 'Float';
1048
- if (isNaN(Number(val)) || isNaN(parseFloat(val))) {
1049
- message(warning, context, `found non-numeric string, but expected type ${ dTypeName }`);
1060
+ if (isNaN(Number(value)) || isNaN(parseFloat(value))) {
1061
+ message(warning, context, `found non-numeric string, but expected type ${ resolvedType }`);
1050
1062
  }
1051
1063
  }
1052
- else if (isComplexType(dTypeName)) {
1053
- message(warning, context, `found String, but expected complex type ${ dTypeName }`);
1064
+ else if (isComplexType(resolvedType)) {
1065
+ message(warning, context, `found String, but expected complex type ${ resolvedType }`);
1054
1066
  }
1055
- else if (isEnumType(dTypeName)) {
1056
- message(warning, context, `found String, but expected enum type ${ dTypeName }`);
1067
+ else if (isEnumType(resolvedType)) {
1068
+ message(warning, context, `found String, but expected enum type ${ resolvedType }`);
1057
1069
  typeName = 'EnumMember';
1058
1070
  }
1059
- else if (dTypeName && dTypeName.startsWith('Edm.') && !castToXmlString.includes(dTypeName)) {
1071
+ else if (resolvedType && resolvedType.startsWith('Edm.') && !castToXmlString.includes(resolvedType)) {
1060
1072
  // this covers also all paths
1061
- typeName = dTypeName.substring(4);
1073
+ typeName = resolvedType.substring(4);
1062
1074
  }
1063
1075
  else {
1064
- if(dTypeName == undefined || castToXmlString.some(t => t === dTypeName))
1065
- dTypeName = 'Edm.String';
1076
+ if(resolvedType == undefined || castToXmlString.some(t => t === resolvedType))
1077
+ resolvedType = 'Edm.String';
1066
1078
  // TODO
1067
1079
  //message(warning, context, "type is not yet handled: found String, expected type: " + dTypeName);
1068
1080
  }
1069
1081
  }
1070
- else if (typeof val === 'boolean') {
1071
- if(dTypeName == undefined || dTypeName === 'Edm.Boolean' || dTypeName === 'Edm.PrimitiveType') {
1082
+ else if (typeof value === 'boolean') {
1083
+ if(resolvedType == undefined || resolvedType === 'Edm.Boolean' || resolvedType === 'Edm.PrimitiveType') {
1072
1084
  typeName = 'Bool';
1073
- dTypeName = 'Edm.Boolean';
1085
+ resolvedType = 'Edm.Boolean';
1074
1086
  }
1075
- if (dTypeName === 'Edm.Boolean') {
1076
- val = val ? 'true' : 'false';
1087
+ if (resolvedType === 'Edm.Boolean') {
1088
+ value = value ? 'true' : 'false';
1077
1089
  }
1078
- else if (dTypeName === 'Edm.String') {
1090
+ else if (resolvedType === 'Edm.String') {
1079
1091
  typeName = 'String';
1080
1092
  }
1081
1093
  else {
1082
- message(warning, context, `found Boolean, but expected type ${ dTypeName }`);
1094
+ message(warning, context, `found Boolean, but expected type ${ resolvedType }`);
1083
1095
  }
1084
1096
  }
1085
- else if (typeof val === 'number') {
1086
- if (isComplexType(dTypeName)) {
1087
- message(warning, context, `found number, but expected complex type ${ dTypeName }`);
1097
+ else if (typeof value === 'number') {
1098
+ if (isComplexType(resolvedType)) {
1099
+ message(warning, context, `found number, but expected complex type ${ resolvedType }`);
1088
1100
  }
1089
- else if (dTypeName === 'Edm.String') {
1101
+ else if (resolvedType === 'Edm.String') {
1090
1102
  typeName = 'String';
1091
1103
  }
1092
- else if (dTypeName === 'Edm.PropertyPath') {
1093
- message(warning, context, `found number, but expected type ${ dTypeName }`);
1104
+ else if (resolvedType === 'Edm.PropertyPath') {
1105
+ message(warning, context, `found number, but expected type ${ resolvedType }`);
1094
1106
  }
1095
- else if (dTypeName === 'Edm.Boolean') {
1096
- message(warning, context, `found number, but expected type ${ dTypeName }`);
1107
+ else if (resolvedType === 'Edm.Boolean') {
1108
+ message(warning, context, `found number, but expected type ${ resolvedType }`);
1097
1109
  }
1098
- else if (dTypeName === 'Edm.Decimal') {
1110
+ else if (resolvedType === 'Edm.Decimal') {
1099
1111
  typeName = 'Decimal';
1100
1112
  }
1101
- else if (dTypeName === 'Edm.Double') {
1113
+ else if (resolvedType === 'Edm.Double') {
1102
1114
  typeName = 'Float';
1103
1115
  }
1104
1116
  else {
1105
1117
  //typeName = Number.isInteger(val) ? 'Int' : 'Float';
1106
- if(Number.isInteger(val)) {
1118
+ if(Number.isInteger(value)) {
1107
1119
  typeName = 'Int';
1108
- if(dTypeName == undefined || dTypeName === 'Edm.PrimitiveType' || !dTypeName.startsWith('Edm.'))
1109
- dTypeName = 'Edm.Int64';
1120
+ if(resolvedType == undefined || resolvedType === 'Edm.PrimitiveType' || !resolvedType.startsWith('Edm.'))
1121
+ resolvedType = 'Edm.Int64';
1110
1122
  }
1111
1123
  else {
1112
1124
  typeName = 'Float';
1113
- if(dTypeName == undefined || dTypeName === 'Edm.PrimitiveType'|| !dTypeName.startsWith('Edm.'))
1114
- dTypeName = 'Edm.Double';
1125
+ if(resolvedType == undefined || resolvedType === 'Edm.PrimitiveType'|| !resolvedType.startsWith('Edm.'))
1126
+ resolvedType = 'Edm.Double';
1115
1127
  }
1116
1128
  }
1117
1129
  }
1118
- else if (val === null && dTypeName == null && typeName === 'String') {
1119
- dTypeName = 'Edm.String';
1130
+ else if (value === null && resolvedType == null && typeName === 'String') {
1131
+ resolvedType = 'Edm.String';
1120
1132
  }
1121
1133
  else {
1122
- message(warning, context, `expected simple value, but found value '${ val }' with type '${ typeof val }'`);
1134
+ message(warning, context, `expected simple value, but found value '${ value }' with type '${ typeof value }'`);
1123
1135
  }
1124
1136
 
1125
- if( ['Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(dTypeName) )
1126
- dTypeName = dTypeName.split('.')[1];
1137
+ if( ['Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(resolvedType) )
1138
+ resolvedType = resolvedType.split('.')[1];
1127
1139
 
1128
1140
  return {
1129
1141
  name : typeName,
1130
- jsonName: dTypeName,
1131
- value : val
1142
+ jsonName: resolvedType,
1143
+ value : value
1132
1144
  };
1133
1145
  }
1134
1146
 
@@ -1506,8 +1518,8 @@ function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined,
1506
1518
  // resolve "derived types"
1507
1519
  // -> if dTypeName is a TypeDefinition, replace by
1508
1520
  // underlying type
1509
- function resolveType(dTypeName) {
1510
- let type = getDictType(dTypeName);
1521
+ function resolveTypeDefinition(dTypeName) {
1522
+ const type = getDictType(dTypeName);
1511
1523
  if (type && type.UnderlyingType && type['$kind'] === 'TypeDefinition') {
1512
1524
  return type.UnderlyingType;
1513
1525
  }