@sap/cds-compiler 2.10.2 → 2.11.4
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 +90 -5
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +3 -1
- package/bin/cdsc.js +49 -25
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +8 -36
- package/lib/api/options.js +15 -6
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +34 -10
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +57 -23
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +9 -2
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +66 -108
- package/lib/compiler/index.js +29 -29
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +225 -58
- package/lib/compiler/shared.js +53 -229
- package/lib/compiler/utils.js +184 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +34 -38
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +228 -47
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +73 -14
- package/lib/language/language.g4 +79 -3
- package/lib/main.d.ts +215 -18
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +117 -33
- package/lib/model/csnUtils.js +65 -133
- package/lib/model/enrichCsn.js +62 -37
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +15 -8
- package/lib/render/toHdbcds.js +26 -49
- package/lib/render/toSql.js +61 -39
- package/lib/render/utils/common.js +1 -1
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +19 -3
- package/lib/transform/db/transformExists.js +102 -9
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +93 -448
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/structuralPath.js +1 -5
- package/lib/transform/transformUtilsNew.js +22 -8
- package/lib/transform/translateAssocsToJoins.js +7 -15
- package/lib/utils/file.js +11 -5
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
|
@@ -98,6 +98,8 @@ function assertConsistency( model, stage ) {
|
|
|
98
98
|
'$blocks',
|
|
99
99
|
'$newfeatures',
|
|
100
100
|
'$messageFunctions',
|
|
101
|
+
'$functions',
|
|
102
|
+
'$volatileFunctions',
|
|
101
103
|
],
|
|
102
104
|
},
|
|
103
105
|
':parser': { // top-level from parser
|
|
@@ -247,7 +249,7 @@ function assertConsistency( model, stage ) {
|
|
|
247
249
|
'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
|
|
248
250
|
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
|
|
249
251
|
'_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
|
|
250
|
-
'$tableAliases', 'kind', '_$next', '_combined', '$inlines',
|
|
252
|
+
'$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
|
|
251
253
|
],
|
|
252
254
|
},
|
|
253
255
|
none: { optional: () => true }, // parse error
|
|
@@ -479,6 +481,7 @@ function assertConsistency( model, stage ) {
|
|
|
479
481
|
},
|
|
480
482
|
items: {
|
|
481
483
|
kind: true,
|
|
484
|
+
also: [ 0 ], // 0 for cyclic expansions
|
|
482
485
|
requires: [ 'location' ],
|
|
483
486
|
optional: [
|
|
484
487
|
'enum',
|
|
@@ -539,7 +542,7 @@ function assertConsistency( model, stage ) {
|
|
|
539
542
|
// query specific
|
|
540
543
|
'where', 'columns', 'mixin', 'quantifier', 'offset',
|
|
541
544
|
'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
|
|
542
|
-
'limit',
|
|
545
|
+
'limit', '_status',
|
|
543
546
|
],
|
|
544
547
|
},
|
|
545
548
|
_leadingQuery: { kind: true, test: TODO },
|
|
@@ -588,6 +591,8 @@ function assertConsistency( model, stage ) {
|
|
|
588
591
|
$sources: { parser: true, test: isArray( isString ) },
|
|
589
592
|
$expected: { parser: true, test: isString },
|
|
590
593
|
$messageFunctions: { test: TODO },
|
|
594
|
+
$functions: { test: TODO },
|
|
595
|
+
$volatileFunctions: { test: TODO },
|
|
591
596
|
};
|
|
592
597
|
let _noSyntaxErrors = null;
|
|
593
598
|
assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
|
|
@@ -659,6 +664,8 @@ function assertConsistency( model, stage ) {
|
|
|
659
664
|
}
|
|
660
665
|
|
|
661
666
|
function standard( node, parent, prop, spec, name ) {
|
|
667
|
+
if (spec.also && spec.also.includes( node ))
|
|
668
|
+
return;
|
|
662
669
|
isObject( node, parent, prop, spec, name );
|
|
663
670
|
|
|
664
671
|
const names = Object.getOwnPropertyNames( node );
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Base Definitions for the Core Compiler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const dictKinds = {
|
|
7
|
+
definitions: 'absolute',
|
|
8
|
+
elements: 'element',
|
|
9
|
+
enum: 'enum',
|
|
10
|
+
foreignKeys: 'key',
|
|
11
|
+
actions: 'action',
|
|
12
|
+
params: 'param',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const kindProperties = {
|
|
16
|
+
// TODO: also foreignKeys ?
|
|
17
|
+
namespace: { artifacts: true }, // on-the-fly context
|
|
18
|
+
context: { artifacts: true, normalized: 'namespace' },
|
|
19
|
+
service: { artifacts: true, normalized: 'namespace' },
|
|
20
|
+
entity: { elements: true, actions: true, params: () => false },
|
|
21
|
+
select: { normalized: 'select', elements: true },
|
|
22
|
+
$join: { normalized: 'select' },
|
|
23
|
+
$tableAlias: { normalized: 'alias' }, // table alias in select
|
|
24
|
+
$self: { normalized: 'alias' }, // table alias in select
|
|
25
|
+
$navElement: { normalized: 'element' },
|
|
26
|
+
$inline: { normalized: 'element' }, // column with inline property
|
|
27
|
+
event: { elements: true },
|
|
28
|
+
type: { elements: propExists, enum: propExists },
|
|
29
|
+
aspect: { elements: propExists },
|
|
30
|
+
annotation: { elements: propExists, enum: propExists },
|
|
31
|
+
enum: { normalized: 'element' },
|
|
32
|
+
element: { elements: propExists, enum: propExists, dict: 'elements' },
|
|
33
|
+
mixin: { normalized: 'alias' },
|
|
34
|
+
action: {
|
|
35
|
+
params: () => false, elements: () => false, enum: () => false, dict: 'actions',
|
|
36
|
+
}, // no extend params, only annotate
|
|
37
|
+
function: {
|
|
38
|
+
params: () => false, elements: () => false, enum: () => false, normalized: 'action',
|
|
39
|
+
}, // no extend params, only annotate
|
|
40
|
+
key: { normalized: 'element' },
|
|
41
|
+
param: { elements: () => false, enum: () => false, dict: 'params' },
|
|
42
|
+
source: { artifacts: true }, // TODO -> $source
|
|
43
|
+
using: {},
|
|
44
|
+
extend: {
|
|
45
|
+
isExtension: true,
|
|
46
|
+
noDep: 'special',
|
|
47
|
+
elements: true, /* only for parse-cdl */
|
|
48
|
+
actions: true, /* only for parse-cdl */
|
|
49
|
+
},
|
|
50
|
+
annotate: {
|
|
51
|
+
isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
|
|
52
|
+
},
|
|
53
|
+
builtin: {}, // = CURRENT_DATE, TODO: improve
|
|
54
|
+
$parameters: {}, // $parameters in query entities
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function propExists( prop, parent ) {
|
|
58
|
+
const obj = parent.returns || parent;
|
|
59
|
+
return (obj.items || obj.targetAspect || obj)[prop];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
dictKinds,
|
|
64
|
+
kindProperties,
|
|
65
|
+
};
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const { forEachInDict } = require('../base/dictionaries');
|
|
6
5
|
const { builtinLocation } = require('../base/location');
|
|
7
|
-
const { setProp } = require('
|
|
6
|
+
const { setProp } = require('./utils');
|
|
8
7
|
|
|
9
8
|
const core = {
|
|
10
9
|
String: { parameters: [ 'length' ], category: 'string' },
|
|
@@ -82,18 +81,19 @@ const specialFunctions = {
|
|
|
82
81
|
*/
|
|
83
82
|
const magicVariables = {
|
|
84
83
|
$user: {
|
|
84
|
+
// id and locale are always available
|
|
85
85
|
elements: { id: {}, locale: {} },
|
|
86
86
|
// Allow $user.<any>
|
|
87
87
|
$uncheckedElements: true,
|
|
88
88
|
// Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
|
|
89
89
|
$autoElement: 'id',
|
|
90
|
-
},
|
|
91
|
-
$at: {
|
|
90
|
+
},
|
|
91
|
+
$at: { // CDS-specific, not part of SQL
|
|
92
92
|
elements: {
|
|
93
93
|
from: {}, to: {},
|
|
94
94
|
},
|
|
95
95
|
},
|
|
96
|
-
$now: {},
|
|
96
|
+
$now: {}, // Dito
|
|
97
97
|
$session: {
|
|
98
98
|
// In ABAP CDS session variables are accessed in a generic way via
|
|
99
99
|
// the pseudo variable $session.
|
|
@@ -167,6 +167,31 @@ function isRelationTypeName(typeName) {
|
|
|
167
167
|
return typeCategories.relation.includes(typeName);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Checks whether the given absolute path is inside a reserved namespace.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} absolute
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isInReservedNamespace(absolute) {
|
|
177
|
+
return absolute.startsWith( 'cds.') &&
|
|
178
|
+
!absolute.match(/^cds\.foundation(\.|$)/) &&
|
|
179
|
+
!absolute.match(/^cds\.outbox(\.|$)/); // Requested by Node runtime
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Tell if a type is (directly) a builtin type
|
|
184
|
+
* Note that in CSN builtins are not in the definition of the model, so we can only
|
|
185
|
+
* check against their absolute names. Builtin types are "cds.<something>", i.e. they
|
|
186
|
+
* are directly in 'cds', but not for example in 'cds.foundation'.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} type
|
|
189
|
+
* @returns {boolean}
|
|
190
|
+
*/
|
|
191
|
+
function isBuiltinType(type) {
|
|
192
|
+
return typeof type === 'string' && isInReservedNamespace(type);
|
|
193
|
+
}
|
|
194
|
+
|
|
170
195
|
/**
|
|
171
196
|
* Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
|
|
172
197
|
* `definitions` of the XSN model as well as to `$builtins`.
|
|
@@ -174,6 +199,7 @@ function isRelationTypeName(typeName) {
|
|
|
174
199
|
* @param {XSN.Model} model XSN model without CDS builtins
|
|
175
200
|
*/
|
|
176
201
|
function initBuiltins( model ) {
|
|
202
|
+
const { options } = model;
|
|
177
203
|
setMagicVariables( magicVariables );
|
|
178
204
|
// namespace:"cds" stores the builtins ---
|
|
179
205
|
const cds = createNamespace( 'cds', 'reserved' );
|
|
@@ -241,27 +267,45 @@ function initBuiltins( model ) {
|
|
|
241
267
|
for (const name in builtins) {
|
|
242
268
|
const magic = builtins[name];
|
|
243
269
|
// TODO: rename to $builtinFunction
|
|
244
|
-
const art = { kind: 'builtin', name: {
|
|
270
|
+
const art = { kind: 'builtin', name: { element: name, id: name } };
|
|
245
271
|
artifacts[name] = art;
|
|
246
|
-
|
|
247
|
-
art.elements = forEachInDict( magic.elements, (e, n) => magicElement( e, n, art ));
|
|
272
|
+
|
|
248
273
|
if (magic.$autoElement)
|
|
249
274
|
art.$autoElement = magic.$autoElement;
|
|
250
275
|
if (magic.$uncheckedElements)
|
|
251
276
|
art.$uncheckedElements = magic.$uncheckedElements;
|
|
277
|
+
|
|
278
|
+
createMagicElements( art, magic.elements );
|
|
279
|
+
if (options.variableReplacements)
|
|
280
|
+
createMagicElements( art, options.variableReplacements[name] );
|
|
252
281
|
// setProp( art, '_effectiveType', art );
|
|
253
282
|
}
|
|
254
283
|
model.$magicVariables = { kind: '$magicVariables', artifacts };
|
|
255
284
|
}
|
|
256
285
|
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
286
|
+
function createMagicElements( art, elements ) {
|
|
287
|
+
if (!elements)
|
|
288
|
+
return;
|
|
289
|
+
|
|
290
|
+
const names = Object.keys(elements);
|
|
291
|
+
if (names.length > 0 && !art.elements)
|
|
292
|
+
art.elements = Object.create(null);
|
|
293
|
+
|
|
294
|
+
for (const n of names) {
|
|
295
|
+
const magic = {
|
|
296
|
+
kind: 'builtin',
|
|
297
|
+
name: { id: n, element: `${ art.name.element }.${ n }` },
|
|
298
|
+
};
|
|
299
|
+
// Propagate this property so that it is available for sub-elements.
|
|
300
|
+
if (art.$uncheckedElements)
|
|
301
|
+
magic.$uncheckedElements = art.$uncheckedElements;
|
|
302
|
+
setProp( magic, '_parent', art );
|
|
303
|
+
// setProp( magic, '_effectiveType', magic );
|
|
304
|
+
if (elements[n] && typeof elements[n] === 'object')
|
|
305
|
+
createMagicElements(magic, elements[n]);
|
|
306
|
+
|
|
307
|
+
art.elements[n] = magic;
|
|
308
|
+
}
|
|
265
309
|
}
|
|
266
310
|
}
|
|
267
311
|
|
|
@@ -269,6 +313,8 @@ module.exports = {
|
|
|
269
313
|
functionsWithoutParens,
|
|
270
314
|
specialFunctions,
|
|
271
315
|
initBuiltins,
|
|
316
|
+
isInReservedNamespace,
|
|
317
|
+
isBuiltinType,
|
|
272
318
|
isIntegerTypeName,
|
|
273
319
|
isDecimalTypeName,
|
|
274
320
|
isNumericTypeName,
|
package/lib/compiler/checks.js
CHANGED
|
@@ -632,8 +632,9 @@ function check( model ) { // = XSN
|
|
|
632
632
|
* @returns {void}
|
|
633
633
|
*/
|
|
634
634
|
function checkTokenStreamExpression(xpr, allowAssocTail) {
|
|
635
|
+
const args = Array.isArray(xpr.args) ? xpr.args : Object.values(xpr.args || {});
|
|
635
636
|
// Check for illegal argument usage within the expression
|
|
636
|
-
for (const arg of
|
|
637
|
+
for (const arg of args) {
|
|
637
638
|
if (isVirtualElement(arg))
|
|
638
639
|
error(null, arg.location, 'Virtual elements can\'t be used in an expression');
|
|
639
640
|
|
package/lib/compiler/definer.js
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
// Sub Phase 2 (initXYZ)
|
|
89
89
|
// - set _parent, _main (later: _service?) links, and _block links of members
|
|
90
90
|
// - add _subArtifacts dictionary and "namespace artifacts" for name resolution
|
|
91
|
-
// - duplicate checks
|
|
91
|
+
// - duplicate checks
|
|
92
92
|
// - structure checks ?
|
|
93
93
|
// - annotation assignments
|
|
94
94
|
// - POST: resolvePath() can be called for artifact references (if complete model)
|
|
@@ -101,9 +101,11 @@
|
|
|
101
101
|
// to avoid consequential or repeated errors.
|
|
102
102
|
// - But: The same artifact is added to multiple dictionaries.
|
|
103
103
|
// - Solution part 1: $duplicates as property of the artifact or member
|
|
104
|
-
// for
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
|
|
105
|
+
// dictionary of the whole model, `$tableAliases` dictionary of queries.
|
|
106
|
+
// - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
|
|
107
|
+
// dictionary, `_combined` dictionary for query search, `$tableAliases`
|
|
108
|
+
// of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
|
|
107
109
|
|
|
108
110
|
'use strict';
|
|
109
111
|
|
|
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
|
|
|
111
113
|
const {
|
|
112
114
|
isDeprecatedEnabled, isBetaEnabled,
|
|
113
115
|
setProp, forEachGeneric, forEachInOrder,
|
|
114
|
-
|
|
116
|
+
forEachDefinition,
|
|
115
117
|
forEachMemberRecursivelyWithQuery,
|
|
116
118
|
} = require('../base/model');
|
|
117
119
|
const {
|
|
@@ -120,20 +122,21 @@ const {
|
|
|
120
122
|
const {
|
|
121
123
|
dictLocation,
|
|
122
124
|
} = require('../base/location');
|
|
125
|
+
const { kindProperties, dictKinds } = require('./base');
|
|
123
126
|
const {
|
|
124
|
-
annotationVal,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
dictKinds,
|
|
128
|
-
kindProperties,
|
|
129
|
-
fns,
|
|
127
|
+
annotationVal,
|
|
128
|
+
annotationIsFalse,
|
|
129
|
+
annotateWith,
|
|
130
130
|
linkToOrigin,
|
|
131
131
|
setMemberParent,
|
|
132
132
|
storeExtension,
|
|
133
133
|
dependsOnSilent,
|
|
134
|
-
|
|
134
|
+
pathName,
|
|
135
|
+
augmentPath,
|
|
136
|
+
splitIntoPath,
|
|
137
|
+
} = require('./utils');
|
|
135
138
|
const { compareLayer, layer } = require('./moduleLayers');
|
|
136
|
-
const { initBuiltins } = require('./builtins');
|
|
139
|
+
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
137
140
|
const setLink = setProp;
|
|
138
141
|
|
|
139
142
|
/**
|
|
@@ -148,7 +151,7 @@ const setLink = setProp;
|
|
|
148
151
|
*
|
|
149
152
|
* @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
|
|
150
153
|
*/
|
|
151
|
-
function
|
|
154
|
+
function define( model ) {
|
|
152
155
|
const { options } = model;
|
|
153
156
|
// Get simplified "resolve" functionality and the message function:
|
|
154
157
|
const {
|
|
@@ -160,24 +163,29 @@ function getDefinerFunctions( model ) {
|
|
|
160
163
|
resolveTypeArguments,
|
|
161
164
|
defineAnnotations,
|
|
162
165
|
attachAndEmitValidNames,
|
|
163
|
-
} =
|
|
164
|
-
|
|
165
|
-
let addTextsLanguageAssoc = false;
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
define,
|
|
166
|
+
} = model.$functions;
|
|
167
|
+
Object.assign( model.$functions, {
|
|
169
168
|
initArtifact,
|
|
170
169
|
lateExtensions,
|
|
171
170
|
projectionAncestor,
|
|
172
171
|
hasTruthyProp,
|
|
172
|
+
} );
|
|
173
|
+
// During the definer, we can only resolve artifact references, i.e,
|
|
174
|
+
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
175
|
+
model.$volatileFunctions.environment = function artifactsEnv( art ) {
|
|
176
|
+
return art._subArtifacts || Object.create(null);
|
|
173
177
|
};
|
|
174
178
|
|
|
179
|
+
const extensionsDict = Object.create(null);
|
|
180
|
+
let addTextsLanguageAssoc = false;
|
|
181
|
+
return doDefine();
|
|
182
|
+
|
|
175
183
|
/**
|
|
176
184
|
* Main function of the definer.
|
|
177
185
|
*
|
|
178
186
|
* @returns {XSN.Model}
|
|
179
187
|
*/
|
|
180
|
-
function
|
|
188
|
+
function doDefine() {
|
|
181
189
|
if (options.deprecated &&
|
|
182
190
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
183
191
|
warning( 'api-deprecated-option', {},
|
|
@@ -206,8 +214,6 @@ function getDefinerFunctions( model ) {
|
|
|
206
214
|
|
|
207
215
|
if (options.parseCdl) {
|
|
208
216
|
initExtensionsWithoutApplying();
|
|
209
|
-
// Check for redefinitions
|
|
210
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
211
217
|
// If no extensions shall be applied then we can skip further
|
|
212
218
|
// artifact processing and return the model with an `extensions` property.
|
|
213
219
|
return model;
|
|
@@ -215,7 +221,6 @@ function getDefinerFunctions( model ) {
|
|
|
215
221
|
|
|
216
222
|
applyExtensions();
|
|
217
223
|
|
|
218
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
219
224
|
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
220
225
|
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
221
226
|
model.definitions['sap.common.Languages'];
|
|
@@ -238,13 +243,13 @@ function getDefinerFunctions( model ) {
|
|
|
238
243
|
* @param {XSN.AST} src
|
|
239
244
|
*/
|
|
240
245
|
function addSource( src ) {
|
|
241
|
-
// handle sub model from
|
|
246
|
+
// handle sub model from parser
|
|
242
247
|
if (!src.kind)
|
|
243
248
|
src.kind = 'source';
|
|
244
249
|
|
|
245
250
|
let namespace = src.namespace && src.namespace.path;
|
|
246
251
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
247
|
-
if (
|
|
252
|
+
if (isInReservedNamespace(prefix)) {
|
|
248
253
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
249
254
|
// TODO: use $(NAME)
|
|
250
255
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -280,8 +285,7 @@ function getDefinerFunctions( model ) {
|
|
|
280
285
|
function addDefinition( art, block ) {
|
|
281
286
|
const { absolute } = art.name;
|
|
282
287
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
283
|
-
if (absolute === 'cds' ||
|
|
284
|
-
absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
|
|
288
|
+
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
285
289
|
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
286
290
|
// TODO: use $(NAME)
|
|
287
291
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -305,7 +309,7 @@ function getDefinerFunctions( model ) {
|
|
|
305
309
|
}
|
|
306
310
|
else {
|
|
307
311
|
setLink( art, '_block', block );
|
|
308
|
-
// dictAdd might set $duplicates to true
|
|
312
|
+
// dictAdd might set $duplicates to true
|
|
309
313
|
dictAdd( model.definitions, absolute, art );
|
|
310
314
|
return true;
|
|
311
315
|
}
|
|
@@ -444,6 +448,23 @@ function getDefinerFunctions( model ) {
|
|
|
444
448
|
|
|
445
449
|
// Phase 2 ("init") --------------------------------------------------------
|
|
446
450
|
|
|
451
|
+
function checkRedefinition( art ) {
|
|
452
|
+
if (!art.$duplicates)
|
|
453
|
+
return;
|
|
454
|
+
if (art._main) {
|
|
455
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
456
|
+
name: art.name.id,
|
|
457
|
+
'#': kindProperties[art.kind].normalized || art.kind,
|
|
458
|
+
} );
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
462
|
+
name: art.name.absolute,
|
|
463
|
+
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
464
|
+
} );
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
447
468
|
function initNamespaceAndUsing( src ) {
|
|
448
469
|
if (src.namespace) {
|
|
449
470
|
const decl = src.namespace;
|
|
@@ -488,6 +509,7 @@ function getDefinerFunctions( model ) {
|
|
|
488
509
|
if (!reInit)
|
|
489
510
|
initParentLink( art, model.definitions );
|
|
490
511
|
const block = art._block;
|
|
512
|
+
checkRedefinition( art );
|
|
491
513
|
defineAnnotations( art, art, block );
|
|
492
514
|
initMembers( art, art, block );
|
|
493
515
|
initDollarSelf( art ); // $self
|
|
@@ -513,6 +535,7 @@ function getDefinerFunctions( model ) {
|
|
|
513
535
|
|
|
514
536
|
function initVocabulary( art ) {
|
|
515
537
|
initParentLink( art, model.vocabularies );
|
|
538
|
+
checkRedefinition( art );
|
|
516
539
|
const block = art._block;
|
|
517
540
|
defineAnnotations( art, art, block );
|
|
518
541
|
initMembers( art, art, block );
|
|
@@ -554,20 +577,6 @@ function getDefinerFunctions( model ) {
|
|
|
554
577
|
|
|
555
578
|
// From here til EOF, reexamine code ---------------------------------------
|
|
556
579
|
|
|
557
|
-
// currently called from preProcessArtifact(), do be called in "init"
|
|
558
|
-
function checkRedefinitions( obj, name, prop ) {
|
|
559
|
-
forEachMember( obj, checkRedefinitions, obj.targetAspect );
|
|
560
|
-
if (!obj.$duplicates)
|
|
561
|
-
return;
|
|
562
|
-
if (obj.name.location.file === '<built-in>') {
|
|
563
|
-
// builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
|
|
564
|
-
// The error shall only be printed for the user-defined conflicting artifact.
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
error( 'duplicate-definition', [ obj.name.location, obj ],
|
|
568
|
-
{ name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
|
|
569
|
-
}
|
|
570
|
-
|
|
571
580
|
function initDollarSelf( art ) {
|
|
572
581
|
const selfname = '$self';
|
|
573
582
|
// TODO: use setMemberParent() ?
|
|
@@ -774,12 +783,13 @@ function getDefinerFunctions( model ) {
|
|
|
774
783
|
setMemberParent( table, table.name.id, query );
|
|
775
784
|
setProp( table, '_block', query._block );
|
|
776
785
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
777
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': '
|
|
786
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
778
787
|
} );
|
|
779
788
|
// also add to JOIN nodes for name restrictions:
|
|
780
789
|
for (const p of joinParents) {
|
|
781
|
-
//
|
|
782
|
-
|
|
790
|
+
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
791
|
+
// already used for duplicate aliases of queries:
|
|
792
|
+
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
783
793
|
}
|
|
784
794
|
if (table.name.id[0] === '$') {
|
|
785
795
|
warning( 'syntax-dollar-ident', [ table.name.location, table ], {
|
|
@@ -793,8 +803,8 @@ function getDefinerFunctions( model ) {
|
|
|
793
803
|
|
|
794
804
|
// art is:
|
|
795
805
|
// - entity for top-level queries (including UNION args)
|
|
796
|
-
// - $tableAlias for sub query in FROM
|
|
797
|
-
// - $query for real sub query (in columns, WHERE, ...)
|
|
806
|
+
// - $tableAlias for sub query in FROM - TODO: what about UNION there?
|
|
807
|
+
// - $query for real sub query (in columns, WHERE, ...), again: what about UNION there?
|
|
798
808
|
function initQueryExpression( query, art ) {
|
|
799
809
|
if (!query) // parse error
|
|
800
810
|
return query;
|
|
@@ -846,7 +856,7 @@ function getDefinerFunctions( model ) {
|
|
|
846
856
|
query.kind = 'select';
|
|
847
857
|
query.name = { location: query.location };
|
|
848
858
|
setMemberParent( query, main.$queries.length + 1, main );
|
|
849
|
-
// console.log(
|
|
859
|
+
// console.log(art.kind,art.name,query.name,query._$next.name)
|
|
850
860
|
// if (query.name.query === 1 && query.name.absolute === 'S') throw Error();
|
|
851
861
|
main.$queries.push( query );
|
|
852
862
|
setProp( query, '_parent', art ); // _parent should point to alias/main/query
|
|
@@ -866,7 +876,7 @@ function getDefinerFunctions( model ) {
|
|
|
866
876
|
// assignments on the mixin... (also for future mixin definitions
|
|
867
877
|
// with generated values)
|
|
868
878
|
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
869
|
-
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '
|
|
879
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
870
880
|
} );
|
|
871
881
|
if (mixin.name.id[0] === '$') {
|
|
872
882
|
warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
|
|
@@ -993,6 +1003,7 @@ function getDefinerFunctions( model ) {
|
|
|
993
1003
|
setProp( elem, '_block', bl );
|
|
994
1004
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
995
1005
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1006
|
+
checkRedefinition( elem );
|
|
996
1007
|
defineAnnotations( elem, elem, bl );
|
|
997
1008
|
initMembers( elem, elem, bl, initExtensions );
|
|
998
1009
|
|
|
@@ -1330,7 +1341,7 @@ function getDefinerFunctions( model ) {
|
|
|
1330
1341
|
name: { path: splitIntoPath( location, entityName ), absolute: entityName, location },
|
|
1331
1342
|
location,
|
|
1332
1343
|
elements,
|
|
1333
|
-
$inferred: '
|
|
1344
|
+
$inferred: 'composition-entity',
|
|
1334
1345
|
};
|
|
1335
1346
|
if (target.name) { // named target aspect
|
|
1336
1347
|
setLink( art, '_origin', target );
|
|
@@ -1958,24 +1969,6 @@ function getDefinerFunctions( model ) {
|
|
|
1958
1969
|
}
|
|
1959
1970
|
}
|
|
1960
1971
|
|
|
1961
|
-
// TODO: move to "init" phase
|
|
1962
|
-
/**
|
|
1963
|
-
* Check whether redefinitions of the given artifact name exist and
|
|
1964
|
-
* adapt to `targetAspect`.
|
|
1965
|
-
*
|
|
1966
|
-
* @param {string} name
|
|
1967
|
-
*/
|
|
1968
|
-
function preProcessArtifact( name ) {
|
|
1969
|
-
const art = model.definitions[name];
|
|
1970
|
-
if (Array.isArray(art.$duplicates)) {
|
|
1971
|
-
// A definition name containing a `.` is not invalid (TODO: starting or
|
|
1972
|
-
// ending with a dot is invalid and could be checked here)
|
|
1973
|
-
for (const a of art.$duplicates)
|
|
1974
|
-
checkRedefinitions( a, name, 'definitions' );
|
|
1975
|
-
}
|
|
1976
|
-
checkRedefinitions( art, name, 'definitions' );
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
1972
|
/**
|
|
1980
1973
|
* Process "composition of" artifacts.
|
|
1981
1974
|
*
|
|
@@ -2111,7 +2104,7 @@ function getDefinerFunctions( model ) {
|
|
|
2111
2104
|
name: { path: splitIntoPath( location, absolute ), absolute, location },
|
|
2112
2105
|
location: base.location,
|
|
2113
2106
|
elements,
|
|
2114
|
-
$inferred: 'localized',
|
|
2107
|
+
$inferred: 'localized-entity',
|
|
2115
2108
|
};
|
|
2116
2109
|
const locale = {
|
|
2117
2110
|
name: { location, id: 'locale' },
|
|
@@ -2212,7 +2205,7 @@ function getDefinerFunctions( model ) {
|
|
|
2212
2205
|
name: { location, id: 'texts' },
|
|
2213
2206
|
kind: 'element',
|
|
2214
2207
|
location,
|
|
2215
|
-
$inferred: 'localized
|
|
2208
|
+
$inferred: 'localized',
|
|
2216
2209
|
type: augmentPath( location, 'cds.Composition' ),
|
|
2217
2210
|
cardinality: { targetMax: { literal: 'string', val: '*', location }, location },
|
|
2218
2211
|
target: augmentPath( location, textsName ),
|
|
@@ -2227,7 +2220,7 @@ function getDefinerFunctions( model ) {
|
|
|
2227
2220
|
name: { location, id: 'localized' },
|
|
2228
2221
|
kind: 'element',
|
|
2229
2222
|
location,
|
|
2230
|
-
$inferred: 'localized
|
|
2223
|
+
$inferred: 'localized',
|
|
2231
2224
|
type: augmentPath( location, 'cds.Association' ),
|
|
2232
2225
|
target: augmentPath( location, textsName ),
|
|
2233
2226
|
on: augmentEqual( location, 'localized', keys ),
|
|
@@ -2316,36 +2309,6 @@ function mergeI18nBlocks( model ) {
|
|
|
2316
2309
|
}
|
|
2317
2310
|
}
|
|
2318
2311
|
|
|
2319
|
-
/**
|
|
2320
|
-
* Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
|
|
2321
|
-
* locations).
|
|
2322
|
-
*
|
|
2323
|
-
* @param {XSN.Path} path
|
|
2324
|
-
*/
|
|
2325
|
-
function pathName(path) {
|
|
2326
|
-
return path.map( id => id.id ).join('.');
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
/**
|
|
2330
|
-
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
2331
|
-
* Each segment will have the given location assigned.
|
|
2332
|
-
*
|
|
2333
|
-
* @param {CSN.Location} location
|
|
2334
|
-
* @param {string} name
|
|
2335
|
-
* @returns {XSN.Path}
|
|
2336
|
-
*/
|
|
2337
|
-
function splitIntoPath( location, name ) {
|
|
2338
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
/**
|
|
2342
|
-
* @param {CSN.Location} location
|
|
2343
|
-
* @param {...any} args
|
|
2344
|
-
*/
|
|
2345
|
-
function augmentPath( location, ...args ) {
|
|
2346
|
-
return { path: args.map( id => ({ id, location }) ), location };
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
2312
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
2350
2313
|
const args = relations.map( eq );
|
|
2351
2314
|
return (args.length === 1)
|
|
@@ -2374,9 +2337,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
|
2374
2337
|
// these function could be used to a future lib/compiler/utils.js, but DO NOT
|
|
2375
2338
|
// SHARE with utility functions for CSN processors
|
|
2376
2339
|
|
|
2377
|
-
module.exports = {
|
|
2378
|
-
define: model => getDefinerFunctions( model ).define(),
|
|
2379
|
-
getDefinerFunctions,
|
|
2380
|
-
augmentPath,
|
|
2381
|
-
splitIntoPath,
|
|
2382
|
-
};
|
|
2340
|
+
module.exports = { define };
|