@sap/cds-compiler 4.7.4 → 4.8.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 +47 -2
- package/bin/cdsc.js +15 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +5 -1
- package/lib/api/main.js +61 -23
- package/lib/api/options.js +40 -0
- package/lib/base/builtins.js +89 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +50 -33
- package/lib/base/messages.js +71 -16
- package/lib/base/model.js +0 -2
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +6 -22
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +61 -11
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +2 -2
- package/lib/compiler/index.js +9 -9
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +3 -0
- package/lib/compiler/resolve.js +6 -20
- package/lib/compiler/shared.js +1 -1
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +113 -55
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +3 -3
- package/lib/edm/edmInboundChecks.js +24 -5
- package/lib/edm/edmPreprocessor.js +46 -20
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +9 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1941 -1850
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +8 -7
- package/lib/json/to-csn.js +12 -7
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +9 -10
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/main.js +8 -1
- package/lib/model/cloneCsn.js +15 -6
- package/lib/model/csnRefs.js +141 -35
- package/lib/model/csnUtils.js +1 -4
- package/lib/model/enrichCsn.js +1 -1
- package/lib/modelCompare/compare.js +106 -92
- package/lib/optionProcessor.js +23 -1
- package/lib/render/toCdl.js +3 -2
- 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 +1 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/draft/odata.js +16 -17
- package/lib/transform/effective/main.js +6 -3
- package/lib/transform/effective/misc.js +18 -8
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +8 -7
- package/lib/transform/forRelationalDB.js +103 -112
- package/lib/transform/odata/flattening.js +82 -44
- package/lib/transform/odata/toFinalBaseType.js +9 -25
- package/lib/transform/odata/typesExposure.js +28 -15
- package/lib/transform/parseExpr.js +0 -3
- package/lib/transform/transformUtils.js +12 -8
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
- 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/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 +1 -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/lib/checks/validator.js
CHANGED
|
@@ -75,7 +75,7 @@ const forRelationalDBArtifactValidators = [
|
|
|
75
75
|
// sql.prepend/append
|
|
76
76
|
checkSqlAnnotationOnArtifact,
|
|
77
77
|
// strip down CSN to reduce it's size by removing non-sql relevant parts
|
|
78
|
-
|
|
78
|
+
deleteBoundActions,
|
|
79
79
|
];
|
|
80
80
|
|
|
81
81
|
const forRelationalDBCsnValidators = [
|
|
@@ -282,33 +282,17 @@ function forOdata( csn, that ) {
|
|
|
282
282
|
});
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
const dbRelevantKinds = {
|
|
286
|
-
entity: true,
|
|
287
|
-
type: true,
|
|
288
|
-
aspect: true,
|
|
289
|
-
service: true,
|
|
290
|
-
context: true,
|
|
291
|
-
};
|
|
292
|
-
|
|
293
285
|
/**
|
|
294
286
|
* Shrink the CSN by
|
|
295
287
|
* - deleting bound actions
|
|
296
|
-
*
|
|
288
|
+
*
|
|
289
|
+
* Artifacts can only be shrunk later, when types are resolved
|
|
297
290
|
*
|
|
298
291
|
* @param {CSN.Artifact} artifact
|
|
299
|
-
* @param {string} artifactName
|
|
300
292
|
*/
|
|
301
|
-
function
|
|
302
|
-
if (this.options.transformation !== 'effective')
|
|
303
|
-
|
|
304
|
-
this.csn.definitions[artifactName] = {
|
|
305
|
-
kind: 'action',
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
else if (artifact.actions) {
|
|
309
|
-
delete artifact.actions;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
293
|
+
function deleteBoundActions( artifact ) {
|
|
294
|
+
if (this.options.transformation !== 'effective')
|
|
295
|
+
delete artifact.actions;
|
|
312
296
|
}
|
|
313
297
|
|
|
314
298
|
module.exports = { forRelationalDB, forOdata };
|
|
@@ -66,10 +66,9 @@
|
|
|
66
66
|
|
|
67
67
|
'use strict';
|
|
68
68
|
|
|
69
|
+
const { Location } = require('../base/location');
|
|
69
70
|
const { locationString, hasErrors } = require('../base/messages');
|
|
70
|
-
const {
|
|
71
|
-
XsnSource, XsnName, XsnArtifact, CsnLocation,
|
|
72
|
-
} = require('./classes');
|
|
71
|
+
const { XsnSource, XsnName, XsnArtifact } = require('./xsn-model');
|
|
73
72
|
|
|
74
73
|
|
|
75
74
|
// Properties that can appear where a type can have type arguments.
|
|
@@ -135,7 +134,7 @@ function assertConsistency( model, stage ) {
|
|
|
135
134
|
// with syntax errors (currently even internal artifacts like $using):
|
|
136
135
|
isRequired: parent => noSyntaxErrors() || parent && parent.kind,
|
|
137
136
|
kind: true,
|
|
138
|
-
instanceOf:
|
|
137
|
+
instanceOf: Location,
|
|
139
138
|
requires: [ 'file' ], // line is optional in top-level location
|
|
140
139
|
optional: [ 'line', 'col', 'endLine', 'endCol', '$notFound' ],
|
|
141
140
|
schema: {
|
|
@@ -317,7 +316,6 @@ function assertConsistency( model, stage ) {
|
|
|
317
316
|
'kind', 'name', '_block', '_parent', '_main', 'elements',
|
|
318
317
|
'_effectiveType', '$effectiveSeqNo', '_origin', '_joinParent', '$joinArgsIndex',
|
|
319
318
|
'$duplicates', // duplicate query in FROM clause
|
|
320
|
-
'$inferred', // table alias with $inferred: '$internal'
|
|
321
319
|
],
|
|
322
320
|
},
|
|
323
321
|
none: { optional: () => true }, // parse error
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -64,18 +64,6 @@ const typeParameters = {
|
|
|
64
64
|
// a.k.a "typeProperties"
|
|
65
65
|
typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
|
|
66
66
|
|
|
67
|
-
/**
|
|
68
|
-
* Properties that are required next to `=` to make an annotation value an actual expression
|
|
69
|
-
* and not some foreign structure.
|
|
70
|
-
*
|
|
71
|
-
* @type {string[]}
|
|
72
|
-
*/
|
|
73
|
-
const xprInAnnoProperties = [
|
|
74
|
-
'ref', 'xpr', 'list', 'literal', 'val',
|
|
75
|
-
'#', 'func', 'args', 'SELECT', 'SET',
|
|
76
|
-
'cast',
|
|
77
|
-
];
|
|
78
|
-
|
|
79
67
|
|
|
80
68
|
// const hana = {
|
|
81
69
|
// BinaryFloat: {},
|
|
@@ -87,15 +75,6 @@ const xprInAnnoProperties = [
|
|
|
87
75
|
// hana: { kind: 'context' },
|
|
88
76
|
// };
|
|
89
77
|
|
|
90
|
-
/**
|
|
91
|
-
* Functions without parentheses in CDL (common standard SQL-92 functions)
|
|
92
|
-
* (do not add more - make it part of the SQL renderer to remove parentheses for
|
|
93
|
-
* other funny SQL functions like CURRENT_UTCTIMESTAMP).
|
|
94
|
-
*/
|
|
95
|
-
const functionsWithoutParens = [
|
|
96
|
-
'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
|
|
97
|
-
'CURRENT_USER', 'SESSION_USER', 'SYSTEM_USER',
|
|
98
|
-
];
|
|
99
78
|
|
|
100
79
|
const specialFunctions = compileFunctions( {
|
|
101
80
|
'': [ // the default
|
|
@@ -317,13 +296,6 @@ function checkDate( year, month, day ) {
|
|
|
317
296
|
return !Number.isNaN( year ) && month > 0 && month < 13 && day > 0 && day < 32;
|
|
318
297
|
}
|
|
319
298
|
|
|
320
|
-
/**
|
|
321
|
-
* Return whether JSON object `val` is a representation for an annotation expression
|
|
322
|
-
*/
|
|
323
|
-
function isAnnotationExpression( val ) {
|
|
324
|
-
return val?.['='] !== undefined && xprInAnnoProperties.some( prop => val[prop] !== undefined );
|
|
325
|
-
}
|
|
326
|
-
|
|
327
299
|
/**
|
|
328
300
|
* Check that the given time is within boundaries.
|
|
329
301
|
* Checks according to ISO 8601.
|
|
@@ -371,44 +343,6 @@ function isGeoTypeName( typeName ) {
|
|
|
371
343
|
return typeCategories.geo.includes( typeName );
|
|
372
344
|
}
|
|
373
345
|
|
|
374
|
-
/**
|
|
375
|
-
* Checks whether the given absolute path is inside a reserved namespace.
|
|
376
|
-
*
|
|
377
|
-
* @param {string} absolute
|
|
378
|
-
* @returns {boolean}
|
|
379
|
-
*/
|
|
380
|
-
function isInReservedNamespace( absolute ) {
|
|
381
|
-
return absolute === 'cds' || absolute.startsWith( 'cds.' ) &&
|
|
382
|
-
!absolute.match( /^cds\.foundation(\.|$)/ ) &&
|
|
383
|
-
!absolute.match( /^cds\.outbox(\.|$)/ ) && // Requested by Node runtime
|
|
384
|
-
!absolute.match( /^cds\.xt(\.|$)/ ); // Requested by Mtx
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Tell if a type is (directly) a builtin type
|
|
389
|
-
* Note that in CSN builtins are not in the definition of the model, so we can only
|
|
390
|
-
* check against their absolute names. Builtin types are "cds.<something>", i.e. they
|
|
391
|
-
* are directly in 'cds', but not for example in 'cds.foundation'.
|
|
392
|
-
*
|
|
393
|
-
* TODO: Handle `{ ref: [ "cds.Integer" ] }`
|
|
394
|
-
*
|
|
395
|
-
* @param {string} type
|
|
396
|
-
* @returns {boolean}
|
|
397
|
-
*/
|
|
398
|
-
function isBuiltinType( type ) {
|
|
399
|
-
return typeof type === 'string' && isInReservedNamespace( type );
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Tell if a name is a magic variable
|
|
404
|
-
*
|
|
405
|
-
* @param {string} name
|
|
406
|
-
* @returns {boolean}
|
|
407
|
-
*/
|
|
408
|
-
function isMagicVariable( name ) {
|
|
409
|
-
return typeof name === 'string' && Object.prototype.hasOwnProperty.call(magicVariables, name);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
346
|
/**
|
|
413
347
|
* Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
|
|
414
348
|
* `definitions` of the XSN model as well as to `$builtins`.
|
|
@@ -424,8 +358,6 @@ function initBuiltins( model ) {
|
|
|
424
358
|
|
|
425
359
|
// Also add the core artifacts to model.definitions`
|
|
426
360
|
const c = { ...core };
|
|
427
|
-
if (!isBetaEnabled( model.options, 'vectorType' ))
|
|
428
|
-
delete c.Vector;
|
|
429
361
|
model.$builtins = env( c, 'cds.', cds );
|
|
430
362
|
model.$builtins.cds = cds;
|
|
431
363
|
|
|
@@ -541,14 +473,8 @@ function initBuiltins( model ) {
|
|
|
541
473
|
|
|
542
474
|
module.exports = {
|
|
543
475
|
typeParameters,
|
|
544
|
-
xprInAnnoProperties,
|
|
545
|
-
functionsWithoutParens,
|
|
546
476
|
specialFunctions,
|
|
547
477
|
quotedLiteralPatterns,
|
|
548
478
|
initBuiltins,
|
|
549
|
-
isAnnotationExpression,
|
|
550
|
-
isInReservedNamespace,
|
|
551
|
-
isBuiltinType,
|
|
552
|
-
isMagicVariable,
|
|
553
479
|
isGeoTypeName,
|
|
554
480
|
};
|
package/lib/compiler/checks.js
CHANGED
|
@@ -29,7 +29,7 @@ const $location = Symbol.for( 'cds.$location' );
|
|
|
29
29
|
*/
|
|
30
30
|
function check( model ) {
|
|
31
31
|
const {
|
|
32
|
-
error, warning, info,
|
|
32
|
+
error, warning, info, message,
|
|
33
33
|
} = model.$messageFunctions;
|
|
34
34
|
|
|
35
35
|
checkSapCommonLocale( model );
|
|
@@ -41,6 +41,7 @@ function check( model ) {
|
|
|
41
41
|
return;
|
|
42
42
|
|
|
43
43
|
function checkDefinition( def ) {
|
|
44
|
+
checkEvent( def );
|
|
44
45
|
checkGenericConstruct( def );
|
|
45
46
|
if (def.includes && def.elements)
|
|
46
47
|
checkElementIncludeOverride( def );
|
|
@@ -49,6 +50,16 @@ function check( model ) {
|
|
|
49
50
|
def.$queries.forEach( checkQuery );
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
function checkEvent( def ) {
|
|
54
|
+
// Ensure that events are structured. Up to compiler v4, we allowed non-structured events,
|
|
55
|
+
// because when we introduced them, it was not fully specified what they are.
|
|
56
|
+
// TODO(v5):
|
|
57
|
+
// - Make this a (configurable) error; move to acceptTypeOrElement
|
|
58
|
+
// - require type/elements to be set in parser
|
|
59
|
+
if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)
|
|
60
|
+
message( 'def-expected-structured', [ (def.type || def.name).location, def ] );
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
function checkAnnotationDefinition( art ) {
|
|
53
64
|
// TODO: Should we check elements similar to definition-elements as well?
|
|
54
65
|
checkEnumType( art );
|
|
@@ -61,6 +72,10 @@ function check( model ) {
|
|
|
61
72
|
function checkGenericConstruct( art ) {
|
|
62
73
|
checkName( art );
|
|
63
74
|
checkTypeArguments( art );
|
|
75
|
+
|
|
76
|
+
if (art.value && !art.$calcDepElement && art.type)
|
|
77
|
+
checkTypeCast( art.value, art );
|
|
78
|
+
|
|
64
79
|
if (model.vocabularies) {
|
|
65
80
|
Object.keys( art )
|
|
66
81
|
.filter( a => a.startsWith( '@' ) )
|
|
@@ -202,13 +217,38 @@ function check( model ) {
|
|
|
202
217
|
}
|
|
203
218
|
}
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Check the type in an SQL cast expression.
|
|
222
|
+
*
|
|
223
|
+
* @param xpr
|
|
224
|
+
* @param {XSN.Artifact} user
|
|
225
|
+
*/
|
|
226
|
+
function requireExplicitTypeInSqlCast( xpr, user ) {
|
|
227
|
+
if (!xpr.type) {
|
|
228
|
+
error( 'expr-missing-type', [ xpr.location, user ], { },
|
|
208
229
|
'Missing type in SQL cast function' );
|
|
209
230
|
}
|
|
210
231
|
}
|
|
211
232
|
|
|
233
|
+
function checkTypeCast( xpr, user ) {
|
|
234
|
+
const isCast = (xpr.op?.val === 'cast');
|
|
235
|
+
const elem = isCast
|
|
236
|
+
? xpr.args[0]?._artifact
|
|
237
|
+
: xpr._artifact;
|
|
238
|
+
const type = isCast ? xpr.type : user.type;
|
|
239
|
+
if (elem && type) { // has explicit type
|
|
240
|
+
if (type._artifact?.elements)
|
|
241
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'to-structure' } );
|
|
242
|
+
else if (elem.elements) // TODO: calc elements
|
|
243
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-structure' } );
|
|
244
|
+
else if (elem.target && !type._artifact?.target)
|
|
245
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-assoc' } );
|
|
246
|
+
else if (!elem.target && type._artifact?.target && !user.type?.$inferred)
|
|
247
|
+
// $inferred already reported in resolve.js
|
|
248
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'assoc' } );
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
212
252
|
function checkLocalizedElement( elem ) {
|
|
213
253
|
if (elem.localized?.val) {
|
|
214
254
|
const type = elem._effectiveType;
|
|
@@ -340,11 +380,8 @@ function check( model ) {
|
|
|
340
380
|
.find( name => !enumNode.enum[name].value );
|
|
341
381
|
if (emptyValue) {
|
|
342
382
|
const failedEnum = enumNode.enum[emptyValue];
|
|
343
|
-
|
|
383
|
+
message( 'type-missing-enum-value', [ failedEnum.location, failedEnum ], {
|
|
344
384
|
'#': isNumeric ? 'numeric' : 'std', name: emptyValue,
|
|
345
|
-
}, {
|
|
346
|
-
std: 'Missing value for non-string enum element $(NAME)',
|
|
347
|
-
numeric: 'Missing value for numeric enum element $(NAME)',
|
|
348
385
|
} );
|
|
349
386
|
}
|
|
350
387
|
}
|
|
@@ -551,15 +588,26 @@ function check( model ) {
|
|
|
551
588
|
if (xorElements) {
|
|
552
589
|
// one of the two elements is not structured
|
|
553
590
|
const prop = !elem.elements ? 'new-not-structured' : 'old-not-structured';
|
|
554
|
-
const name = elem.name.id;
|
|
555
591
|
// Position at type/struct, not name
|
|
556
592
|
const loc = elem.type?.location || elem.elements?.[$location] || elem.location;
|
|
557
593
|
error( 'ref-invalid-override', [ loc, elem ],
|
|
558
|
-
{ '#': prop, art: original._main, name } );
|
|
594
|
+
{ '#': prop, art: original._main, name: elem.name.id } );
|
|
559
595
|
return false;
|
|
560
596
|
}
|
|
561
597
|
else if (original.elements &&
|
|
562
|
-
|
|
598
|
+
!checkSubStructureOverride( elem, elem.elements, original.elements )) {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const xorTarget = !(elem.target || elem.targetAspect) !==
|
|
603
|
+
!(original.target || original.targetAspect);
|
|
604
|
+
if (xorTarget) {
|
|
605
|
+
// one of the two elements is not an association
|
|
606
|
+
const prop = !elem.target ? 'new-not-target' : 'old-not-target';
|
|
607
|
+
// Position at type/assoc, not name
|
|
608
|
+
const loc = elem.target?.location || elem.type?.location || elem.location;
|
|
609
|
+
error( 'ref-invalid-override', [ loc, elem ],
|
|
610
|
+
{ '#': prop, art: original._main, name: elem.name.id } );
|
|
563
611
|
return false;
|
|
564
612
|
}
|
|
565
613
|
return true;
|
|
@@ -599,6 +647,7 @@ function check( model ) {
|
|
|
599
647
|
checkExpressionAssociationUsage( xpr, user, false );
|
|
600
648
|
if (xpr.op?.val === 'cast') {
|
|
601
649
|
requireExplicitTypeInSqlCast( xpr, user );
|
|
650
|
+
checkTypeCast( xpr, user );
|
|
602
651
|
checkTypeArguments( xpr, user );
|
|
603
652
|
}
|
|
604
653
|
}
|
|
@@ -631,6 +680,7 @@ function check( model ) {
|
|
|
631
680
|
// the cast, as otherwise we will check it twice (once here, once via element).
|
|
632
681
|
if (elem.value?.op?.val === 'cast' && elem.type?.$inferred !== 'cast') {
|
|
633
682
|
requireExplicitTypeInSqlCast( elem.value, elem );
|
|
683
|
+
checkTypeCast( elem.value, elem );
|
|
634
684
|
checkTypeArguments( elem.value, elem );
|
|
635
685
|
}
|
|
636
686
|
visitSubExpression( elem.value, elem, (xpr) => {
|
package/lib/compiler/define.js
CHANGED
|
@@ -140,7 +140,8 @@ const {
|
|
|
140
140
|
targetCantBeAspect,
|
|
141
141
|
} = require('./utils');
|
|
142
142
|
const { compareLayer } = require('./moduleLayers');
|
|
143
|
-
const { initBuiltins
|
|
143
|
+
const { initBuiltins } = require('./builtins');
|
|
144
|
+
const { isInReservedNamespace } = require('../base/builtins');
|
|
144
145
|
|
|
145
146
|
const $location = Symbol.for( 'cds.$location' );
|
|
146
147
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
@@ -747,7 +748,6 @@ function define( model ) {
|
|
|
747
748
|
// We don't worry about duplicate names here.
|
|
748
749
|
const id = `$_select_${ query._main.$queries.length + 1 }__`;
|
|
749
750
|
table.name = { id, location: table.location, $inferred: '$internal' };
|
|
750
|
-
table.$inferred = '$internal';
|
|
751
751
|
}
|
|
752
752
|
addAsAlias();
|
|
753
753
|
// Store _origin to leading query of table.query for name resolution
|
|
@@ -796,7 +796,7 @@ function define( model ) {
|
|
|
796
796
|
setMemberParent( table, table.name.id, query );
|
|
797
797
|
setLink( table, '_block', query._block );
|
|
798
798
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
|
|
799
|
-
if (tableAlias.$inferred === '$internal') {
|
|
799
|
+
if (tableAlias.name.$inferred === '$internal') {
|
|
800
800
|
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
801
801
|
// TODO: the semanticLoc query is not initialized yet, and thus cannot
|
|
802
802
|
// be used here
|
package/lib/compiler/extend.js
CHANGED
|
@@ -25,12 +25,12 @@ const {
|
|
|
25
25
|
} = require('./utils');
|
|
26
26
|
const layers = require('./moduleLayers');
|
|
27
27
|
const { CompilerAssertion } = require('../base/error');
|
|
28
|
-
const {
|
|
28
|
+
const { Location } = require('../base/location');
|
|
29
29
|
|
|
30
30
|
const $location = Symbol.for( 'cds.$location' );
|
|
31
31
|
|
|
32
32
|
// attach stupid location - TODO: remove in v5
|
|
33
|
-
const genLocation = new
|
|
33
|
+
const genLocation = new Location( '' );
|
|
34
34
|
|
|
35
35
|
const draftElements = [
|
|
36
36
|
'IsActiveEntity',
|
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,
|
|
@@ -93,7 +93,7 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
|
|
|
93
93
|
return parser( source, filename, options, messageFunctions );
|
|
94
94
|
|
|
95
95
|
const model = new XsnSource();
|
|
96
|
-
model.location = new
|
|
96
|
+
model.location = new Location( filename );
|
|
97
97
|
messageFunctions.error( 'file-unknown-ext', emptyWeakLocation( filename ),
|
|
98
98
|
{ file: ext, '#': !ext && 'none' }, {
|
|
99
99
|
std: 'Unknown file extension $(FILE)',
|
|
@@ -174,12 +174,12 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
174
174
|
return []; // no further dependency processing
|
|
175
175
|
// no parallel readAndParse with same resolved filename should read the file,
|
|
176
176
|
// also ensure deterministic sequence in sources:
|
|
177
|
-
sources[filename] = { location: new
|
|
177
|
+
sources[filename] = { location: new Location( rel ) };
|
|
178
178
|
|
|
179
179
|
const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
|
|
180
180
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
181
181
|
sources[filename] = ast;
|
|
182
|
-
ast.location = new
|
|
182
|
+
ast.location = new Location( rel );
|
|
183
183
|
ast.dirname = path.dirname( filename );
|
|
184
184
|
assertConsistency( ast, options );
|
|
185
185
|
|
|
@@ -293,7 +293,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
293
293
|
}
|
|
294
294
|
// no parallel readAndParse with same resolved filename should read the file,
|
|
295
295
|
// also ensure deterministic sequence in a.sources:
|
|
296
|
-
a.sources[filename] = { location: new
|
|
296
|
+
a.sources[filename] = { location: new Location( rel ) };
|
|
297
297
|
|
|
298
298
|
cdsFs( fileCache, options.traceFs ).readFileSync( filename, 'utf8', (err, source) => {
|
|
299
299
|
if (err) {
|
|
@@ -303,7 +303,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
303
303
|
try {
|
|
304
304
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
305
305
|
a.sources[filename] = ast;
|
|
306
|
-
ast.location = new
|
|
306
|
+
ast.location = new Location( rel );
|
|
307
307
|
ast.dirname = path.dirname( filename );
|
|
308
308
|
assertConsistency( ast, options );
|
|
309
309
|
cb( null, ast );
|
|
@@ -374,7 +374,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
374
374
|
if (typeof source === 'string') {
|
|
375
375
|
const ast = parseX( source, filename, options, model.$messageFunctions );
|
|
376
376
|
sources[filename] = ast;
|
|
377
|
-
ast.location = new
|
|
377
|
+
ast.location = new Location( filename );
|
|
378
378
|
assertConsistency( ast, options );
|
|
379
379
|
}
|
|
380
380
|
else if (options.$xsnObjects) { // source is a XSN object with option $xsnObjects
|
|
@@ -383,7 +383,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
383
383
|
else { // source is a CSN object
|
|
384
384
|
const ast = parseCsn.augment( source, filename, options, model.$messageFunctions );
|
|
385
385
|
sources[filename] = ast;
|
|
386
|
-
ast.location = new
|
|
386
|
+
ast.location = new Location( filename );
|
|
387
387
|
assertConsistency( ast, options );
|
|
388
388
|
}
|
|
389
389
|
|
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,6 +13,7 @@ const {
|
|
|
13
13
|
forEachMember,
|
|
14
14
|
forEachGeneric,
|
|
15
15
|
isDeprecatedEnabled,
|
|
16
|
+
isBetaEnabled,
|
|
16
17
|
} = require( '../base/model');
|
|
17
18
|
const {
|
|
18
19
|
setLink,
|
|
@@ -77,6 +78,8 @@ function propagate( model ) {
|
|
|
77
78
|
const { warning, throwWithError } = model.$messageFunctions;
|
|
78
79
|
|
|
79
80
|
forEachDefinition( model, run );
|
|
81
|
+
if (isBetaEnabled( model.options, 'v5preview' ))
|
|
82
|
+
forEachGeneric( model, 'vocabularies', run );
|
|
80
83
|
|
|
81
84
|
// TODO: move 'virtual' handling/checks to resolver if
|
|
82
85
|
// 'deprecated.oldVirtualNotNullPropagation' is gone
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -519,8 +519,6 @@ function resolve( model ) {
|
|
|
519
519
|
if (art.value) {
|
|
520
520
|
if (art.$syntax === 'calc')
|
|
521
521
|
checkCalculatedElement( art );
|
|
522
|
-
else if (art.type && !art.$inferred)
|
|
523
|
-
checkColumnTypeCast( art );
|
|
524
522
|
}
|
|
525
523
|
|
|
526
524
|
resolveExprInAnnotations( art );
|
|
@@ -849,21 +847,6 @@ function resolve( model ) {
|
|
|
849
847
|
}
|
|
850
848
|
}
|
|
851
849
|
|
|
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
850
|
/**
|
|
868
851
|
* Return type containing the assoc spec (keys, on); note that no
|
|
869
852
|
* propagation/rewrite has been done yet, cyclic dependency must have been
|
|
@@ -1223,8 +1206,9 @@ function resolve( model ) {
|
|
|
1223
1206
|
'Only an association can be redirected' );
|
|
1224
1207
|
return;
|
|
1225
1208
|
}
|
|
1226
|
-
else if ((elem.value || elem.expand) && elem.type &&
|
|
1227
|
-
|
|
1209
|
+
else if ((elem.value || elem.expand) && elem.type &&
|
|
1210
|
+
(!elem.type.$inferred || elem.type.$inferred === 'cast')) {
|
|
1211
|
+
error( 'type-invalid-cast', [ elem.type.location, elem ], { '#': 'assoc' } );
|
|
1228
1212
|
return;
|
|
1229
1213
|
}
|
|
1230
1214
|
// console.log(message( null, elem.location, elem, {target,art:assoc}, 'Info','RE')
|
|
@@ -1399,7 +1383,9 @@ function resolve( model ) {
|
|
|
1399
1383
|
const alreadyReported = model.$messageFunctions.messages
|
|
1400
1384
|
.find(msg => msg.messageId === 'anno-experimental-expressions');
|
|
1401
1385
|
if (!alreadyReported) {
|
|
1402
|
-
|
|
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, {
|
|
1403
1389
|
option: 'annotationExpressions',
|
|
1404
1390
|
// eslint-disable-next-line max-len
|
|
1405
1391
|
}, 'Expressions in annotation values are a beta feature. Use at your own risk. (This message can be suppressed with beta flag $(OPTION))' );
|
package/lib/compiler/shared.js
CHANGED
|
@@ -710,7 +710,7 @@ function fns( model ) {
|
|
|
710
710
|
// duplicate table aliases, only mention non-ambiguous source elems
|
|
711
711
|
const uniqueNames = arr.filter( e => !e.$duplicates );
|
|
712
712
|
if (uniqueNames.length) {
|
|
713
|
-
const names = uniqueNames.filter( e => e._parent
|
|
713
|
+
const names = uniqueNames.filter( e => e._parent.name?.$inferred !== '$internal' )
|
|
714
714
|
.map( e => `${ e._parent.name.id }.${ e.name.id }` );
|
|
715
715
|
let variant = names.length === uniqueNames.length ? 'std' : 'few';
|
|
716
716
|
if (names.length === 0)
|
|
@@ -20,7 +20,7 @@ const {
|
|
|
20
20
|
traverseQueryExtra,
|
|
21
21
|
setExpandStatus,
|
|
22
22
|
} = require('./utils');
|
|
23
|
-
const {
|
|
23
|
+
const { Location } = require('../base/location');
|
|
24
24
|
const { CompilerAssertion } = require('../base/error');
|
|
25
25
|
|
|
26
26
|
const $location = Symbol.for( 'cds.$location' );
|
|
@@ -287,7 +287,7 @@ function tweakAssocs( model ) {
|
|
|
287
287
|
const loc = otherAssoc.foreignKeys[$location] || dictLocation( otherAssoc.foreignKeys );
|
|
288
288
|
const location = loc && (!loc.endCol
|
|
289
289
|
? loc
|
|
290
|
-
: new
|
|
290
|
+
: new Location( loc.file, loc.endLine, loc.endCol - 1, loc.endLine, loc.endCol ));
|
|
291
291
|
const baseAssoc = assocWithExplicitSpec( thisAssoc );
|
|
292
292
|
if (inferredForeignKeys( baseAssoc.foreignKeys )) { // still inferred = via target keys
|
|
293
293
|
error( 'rewrite-key-not-covered-implicit', [ location, otherAssoc ],
|
package/lib/compiler/utils.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
|
|
14
|
-
const { weakLocation } = require('../base/location');
|
|
15
|
-
const { XsnName, XsnArtifact
|
|
14
|
+
const { Location, weakLocation } = require('../base/location');
|
|
15
|
+
const { XsnName, XsnArtifact } = require('./xsn-model');
|
|
16
16
|
|
|
17
17
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
18
18
|
|
|
@@ -262,7 +262,7 @@ function copyExpr( expr, location ) {
|
|
|
262
262
|
const proto = Object.getPrototypeOf( expr );
|
|
263
263
|
if (proto && proto !== Object.prototype && proto !== XsnName.prototype &&
|
|
264
264
|
// do not copy object from special classes outside the compiler domain&&
|
|
265
|
-
proto !== XsnArtifact.prototype && proto !==
|
|
265
|
+
proto !== XsnArtifact.prototype && proto !== Location.prototype)
|
|
266
266
|
return expr;
|
|
267
267
|
const r = Object.create( proto );
|
|
268
268
|
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
@@ -39,24 +39,8 @@ class XsnName {
|
|
|
39
39
|
location;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
class CsnLocation {
|
|
43
|
-
file;
|
|
44
|
-
line;
|
|
45
|
-
col;
|
|
46
|
-
endLine;
|
|
47
|
-
endCol;
|
|
48
|
-
constructor(file, line, col, endLine, endCol) {
|
|
49
|
-
this.file = file;
|
|
50
|
-
this.line = line;
|
|
51
|
-
this.col = col;
|
|
52
|
-
this.endLine = endLine;
|
|
53
|
-
this.endCol = endCol;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
42
|
module.exports = {
|
|
58
43
|
XsnSource,
|
|
59
44
|
XsnArtifact,
|
|
60
45
|
XsnName,
|
|
61
|
-
CsnLocation,
|
|
62
46
|
};
|