@sap/cds-compiler 3.3.2 → 3.4.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 (76) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/bin/cdsc.js +3 -1
  3. package/doc/CHANGELOG_BETA.md +17 -0
  4. package/lib/api/main.js +147 -18
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/keywords.js +104 -0
  8. package/lib/base/message-registry.js +137 -68
  9. package/lib/base/messages.js +59 -48
  10. package/lib/base/model.js +1 -0
  11. package/lib/checks/actionsFunctions.js +1 -1
  12. package/lib/checks/cdsPersistence.js +1 -1
  13. package/lib/checks/checkForTypes.js +13 -8
  14. package/lib/checks/defaultValues.js +3 -1
  15. package/lib/checks/elements.js +1 -1
  16. package/lib/checks/parameters.js +4 -2
  17. package/lib/checks/queryNoDbArtifacts.js +1 -1
  18. package/lib/checks/sql-snippets.js +12 -10
  19. package/lib/checks/validator.js +14 -4
  20. package/lib/compiler/assert-consistency.js +8 -7
  21. package/lib/compiler/checks.js +30 -20
  22. package/lib/compiler/define.js +89 -25
  23. package/lib/compiler/extend.js +33 -28
  24. package/lib/compiler/finalize-parse-cdl.js +14 -9
  25. package/lib/compiler/populate.js +30 -8
  26. package/lib/compiler/propagator.js +23 -28
  27. package/lib/compiler/resolve.js +11 -5
  28. package/lib/compiler/shared.js +66 -48
  29. package/lib/compiler/tweak-assocs.js +2 -3
  30. package/lib/compiler/utils.js +11 -0
  31. package/lib/edm/annotations/genericTranslation.js +7 -4
  32. package/lib/edm/csn2edm.js +1 -1
  33. package/lib/gen/language.checksum +1 -1
  34. package/lib/gen/language.interp +1 -1
  35. package/lib/gen/languageParser.js +3565 -3544
  36. package/lib/json/csnVersion.js +13 -13
  37. package/lib/json/from-csn.js +140 -158
  38. package/lib/json/to-csn.js +23 -5
  39. package/lib/language/.eslintrc.json +4 -0
  40. package/lib/language/antlrParser.js +7 -10
  41. package/lib/language/docCommentParser.js +1 -2
  42. package/lib/language/errorStrategy.js +54 -27
  43. package/lib/language/genericAntlrParser.js +115 -84
  44. package/lib/language/language.g4 +29 -25
  45. package/lib/language/multiLineStringParser.js +75 -63
  46. package/lib/main.js +1 -0
  47. package/lib/model/csnRefs.js +4 -3
  48. package/lib/model/csnUtils.js +39 -7
  49. package/lib/model/sortViews.js +7 -3
  50. package/lib/modelCompare/compare.js +49 -15
  51. package/lib/modelCompare/filter.js +83 -0
  52. package/lib/optionProcessor.js +5 -1
  53. package/lib/render/manageConstraints.js +9 -5
  54. package/lib/render/toCdl.js +120 -62
  55. package/lib/render/toHdbcds.js +1 -1
  56. package/lib/render/toSql.js +6 -2
  57. package/lib/render/utils/common.js +7 -0
  58. package/lib/sql-identifier.js +7 -0
  59. package/lib/transform/db/assertUnique.js +27 -38
  60. package/lib/transform/db/expansion.js +11 -4
  61. package/lib/transform/db/temporal.js +3 -1
  62. package/lib/transform/db/transformExists.js +7 -1
  63. package/lib/transform/db/views.js +42 -13
  64. package/lib/transform/draft/db.js +2 -2
  65. package/lib/transform/forOdataNew.js +7 -3
  66. package/lib/transform/forRelationalDB.js +12 -6
  67. package/lib/transform/localized.js +1 -1
  68. package/lib/transform/odata/typesExposure.js +2 -1
  69. package/lib/transform/parseExpr.js +245 -0
  70. package/lib/transform/transformUtilsNew.js +23 -14
  71. package/lib/transform/translateAssocsToJoins.js +12 -12
  72. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  73. package/lib/utils/term.js +5 -5
  74. package/package.json +2 -2
  75. package/share/messages/message-explanations.json +1 -1
  76. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +1 -1
