@sap/cds-compiler 2.13.8 → 2.15.2
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 +109 -4
- package/bin/cdsc.js +112 -37
- package/lib/api/main.js +20 -22
- package/lib/api/options.js +2 -3
- package/lib/api/validate.js +6 -6
- package/lib/base/message-registry.js +89 -14
- package/lib/base/messages.js +85 -64
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/validator.js +2 -4
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +11 -0
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +59 -11
- package/lib/compiler/extend.js +20 -3
- package/lib/compiler/finalize-parse-cdl.js +26 -20
- package/lib/compiler/index.js +75 -26
- package/lib/compiler/populate.js +6 -5
- package/lib/compiler/propagator.js +4 -1
- package/lib/compiler/resolve.js +104 -16
- package/lib/compiler/shared.js +61 -27
- package/lib/compiler/tweak-assocs.js +7 -1
- package/lib/edm/annotations/genericTranslation.js +33 -15
- package/lib/edm/csn2edm.js +216 -98
- package/lib/edm/edm.js +298 -225
- package/lib/edm/edmPreprocessor.js +486 -415
- package/lib/edm/edmUtils.js +22 -22
- package/lib/gen/Dictionary.json +90 -16
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageParser.js +4636 -4368
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +0 -2
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +47 -2
- package/lib/language/language.g4 +59 -27
- package/lib/main.d.ts +19 -1
- package/lib/main.js +6 -0
- package/lib/model/csnRefs.js +33 -6
- package/lib/model/csnUtils.js +193 -75
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +2 -2
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +62 -26
- package/lib/render/toCdl.js +844 -679
- package/lib/render/toHdbcds.js +189 -243
- package/lib/render/toSql.js +180 -198
- package/lib/render/utils/common.js +131 -15
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/constraints.js +3 -1
- package/lib/transform/db/expansion.js +15 -10
- package/lib/transform/db/flattening.js +94 -64
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +6 -3
- package/lib/transform/forHanaNew.js +43 -26
- package/lib/transform/forOdataNew.js +43 -42
- package/lib/transform/localized.js +12 -7
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +145 -197
- package/lib/transform/transformUtilsNew.js +9 -12
- package/lib/transform/translateAssocsToJoins.js +1 -1
- package/lib/transform/universalCsn/coreComputed.js +5 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
- package/lib/utils/moduleResolve.js +13 -6
- package/package.json +1 -1
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- 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
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
'use strict';
|
|
32
32
|
|
|
33
|
+
const { CompilerAssertion } = require("./error");
|
|
34
|
+
const { createDict } = require("../utils/objectUtils");
|
|
35
|
+
|
|
33
36
|
/**
|
|
34
37
|
* Central register of messages and their configuration.
|
|
35
38
|
* Group by id-category.
|
|
@@ -62,7 +65,7 @@ const centralMessages = {
|
|
|
62
65
|
'assoc-as-type': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: allow more, but not all
|
|
63
66
|
'def-unexpected-paramview-assoc': { severity: 'Error' },
|
|
64
67
|
'def-unexpected-calcview-assoc': { severity: 'Error' },
|
|
65
|
-
|
|
68
|
+
'chained-array-of': { severity: 'Error' },
|
|
66
69
|
'check-proper-type': { severity: 'Error', configurableFor: [ 'compile' ] },
|
|
67
70
|
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.rename' ] },
|
|
68
71
|
|
|
@@ -77,6 +80,7 @@ const centralMessages = {
|
|
|
77
80
|
'expected-type': { severity: 'Error' },
|
|
78
81
|
'ref-sloppy-type': { severity: 'Error' },
|
|
79
82
|
'type-unexpected-typeof': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: make it non-config
|
|
83
|
+
'type-expected-builtin': { severity: 'Error', configurableFor: true },
|
|
80
84
|
'expected-actionparam-type': { severity: 'Error' },
|
|
81
85
|
'ref-sloppy-actionparam-type': { severity: 'Error' },
|
|
82
86
|
'expected-event-type': { severity: 'Error' },
|
|
@@ -97,6 +101,7 @@ const centralMessages = {
|
|
|
97
101
|
'query-unexpected-assoc-hdbcds': { severity: 'Error' },
|
|
98
102
|
'query-unexpected-structure-hdbcds': { severity: 'Error' },
|
|
99
103
|
'query-ignoring-param-nullability': { severity: 'Info' },
|
|
104
|
+
'query-expected-identifier': { severity: 'Error' },
|
|
100
105
|
|
|
101
106
|
'recalculated-localized': { severity: 'Info' }, // KEEP: Downgrade in lib/transform/translateAssocsToJoins.js
|
|
102
107
|
'redirected-implicitly-ambiguous': { severity: 'Error', configurableFor: true }, // does not hurt us - TODO: ref-ambiguous-target
|
|
@@ -142,6 +147,8 @@ const centralMessages = {
|
|
|
142
147
|
'syntax-invalid-escape': { severity: 'Error' },
|
|
143
148
|
'syntax-missing-escape': { severity: 'Error' },
|
|
144
149
|
|
|
150
|
+
'syntax-expected-integer': { severity: 'Error' },
|
|
151
|
+
|
|
145
152
|
'type-managed-composition': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: non-config
|
|
146
153
|
|
|
147
154
|
'def-missing-element': { severity: 'Error' },
|
|
@@ -151,22 +158,46 @@ const centralMessages = {
|
|
|
151
158
|
'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
|
|
152
159
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
153
160
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
161
|
+
'odata-spec-violation-id': { severity: 'Error' },
|
|
154
162
|
'odata-spec-violation-type': { severity: 'Error', configurableFor: [ 'to.edmx' ] },
|
|
163
|
+
'odata-spec-violation-type-unknown': { severity: 'Warning' },
|
|
155
164
|
'odata-spec-violation-no-key': { severity: 'Warning' },
|
|
156
165
|
'odata-spec-violation-key-array': { severity: 'Error' }, // more than 30 chars
|
|
157
166
|
'odata-spec-violation-key-null': { severity: 'Error' }, // more than 30 chars
|
|
158
167
|
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
159
168
|
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
160
|
-
'odata-spec-violation-namespace-name': { severity: 'Warning' }, // more than 30 chars
|
|
161
169
|
};
|
|
162
170
|
|
|
171
|
+
// Old/Deprecated message IDs that we only still use for backwards-compatibility.
|
|
172
|
+
// We keep them in a separate array for easier access. No need to go through all
|
|
173
|
+
// existing messages and search for the old one in `oldNames` property.
|
|
174
|
+
// The keys will be added to `oldNames` of the new message, which is used for reclassification.
|
|
175
|
+
const oldMessageIds = createDict({
|
|
176
|
+
'old-anno-duplicate': 'anno-duplicate', // Example
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Set up the old-to-new message ID mapping in the message registry.
|
|
180
|
+
for (const oldName in oldMessageIds) {
|
|
181
|
+
const newName = oldMessageIds[oldName];
|
|
182
|
+
if (centralMessages[oldName])
|
|
183
|
+
throw new CompilerAssertion(`Mapping from ${oldName} not possible: ID is still used in message registry.`);
|
|
184
|
+
if (!centralMessages[newName])
|
|
185
|
+
throw new CompilerAssertion(`Mapping from ${oldName} to new message ID ${newName} does not exist!`);
|
|
186
|
+
|
|
187
|
+
if (!centralMessages[newName].oldNames)
|
|
188
|
+
centralMessages[newName].oldNames = [ oldName ];
|
|
189
|
+
else
|
|
190
|
+
centralMessages[newName].oldNames.push(oldName);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
163
194
|
// For messageIds, where no text has been provided via code (central def)
|
|
164
195
|
const centralMessageTexts = {
|
|
165
196
|
'anno-duplicate': 'Duplicate assignment with $(ANNO)',
|
|
166
197
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
167
198
|
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
168
199
|
'anno-unexpected-ellipsis-layers': 'No base annotation available to apply $(CODE)',
|
|
169
|
-
'
|
|
200
|
+
'chained-array-of': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
|
|
170
201
|
'syntax-csn-expected-object': 'Expected object for property $(PROP)',
|
|
171
202
|
'syntax-csn-expected-column': 'Expected object or string \'*\' for property $(PROP)',
|
|
172
203
|
'syntax-csn-expected-natnum': 'Expected non-negative number for property $(PROP)',
|
|
@@ -204,6 +235,16 @@ const centralMessageTexts = {
|
|
|
204
235
|
std: 'Missing escape. Replace $(CODE) with $(NEWCODE)',
|
|
205
236
|
placeholder: 'Placeholders are not supported. Replace $(CODE) with $(NEWCODE)',
|
|
206
237
|
},
|
|
238
|
+
'syntax-expected-integer': {
|
|
239
|
+
std: 'A safe integer is expected here',
|
|
240
|
+
normal: 'An integer number is expected here',
|
|
241
|
+
unsafe: 'The provided integer is too large',
|
|
242
|
+
},
|
|
243
|
+
'syntax-duplicate-argument': {
|
|
244
|
+
std: 'Unexpected argument $(CODE)',
|
|
245
|
+
unknown: 'Unknown argument $(CODE)',
|
|
246
|
+
duplicate: 'Duplicate argument $(CODE)',
|
|
247
|
+
},
|
|
207
248
|
'ref-undefined-def': {
|
|
208
249
|
std: 'Artifact $(ART) has not been found',
|
|
209
250
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -213,7 +254,8 @@ const centralMessageTexts = {
|
|
|
213
254
|
// TODO: proposal 'No definition found for $(NAME)',
|
|
214
255
|
'ref-undefined-element': {
|
|
215
256
|
std: 'Element $(ART) has not been found',
|
|
216
|
-
element: 'Artifact $(ART) has no element $(MEMBER)'
|
|
257
|
+
element: 'Artifact $(ART) has no element $(MEMBER)',
|
|
258
|
+
aspect: 'Element $(ID) has not been found in the anonymous target aspect'
|
|
217
259
|
},
|
|
218
260
|
'ref-unknown-var': {
|
|
219
261
|
std: 'Replacement $(ID) not found'
|
|
@@ -231,6 +273,19 @@ const centralMessageTexts = {
|
|
|
231
273
|
param: 'Unexpected $(KEYWORD) for the type of a parameter definition',
|
|
232
274
|
select: 'Unexpected $(KEYWORD) for type references in queries',
|
|
233
275
|
},
|
|
276
|
+
|
|
277
|
+
'type-missing-argument': 'Missing value for argument $(NAME) in reference to type $(ID)',
|
|
278
|
+
'type-ignoring-argument': 'Too many arguments for type $(ART)',
|
|
279
|
+
'type-unexpected-argument': {
|
|
280
|
+
std: 'Too many arguments for type $(ART)',
|
|
281
|
+
type: 'Unexpected argument $(PROP) for type $(ART) with base type $(TYPE)',
|
|
282
|
+
builtin: 'Unexpected argument $(PROP) for type $(ART)',
|
|
283
|
+
'non-scalar': 'Only scalar types can have arguments',
|
|
284
|
+
max: 'Expecting argument $(PROP) for type $(TYPE) to not exceed $(NUMBER)',
|
|
285
|
+
min: 'Expecting argument $(PROP) for type $(TYPE) to be greater than or equal to $(NUMBER)',
|
|
286
|
+
'incorrect-type': 'Expected $(NAMES) for argument $(PROP), but found $(CODE)',
|
|
287
|
+
},
|
|
288
|
+
|
|
234
289
|
'anno-builtin': 'Builtin types should not be annotated. Use custom type instead',
|
|
235
290
|
'anno-undefined-def': 'Artifact $(ART) has not been found',
|
|
236
291
|
'anno-undefined-art': 'No artifact has been found with name $(NAME)',
|
|
@@ -296,6 +351,10 @@ const centralMessageTexts = {
|
|
|
296
351
|
std: 'Ignoring nullability constraint on parameter when generating SAP HANA CDS view',
|
|
297
352
|
sql: 'Ignoring nullability constraint on parameter when generating SQL view'
|
|
298
353
|
},
|
|
354
|
+
'query-expected-identifier': {
|
|
355
|
+
std: 'Expected identifier for select item',
|
|
356
|
+
assoc: 'Expected identifier as the association\'s name',
|
|
357
|
+
},
|
|
299
358
|
|
|
300
359
|
'ref-sloppy-type': 'A type or an element is expected here',
|
|
301
360
|
'ref-sloppy-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
@@ -312,11 +371,16 @@ const centralMessageTexts = {
|
|
|
312
371
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
313
372
|
|
|
314
373
|
// OData version dependent messages
|
|
315
|
-
'odata-spec-violation-array': 'Unexpected array type for $(
|
|
316
|
-
'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for $(
|
|
317
|
-
'odata-spec-violation-returns': 'Expected $(KIND) to return one or many values of scalar, complex, entity or view type for $(
|
|
318
|
-
'odata-spec-violation-assoc': 'Unexpected association in structured type for $(
|
|
319
|
-
'odata-spec-violation-constraints': 'Partial referential constraints produced for $(
|
|
374
|
+
'odata-spec-violation-array': 'Unexpected array type for OData $(VERSION)',
|
|
375
|
+
'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for OData $(VERSION)',
|
|
376
|
+
'odata-spec-violation-returns': 'Expected $(KIND) to return one or many values of scalar, complex, entity or view type for OData $(VERSION)',
|
|
377
|
+
'odata-spec-violation-assoc': 'Unexpected association in structured type for OData $(VERSION)',
|
|
378
|
+
'odata-spec-violation-constraints': 'Partial referential constraints produced for OData $(VERSION)',
|
|
379
|
+
'odata-spec-violation-id': {
|
|
380
|
+
std: 'Expected EDM name $(ID) to start with a letter or underscore, followed by at most 127 letters, underscores or digits',
|
|
381
|
+
'v2firstchar': 'Unexpected first character $(PROP) of EDM Name $(ID) for OData $(VERSION)',
|
|
382
|
+
'qualifier': 'Expected annotation qualifier $(ID) to start with a letter or underscore, followed by at most 127 letters, underscores or digits'
|
|
383
|
+
},
|
|
320
384
|
// version independent messages
|
|
321
385
|
'odata-spec-violation-key-array': {
|
|
322
386
|
std: 'Unexpected array type for element $(NAME)',
|
|
@@ -331,21 +395,30 @@ const centralMessageTexts = {
|
|
|
331
395
|
scalar: 'Unexpected $(TYPE) mapped to $(ID) as type for key element' // flat
|
|
332
396
|
},
|
|
333
397
|
'odata-spec-violation-no-key': 'Expected entity to have a primary key',
|
|
334
|
-
'odata-spec-violation-type': '
|
|
398
|
+
'odata-spec-violation-type-unknown': 'Unknown Edm Type $(TYPE)',
|
|
399
|
+
'odata-spec-violation-type': {
|
|
400
|
+
std: 'Expected element to have a type',
|
|
401
|
+
incompatible: 'Unexpected EDM Type $(TYPE) for OData $(VERSION)',
|
|
402
|
+
facet: 'Unexpected EDM Type facet $(NAME) of type $(TYPE) for OData $(VERSION)',
|
|
403
|
+
},
|
|
335
404
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
|
|
336
|
-
'odata-spec-violation-namespace':
|
|
405
|
+
'odata-spec-violation-namespace': {
|
|
406
|
+
std: 'Expected service name not to be one of the reserved names $(NAMES)',
|
|
407
|
+
length: 'Expected service name not to exceed 511 characters',
|
|
408
|
+
},
|
|
337
409
|
// Other odata/edm errors
|
|
338
410
|
'odata-definition-exists': {
|
|
339
411
|
std: 'Entity can\'t be created due to name collision with existing definition $(NAME)',
|
|
340
412
|
proxy: 'No proxy entity created due to name collision with existing definition $(NAME) of kind $(KIND)'
|
|
341
|
-
}
|
|
413
|
+
},
|
|
414
|
+
'odata-navigation': 'No OData navigation property generated, target $(TARGET) is outside of service $(SERVICE)'
|
|
342
415
|
}
|
|
343
416
|
|
|
344
417
|
/**
|
|
345
418
|
* Configuration for a message in the central message register.
|
|
346
419
|
*
|
|
347
420
|
* @typedef {object} MessageConfig
|
|
348
|
-
* @property {
|
|
421
|
+
* @property {MessageSeverity} severity Default severity for the message.
|
|
349
422
|
* @property {string[]|'deprecated'|true} [configurableFor]
|
|
350
423
|
* Whether the error can be reclassified to a warning or lower.
|
|
351
424
|
* If not `true` then an array is expected with specified modules in which the error is downgradable.
|
|
@@ -356,6 +429,8 @@ const centralMessageTexts = {
|
|
|
356
429
|
* @property {boolean} [throughMessageCall]
|
|
357
430
|
* If set, it means that a message-id was added to the registry in test-mode through a `message.<severity>()`
|
|
358
431
|
* call. Used for ensuring that all calls with the same message-id have the same severity.
|
|
432
|
+
* @property {string[]} [oldNames] Aliases for the message id. Used for reclassification as well as "explain" messages.
|
|
433
|
+
* Don't set this property directly! Append to object oldMessageIds instead!
|
|
359
434
|
*/
|
|
360
435
|
|
|
361
|
-
module.exports = { centralMessages, centralMessageTexts };
|
|
436
|
+
module.exports = { centralMessages, centralMessageTexts, oldMessageIds };
|
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,7 +25,8 @@ 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
|
+
* Returns true if at least one of the given messages is of severity "Error".
|
|
29
|
+
*
|
|
28
30
|
* @param {CSN.Message[]} messages
|
|
29
31
|
* @returns {boolean}
|
|
30
32
|
*/
|
|
@@ -34,7 +36,8 @@ 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
42
|
* @param {CSN.Message[]} messages
|
|
40
43
|
* @param {string} moduleName
|
|
@@ -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 );
|
|
@@ -329,15 +320,13 @@ function createMessageFunctions( options, moduleName, model = null ) {
|
|
|
329
320
|
* @param {object} model
|
|
330
321
|
* @param {CSN.Options} [options]
|
|
331
322
|
* @param {string} [moduleName]
|
|
332
|
-
* @param {boolean} [throwOnlyWithNew=false] behave like createMessageFunctions
|
|
333
323
|
*/
|
|
334
|
-
function makeMessageFunction( model, options, moduleName = null
|
|
324
|
+
function makeMessageFunction( model, options, moduleName = null ) {
|
|
335
325
|
// ensure message consistency during runtime with --test-mode
|
|
336
326
|
if (options.testMode)
|
|
337
327
|
_check$Init( options );
|
|
338
328
|
|
|
339
329
|
const hasMessageArray = !!options.messages;
|
|
340
|
-
const severities = options.severities || {};
|
|
341
330
|
const deprecatedDowngradable = isDeprecatedEnabled( options, 'downgradableErrors' );
|
|
342
331
|
/**
|
|
343
332
|
* Array of collected compiler messages. Only use it for debugging. Will not
|
|
@@ -346,10 +335,17 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
346
335
|
* @type {CSN.Message[]}
|
|
347
336
|
*/
|
|
348
337
|
let messages = options.messages || [];
|
|
338
|
+
/**
|
|
339
|
+
* Whether an error was emitted in the module. Also includes reclassified errors.
|
|
340
|
+
* @type {boolean}
|
|
341
|
+
*/
|
|
349
342
|
let hasNewError = false;
|
|
343
|
+
|
|
344
|
+
reclassifyMessagesForModule();
|
|
345
|
+
|
|
350
346
|
return {
|
|
351
347
|
message, error, warning, info, debug, messages,
|
|
352
|
-
throwWithError
|
|
348
|
+
throwWithError, throwWithAnyError,
|
|
353
349
|
callTransparently, moduleName,
|
|
354
350
|
};
|
|
355
351
|
|
|
@@ -361,11 +357,6 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
361
357
|
texts = { std: textOrArguments };
|
|
362
358
|
textOrArguments = {};
|
|
363
359
|
}
|
|
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
360
|
|
|
370
361
|
const [ fileLocation, semanticLocation, definition ] = _normalizeMessageLocation(location);
|
|
371
362
|
const text = messageText( texts || centralMessageTexts[id], textOrArguments );
|
|
@@ -377,6 +368,12 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
377
368
|
if (definition)
|
|
378
369
|
msg.$location.address = { definition };
|
|
379
370
|
|
|
371
|
+
if (id) {
|
|
372
|
+
if (options.testMode && !options.$recompile)
|
|
373
|
+
_check$Consistency( id, moduleName, severity, texts, options )
|
|
374
|
+
msg.severity = reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable );
|
|
375
|
+
}
|
|
376
|
+
|
|
380
377
|
messages.push( msg );
|
|
381
378
|
hasNewError = hasNewError || msg.severity === 'Error' &&
|
|
382
379
|
!(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
|
|
@@ -406,7 +403,7 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
406
403
|
|
|
407
404
|
if (isShortSignature) {
|
|
408
405
|
if (texts)
|
|
409
|
-
throw new
|
|
406
|
+
throw new CompilerAssertion('No "texts" argument expected because text was already provided as third argument.');
|
|
410
407
|
} else {
|
|
411
408
|
if (textArguments !== undefined && typeof textArguments !== 'object')
|
|
412
409
|
_expectedType('textArguments', textArguments, 'object')
|
|
@@ -415,7 +412,7 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
415
412
|
}
|
|
416
413
|
|
|
417
414
|
function _expectedType(field, value, type) {
|
|
418
|
-
throw new
|
|
415
|
+
throw new CompilerAssertion(`Invalid argument type for ${ field }! Expected ${ type } but got ${ typeof value }. Do you use the old function signature?`);
|
|
419
416
|
}
|
|
420
417
|
}
|
|
421
418
|
|
|
@@ -470,9 +467,9 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
470
467
|
*/
|
|
471
468
|
function message(id, location, textArguments = null, texts = null) {
|
|
472
469
|
if (!id)
|
|
473
|
-
throw new
|
|
470
|
+
throw new CompilerAssertion('A message id is missing!');
|
|
474
471
|
if (!centralMessages[id])
|
|
475
|
-
throw new
|
|
472
|
+
throw new CompilerAssertion(`Message id '${ id }' is missing an entry in the central message register!`);
|
|
476
473
|
return _message(id, location, textArguments, null, texts);
|
|
477
474
|
}
|
|
478
475
|
|
|
@@ -524,12 +521,31 @@ function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNe
|
|
|
524
521
|
function throwWithAnyError() {
|
|
525
522
|
if (!messages || !messages.length)
|
|
526
523
|
return;
|
|
527
|
-
reclassifyMessagesForModule( messages, severities, moduleName ); // TODO: no, at the beginning of the module
|
|
528
524
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
529
525
|
if (hasError( messages, moduleName ))
|
|
530
526
|
throw new CompilationError( messages, options.attachValidNames && model );
|
|
531
527
|
}
|
|
532
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Reclassifies all messages according to the current module.
|
|
531
|
+
* This is required because if throwWithError() throws and the message's
|
|
532
|
+
* severities has `errorFor` set, then the message may still appear to be a warning.
|
|
533
|
+
*/
|
|
534
|
+
function reclassifyMessagesForModule() {
|
|
535
|
+
for (const msg of messages) {
|
|
536
|
+
if (msg.messageId && msg.severity !== 'Error') {
|
|
537
|
+
const severity = reclassifiedSeverity(msg, options, moduleName, deprecatedDowngradable);
|
|
538
|
+
if (severity !== msg.severity) {
|
|
539
|
+
msg.severity = severity;
|
|
540
|
+
// Re-set the module regardless of severity, since we reclassified it.
|
|
541
|
+
Object.defineProperty( msg, '$module', { value: moduleName, configurable: true } );
|
|
542
|
+
hasNewError = hasNewError || severity === 'Error' &&
|
|
543
|
+
!(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
533
549
|
/**
|
|
534
550
|
* Collects all messages during the call of the callback function instead of
|
|
535
551
|
* storing them in the model. Returns the collected messages.
|
|
@@ -565,7 +581,7 @@ function _check$Init( options ) {
|
|
|
565
581
|
|
|
566
582
|
function _check$Consistency( id, moduleName, severity, texts, options ) {
|
|
567
583
|
if (id.length > 30 && !centralMessages[id])
|
|
568
|
-
throw new
|
|
584
|
+
throw new CompilerAssertion( `The message ID "${id}" has more than 30 chars and must be listed centrally` );
|
|
569
585
|
if (!options.severities)
|
|
570
586
|
_check$Severities( id, moduleName || '?', severity );
|
|
571
587
|
for (const [variant, text] of
|
|
@@ -582,20 +598,19 @@ function _check$Severities( id, moduleName, severity ) {
|
|
|
582
598
|
if (!expected)
|
|
583
599
|
test$severities[id] = severity;
|
|
584
600
|
else if (expected !== severity)
|
|
585
|
-
throw new
|
|
601
|
+
throw new CompilerAssertion( `Expecting severity "${expected}" from previous call, not "${severity}" for message ID "${id}"` );
|
|
586
602
|
return;
|
|
587
603
|
}
|
|
588
604
|
// 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
|
|
605
|
+
if (!isDowngradable( id, moduleName )) { // always an error in module
|
|
591
606
|
if (severity !== 'Error')
|
|
592
|
-
throw new
|
|
607
|
+
throw new CompilerAssertion( `Expecting severity "Error", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
593
608
|
}
|
|
594
609
|
else if (spec.severity === 'Error') {
|
|
595
|
-
throw new
|
|
610
|
+
throw new CompilerAssertion( `Expecting the use of function message() when message ID "${id}" is a configurable error in module "${moduleName}"` );
|
|
596
611
|
}
|
|
597
612
|
else if (spec.severity !== severity) {
|
|
598
|
-
throw new
|
|
613
|
+
throw new CompilerAssertion( `Expecting severity "${spec.severity}", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
599
614
|
}
|
|
600
615
|
}
|
|
601
616
|
|
|
@@ -606,7 +621,7 @@ function _check$Texts( id, prop, value ) {
|
|
|
606
621
|
if (!expected)
|
|
607
622
|
test$texts[id][prop] = value;
|
|
608
623
|
else if (expected !== value)
|
|
609
|
-
throw new
|
|
624
|
+
throw new CompilerAssertion( `Expecting text "${expected}", not "${value}" for message ID "${id}" and text variant "${prop}"`);
|
|
610
625
|
}
|
|
611
626
|
|
|
612
627
|
const quote = { // could be an option in the future
|
|
@@ -649,6 +664,7 @@ const paramsTransform = {
|
|
|
649
664
|
expecting: transformManyWith( tokenSymbol ),
|
|
650
665
|
// msg: m => m,
|
|
651
666
|
$reviewed: ignoreTextTransform,
|
|
667
|
+
version: quote.meta,
|
|
652
668
|
};
|
|
653
669
|
|
|
654
670
|
function ignoreTextTransform() {
|
|
@@ -813,7 +829,7 @@ function messageString( err, normalizeFilename, noMessageId, noHome ) {
|
|
|
813
829
|
return (err.$location && err.$location.file
|
|
814
830
|
? locationString( err.$location, normalizeFilename ) + ': '
|
|
815
831
|
: '') +
|
|
816
|
-
(err.severity||'Error') +
|
|
832
|
+
(err.severity || 'Error') +
|
|
817
833
|
// TODO: use [message-id]
|
|
818
834
|
(err.messageId && !noMessageId ? ' ' + err.messageId + ': ' : ': ') +
|
|
819
835
|
err.message +
|
|
@@ -1395,6 +1411,7 @@ function _quoted( name ) {
|
|
|
1395
1411
|
|
|
1396
1412
|
/**
|
|
1397
1413
|
* Get the explanation string for the given message-id.
|
|
1414
|
+
* Ensure to have called hasMessageExplanation() before.
|
|
1398
1415
|
*
|
|
1399
1416
|
* @param {string} messageId
|
|
1400
1417
|
* @returns {string}
|
|
@@ -1402,18 +1419,22 @@ function _quoted( name ) {
|
|
|
1402
1419
|
* @see hasMessageExplanation()
|
|
1403
1420
|
*/
|
|
1404
1421
|
function explainMessage(messageId) {
|
|
1422
|
+
messageId = oldMessageIds[messageId] || messageId;
|
|
1405
1423
|
const filename = path.join(__dirname, '..', '..', 'share', 'messages', `${messageId}.md`);
|
|
1406
1424
|
return fs.readFileSync(filename, 'utf8');
|
|
1407
1425
|
}
|
|
1408
1426
|
|
|
1409
1427
|
/**
|
|
1410
1428
|
* Returns true if the given message has an explanation file.
|
|
1429
|
+
* Takes into account changed message ids, i.e. looks up if the new
|
|
1430
|
+
* message id has an explanation.
|
|
1411
1431
|
*
|
|
1412
1432
|
* @param {string} messageId
|
|
1413
1433
|
* @returns {boolean}
|
|
1414
1434
|
*/
|
|
1415
1435
|
function hasMessageExplanation(messageId) {
|
|
1416
|
-
|
|
1436
|
+
const id = oldMessageIds[messageId] || messageId || false;
|
|
1437
|
+
return id && _messageIdsWithExplanation.includes(id);
|
|
1417
1438
|
}
|
|
1418
1439
|
|
|
1419
1440
|
/**
|
|
@@ -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
|
*/
|