@sap/cds-compiler 4.7.6 → 4.9.0

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 (129) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +28 -1
  6. package/bin/cdshi.js +13 -3
  7. package/doc/CHANGELOG_BETA.md +24 -1
  8. package/lib/api/main.js +119 -46
  9. package/lib/api/options.js +51 -0
  10. package/lib/api/validate.js +1 -5
  11. package/lib/base/builtins.js +116 -0
  12. package/lib/base/keywords.js +5 -1
  13. package/lib/base/location.js +91 -14
  14. package/lib/base/message-registry.js +76 -46
  15. package/lib/base/messages.js +121 -35
  16. package/lib/base/model.js +4 -7
  17. package/lib/checks/actionsFunctions.js +3 -3
  18. package/lib/checks/annotationsOData.js +3 -0
  19. package/lib/checks/defaultValues.js +5 -2
  20. package/lib/checks/elements.js +2 -1
  21. package/lib/checks/enricher.js +2 -2
  22. package/lib/checks/queryNoDbArtifacts.js +5 -3
  23. package/lib/checks/utils.js +1 -1
  24. package/lib/checks/validator.js +8 -56
  25. package/lib/compiler/assert-consistency.js +11 -7
  26. package/lib/compiler/builtins.js +0 -74
  27. package/lib/compiler/checks.js +105 -29
  28. package/lib/compiler/define.js +37 -25
  29. package/lib/compiler/extend.js +35 -12
  30. package/lib/compiler/index.js +9 -10
  31. package/lib/compiler/lsp-api.js +5 -0
  32. package/lib/compiler/populate.js +13 -5
  33. package/lib/compiler/propagator.js +24 -18
  34. package/lib/compiler/resolve.js +47 -45
  35. package/lib/compiler/shared.js +61 -21
  36. package/lib/compiler/tweak-assocs.js +15 -90
  37. package/lib/compiler/utils.js +3 -3
  38. package/lib/compiler/xpr-rewrite.js +689 -0
  39. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  40. package/lib/edm/annotations/edmJson.js +7 -5
  41. package/lib/edm/annotations/genericTranslation.js +149 -71
  42. package/lib/edm/csn2edm.js +25 -9
  43. package/lib/edm/edm.js +7 -7
  44. package/lib/edm/edmInboundChecks.js +57 -5
  45. package/lib/edm/edmPreprocessor.js +54 -25
  46. package/lib/edm/edmUtils.js +3 -16
  47. package/lib/gen/Dictionary.json +138 -14
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +1 -1
  50. package/lib/gen/languageParser.js +2085 -1989
  51. package/lib/json/csnVersion.js +7 -4
  52. package/lib/json/from-csn.js +21 -11
  53. package/lib/json/to-csn.js +8 -4
  54. package/lib/language/antlrParser.js +1 -1
  55. package/lib/language/genericAntlrParser.js +23 -16
  56. package/lib/language/multiLineStringParser.js +2 -2
  57. package/lib/language/textUtils.js +1 -1
  58. package/lib/main.d.ts +90 -14
  59. package/lib/main.js +9 -1
  60. package/lib/model/cloneCsn.js +21 -9
  61. package/lib/model/csnRefs.js +153 -42
  62. package/lib/model/csnUtils.js +14 -11
  63. package/lib/model/enrichCsn.js +4 -2
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/model/sortViews.js +14 -6
  66. package/lib/modelCompare/compare.js +135 -122
  67. package/lib/optionProcessor.js +49 -2
  68. package/lib/render/DuplicateChecker.js +6 -6
  69. package/lib/render/manageConstraints.js +1 -0
  70. package/lib/render/toCdl.js +6 -3
  71. package/lib/render/toHdbcds.js +4 -48
  72. package/lib/render/toSql.js +6 -3
  73. package/lib/transform/addTenantFields.js +58 -35
  74. package/lib/transform/db/applyTransformations.js +34 -1
  75. package/lib/transform/db/constraints.js +1 -1
  76. package/lib/transform/db/expansion.js +11 -3
  77. package/lib/transform/db/flattening.js +71 -46
  78. package/lib/transform/db/groupByOrderBy.js +2 -2
  79. package/lib/transform/db/temporal.js +6 -3
  80. package/lib/transform/db/transformExists.js +2 -2
  81. package/lib/transform/db/views.js +1 -4
  82. package/lib/transform/effective/annotations.js +194 -0
  83. package/lib/transform/effective/main.js +11 -10
  84. package/lib/transform/effective/misc.js +45 -14
  85. package/lib/transform/effective/types.js +4 -3
  86. package/lib/transform/forOdata.js +29 -12
  87. package/lib/transform/forRelationalDB.js +104 -113
  88. package/lib/transform/localized.js +7 -6
  89. package/lib/transform/odata/flattening.js +228 -107
  90. package/lib/transform/odata/toFinalBaseType.js +10 -26
  91. package/lib/transform/odata/typesExposure.js +41 -25
  92. package/lib/transform/parseExpr.js +4 -7
  93. package/lib/transform/transformUtils.js +50 -43
  94. package/lib/transform/translateAssocsToJoins.js +48 -48
  95. package/lib/transform/universalCsn/coreComputed.js +2 -1
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  97. package/package.json +2 -2
  98. package/share/messages/README.md +4 -0
  99. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  100. package/share/messages/anno-missing-rewrite.md +45 -0
  101. package/share/messages/check-proper-type-of.md +1 -1
  102. package/share/messages/def-duplicate-autoexposed.md +1 -1
  103. package/share/messages/extend-repeated-intralayer.md +3 -16
  104. package/share/messages/extend-unrelated-layer.md +1 -1
  105. package/share/messages/message-explanations.json +2 -0
  106. package/share/messages/redirected-to-ambiguous.md +1 -1
  107. package/share/messages/redirected-to-complex.md +1 -1
  108. package/share/messages/redirected-to-unrelated.md +1 -1
  109. package/share/messages/rewrite-not-supported.md +1 -1
  110. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  111. package/share/messages/type-missing-enum-value.md +59 -0
  112. package/share/messages/wildcard-excluding-one.md +1 -1
  113. package/bin/.eslintrc.json +0 -17
  114. package/lib/api/.eslintrc.json +0 -37
  115. package/lib/checks/.eslintrc.json +0 -31
  116. package/lib/compiler/.eslintrc.json +0 -8
  117. package/lib/edm/.eslintrc.json +0 -46
  118. package/lib/inspect/.eslintrc.json +0 -4
  119. package/lib/json/.eslintrc.json +0 -4
  120. package/lib/language/.eslintrc.json +0 -4
  121. package/lib/model/.eslintrc.json +0 -13
  122. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  123. package/lib/render/.eslintrc.json +0 -22
  124. package/lib/transform/.eslintrc.json +0 -13
  125. package/lib/transform/db/.eslintrc.json +0 -41
  126. package/lib/transform/draft/.eslintrc.json +0 -4
  127. package/lib/transform/effective/.eslintrc.json +0 -4
  128. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  129. package/lib/utils/.eslintrc.json +0 -7
