@sap/cds-compiler 2.12.0 → 2.15.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { isBetaEnabled } = require('../base/model');
4
-
5
3
  // Only to be used with validator.js - a correct this value needs to be provided!
6
4
 
7
5
  /**
@@ -14,20 +12,18 @@ const { isBetaEnabled } = require('../base/model');
14
12
  * @returns {void}
15
13
  */
16
14
  function checkSqlAnnotationOnElement(member, memberName, prop, path) {
17
- if (isBetaEnabled(this.options, 'sqlSnippets')) {
18
- if (member['@sql.replace'])
19
- this.error(null, path, { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
20
- if (member['@sql.prepend'])
21
- this.error('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, `Annotation $(ANNO) can't be used on elements` );
15
+ if (member['@sql.replace'])
16
+ this.error(null, path, { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
17
+ if (member['@sql.prepend'])
18
+ this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, `Annotation $(ANNO) can't be used on elements` );
22
19
 
23
- if (member['@sql.append']) {
24
- if (this.artifact.query)
25
- this.error('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on elements in views` );
26
- else if (this.csnUtils.isStructured(member))
27
- this.error('anno-invalid-sql-struct', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on structured elements` );
28
- else
29
- checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
30
- }
20
+ if (member['@sql.append']) {
21
+ if (this.artifact.query)
22
+ this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on elements in views` );
23
+ else if (this.csnUtils.isStructured(member))
24
+ this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on structured elements` );
25
+ else
26
+ checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
31
27
  }
32
28
  }
33
29
 
@@ -54,19 +50,24 @@ function checkValidAnnoValue(carrier, annotation, path, error, options) {
54
50
  * @param {string} artifactName
55
51
  */
56
52
  function checkSqlAnnotationOnArtifact(artifact, artifactName) {
57
- if (isBetaEnabled(this.options, 'sqlSnippets')) {
58
- if (artifact['@sql.prepend']) {
59
- if (artifact.query)
60
- this.error('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, `Annotation $(NAME) can't be used on views` );
61
- else
62
- checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
63
- }
53
+ if (artifact.kind !== 'entity') {
54
+ if (artifact['@sql.prepend'])
55
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
56
+ if (artifact['@sql.append'])
57
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
58
+ }
59
+ else if (artifact['@sql.prepend']) {
60
+ if (artifact.query)
61
+ this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, `Annotation $(NAME) can't be used on views` );
62
+ else
63
+ checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
64
+ }
64
65
 
65
- if (artifact['@sql.replace'])
66
- this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
67
66
 
68
- checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
69
- }
67
+ if (artifact['@sql.replace'])
68
+ this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
69
+
70
+ checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
70
71
  }
71
72
 
72
73
  // Anything that could terminate the "old" statement and start a new one basically.
@@ -19,7 +19,7 @@ function checkDecimalScale(member, memberName, prop, path) {
19
19
  // skip is already filtered in validator, here for completeness
20
20
  hasAnnotationValue(this.artifact, '@cds.persistence.skip'))
21
21
  return;
22
- if (member.scale && [ 'variable', 'floating' ].includes(member.scale))
22
+ if (member.scale && (member.scale === 'variable' || member.scale === 'floating'))
23
23
  this.error(null, path, { name: member.scale }, 'Unexpected scale $(NAME)');
24
24
  }
25
25
 
@@ -14,11 +14,10 @@ const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
14
14
  const checkForEmptyOrOnlyVirtual = require('./emptyOrOnlyVirtual');
15
15
  // forOdata
16
16
  const { validateDefaultValues } = require('./defaultValues');
17
- // const { checkChainedArray } = require('./arrayOfs');
18
17
  const { checkActionOrFunction } = require('./actionsFunctions');
19
18
  const {
20
- checkCoreMediaTypeAllowence, checkForMultipleCoreMediaTypes,
21
- checkAnalytics, checkAtSapAnnotations, checkReadOnlyAndInsertOnly,
19
+ checkCoreMediaTypeAllowence, checkAnalytics,
20
+ checkAtSapAnnotations, checkReadOnlyAndInsertOnly,
22
21
  } = require('./annotationsOData');
23
22
  // both
24
23
  const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
@@ -92,7 +91,6 @@ const forOdataArtifactValidators
92
91
  // the renderer does not work because the enricher can't handle certain
93
92
  // OData specifics.
94
93
  // checkChainedArray,
95
- checkForMultipleCoreMediaTypes,
96
94
  checkReadOnlyAndInsertOnly,
97
95
  ];
98
96
 
@@ -129,7 +127,7 @@ function _validate(csn, that,
129
127
  iterateOptions = {}) {
130
128
  const { cleanup } = enrich(csn);
131
129
 
132
- applyTransformations(csn, mergeCsnValidators(csnValidators, that), [], true, { drillRef: true });
130
+ applyTransformations(csn, mergeCsnValidators(csnValidators, that), [], { drillRef: true });
133
131
 
134
132
  forEachDefinition(csn, (artifact, artifactName, prop, path) => {
135
133
  artifactValidators.forEach((artifactValidator) => {
@@ -204,12 +202,9 @@ function forHana(csn, that) {
204
202
  ),
205
203
  forHanaQueryValidators.concat(commonQueryValidators),
206
204
  {
207
- skipArtifact: artifact => artifact.abstract || hasAnnotationValue(artifact, '@cds.persistence.skip'),
208
- skip: [
209
- 'action',
210
- 'function',
211
- 'event',
212
- ],
205
+ skipArtifact: artifact => artifact.abstract ||
206
+ hasAnnotationValue(artifact, '@cds.persistence.skip') ||
207
+ [ 'action', 'function', 'event' ].includes(artifact.kind),
213
208
  });
214
209
  }
215
210
 
@@ -72,6 +72,7 @@ const { locationString, hasErrors } = require('../base/messages');
72
72
  // Properties that can appear where a type can have type arguments.
73
73
  const typeProperties = [
74
74
  'type', '$typeArgs', 'length', 'precision', 'scale', 'srid',
75
+ '_effectiveType',
75
76
  ];
76
77
 
77
78
  function assertConsistency( model, stage ) {
@@ -298,6 +299,7 @@ function assertConsistency( model, stage ) {
298
299
  },
299
300
  expand: { kind: [ 'element' ], inherits: 'columns' },
300
301
  inline: { kind: [ 'element' ], inherits: 'columns' },
302
+ $noOrigin: { kind: [ 'element' ], test: TODO },
301
303
  excludingDict: {
302
304
  kind: 'element',
303
305
  test: isDictionary( definition ), // definition since redef
@@ -325,8 +327,8 @@ function assertConsistency( model, stage ) {
325
327
  kind: true,
326
328
  requires: [ 'location' ],
327
329
  optional: [
328
- 'path', 'elements', '_outer',
329
- 'scope', '_artifact', '$inferred', '$expand',
330
+ 'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
331
+ 'scope', '_artifact', '$inferred', '$expand', '$tableAliases', '_$next',
330
332
  '_effectiveType', // by propagation
331
333
  ],
332
334
  },
@@ -641,7 +643,8 @@ function assertConsistency( model, stage ) {
641
643
  * and `localized` namespaces.
642
644
  */
643
645
  function builtin( node, parent, prop, spec, name ) {
644
- if (![ 'string', 'boolean' ].includes(typeof node))
646
+ const type = typeof node;
647
+ if (type !== 'string' && type !== 'boolean')
645
648
  throw new Error(`Property '${ prop }' must be a boolean or string but was '${ typeof node }'${ at( [ node, parent ], prop, name ) }` );
646
649
 
647
650
  if (parent.kind !== 'namespace')
@@ -45,6 +45,7 @@ const kindProperties = {
45
45
  noDep: 'special',
46
46
  elements: true, /* only for parse-cdl */
47
47
  actions: true, /* only for parse-cdl */
48
+ enum: true, /* only for parse-cdl */
48
49
  },
49
50
  annotate: {
50
51
  isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
@@ -1,9 +1,14 @@
1
1
  // The builtin artifacts of CDS
2
2
 
3
+ // TODO: split this file
4
+ // - in base/: common definitions
5
+ // - in compiler/: XSN-specific
6
+ // - in ?: CSN-specific
7
+
3
8
  'use strict';
4
9
 
5
10
  const { builtinLocation } = require('../base/location');
6
- const { setProp } = require('./utils');
11
+ const { setLink: setProp } = require('./utils');
7
12
 
8
13
  const core = {
9
14
  String: { parameters: [ 'length' ], category: 'string' },
@@ -41,6 +46,16 @@ const coreHana = {
41
46
  ST_GEOMETRY: { parameters: [ { name: 'srid', literal: 'number', val: 0 } ], category: 'geo' },
42
47
  };
43
48
 
49
+ const typeParameters = {
50
+ expectedLiteralsFor: {
51
+ length: [ 'number' ],
52
+ scale: [ 'number', 'string' ],
53
+ precision: [ 'number' ],
54
+ srid: [ 'number' ],
55
+ },
56
+ };
57
+ typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
58
+
44
59
  // const hana = {
45
60
  // BinaryFloat: {},
46
61
  // LocalDate: {},
@@ -56,7 +71,6 @@ const coreHana = {
56
71
  * (do not add more - make it part of the SQL renderer to remove parentheses for
57
72
  * other funny SQL functions like CURRENT_UTCTIMESTAMP).
58
73
  */
59
-
60
74
  const functionsWithoutParens = [
61
75
  'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
62
76
  'CURRENT_USER', 'SESSION_USER', 'SYSTEM_USER',
@@ -176,7 +190,8 @@ function isRelationTypeName(typeName) {
176
190
  function isInReservedNamespace(absolute) {
177
191
  return absolute.startsWith( 'cds.') &&
178
192
  !absolute.match(/^cds\.foundation(\.|$)/) &&
179
- !absolute.match(/^cds\.outbox(\.|$)/); // Requested by Node runtime
193
+ !absolute.match(/^cds\.outbox(\.|$)/) && // Requested by Node runtime
194
+ !absolute.match(/^cds\.xt(\.|$)/); // Requested by Mtx
180
195
  }
181
196
 
182
197
  /**
@@ -214,9 +229,6 @@ function initBuiltins( model ) {
214
229
  model.$builtins.hana = hana;
215
230
  cds._subArtifacts.hana = hana;
216
231
  env( coreHana, 'cds.hana.', hana );
217
- // namespace:"localized" stores localized convenience views ---
218
- const localized = createNamespace( 'localized', true );
219
- model.definitions.localized = localized;
220
232
  model.$internal = { $frontend: '$internal' };
221
233
  return;
222
234
 
@@ -310,6 +322,7 @@ function initBuiltins( model ) {
310
322
  }
311
323
 
312
324
  module.exports = {
325
+ typeParameters,
313
326
  functionsWithoutParens,
314
327
  specialFunctions,
315
328
  initBuiltins,
@@ -24,6 +24,7 @@ function check( model ) { // = XSN
24
24
  error, warning, message,
25
25
  } = model.$messageFunctions;
26
26
  forEachDefinition( model, checkArtifact );
27
+ checkSapCommonLocale( model, model.$messageFunctions );
27
28
  return;
28
29
 
29
30
  function checkArtifact( art ) {
@@ -447,69 +448,10 @@ function check( model ) { // = XSN
447
448
  // params are limited to actual values and params
448
449
  if (pathStep.args)
449
450
  checkExpression(pathStep.args);
450
-
451
- if (!path[0] || !path[0]._navigation) { // TODO: Discuss (see #4108)
452
- checkPathForMissingArguments(pathStep);
453
- }
454
451
  }
455
452
  });
456
453
  }
457
454
 
458
- /**
459
- * Check whether the argument count of the given path expression matches its artifact.
460
- * If there is a mismatch, an error is issued.
461
- *
462
- * @param {object} pathStep The expression to check
463
- */
464
- function checkPathForMissingArguments(pathStep) {
465
- // _artifact may not be set, e.g. for functions like `convert_currency( amount => 3 )`
466
- // _navigation must not be set or we would (for example) check each field of an entity
467
- if (!pathStep._artifact || pathStep._navigation)
468
- return;
469
-
470
- const isAssociation = !!pathStep._artifact.target;
471
- if (isAssociation) {
472
- const targetFinalType = pathStep._artifact.target._artifact &&
473
- pathStep._artifact.target._artifact._effectiveType;
474
- const finalTypeParams = targetFinalType ? targetFinalType.params : null;
475
- compareActualNamedArgsWithFormalNamedArgs(pathStep.args, finalTypeParams);
476
- }
477
- else {
478
- // Parameters can only be provided when navigating along associations, so because this path
479
- // is for non-associations, checking arguments along a navigation is unnecessary and faulty.
480
- compareActualNamedArgsWithFormalNamedArgs(pathStep.args, pathStep._artifact.params);
481
- }
482
-
483
- /**
484
- * Compare two argument dictionaries for correct argument count.
485
- * @param {object} actualArgs
486
- * @param {object} formalArgs
487
- */
488
- function compareActualNamedArgsWithFormalNamedArgs(actualArgs, formalArgs) {
489
- actualArgs = actualArgs || {};
490
- formalArgs = formalArgs || {};
491
-
492
- const aArgsCount = Object.keys(actualArgs).length;
493
- const expectedNames = Object.keys(formalArgs);
494
-
495
- const missingArgs = [];
496
- for (const fAName in formalArgs) {
497
- if (!actualArgs[fAName] && !formalArgs[fAName].default)
498
- missingArgs.push(fAName);
499
- }
500
-
501
- if (missingArgs.length) {
502
- error(null, [ pathStep.location, pathStep ],
503
- { names: missingArgs, expected: expectedNames.length, given: aArgsCount },
504
- 'Expected $(EXPECTED) arguments but $(GIVEN) given; missing: $(NAMES)');
505
- }
506
- // Note:
507
- // Unknown arguments are already handled by messages
508
- // args-expected-named and args-undefined-param
509
- }
510
- }
511
-
512
-
513
455
  function checkAssociation(elem) {
514
456
  // TODO: yes, a check similar to this could make it into the compiler)
515
457
  // when virtual element is part of association
@@ -635,7 +577,7 @@ function check( model ) { // = XSN
635
577
  return checkTreeLikeExpression(xpr, allowAssocTail);
636
578
  }
637
579
  /**
638
- * Check wether the supplied argument is a virtual element
580
+ * Check whether the supplied argument is a virtual element
639
581
  *
640
582
  * TO CLARIFY: do we want the "no virtual element" check for virtual elements/columns, too?
641
583
  *
@@ -945,6 +887,27 @@ function check( model ) { // = XSN
945
887
  }
946
888
  }
947
889
 
890
+ /**
891
+ * Checks that sap.common.Locale is of type cds.String. This limitation may
892
+ * be lifted later on.
893
+ *
894
+ * @param {XSN.Model} model
895
+ * @param {object} messageFunctions
896
+ */
897
+ function checkSapCommonLocale( model, messageFunctions ) {
898
+ const localeArt = model.definitions['sap.common.Locale'];
899
+ if (localeArt) {
900
+ const type = localeArt._effectiveType;
901
+ const isCdsString = type && type.name && type.name.absolute === 'cds.String';
902
+ if (!isCdsString) {
903
+ const { message } = messageFunctions;
904
+ message('type-expected-builtin', [ localeArt.name.location, localeArt ],
905
+ { name: 'sap.common.Locale' },
906
+ 'Expected $(NAME) to be a string type');
907
+ }
908
+ }
909
+ }
910
+
948
911
  // For each property named 'path' in 'node' (recursively), call callback(path, node)
949
912
  //
950
913
  // TODO: remove - this is not a good way to traverse expressions
@@ -16,7 +16,7 @@
16
16
 
17
17
  'use strict';
18
18
 
19
- const { setProp } = require('../base/model');
19
+ const { setLink: setProp } = require('./utils'); // check enum/non-enum
20
20
 
21
21
  // Detect cyclic dependencies between all nodes reachable from `definitions`.
22
22
  // If such a dependency is found, call `reportCycle` with arguments `dep.art`