@sap/cds-compiler 2.13.6 → 2.15.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 (78) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/bin/cdsc.js +112 -37
  3. package/lib/api/main.js +20 -22
  4. package/lib/api/options.js +2 -3
  5. package/lib/api/validate.js +6 -6
  6. package/lib/base/message-registry.js +92 -17
  7. package/lib/base/messages.js +85 -64
  8. package/lib/base/optionProcessorHelper.js +19 -0
  9. package/lib/checks/annotationsOData.js +11 -32
  10. package/lib/checks/arrayOfs.js +1 -34
  11. package/lib/checks/validator.js +2 -4
  12. package/lib/compiler/assert-consistency.js +1 -0
  13. package/lib/compiler/base.js +1 -0
  14. package/lib/compiler/builtins.js +11 -0
  15. package/lib/compiler/checks.js +22 -70
  16. package/lib/compiler/define.js +59 -11
  17. package/lib/compiler/extend.js +20 -3
  18. package/lib/compiler/finalize-parse-cdl.js +26 -20
  19. package/lib/compiler/index.js +75 -26
  20. package/lib/compiler/populate.js +6 -5
  21. package/lib/compiler/propagator.js +4 -1
  22. package/lib/compiler/resolve.js +104 -16
  23. package/lib/compiler/shared.js +61 -27
  24. package/lib/compiler/tweak-assocs.js +7 -1
  25. package/lib/edm/annotations/genericTranslation.js +93 -21
  26. package/lib/edm/csn2edm.js +216 -98
  27. package/lib/edm/edm.js +305 -226
  28. package/lib/edm/edmPreprocessor.js +499 -423
  29. package/lib/edm/edmUtils.js +22 -22
  30. package/lib/gen/Dictionary.json +98 -22
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +3 -1
  33. package/lib/gen/languageParser.js +4636 -4368
  34. package/lib/json/csnVersion.js +10 -11
  35. package/lib/json/from-csn.js +3 -2
  36. package/lib/json/to-csn.js +0 -2
  37. package/lib/language/docCommentParser.js +2 -2
  38. package/lib/language/genericAntlrParser.js +47 -2
  39. package/lib/language/language.g4 +59 -27
  40. package/lib/main.d.ts +19 -1
  41. package/lib/main.js +6 -0
  42. package/lib/model/csnRefs.js +33 -6
  43. package/lib/model/csnUtils.js +193 -75
  44. package/lib/model/enrichCsn.js +1 -0
  45. package/lib/model/revealInternalProperties.js +2 -2
  46. package/lib/modelCompare/compare.js +6 -6
  47. package/lib/optionProcessor.js +62 -26
  48. package/lib/render/toCdl.js +844 -679
  49. package/lib/render/toHdbcds.js +189 -243
  50. package/lib/render/toSql.js +180 -198
  51. package/lib/render/utils/common.js +131 -15
  52. package/lib/transform/db/.eslintrc.json +1 -1
  53. package/lib/transform/db/associations.js +2 -2
  54. package/lib/transform/db/constraints.js +3 -1
  55. package/lib/transform/db/expansion.js +15 -10
  56. package/lib/transform/db/flattening.js +95 -68
  57. package/lib/transform/db/transformExists.js +7 -7
  58. package/lib/transform/db/views.js +6 -3
  59. package/lib/transform/forHanaNew.js +43 -26
  60. package/lib/transform/forOdataNew.js +43 -42
  61. package/lib/transform/localized.js +12 -7
  62. package/lib/transform/odata/toFinalBaseType.js +8 -6
  63. package/lib/transform/odata/typesExposure.js +145 -197
  64. package/lib/transform/transformUtilsNew.js +9 -12
  65. package/lib/transform/translateAssocsToJoins.js +5 -1
  66. package/lib/transform/universalCsn/coreComputed.js +5 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
  68. package/lib/utils/moduleResolve.js +13 -6
  69. package/package.json +1 -1
  70. package/share/messages/message-explanations.json +2 -1
  71. package/share/messages/syntax-expected-integer.md +37 -0
  72. package/lib/transform/odata/attachPath.js +0 -96
  73. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  74. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  75. package/lib/transform/odata/referenceFlattener.js +0 -296
  76. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  77. package/lib/transform/odata/structuralPath.js +0 -72
  78. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -13,42 +13,22 @@
