@sap/cds-compiler 4.8.0 → 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 +29 -4
- 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 +14 -1
- package/doc/CHANGELOG_BETA.md +19 -0
- package/lib/api/main.js +59 -24
- package/lib/api/options.js +12 -1
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +27 -0
- package/lib/base/message-registry.js +32 -19
- package/lib/base/messages.js +50 -19
- package/lib/base/model.js +4 -5
- package/lib/checks/actionsFunctions.js +2 -2
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +3 -2
- package/lib/checks/validator.js +2 -34
- package/lib/compiler/assert-consistency.js +8 -2
- package/lib/compiler/checks.js +44 -18
- package/lib/compiler/define.js +34 -22
- package/lib/compiler/extend.js +33 -10
- package/lib/compiler/index.js +0 -1
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/propagator.js +21 -18
- package/lib/compiler/resolve.js +44 -28
- package/lib/compiler/shared.js +60 -20
- package/lib/compiler/tweak-assocs.js +13 -88
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/edm/annotations/genericTranslation.js +80 -60
- package/lib/edm/edm.js +4 -4
- package/lib/edm/edmInboundChecks.js +33 -0
- package/lib/edm/edmPreprocessor.js +9 -6
- package/lib/gen/Dictionary.json +129 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1523 -1518
- package/lib/json/from-csn.js +13 -4
- package/lib/json/to-csn.js +10 -11
- package/lib/language/genericAntlrParser.js +14 -6
- package/lib/main.d.ts +67 -14
- package/lib/main.js +1 -0
- package/lib/model/cloneCsn.js +6 -3
- package/lib/model/csnRefs.js +12 -7
- package/lib/model/csnUtils.js +13 -7
- package/lib/model/enrichCsn.js +3 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +33 -34
- package/lib/optionProcessor.js +27 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +3 -1
- package/lib/transform/db/applyTransformations.js +33 -0
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +8 -3
- 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/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +6 -8
- package/lib/transform/effective/misc.js +31 -10
- package/lib/transform/forOdata.js +23 -7
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +189 -106
- package/lib/transform/odata/toFinalBaseType.js +1 -1
- package/lib/transform/odata/typesExposure.js +15 -12
- package/lib/transform/parseExpr.js +4 -4
- package/lib/transform/transformUtils.js +40 -37
- package/lib/transform/translateAssocsToJoins.js +47 -47
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/message-explanations.json +1 -0
- 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/checks.js
CHANGED
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
} = require('../base/model');
|
|
20
20
|
const { CompilerAssertion } = require('../base/error');
|
|
21
21
|
const { typeParameters } = require('./builtins');
|
|
22
|
+
const { propagationRules } = require('../base/builtins');
|
|
22
23
|
|
|
23
24
|
const $location = Symbol.for( 'cds.$location' );
|
|
24
25
|
|
|
@@ -69,6 +70,13 @@ function check( model ) {
|
|
|
69
70
|
} );
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
function* iterateAnnotations( art ) {
|
|
74
|
+
for (const prop in art) {
|
|
75
|
+
if (prop.charAt(0) === '@')
|
|
76
|
+
yield prop;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
function checkGenericConstruct( art ) {
|
|
73
81
|
checkName( art );
|
|
74
82
|
checkTypeArguments( art );
|
|
@@ -76,11 +84,9 @@ function check( model ) {
|
|
|
76
84
|
if (art.value && !art.$calcDepElement && art.type)
|
|
77
85
|
checkTypeCast( art.value, art );
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
.forEach( a => checkAnnotationAssignment1( art, art[a] ) );
|
|
83
|
-
}
|
|
87
|
+
for (const anno of iterateAnnotations( art ))
|
|
88
|
+
checkAnnotationAssignment1( art, art[anno] );
|
|
89
|
+
|
|
84
90
|
checkTypeStructure( art );
|
|
85
91
|
checkAssociation( art ); // type def could be assoc
|
|
86
92
|
if (art.kind === 'enum')
|
|
@@ -236,6 +242,8 @@ function check( model ) {
|
|
|
236
242
|
? xpr.args[0]?._artifact
|
|
237
243
|
: xpr._artifact;
|
|
238
244
|
const type = isCast ? xpr.type : user.type;
|
|
245
|
+
if (!isCast && type.$inferred)
|
|
246
|
+
return; // e.g. $inferred:'generated'
|
|
239
247
|
if (elem && type) { // has explicit type
|
|
240
248
|
if (type._artifact?.elements)
|
|
241
249
|
error( 'type-invalid-cast', [ type.location, user ], { '#': 'to-structure' } );
|
|
@@ -264,9 +272,11 @@ function check( model ) {
|
|
|
264
272
|
// "key" keyword at localized element in SELECT list.
|
|
265
273
|
// TODO: not in inferred elements, but also inside aspects
|
|
266
274
|
if (elem.key?.val && elem._main?.query) {
|
|
275
|
+
// either the element was casted to localized (no `_origin`) or
|
|
267
276
|
// original element is localized but not key, as that would have
|
|
268
277
|
// already resulted in a warning by localized.js
|
|
269
|
-
if (elem._origin
|
|
278
|
+
if ((!elem._origin && elem.localized?.val) ||
|
|
279
|
+
(elem._origin?.localized?.val && !elem._origin.key?.val)) {
|
|
270
280
|
warning( 'def-ignoring-localized', [ elem.key.location, elem ], { keyword: 'localized' },
|
|
271
281
|
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
272
282
|
}
|
|
@@ -860,21 +870,37 @@ function check( model ) {
|
|
|
860
870
|
return false;
|
|
861
871
|
}
|
|
862
872
|
|
|
873
|
+
/**
|
|
874
|
+
* Returns true if the given annotation accepts expressions as values.
|
|
875
|
+
*
|
|
876
|
+
* @param {object} anno
|
|
877
|
+
* @param {XSN.Artifact} art
|
|
878
|
+
* @returns {boolean}
|
|
879
|
+
*/
|
|
880
|
+
function checkAnnotationAcceptsExpressions( anno, art ) {
|
|
881
|
+
const name = anno.name?.id;
|
|
882
|
+
if (!name)
|
|
883
|
+
return true;
|
|
884
|
+
if (!propagationRules[`@${ name }`])
|
|
885
|
+
return true;
|
|
886
|
+
error( 'anno-unexpected-expr', [ anno.location, art ], { anno: name },
|
|
887
|
+
'Unexpected expression as value for $(ANNO)' );
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
863
890
|
|
|
864
|
-
// Former checkAnnotationAssignments.js ------------------------------------
|
|
865
|
-
|
|
866
|
-
// Check the annotation assignments (if any) of 'annotatable', possibly using annotation
|
|
867
|
-
// definitions from 'model'. Report errors on 'options.messages.
|
|
868
|
-
//
|
|
869
|
-
// TODO: rework completely!
|
|
870
|
-
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
|
|
871
|
-
|
|
872
|
-
// Has been slightly adapted for model.vocabularies but comments need to be
|
|
873
|
-
// adapted, etc.
|
|
874
891
|
function checkAnnotationAssignment1( art, anno ) {
|
|
875
|
-
if (art.$contains?.$annotation)
|
|
876
|
-
|
|
892
|
+
if (art.$contains?.$annotation && anno.kind === '$annotation') {
|
|
893
|
+
if (checkAnnotationAcceptsExpressions( anno, art ))
|
|
894
|
+
checkAnnotationExpressions( anno, art );
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (!model.vocabularies)
|
|
898
|
+
return;
|
|
877
899
|
|
|
900
|
+
// Has been slightly adapted for model.vocabularies but comments need to be
|
|
901
|
+
// adapted, etc.
|
|
902
|
+
// TODO: rework completely!
|
|
903
|
+
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
|
|
878
904
|
// Sanity checks (ignore broken assignments)
|
|
879
905
|
if (!anno.name?.id)
|
|
880
906
|
return;
|
package/lib/compiler/define.js
CHANGED
|
@@ -213,6 +213,12 @@ function define( model ) {
|
|
|
213
213
|
dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
|
|
214
214
|
|
|
215
215
|
addI18nBlocks();
|
|
216
|
+
|
|
217
|
+
const { $self } = model.definitions;
|
|
218
|
+
if ($self) {
|
|
219
|
+
message( 'name-deprecated-$self', [ $self.location, $self ], { name: '$self' },
|
|
220
|
+
'Do not use $(NAME) as name for an artifact definition' );
|
|
221
|
+
}
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
// Phase 1: ----------------------------------------------------------------
|
|
@@ -228,14 +234,15 @@ function define( model ) {
|
|
|
228
234
|
if (!src.kind)
|
|
229
235
|
src.kind = 'source';
|
|
230
236
|
|
|
231
|
-
let
|
|
237
|
+
let namespace = src.namespace?.name;
|
|
232
238
|
let prefix = '';
|
|
233
239
|
if (namespace?.path && !namespace.path.broken) {
|
|
234
240
|
namespace.id = pathName( namespace.path );
|
|
235
241
|
prefix = `${ namespace.id }.`;
|
|
236
242
|
}
|
|
237
243
|
if (isInReservedNamespace( prefix )) {
|
|
238
|
-
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ],
|
|
244
|
+
error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
|
|
245
|
+
{ name: 'cds' },
|
|
239
246
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
240
247
|
namespace = null;
|
|
241
248
|
}
|
|
@@ -523,7 +530,7 @@ function define( model ) {
|
|
|
523
530
|
if (src.$frontend && src.$frontend !== 'cdl')
|
|
524
531
|
return;
|
|
525
532
|
if (src.namespace) {
|
|
526
|
-
const decl = src.namespace;
|
|
533
|
+
const decl = src.namespace.name;
|
|
527
534
|
if (!decl.id) // parsing may have failed
|
|
528
535
|
return;
|
|
529
536
|
if (!model.definitions[decl.id]) {
|
|
@@ -562,8 +569,8 @@ function define( model ) {
|
|
|
562
569
|
initArtifactParentLink( art, model.definitions );
|
|
563
570
|
const block = art._block;
|
|
564
571
|
checkRedefinition( art );
|
|
565
|
-
initMembers( art, art, block );
|
|
566
572
|
initDollarSelf( art ); // $self
|
|
573
|
+
initMembers( art, art, block );
|
|
567
574
|
if (art.params)
|
|
568
575
|
initDollarParameters( art ); // $parameters
|
|
569
576
|
|
|
@@ -636,6 +643,7 @@ function define( model ) {
|
|
|
636
643
|
name: { id: '$parameters' },
|
|
637
644
|
kind: '$parameters',
|
|
638
645
|
location: art.location,
|
|
646
|
+
deprecated: true, // hide in code completion
|
|
639
647
|
};
|
|
640
648
|
setLink( parameters, '_parent', art );
|
|
641
649
|
setLink( parameters, '_main', art );
|
|
@@ -679,8 +687,14 @@ function define( model ) {
|
|
|
679
687
|
setLink( self, '_origin', query );
|
|
680
688
|
setLink( self, '_parent', query );
|
|
681
689
|
setLink( self, '_main', query._main );
|
|
690
|
+
|
|
691
|
+
const projection = { ...self, deprecated: true }; // hide in code completion
|
|
692
|
+
setLink( projection, '_origin', query );
|
|
693
|
+
setLink( projection, '_parent', query );
|
|
694
|
+
setLink( projection, '_main', query._main );
|
|
695
|
+
|
|
682
696
|
query.$tableAliases.$self = self;
|
|
683
|
-
query.$tableAliases.$projection =
|
|
697
|
+
query.$tableAliases.$projection = projection;
|
|
684
698
|
}
|
|
685
699
|
initSubQuery( query ); // check for SELECT clauses after from / mixin
|
|
686
700
|
}
|
|
@@ -858,12 +872,13 @@ function define( model ) {
|
|
|
858
872
|
}
|
|
859
873
|
|
|
860
874
|
function initMixins( query, art ) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
875
|
+
forEachInOrder( query, 'mixin', initMixin );
|
|
876
|
+
|
|
877
|
+
function initMixin( mixin, name ) {
|
|
878
|
+
setLink( mixin, '_block', art._block );
|
|
879
|
+
setMemberParent( mixin, name, query );
|
|
880
|
+
checkRedefinition( mixin );
|
|
864
881
|
if (!(mixin.$duplicates)) {
|
|
865
|
-
setMemberParent( mixin, name, query );
|
|
866
|
-
setLink( mixin, '_block', art._block );
|
|
867
882
|
// TODO: do some initMembers() ? If people had annotation
|
|
868
883
|
// assignments on the mixin... (also for future mixin definitions
|
|
869
884
|
// with generated values)
|
|
@@ -924,14 +939,14 @@ function define( model ) {
|
|
|
924
939
|
setLink( col, '_block', parent._block );
|
|
925
940
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
926
941
|
if (col.doc) {
|
|
927
|
-
|
|
942
|
+
message( 'syntax-ignoring-anno', [ col.doc.location, col ],
|
|
928
943
|
{ '#': 'doc', code: '.{ ‹inline› }' } );
|
|
929
944
|
}
|
|
930
945
|
// col.$annotations no available for CSN input, have to search.
|
|
931
|
-
//
|
|
946
|
+
// Message about first annotation should be enough to avoid spam.
|
|
932
947
|
const firstAnno = Object.keys( col ).find( key => key.startsWith( '@' ) );
|
|
933
948
|
if (firstAnno) {
|
|
934
|
-
|
|
949
|
+
message( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
|
|
935
950
|
{ code: '.{ ‹inline› }' } );
|
|
936
951
|
}
|
|
937
952
|
}
|
|
@@ -1151,7 +1166,7 @@ function define( model ) {
|
|
|
1151
1166
|
checkRedefinition( elem );
|
|
1152
1167
|
initMembers( elem, elem, bl, initExtensions );
|
|
1153
1168
|
if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
|
|
1154
|
-
initBoundSelfParam( elem.params );
|
|
1169
|
+
initBoundSelfParam( elem.params, elem._main );
|
|
1155
1170
|
|
|
1156
1171
|
// for a correct home path, setMemberParent needed to be called
|
|
1157
1172
|
|
|
@@ -1177,27 +1192,24 @@ function define( model ) {
|
|
|
1177
1192
|
}
|
|
1178
1193
|
}
|
|
1179
1194
|
|
|
1180
|
-
function initBoundSelfParam( params ) {
|
|
1195
|
+
function initBoundSelfParam( params, main ) {
|
|
1181
1196
|
if (!params)
|
|
1182
1197
|
return;
|
|
1183
1198
|
if (boundSelfParamType === true) { // first try
|
|
1184
1199
|
const def = model.definitions.$self;
|
|
1185
1200
|
if (def) {
|
|
1186
|
-
// TODO v5: bring this always, probably even as error
|
|
1187
|
-
warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
|
|
1188
|
-
'Do not use $(NAME) as name for an artifact definition' );
|
|
1189
1201
|
boundSelfParamType = false;
|
|
1190
1202
|
return;
|
|
1191
1203
|
}
|
|
1192
|
-
|
|
1193
|
-
boundSelfParamType = { name: { id: '$self', location }, location };
|
|
1204
|
+
boundSelfParamType = '$self';
|
|
1194
1205
|
}
|
|
1195
1206
|
const first = params[Object.keys( params )[0] || ''];
|
|
1196
1207
|
const type = first?.type || first?.items?.type; // this sequence = no derived type
|
|
1197
1208
|
const path = type?.path;
|
|
1198
1209
|
if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
|
|
1199
|
-
|
|
1200
|
-
setLink(
|
|
1210
|
+
const { $self } = main.$tableAliases;
|
|
1211
|
+
setLink( type, '_artifact', $self );
|
|
1212
|
+
setLink( path[0], '_artifact', $self );
|
|
1201
1213
|
}
|
|
1202
1214
|
}
|
|
1203
1215
|
|
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');
|
|
@@ -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
|
@@ -20,30 +20,16 @@ const {
|
|
|
20
20
|
linkToOrigin,
|
|
21
21
|
withAssociation,
|
|
22
22
|
viewFromPrimary,
|
|
23
|
+
copyExpr,
|
|
23
24
|
} = require('./utils');
|
|
25
|
+
const { propagationRules } = require('../base/builtins');
|
|
24
26
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
27
|
+
const { xprRewriteFns } = require('./xpr-rewrite');
|
|
25
28
|
// const { ref } = require( '../model/revealInternalProperties' )
|
|
26
29
|
|
|
27
30
|
// Note that propagation here is also used for deep-copying (function `onlyViaParent`)
|
|
28
31
|
function propagate( model ) {
|
|
29
32
|
const props = {
|
|
30
|
-
'@com.sap.gtt.core.CoreModel.Indexable': never,
|
|
31
|
-
'@cds.persistence.exists': never, // also copied in generate.js
|
|
32
|
-
'@cds.persistence.table': never,
|
|
33
|
-
'@cds.persistence.calcview': never,
|
|
34
|
-
'@cds.persistence.udf': never,
|
|
35
|
-
'@cds.persistence.skip': notWithPersistenceTable, // also copied in generate.js
|
|
36
|
-
// '@cds.tenant.independent' is propagated as normal, but also copied in generate.js
|
|
37
|
-
'@sql.append': never,
|
|
38
|
-
'@sql.prepend': never,
|
|
39
|
-
'@sql.replace': never,
|
|
40
|
-
'@Analytics.hidden': never,
|
|
41
|
-
'@Analytics.visible': never,
|
|
42
|
-
'@cds.autoexpose': onlyViaArtifact,
|
|
43
|
-
'@cds.autoexposed': never, // in case people set it themselves
|
|
44
|
-
'@cds.external': never,
|
|
45
|
-
'@cds.redirection.target': never,
|
|
46
|
-
'@fiori.draft.enabled': onlyViaArtifact,
|
|
47
33
|
'@': annotation, // always except in 'items' (and parameters for entity return types)
|
|
48
34
|
doc: annotation, // always except in 'items' (and parameters for entity return types)
|
|
49
35
|
default: withKind, // always except in 'items'
|
|
@@ -72,7 +58,17 @@ function propagate( model ) {
|
|
|
72
58
|
returns,
|
|
73
59
|
$filtered: annotation,
|
|
74
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
|
+
|
|
75
70
|
const { options } = model;
|
|
71
|
+
const { rewriteAnnotationsRefs } = xprRewriteFns( model );
|
|
76
72
|
// eslint-disable-next-line max-len
|
|
77
73
|
const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
|
|
78
74
|
const { warning, throwWithError } = model.$messageFunctions;
|
|
@@ -103,7 +99,7 @@ function propagate( model ) {
|
|
|
103
99
|
const news = [];
|
|
104
100
|
for (const target of targets) {
|
|
105
101
|
const origin = getOrigin( target );
|
|
106
|
-
if (origin) {
|
|
102
|
+
if (origin && origin.kind !== '$self') {
|
|
107
103
|
// Calculated elements that are simple references: `calc = field;`.
|
|
108
104
|
// Respect sibling properties in inheritance.
|
|
109
105
|
if (target._calcOrigin?._origin && target.value?._artifact) {
|
|
@@ -202,6 +198,10 @@ function propagate( model ) {
|
|
|
202
198
|
target[prop] = [ ...val ];
|
|
203
199
|
target[prop].$inferred = 'prop';
|
|
204
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
|
+
}
|
|
205
205
|
else {
|
|
206
206
|
target[prop] = Object.assign( {}, val, { $inferred: 'prop' } );
|
|
207
207
|
if (val._artifact !== undefined)
|
|
@@ -270,6 +270,7 @@ function propagate( model ) {
|
|
|
270
270
|
target[prop][$inferred] = 'prop';
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
// Only propagate if parent object (which is not necessarily `_parent`) was propagated.
|
|
273
274
|
function onlyViaParent( prop, target, source ) {
|
|
274
275
|
if (target.$inferred === 'proxy' || target.$inferred === 'expanded')
|
|
275
276
|
// assocs and enums do not have 'include'
|
|
@@ -279,6 +280,8 @@ function propagate( model ) {
|
|
|
279
280
|
function targetAspect( prop, target, source ) {
|
|
280
281
|
if (target.targetAspect)
|
|
281
282
|
return;
|
|
283
|
+
if (target.type?._artifact === model.definitions['cds.Association'])
|
|
284
|
+
return; // don't propagate targetAspect to associations (e.g. via $filtered)
|
|
282
285
|
const ta = source.targetAspect;
|
|
283
286
|
if (!ta.elements && !ta._origin) { // _origin set for elements in source
|
|
284
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) {
|
|
@@ -557,10 +564,12 @@ function resolve( model ) {
|
|
|
557
564
|
const sType = specifiedElement.type?._artifact;
|
|
558
565
|
const iTypeArt = getInferredPropFromOrigin( 'type' )?._artifact;
|
|
559
566
|
const iType = iTypeArt || inferredElement;
|
|
560
|
-
|
|
561
|
-
// xor: could be missing a type;
|
|
562
567
|
// FIXME: The coding above returns incorrect iType for expand on associations
|
|
563
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;
|
|
564
573
|
if (!specifiedElement.type && inferredElement.type) {
|
|
565
574
|
error( 'query-mismatched-element', [ specifiedElement.location, user ], {
|
|
566
575
|
'#': !specifiedElement.type ? 'missing' : 'extra', name: user.name.id, prop: 'type',
|
|
@@ -568,7 +577,7 @@ function resolve( model ) {
|
|
|
568
577
|
return;
|
|
569
578
|
}
|
|
570
579
|
// If specified type is `null`, type could not be resolved.
|
|
571
|
-
else if (sType && sType !== iType &&
|
|
580
|
+
else if (!compToAssoc && sType && sType !== iType &&
|
|
572
581
|
// Special case for $recompilation: allow one level of type indirection. See #12113.
|
|
573
582
|
(!model.options.$recompile || sType !== iType.type?._artifact)) {
|
|
574
583
|
const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
|
|
@@ -1348,6 +1357,19 @@ function resolve( model ) {
|
|
|
1348
1357
|
// General resolver functions
|
|
1349
1358
|
//--------------------------------------------------------------------------
|
|
1350
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
|
+
|
|
1351
1373
|
// Resolve the type and its arguments if applicable.
|
|
1352
1374
|
function resolveTypeExpr( art, user ) {
|
|
1353
1375
|
const typeArt = resolvePath( art.type, 'type', user );
|
|
@@ -1367,7 +1389,6 @@ function resolve( model ) {
|
|
|
1367
1389
|
if (!anno.kind)
|
|
1368
1390
|
initAnnotationForExpression( anno, art );
|
|
1369
1391
|
resolveExpr( expr, 'annotation', anno );
|
|
1370
|
-
reportUnsupportedAnnoExpr( expr );
|
|
1371
1392
|
}
|
|
1372
1393
|
else if (expr.literal === 'array') {
|
|
1373
1394
|
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
@@ -1377,32 +1398,22 @@ function resolve( model ) {
|
|
|
1377
1398
|
}
|
|
1378
1399
|
}
|
|
1379
1400
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
// due to test mode and shuffling, we would get a different location on each compilation.
|
|
1387
|
-
const loc = model.options.testMode ? null : [ expr.location ];
|
|
1388
|
-
info( 'anno-experimental-expressions', loc, {
|
|
1389
|
-
option: 'annotationExpressions',
|
|
1390
|
-
// eslint-disable-next-line max-len
|
|
1391
|
-
}, 'Expressions in annotation values are a beta feature. Use at your own risk. (This message can be suppressed with beta flag $(OPTION))' );
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
// 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
|
+
*/
|
|
1396
1407
|
function initAnnotationForExpression( anno, art ) {
|
|
1397
1408
|
anno.kind = '$annotation';
|
|
1398
1409
|
setLink( anno, '_outer', art );
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
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.
|
|
1406
1417
|
}
|
|
1407
1418
|
|
|
1408
1419
|
function resolveExpr( expr, exprCtx, user ) {
|
|
@@ -1434,6 +1445,11 @@ function resolve( model ) {
|
|
|
1434
1445
|
resolveParamsAndWhere( step, expected, user, step === last );
|
|
1435
1446
|
// TODO: delete 4th arg
|
|
1436
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
|
+
}
|
|
1437
1453
|
}
|
|
1438
1454
|
else if (expr.query) {
|
|
1439
1455
|
const { query } = expr;
|