@sap/cds-compiler 4.7.6 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
|
@@ -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.
|
|
@@ -130,14 +129,18 @@ function assertConsistency( model, stage ) {
|
|
|
130
129
|
],
|
|
131
130
|
instanceOf: XsnSource,
|
|
132
131
|
},
|
|
132
|
+
tokenIndex: { test: isNumber },
|
|
133
133
|
location: {
|
|
134
134
|
// every thing with a $location in CSN must have a XSN location even
|
|
135
135
|
// with syntax errors (currently even internal artifacts like $using):
|
|
136
136
|
isRequired: parent => noSyntaxErrors() || parent && parent.kind,
|
|
137
137
|
kind: true,
|
|
138
|
-
instanceOf:
|
|
138
|
+
instanceOf: Location,
|
|
139
139
|
requires: [ 'file' ], // line is optional in top-level location
|
|
140
|
-
optional: [
|
|
140
|
+
optional: [
|
|
141
|
+
'line', 'col', 'endLine', 'endCol', '$notFound',
|
|
142
|
+
'tokenIndex', // in parser for $lsp
|
|
143
|
+
],
|
|
141
144
|
schema: {
|
|
142
145
|
line: { test: isNumber },
|
|
143
146
|
col: { test: isNumber },
|
|
@@ -233,7 +236,7 @@ function assertConsistency( model, stage ) {
|
|
|
233
236
|
test: (model.$frontend !== 'json') ? standard : TODO,
|
|
234
237
|
// TODO: the JSON parser should augment 'namespace' correctly or better: hide it
|
|
235
238
|
requires: [ 'location' ],
|
|
236
|
-
optional: [ '
|
|
239
|
+
optional: [ 'kind', 'name' ],
|
|
237
240
|
},
|
|
238
241
|
usings: {
|
|
239
242
|
test: isArray(),
|
|
@@ -317,7 +320,6 @@ function assertConsistency( model, stage ) {
|
|
|
317
320
|
'kind', 'name', '_block', '_parent', '_main', 'elements',
|
|
318
321
|
'_effectiveType', '$effectiveSeqNo', '_origin', '_joinParent', '$joinArgsIndex',
|
|
319
322
|
'$duplicates', // duplicate query in FROM clause
|
|
320
|
-
'$inferred', // table alias with $inferred: '$internal'
|
|
321
323
|
],
|
|
322
324
|
},
|
|
323
325
|
none: { optional: () => true }, // parse error
|
|
@@ -517,11 +519,13 @@ function assertConsistency( model, stage ) {
|
|
|
517
519
|
'_effectiveType', '$effectiveSeqNo', '_origin', '_deps',
|
|
518
520
|
// CSN parser may let these properties slip through to XSN, even if input is invalid.
|
|
519
521
|
'args', 'op', 'func', 'suffix',
|
|
522
|
+
'$invalidPaths',
|
|
520
523
|
],
|
|
521
524
|
// TODO: name requires if not in parser?
|
|
522
525
|
},
|
|
523
526
|
$priority: { test: isOneOf( [ undefined, false, 'extend', 'annotate' ] ) },
|
|
524
527
|
$annotations: { parser: true, kind: true, test: TODO }, // deprecated, still there for cds-lsp
|
|
528
|
+
$invalidPaths: { test: isBoolean },
|
|
525
529
|
name: {
|
|
526
530
|
isRequired: stageParser && (() => false), // not required in parser
|
|
527
531
|
kind: true,
|
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
|
@@ -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
|
|
|
@@ -29,7 +30,7 @@ const $location = Symbol.for( 'cds.$location' );
|
|
|
29
30
|
*/
|
|
30
31
|
function check( model ) {
|
|
31
32
|
const {
|
|
32
|
-
error, warning, info,
|
|
33
|
+
error, warning, info, message,
|
|
33
34
|
} = model.$messageFunctions;
|
|
34
35
|
|
|
35
36
|
checkSapCommonLocale( model );
|
|
@@ -41,6 +42,7 @@ function check( model ) {
|
|
|
41
42
|
return;
|
|
42
43
|
|
|
43
44
|
function checkDefinition( def ) {
|
|
45
|
+
checkEvent( def );
|
|
44
46
|
checkGenericConstruct( def );
|
|
45
47
|
if (def.includes && def.elements)
|
|
46
48
|
checkElementIncludeOverride( def );
|
|
@@ -49,6 +51,16 @@ function check( model ) {
|
|
|
49
51
|
def.$queries.forEach( checkQuery );
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
function checkEvent( def ) {
|
|
55
|
+
// Ensure that events are structured. Up to compiler v4, we allowed non-structured events,
|
|
56
|
+
// because when we introduced them, it was not fully specified what they are.
|
|
57
|
+
// TODO(v5):
|
|
58
|
+
// - Make this a (configurable) error; move to acceptTypeOrElement
|
|
59
|
+
// - require type/elements to be set in parser
|
|
60
|
+
if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)
|
|
61
|
+
message( 'def-expected-structured', [ (def.type || def.name).location, def ] );
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
function checkAnnotationDefinition( art ) {
|
|
53
65
|
// TODO: Should we check elements similar to definition-elements as well?
|
|
54
66
|
checkEnumType( art );
|
|
@@ -58,14 +70,23 @@ function check( model ) {
|
|
|
58
70
|
} );
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
function* iterateAnnotations( art ) {
|
|
74
|
+
for (const prop in art) {
|
|
75
|
+
if (prop.charAt(0) === '@')
|
|
76
|
+
yield prop;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
function checkGenericConstruct( art ) {
|
|
62
81
|
checkName( art );
|
|
63
82
|
checkTypeArguments( art );
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
|
|
84
|
+
if (art.value && !art.$calcDepElement && art.type)
|
|
85
|
+
checkTypeCast( art.value, art );
|
|
86
|
+
|
|
87
|
+
for (const anno of iterateAnnotations( art ))
|
|
88
|
+
checkAnnotationAssignment1( art, art[anno] );
|
|
89
|
+
|
|
69
90
|
checkTypeStructure( art );
|
|
70
91
|
checkAssociation( art ); // type def could be assoc
|
|
71
92
|
if (art.kind === 'enum')
|
|
@@ -202,13 +223,40 @@ function check( model ) {
|
|
|
202
223
|
}
|
|
203
224
|
}
|
|
204
225
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Check the type in an SQL cast expression.
|
|
228
|
+
*
|
|
229
|
+
* @param xpr
|
|
230
|
+
* @param {XSN.Artifact} user
|
|
231
|
+
*/
|
|
232
|
+
function requireExplicitTypeInSqlCast( xpr, user ) {
|
|
233
|
+
if (!xpr.type) {
|
|
234
|
+
error( 'expr-missing-type', [ xpr.location, user ], { },
|
|
208
235
|
'Missing type in SQL cast function' );
|
|
209
236
|
}
|
|
210
237
|
}
|
|
211
238
|
|
|
239
|
+
function checkTypeCast( xpr, user ) {
|
|
240
|
+
const isCast = (xpr.op?.val === 'cast');
|
|
241
|
+
const elem = isCast
|
|
242
|
+
? xpr.args[0]?._artifact
|
|
243
|
+
: xpr._artifact;
|
|
244
|
+
const type = isCast ? xpr.type : user.type;
|
|
245
|
+
if (!isCast && type.$inferred)
|
|
246
|
+
return; // e.g. $inferred:'generated'
|
|
247
|
+
if (elem && type) { // has explicit type
|
|
248
|
+
if (type._artifact?.elements)
|
|
249
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'to-structure' } );
|
|
250
|
+
else if (elem.elements) // TODO: calc elements
|
|
251
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-structure' } );
|
|
252
|
+
else if (elem.target && !type._artifact?.target)
|
|
253
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'from-assoc' } );
|
|
254
|
+
else if (!elem.target && type._artifact?.target && !user.type?.$inferred)
|
|
255
|
+
// $inferred already reported in resolve.js
|
|
256
|
+
error( 'type-invalid-cast', [ type.location, user ], { '#': 'assoc' } );
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
212
260
|
function checkLocalizedElement( elem ) {
|
|
213
261
|
if (elem.localized?.val) {
|
|
214
262
|
const type = elem._effectiveType;
|
|
@@ -224,9 +272,11 @@ function check( model ) {
|
|
|
224
272
|
// "key" keyword at localized element in SELECT list.
|
|
225
273
|
// TODO: not in inferred elements, but also inside aspects
|
|
226
274
|
if (elem.key?.val && elem._main?.query) {
|
|
275
|
+
// either the element was casted to localized (no `_origin`) or
|
|
227
276
|
// original element is localized but not key, as that would have
|
|
228
277
|
// already resulted in a warning by localized.js
|
|
229
|
-
if (elem._origin
|
|
278
|
+
if ((!elem._origin && elem.localized?.val) ||
|
|
279
|
+
(elem._origin?.localized?.val && !elem._origin.key?.val)) {
|
|
230
280
|
warning( 'def-ignoring-localized', [ elem.key.location, elem ], { keyword: 'localized' },
|
|
231
281
|
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
232
282
|
}
|
|
@@ -340,11 +390,8 @@ function check( model ) {
|
|
|
340
390
|
.find( name => !enumNode.enum[name].value );
|
|
341
391
|
if (emptyValue) {
|
|
342
392
|
const failedEnum = enumNode.enum[emptyValue];
|
|
343
|
-
|
|
393
|
+
message( 'type-missing-enum-value', [ failedEnum.location, failedEnum ], {
|
|
344
394
|
'#': 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
395
|
} );
|
|
349
396
|
}
|
|
350
397
|
}
|
|
@@ -551,15 +598,26 @@ function check( model ) {
|
|
|
551
598
|
if (xorElements) {
|
|
552
599
|
// one of the two elements is not structured
|
|
553
600
|
const prop = !elem.elements ? 'new-not-structured' : 'old-not-structured';
|
|
554
|
-
const name = elem.name.id;
|
|
555
601
|
// Position at type/struct, not name
|
|
556
602
|
const loc = elem.type?.location || elem.elements?.[$location] || elem.location;
|
|
557
603
|
error( 'ref-invalid-override', [ loc, elem ],
|
|
558
|
-
{ '#': prop, art: original._main, name } );
|
|
604
|
+
{ '#': prop, art: original._main, name: elem.name.id } );
|
|
559
605
|
return false;
|
|
560
606
|
}
|
|
561
607
|
else if (original.elements &&
|
|
562
|
-
|
|
608
|
+
!checkSubStructureOverride( elem, elem.elements, original.elements )) {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const xorTarget = !(elem.target || elem.targetAspect) !==
|
|
613
|
+
!(original.target || original.targetAspect);
|
|
614
|
+
if (xorTarget) {
|
|
615
|
+
// one of the two elements is not an association
|
|
616
|
+
const prop = !elem.target ? 'new-not-target' : 'old-not-target';
|
|
617
|
+
// Position at type/assoc, not name
|
|
618
|
+
const loc = elem.target?.location || elem.type?.location || elem.location;
|
|
619
|
+
error( 'ref-invalid-override', [ loc, elem ],
|
|
620
|
+
{ '#': prop, art: original._main, name: elem.name.id } );
|
|
563
621
|
return false;
|
|
564
622
|
}
|
|
565
623
|
return true;
|
|
@@ -599,6 +657,7 @@ function check( model ) {
|
|
|
599
657
|
checkExpressionAssociationUsage( xpr, user, false );
|
|
600
658
|
if (xpr.op?.val === 'cast') {
|
|
601
659
|
requireExplicitTypeInSqlCast( xpr, user );
|
|
660
|
+
checkTypeCast( xpr, user );
|
|
602
661
|
checkTypeArguments( xpr, user );
|
|
603
662
|
}
|
|
604
663
|
}
|
|
@@ -631,6 +690,7 @@ function check( model ) {
|
|
|
631
690
|
// the cast, as otherwise we will check it twice (once here, once via element).
|
|
632
691
|
if (elem.value?.op?.val === 'cast' && elem.type?.$inferred !== 'cast') {
|
|
633
692
|
requireExplicitTypeInSqlCast( elem.value, elem );
|
|
693
|
+
checkTypeCast( elem.value, elem );
|
|
634
694
|
checkTypeArguments( elem.value, elem );
|
|
635
695
|
}
|
|
636
696
|
visitSubExpression( elem.value, elem, (xpr) => {
|
|
@@ -810,21 +870,37 @@ function check( model ) {
|
|
|
810
870
|
return false;
|
|
811
871
|
}
|
|
812
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
|
+
}
|
|
813
890
|
|
|
814
|
-
// Former checkAnnotationAssignments.js ------------------------------------
|
|
815
|
-
|
|
816
|
-
// Check the annotation assignments (if any) of 'annotatable', possibly using annotation
|
|
817
|
-
// definitions from 'model'. Report errors on 'options.messages.
|
|
818
|
-
//
|
|
819
|
-
// TODO: rework completely!
|
|
820
|
-
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
|
|
821
|
-
|
|
822
|
-
// Has been slightly adapted for model.vocabularies but comments need to be
|
|
823
|
-
// adapted, etc.
|
|
824
891
|
function checkAnnotationAssignment1( art, anno ) {
|
|
825
|
-
if (art.$contains?.$annotation)
|
|
826
|
-
|
|
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;
|
|
827
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
|
|
828
904
|
// Sanity checks (ignore broken assignments)
|
|
829
905
|
if (!anno.name?.id)
|
|
830
906
|
return;
|
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' );
|
|
@@ -212,6 +213,12 @@ function define( model ) {
|
|
|
212
213
|
dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
|
|
213
214
|
|
|
214
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
|
+
}
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
// Phase 1: ----------------------------------------------------------------
|
|
@@ -227,14 +234,15 @@ function define( model ) {
|
|
|
227
234
|
if (!src.kind)
|
|
228
235
|
src.kind = 'source';
|
|
229
236
|
|
|
230
|
-
let
|
|
237
|
+
let namespace = src.namespace?.name;
|
|
231
238
|
let prefix = '';
|
|
232
239
|
if (namespace?.path && !namespace.path.broken) {
|
|
233
240
|
namespace.id = pathName( namespace.path );
|
|
234
241
|
prefix = `${ namespace.id }.`;
|
|
235
242
|
}
|
|
236
243
|
if (isInReservedNamespace( prefix )) {
|
|
237
|
-
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ],
|
|
244
|
+
error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
|
|
245
|
+
{ name: 'cds' },
|
|
238
246
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
239
247
|
namespace = null;
|
|
240
248
|
}
|
|
@@ -522,7 +530,7 @@ function define( model ) {
|
|
|
522
530
|
if (src.$frontend && src.$frontend !== 'cdl')
|
|
523
531
|
return;
|
|
524
532
|
if (src.namespace) {
|
|
525
|
-
const decl = src.namespace;
|
|
533
|
+
const decl = src.namespace.name;
|
|
526
534
|
if (!decl.id) // parsing may have failed
|
|
527
535
|
return;
|
|
528
536
|
if (!model.definitions[decl.id]) {
|
|
@@ -561,8 +569,8 @@ function define( model ) {
|
|
|
561
569
|
initArtifactParentLink( art, model.definitions );
|
|
562
570
|
const block = art._block;
|
|
563
571
|
checkRedefinition( art );
|
|
564
|
-
initMembers( art, art, block );
|
|
565
572
|
initDollarSelf( art ); // $self
|
|
573
|
+
initMembers( art, art, block );
|
|
566
574
|
if (art.params)
|
|
567
575
|
initDollarParameters( art ); // $parameters
|
|
568
576
|
|
|
@@ -635,6 +643,7 @@ function define( model ) {
|
|
|
635
643
|
name: { id: '$parameters' },
|
|
636
644
|
kind: '$parameters',
|
|
637
645
|
location: art.location,
|
|
646
|
+
deprecated: true, // hide in code completion
|
|
638
647
|
};
|
|
639
648
|
setLink( parameters, '_parent', art );
|
|
640
649
|
setLink( parameters, '_main', art );
|
|
@@ -678,8 +687,14 @@ function define( model ) {
|
|
|
678
687
|
setLink( self, '_origin', query );
|
|
679
688
|
setLink( self, '_parent', query );
|
|
680
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
|
+
|
|
681
696
|
query.$tableAliases.$self = self;
|
|
682
|
-
query.$tableAliases.$projection =
|
|
697
|
+
query.$tableAliases.$projection = projection;
|
|
683
698
|
}
|
|
684
699
|
initSubQuery( query ); // check for SELECT clauses after from / mixin
|
|
685
700
|
}
|
|
@@ -747,7 +762,6 @@ function define( model ) {
|
|
|
747
762
|
// We don't worry about duplicate names here.
|
|
748
763
|
const id = `$_select_${ query._main.$queries.length + 1 }__`;
|
|
749
764
|
table.name = { id, location: table.location, $inferred: '$internal' };
|
|
750
|
-
table.$inferred = '$internal';
|
|
751
765
|
}
|
|
752
766
|
addAsAlias();
|
|
753
767
|
// Store _origin to leading query of table.query for name resolution
|
|
@@ -796,7 +810,7 @@ function define( model ) {
|
|
|
796
810
|
setMemberParent( table, table.name.id, query );
|
|
797
811
|
setLink( table, '_block', query._block );
|
|
798
812
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
|
|
799
|
-
if (tableAlias.$inferred === '$internal') {
|
|
813
|
+
if (tableAlias.name.$inferred === '$internal') {
|
|
800
814
|
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
801
815
|
// TODO: the semanticLoc query is not initialized yet, and thus cannot
|
|
802
816
|
// be used here
|
|
@@ -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
|
|