13
13
  * @param {CSN.Element} member Member to be checked
14
14
  */
15
15
  function checkCoreMediaTypeAllowence(member) {
16
- const allowedCoreMediaTypes = [
17
- 'cds.String',
18
- 'cds.LargeString',
19
- 'cds.hana.VARCHAR',
20
- 'cds.hana.CHAR',
21
- 'cds.Binary',
22
- 'cds.LargeBinary',
23
- 'cds.hana.CLOB',
24
- 'cds.hana.BINARY',
25
- ];
26
- if (member['@Core.MediaType'] && member.type && !allowedCoreMediaTypes.includes(member.type)) {
16
+ const allowedCoreMediaTypes = {
17
+ 'cds.String': 1,
18
+ 'cds.LargeString': 1,
19
+ 'cds.hana.VARCHAR': 1,
20
+ 'cds.hana.CHAR': 1,
21
+ 'cds.Binary': 1,
22
+ 'cds.LargeBinary': 1,
23
+ 'cds.hana.CLOB': 1,
24
+ 'cds.hana.BINARY': 1,
25
+ };
26
+ if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalBaseType(member.type) in allowedCoreMediaTypes)) {
27
27
  this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
28
28
  'Element annotated with “@Core.MediaType” should be of a type mapped to $(NAMES)');
29
29
  }
30
30
  }
31
31
 
32
- /**
33
- * Make sure only one element in a definition is annotated with `@Core.MediaType`
34
- * This is only OData V2 relevant.
35
- *
36
- * @param {CSN.Artifact} artifact Definition to be checked
37
- * @param {string} artifactName The name of the artifact
38
- */
39
- function checkForMultipleCoreMediaTypes(artifact, artifactName) {
40
- if (!this.csnUtils.getServiceName(artifactName))
41
- return;
42
- if (this.options.toOdata && this.options.toOdata.version === 'v2' && artifact.elements) {
43
- const mediaTypeElementsNames = Object.keys(artifact.elements)
44
- .filter(elementName => artifact.elements[elementName]['@Core.MediaType']);
45
- if (mediaTypeElementsNames.length > 1) {
46
- this.error(null, artifact.$path, { names: mediaTypeElementsNames },
47
- `Multiple elements $(NAMES) annotated with “@Core.MediaType”, OData V2 allows only one`);
48
- }
49
- }
50
- }
51
-
52
32
  /**
53
33
  * Check if `@Aggregation.default` is assigned together with `@Analytics.Measure`
54
34
  *
@@ -88,7 +68,6 @@ function checkReadOnlyAndInsertOnly(artifact, artifactName) {
88
68
 
89
69
  module.exports = {
90
70
  checkCoreMediaTypeAllowence,
91
- checkForMultipleCoreMediaTypes,
92
71
  checkAnalytics,
93
72
  checkAtSapAnnotations,
94
73
  checkReadOnlyAndInsertOnly,
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { forEachMemberRecursively } = require('../model/csnUtils');
4
-
5
3
  // Only to be used with validator.js - a correct `this` value needs to be provided!
6
4
 
7
5
  /**
@@ -43,35 +41,4 @@ function validateAssociationsInItems(member) {
43
41
  }
44
42
  }
45
43
 
46
- /**
47
- * Check that there are no .items containing .items.
48
- *
49
- * @param {CSN.Artifact} art Artifact
50
- * @param {string} artName Name of the artifact
51
- */
52
- function checkChainedArray(art, artName) {
53
- if (!this.csnUtils.getServiceName(artName))
54
- return;
55
- checkIfItemsOfItems.bind(this)(art);
56
- forEachMemberRecursively(art, checkIfItemsOfItems.bind(this));
57
-
58
- /**
59
- *
60
- * @param {object} construct the construct to be checked
61
- */
62
- function checkIfItemsOfItems(construct) {
63
- const constructType = this.csnUtils.effectiveType(construct);
64
- if (constructType.items) {
65
- if (constructType.items.items) {
66
- this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
67
- return;
68
- }
69
-
70
- const itemsType = this.csnUtils.effectiveType(constructType.items);
71
- if (itemsType.items)
72
- this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
73
- }
74
- }
75
- }
76
-
77
- module.exports = { validateAssociationsInItems, checkChainedArray };
44
+ module.exports = { validateAssociationsInItems };
@@ -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
 
