@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/compiler/shared.js
CHANGED
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
const { searchName } = require('../base/messages');
|
|
7
7
|
const { dictAddArray } = require('../base/dictionaries');
|
|
8
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
8
9
|
|
|
9
10
|
const {
|
|
10
11
|
setLink,
|
|
11
12
|
setArtifactLink,
|
|
12
13
|
dependsOn,
|
|
13
14
|
pathName,
|
|
15
|
+
annotationHasEllipsis,
|
|
14
16
|
} = require('./utils');
|
|
15
17
|
|
|
16
18
|
function artifactsEnv( art ) {
|
|
@@ -143,6 +145,13 @@ function fns( model ) {
|
|
|
143
145
|
rewrite: {
|
|
144
146
|
next: '_$next', dollar: true, escape: 'param', noDep: true, rewrite: true,
|
|
145
147
|
}, // TODO: assertion that there is no next/escape used
|
|
148
|
+
'order-by': {
|
|
149
|
+
next: '_$next',
|
|
150
|
+
dollar: true,
|
|
151
|
+
escape: 'param',
|
|
152
|
+
assoc: 'nav',
|
|
153
|
+
deprecatedSourceRefs: true,
|
|
154
|
+
},
|
|
146
155
|
'order-by-union': {
|
|
147
156
|
next: '_$next', dollar: true, escape: 'param', noDep: true, noExt: true,
|
|
148
157
|
},
|
|
@@ -159,6 +168,7 @@ function fns( model ) {
|
|
|
159
168
|
resolveUncheckedPath,
|
|
160
169
|
resolveTypeArgumentsUnchecked,
|
|
161
170
|
resolvePath,
|
|
171
|
+
checkAnnotate,
|
|
162
172
|
defineAnnotations,
|
|
163
173
|
attachAndEmitValidNames,
|
|
164
174
|
} );
|
|
@@ -512,7 +522,7 @@ function fns( model ) {
|
|
|
512
522
|
if (args.length > 0) {
|
|
513
523
|
const loc = [ args[args.length - 1].location, user ];
|
|
514
524
|
if (typeArtifact.builtin)
|
|
515
|
-
|
|
525
|
+
message( 'type-ignoring-argument', loc, { art: typeArtifact } );
|
|
516
526
|
else
|
|
517
527
|
error( 'type-unexpected-argument', loc, { '#': 'std', art: typeArtifact });
|
|
518
528
|
}
|
|
@@ -601,6 +611,21 @@ function fns( model ) {
|
|
|
601
611
|
else if (r) {
|
|
602
612
|
return setArtifactLink( head, r );
|
|
603
613
|
}
|
|
614
|
+
else if (spec.deprecatedSourceRefs && env._combined &&
|
|
615
|
+
isDeprecatedEnabled( options, 'autoCorrectOrderBySourceRefs' )) {
|
|
616
|
+
// User has provided a source element without table alias where a query
|
|
617
|
+
// element is expected. Possible on many DBs (and compiler v1), in CAP
|
|
618
|
+
// only with table alias. Auto-correct it if no duplicate.
|
|
619
|
+
// TODO: we could use that info also in messages when the deprecated flag is not set
|
|
620
|
+
const s = env._combined[head.id];
|
|
621
|
+
if (s && !Array.isArray(s)) {
|
|
622
|
+
path.$prefix = s.name.alias; // pushing it to path directly could be problematic
|
|
623
|
+
warning( null, [ head.location, user ],
|
|
624
|
+
{ id: head.id, newcode: `${ s.name.alias }.${ head.id }` },
|
|
625
|
+
'Replace source element reference $(ID) by $(NEWCODE); auto-corrected' );
|
|
626
|
+
return setArtifactLink( head, s );
|
|
627
|
+
}
|
|
628
|
+
}
|
|
604
629
|
}
|
|
605
630
|
if (spec.noMessage || msgArt === true && extDict === model.definitions)
|
|
606
631
|
return null;
|
|
@@ -673,6 +698,9 @@ function fns( model ) {
|
|
|
673
698
|
art = item._artifact;
|
|
674
699
|
if (Array.isArray(art))
|
|
675
700
|
return false;
|
|
701
|
+
if (art.$requireElementAccess && path.length === 1)
|
|
702
|
+
// Path with only one item, but we expect an element, e.g. `$at.from`.
|
|
703
|
+
signalMissingElementAccess(art, [ item.location, user ]);
|
|
676
704
|
continue;
|
|
677
705
|
}
|
|
678
706
|
|
|
@@ -808,11 +836,30 @@ function fns( model ) {
|
|
|
808
836
|
attachAndEmitValidNames(err, ...valid.reverse());
|
|
809
837
|
}
|
|
810
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Emit a 'ref-expected-element' error for magic variable references
|
|
841
|
+
* that require element accesses but don't do.
|
|
842
|
+
* For example: `$at`, but `$at.from` or `$at.to` is required.
|
|
843
|
+
*
|
|
844
|
+
* @param {object} art
|
|
845
|
+
* @param {any} location
|
|
846
|
+
*/
|
|
847
|
+
function signalMissingElementAccess(art, location) {
|
|
848
|
+
const err = message( 'ref-expected-element', location,
|
|
849
|
+
{ '#': 'magicVar', id: art.name.id } );
|
|
850
|
+
// Mapping for better valid names: from -> $at.from
|
|
851
|
+
const valid = Object.keys(art.elements || {}).reduce((prev, curr) => {
|
|
852
|
+
prev[`${ art.name.id }.${ curr }`] = true;
|
|
853
|
+
return prev;
|
|
854
|
+
}, Object.create(null));
|
|
855
|
+
attachAndEmitValidNames(err, valid);
|
|
856
|
+
}
|
|
857
|
+
|
|
811
858
|
/**
|
|
812
859
|
* Attaches a dictionary of valid names to the given compiler message.
|
|
813
860
|
* In test mode, an info message is emitted with a list of valid names.
|
|
814
861
|
*
|
|
815
|
-
* @param {
|
|
862
|
+
* @param {CompileMessage} msg CDS Compiler message
|
|
816
863
|
* @param {...object} validDicts One ore more artifact dictionaries such as in `_block`.
|
|
817
864
|
*/
|
|
818
865
|
function attachAndEmitValidNames(msg, ...validDicts) {
|
|
@@ -838,101 +885,61 @@ function fns( model ) {
|
|
|
838
885
|
}
|
|
839
886
|
}
|
|
840
887
|
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (construct.$annotations && construct.$annotations.doc )
|
|
868
|
-
art.doc = construct.$annotations.doc; // e.g. through `annotate` statement in CDL
|
|
869
|
-
else if (construct.doc)
|
|
870
|
-
art.doc = construct.doc; // e.g. through `extensions` array in CSN
|
|
871
|
-
if (!construct.$annotations) {
|
|
872
|
-
if (!block || block.$frontend !== 'json')
|
|
873
|
-
return; // namespace, or in CDL source without @annos:
|
|
874
|
-
// CSN input: set _block and $priority, shallow-copy from extension
|
|
875
|
-
for (const annoProp in construct) {
|
|
876
|
-
if (annoProp.charAt(0) === '@') {
|
|
877
|
-
let annos = construct[annoProp];
|
|
878
|
-
if (!(Array.isArray(annos)))
|
|
879
|
-
annos = [ annos ];
|
|
880
|
-
for (const a of annos) {
|
|
881
|
-
setLink( a, '_block', block );
|
|
882
|
-
a.$priority = priority;
|
|
883
|
-
if (construct !== art)
|
|
884
|
-
addAnnotation( art, annoProp, a );
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
return;
|
|
888
|
+
// Issue messages for annotations on namespaces and builtins
|
|
889
|
+
// (TODO: really here?, probably split main artifacts vs returns)
|
|
890
|
+
// see also lateExtensions() where similar messages are reported
|
|
891
|
+
function checkAnnotate( construct, art ) {
|
|
892
|
+
// Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
|
|
893
|
+
// they can still be applied. Namespace annotations are extracted in to-csn.js
|
|
894
|
+
// In parseCdl mode USINGs and other unknown references are generated as
|
|
895
|
+
// namespaces which would lead to false positives.
|
|
896
|
+
// TODO: should this really be different to annotate-unknown?
|
|
897
|
+
if (art.kind === 'namespace') {
|
|
898
|
+
info( 'anno-namespace', [ construct.name.location, construct ], {},
|
|
899
|
+
'Namespaces can\'t be annotated' );
|
|
900
|
+
}
|
|
901
|
+
// Builtin annotations would also get lost. Same as for namespaces:
|
|
902
|
+
// extracted in to-csn.js
|
|
903
|
+
else if (art.builtin === true) {
|
|
904
|
+
info( 'anno-builtin', [ construct.name.location, construct ], {},
|
|
905
|
+
'Builtin types should not be annotated. Use custom type instead' );
|
|
906
|
+
}
|
|
907
|
+
else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
|
|
908
|
+
art.kind !== 'function' ) {
|
|
909
|
+
// `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
|
|
910
|
+
// for non-actions. We can't only check for !art.returns, because `action A();` is valid.
|
|
911
|
+
// `art._block` ensures that `art` is a defined def.
|
|
912
|
+
warning('anno-unexpected-returns', [ construct.name.location, construct ],
|
|
913
|
+
{ keyword: 'returns', kind: art.kind }, 'Unexpected $(KEYWORD) for $(KIND)');
|
|
889
914
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Set _block links for annotations (necessary for layering).
|
|
918
|
+
// Also copy annotations from `construct` to `art` (TODO: separate that functionality).
|
|
919
|
+
function defineAnnotations( construct, art, block, priority = false ) {
|
|
920
|
+
if (construct.doc)
|
|
921
|
+
art.doc = construct.doc; // e.g. through `extensions` array in CSN
|
|
922
|
+
|
|
923
|
+
// set _block (for layering) and $priority, shallow-copy from extension
|
|
924
|
+
// TODO: think of removing $priority, then
|
|
925
|
+
// no _block: define, _block: annotate/extend/edmx
|
|
926
|
+
// would fit with extending defs with props like length
|
|
927
|
+
for (const annoProp in construct) {
|
|
928
|
+
if (annoProp.charAt(0) === '@') {
|
|
929
|
+
let annos = construct[annoProp];
|
|
930
|
+
if (!(Array.isArray(annos)))
|
|
931
|
+
annos = [ annos ];
|
|
932
|
+
for (const a of annos) {
|
|
933
|
+
setLink( a, '_block', block );
|
|
934
|
+
a.$priority = priority; // is now: undefined (auto-set) | false | 'annotate' | 'extend'
|
|
935
|
+
if (construct !== art)
|
|
936
|
+
addAnnotation( art, annoProp, a );
|
|
937
|
+
if (!priority && annotationHasEllipsis( a )) {
|
|
938
|
+
error( 'anno-unexpected-ellipsis',
|
|
939
|
+
[ a.name.location, art ], { code: '...' } );
|
|
915
940
|
}
|
|
916
|
-
flatten( [ ...path, ...item.name.path ], `${ annoProp }.${ prop }`, item, iHaveVariant || item.name.variant);
|
|
917
941
|
}
|
|
918
|
-
for (const prop in value.struct) {
|
|
919
|
-
const item = value.struct[prop];
|
|
920
|
-
flatten( [ ...path, item.name ], `${ annoProp }.${ prop }`, item, iHaveVariant );
|
|
921
|
-
}
|
|
922
|
-
return;
|
|
923
942
|
}
|
|
924
|
-
const anno = Object.assign( {}, value ); // shallow copy
|
|
925
|
-
anno.name = {
|
|
926
|
-
path,
|
|
927
|
-
location: location ||
|
|
928
|
-
value.name && value.name.location ||
|
|
929
|
-
value.path && value.path.location,
|
|
930
|
-
};
|
|
931
|
-
setLink( anno, '_block', block );
|
|
932
|
-
// TODO: _parent, _main is set later (if we have ElementRef), or do we
|
|
933
|
-
// set _artifact?
|
|
934
|
-
anno.$priority = priority;
|
|
935
|
-
addAnnotation( art, annoProp, anno );
|
|
936
943
|
}
|
|
937
944
|
}
|
|
938
945
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
|
-
isDeprecatedEnabled,
|
|
7
6
|
forEachDefinition,
|
|
8
7
|
forEachGeneric,
|
|
9
8
|
forEachInOrder,
|
|
@@ -26,7 +25,6 @@ const $location = Symbol.for('cds.$location');
|
|
|
26
25
|
|
|
27
26
|
// Export function of this file.
|
|
28
27
|
function tweakAssocs( model ) {
|
|
29
|
-
const { options } = model;
|
|
30
28
|
// Get shared functionality and the message function:
|
|
31
29
|
const {
|
|
32
30
|
info, warning, error,
|
|
@@ -38,11 +36,6 @@ function tweakAssocs( model ) {
|
|
|
38
36
|
} = model.$functions;
|
|
39
37
|
const { environment } = model.$volatileFunctions;
|
|
40
38
|
|
|
41
|
-
// behavior depending on option `deprecated`:
|
|
42
|
-
const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
|
|
43
|
-
// TODO: we should get rid of noElementsExpansion soon; both
|
|
44
|
-
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
45
|
-
|
|
46
39
|
// Phase 5: rewrite associations
|
|
47
40
|
forEachDefinition( model, rewriteSimple );
|
|
48
41
|
// TODO: sequence not good enough with derived type of structure with
|
|
@@ -95,15 +88,14 @@ function tweakAssocs( model ) {
|
|
|
95
88
|
if (!target || target._service) // assoc to other service is OK
|
|
96
89
|
return;
|
|
97
90
|
if (!elem.$inferred) { // && !elem.target.$inferred
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} );
|
|
91
|
+
info( 'assoc-target-not-in-service', [ elem.target.location, elem ],
|
|
92
|
+
{ target, '#': (elem._main.query ? 'select' : 'define') }, {
|
|
93
|
+
std: 'Target $(TARGET) of association is outside any service', // not used
|
|
94
|
+
// eslint-disable-next-line max-len
|
|
95
|
+
define: 'Target $(TARGET) of explicitly defined association is outside any service',
|
|
96
|
+
// eslint-disable-next-line max-len
|
|
97
|
+
select: 'Target $(TARGET) of explicitly selected association is outside any service',
|
|
98
|
+
} );
|
|
107
99
|
}
|
|
108
100
|
else {
|
|
109
101
|
info( 'assoc-outside-service', [ elem.target.location, elem ],
|
|
@@ -114,7 +106,7 @@ function tweakAssocs( model ) {
|
|
|
114
106
|
|
|
115
107
|
function rewriteAssociationCheck( element ) {
|
|
116
108
|
const elem = element.items || element; // TODO v2: nested items
|
|
117
|
-
if (elem.elements
|
|
109
|
+
if (elem.elements)
|
|
118
110
|
forEachGeneric( elem, 'elements', rewriteAssociationCheck );
|
|
119
111
|
if (!elem.target)
|
|
120
112
|
return;
|
|
@@ -208,7 +200,7 @@ function tweakAssocs( model ) {
|
|
|
208
200
|
|
|
209
201
|
function rewriteAssociation( element ) {
|
|
210
202
|
let elem = element.items || element; // TODO v2: nested items
|
|
211
|
-
if (elem.elements
|
|
203
|
+
if (elem.elements)
|
|
212
204
|
forEachGeneric( elem, 'elements', rewriteAssociation );
|
|
213
205
|
if (!originTarget( elem ))
|
|
214
206
|
return;
|
|
@@ -287,7 +279,7 @@ function tweakAssocs( model ) {
|
|
|
287
279
|
// same (TODO later: set status whether rewrite changes anything),
|
|
288
280
|
// especially problematic are refs starting with $self:
|
|
289
281
|
setExpandStatus( elem, 'target' );
|
|
290
|
-
if (
|
|
282
|
+
if (elem._parent && elem._parent.kind === 'element') {
|
|
291
283
|
// managed association as sub element not supported yet
|
|
292
284
|
error( null, [ elem.location, elem ], {},
|
|
293
285
|
// eslint-disable-next-line max-len
|
|
@@ -370,7 +362,8 @@ function tweakAssocs( model ) {
|
|
|
370
362
|
const item = expr.path[root.kind === '$self' ? 1 : 0];
|
|
371
363
|
if (!item)
|
|
372
364
|
return; // just $self
|
|
373
|
-
|
|
365
|
+
// corresponding elem in including structure
|
|
366
|
+
const elem = (assoc._main.items || assoc._main).elements[item.id];
|
|
374
367
|
if (!(Array.isArray(elem) || // no msg for redefs
|
|
375
368
|
elem === item._artifact || // redirection for explicit def
|
|
376
369
|
elem._origin === item._artifact)) {
|
package/lib/compiler/utils.js
CHANGED
|
@@ -31,6 +31,10 @@ function annotationVal( anno ) {
|
|
|
31
31
|
function annotationIsFalse( anno ) { // falsy, but not null (unset)
|
|
32
32
|
return anno && (anno.val === false || anno.val === 0 || anno.val === '');
|
|
33
33
|
}
|
|
34
|
+
function annotationHasEllipsis( anno ) {
|
|
35
|
+
const { val } = anno || {};
|
|
36
|
+
return Array.isArray( val ) && val.some( v => v.literal === 'token' && v.val === '...' );
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* Set compiler-calculated annotation value.
|
|
@@ -91,6 +95,14 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
91
95
|
return elem;
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Set the member `elem` to have a _parent link to `parent` and a corresponding
|
|
100
|
+
* _main link. Also set the member's name accordingly, where argument `name`
|
|
101
|
+
* is most often the property `elem.name.id`.
|
|
102
|
+
*
|
|
103
|
+
* If argument `prop` is provided, add `elem` to the dictionary of that name,
|
|
104
|
+
* e.g. `elements`.
|
|
105
|
+
*/
|
|
94
106
|
function setMemberParent( elem, name, parent, prop ) {
|
|
95
107
|
if (prop) { // extension or structure include
|
|
96
108
|
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
@@ -103,9 +115,10 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
103
115
|
parent = parent._outer;
|
|
104
116
|
setLink( elem, '_parent', parent );
|
|
105
117
|
setLink( elem, '_main', parent._main || parent );
|
|
106
|
-
const parentName = parent.name || parent._outer
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
const parentName = parent.name || parent._outer?.name;
|
|
119
|
+
if (parentName) // may not be available in e.g. cast() - TODO recheck (#9503)
|
|
120
|
+
elem.name.absolute = parentName.absolute;
|
|
121
|
+
if (!parentName || name == null)
|
|
109
122
|
return;
|
|
110
123
|
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
111
124
|
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
@@ -184,8 +197,8 @@ function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = fa
|
|
|
184
197
|
*
|
|
185
198
|
* @param {XSN.Path} path
|
|
186
199
|
*/
|
|
187
|
-
function pathName(path) {
|
|
188
|
-
return (path.broken) ?
|
|
200
|
+
function pathName( path ) {
|
|
201
|
+
return (path && !path.broken) ? path.map( id => id.id ).join('.') : '';
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
/**
|
|
@@ -353,7 +366,7 @@ function traverseQueryExtra( main, callback ) {
|
|
|
353
366
|
// that value is only on elements, types, and params -> no other members
|
|
354
367
|
// when set, only on elem/art with expanded elements
|
|
355
368
|
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
356
|
-
// no
|
|
369
|
+
// no individual annotations on any (sub) member
|
|
357
370
|
// when set, traverse all parents where the value has been 'origin' before
|
|
358
371
|
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
359
372
|
// not counting propagated ones; set up to the definition (main artifact)
|
|
@@ -381,6 +394,7 @@ module.exports = {
|
|
|
381
394
|
pushLink,
|
|
382
395
|
annotationVal,
|
|
383
396
|
annotationIsFalse,
|
|
397
|
+
annotationHasEllipsis,
|
|
384
398
|
annotateWith,
|
|
385
399
|
setLink,
|
|
386
400
|
setArtifactLink,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const edmUtils = require('../edmUtils.js');
|
|
4
3
|
const { makeMessageFunction } = require('../../base/messages.js');
|
|
4
|
+
const { forEachDefinition } = require('../../model/csnUtils.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
/**************************************************************************************************
|
|
@@ -37,15 +37,14 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
37
37
|
let targetName = (typeof assoc.target === 'object') ? assoc.target.name : assoc.target;
|
|
38
38
|
let target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
|
|
39
39
|
|
|
40
|
-
let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key);
|
|
40
|
+
let keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
|
|
41
41
|
if (keyNames.length === 0) {
|
|
42
42
|
keyNames.push('MISSING');
|
|
43
43
|
warning(null, null, `in annotation preprocessing: target ${targetName} has no key`);
|
|
44
44
|
}
|
|
45
45
|
else if (keyNames.length > 1)
|
|
46
46
|
warning(null, null, `in annotation preprocessing: target ${targetName} has multiple key elements`);
|
|
47
|
-
|
|
48
|
-
// TODO: what happens if key of target is itself a managed association?
|
|
47
|
+
|
|
49
48
|
return keyNames[0];
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -58,15 +57,15 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
58
57
|
function resolveShortcuts() {
|
|
59
58
|
let art = null;
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
if(artifactName
|
|
60
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
61
|
+
if(artifactName === serviceName || artifactName.startsWith(serviceName + '.')) {
|
|
63
62
|
art = artifactName;
|
|
64
63
|
handleAnnotations(artifactName, artifact);
|
|
65
|
-
|
|
64
|
+
artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
|
|
66
65
|
handleAnnotations(elementName, element);
|
|
67
66
|
});
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
artifact.actions && Object.values(artifact.actions).forEach(action => {
|
|
68
|
+
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
70
69
|
handleAnnotations(paramName, param);
|
|
71
70
|
});
|
|
72
71
|
});
|
|
@@ -155,7 +154,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
155
154
|
return false;
|
|
156
155
|
}
|
|
157
156
|
let assoc = csn.definitions[art].elements[assocName];
|
|
158
|
-
if (!assoc || !
|
|
157
|
+
if (!assoc || !assoc.target) {
|
|
159
158
|
warning(null, null, `in annotation preprocessing/${aNameWithoutQualifier}: there is no association "${assocName}", ${ctx}`);
|
|
160
159
|
return false;
|
|
161
160
|
}
|
|
@@ -165,7 +164,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
165
164
|
}
|
|
166
165
|
else if (aNameWithoutQualifier === '@Common.ValueList.entity') {
|
|
167
166
|
// if both annotations are present, ignore 'entity' and raise a message
|
|
168
|
-
if (annoNames.map(x=>x.split('#')[0]).find(x=>(x
|
|
167
|
+
if (annoNames.map(x=>x.split('#')[0]).find(x=>(x==='@Common.ValueList.viaAssociation'))) {
|
|
169
168
|
warning(null, null, `in annotation preprocessing/@Common.ValueList: 'entity' is ignored, as 'viaAssociation' is present, ${ctx}`);
|
|
170
169
|
return false;
|
|
171
170
|
}
|
|
@@ -196,7 +195,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
196
195
|
// name of the element carrying the value help annotation
|
|
197
196
|
// if this is a managed assoc, use fk field instead (if there is a single one)
|
|
198
197
|
let localDataProp = carrierName.split('/').pop();
|
|
199
|
-
if (
|
|
198
|
+
if (carrier.target && carrier.on === undefined) {
|
|
200
199
|
localDataProp = localDataProp + fkSeparator + getKeyOfTargetOfManagedAssoc(carrier);
|
|
201
200
|
}
|
|
202
201
|
|
|
@@ -212,7 +211,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
212
211
|
// valueListProp: the (single) key field of the value list entity
|
|
213
212
|
// if no key or multiple keys -> warning
|
|
214
213
|
let valueListProp = null;
|
|
215
|
-
let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key );
|
|
214
|
+
let keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key && !vlEntity.elements[x].target );
|
|
216
215
|
if (keys.length === 0) {
|
|
217
216
|
warning(null, null, `in annotation preprocessing/value help shortcut: entity "${enameFull}" has no key, ${ctx}`);
|
|
218
217
|
return false;
|