@sap/cds-compiler 3.7.2 → 3.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -4
- package/bin/cdsc.js +3 -0
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +15 -0
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +61 -22
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +5 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +64 -22
- package/lib/base/messages.js +12 -7
- package/lib/base/model.js +3 -2
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/hasPersistedElements.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/onConditions.js +9 -6
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +1 -2
- package/lib/compiler/assert-consistency.js +25 -6
- package/lib/compiler/base.js +51 -2
- package/lib/compiler/builtins.js +15 -6
- package/lib/compiler/checks.js +4 -4
- package/lib/compiler/define.js +59 -80
- package/lib/compiler/extend.js +717 -498
- package/lib/compiler/finalize-parse-cdl.js +4 -3
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +2 -2
- package/lib/compiler/populate.js +17 -9
- package/lib/compiler/propagator.js +12 -5
- package/lib/compiler/resolve.js +26 -173
- package/lib/compiler/shared.js +20 -58
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +2 -2
- package/lib/edm/annotations/genericTranslation.js +124 -46
- package/lib/edm/csn2edm.js +22 -1
- package/lib/edm/edmPreprocessor.js +41 -21
- package/lib/gen/Dictionary.json +4 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4844 -4508
- package/lib/inspect/inspectPropagation.js +20 -36
- package/lib/json/from-csn.js +56 -7
- package/lib/json/to-csn.js +71 -110
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +49 -9
- package/lib/language/language.g4 +106 -83
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +19 -4
- package/lib/model/csnUtils.js +11 -74
- package/lib/model/revealInternalProperties.js +3 -0
- package/lib/optionProcessor.js +3 -0
- package/lib/render/toCdl.js +203 -104
- package/lib/render/toHdbcds.js +0 -1
- package/lib/render/toRename.js +14 -51
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/rewriteCalculatedElements.js +55 -14
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +21 -14
- package/lib/transform/parseExpr.js +2 -0
- package/lib/transform/transformUtilsNew.js +36 -9
- package/lib/transform/translateAssocsToJoins.js +11 -4
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
package/lib/compiler/define.js
CHANGED
|
@@ -119,7 +119,11 @@
|
|
|
119
119
|
|
|
120
120
|
'use strict';
|
|
121
121
|
|
|
122
|
-
const {
|
|
122
|
+
const {
|
|
123
|
+
forEachGeneric,
|
|
124
|
+
forEachInOrder,
|
|
125
|
+
forEachMember,
|
|
126
|
+
} = require('../base/model');
|
|
123
127
|
const shuffleGen = require('../base/shuffle');
|
|
124
128
|
const {
|
|
125
129
|
dictAdd, dictAddArray, dictForEach, pushToDict,
|
|
@@ -132,7 +136,6 @@ const {
|
|
|
132
136
|
dependsOnSilent,
|
|
133
137
|
pathName,
|
|
134
138
|
splitIntoPath,
|
|
135
|
-
annotationHasEllipsis,
|
|
136
139
|
isDirectComposition,
|
|
137
140
|
} = require('./utils');
|
|
138
141
|
const { compareLayer } = require('./moduleLayers');
|
|
@@ -156,22 +159,19 @@ function define( model ) {
|
|
|
156
159
|
const { options } = model;
|
|
157
160
|
// Get simplified "resolve" functionality and the message function:
|
|
158
161
|
const {
|
|
159
|
-
error, warning, info,
|
|
162
|
+
error, warning, info, messages,
|
|
160
163
|
} = model.$messageFunctions;
|
|
161
164
|
const {
|
|
162
165
|
resolveUncheckedPath,
|
|
163
166
|
} = model.$functions;
|
|
164
167
|
const { shuffleDict, shuffleArray } = shuffleGen( options.testMode );
|
|
165
168
|
|
|
166
|
-
const extensionsDict = Object.create(null);
|
|
167
169
|
Object.assign( model.$functions, {
|
|
168
170
|
shuffleDict,
|
|
169
171
|
shuffleArray,
|
|
170
172
|
initArtifact,
|
|
171
173
|
initMembers,
|
|
172
|
-
|
|
173
|
-
checkDefinitions,
|
|
174
|
-
initAnnotations,
|
|
174
|
+
checkDefinitions, // TODO: remove
|
|
175
175
|
} );
|
|
176
176
|
// During the definer, we can only resolve artifact references, i.e,
|
|
177
177
|
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
@@ -201,7 +201,7 @@ function define( model ) {
|
|
|
201
201
|
setLink( model, '_entities', [] ); // for entities with includes
|
|
202
202
|
model.$entity = 0;
|
|
203
203
|
model.$compositionTargets = Object.create(null);
|
|
204
|
-
model.$lateExtensions = Object.create(null); //
|
|
204
|
+
model.$lateExtensions = Object.create(null); // TODO: rename to $collectedExtensions
|
|
205
205
|
|
|
206
206
|
initBuiltins( model );
|
|
207
207
|
const sourceNames = shuffleArray( Object.keys( model.sources ) );
|
|
@@ -211,7 +211,7 @@ function define( model ) {
|
|
|
211
211
|
initNamespaceAndUsing( model.sources[name] );
|
|
212
212
|
dictForEach( model.definitions, initArtifact );
|
|
213
213
|
dictForEach( model.vocabularies, initVocabulary );
|
|
214
|
-
dictForEach(
|
|
214
|
+
dictForEach( model.$lateExtensions, e => e._extensions.forEach( initExtension ) );
|
|
215
215
|
|
|
216
216
|
addI18nBlocks();
|
|
217
217
|
}
|
|
@@ -397,19 +397,52 @@ function define( model ) {
|
|
|
397
397
|
return;
|
|
398
398
|
delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
|
|
399
399
|
ext.name.absolute = absolute; // definition might not be there yet, no _artifact link
|
|
400
|
-
|
|
400
|
+
const location = { file: '' }; // stupid required location
|
|
401
|
+
const late = model.$lateExtensions[absolute] ||
|
|
402
|
+
(model.$lateExtensions[absolute] = {
|
|
403
|
+
kind: 'annotate',
|
|
404
|
+
name: { absolute, location },
|
|
405
|
+
$inferred: '',
|
|
406
|
+
location,
|
|
407
|
+
});
|
|
408
|
+
pushToDict( late, '_extensions', ext );
|
|
401
409
|
if (!ext.artifacts)
|
|
402
410
|
return;
|
|
411
|
+
|
|
403
412
|
// Directly add the artifacts of context and service extension:
|
|
404
413
|
if (!model.$blocks)
|
|
405
414
|
model.$blocks = Object.create( null );
|
|
406
415
|
// Set block number for debugging (--raw-output):
|
|
407
416
|
// eslint-disable-next-line no-multi-assign
|
|
408
417
|
ext.name.select = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
|
|
418
|
+
// add "namespace" for the case that ext.artifacts is empty (TODO: later)
|
|
419
|
+
// now add all definitions in ext.artifacts:
|
|
409
420
|
const prefix = `${ absolute }.`;
|
|
410
421
|
dictForEach( ext.artifacts, a => addArtifact( a, ext, prefix ) );
|
|
411
422
|
}
|
|
412
423
|
|
|
424
|
+
function initExtension( parent ) {
|
|
425
|
+
forEachMember( parent, function init( sub ) {
|
|
426
|
+
if (sub.kind !== 'extend' && sub.kind !== 'annotate')
|
|
427
|
+
return; // for defs inside, set somewhere else - TODO: rethink
|
|
428
|
+
setLink( sub, '_block', parent._block );
|
|
429
|
+
setLink( sub, '_parent', parent );
|
|
430
|
+
setLink( sub, '_main', parent._main || parent );
|
|
431
|
+
initExtension( sub );
|
|
432
|
+
} );
|
|
433
|
+
if (parent.kind !== 'extend')
|
|
434
|
+
return;
|
|
435
|
+
if (parent.columns) // TODO: sub queries? expand/inline?
|
|
436
|
+
parent.columns.forEach( c => setLink( c, '_block', parent._block ) );
|
|
437
|
+
if (parent.scale && !parent.precision) {
|
|
438
|
+
// TODO: where could we store the location of the name?
|
|
439
|
+
error( 'syntax-missing-type-property', [ parent.scale.location ],
|
|
440
|
+
{ prop: 'scale', otherprop: 'precision' },
|
|
441
|
+
'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
|
|
442
|
+
parent.scale = undefined; // no consequential error
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
413
446
|
function addVocabulary( vocab, block, prefix ) {
|
|
414
447
|
setLink( vocab, '_block', block );
|
|
415
448
|
const { name } = vocab;
|
|
@@ -533,13 +566,10 @@ function define( model ) {
|
|
|
533
566
|
initArtifactParentLink( art, model.definitions );
|
|
534
567
|
const block = art._block;
|
|
535
568
|
checkRedefinition( art );
|
|
536
|
-
initAnnotations( art, block );
|
|
537
569
|
initMembers( art, art, block );
|
|
538
570
|
initDollarSelf( art ); // $self
|
|
539
571
|
if (art.params)
|
|
540
572
|
initDollarParameters( art );
|
|
541
|
-
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
542
|
-
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
543
573
|
|
|
544
574
|
if (!art.query)
|
|
545
575
|
return;
|
|
@@ -560,7 +590,6 @@ function define( model ) {
|
|
|
560
590
|
initArtifactParentLink( art, model.vocabularies );
|
|
561
591
|
checkRedefinition( art );
|
|
562
592
|
const block = art._block;
|
|
563
|
-
initAnnotations( art, block );
|
|
564
593
|
initMembers( art, art, block );
|
|
565
594
|
}
|
|
566
595
|
|
|
@@ -586,57 +615,6 @@ function define( model ) {
|
|
|
586
615
|
parent._subArtifacts[absolute.substring( dot + 1 )] = art; // not dictAdd()
|
|
587
616
|
}
|
|
588
617
|
|
|
589
|
-
/** Initialize the extension `ext`.
|
|
590
|
-
*
|
|
591
|
-
* Currently:
|
|
592
|
-
*
|
|
593
|
-
* - initialize annotations (set _block, $priority, `...` check) on "main"
|
|
594
|
-
* extension and its columns do more later
|
|
595
|
-
* - for members in compile(): init annotations via extendMembers/annotateMembers
|
|
596
|
-
* - for members in parse.cdl(): init annotation via initMembers
|
|
597
|
-
*
|
|
598
|
-
* In the future (after name cleanup): -- TODO --
|
|
599
|
-
*
|
|
600
|
-
* - also initialize members and member extensions/annotations here
|
|
601
|
-
* - we might also do other things, like calculating whether an `extend` is
|
|
602
|
-
* `annotate`-like, i.e. only contains name-resolution irrelevant extensions.
|
|
603
|
-
*/
|
|
604
|
-
function initExtension( ext ) {
|
|
605
|
-
const block = ext._block;
|
|
606
|
-
initAnnotations( ext, block, ext.kind );
|
|
607
|
-
if (ext.columns) // the columns themselves are "definitions"
|
|
608
|
-
ext.columns.forEach( col => initAnnotations( col, block ) );
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Set _block links for annotations (necessary for layering) and do a late
|
|
612
|
-
// syntax check (`...` only with extensions, not definitions).
|
|
613
|
-
// extKind is either ext.kind (=art is extension) or false (=art is not an extension)
|
|
614
|
-
function initAnnotations( art, block, extKind = false ) {
|
|
615
|
-
// TODO: think of removing $priority, then
|
|
616
|
-
// no _block: define, _block: annotate/extend/edmx
|
|
617
|
-
// would fit with extending defs with props like length
|
|
618
|
-
for (const prop in art) {
|
|
619
|
-
if (prop.charAt(0) === '@' || prop === 'doc') {
|
|
620
|
-
const anno = art[prop];
|
|
621
|
-
// TODO: make anno never be an array, see addAnnotation() in genericAntlrParser
|
|
622
|
-
if (Array.isArray( anno ))
|
|
623
|
-
anno.forEach( init );
|
|
624
|
-
else
|
|
625
|
-
init( anno );
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return;
|
|
629
|
-
|
|
630
|
-
function init( anno ) {
|
|
631
|
-
setLink( anno, '_block', block );
|
|
632
|
-
anno.$priority = extKind;
|
|
633
|
-
if (!extKind && annotationHasEllipsis( anno )) {
|
|
634
|
-
error( 'anno-unexpected-ellipsis',
|
|
635
|
-
[ anno.name.location, art ], { code: '...' } );
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
618
|
// Init special things: -------------------------------------------------------
|
|
641
619
|
|
|
642
620
|
function initDollarSelf( art ) {
|
|
@@ -772,10 +750,11 @@ function define( model ) {
|
|
|
772
750
|
// (tab refs on the right of union are unnecessary)
|
|
773
751
|
}
|
|
774
752
|
else if (table.query) {
|
|
775
|
-
if (!table.name
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
753
|
+
if (!table.name?.id) {
|
|
754
|
+
// We don't worry about duplicate names here.
|
|
755
|
+
const id = `$_select_${ query._main.$queries.length + 1 }__`;
|
|
756
|
+
table.name = { id, location: table.location, $inferred: '$internal' };
|
|
757
|
+
table.$inferred = '$internal';
|
|
779
758
|
}
|
|
780
759
|
addAsAlias();
|
|
781
760
|
// Store _origin to leading query of table.query for name resolution
|
|
@@ -822,8 +801,15 @@ function define( model ) {
|
|
|
822
801
|
table.kind = '$tableAlias';
|
|
823
802
|
setMemberParent( table, table.name.id, query );
|
|
824
803
|
setLink( table, '_block', query._block );
|
|
825
|
-
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
826
|
-
|
|
804
|
+
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
|
|
805
|
+
if (tableAlias.$inferred === '$internal') {
|
|
806
|
+
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
807
|
+
error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
|
|
808
|
+
{ '#': 'duplicate', code: 'as ‹alias›' });
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
812
|
+
}
|
|
827
813
|
} );
|
|
828
814
|
// also add to JOIN nodes for name restrictions:
|
|
829
815
|
for (const p of joinParents) {
|
|
@@ -831,7 +817,7 @@ function define( model ) {
|
|
|
831
817
|
// already used for duplicate aliases of queries:
|
|
832
818
|
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
833
819
|
}
|
|
834
|
-
if (table.name
|
|
820
|
+
if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
|
|
835
821
|
warning( 'name-invalid-dollar-alias', [ table.name.location, table ], {
|
|
836
822
|
'#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
|
|
837
823
|
name: '$',
|
|
@@ -934,7 +920,6 @@ function define( model ) {
|
|
|
934
920
|
// Either expression (value), expand or new association (target && type)
|
|
935
921
|
else if (col.value || col.expand || (col.target && col.type)) {
|
|
936
922
|
setLink( col, '_block', parent._block );
|
|
937
|
-
initAnnotations( col, parent._block );
|
|
938
923
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
939
924
|
if (col.doc) {
|
|
940
925
|
warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
|
|
@@ -1009,7 +994,7 @@ function define( model ) {
|
|
|
1009
994
|
*
|
|
1010
995
|
* If not for extensions: construct === parent
|
|
1011
996
|
*
|
|
1012
|
-
* Param `initExtensions` is for parse.cdl
|
|
997
|
+
* Param `initExtensions` is for parse.cdl - TODO delete
|
|
1013
998
|
*
|
|
1014
999
|
* TODO: separate extension!
|
|
1015
1000
|
*/
|
|
@@ -1166,7 +1151,6 @@ function define( model ) {
|
|
|
1166
1151
|
setMemberParent( elem, name, parent, add && prop );
|
|
1167
1152
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1168
1153
|
checkRedefinition( elem );
|
|
1169
|
-
initAnnotations( elem, bl );
|
|
1170
1154
|
initMembers( elem, elem, bl, initExtensions );
|
|
1171
1155
|
if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
|
|
1172
1156
|
initBoundSelfParam( elem.params );
|
|
@@ -1181,11 +1165,6 @@ function define( model ) {
|
|
|
1181
1165
|
if (!elem.target)
|
|
1182
1166
|
elem.type = { ...elem.value.type, $inferred: 'cast' };
|
|
1183
1167
|
}
|
|
1184
|
-
if (!isBetaEnabled( options, 'calculatedElements' )) {
|
|
1185
|
-
const loc = [ elem.value.location, elem ];
|
|
1186
|
-
// TODO: this could be considered a syntax check
|
|
1187
|
-
message( 'def-unsupported-calc-elem', loc, { '#': 'std' } );
|
|
1188
|
-
}
|
|
1189
1168
|
elem.$syntax = 'calc';
|
|
1190
1169
|
}
|
|
1191
1170
|
}
|