@@ -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 ) {
@@ -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,
@@ -46,6 +46,16 @@ const coreHana = {
46
46
  ST_GEOMETRY: { parameters: [ { name: 'srid', literal: 'number', val: 0 } ], category: 'geo' },
47
47
  };
48
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
+
49
59
  // const hana = {
50
60
  // BinaryFloat: {},
51
61
  // LocalDate: {},
@@ -312,6 +322,7 @@ function initBuiltins( model ) {
312
322
  }
313
323
 
314
324
  module.exports = {
325
+ typeParameters,
315
326
  functionsWithoutParens,
316
327
  specialFunctions,
317
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,80 +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
- * TODO: remove this function - it also checks for parameter in type
463
- * references. We could have a warning, see also configurable errors
464
- * 'args-no-params', 'args-undefined-param'.
465
- *
466
- * @param {object} pathStep The expression to check
467
- */
468
- function checkPathForMissingArguments(pathStep) {
469
- // _artifact may not be set, e.g. for functions like `convert_currency( amount => 3 )`
470
- // _navigation must not be set or we would (for example) check each field of an entity
471
- if (!pathStep._artifact || pathStep._navigation)
472
- return;
473
-
474
- const isAssociation = !!pathStep._artifact.target;
475
- if (isAssociation) {
476
- const targetFinalType = pathStep._artifact.target._artifact &&
477
- pathStep._artifact.target._artifact._effectiveType;
478
- const finalTypeParams = targetFinalType ? targetFinalType.params : null;
479
- compareActualNamedArgsWithFormalNamedArgs(pathStep.args, finalTypeParams);
480
- }
481
- else {
482
- // Parameters can only be provided when navigating along associations, so because this path
483
- // is for non-associations, checking arguments along a navigation is unnecessary and faulty.
484
- compareActualNamedArgsWithFormalNamedArgs(pathStep.args, pathStep._artifact.params);
485
- }
486
-
487
- /**
488
- * Compare two argument dictionaries for correct argument count.
489
- * @param {object} actualArgs
490
- * @param {object} formalArgs
491
- */
492
- function compareActualNamedArgsWithFormalNamedArgs(actualArgs, formalArgs) {
493
- actualArgs = actualArgs || {};
494
- formalArgs = formalArgs || {};
495
-
496
- const aArgsCount = Object.keys(actualArgs).length;
497
- const expectedNames = Object.keys(formalArgs);
498
-
499
- const missingArgs = [];
500
- for (const fAName in formalArgs) {
501
- if (!actualArgs[fAName]) {
502
- // Note: _effectiveType points to cds.String for `type T : DefaultString`.
503
- // And `default` may appear at any `type` in the hierarchy.
504
- let fArg = formalArgs[fAName];
505
- while (fArg.type && !fArg.default)
506
- fArg = fArg.type._artifact;
507
- if (!fArg.default)
508
- missingArgs.push(fAName);
509
- }
510
- }
511
-
512
- if (missingArgs.length) {
513
- error(null, [ pathStep.location, pathStep ],
514
- { names: missingArgs, expected: expectedNames.length, given: aArgsCount },
515
- 'Expected $(EXPECTED) arguments but $(GIVEN) given; missing: $(NAMES)');
516
- }
517
- // Note:
518
- // Unknown arguments are already handled by messages
519
- // args-expected-named and args-undefined-param
520
- }
521
- }
522
-
523
-
524
455
  function checkAssociation(elem) {
525
456
  // TODO: yes, a check similar to this could make it into the compiler)
526
457
  // when virtual element is part of association
@@ -956,6 +887,27 @@ function check( model ) { // = XSN
956
887
  }
957
888
  }
958
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
+
959
911
  // For each property named 'path' in 'node' (recursively), call callback(path, node)
960
912
  //
961
913
  // TODO: remove - this is not a good way to traverse expressions
@@ -77,7 +77,7 @@
77
77
  // References are resolved in the "resolve" phase of the compiler, see
78
78
  // './resolver.js'. We then get the properties `type.absolute` and `length`.
79
79
 
80
- // Sub phase 1 (addXYZ) - only for main artifats
80
+ // Sub phase 1 (addXYZ) - only for main artifacts
81
81
  // - set _block links
82
82
  // - store definitions (including context extensions), NO duplicate check
83
83
  // - artifact name check
@@ -572,7 +572,7 @@ function define( model ) {
572
572
  if (query.on)
573
573
  initExprForQuery( query.on, query );
574
574
  // TODO: MIXIN with name = ...subquery (not yet supported anyway)
575
- initSelectItems( query, query.columns );
575
+ initSelectItems( query, query.columns, query );
576
576
  if (query.where)
577
577
  initExprForQuery( query.where, query );
578
578
  if (query.having)
@@ -580,12 +580,14 @@ function define( model ) {
580
580
  initMembers( query, query, query._block );
581
581
  }
582
582
 
583
- function initSelectItems( parent, columns ) {
583
+ function initSelectItems( parent, columns, user ) {
584
584
  // TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
585
585
  let wildcard = null;
586
+ let hasItems = false;
586
587
  for (const col of columns || parent.expand || parent.inline || []) {
587
588
  if (!col) // parse error
588
589
  continue;
590
+ hasItems = true;
589
591
  if (!columns) {
590
592
  if (parent.value)
591
593
  setLink( col, '_pathHead', parent ); // also set for '*' in expand/inline
@@ -606,13 +608,27 @@ function define( model ) {
606
608
  col.val = null; // do not consider it for expandWildcard()
607
609
  }
608
610
  }
609
- else if (col.value || col.expand) {
611
+ // Either expression (value), expand or new association (target && type)
612
+ else if (col.value || col.expand || (col.target && col.type)) {
610
613
  setLink( col, '_block', parent._block );
611
614
  defineAnnotations( col, col, parent._block ); // TODO: complain with inline
612
615
  // TODO: allow sub queries? at least in top-level expand without parallel ref
613
616
  if (columns)
614
617
  initExprForQuery( col.value, parent );
615
- initSelectItems( col );
618
+ initSelectItems( col, null, user );
619
+ }
620
+ }
621
+
622
+ if (hasItems && !wildcard && parent.excludingDict) {
623
+ // TODO: Better way to get source file?
624
+ let block = parent;
625
+ while (block._block)
626
+ block = block._block;
627
+
628
+ if (block.$frontend === 'cdl') {
629
+ warning('query-ignoring-exclude', [ parent.excludingDict[$location], user ],
630
+ { prop: '*' },
631
+ 'Excluding elements without wildcard $(PROP) has no effect');
616
632
  }
617
633
  }
618
634
  }
@@ -898,15 +914,43 @@ function define( model ) {
898
914
  delete obj.on; // continuation semantics: not specified
899
915
  }
900
916
  if (targetAspect.elements) {
917
+ const inEntity = parent._main && parent._main.kind === 'entity';
918
+ // TODO: also allow indirectly (component in component in entity)?
901
919
  setLink( targetAspect, '_outer', obj );
902
920
  setLink( targetAspect, '_parent', parent._parent );
903
921
  setLink( targetAspect, '_main', null ); // for name resolution
904
- obj = targetAspect;
905
- parent = obj;
922
+
923
+ parent = targetAspect;
906
924
  construct = parent; // avoid extension behavior
907
- obj.kind = 'aspect'; // TODO: probably '$aspect' to detect
908
- setLink( obj, '_block', block );
909
- initDollarSelf( obj );
925
+ targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
926
+ setLink( targetAspect, '_block', block );
927
+ initDollarSelf( targetAspect );
928
+ // allow ref of up_ in anonymous aspect inside entity
929
+ // (TODO: complain if used and the managed composition is included into
930
+ // another entity - might induce auto-redirection):
931
+ if (inEntity && !targetAspect.elements.up_) {
932
+ const up = {
933
+ name: {
934
+ id: 'up_',
935
+ alias: 'up_',
936
+ element: obj.name.element,
937
+ absolute: obj.name.absolute,
938
+ },
939
+ kind: '$navElement',
940
+ location: obj.location,
941
+ };
942
+ setLink( up, '_parent', targetAspect );
943
+ setLink( up, '_main', targetAspect ); // used on main artifact
944
+ // recompilation case: both target and targetAspect → allow up_ in that case, too:
945
+ const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
946
+ const entity = name && model.definitions[name];
947
+ if (entity && entity.elements)
948
+ setLink( up, '_origin', entity.elements.up_ );
949
+ // processAspectComposition/expand() sets _origin to element of
950
+ // generated target entity
951
+ targetAspect.$tableAliases.up_ = up;
952
+ }
953
+ obj = targetAspect;
910
954
  }
911
955
  }
912
956
  if (obj !== parent && obj.elements && parent.enum) {
@@ -926,6 +970,7 @@ function define( model ) {
926
970
  if (checkDefinitions( construct, parent, 'enum', obj.enum || false ))
927
971
  forEachGeneric( obj, 'enum', init );
928
972
  }
973
+
929
974
  if (obj.foreignKeys) // cannot be extended or annotated - TODO: check anyway?
930
975
  forEachInOrder( obj, 'foreignKeys', init );
931
976
  if (checkDefinitions( construct, parent, 'actions' ))
@@ -1009,7 +1054,7 @@ function define( model ) {
1009
1054
  // - artifacts (CDL-only anyway) only inside [extend] context|service
1010
1055
  if (!dict)
1011
1056
  return false;
1012
- const names = Object.getOwnPropertyNames( dict );
1057
+ const names = Object.keys( dict );
1013
1058
  if (!names.length) // TODO: re-check - really allow empty dict if no other?
1014
1059
  return false;
1015
1060
  const feature = kindProperties[parent.kind][prop];
@@ -1046,6 +1091,9 @@ function define( model ) {
1046
1091
  error( 'unexpected-elements', [ location, construct ], {},
1047
1092
  'Elements only exist in entities, types or typed constructs' );
1048
1093
  }
1094
+ else if (prop === 'columns') {
1095
+ error( 'extend-columns', [ location, construct ], { art: construct } );
1096
+ }
1049
1097
  else { // if (prop === 'enum') {
1050
1098
  error( 'unexpected-enum', [ location, construct ], {},
1051
1099
  'Enum symbols can only be defined for types or typed constructs' );
@@ -158,6 +158,7 @@ function extend( model ) {
158
158
  checkDefinitions( ext, art, 'enum');
159
159
  checkDefinitions( ext, art, 'actions');
160
160
  checkDefinitions( ext, art, 'params');
161
+ checkDefinitions( ext, art, 'columns');
161
162
  defineAnnotations( ext, art, ext._block, ext.kind );
162
163
  }
163
164
  return true;
@@ -245,7 +246,7 @@ function extend( model ) {
245
246
  while (obj.items)
246
247
  obj = obj.items;
247
248
  const validDict = obj[prop] || prop === 'elements' && obj.enum;
248
- const member = validDict[name];
249
+ const member = validDict && validDict[name];
249
250
  if (!member)
250
251
  extendNothing( dict[name], prop, name, art, validDict );
251
252
  else if (!(member.$duplicates))
@@ -262,6 +263,10 @@ function extend( model ) {
262
263
  */
263
264
  function extendColumns( ext, art ) {
264
265
  // TODO: consider reportUnstableExtensions
266
+
267
+ for (const col of ext.columns)
268
+ defineAnnotations( col, col, ext._block, ext.kind );
269
+
265
270
  const { location } = ext.name;
266
271
  const { query } = art;
267
272
  if (!query) {
@@ -602,13 +607,18 @@ function extend( model ) {
602
607
  elements,
603
608
  $inferred: 'localized-entity',
604
609
  };
610
+ // If there is a type `sap.common.Locale`, then use it as the type for the element `locale`.
611
+ // If not, use the default `cds.String` with a length of 14.
612
+ const hasLocaleType = model.definitions['sap.common.Locale'] &&
613
+ model.definitions['sap.common.Locale'].kind === 'type';
605
614
  const locale = {
606
615
  name: { location, id: 'locale' },
607
616
  kind: 'element',
608
- type: augmentPath( location, 'cds.String' ),
609
- length: { literal: 'number', val: 14, location },
617
+ type: augmentPath( location, hasLocaleType ? 'sap.common.Locale' : 'cds.String' ),
610
618
  location,
611
619
  };
620
+ if (!hasLocaleType)
621
+ locale.length = { literal: 'number', val: 14, location };
612
622
 
613
623
  if (!fioriEnabled) {
614
624
  locale.key = { val: true, location };
@@ -809,6 +819,13 @@ function extend( model ) {
809
819
  };
810
820
  setArtifactLink( elem.target, entity );
811
821
  if (entity) {
822
+ // Support using the up_ element in the generated entity to be used
823
+ // inside the anonymous aspect:
824
+ const { up_ } = target.$tableAliases;
825
+ // TODO: invalidate "up_" alias (at least further navigation) if it
826
+ // already has an _origin (when the managed composition is included)
827
+ if (up_)
828
+ setLink( up_, '_origin', entity.elements.up_ );
812
829
  model.$compositionTargets[entity.name.absolute] = true;
813
830
  processAspectComposition( entity );
814
831
  processLocalizedData( entity );
@@ -16,7 +16,7 @@ function finalizeParseCdl( model ) {
16
16
  const { message, error } = model.$messageFunctions;
17
17
  const {
18
18
  resolveUncheckedPath,
19
- resolveTypeArguments,
19
+ resolveTypeArgumentsUnchecked,
20
20
  defineAnnotations,
21
21
  initMembers,
22
22
  extensionsDict,
@@ -26,8 +26,7 @@ function finalizeParseCdl( model ) {
26
26
  return;
27
27
 
28
28
  function resolveTypesAndExtensionsForParseCdl() {
29
- if (!model.extensions)
30
- model.extensions = [];
29
+ const extensions = [];
31
30
 
32
31
  // TODO: probably better to loop over extensions of all sources (there is just one)
33
32
  for (const name in extensionsDict) {
@@ -38,14 +37,21 @@ function finalizeParseCdl( model ) {
38
37
  mergeAnnotatesForSameArtifact( ext );
39
38
  // Initialize members and define annotations in sub-elements.
40
39
  initMembers( ext, ext, ext._block, true );
41
- model.extensions.push( ext );
40
+ extensions.push( ext );
41
+ for (const col of ext.columns || []) {
42
+ // Note, no `priority` argument, since we don't apply the extension in the end.
43
+ defineAnnotations( col, col, ext._block );
44
+ }
42
45
  }
43
46
  }
44
47
 
45
48
  forEachGeneric(model, 'definitions', art => resolveTypesForParseCdl(art, art));
46
49
  forEachGeneric(model, 'vocabularies', art => resolveTypesForParseCdl(art, art));
47
- if (model.extensions)
50
+
51
+ if (extensions.length > 0) {
52
+ model.extensions = extensions;
48
53
  model.extensions.forEach(ext => resolveTypesForParseCdl(ext, ext));
54
+ }
49
55
  }
50
56
 
51
57
  /**
@@ -144,11 +150,13 @@ function finalizeParseCdl( model ) {
144
150
 
145
151
  /**
146
152
  * Resolves `artWithType.type` in an unchecked manner. Handles `type of` cases.
153
+ * `artWithType` has the `type` property, i.e. it could be an `items` object.
154
+ * `user` is the actual artifact, e.g. entity or element.
147
155
  *
148
156
  * @param {object} artWithType
149
- * @param {XSN.Artifact} artifact
157
+ * @param {XSN.Artifact} user
150
158
  */
151
- function resolveTypeUnchecked(artWithType, artifact) {
159
+ function resolveTypeUnchecked(artWithType, user) {
152
160
  if (!artWithType.type)
153
161
  return;
154
162
  const root = artWithType.type.path && artWithType.type.path[0];
@@ -159,19 +167,18 @@ function finalizeParseCdl( model ) {
159
167
  // without special treatment.
160
168
  if (artWithType.type.scope !== 'typeOf') {
161
169
  // elem: Type or elem: type of Artifact:elem
162
- const name = resolveUncheckedPath(artWithType.type, 'type', artifact);
163
- const def = name && model.definitions[name];
164
- if (def)
165
- resolveTypeArguments( artWithType, def, artifact );
170
+ const name = resolveUncheckedPath( artWithType.type, 'type', user );
171
+ const type = name && model.definitions[name] || { name: { absolute: name } };
172
+ resolveTypeArgumentsUnchecked( artWithType, type, user );
166
173
  return;
167
174
  }
168
- else if (!artifact._main) {
169
- error( 'ref-undefined-typeof', [ artWithType.type.location, artifact ], {},
175
+ else if (!user._main) {
176
+ error( 'ref-undefined-typeof', [ artWithType.type.location, user ], {},
170
177
  'Current artifact has no element to refer to as type' );
171
178
  return;
172
179
  }
173
180
  else if (root.id === '$self' || root.id === '$projection') {
174
- setArtifactLink( root, artifact._main );
181
+ setArtifactLink( root, user._main );
175
182
  }
176
183
  else {
177
184
  // For better error messages, check for invalid TYPE OFs similarly
@@ -179,19 +186,18 @@ function finalizeParseCdl( model ) {
179
186
  let struct = artWithType;
180
187
  while (struct.kind === 'element')
181
188
  struct = struct._parent;
182
- if (struct.kind === 'select' || struct !== artifact._main) {
183
- message( 'type-unexpected-typeof', [ artWithType.type.location, artifact ],
189
+ if (struct.kind === 'select' || struct !== user._main) {
190
+ message( 'type-unexpected-typeof', [ artWithType.type.location, user ],
184
191
  { keyword: 'type of', '#': struct.kind } );
185
192
  return;
186
193
  }
187
194
 
188
- const fake = { name: { absolute: artifact.name.absolute } };
195
+ const fake = { name: { absolute: user.name.absolute } };
189
196
  // to-csn just needs a fake element whose absolute name and _parent/_main links are correct
190
- setLink( fake, '_parent', artifact._parent );
191
- setLink( fake, '_main', artifact._main ); // value does not matter...
197
+ setLink( fake, '_parent', user._parent );
198
+ setLink( fake, '_main', user._main ); // value does not matter...
192
199
  setArtifactLink( root, fake );
193
200
  }
194
- resolveTypeArguments( artifact, {}, artifact ); // issue error for type args
195
201
  }
196
202
 
197
203
  function chooseAndReportDuplicateAnnotation(artifact, annoName) {