@sap/cds-compiler 2.13.8 → 3.0.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 +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
package/lib/base/messages.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
const { term } = require('../utils/term');
|
|
8
8
|
const { locationString } = require('./location');
|
|
9
9
|
const { isDeprecatedEnabled } = require('./model');
|
|
10
|
-
const { centralMessages, centralMessageTexts } = require('./message-registry');
|
|
10
|
+
const { centralMessages, centralMessageTexts, oldMessageIds } = require('./message-registry');
|
|
11
11
|
const { copyPropIfExist } = require('../utils/objectUtils');
|
|
12
12
|
const _messageIdsWithExplanation = require('../../share/messages/message-explanations.json').messages;
|
|
13
13
|
const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
|
|
14
|
+
const { CompilerAssertion } = require("./error");
|
|
14
15
|
|
|
15
16
|
const fs = require('fs');
|
|
16
17
|
const path = require('path');
|
|
@@ -24,8 +25,9 @@ let test$severities = null;
|
|
|
24
25
|
let test$texts = null;
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
* Returns true if at least one of the given messages is of severity "Error"
|
|
28
|
-
*
|
|
28
|
+
* Returns true if at least one of the given messages is of severity "Error".
|
|
29
|
+
*
|
|
30
|
+
* @param {CompileMessage[]} messages
|
|
29
31
|
* @returns {boolean}
|
|
30
32
|
*/
|
|
31
33
|
function hasErrors( messages ) {
|
|
@@ -34,9 +36,10 @@ function hasErrors( messages ) {
|
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* Returns true if at least one of the given messages is of severity "Error"
|
|
37
|
-
* and *cannot* be reclassified to a warning.
|
|
39
|
+
* and *cannot* be reclassified to a warning for the given module.
|
|
40
|
+
* Won't detect already downgraded messages.
|
|
38
41
|
*
|
|
39
|
-
* @param {
|
|
42
|
+
* @param {CompileMessage[]} messages
|
|
40
43
|
* @param {string} moduleName
|
|
41
44
|
* @returns {boolean}
|
|
42
45
|
*/
|
|
@@ -122,7 +125,7 @@ class CompileMessage {
|
|
|
122
125
|
* Creates an instance of CompileMessage.
|
|
123
126
|
* @param {any} location Location of the message
|
|
124
127
|
* @param {string} msg The message text
|
|
125
|
-
* @param {
|
|
128
|
+
* @param {MessageSeverity} [severity='Error'] Severity: Debug, Info, Warning, Error
|
|
126
129
|
* @param {string} [id] The ID of the message - visible as property messageId
|
|
127
130
|
* @param {any} [home]
|
|
128
131
|
* @param {string} [moduleName] Name of the module that created this message
|
|
@@ -141,7 +144,7 @@ class CompileMessage {
|
|
|
141
144
|
Object.defineProperty( this, 'messageId', { value: id } );
|
|
142
145
|
// this.messageId = id; // ids not yet finalized
|
|
143
146
|
if (moduleName)
|
|
144
|
-
Object.defineProperty( this, '$module', { value: moduleName } );
|
|
147
|
+
Object.defineProperty( this, '$module', { value: moduleName, configurable: true } );
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
toString() { // should have no argument...
|
|
@@ -184,7 +187,7 @@ const severitySpecs = {
|
|
|
184
187
|
};
|
|
185
188
|
|
|
186
189
|
/**
|
|
187
|
-
*
|
|
190
|
+
* Get the reclassified severity of the given message using:
|
|
188
191
|
*
|
|
189
192
|
* 1. The specified severity: either centrally provided or via the input severity
|
|
190
193
|
* - when generally specified as 'Error', immediately return 'Error'
|
|
@@ -195,17 +198,14 @@ const severitySpecs = {
|
|
|
195
198
|
* been returned according to 1, return the severity according to the user wishes.
|
|
196
199
|
* 3. Otherwise, use the specified severity.
|
|
197
200
|
*
|
|
198
|
-
* @param {
|
|
199
|
-
* @param {CSN.
|
|
200
|
-
* @param {object} severities
|
|
201
|
+
* @param {object} msg The CompileMessage.
|
|
202
|
+
* @param {CSN.Options} options
|
|
201
203
|
* @param {string} moduleName
|
|
202
|
-
* @
|
|
203
|
-
*
|
|
204
|
-
* TODO: we should pass options as usual
|
|
205
|
-
* TODO: should be part of the returned function
|
|
204
|
+
* @param {boolean} deprecatedDowngradable
|
|
205
|
+
* @returns {MessageSeverity}
|
|
206
206
|
*/
|
|
207
|
-
function reclassifiedSeverity(
|
|
208
|
-
const spec = centralMessages[
|
|
207
|
+
function reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable ) {
|
|
208
|
+
const spec = centralMessages[msg.messageId] || { severity: msg.severity, configurableFor: null, errorFor: null };
|
|
209
209
|
if (spec.severity === 'Error') {
|
|
210
210
|
const { configurableFor } = spec;
|
|
211
211
|
if (!(Array.isArray( configurableFor )
|
|
@@ -218,7 +218,17 @@ function reclassifiedSeverity( id, severity, severities, moduleName, deprecatedD
|
|
|
218
218
|
if (Array.isArray( errorFor ) && errorFor.includes( moduleName ))
|
|
219
219
|
return 'Error';
|
|
220
220
|
}
|
|
221
|
-
|
|
221
|
+
|
|
222
|
+
if (!options.severities)
|
|
223
|
+
return spec.severity;
|
|
224
|
+
|
|
225
|
+
let newSeverity = options.severities[msg.messageId];
|
|
226
|
+
// The user could have specified a severity through an old message ID.
|
|
227
|
+
if (!newSeverity && spec.oldNames) {
|
|
228
|
+
const oldName = spec.oldNames.find((name => options.severities[name]))
|
|
229
|
+
newSeverity = options.severities[oldName];
|
|
230
|
+
}
|
|
231
|
+
return normalizedSeverity( newSeverity ) || spec.severity;
|
|
222
232
|
}
|
|
223
233
|
|
|
224
234
|
function normalizedSeverity( severity ) {
|
|
@@ -228,24 +238,6 @@ function normalizedSeverity( severity ) {
|
|
|
228
238
|
return s ? s.name : 'Error';
|
|
229
239
|
}
|
|
230
240
|
|
|
231
|
-
/**
|
|
232
|
-
* Reclassifies all messages according to the current module.
|
|
233
|
-
* This is required because if throwWithError() throws and the message's
|
|
234
|
-
* severities has `errorFor` set, then the message may still appear to be a warning.
|
|
235
|
-
*
|
|
236
|
-
* TODO: this actually likely needs to be called by the backend module at the beginning!
|
|
237
|
-
*
|
|
238
|
-
* @param {CSN.Message[]} messages
|
|
239
|
-
* @param {object} severities
|
|
240
|
-
* @param {string} moduleName
|
|
241
|
-
*/
|
|
242
|
-
function reclassifyMessagesForModule(messages, severities, moduleName, deprecatedDowngradable) {
|
|
243
|
-
for (const msg of messages) {
|
|
244
|
-
if (msg.messageId && msg.severity !== 'Error')
|
|
245
|
-
msg.severity = reclassifiedSeverity(msg.messageId, msg.severity, severities, moduleName, deprecatedDowngradable);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
241
|
/**
|
|
250
242
|
* Compare two severities. Returns 0 if they are the same, and <0 if
|
|
251
243
|
* `a` has a lower `level` than `b` according to {@link severitySpecs},
|
|
@@ -253,8 +245,8 @@ function reclassifyMessagesForModule(messages, severities, moduleName, deprecate
|
|
|
253
245
|
*
|
|
254
246
|
* compareSeverities('Error', 'Info') => Error < Info => -1
|
|
255
247
|
*
|
|
256
|
-
* @param {
|
|
257
|
-
* @param {
|
|
248
|
+
* @param {MessageSeverity} a
|
|
249
|
+
* @param {MessageSeverity} b
|
|
258
250
|
* @see severitySpecs
|
|
259
251
|
*/
|
|
260
252
|
function compareSeverities( a, b ) {
|
|
@@ -289,10 +281,9 @@ function searchForLocation( model, csnPath ) {
|
|
|
289
281
|
|
|
290
282
|
/**
|
|
291
283
|
* Create the `message` functions to emit messages.
|
|
292
|
-
* See internalDoc/ReportingMessages.md for detail
|
|
293
284
|
*
|
|
294
285
|
* @example
|
|
295
|
-
* ```
|
|
286
|
+
* ```js
|
|
296
287
|
* const { createMessageFunctions } = require(‘../base/messages’);
|
|
297
288
|
* function module( …, options ) {
|
|
298
289
|
* const { message, info, throwWithError } = createMessageFunctions( options, moduleName );
|
|
@@ -308,14 +299,14 @@ function searchForLocation( model, csnPath ) {
|
|
|
308
299
|
* @param {object} [model=null] the CSN or XSN model, used for convenience
|
|
309
300
|
*/
|
|
310
301
|
function createMessageFunctions( options, moduleName, model = null ) {
|
|
311
|
-
return makeMessageFunction( model, options, moduleName
|
|
302
|
+
return makeMessageFunction( model, options, moduleName );
|
|
312
303
|
}
|
|
313
304
|
|
|
314
305
|
/**
|
|
315
306
|
* Create the `message` function to emit messages.
|
|
316
307
|
*
|
|
317
308
|
* @example
|
|
318
|
-
* ```
|
|
309
|
+
* ```js
|
|
319
310
|
* const { makeMessageFunction } = require(‘../base/messages’);
|
|
320
311
|
* function module( …, options ) {
|
|
321
312
|
* const { message, info, throwWithError } = makeMessageFunction( model, options, moduleName );
|
|
@@ -328,28 +319,36 @@ function createMessageFunctions( options, moduleName, model = null ) {
|
|
|
328
319
|
* ```
|
|
329
320
|
* @param {object} model
|
|
330
321
|
* @param {CSN.Options} [options]
|
|
331
|
-
* @param {string} [moduleName]
|
|
332
|
-
* @param {boolean} [throwOnlyWithNew=false] behave like createMessageFunctions
|
|
322
|
+
* @param {string|null} [moduleName]
|
|
333
323
|
*/
|
|
334
|
-
function makeMessageFunction( model, options, moduleName = null
|
|
335
|
-
|
|
336
|
-
|
|
324
|
+
function makeMessageFunction( model, options, moduleName = null ) {
|
|
325
|
+
if (options.testMode) {
|
|
326
|
+
// ensure message consistency during runtime with --test-mode
|
|
337
327
|
_check$Init( options );
|
|
328
|
+
if (!options.messages)
|
|
329
|
+
throw new CompilerAssertion('makeMessageFunction() expects options.messages to exist in testMode!');
|
|
330
|
+
}
|
|
338
331
|
|
|
339
332
|
const hasMessageArray = !!options.messages;
|
|
340
|
-
const
|
|
341
|
-
const deprecatedDowngradable = isDeprecatedEnabled( options, 'downgradableErrors' );
|
|
333
|
+
const deprecatedDowngradable = isDeprecatedEnabled( options, '_downgradableErrors' );
|
|
342
334
|
/**
|
|
343
335
|
* Array of collected compiler messages. Only use it for debugging. Will not
|
|
344
336
|
* contain the messages created during a `callTransparently` call.
|
|
345
337
|
*
|
|
346
|
-
* @type {
|
|
338
|
+
* @type {CompileMessage[]}
|
|
347
339
|
*/
|
|
348
340
|
let messages = options.messages || [];
|
|
341
|
+
/**
|
|
342
|
+
* Whether an error was emitted in the module. Also includes reclassified errors.
|
|
343
|
+
* @type {boolean}
|
|
344
|
+
*/
|
|
349
345
|
let hasNewError = false;
|
|
346
|
+
|
|
347
|
+
reclassifyMessagesForModule();
|
|
348
|
+
|
|
350
349
|
return {
|
|
351
350
|
message, error, warning, info, debug, messages,
|
|
352
|
-
throwWithError
|
|
351
|
+
throwWithError, throwWithAnyError,
|
|
353
352
|
callTransparently, moduleName,
|
|
354
353
|
};
|
|
355
354
|
|
|
@@ -361,22 +360,23 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
361
360
|
texts = { std: textOrArguments };
|
|
362
361
|
textOrArguments = {};
|
|
363
362
|
}
|
|
364
|
-
if (id) {
|
|
365
|
-
if (options.testMode && !options.$recompile)
|
|
366
|
-
_check$Consistency( id, moduleName, severity, texts, options )
|
|
367
|
-
severity = reclassifiedSeverity( id, severity, severities, moduleName, deprecatedDowngradable );
|
|
368
|
-
}
|
|
369
363
|
|
|
370
364
|
const [ fileLocation, semanticLocation, definition ] = _normalizeMessageLocation(location);
|
|
371
365
|
const text = messageText( texts || centralMessageTexts[id], textOrArguments );
|
|
372
366
|
|
|
373
|
-
/** @type {
|
|
367
|
+
/** @type {CompileMessage} */
|
|
374
368
|
const msg = new CompileMessage( fileLocation, text, severity, id, semanticLocation, moduleName );
|
|
375
369
|
if (options.internalMsg)
|
|
376
370
|
msg.error = new Error( 'stack' );
|
|
377
371
|
if (definition)
|
|
378
372
|
msg.$location.address = { definition };
|
|
379
373
|
|
|
374
|
+
if (id) {
|
|
375
|
+
if (options.testMode && !options.$recompile)
|
|
376
|
+
_check$Consistency( id, moduleName, severity, texts, options )
|
|
377
|
+
msg.severity = reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable );
|
|
378
|
+
}
|
|
379
|
+
|
|
380
380
|
messages.push( msg );
|
|
381
381
|
hasNewError = hasNewError || msg.severity === 'Error' &&
|
|
382
382
|
!(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
|
|
@@ -406,7 +406,7 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
406
406
|
|
|
407
407
|
if (isShortSignature) {
|
|
408
408
|
if (texts)
|
|
409
|
-
throw new
|
|
409
|
+
throw new CompilerAssertion('No "texts" argument expected because text was already provided as third argument.');
|
|
410
410
|
} else {
|
|
411
411
|
if (textArguments !== undefined && typeof textArguments !== 'object')
|
|
412
412
|
_expectedType('textArguments', textArguments, 'object')
|
|
@@ -415,7 +415,7 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
function _expectedType(field, value, type) {
|
|
418
|
-
throw new
|
|
418
|
+
throw new CompilerAssertion(`Invalid argument type for ${ field }! Expected ${ type } but got ${ typeof value }. Do you use the old function signature?`);
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
|
|
@@ -470,9 +470,9 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
470
470
|
*/
|
|
471
471
|
function message(id, location, textArguments = null, texts = null) {
|
|
472
472
|
if (!id)
|
|
473
|
-
throw new
|
|
473
|
+
throw new CompilerAssertion('A message id is missing!');
|
|
474
474
|
if (!centralMessages[id])
|
|
475
|
-
throw new
|
|
475
|
+
throw new CompilerAssertion(`Message id '${ id }' is missing an entry in the central message register!`);
|
|
476
476
|
return _message(id, location, textArguments, null, texts);
|
|
477
477
|
}
|
|
478
478
|
|
|
@@ -524,12 +524,31 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
524
524
|
function throwWithAnyError() {
|
|
525
525
|
if (!messages || !messages.length)
|
|
526
526
|
return;
|
|
527
|
-
reclassifyMessagesForModule( messages, severities, moduleName ); // TODO: no, at the beginning of the module
|
|
528
527
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
529
528
|
if (hasError( messages, moduleName ))
|
|
530
529
|
throw new CompilationError( messages, options.attachValidNames && model );
|
|
531
530
|
}
|
|
532
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Reclassifies all messages according to the current module.
|
|
534
|
+
* This is required because if throwWithError() throws and the message's
|
|
535
|
+
* severities has `errorFor` set, then the message may still appear to be a warning.
|
|
536
|
+
*/
|
|
537
|
+
function reclassifyMessagesForModule() {
|
|
538
|
+
for (const msg of messages) {
|
|
539
|
+
if (msg.messageId && msg.severity !== 'Error') {
|
|
540
|
+
const severity = reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable);
|
|
541
|
+
if (severity !== msg.severity) {
|
|
542
|
+
msg.severity = severity;
|
|
543
|
+
// Re-set the module regardless of severity, since we reclassified it.
|
|
544
|
+
Object.defineProperty( msg, '$module', { value: moduleName, configurable: true } );
|
|
545
|
+
hasNewError = hasNewError || severity === 'Error' &&
|
|
546
|
+
!(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
533
552
|
/**
|
|
534
553
|
* Collects all messages during the call of the callback function instead of
|
|
535
554
|
* storing them in the model. Returns the collected messages.
|
|
@@ -537,7 +556,7 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
537
556
|
*
|
|
538
557
|
* @param {Function} callback
|
|
539
558
|
* @param {...any} args
|
|
540
|
-
* @returns {
|
|
559
|
+
* @returns {CompileMessage[]}
|
|
541
560
|
*/
|
|
542
561
|
function callTransparently(callback, ...args) {
|
|
543
562
|
const backup = messages;
|
|
@@ -563,9 +582,25 @@ function _check$Init( options ) {
|
|
|
563
582
|
}
|
|
564
583
|
}
|
|
565
584
|
|
|
585
|
+
/**
|
|
586
|
+
* Check the consistency of the given message and run some basic lint checks. These include:
|
|
587
|
+
*
|
|
588
|
+
* - Long message IDs must be listed centrally.
|
|
589
|
+
* - Messages with the same ID must have the same severity (in a module).
|
|
590
|
+
* - Messages with the same ID must have the same message texts.
|
|
591
|
+
* This ensures that $(PLACEHOLDERS) are used and that we don't accidentally
|
|
592
|
+
* use the same ID for different meanings, i.e. texts.
|
|
593
|
+
*
|
|
594
|
+
* @param {string} id
|
|
595
|
+
* @param {string} moduleName
|
|
596
|
+
* @param {string} severity
|
|
597
|
+
* @param {string|object} texts
|
|
598
|
+
* @param {CSN.Options} options
|
|
599
|
+
* @private
|
|
600
|
+
*/
|
|
566
601
|
function _check$Consistency( id, moduleName, severity, texts, options ) {
|
|
567
602
|
if (id.length > 30 && !centralMessages[id])
|
|
568
|
-
throw new
|
|
603
|
+
throw new CompilerAssertion( `The message ID "${id}" has more than 30 chars and must be listed centrally` );
|
|
569
604
|
if (!options.severities)
|
|
570
605
|
_check$Severities( id, moduleName || '?', severity );
|
|
571
606
|
for (const [variant, text] of
|
|
@@ -573,6 +608,16 @@ function _check$Consistency( id, moduleName, severity, texts, options ) {
|
|
|
573
608
|
_check$Texts( id, variant, text );
|
|
574
609
|
}
|
|
575
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Check the consistency of the message severity for the given message ID.
|
|
613
|
+
* Messages with the same ID must have the same severity (in a module).
|
|
614
|
+
* Non-downgradable errors must never be called with a lower severity.
|
|
615
|
+
*
|
|
616
|
+
* @param {string} id
|
|
617
|
+
* @param {string} moduleName
|
|
618
|
+
* @param {string} severity
|
|
619
|
+
* @private
|
|
620
|
+
*/
|
|
576
621
|
function _check$Severities( id, moduleName, severity ) {
|
|
577
622
|
if (!severity) // if just used message(), we are automatically consistent
|
|
578
623
|
return;
|
|
@@ -582,23 +627,34 @@ function _check$Severities( id, moduleName, severity ) {
|
|
|
582
627
|
if (!expected)
|
|
583
628
|
test$severities[id] = severity;
|
|
584
629
|
else if (expected !== severity)
|
|
585
|
-
throw new
|
|
630
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "${expected}" from previous call, not "${severity}" for message ID "${id}"` );
|
|
586
631
|
return;
|
|
587
632
|
}
|
|
588
633
|
// now try whether the message could be something less than an Error in the module due to user wishes
|
|
589
|
-
|
|
590
|
-
if (user === 'Error') { // always an error in module
|
|
634
|
+
if (!isDowngradable( id, moduleName )) { // always an error in module
|
|
591
635
|
if (severity !== 'Error')
|
|
592
|
-
throw new
|
|
636
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "Error", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
593
637
|
}
|
|
594
638
|
else if (spec.severity === 'Error') {
|
|
595
|
-
throw new
|
|
639
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting the use of function message() when message ID "${id}" is a configurable error in module "${moduleName}"` );
|
|
596
640
|
}
|
|
597
641
|
else if (spec.severity !== severity) {
|
|
598
|
-
throw new
|
|
642
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "${spec.severity}", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
599
643
|
}
|
|
600
644
|
}
|
|
601
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Check the consistency of the message text for the given message ID.
|
|
648
|
+
*
|
|
649
|
+
* Messages with the same ID must have the same message texts.
|
|
650
|
+
* This ensures that $(PLACEHOLDERS) are used and that we don't accidentally
|
|
651
|
+
* use the same ID for different meanings, i.e. texts.
|
|
652
|
+
*
|
|
653
|
+
* @param {string} id
|
|
654
|
+
* @param {string} prop
|
|
655
|
+
* @param {string} value
|
|
656
|
+
* @private
|
|
657
|
+
*/
|
|
602
658
|
function _check$Texts( id, prop, value ) {
|
|
603
659
|
if (!test$texts[id])
|
|
604
660
|
test$texts[id] = Object.create(null);
|
|
@@ -606,7 +662,7 @@ function _check$Texts( id, prop, value ) {
|
|
|
606
662
|
if (!expected)
|
|
607
663
|
test$texts[id][prop] = value;
|
|
608
664
|
else if (expected !== value)
|
|
609
|
-
throw new
|
|
665
|
+
throw new CompilerAssertion( `Different texts for the same message ID. Expecting "${expected}", not "${value}" for ID "${id}" and text variant "${prop}"`);
|
|
610
666
|
}
|
|
611
667
|
|
|
612
668
|
const quote = { // could be an option in the future
|
|
@@ -649,6 +705,7 @@ const paramsTransform = {
|
|
|
649
705
|
expecting: transformManyWith( tokenSymbol ),
|
|
650
706
|
// msg: m => m,
|
|
651
707
|
$reviewed: ignoreTextTransform,
|
|
708
|
+
version: quote.meta,
|
|
652
709
|
};
|
|
653
710
|
|
|
654
711
|
function ignoreTextTransform() {
|
|
@@ -803,7 +860,7 @@ function weakLocation( loc ) {
|
|
|
803
860
|
* Example:
|
|
804
861
|
* <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
|
|
805
862
|
*
|
|
806
|
-
* @param {
|
|
863
|
+
* @param {CompileMessage} err
|
|
807
864
|
* @param {boolean} [normalizeFilename]
|
|
808
865
|
* @param {boolean} [noMessageId]
|
|
809
866
|
* @param {boolean} [noHome]
|
|
@@ -813,7 +870,7 @@ function messageString( err, normalizeFilename, noMessageId, noHome ) {
|
|
|
813
870
|
return (err.$location && err.$location.file
|
|
814
871
|
? locationString( err.$location, normalizeFilename ) + ': '
|
|
815
872
|
: '') +
|
|
816
|
-
(err.severity||'Error') +
|
|
873
|
+
(err.severity || 'Error') +
|
|
817
874
|
// TODO: use [message-id]
|
|
818
875
|
(err.messageId && !noMessageId ? ' ' + err.messageId + ': ' : ': ') +
|
|
819
876
|
err.message +
|
|
@@ -825,7 +882,7 @@ function messageString( err, normalizeFilename, noMessageId, noHome ) {
|
|
|
825
882
|
* Return message hash which is either the message string without the file location,
|
|
826
883
|
* or the full message string if no semantic location is provided.
|
|
827
884
|
*
|
|
828
|
-
* @param {
|
|
885
|
+
* @param {CompileMessage} msg
|
|
829
886
|
* @returns {string} can be used to uniquely identify a message
|
|
830
887
|
*/
|
|
831
888
|
function messageHash(msg) {
|
|
@@ -851,7 +908,7 @@ function messageHash(msg) {
|
|
|
851
908
|
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
852
909
|
* ```
|
|
853
910
|
*
|
|
854
|
-
* @param {
|
|
911
|
+
* @param {CompileMessage} err
|
|
855
912
|
* @param {object} [config = {}]
|
|
856
913
|
* @param {boolean} [config.normalizeFilename] Replace windows `\` with forward slashes `/`.
|
|
857
914
|
* @param {boolean} [config.noMessageId]
|
|
@@ -899,7 +956,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
899
956
|
*
|
|
900
957
|
* @param {string[]} sourceLines The source code split up into lines, e.g. by `splitLines(src)`
|
|
901
958
|
* from `lib/utils/file.js`
|
|
902
|
-
* @param {
|
|
959
|
+
* @param {CompileMessage} err Error object containing all details like line, message, etc.
|
|
903
960
|
* @param {object} [config = {}]
|
|
904
961
|
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
|
|
905
962
|
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
@@ -978,8 +1035,8 @@ function messageContext(sourceLines, err, config) {
|
|
|
978
1035
|
* larger than `b`, and -1 if `a` is smaller than `b`. Messages without a location
|
|
979
1036
|
* are considered larger than messages with a location.
|
|
980
1037
|
*
|
|
981
|
-
* @param {
|
|
982
|
-
* @param {
|
|
1038
|
+
* @param {CompileMessage} a
|
|
1039
|
+
* @param {CompileMessage} b
|
|
983
1040
|
*/
|
|
984
1041
|
function compareMessage( a, b ) {
|
|
985
1042
|
const aFile = a.$location && a.$location.file;
|
|
@@ -1013,8 +1070,8 @@ function compareMessage( a, b ) {
|
|
|
1013
1070
|
* location and severity, >0 if `a` is larger than `b`, and <0 if `a` is smaller
|
|
1014
1071
|
* than `b`. See `compareSeverities()` for how severities are compared.
|
|
1015
1072
|
*
|
|
1016
|
-
* @param {
|
|
1017
|
-
* @param {
|
|
1073
|
+
* @param {CompileMessage} a
|
|
1074
|
+
* @param {CompileMessage} b
|
|
1018
1075
|
*/
|
|
1019
1076
|
function compareMessageSeverityAware( a, b ) {
|
|
1020
1077
|
const c = compareSeverities(a.severity, b.severity);
|
|
@@ -1026,7 +1083,7 @@ function compareMessageSeverityAware( a, b ) {
|
|
|
1026
1083
|
* Messages without semantic locations are considered smaller (for syntax errors)
|
|
1027
1084
|
* and (currently - should not happen in v2) larger for other messages.
|
|
1028
1085
|
*
|
|
1029
|
-
* @param {
|
|
1086
|
+
* @param {CompileMessage} msg
|
|
1030
1087
|
*/
|
|
1031
1088
|
function homeSortName( { home, messageId } ) {
|
|
1032
1089
|
return (!home)
|
|
@@ -1045,7 +1102,7 @@ function homeSortName( { home, messageId } ) {
|
|
|
1045
1102
|
* A message is more precise if it is contained in the other or if
|
|
1046
1103
|
* the first does not have an endLine/endCol.
|
|
1047
1104
|
*
|
|
1048
|
-
* @param {
|
|
1105
|
+
* @param {CompileMessage[]} messages
|
|
1049
1106
|
*/
|
|
1050
1107
|
function deduplicateMessages( messages ) {
|
|
1051
1108
|
const seen = new Map();
|
|
@@ -1395,6 +1452,7 @@ function _quoted( name ) {
|
|
|
1395
1452
|
|
|
1396
1453
|
/**
|
|
1397
1454
|
* Get the explanation string for the given message-id.
|
|
1455
|
+
* Ensure to have called hasMessageExplanation() before.
|
|
1398
1456
|
*
|
|
1399
1457
|
* @param {string} messageId
|
|
1400
1458
|
* @returns {string}
|
|
@@ -1402,18 +1460,22 @@ function _quoted( name ) {
|
|
|
1402
1460
|
* @see hasMessageExplanation()
|
|
1403
1461
|
*/
|
|
1404
1462
|
function explainMessage(messageId) {
|
|
1463
|
+
messageId = oldMessageIds[messageId] || messageId;
|
|
1405
1464
|
const filename = path.join(__dirname, '..', '..', 'share', 'messages', `${messageId}.md`);
|
|
1406
1465
|
return fs.readFileSync(filename, 'utf8');
|
|
1407
1466
|
}
|
|
1408
1467
|
|
|
1409
1468
|
/**
|
|
1410
1469
|
* Returns true if the given message has an explanation file.
|
|
1470
|
+
* Takes into account changed message ids, i.e. looks up if the new
|
|
1471
|
+
* message id has an explanation.
|
|
1411
1472
|
*
|
|
1412
1473
|
* @param {string} messageId
|
|
1413
1474
|
* @returns {boolean}
|
|
1414
1475
|
*/
|
|
1415
1476
|
function hasMessageExplanation(messageId) {
|
|
1416
|
-
|
|
1477
|
+
const id = oldMessageIds[messageId] || messageId || false;
|
|
1478
|
+
return id && _messageIdsWithExplanation.includes(id);
|
|
1417
1479
|
}
|
|
1418
1480
|
|
|
1419
1481
|
/**
|
package/lib/base/model.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { forEach } = require("../utils/objectUtils");
|
|
4
|
+
|
|
3
5
|
const queryOps = {
|
|
4
6
|
query: 'select', // TODO: rename to SELECT
|
|
5
7
|
union: 'union',
|
|
@@ -20,7 +22,6 @@ const queryOps = {
|
|
|
20
22
|
const availableBetaFlags = {
|
|
21
23
|
// enabled by --beta-mode
|
|
22
24
|
toRename: true,
|
|
23
|
-
addTextsLanguageAssoc: true,
|
|
24
25
|
assocsWithParams: true,
|
|
25
26
|
hanaAssocRealCardinality: true,
|
|
26
27
|
mapAssocToJoinCardinality: true,
|
|
@@ -58,16 +59,56 @@ function isBetaEnabled( options, feature ) {
|
|
|
58
59
|
* Please do not move this function to the "option processor" code.
|
|
59
60
|
*
|
|
60
61
|
* @param {object} options Options
|
|
61
|
-
* @param {string} [feature] Feature to check for
|
|
62
|
+
* @param {string|null} [feature] Feature to check for
|
|
62
63
|
* @returns {boolean}
|
|
63
64
|
*/
|
|
64
65
|
function isDeprecatedEnabled( options, feature = null ) {
|
|
65
66
|
const { deprecated } = options;
|
|
66
67
|
if (!feature)
|
|
67
68
|
return !!deprecated;
|
|
69
|
+
|
|
68
70
|
return deprecated && typeof deprecated === 'object' && deprecated[feature];
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
const oldDeprecatedFlags_v2 = [
|
|
74
|
+
'createLocalizedViews',
|
|
75
|
+
'downgradableErrors',
|
|
76
|
+
'generatedEntityNameWithUnderscore',
|
|
77
|
+
'longAutoexposed',
|
|
78
|
+
'noElementsExpansion',
|
|
79
|
+
'noInheritedAutoexposeViaComposition',
|
|
80
|
+
'noScopedRedirections',
|
|
81
|
+
'oldVirtualNotNullPropagation',
|
|
82
|
+
'parensAsStrings',
|
|
83
|
+
'projectionAsQuery',
|
|
84
|
+
'redirectInSubQueries',
|
|
85
|
+
'renderVirtualElements',
|
|
86
|
+
'shortAutoexposed',
|
|
87
|
+
'unmanagedUpInComponent',
|
|
88
|
+
'v1KeysForTemporal',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* In cds-compiler v3, we removed old v2 deprecated flags. That can lead to silent
|
|
93
|
+
* errors such as entity/view names changing. To ensure that the user is forced
|
|
94
|
+
* to change their code, emit an error if one of such removed flags was used.
|
|
95
|
+
*
|
|
96
|
+
* @param {CSN.Options} options
|
|
97
|
+
* @param error Error message function returned by makeMessageFunctions().
|
|
98
|
+
*/
|
|
99
|
+
function checkRemovedDeprecatedFlags( options, { error } ) {
|
|
100
|
+
// Assume that we emitted these errors once if a message with this ID was found.
|
|
101
|
+
if (!options.deprecated || options.messages?.some(m => m.messageId === 'api-invalid-deprecated'))
|
|
102
|
+
return;
|
|
103
|
+
|
|
104
|
+
forEach(options.deprecated, (key, val) => {
|
|
105
|
+
if (val && oldDeprecatedFlags_v2.includes(key)) {
|
|
106
|
+
error('api-invalid-deprecated', null, { name: key },
|
|
107
|
+
'Deprecated flag $(NAME) has been removed in CDS compiler v3');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
71
112
|
// Apply function `callback` to all artifacts in dictionary
|
|
72
113
|
// `model.definitions`. See function `forEachGeneric` for details.
|
|
73
114
|
function forEachDefinition( model, callback ) {
|
|
@@ -129,6 +170,7 @@ module.exports = {
|
|
|
129
170
|
isBetaEnabled,
|
|
130
171
|
availableBetaFlags,
|
|
131
172
|
isDeprecatedEnabled,
|
|
173
|
+
checkRemovedDeprecatedFlags,
|
|
132
174
|
queryOps,
|
|
133
175
|
forEachDefinition,
|
|
134
176
|
forEachMember,
|
|
@@ -339,7 +339,11 @@ function createOptionProcessor() {
|
|
|
339
339
|
// No more options after '--'
|
|
340
340
|
seenDashDash = true;
|
|
341
341
|
}
|
|
342
|
+
else if (!seenDashDash && arg.startsWith('--')) {
|
|
343
|
+
i += processOption(i);
|
|
344
|
+
}
|
|
342
345
|
else if (!seenDashDash && arg.startsWith('-')) {
|
|
346
|
+
splitSingleLetterOption(argv, i); // `-ab` -> `-a -b`
|
|
343
347
|
i += processOption(i);
|
|
344
348
|
}
|
|
345
349
|
else {
|
|
@@ -640,6 +644,21 @@ function createOptionProcessor() {
|
|
|
640
644
|
}
|
|
641
645
|
}
|
|
642
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Splits `-abc` into `-a -b -c`. Does this in-place on argv.
|
|
649
|
+
*
|
|
650
|
+
* @param {string[]} argv Argument array
|
|
651
|
+
* @param {number} i Current option index.
|
|
652
|
+
*/
|
|
653
|
+
function splitSingleLetterOption(argv, i) {
|
|
654
|
+
const arg = argv[i];
|
|
655
|
+
if (arg.length > 2) { // must be at least `-ab`.
|
|
656
|
+
const rest = argv.slice(i + 1);
|
|
657
|
+
argv.length = i; // trim array
|
|
658
|
+
argv.push(...arg.split('').slice(1).map(a => `-${a}`), ...rest);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
643
662
|
/**
|
|
644
663
|
* Return a camelCase name "fooBar" for a long option "--foo-bar"
|
|
645
664
|
*/
|
|
@@ -16,8 +16,8 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
16
16
|
if (!(art.kind === 'action' || art.kind === 'function') && !art.actions)
|
|
17
17
|
return;
|
|
18
18
|
|
|
19
|
-
// const isMultiSchema = this.options.
|
|
20
|
-
// (this.options.
|
|
19
|
+
// const isMultiSchema = this.options.odataFormat === 'structured' &&
|
|
20
|
+
// (this.options.odataProxies || this.options.odataXServiceRefs);
|
|
21
21
|
|
|
22
22
|
const serviceName = this.csnUtils.getServiceName(artName);
|
|
23
23
|
if (!serviceName)
|
|
@@ -85,9 +85,11 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
85
85
|
* @param {string} actKind 'action' or 'function'
|
|
86
86
|
*/
|
|
87
87
|
function checkReturns(returns, currPath, actKind) {
|
|
88
|
-
const finalReturnType = returns.type ? this.csnUtils.
|
|
88
|
+
const finalReturnType = returns.type ? this.csnUtils.getFinalBaseTypeWithProps(returns.type) : returns;
|
|
89
|
+
if (!finalReturnType)
|
|
90
|
+
return; // no type, e.g. `type of V:calculated`; already an error in `checkTypeOfHasProperType()`
|
|
89
91
|
|
|
90
|
-
if (this.csnUtils.isAssocOrComposition(finalReturnType)) {
|
|
92
|
+
if (this.csnUtils.isAssocOrComposition(finalReturnType.type)) {
|
|
91
93
|
this.error(null, currPath, { '#': actKind },
|
|
92
94
|
{
|
|
93
95
|
std: 'An association is not allowed as this artifact\'s return type', // Not used
|
|
@@ -98,7 +100,7 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
98
100
|
|
|
99
101
|
if (finalReturnType.items) // check array return type
|
|
100
102
|
checkReturns.bind(this)(finalReturnType.items, currPath.concat('items'), actKind);
|
|
101
|
-
else // check if return type is user
|
|
103
|
+
else // check if return type is user defined from the current service
|
|
102
104
|
checkUserDefinedType.bind(this)(finalReturnType, returns.type, currPath);
|
|
103
105
|
}
|
|
104
106
|
|