@@ -9,6 +9,7 @@ const {
9
9
  forEachDefinition,
10
10
  forEachMember,
11
11
  isDeprecatedEnabled,
12
+ isBetaEnabled,
12
13
  } = require('../base/model');
13
14
  const { dictAdd, pushToDict } = require('../base/dictionaries');
14
15
  const { kindProperties, dictKinds } = require('./base');
@@ -25,12 +26,12 @@ const {
25
26
  } = require('./utils');
26
27
  const layers = require('./moduleLayers');
27
28
  const { CompilerAssertion } = require('../base/error');
28
- const { CsnLocation } = require('./classes');
29
+ const { Location } = require('../base/location');
29
30
 
30
31
  const $location = Symbol.for( 'cds.$location' );
31
32
 
32
33
  // attach stupid location - TODO: remove in v5
33
- const genLocation = new CsnLocation( '' );
34
+ const genLocation = new Location( '' );
34
35
 
35
36
  const draftElements = [
36
37
  'IsActiveEntity',
@@ -71,6 +72,7 @@ function extend( model ) {
71
72
  } );
72
73
 
73
74
  const includesNonShadowedFirst = isDeprecatedEnabled( model.options, 'includesNonShadowedFirst' );
75
+ const isV5preview = isBetaEnabled( model.options, 'v5preview' );
74
76
 
