@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/base/location.js
CHANGED
|
@@ -1,10 +1,85 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// This file contains functions related to XSN/CSN-location objects,
|
|
4
|
-
//
|
|
4
|
+
// and the class definition for semantic locations
|
|
5
5
|
|
|
6
6
|
const { copyPropIfExist } = require('../utils/objectUtils');
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
class Location {
|
|
9
|
+
file;
|
|
10
|
+
line;
|
|
11
|
+
col;
|
|
12
|
+
endLine;
|
|
13
|
+
endCol;
|
|
14
|
+
constructor(file, line, col, endLine, endCol) {
|
|
15
|
+
this.file = file;
|
|
16
|
+
this.line = line;
|
|
17
|
+
this.col = col;
|
|
18
|
+
this.endLine = endLine;
|
|
19
|
+
this.endCol = endCol;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class SemanticLocation {
|
|
24
|
+
mainKind;
|
|
25
|
+
absolute;
|
|
26
|
+
action;
|
|
27
|
+
param;
|
|
28
|
+
select;
|
|
29
|
+
mixin;
|
|
30
|
+
element = [];
|
|
31
|
+
suffix;
|
|
32
|
+
innerKind;
|
|
33
|
+
|
|
34
|
+
toString() {
|
|
35
|
+
return semanticLocationString( this );
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function semanticLocationString( name, extended = false ) {
|
|
40
|
+
const parts = [ `${ name.mainKind }:“${ name.absolute }”` ];
|
|
41
|
+
if (name.action != null)
|
|
42
|
+
parts.push( `“action:${ name.action }”` );
|
|
43
|
+
if (name.param != null)
|
|
44
|
+
parts.push( name.param ? `param:“${ name.param }”` : 'returns' );
|
|
45
|
+
if (name.select != null)
|
|
46
|
+
parts.push( name.select ? `select:${ name.select }` : 'select' );
|
|
47
|
+
if (name.mixin != null) {
|
|
48
|
+
const prop = (name.innerKind === 'alias') ? 'alias' : 'mixin';
|
|
49
|
+
parts.push( `${ prop }:${ stringOrRaw( name.mixin ) }` );
|
|
50
|
+
}
|
|
51
|
+
const { element, innerKind } = name;
|
|
52
|
+
if (element.length) {
|
|
53
|
+
const append = innerKind === 'item' || innerKind === 'aspect';
|
|
54
|
+
const prop = !append && innerKind || !innerKind && name.select != null && 'column';
|
|
55
|
+
if (!prop || append) {
|
|
56
|
+
parts.push( `element:${ stringOrRaw( element ) }` );
|
|
57
|
+
if (append)
|
|
58
|
+
parts.push( innerKind );
|
|
59
|
+
}
|
|
60
|
+
else if (prop === 'column') {
|
|
61
|
+
const pos = extended ? -1
|
|
62
|
+
: element.findIndex( ( e, idx ) => idx && typeof e !== 'string' );
|
|
63
|
+
parts.push( `column:${ stringOrRaw( pos > 0 ? element.slice( 0, pos ) : element ) }` );
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (element.length > 1)
|
|
67
|
+
parts.push( `element:${ stringOrRaw( element.slice( 0, -1 ) ) }` );
|
|
68
|
+
parts.push( `${ innerKind }:“${ element[element.length - 1] }”` );
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (name.suffix)
|
|
72
|
+
parts.push( name.suffix );
|
|
73
|
+
return parts.join( '/' );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function stringOrRaw( val ) {
|
|
77
|
+
if (!Array.isArray( val ))
|
|
78
|
+
return (typeof val === 'string') ? `“${ val }”` : val;
|
|
79
|
+
return val.every( e => typeof e === 'string' ) // && no '.' ?
|
|
80
|
+
? `“${ val.join( '.' ) }”`
|
|
81
|
+
: val.map( stringOrRaw ).join( '→' ); // for XSN output, is sliced otherwise
|
|
82
|
+
}
|
|
8
83
|
|
|
9
84
|
/**
|
|
10
85
|
* Create a location with properties `file`, `line` and `col` from argument
|
|
@@ -42,7 +117,7 @@ function combinedLocation( start, end ) {
|
|
|
42
117
|
*/
|
|
43
118
|
function emptyLocation( filename ) {
|
|
44
119
|
return {
|
|
45
|
-
__proto__:
|
|
120
|
+
__proto__: Location.prototype,
|
|
46
121
|
file: filename,
|
|
47
122
|
line: 1,
|
|
48
123
|
col: 1,
|
|
@@ -62,7 +137,7 @@ function emptyLocation( filename ) {
|
|
|
62
137
|
*/
|
|
63
138
|
function emptyWeakLocation( filename ) {
|
|
64
139
|
return {
|
|
65
|
-
__proto__:
|
|
140
|
+
__proto__: Location.prototype,
|
|
66
141
|
file: filename,
|
|
67
142
|
line: 1,
|
|
68
143
|
col: 1,
|
|
@@ -72,12 +147,12 @@ function emptyWeakLocation( filename ) {
|
|
|
72
147
|
}
|
|
73
148
|
|
|
74
149
|
/**
|
|
75
|
-
* @param {
|
|
76
|
-
* @returns {
|
|
150
|
+
* @param {Location} loc
|
|
151
|
+
* @returns {Location}
|
|
77
152
|
*/
|
|
78
153
|
function weakLocation( loc ) {
|
|
79
154
|
return (!loc?.endLine) ? loc : {
|
|
80
|
-
__proto__:
|
|
155
|
+
__proto__: Location.prototype,
|
|
81
156
|
file: loc.file,
|
|
82
157
|
line: loc.line,
|
|
83
158
|
col: loc.col,
|
|
@@ -96,8 +171,8 @@ function weakLocation( loc ) {
|
|
|
96
171
|
* a double-click on the _last_ identifier token of the reference jumps to the artifact
|
|
97
172
|
* represented by the complete reference.
|
|
98
173
|
*
|
|
99
|
-
* @param {
|
|
100
|
-
* @returns {
|
|
174
|
+
* @param {Location} loc
|
|
175
|
+
* @returns {Location}
|
|
101
176
|
*/
|
|
102
177
|
function weakRefLocation( ref ) {
|
|
103
178
|
if (!ref)
|
|
@@ -105,7 +180,7 @@ function weakRefLocation( ref ) {
|
|
|
105
180
|
const { path } = ref;
|
|
106
181
|
const loc = path?.length ? path[path.length - 1].location : ref.location;
|
|
107
182
|
return (!loc?.endLine) ? loc : {
|
|
108
|
-
__proto__:
|
|
183
|
+
__proto__: Location.prototype,
|
|
109
184
|
file: loc.file,
|
|
110
185
|
line: loc.line,
|
|
111
186
|
col: loc.col,
|
|
@@ -115,12 +190,12 @@ function weakRefLocation( ref ) {
|
|
|
115
190
|
}
|
|
116
191
|
|
|
117
192
|
/**
|
|
118
|
-
* @param {
|
|
119
|
-
* @returns {
|
|
193
|
+
* @param {Location} loc
|
|
194
|
+
* @returns {Location}
|
|
120
195
|
*/
|
|
121
196
|
function weakEndLocation( loc ) {
|
|
122
197
|
return loc && {
|
|
123
|
-
__proto__:
|
|
198
|
+
__proto__: Location.prototype,
|
|
124
199
|
file: loc.file,
|
|
125
200
|
line: loc.endLine,
|
|
126
201
|
col: loc.endCol && loc.endCol - 1,
|
|
@@ -203,7 +278,7 @@ function dictLocation( dict, extraLocation ) {
|
|
|
203
278
|
const lineB = (b.endLine || b.line);
|
|
204
279
|
return (lineA > lineB || (lineA === lineB && (a.endCol || a.col) > (b.endCol || b.col)) ? a : b);
|
|
205
280
|
});
|
|
206
|
-
return new
|
|
281
|
+
return new Location( min.file, min.line, min.col, max.endLine, max.endCol );
|
|
207
282
|
}
|
|
208
283
|
|
|
209
284
|
function _objLocations( obj ) {
|
|
@@ -211,6 +286,8 @@ function _objLocations( obj ) {
|
|
|
211
286
|
}
|
|
212
287
|
|
|
213
288
|
module.exports = {
|
|
289
|
+
Location,
|
|
290
|
+
SemanticLocation,
|
|
214
291
|
combinedLocation,
|
|
215
292
|
emptyLocation,
|
|
216
293
|
emptyWeakLocation,
|
|
@@ -162,31 +162,32 @@ const centralMessages = {
|
|
|
162
162
|
|
|
163
163
|
'type-unsupported-precision-change': { severity: 'Error' },
|
|
164
164
|
'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
|
|
165
|
+
'type-missing-enum-value': { severity: 'Warning', errorFor: [ 'v5' ] },
|
|
165
166
|
|
|
166
167
|
'def-missing-element': { severity: 'Error' },
|
|
167
|
-
|
|
168
|
+
'def-expected-structured': { severity: 'Warning', configurableFor: true, errorFor: [ 'v5' ] },
|
|
168
169
|
'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
|
|
169
170
|
|
|
170
171
|
'def-invalid-key-cardinality': { severity: 'Error' },
|
|
171
172
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
172
|
-
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
173
|
+
'odata-spec-violation-array': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
173
174
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
174
|
-
'odata-spec-violation-assoc': { severity: 'Warning' }, // more than 30 chars
|
|
175
|
+
'odata-spec-violation-assoc': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
175
176
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
176
177
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
177
178
|
'odata-spec-violation-id': { severity: 'Error', configurableFor: true },
|
|
178
|
-
'odata-spec-violation-namespace': { severity: 'Warning' }, // more than 30 chars
|
|
179
|
+
'odata-spec-violation-namespace': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
179
180
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
180
|
-
'odata-spec-violation-param': { severity: 'Warning' }, // more than 30 chars
|
|
181
|
+
'odata-spec-violation-param': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
181
182
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
182
|
-
'odata-spec-violation-returns': { severity: 'Warning' }, // more than 30 chars
|
|
183
|
+
'odata-spec-violation-returns': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
183
184
|
'odata-spec-violation-type': { severity: 'Error', configurableFor: true },
|
|
184
|
-
'odata-spec-violation-type-unknown': { severity: 'Warning' },
|
|
185
|
-
'odata-spec-violation-no-key': { severity: 'Warning' },
|
|
185
|
+
'odata-spec-violation-type-unknown': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
|
|
186
|
+
'odata-spec-violation-no-key': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] },
|
|
186
187
|
'odata-spec-violation-key-array': { severity: 'Error', configurableFor: true }, // more than 30 chars
|
|
187
188
|
'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
|
|
188
|
-
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
189
|
-
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
189
|
+
'odata-spec-violation-key-type': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
190
|
+
'odata-spec-violation-property-name': { severity: 'Warning', configurableFor: true, errorFor: ['v5'] }, // more than 30 chars
|
|
190
191
|
'odata-anno-preproc': { severity: 'Warning' },
|
|
191
192
|
'odata-anno-dict': { severity: 'Warning' },
|
|
192
193
|
'odata-anno-vocref': { severity: 'Warning' },
|
|
@@ -251,6 +252,7 @@ const centralMessageTexts = {
|
|
|
251
252
|
'valid-structured': 'Structured OData is only supported with OData version v4',
|
|
252
253
|
'sql-dialect-and-naming': 'sqlDialect $(NAME) can\'t be combined with sqlMapping $(PROP)',
|
|
253
254
|
'sql-dialect-and-localized': 'Option $(OPTION) can\'t be combined with SQL dialect $(VALUE) or the to.hdi()/to.hdbcds() backend',
|
|
255
|
+
'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)'
|
|
254
256
|
},
|
|
255
257
|
'api-unexpected-combination': {
|
|
256
258
|
std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
|
|
@@ -631,7 +633,6 @@ const centralMessageTexts = {
|
|
|
631
633
|
expr: 'Associations can\'t be used as values in expressions',
|
|
632
634
|
'expr-comp': 'Compositions can\'t be used as values in expressions',
|
|
633
635
|
'assoc-stored': 'Associations and compositions can\'t be used as values in stored calculated elements',
|
|
634
|
-
cast: 'Casting to an association is not supported',
|
|
635
636
|
|
|
636
637
|
'managed-filter': 'Unexpected managed association $(NAME) in filter expression of $(ID)',
|
|
637
638
|
'unmanaged-filter': 'Unexpected unmanaged association $(NAME) in filter expression of $(ID)'
|
|
@@ -688,7 +689,10 @@ const centralMessageTexts = {
|
|
|
688
689
|
'type-unexpected-assoc': {
|
|
689
690
|
std: 'An unmanaged association can\'t be used as type',
|
|
690
691
|
},
|
|
691
|
-
|
|
692
|
+
'type-missing-enum-value': {
|
|
693
|
+
std: 'Missing value for non-string enum element $(NAME)',
|
|
694
|
+
numeric: 'Missing value for numeric enum element $(NAME)',
|
|
695
|
+
},
|
|
692
696
|
'type-missing-argument': 'Missing value for argument $(NAME) in reference to type $(ID)',
|
|
693
697
|
'type-ignoring-argument': 'Too many arguments for type $(ART)',
|
|
694
698
|
'type-unexpected-argument': {
|
|
@@ -803,6 +807,8 @@ const centralMessageTexts = {
|
|
|
803
807
|
'texts-aspect-locale': 'Element $(ELEMREF) of $(ART) must be of type $(TYPE) or $(OTHERTYPE)',
|
|
804
808
|
},
|
|
805
809
|
|
|
810
|
+
'def-expected-structured': 'Events must either be structured or be projections',
|
|
811
|
+
|
|
806
812
|
'duplicate-definition': {
|
|
807
813
|
std: 'Duplicate definition of $(NAME)',
|
|
808
814
|
absolute: 'Duplicate definition of artifact $(NAME)',
|
|
@@ -829,10 +835,13 @@ const centralMessageTexts = {
|
|
|
829
835
|
$self: 'Can\'t refer to the query\'s own elements',
|
|
830
836
|
},
|
|
831
837
|
'ref-invalid-override': {
|
|
832
|
-
std: 'Overridden element of include must not change
|
|
838
|
+
std: 'Overridden element of include must not change its type drastically', // unused
|
|
833
839
|
'new-not-structured': 'Expected element $(NAME) to be structured, because it overrides the included element from $(ART)',
|
|
834
840
|
'old-not-structured': 'Expected element $(NAME) to be scalar, because it overrides the included element from $(ART)',
|
|
835
|
-
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
|
|
841
|
+
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)',
|
|
842
|
+
|
|
843
|
+
'new-not-target': 'Expected element $(NAME) to be an association, because it overrides the included element from $(ART)',
|
|
844
|
+
'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
|
|
836
845
|
},
|
|
837
846
|
|
|
838
847
|
'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
|
|
@@ -974,7 +983,8 @@ const centralMessageTexts = {
|
|
|
974
983
|
'from-structure': 'Structured elements can\'t be cast to a different type',
|
|
975
984
|
'expr-to-structure': 'Can\'t cast an expression to a structured type',
|
|
976
985
|
'val-to-structure': 'Can\'t cast $(VALUE) to a structured type',
|
|
977
|
-
'from-assoc': 'Invalid type cast on an association'
|
|
986
|
+
'from-assoc': 'Invalid type cast on an association',
|
|
987
|
+
'assoc': 'Can\'t cast to an association',
|
|
978
988
|
},
|
|
979
989
|
|
|
980
990
|
// -----------------------------------------------------------------------------------
|
|
@@ -997,6 +1007,21 @@ const centralMessageTexts = {
|
|
|
997
1007
|
publishingFilter: 'Can\'t publish managed association $(ID) with filter, as it must have at least one foreign key',
|
|
998
1008
|
},
|
|
999
1009
|
|
|
1010
|
+
// tenenat isolation via discriminator column:
|
|
1011
|
+
'tenant-invalid-alias-name': {
|
|
1012
|
+
std: 'Can\'t have a table alias named $(NAME) in a tenant-dependent entity',
|
|
1013
|
+
implicit: 'Provide an explicit table alias name; do not use $(NAME)',
|
|
1014
|
+
mixin: 'Can\'t define a mixin named $(NAME) in a tenant-dependent entity',
|
|
1015
|
+
},
|
|
1016
|
+
'tenant-invalid-composition': {
|
|
1017
|
+
std: 'Can\'t define a composition of a tenant-independent entity $(TARGET) in a tenant-dependent entity',
|
|
1018
|
+
type: 'Can\'t use type $(TYPE) with a composition of a tenant-independent entity in a tenant-dependent entity',
|
|
1019
|
+
},
|
|
1020
|
+
'tenant-invalid-target': {
|
|
1021
|
+
std: 'Can\'t define an association to a tenant-dependent entity $(TARGET) in a tenant-independent entity',
|
|
1022
|
+
type: 'Can\'t use type $(TYPE) with an association to a tenant-dependent entity in a tenant-independent entity',
|
|
1023
|
+
},
|
|
1024
|
+
|
|
1000
1025
|
// -----------------------------------------------------------------------------------
|
|
1001
1026
|
// OData Message section starts here
|
|
1002
1027
|
// -----------------------------------------------------------------------------------
|
|
@@ -1125,6 +1150,7 @@ const centralMessageTexts = {
|
|
|
1125
1150
|
'notadynexpr': '$(OP) is not a renderable dynamic expression in $(ANNO)',
|
|
1126
1151
|
'use': 'Function $(OP) is not a renderable dynamic expression in $(ANNO), please use $(CODE) instead',
|
|
1127
1152
|
'canonfuncalias': 'Expected function name $(CODE) to be of the form $(META).$(OTHERMETA) for $(OP) in $(ANNO)',
|
|
1153
|
+
'unexpected': 'Unexpected expression in $(ANNO)',
|
|
1128
1154
|
}
|
|
1129
1155
|
,
|
|
1130
1156
|
'odata-anno-xpr-type': {
|
|
@@ -1136,30 +1162,21 @@ const centralMessageTexts = {
|
|
|
1136
1162
|
'atleast': 'Expected at least $(COUNT) argument(s) for $(OP) in $(ANNO)',
|
|
1137
1163
|
'atmost': 'Expected at most $(COUNT) argument(s) for $(OP) in $(ANNO)',
|
|
1138
1164
|
'wrongcount': 'Expected exactly one $(PROP) for $(OP) in $(ANNO)',
|
|
1139
|
-
'wrongref': 'Unexpected arguments or filters in $(ELEMREF) in $(ANNO)',
|
|
1140
1165
|
'wrongval': 'Unexpected value for $(OP) in $(ANNO)',
|
|
1141
1166
|
'wrongval_meta': 'Expected value for $(OP) to be a $(META) in $(ANNO)',
|
|
1142
1167
|
'wrongval_meta_list': 'Expected value for $(OP) to be a $(META) or $(RAWVALUES) in $(ANNO)',
|
|
1143
1168
|
},
|
|
1144
1169
|
'odata-anno-xpr-ref': {
|
|
1145
1170
|
'std': '$(ANNO) can\'t be propagated to $(NAME) because path $(ELEMREF) is not resolvable via type reference $(CODE)',
|
|
1146
|
-
'
|
|
1147
|
-
'
|
|
1148
|
-
'
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
},
|
|
1156
|
-
'tenant-invalid-composition': {
|
|
1157
|
-
std: 'Can\'t define a composition of a tenant-independent entity $(TARGET) in a tenant-dependent entity',
|
|
1158
|
-
type: 'Can\'t use type $(TYPE) with a composition of a tenant-independent entity in a tenant-dependent entity',
|
|
1159
|
-
},
|
|
1160
|
-
'tenant-invalid-target': {
|
|
1161
|
-
std: 'Can\'t define an association to a tenant-dependent entity $(TARGET) in a tenant-independent entity',
|
|
1162
|
-
type: 'Can\'t use type $(TYPE) with an association to a tenant-dependent entity in a tenant-independent entity',
|
|
1171
|
+
'args': 'Unexpected arguments or filters in $(ELEMREF) in $(ANNO)',
|
|
1172
|
+
'flatten_builtin': 'Expected path $(ELEMREF) in $(ANNO) to end with a leaf element while flattening $(NAME)',
|
|
1173
|
+
'flatten_builtin_type': 'Expected path $(ELEMREF) in $(ANNO) to end with a leaf element while flattening',
|
|
1174
|
+
'invalid': 'Invalid path $(ELEMREF) in $(ANNO)',
|
|
1175
|
+
// genericTranslation
|
|
1176
|
+
'notaparam': 'Element path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a parameter entity',
|
|
1177
|
+
'notaneelement': 'Parameter path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a type entity',
|
|
1178
|
+
'notrendered': 'Path step $(COUNT) of $(ELEMREF) in $(ANNO) refers to an unrendered propery in the OData API',
|
|
1179
|
+
'tomany': 'Unexpected to-many transition in path step $(COUNT) of $(ELEMREF) in $(ANNO)',
|
|
1163
1180
|
},
|
|
1164
1181
|
// -----------------------------------------------------------------------------------
|
|
1165
1182
|
// OData Message section ends here, no messages below this line
|
package/lib/base/messages.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
7
|
const { term } = require('../utils/term');
|
|
8
|
-
const { locationString } = require('./location');
|
|
9
|
-
const { isDeprecatedEnabled } = require('./model');
|
|
8
|
+
const { Location, locationString } = require('./location');
|
|
9
|
+
const { isDeprecatedEnabled, isBetaEnabled } = require('./model');
|
|
10
10
|
const { centralMessages, centralMessageTexts, oldMessageIds } = require('./message-registry');
|
|
11
11
|
const _messageIdsWithExplanation = require('../../share/messages/message-explanations.json').messages;
|
|
12
12
|
const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
|
|
@@ -17,7 +17,6 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { inspect } = require('util')
|
|
20
|
-
const { CsnLocation } = require('../compiler/classes');
|
|
21
20
|
|
|
22
21
|
// term instance for messages
|
|
23
22
|
const colorTerm = term();
|
|
@@ -74,6 +73,9 @@ function isDowngradable( messageId, moduleName, options ) {
|
|
|
74
73
|
return false;
|
|
75
74
|
if (msg.severity !== 'Error')
|
|
76
75
|
return true;
|
|
76
|
+
// v5 messages are downgradable (except if errorFor also contains the current module).
|
|
77
|
+
if (msg.errorFor && msg.errorFor.includes('v5'))
|
|
78
|
+
return true;
|
|
77
79
|
const { configurableFor } = msg;
|
|
78
80
|
return (Array.isArray( configurableFor ))
|
|
79
81
|
? configurableFor.includes( moduleName )
|
|
@@ -88,11 +90,12 @@ function isDowngradable( messageId, moduleName, options ) {
|
|
|
88
90
|
class CompilationError extends Error {
|
|
89
91
|
/**
|
|
90
92
|
* @param {CompileMessage[]} messages
|
|
93
|
+
* @param {boolean} [dontSerializeMessages] If true, compiler messages are NOT part of the errors message text.
|
|
91
94
|
* @param {XSN.Model} [model] the XSN model, only to be set with options.attachValidNames
|
|
92
95
|
*/
|
|
93
|
-
constructor(messages, model) {
|
|
94
|
-
|
|
95
|
-
super( `CDS compilation failed\n${
|
|
96
|
+
constructor(messages, model, dontSerializeMessages) {
|
|
97
|
+
const msg = !dontSerializeMessages && messages?.map( m => m.toString() ).join('\n') || '';
|
|
98
|
+
super( `CDS compilation failed\n${msg}` );
|
|
96
99
|
/** @since v4.0.0 */
|
|
97
100
|
this.code = 'ERR_CDS_COMPILATION_FAILURE';
|
|
98
101
|
this.messages = [ ...messages ].sort(compareMessageSeverityAware);
|
|
@@ -113,6 +116,27 @@ class CompilationError extends Error {
|
|
|
113
116
|
return this.stack || this.message;
|
|
114
117
|
}
|
|
115
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Called when the exception is printed, e.g. when it is not caught.
|
|
121
|
+
* To give users a bit of information what went wrong, return stringified
|
|
122
|
+
* error messages. But only errors to avoid spamming users.
|
|
123
|
+
*
|
|
124
|
+
* Compiler consumers should catch compilation errors and properly handle
|
|
125
|
+
* them by printing messages themselves.
|
|
126
|
+
*
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
toString() {
|
|
130
|
+
let messages = [ 'CDS compilation failed' ];
|
|
131
|
+
if (this.messages) {
|
|
132
|
+
messages = messages.concat(this.messages
|
|
133
|
+
.filter(msg => msg.severity === 'Error')
|
|
134
|
+
.map( m => m.toString())
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return messages.join('\n');
|
|
138
|
+
}
|
|
139
|
+
|
|
116
140
|
/**
|
|
117
141
|
* @deprecated Use `.messages` instead.
|
|
118
142
|
*/
|
|
@@ -140,7 +164,7 @@ class CompileMessage {
|
|
|
140
164
|
*/
|
|
141
165
|
constructor(location, msg, severity = 'Error', id = null, home = null, moduleName = null) {
|
|
142
166
|
this.message = msg;
|
|
143
|
-
this.$location = { __proto__:
|
|
167
|
+
this.$location = { __proto__: Location.prototype, ...location, address: undefined };
|
|
144
168
|
this.validNames = null;
|
|
145
169
|
this.home = home; // semantic location, e.g. 'entity:"E"/element:"x"'
|
|
146
170
|
this.severity = severity;
|
|
@@ -183,18 +207,28 @@ const severitySpecs = {
|
|
|
183
207
|
*/
|
|
184
208
|
function reclassifiedSeverity( msg, options, moduleName ) {
|
|
185
209
|
const spec = centralMessages[msg.messageId] || { severity: msg.severity, configurableFor: null, errorFor: null };
|
|
210
|
+
let severity = spec.severity;
|
|
211
|
+
|
|
186
212
|
if (spec.severity === 'Error') {
|
|
187
213
|
if (!isDowngradable(msg.messageId, moduleName, options))
|
|
188
214
|
return 'Error';
|
|
189
215
|
}
|
|
190
216
|
else {
|
|
191
217
|
const { errorFor } = spec;
|
|
192
|
-
if (Array.isArray( errorFor )
|
|
193
|
-
|
|
218
|
+
if (Array.isArray( errorFor )) {
|
|
219
|
+
if (errorFor.includes(moduleName))
|
|
220
|
+
return 'Error';
|
|
221
|
+
|
|
222
|
+
if (errorFor.includes('v5') && isBetaEnabled(options, 'v5preview')) {
|
|
223
|
+
severity = 'Error';
|
|
224
|
+
if (!isDowngradable(msg.messageId, moduleName, options))
|
|
225
|
+
return severity;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
194
228
|
}
|
|
195
229
|
|
|
196
230
|
if (!options.severities)
|
|
197
|
-
return
|
|
231
|
+
return severity;
|
|
198
232
|
|
|
199
233
|
let newSeverity = options.severities[msg.messageId];
|
|
200
234
|
// The user could have specified a severity through an old message ID.
|
|
@@ -202,7 +236,7 @@ function reclassifiedSeverity( msg, options, moduleName ) {
|
|
|
202
236
|
const oldName = spec.oldNames.find((name => options.severities[name]));
|
|
203
237
|
newSeverity = options.severities[oldName];
|
|
204
238
|
}
|
|
205
|
-
return normalizedSeverity( newSeverity ) ||
|
|
239
|
+
return normalizedSeverity( newSeverity ) || severity;
|
|
206
240
|
}
|
|
207
241
|
|
|
208
242
|
function normalizedSeverity( severity ) {
|
|
@@ -333,6 +367,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
333
367
|
callTransparently,
|
|
334
368
|
moduleName,
|
|
335
369
|
setModel,
|
|
370
|
+
setOptions,
|
|
336
371
|
};
|
|
337
372
|
|
|
338
373
|
function _message( id, location, textOrArguments, severity, texts = null ) {
|
|
@@ -352,7 +387,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
352
387
|
if (options.internalMsg)
|
|
353
388
|
msg.error = new Error( 'stack' );
|
|
354
389
|
if (definition)
|
|
355
|
-
msg.$location.address = { definition };
|
|
390
|
+
msg.$location.address = { definition }; // TODO: remove
|
|
356
391
|
|
|
357
392
|
if (id) {
|
|
358
393
|
if (options.testMode && !options.$recompile)
|
|
@@ -406,6 +441,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
406
441
|
/**
|
|
407
442
|
* Normalize the given location. Location may be a CSN path, XSN/CSN location or an
|
|
408
443
|
* array of the form `[CSN.Location, XSN user, suffix]`.
|
|
444
|
+
* TODO: normalize to [ Location, SemanticLocation ]
|
|
409
445
|
*
|
|
410
446
|
* @param {any} location
|
|
411
447
|
* @returns {[CSN.Location, string, string]} Location, semantic location and definition.
|
|
@@ -428,6 +464,9 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
428
464
|
];
|
|
429
465
|
}
|
|
430
466
|
|
|
467
|
+
if (location[1]?.mainKind)
|
|
468
|
+
return [ location[0], location[1].toString(), null ];
|
|
469
|
+
|
|
431
470
|
let semanticLocation = location[1] ? homeName( location[1], false ) : null;
|
|
432
471
|
if (location[2]) { // optional suffix, e.g. annotation
|
|
433
472
|
semanticLocation += `/${ (typeof location[2] === 'string') ? location[2]: homeName(location[2]) }`;
|
|
@@ -494,8 +533,10 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
494
533
|
}
|
|
495
534
|
|
|
496
535
|
function throwWithError() {
|
|
497
|
-
if (hasNewError)
|
|
498
|
-
|
|
536
|
+
if (hasNewError) {
|
|
537
|
+
const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
|
|
538
|
+
throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
|
|
539
|
+
}
|
|
499
540
|
}
|
|
500
541
|
|
|
501
542
|
/**
|
|
@@ -510,8 +551,10 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
510
551
|
if (!messages || !messages.length)
|
|
511
552
|
return;
|
|
512
553
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
513
|
-
if (hasError( messages, moduleName, options ))
|
|
514
|
-
|
|
554
|
+
if (hasError( messages, moduleName, options )) {
|
|
555
|
+
const dontSerializeMessages = isBetaEnabled(options, 'v5preview');
|
|
556
|
+
throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
|
|
557
|
+
}
|
|
515
558
|
}
|
|
516
559
|
|
|
517
560
|
/**
|
|
@@ -561,6 +604,18 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
561
604
|
function setModel( _model ) {
|
|
562
605
|
model = _model;
|
|
563
606
|
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Change the options used to determine message severities.
|
|
610
|
+
* This is necessary if you change `options.severities`, as otherwise they may not be picked up.
|
|
611
|
+
*
|
|
612
|
+
* @param {CSN.Model} _model
|
|
613
|
+
*/
|
|
614
|
+
function setOptions( _options ) {
|
|
615
|
+
options = _options;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
|
|
564
619
|
}
|
|
565
620
|
|
|
566
621
|
/**
|
package/lib/base/model.js
CHANGED
|
@@ -29,7 +29,6 @@ const availableBetaFlags = {
|
|
|
29
29
|
annotationExpressions: true,
|
|
30
30
|
odataPathsInAnnotationExpressions: true,
|
|
31
31
|
odataAnnotationExpressions: true,
|
|
32
|
-
assocsWithParams: true, // beta, because runtimes don't support it, yet.
|
|
33
32
|
hanaAssocRealCardinality: true,
|
|
34
33
|
mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
|
|
35
34
|
enableUniversalCsn: true,
|
|
@@ -39,7 +38,6 @@ const availableBetaFlags = {
|
|
|
39
38
|
effectiveCsn: true,
|
|
40
39
|
tenantVariable: true,
|
|
41
40
|
calcAssoc: true,
|
|
42
|
-
vectorType: true,
|
|
43
41
|
v5preview: true,
|
|
44
42
|
// disabled by --beta-mode
|
|
45
43
|
nestedServices: false,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isBuiltinType } = require('../
|
|
3
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
4
4
|
const { isBetaEnabled } = require('../base/model');
|
|
5
5
|
|
|
6
6
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
package/lib/checks/elements.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
forEachMember, forEachMemberRecursively,
|
|
4
|
+
forEachMember, forEachMemberRecursively, cardinality2str,
|
|
5
5
|
} = require('../model/csnUtils');
|
|
6
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
6
7
|
const { isGeoTypeName } = require('../compiler/builtins');
|
|
7
8
|
const { setProp } = require('../base/model');
|
|
8
9
|
// Only to be used with validator.js - a correct `this` value needs to be provided!
|
package/lib/checks/enricher.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const { csnRefs } = require('../model/csnRefs');
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
|
-
const {
|
|
9
|
+
const { isAnnotationExpression } = require('../base/builtins');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The following properties are attached as non-enumerable where appropriate:
|
|
@@ -96,7 +96,7 @@ function enrichCsn( csn, options ) {
|
|
|
96
96
|
*/
|
|
97
97
|
function annotation( _parent, _prop, node ) {
|
|
98
98
|
if (options.enrichAnnotations) {
|
|
99
|
-
if (
|
|
99
|
+
if (isAnnotationExpression(node)) {
|
|
100
100
|
standard(_parent, _prop, node);
|
|
101
101
|
}
|
|
102
102
|
else if (node && typeof node === 'object') {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { hasAnnotationValue, isPersistedOnDatabase
|
|
3
|
+
const { hasAnnotationValue, isPersistedOnDatabase } = require('../model/csnUtils');
|
|
4
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
4
5
|
const { requireForeignKeyAccess } = require('./onConditions');
|
|
5
6
|
const { pathId } = require('../model/csnRefs');
|
|
6
7
|
|
package/lib/checks/utils.js
CHANGED