@@ -35,7 +35,8 @@ function extend( model ) {
35
35
  resolvePath,
36
36
  resolveUncheckedPath,
37
37
  checkAnnotate,
38
- defineAnnotations,
38
+ initAnnotations,
39
+ copyAnnotationsForExtensions,
39
40
  attachAndEmitValidNames,
40
41
  checkDefinitions,
41
42
  initArtifact,
@@ -163,6 +164,7 @@ function extend( model ) {
163
164
  }
164
165
 
165
166
  function extendContext( name, art ) {
167
+ // TODO: remove this function - add remains to define.js
166
168
  // (ext.expectedKind == art.kind) already checked by parser except for context/service
167
169
  if (!kindProperties[art.kind].artifacts) {
168
170
  // no context or service => warn about context extensions
@@ -189,9 +191,8 @@ function extend( model ) {
189
191
  checkDefinitions( ext, art, 'columns');
190
192
  if (ext.includes)
191
193
  applyIncludes( ext, art ); // emits error if `includes` is set
192
- if (ext.kind === 'annotate' || ext.kind === 'extend')
193
- checkAnnotate( ext, art );
194
- defineAnnotations( ext, art, ext._block, ext.kind );
194
+ checkAnnotate( ext, art );
195
+ copyAnnotationsForExtensions( ext, art );
195
196
  }
196
197
  return true;
197
198
  }
@@ -207,7 +208,8 @@ function extend( model ) {
207
208
  * @param {boolean|'gen'} [noIncludes=false]
208
209
  */
209
210
  function extendArtifact( extensions, art, noIncludes = false) {
210
- if (!noIncludes && !(canApplyIncludes( art ) && extensions.every( canApplyIncludes )))
211
+ if (!noIncludes && !(canApplyIncludes( art, art ) &&
212
+ extensions.every( ext => canApplyIncludes(ext, art) )))
211
213
  return false;
212
214
  if (!art.query) {
213
215
  model._entities.push( art ); // add structure with includes in dep order
@@ -251,9 +253,9 @@ function extend( model ) {
251
253
  art.includes = [ ...ext.includes ];
252
254
  applyIncludes( ext, art );
253
255
  }
254
- if (ext.kind === 'annotate' || ext.kind === 'extend')
255
- checkAnnotate( ext, art );
256
- defineAnnotations( ext, art, ext._block, ext.kind );
256
+ checkAnnotate( ext, art );
257
+ initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
258
+ copyAnnotationsForExtensions( ext, art );
257
259
  // TODO: do we allow to add elements with array of {...}? If yes, adapt
258
260
  initMembers( ext, art, ext._block ); // might set _extend, _annotate
259
261
  storeTypeExtension( ext, art );
@@ -302,9 +304,6 @@ function extend( model ) {
302
304
  function extendColumns( ext, art ) {
303
305
  // TODO: consider reportUnstableExtensions
304
306
 
305
- for (const col of ext.columns)
306
- defineAnnotations( col, col, ext._block, ext.kind );
307
-
308
307
  const { location } = ext.name;
309
308
  const { query } = art;
310
309
  if (!query) {
@@ -392,7 +391,7 @@ function extend( model ) {
392
391
  // TODO: Future: Sub-message that points to previous extension?
393
392
  error( 'ext-invalid-type-property', [ ext[prop].location, ext ], {
394
393
  prop,
395
- literal: ext[prop].val,
394
+ value: ext[prop].val,
396
395
  number: art[prop].val,
397
396
  '#': previousSuccess[prop] ? 'ext-smaller' : 'smaller',
398
397
  } );
@@ -580,16 +579,20 @@ function extend( model ) {
580
579
  // includes ----------------------------------------------------------------
581
580
 
582
581
  /**
583
- * Returns true, if `includes` can be applied. They can't be applied if
584
- * any of the artifacts referenced in `includes` are yet to be extended.
582
+ * Returns true, if `art.includes` can be applied on `target`.
583
+ * They can't be applied if any of the artifacts referenced in
584
+ * `art.includes` are yet to be extended.
585
+ * `art !== target` if `art` is an extension.
585
586
  *
586
587
  * @param {XSN.Definition} art
588
+ * @param {XSN.Artifact} target
587
589
  * @returns {boolean}
588
590
  */
589
- function canApplyIncludes( art ) {
591
+ function canApplyIncludes( art, target ) {
590
592
  if (art.includes) {
593
+ const isView = !!target.query;
591
594
  for (const ref of art.includes) {
592
- const template = resolvePath( ref, 'include', art );
595
+ const template = resolvePath( ref, isView ? 'viewInclude' : 'include', art );
593
596
  if (template && template.name.absolute in extensionsDict)
594
597
  return false;
595
598
  }
@@ -612,20 +615,22 @@ function extend( model ) {
612
615
  */
613
616
  function applyIncludes( ext, art ) {
614
617
  if (kindProperties[art.kind].include !== true) {
615
- error('extend-unexpected-include', [ ext.includes[0]?.location, ext ], { kind: art.kind },
616
- 'Can\'t extend $(KIND) with includes');
618
+ error('extend-unexpected-include', [ ext.includes[0]?.location, ext ],
619
+ { kind: art.kind });
617
620
  return;
618
621
  }
619
622
 
620
- if (!art._ancestors)
621
- setLink( art, '_ancestors', [] ); // recursive array of includes
622
- for (const ref of ext.includes) {
623
- const template = ref._artifact;
624
- // !template -> non-includable, e.g. scalar type, or cyclic
625
- if (template) {
626
- if (template._ancestors)
627
- art._ancestors.push( ...template._ancestors );
628
- art._ancestors.push( template );
623
+ if (!art.query) {
624
+ if (!art._ancestors)
625
+ setLink( art, '_ancestors', [] ); // recursive array of includes
626
+ for (const ref of ext.includes) {
627
+ const template = ref._artifact;
628
+ // !template -> non-includable, e.g. scalar type, or cyclic
629
+ if (template) {
630
+ if (template._ancestors)
631
+ art._ancestors.push( ...template._ancestors );
632
+ art._ancestors.push( template );
633
+ }
629
634
  }
630
635
  }
631
636
  includeMembers( ext, art, 'elements' );
@@ -1272,7 +1277,7 @@ function copyPersistenceAnnotations(target, source, options) {
1272
1277
  copy( '@cds.persistence.exists' );
1273
1278
  copy( '@cds.persistence.skip' );
1274
1279
 
1275
- function copy(anno) {
1280
+ function copy( anno ) {
1276
1281
  if ( source[anno] && !target[anno] )
1277
1282
  target[anno] = { ...source[anno], $inferred: 'parent-origin' };
1278
1283
  }
@@ -1,5 +1,16 @@
1
1
  // Things which needs to done for parse.cdl after define()
2
2
 
3
+ // For transforming a single CDL file into CSN, parse.cdl resolves block-relative
4
+ // _artifact_ references (in `type`, `includes`, `target`, `targetAspect`, `from`,
5
+ // `extend`, `annotate`) to absolute names. This is the task of the export
6
+ // function of this file, which the compiler calls after the export function of
7
+ // './define.js'.
8
+
9
+ // There should be no need to do other things here – if you think there is,
10
+ // consider adding it to './define.js'. For “semantic locations” in error
11
+ // messages, we set also “structural” links and names inside `extensions` (by
12
+ // calling functions from './define.js').
13
+
3
14
  'use strict';
4
15
 
5
16
  const { dictAddArray } = require('../base/dictionaries');
@@ -18,7 +29,6 @@ function finalizeParseCdl( model ) {
18
29
  const {
19
30
  resolveUncheckedPath,
20
31
  resolveTypeArgumentsUnchecked,
21
- defineAnnotations,
22
32
  initMembers,
23
33
  extensionsDict,
24
34
  } = model.$functions;
@@ -29,20 +39,13 @@ function finalizeParseCdl( model ) {
29
39
  function resolveTypesAndExtensionsForParseCdl() {
30
40
  const extensions = [];
31
41
 
32
- // TODO: probably better to loop over extensions of all sources (there is just one)
33
42
  for (const name in extensionsDict) {
34
43
  for (const ext of extensionsDict[name]) {
35
44
  ext.name.absolute = resolveUncheckedPath( ext.name, 'extend', ext );
36
- // Define annotations of this top-level extension
37
- defineAnnotations( ext, ext, ext._block, 'extend' );
38
- mergeAnnotatesForSameArtifact( ext );
45
+ mergeAnnotatesForSameArtifact( ext ); // TODO: should not be necessary anymore
39
46
  // Initialize members and define annotations in sub-elements.
40
47
  initMembers( ext, ext, ext._block, true );
41
48
  extensions.push( ext );
42
- for (const col of ext.columns || []) {
43
- // Note, no `priority` argument, since we don't apply the extension in the end.
44
- defineAnnotations( col, col, ext._block );
45
- }
46
49
  }
47
50
  }
48
51
 
@@ -50,6 +53,8 @@ function finalizeParseCdl( model ) {
50
53
  forEachGeneric(model, 'vocabularies', art => resolveTypesForParseCdl(art, art));
51
54
 
52
55
  if (extensions.length > 0) {
56
+ // TODO: do a sort based on the location in case there were two extensions
57
+ // on the same definition?
53
58
  model.extensions = extensions;
54
59
  model.extensions.forEach(ext => resolveTypesForParseCdl(ext, ext));
55
60
  }
@@ -28,6 +28,7 @@ const {
28
28
  } = require('../base/dictionaries');
29
29
  const { dictLocation } = require('../base/location');
30
30
  const { weakLocation } = require('../base/messages');
31
+ const { CompilerAssertion } = require('../base/error');
31
32
 
32
33
  const { kindProperties } = require('./base');
33
34
  const {
@@ -35,6 +36,8 @@ const {
35
36
  setLink,
36
37
  setArtifactLink,
37
38
  annotationVal,
39
+ annotationIsFalse,
40
+ annotationLocation,
38
41
  augmentPath,
39
42
  splitIntoPath,
40
43
  linkToOrigin,
@@ -623,10 +626,10 @@ function populate( model ) {
623
626
  return '';
624
627
  const path = col.value &&
625
628
  (col.value.path || !col.value.args && col.value.func && col.value.func.path);
626
- if (path) {
629
+ if (path && path.length) {
627
630
  const last = !path.broken && path.length && path[path.length - 1];
628
631
  if (last) {
629
- col.name = { id: last.id, location: last.location, $inferred: 'as' };
632
+ col.name = { id: last.id || '', location: last.location, $inferred: 'as' };
630
633
  return col.name.id;
631
634
  }
632
635
  }
@@ -678,8 +681,8 @@ function populate( model ) {
678
681
  else if (elem._pathHead?.kind === '$inline' && elem.value.path.length === 1) {
679
682
  const hpath = elem._pathHead.value?.path;
680
683
  const head = hpath?.length === 1 && hpath[0]._navigation;
681
- // Alias .{ elem }
682
- if (head?.kind === '$tableAlias')
684
+ // Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
685
+ if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
683
686
  pushLink( head.elements[item.id], '_projections', elem );
684
687
  }
685
688
  }
@@ -1022,10 +1025,12 @@ function populate( model ) {
1022
1025
  const targetScope = definitionScope( target );
1023
1026
  if (targetScope === assocScope) { // intra-scope in model
1024
1027
  const elemScope = definitionScope( elem._main || elem );
1025
- if (targetScope === target || // unscoped target in model
1028
+ // without the if, compile.recompile.json versus expected csn.json in
1029
+ // test3/Redirections/AutoExposeDeepScoped would fail
1030
+ if (targetScope === target || // model target is scope root
1026
1031
  assocScope === assocMain || // unscoped assoc source in model
1027
1032
  elemScope !== (elem._main || elem)) // scoped assoc source in service
1028
- return elemScope; // own scope, then global
1033
+ return elemScope; // own scope, then global
1029
1034
  }
1030
1035
  if (targetScope === target) // unscoped target in model / other service
1031
1036
  return false; // all (there could be no scoped autoexposed)
@@ -1158,8 +1163,25 @@ function populate( model ) {
1158
1163
  const autoexposed = model.definitions[absolute];
1159
1164
  if (autoexposed && (autoexposed.kind !== 'namespace' || !scopedRedirections)) {
1160
1165
  if (isDirectProjection( autoexposed, target )) {
1161
- if (options.testMode)
1162
- throw new Error( `Tried to auto-expose ${ target.name.absolute } twice`);
1166
+ const anno = autoexposed['@cds.redirection.target'];
1167
+ if (annotationIsFalse( anno )) {
1168
+ // It would probably be cleaner to ignore a dubious
1169
+ // `@cds.redirection.target: false` earlier, but that is not easy to detect
1170
+ // due to the name of the autoexposed entity with scoped redirections
1171
+ if (!anno.$errorReported) {
1172
+ info( 'anno-redirecting-anyway',
1173
+ [ annotationLocation( anno ), autoexposed ],
1174
+ { target, art: absolute, code: '@cds.redirection.target: false' },
1175
+ '$(TARGET) is auto-redirected to $(ART) even with $(CODE)' );
1176
+ anno.$errorReported = 'anno-redirecting-anyway';
1177
+ }
1178
+ }
1179
+ else if (autoexposed._parent === service ||
1180
+ !annotationVal( autoexposed['@cds.autoexposed'] )) {
1181
+ // existing def not auto-exposed, or un-scoped auto-exposed: should not happen
1182
+ if (options.testMode)
1183
+ throw new CompilerAssertion( `Tried to auto-expose ${ target.name.absolute } twice`);
1184
+ }
1163
1185
  return autoexposed;
1164
1186
  }
1165
1187
  error( 'duplicate-autoexposed', [ service.name.location, service ],
@@ -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,40 +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
- step({ 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
- }
101
- else if (target.includes) {
102
- let targets = [ target ];
103
- while (targets.length) {
104
- const news = [];
105
- for (const t of targets) {
106
- for (const ref of t.includes || []) {
107
- const s = ref._artifact;
108
- if (!s) // ref error
109
- continue;
110
- chain.push( { target: t, source: s } );
111
- if (checkAndSetStatus( s ))
112
- news.push( s );
113
- }
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
+ }
100
+
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 );
114
108
  }
115
- targets = news;
116
109
  }
110
+ targets = news;
117
111
  }
112
+
118
113
  chain.reverse().forEach( step );
119
114
  runMembers( art );
120
- // 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);
121
116
  }
122
117
 
123
118
  function runMembers( art ) {
@@ -87,7 +87,8 @@ function resolve( model ) {
87
87
  const {
88
88
  resolvePath,
89
89
  checkAnnotate,
90
- defineAnnotations,
90
+ initAnnotations,
91
+ copyAnnotationsForExtensions,
91
92
  attachAndEmitValidNames,
92
93
  lateExtensions,
93
94
  applyTypeExtensions,
@@ -371,7 +372,7 @@ function resolve( model ) {
371
372
  }
372
373
  }
373
374
  if (obj.target) {
374
- // console.log(obj.name,obj._origin.name)
375
+ // console.log(obj.name,obj._origin?.name,obj)
375
376
  if (obj._origin && obj._origin.$inferred === 'REDIRECTED')
376
377
  resolveTarget( art, obj._origin );
377
378
  // console.log(error( 'test-target', [ obj.location, obj ],
@@ -521,9 +522,9 @@ function resolve( model ) {
521
522
  setArtifactLink( ext.name, art );
522
523
 
523
524
  if (art) {
524
- if (ext.kind === 'annotate' || ext.kind === 'extend')
525
- checkAnnotate( ext, art );
526
- defineAnnotations( ext, art, ext._block, ext.kind );
525
+ checkAnnotate( ext, art );
526
+ initAnnotations( ext, ext._block, ext.kind ); // TODO: do in define.js
527
+ copyAnnotationsForExtensions( ext, art );
527
528
  // eslint-disable-next-line no-shadow
528
529
  forEachMember( ext, ( elem, name, prop ) => {
529
530
  storeExtension( elem, name, prop, art, ext._block );
@@ -1079,6 +1080,11 @@ function resolve( model ) {
1079
1080
  'Only an association can be redirected' );
1080
1081
  return;
1081
1082
  }
1083
+ else if ((elem.value || elem.expand) && elem.type && !elem.type.$inferred) {
1084
+ error( 'ref-unexpected-assoc', [ elem.type.location, elem ], {},
1085
+ 'Casting to an association is not supported' );
1086
+ return;
1087
+ }
1082
1088
  // console.log(message( null, elem.location, elem, {target,art:assoc}, 'Info','RE')
1083
1089
  // .toString(), elem.value)
1084
1090
  const nav = elem._main && elem._main.query && elem.value && pathNavigation( elem.value );
@@ -12,7 +12,6 @@ const {
12
12
  setArtifactLink,
13
13
  dependsOn,
14
14
  pathName,
15
- annotationHasEllipsis,
16
15
  } = require('./utils');
17
16
 
18
17
  function artifactsEnv( art ) {
@@ -87,6 +86,11 @@ function fns( model ) {
87
86
  expectedMsgId: 'expected-struct',
88
87
  envFn: artifactsEnv,
89
88
  },
89
+ viewInclude: {
90
+ check: checkViewIncludesRef,
91
+ expectedMsgId: 'ref-expecting-bare-aspect',
92
+ envFn: artifactsEnv,
93
+ },
90
94
  target: {
91
95
  check: checkEntityRef,
92
96
  expectedMsgId: 'expected-entity',
@@ -135,15 +139,22 @@ function fns( model ) {
135
139
  dollar: true,
136
140
  rootEnv: 'elements', // the final environment for the path root
137
141
  noDep: true, // do not set dependency for circular-check
142
+ allowSelf: true,
138
143
  }, // TODO: special assoc for only on user
139
144
  'mixin-on': {
140
145
  escape: 'param', // TODO: extra check that assocs containing param in ON is not published
141
146
  next: '_$next', // TODO: lexical: ... how to find the (next) lexical environment
142
147
  dollar: true,
143
148
  noDep: true, // do not set dependency for circular-check
149
+ allowSelf: true,
144
150
  }, // TODO: special assoc for only on user
145
151
  rewrite: {
146
- next: '_$next', dollar: true, escape: 'param', noDep: true, rewrite: true,
152
+ next: '_$next',
153
+ dollar: true,
154
+ escape: 'param',
155
+ noDep: true,
156
+ allowSelf: true,
157
+ rewrite: true,
147
158
  }, // TODO: assertion that there is no next/escape used
148
159
  'order-by': {
149
160
  next: '_$next',
@@ -153,7 +164,11 @@ function fns( model ) {
153
164
  deprecatedSourceRefs: true,
154
165
  },
155
166
  'order-by-union': {
156
- next: '_$next', dollar: true, escape: 'param', noDep: true, noExt: true,
167
+ next: '_$next',
168
+ dollar: true,
169
+ escape: 'param',
170
+ noDep: true,
171
+ noExt: true,
157
172
  },
158
173
  // expr TODO: better - on condition for assoc, other on
159
174
  // expr TODO: write dependency, but care for $self
@@ -169,7 +184,7 @@ function fns( model ) {
169
184
  resolveTypeArgumentsUnchecked,
170
185
  resolvePath,
171
186
  checkAnnotate,
172
- defineAnnotations,
187
+ copyAnnotationsForExtensions,
173
188
  attachAndEmitValidNames,
174
189
  } );
175
190
  return;
@@ -183,7 +198,22 @@ function fns( model ) {
183
198
  // - derived structure types: would have to follow type in extend/include;
184
199
  // - entities with params: clarify inheritance, use of param in ON/DEFAULT;
185
200
  // - query entities/events: difficult sequence of resolve steps
186
- return !(art.elements && !art.query && !art.type && !art.params);
201
+ // - aspect without elements (useful for actions/annotations)
202
+ return !(art.elements && !art.query && !art.type && !art.params) && art.kind !== 'aspect';
203
+ }
204
+
205
+ /**
206
+ * Returns true, if the given artifact can be included by a query entity / view.
207
+ *
208
+ * We currently allow:
209
+ * - aspects without elements (the aspect may have actions):
210
+ * either no `elements` property or empty dictionary
211
+ *
212
+ * @param {XSN.Artifact} art
213
+ * @return {boolean}
214
+ */
215
+ function checkViewIncludesRef( art ) {
216
+ return !(art.kind === 'aspect' && (!art.elements || Object.keys(art.elements).length === 0));
187
217
  }
188
218
 
189
219
  /**
@@ -331,7 +361,8 @@ function fns( model ) {
331
361
  return setArtifactLink( ref, art );
332
362
  }
333
363
  else if (!spec.envFn && user._pathHead) {
334
- // eslint-disable-next-line no-empty
364
+ if (art.kind === '$self')
365
+ rejectBareSelf( spec, path, user, extDict );
335
366
  }
336
367
  else if (art.kind === 'using') {
337
368
  const def = model.definitions[art.name.absolute];
@@ -374,7 +405,10 @@ function fns( model ) {
374
405
  // TODO: set art?
375
406
  }
376
407
  else if (art.kind === '$tableAlias' || art.kind === '$self') {
377
- if (spec.noAliasOrMixin && art.kind !== '$self') { // TODO: extra kind $self?
408
+ if (art.kind === '$self') {
409
+ rejectBareSelf( spec, path, user, extDict );
410
+ }
411
+ else if (spec.noAliasOrMixin) {
378
412
  // TODO: good enough for now - change later to not search for table aliases at all
379
413
  signalNotFound( 'ref-rejected-on', [ head.location, user ], extDict && [ extDict ],
380
414
  { '#': 'alias', id: head.id } );
@@ -447,6 +481,15 @@ function fns( model ) {
447
481
  return setArtifactLink( ref, art );
448
482
  }
449
483
 
484
+ function rejectBareSelf( spec, path, user, extDict ) {
485
+ if (path.length === 1 && !spec.allowSelf && !user.expand && !user.inline) {
486
+ const head = path[0];
487
+ // TODO: extra text variant for JOIN-ON (if we have an extra `expected`)
488
+ signalNotFound( 'ref-unexpected-self', [ head.location, user ], extDict && [ extDict ],
489
+ { id: head.id } );
490
+ }
491
+ }
492
+
450
493
  // Issue errors for "smart" element-in-artifact references
451
494
  // without a colon; and errors for misplaced colons in references.
452
495
  // This function likely disappears again in cds-compiler v2.x.
@@ -670,8 +713,7 @@ function fns( model ) {
670
713
  { art: searchName( msgArt, head.id, 'element' ) } );
671
714
  }
672
715
  else {
673
- signalNotFound( 'ref-undefined-var', [ head.location, user ], valid, { id: head.id },
674
- 'Element or variable $(ID) has not been found' );
716
+ signalNotFound( 'ref-undefined-var', [ head.location, user ], valid, { id: head.id } );
675
717
  }
676
718
  }
677
719
  else if (env.$frontend && env.$frontend !== 'cdl' || spec.global) {
@@ -804,13 +846,11 @@ function fns( model ) {
804
846
  // TODO: views elements are proxies to query-0 elements, not the same
805
847
  // TODO: better message text
806
848
  signalNotFound( 'query-undefined-element', [ item.location, user ],
807
- [ env ], { id: item.id },
808
- 'Element $(ID) has not been found in the elements of the query' );
849
+ [ env ], { id: item.id } );
809
850
  }
810
851
  else if (art.kind === '$parameters') {
811
852
  signalNotFound( 'ref-undefined-param', [ item.location, user ],
812
- [ env ], { art: searchName( art._main, item.id, 'param' ) },
813
- { param: 'Entity $(ART) has no parameter $(MEMBER)' } );
853
+ [ env ], { art: art._main, id: item.id } );
814
854
  }
815
855
  else {
816
856
  const variant = art.kind === 'aspect' && !art.name && 'aspect';
@@ -832,14 +872,13 @@ function fns( model ) {
832
872
  * @param {any} location
833
873
  * @param {object[]} valid
834
874
  * @param {object} [textParams]
835
- * @param {any} [texts]
836
875
  */
837
- function signalNotFound(msgId, location, valid, textParams, texts ) {
876
+ function signalNotFound(msgId, location, valid, textParams ) {
838
877
  if (location.$notFound)
839
878
  return;
840
879
  location.$notFound = true;
841
880
  /** @type {object} */
842
- const err = message( msgId, location, textParams, texts );
881
+ const err = message( msgId, location, textParams );
843
882
  if (valid)
844
883
  attachAndEmitValidNames(err, ...valid.reverse());
845
884
  }
@@ -924,44 +963,23 @@ function fns( model ) {
924
963
  }
925
964
  }
926
965
 
927
- // Set _block links for annotations (necessary for layering).
928
- // Also copy annotations from `construct` to `art` (TODO: separate that functionality).
929
- function defineAnnotations( construct, art, block, priority = false ) {
930
- if (construct.doc)
931
- art.doc = construct.doc; // e.g. through `extensions` array in CSN
932
-
933
- // set _block (for layering) and $priority, shallow-copy from extension
934
- // TODO: think of removing $priority, then
935
- // no _block: define, _block: annotate/extend/edmx
936
- // would fit with extending defs with props like length
937
- for (const annoProp in construct) {
966
+ // Copy annotations from `ext` to `art`, overwriting inferred ones.
967
+ // TODO: move to extend.js if not used anymore in define.js
968
+ function copyAnnotationsForExtensions( ext, art ) {
969
+ if (ext.doc)
970
+ art.doc = ext.doc; // e.g. through `extensions` array in CSN
971
+ for (const annoProp in ext) {
938
972
  if (annoProp.charAt(0) === '@') {
939
- let annos = construct[annoProp];
940
- if (!(Array.isArray(annos)))
941
- annos = [ annos ];
942
- for (const a of annos) {
943
- setLink( a, '_block', block );
944
- a.$priority = priority; // is now: undefined (auto-set) | false | 'annotate' | 'extend'
945
- if (construct !== art)
946
- addAnnotation( art, annoProp, a );
947
- if (!priority && annotationHasEllipsis( a )) {
948
- error( 'anno-unexpected-ellipsis',
949
- [ a.name.location, art ], { code: '...' } );
950
- }
951
- }
973
+ const extAnno = ext[annoProp];
974
+ if (art[annoProp]?.$inferred)
975
+ art[annoProp] = extAnno; // overwrite $inferred annos
976
+ else
977
+ dictAddArray( art, annoProp, extAnno );
952
978
  }
953
979
  }
954
980
  }
955
981
  }
956
982
 
957
- // Add annotation to definition - overwriting $inferred annos
958
- function addAnnotation( art, annoProp, anno ) {
959
- const old = art[annoProp];
960
- if (old && old.$inferred)
961
- delete art[annoProp];
962
- dictAddArray( art, annoProp, anno );
963
- }
964
-
965
983
  module.exports = {
966
984
  fns,
967
985
  };
@@ -484,9 +484,8 @@ function tweakAssocs( model ) {
484
484
  if (elem && !Array.isArray(elem))
485
485
  return elem;
486
486
  // TODO: better (extra message), TODO: do it
487
- error( 'query-undefined-element', [ item.location, assoc ], { id: name || item.id },
488
- // eslint-disable-next-line max-len
489
- 'Element $(ID) has not been found in the elements of the query; please use REDIRECTED TO with an explicit ON-condition' );
487
+ error( 'query-undefined-element', [ item.location, assoc ],
488
+ { id: name || item.id, '#': 'redirected' } );
490
489
  return (elem) ? false : null;
491
490
  }
492
491
  }
@@ -35,6 +35,16 @@ function annotationHasEllipsis( anno ) {
35
35
  const { val } = anno || {};
36
36
  return Array.isArray( val ) && val.find( v => v.literal === 'token' && v.val === '...' );
37
37
  }
38
+ function annotationLocation( anno ) {
39
+ const { name, location } = anno;
40
+ return {
41
+ file: name.location.file,
42
+ line: name.location.line,
43
+ col: name.location.col,
44
+ endLine: location.endLine,
45
+ endCol: location.endCol,
46
+ };
47
+ }
38
48
 
39
49
  /**
40
50
  * Set compiler-calculated annotation value.
@@ -395,6 +405,7 @@ module.exports = {
395
405
  annotationVal,
396
406
  annotationIsFalse,
397
407
  annotationHasEllipsis,
408
+ annotationLocation,
398
409
  annotateWith,
399
410
  setLink,
400
411
  setArtifactLink,