75
77
  sortModelSources();
76
78
  const extensionsDict = Object.create( null ); // TODO TMP
@@ -222,6 +224,7 @@ function extend( model ) {
222
224
  code: 'extend … with definitions',
223
225
  keyword: 'extend service',
224
226
  };
227
+ // TODO(v5): Discuss: make this an error?
225
228
  warning( 'ext-invalid-kind', [ loc, ext ], msgArgs, {
226
229
  std: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) instead',
227
230
  annotate: 'There is no artifact $(ART), use $(CODE) instead',
@@ -818,7 +821,7 @@ function extend( model ) {
818
821
  annotate[prop] = art[prop];
819
822
  }
820
823
  }
821
- if (extensions.length === 1) { // i.e. no proper location if from more than one extensions
824
+ if (extensions.length === 1) { // i.e. no proper location if from more than one extension
822
825
  annotate.location = extensions[0].location;
823
826
  annotate.name.location = extensions[0].name.location;
824
827
  }
@@ -829,18 +832,38 @@ function extend( model ) {
829
832
  }
830
833
 
831
834
  function checkRemainingMainExtensions( art, ext, localized ) {
832
- if (localized) // TODO v5: ignore only for annotate
835
+ if (localized) {
836
+ if (isV5preview && ext.kind === 'extend') {
837
+ // In v5, reject any `extend` on localized.
838
+ error( 'ref-undefined-art', [ ext.location || ext.name.location, ext ],
839
+ { '#': 'localized', keyword: 'annotate' } );
840
+ }
833
841
  return;
842
+ }
843
+
834
844
  if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
835
845
  return;
836
- if (art?.kind === 'namespace') {
837
- // TODO: not at all different to having no definition
838
- info( 'anno-namespace', [ ext.name.location, ext ], {}, // TODO: better location?
839
- 'Namespaces can\'t be annotated' );
840
- }
841
- else if (art?.builtin) {
842
- info( 'anno-builtin', [ ext.name.location, ext ], {}, // TODO: better location?
843
- 'Builtin types should not be annotated. Use custom type instead' );
846
+
847
+ if (art?.builtin) {
848
+ info( 'anno-builtin', [ ext.name.location, ext ], {} ); // TODO: better location?
849
+ }
850
+ else if (art?.kind === 'namespace') {
851
+ const hasAnnotations = Object.keys(ext).find(a => a.charAt(0) === '@');
852
+ const firstAnno = ext[hasAnnotations];
853
+ // In v5, extending namespaces is only allowed for `extend with definitions`.
854
+ // Neither annotations nor other extensions are allowed.
855
+ // Non-artifact extensions are reported in resolvePath() already (for v5).
856
+ if ((hasAnnotations || !ext.artifacts) ) {
857
+ if (isV5preview) {
858
+ error( 'ref-undefined-art', [ (firstAnno?.name || ext.name).location, ext ], {
859
+ '#': 'namespace', art: ext,
860
+ } );
861
+ }
862
+ else {
863
+ info( 'anno-namespace', [ (firstAnno?.name || ext.name).location, ext ], {},
864
+ 'Namespaces can\'t be annotated nor extended' );
865
+ }
866
+ }
844
867
  }
845
868
  }
846
869
 
@@ -30,7 +30,7 @@ const tweakAssocs = require('./tweak-assocs');
30
30
  const propagator = require('./propagator');
31
31
  const check = require('./checks');
32
32
 
33
- const { emptyWeakLocation } = require('../base/location');
33
+ const { Location, emptyWeakLocation } = require('../base/location');
34
34
  const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
35
35
  const { checkRemovedDeprecatedFlags } = require('../base/model');
36
36
  const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
@@ -38,7 +38,7 @@ const { cdsFs } = require('../utils/file');
38
38
 
39
39
  const fs = require('fs');
40
40
  const path = require('path');
41
- const { CsnLocation, XsnSource } = require('./classes');
41
+ const { XsnSource } = require('./xsn-model');
42
42
 
43
43
  const extensionParsers = {
44
44
  csn: parseCsn.parse,
@@ -56,7 +56,6 @@ class InvocationError extends Error {
56
56
  super( ...args );
57
57
  this.code = 'ERR_CDS_COMPILER_INVOCATION';
58
58
  this.errors = errs;
59
- this.hasBeenReported = false;
60
59
  }
61
60
  }
62
61
 
@@ -93,7 +92,7 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
93
92
  return parser( source, filename, options, messageFunctions );
94
93
 
95
94
  const model = new XsnSource();
96
- model.location = new CsnLocation( filename );
95
+ model.location = new Location( filename );
97
96
  messageFunctions.error( 'file-unknown-ext', emptyWeakLocation( filename ),
98
97
  { file: ext, '#': !ext && 'none' }, {
99
98
  std: 'Unknown file extension $(FILE)',
@@ -174,12 +173,12 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
174
173
  return []; // no further dependency processing
175
174
  // no parallel readAndParse with same resolved filename should read the file,
176
175
  // also ensure deterministic sequence in sources:
177
- sources[filename] = { location: new CsnLocation( rel ) };
176
+ sources[filename] = { location: new Location( rel ) };
178
177
 
179
178
  const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
180
179
  const ast = parseX( source, rel, options, model.$messageFunctions );
181
180
  sources[filename] = ast;
182
- ast.location = new CsnLocation( rel );
181
+ ast.location = new Location( rel );
183
182
  ast.dirname = path.dirname( filename );
184
183
  assertConsistency( ast, options );
185
184
 
@@ -293,7 +292,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
293
292
  }
294
293
  // no parallel readAndParse with same resolved filename should read the file,
295
294
  // also ensure deterministic sequence in a.sources:
296
- a.sources[filename] = { location: new CsnLocation( rel ) };
295
+ a.sources[filename] = { location: new Location( rel ) };
297
296
 
298
297
  cdsFs( fileCache, options.traceFs ).readFileSync( filename, 'utf8', (err, source) => {
299
298
  if (err) {
@@ -303,7 +302,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
303
302
  try {
304
303
  const ast = parseX( source, rel, options, model.$messageFunctions );
305
304
  a.sources[filename] = ast;
306
- ast.location = new CsnLocation( rel );
305
+ ast.location = new Location( rel );
307
306
  ast.dirname = path.dirname( filename );
308
307
  assertConsistency( ast, options );
309
308
  cb( null, ast );
@@ -374,7 +373,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
374
373
  if (typeof source === 'string') {
375
374
  const ast = parseX( source, filename, options, model.$messageFunctions );
376
375
  sources[filename] = ast;
377
- ast.location = new CsnLocation( filename );
376
+ ast.location = new Location( filename );
378
377
  assertConsistency( ast, options );
379
378
  }
380
379
  else if (options.$xsnObjects) { // source is a XSN object with option $xsnObjects
@@ -383,7 +382,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
383
382
  else { // source is a CSN object
384
383
  const ast = parseCsn.augment( source, filename, options, model.$messageFunctions );
385
384
  sources[filename] = ast;
386
- ast.location = new CsnLocation( filename );
385
+ ast.location = new Location( filename );
387
386
  assertConsistency( ast, options );
388
387
  }
389
388
 
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ // Placeholder for future LSP API.
4
+
5
+ module.exports = {};
@@ -633,8 +633,14 @@ function populate( model ) {
633
633
  }
634
634
 
635
635
  function resolveExcluding( name, env, excludingDict, user ) {
636
- if (env[name])
636
+ const found = env[name];
637
+ if (found) { // set links for LSP; if Array, then via multiple query sources ($navElement)
638
+ const art = (Array.isArray(found) && found.map(f => f._origin)) ||
639
+ (found.kind === '$navElement' && found._origin) ||
640
+ found;
641
+ setArtifactLink( excludingDict[name].name, art );
637
642
  return;
643
+ }
638
644
  /** @type {object} */
639
645
  // console.log(name,Object.keys(env),Object.keys(excludingDict))
640
646
  const compileMessageRef = info(
@@ -745,7 +751,6 @@ function populate( model ) {
745
751
  location: col.value.location || col.location,
746
752
  $inferred: '$internal',
747
753
  };
748
- col.$inferred = '$internal';
749
754
  return col.name.id;
750
755
  }
751
756
  // invent a name for code completion in expression, see also #10596
@@ -880,9 +885,12 @@ function populate( model ) {
880
885
  if (envParent || query.kind !== 'select') {
881
886
  // already done in populateQuery (TODO: change that and check whether
882
887
  // `*` is allowed at all in definer)
883
- const user = colParent || query;
884
- for (const name in user.excludingDict)
885
- resolveExcluding( name, env, excludingDict, query );
888
+ if (!colParent || colParent.value._artifact) {
889
+ // avoid "not found" messages if pathHead can't be found
890
+ const user = colParent || query;
891
+ for (const name in user.excludingDict)
892
+ resolveExcluding( name, env, excludingDict, query );
893
+ }
886
894
  }
887
895
  }
888
896
 
@@ -13,36 +13,23 @@ const {
13
13
  forEachMember,
14
14
  forEachGeneric,
15
15
  isDeprecatedEnabled,
16
+ isBetaEnabled,
16
17
  } = require( '../base/model');
17
18
  const {
18
19
  setLink,
19
20
  linkToOrigin,
20
21
  withAssociation,
21
22
  viewFromPrimary,
23
+ copyExpr,
22
24
  } = require('./utils');
25
+ const { propagationRules } = require('../base/builtins');
23
26
  const $inferred = Symbol.for( 'cds.$inferred' );
27
+ const { xprRewriteFns } = require('./xpr-rewrite');
24
28
  // const { ref } = require( '../model/revealInternalProperties' )
25
29
 
26
30
  // Note that propagation here is also used for deep-copying (function `onlyViaParent`)
27
31
  function propagate( model ) {
28
32
  const props = {
29
- '@com.sap.gtt.core.CoreModel.Indexable': never,
30
- '@cds.persistence.exists': never, // also copied in generate.js
31
- '@cds.persistence.table': never,
32
- '@cds.persistence.calcview': never,
33
- '@cds.persistence.udf': never,
34
- '@cds.persistence.skip': notWithPersistenceTable, // also copied in generate.js
35
- // '@cds.tenant.independent' is propagated as normal, but also copied in generate.js
36
- '@sql.append': never,
37
- '@sql.prepend': never,
38
- '@sql.replace': never,
39
- '@Analytics.hidden': never,
40
- '@Analytics.visible': never,
41
- '@cds.autoexpose': onlyViaArtifact,
42
- '@cds.autoexposed': never, // in case people set it themselves
43
- '@cds.external': never,
44
- '@cds.redirection.target': never,
45
- '@fiori.draft.enabled': onlyViaArtifact,
46
33
  '@': annotation, // always except in 'items' (and parameters for entity return types)
47
34
  doc: annotation, // always except in 'items' (and parameters for entity return types)
48
35
  default: withKind, // always except in 'items'
@@ -71,12 +58,24 @@ function propagate( model ) {
71
58
  returns,
72
59
  $filtered: annotation,
73
60
  };
61
+ const ruleToFunction = {
62
+ __proto__: null,
63
+ never,
64
+ onlyViaArtifact,
65
+ notWithPersistenceTable,
66
+ };
67
+ for (const rule in propagationRules)
68
+ props[rule] = ruleToFunction[propagationRules[rule]];
69
+
74
70
  const { options } = model;
71
+ const { rewriteAnnotationsRefs } = xprRewriteFns( model );
75
72
  // eslint-disable-next-line max-len
76
73
  const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
77
74
  const { warning, throwWithError } = model.$messageFunctions;
78
75
 
79
76
  forEachDefinition( model, run );
77
+ if (isBetaEnabled( model.options, 'v5preview' ))
78
+ forEachGeneric( model, 'vocabularies', run );
80
79
 
81
80
  // TODO: move 'virtual' handling/checks to resolver if
82
81
  // 'deprecated.oldVirtualNotNullPropagation' is gone
@@ -100,7 +99,7 @@ function propagate( model ) {
100
99
  const news = [];
101
100
  for (const target of targets) {
102
101
  const origin = getOrigin( target );
103
- if (origin) {
102
+ if (origin && origin.kind !== '$self') {
104
103
  // Calculated elements that are simple references: `calc = field;`.
105
104
  // Respect sibling properties in inheritance.
106
105
  if (target._calcOrigin?._origin && target.value?._artifact) {
@@ -199,6 +198,10 @@ function propagate( model ) {
199
198
  target[prop] = [ ...val ];
200
199
  target[prop].$inferred = 'prop';
201
200
  }
201
+ else if (prop.charAt(0) === '@' && val?.kind === '$annotation') {
202
+ target[prop] = Object.assign( copyExpr( val ), { $inferred: 'prop' } );
203
+ rewriteAnnotationsRefs( target, source, prop );
204
+ }
202
205
  else {
203
206
  target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );
204
207
  if (val._artifact !== undefined)
@@ -267,6 +270,7 @@ function propagate( model ) {
267
270
  target[prop][$inferred] = 'prop';
268
271
  }
269
272
 
273
+ // Only propagate if parent object (which is not necessarily `_parent`) was propagated.
270
274
  function onlyViaParent( prop, target, source ) {
271
275
  if (target.$inferred === 'proxy' || target.$inferred === 'expanded')
272
276
  // assocs and enums do not have 'include'
@@ -276,6 +280,8 @@ function propagate( model ) {
276
280
  function targetAspect( prop, target, source ) {
277
281
  if (target.targetAspect)
278
282
  return;
283
+ if (target.type?._artifact === model.definitions['cds.Association'])
284
+ return; // don't propagate targetAspect to associations (e.g. via $filtered)
279
285
  const ta = source.targetAspect;
280
286
  if (!ta.elements && !ta._origin) { // _origin set for elements in source
281
287
  notWithExpand( prop, target, source );
@@ -44,7 +44,6 @@ const {
44
44
  forEachGeneric,
45
45
  forEachInOrder,
46
46
  isDeprecatedEnabled,
47
- isBetaEnabled,
48
47
  } = require('../base/model');
49
48
  const { dictAdd } = require('../base/dictionaries');
50
49
  const { dictLocation, weakLocation } = require('../base/location');
@@ -126,6 +125,11 @@ function resolve( model ) {
126
125
  // Phase 4: resolve all artifacts:
127
126
  forEachDefinition( model, resolveRefs );
128
127
  forEachGeneric( model, 'vocabularies', resolveRefs );
128
+ if (model.options.lspMode) {
129
+ for (const name in model.sources)
130
+ resolveDefinitionName( model.sources[name].namespace );
131
+ }
132
+
129
133
  // create “super” ANNOTATE statements for annotations on unknown artifacts:
130
134
  createRemainingAnnotateStatements();
131
135
  // report cyclic dependencies:
@@ -363,6 +367,9 @@ function resolve( model ) {
363
367
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
364
368
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
365
369
 
370
+ if (model.options.lspMode && art.name && !art._main)
371
+ resolveDefinitionName( art );
372
+
366
373
  // Check KEY (TODO: make this an extra function)
367
374
  const { key } = art;
368
375
  if (key?.val && !key.$inferred) {
@@ -519,8 +526,6 @@ function resolve( model ) {
519
526
  if (art.value) {
520
527
  if (art.$syntax === 'calc')
521
528
  checkCalculatedElement( art );
522
- else if (art.type && !art.$inferred)
523
- checkColumnTypeCast( art );
524
529
  }
525
530
 
526
531
  resolveExprInAnnotations( art );
@@ -559,10 +564,12 @@ function resolve( model ) {
559
564
  const sType = specifiedElement.type?._artifact;
560
565
  const iTypeArt = getInferredPropFromOrigin( 'type' )?._artifact;
561
566
  const iType = iTypeArt || inferredElement;
562
-
563
- // xor: could be missing a type;
564
567
  // FIXME: The coding above returns incorrect iType for expand on associations
565
568
 
569
+ // $filtered may change composition to association; we allow that change here.
570
+ const compToAssoc = sType === model.definitions['cds.Association'] && inferredElement.target;
571
+
572
+ // xor: could be missing a type;
566
573
  if (!specifiedElement.type && inferredElement.type) {
567
574
  error( 'query-mismatched-element', [ specifiedElement.location, user ], {
568
575
  '#': !specifiedElement.type ? 'missing' : 'extra', name: user.name.id, prop: 'type',
@@ -570,7 +577,7 @@ function resolve( model ) {
570
577
  return;
571
578
  }
572
579
  // If specified type is `null`, type could not be resolved.
573
- else if (sType && sType !== iType &&
580
+ else if (!compToAssoc && sType && sType !== iType &&
574
581
  // Special case for $recompilation: allow one level of type indirection. See #12113.
575
582
  (!model.options.$recompile || sType !== iType.type?._artifact)) {
576
583
  const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
@@ -849,21 +856,6 @@ function resolve( model ) {
849
856
  }
850
857
  }
851
858
 
852
- function checkColumnTypeCast( art ) {
853
- const elem = (art.value.op?.val === 'cast')
854
- ? art.value.args[0]?._artifact
855
- : art.value._artifact;
856
- if (elem && art.type) { // has explicit type
857
- if (art.type._artifact?.elements)
858
- error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'to-structure' } );
859
- else if (elem.elements) // TODO: calc elements
860
- error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'from-structure' } );
861
- else if (elem.target && !art.type._artifact?.target)
862
- // allow cast to association -> that is already checked and denied elsewhere
863
- error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'from-assoc' } );
864
- }
865
- }
866
-
867
859
  /**
868
860
  * Return type containing the assoc spec (keys, on); note that no
869
861
  * propagation/rewrite has been done yet, cyclic dependency must have been
@@ -1223,8 +1215,9 @@ function resolve( model ) {
1223
1215
  'Only an association can be redirected' );
1224
1216
  return;
1225
1217
  }
1226
- else if ((elem.value || elem.expand) && elem.type && !elem.type.$inferred) {
1227
- error( 'ref-unexpected-assoc', [ elem.type.location, elem ], { '#': 'cast' } );
1218
+ else if ((elem.value || elem.expand) && elem.type &&
1219
+ (!elem.type.$inferred || elem.type.$inferred === 'cast')) {
1220
+ error( 'type-invalid-cast', [ elem.type.location, elem ], { '#': 'assoc' } );
1228
1221
  return;
1229
1222
  }
1230
1223
  // console.log(message( null, elem.location, elem, {target,art:assoc}, 'Info','RE')
@@ -1364,6 +1357,19 @@ function resolve( model ) {
1364
1357
  // General resolver functions
1365
1358
  //--------------------------------------------------------------------------
1366
1359
 
1360
+ // Resolve the n-1 path steps before the definition name for LSP.
1361
+ function resolveDefinitionName( art ) {
1362
+ const path = art?.name?.path;
1363
+ if (!art || art._main || !path || path.length <= 1)
1364
+ return;
1365
+
1366
+ let name = art.name.id;
1367
+ for (let i = path.length - 1; i > 0; --i) {
1368
+ name = name.substring(0, name.length - path[i].id.length - 1);
1369
+ setArtifactLink( path[i - 1], model.definitions[name] || false );
1370
+ }
1371
+ }
1372
+
1367
1373
  // Resolve the type and its arguments if applicable.
1368
1374
  function resolveTypeExpr( art, user ) {
1369
1375
  const typeArt = resolvePath( art.type, 'type', user );
@@ -1383,7 +1389,6 @@ function resolve( model ) {
1383
1389
  if (!anno.kind)
1384
1390
  initAnnotationForExpression( anno, art );
1385
1391
  resolveExpr( expr, 'annotation', anno );
1386
- reportUnsupportedAnnoExpr( expr );
1387
1392
  }
1388
1393
  else if (expr.literal === 'array') {
1389
1394
  expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
@@ -1393,30 +1398,22 @@ function resolve( model ) {
1393
1398
  }
1394
1399
  }
1395
1400
 
1396
- function reportUnsupportedAnnoExpr( expr ) {
1397
- if (isBetaEnabled( model.options, 'annotationExpressions' ))
1398
- return;
1399
- const alreadyReported = model.$messageFunctions.messages
1400
- .find(msg => msg.messageId === 'anno-experimental-expressions');
1401
- if (!alreadyReported) {
1402
- info( 'anno-experimental-expressions', [ expr.location ], {
1403
- option: 'annotationExpressions',
1404
- // eslint-disable-next-line max-len
1405
- }, 'Expressions in annotation values are a beta feature. Use at your own risk. (This message can be suppressed with beta flag $(OPTION))' );
1406
- }
1407
- }
1408
-
1409
- // for faster processing, mark artifacts and annotations which contain anno expressions
1401
+ /**
1402
+ * For faster processing, mark artifacts and annotations which contain anno expressions
1403
+ *
1404
+ * @param {object} anno
1405
+ * @param {XSN.Artifact} art
1406
+ */
1410
1407
  function initAnnotationForExpression( anno, art ) {
1411
1408
  anno.kind = '$annotation';
1412
1409
  setLink( anno, '_outer', art );
1413
- while (!art.$contains?.$annotation) {
1414
- art.$contains ??= {};
1415
- art.$contains.$annotation = true; // TODO: extra values for elem and $self refs
1416
- if (!art._main)
1417
- break;
1418
- art = art._parent; // TODO: really go up?
1419
- }
1410
+ art.$contains ??= {};
1411
+ art.$contains.$annotation = { // set in resolveExprItem
1412
+ $path: false,
1413
+ $self: false,
1414
+ };
1415
+ // Think about tagging parents too (like before #12636).
1416
+ // Might be useful for future recursive types.
1420
1417
  }
1421
1418
 
1422
1419
  function resolveExpr( expr, exprCtx, user ) {
@@ -1448,6 +1445,11 @@ function resolve( model ) {
1448
1445
  resolveParamsAndWhere( step, expected, user, step === last );
1449
1446
  // TODO: delete 4th arg
1450
1447
  }
1448
+
1449
+ if (expected === 'annotation') {
1450
+ user._outer.$contains.$annotation.$path = true;
1451
+ user._outer.$contains.$annotation.$self ||= expr.path[0]?._navigation?.kind === '$self';
1452
+ }
1451
1453
  }
1452
1454
  else if (expr.query) {
1453
1455
  const { query } = expr;