@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.
- package/CHANGELOG.md +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
package/lib/compiler/extend.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
|
|
837
|
-
|
|
838
|
-
info( 'anno-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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
|
|
package/lib/compiler/index.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
385
|
+
ast.location = new Location( filename );
|
|
387
386
|
assertConsistency( ast, options );
|
|
388
387
|
}
|
|
389
388
|
|
package/lib/compiler/populate.js
CHANGED
|
@@ -633,8 +633,14 @@ function populate( model ) {
|
|
|
633
633
|
}
|
|
634
634
|
|
|
635
635
|
function resolveExcluding( name, env, excludingDict, user ) {
|
|
636
|
-
|
|
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
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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 );
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -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 &&
|
|
1227
|
-
|
|
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
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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;
|