@sap/cds-compiler 4.9.6 → 5.1.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 +92 -0
- package/bin/cds_remove_invalid_whitespace.js +2 -1
- package/bin/cdsc.js +49 -19
- package/bin/cdshi.js +3 -1
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +16 -19
- package/lib/api/options.js +5 -14
- package/lib/api/trace.js +0 -1
- package/lib/base/builtins.js +1 -0
- package/lib/base/location.js +4 -1
- package/lib/base/message-registry.js +43 -29
- package/lib/base/messages.js +23 -26
- package/lib/base/meta.js +10 -0
- package/lib/base/model.js +0 -2
- package/lib/base/node-helpers.js +0 -1
- package/lib/base/optionProcessorHelper.js +11 -0
- package/lib/checks/dbFeatureFlags.js +5 -0
- package/lib/checks/enricher.js +1 -5
- package/lib/checks/structuredAnnoExpressions.js +30 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -1
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +18 -2
- package/lib/compiler/checks.js +2 -5
- package/lib/compiler/define.js +8 -8
- package/lib/compiler/extend.js +108 -37
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/index.js +27 -10
- package/lib/compiler/lsp-api.js +501 -2
- package/lib/compiler/populate.js +60 -13
- package/lib/compiler/propagator.js +10 -8
- package/lib/compiler/resolve.js +117 -94
- package/lib/compiler/shared.js +114 -32
- package/lib/compiler/tweak-assocs.js +31 -21
- package/lib/compiler/utils.js +2 -1
- package/lib/compiler/xsn-model.js +4 -0
- package/lib/edm/annotations/genericTranslation.js +69 -35
- package/lib/edm/csn2edm.js +16 -4
- package/lib/edm/edm.js +10 -3
- package/lib/edm/edmAnnoPreprocessor.js +1 -2
- package/lib/edm/edmPreprocessor.js +8 -10
- package/lib/gen/Dictionary.json +66 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4995 -4817
- package/lib/json/csnVersion.js +1 -1
- package/lib/json/from-csn.js +4 -7
- package/lib/json/to-csn.js +25 -12
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/errorStrategy.js +0 -1
- package/lib/language/genericAntlrParser.js +35 -12
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/language/textUtils.js +1 -0
- package/lib/main.d.ts +28 -9
- package/lib/main.js +9 -9
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +22 -5
- package/lib/model/csnUtils.js +0 -14
- package/lib/model/revealInternalProperties.js +1 -2
- package/lib/modelCompare/compare.js +13 -11
- package/lib/optionProcessor.js +30 -9
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +44 -14
- package/lib/render/toHdbcds.js +1 -2
- package/lib/render/toSql.js +45 -8
- package/lib/render/utils/common.js +12 -9
- package/lib/render/utils/stringEscapes.js +1 -0
- package/lib/transform/db/applyTransformations.js +13 -8
- package/lib/transform/db/associations.js +62 -54
- package/lib/transform/db/backlinks.js +20 -5
- package/lib/transform/db/expansion.js +1 -6
- package/lib/transform/db/flattening.js +86 -109
- package/lib/transform/db/killAnnotations.js +3 -0
- package/lib/transform/db/processSqlServices.js +63 -0
- package/lib/transform/db/temporal.js +3 -4
- package/lib/transform/db/views.js +0 -1
- package/lib/transform/draft/odata.js +56 -3
- package/lib/transform/effective/annotations.js +3 -2
- package/lib/transform/effective/flattening.js +135 -0
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/effective/types.js +13 -9
- package/lib/transform/forOdata.js +0 -2
- package/lib/transform/forRelationalDB.js +9 -19
- package/lib/transform/localized.js +7 -8
- package/lib/transform/odata/flattening.js +39 -31
- package/lib/transform/odata/typesExposure.js +5 -17
- package/lib/transform/transformUtils.js +1 -1
- package/lib/transform/translateAssocsToJoins.js +0 -1
- package/lib/utils/file.js +87 -8
- package/lib/utils/moduleResolve.js +59 -8
- package/lib/utils/term.js +3 -2
- package/package.json +7 -3
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/type-unexpected-foreign-keys.md +52 -0
- package/share/messages/type-unexpected-on-condition.md +52 -0
|
@@ -41,7 +41,6 @@ const { createDict } = require('../utils/objectUtils');
|
|
|
41
41
|
* - true = can always be downgraded, we do not really care
|
|
42
42
|
* - [‹module›, …] = can be downgraded in compiler function ‹module›
|
|
43
43
|
* - 'deprecated' = severity can only be changed with deprecated.downgradableErrors
|
|
44
|
-
* - 'v4' = currently like true, but should be 'deprecated' in v5
|
|
45
44
|
*
|
|
46
45
|
* @type {Object<string, MessageConfig>}
|
|
47
46
|
*/
|
|
@@ -61,13 +60,13 @@ const centralMessages = {
|
|
|
61
60
|
'ext-undefined-art': { severity: 'Warning' }, // for annotate statement (for CDL path root)
|
|
62
61
|
'ext-undefined-def': { severity: 'Warning' }, // for annotate statement (for CSN or CDL path cont)
|
|
63
62
|
'ext-undefined-element': { severity: 'Warning' },
|
|
63
|
+
'ext-undefined-key': { severity: 'Warning' },
|
|
64
64
|
'ext-undefined-param': { severity: 'Warning' },
|
|
65
65
|
'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
|
|
66
66
|
'anno-unexpected-localized-skip': { severity: 'Error', configurableFor: true },
|
|
67
|
-
'anno-unexpected-mixin': { severity: 'Warning', errorFor: [ 'v5' ] },
|
|
68
67
|
|
|
69
68
|
'name-invalid-dollar-alias': { severity: 'Error', configurableFor: true },
|
|
70
|
-
'name-deprecated-$self': { severity: '
|
|
69
|
+
'name-deprecated-$self': { severity: 'Error', configurableFor: true },
|
|
71
70
|
|
|
72
71
|
'type-invalid-items': { severity: 'Error' }, // not supported yet
|
|
73
72
|
'assoc-as-type': { severity: 'Error' }, // TODO: allow more, but not all
|
|
@@ -86,7 +85,7 @@ const centralMessages = {
|
|
|
86
85
|
'empty-type': { severity: 'Info' }, // only still an error in old transformers
|
|
87
86
|
|
|
88
87
|
'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
|
|
89
|
-
'ref-deprecated-self-element': { severity: '
|
|
88
|
+
'ref-deprecated-self-element': { severity: 'Error', configurableFor: true },
|
|
90
89
|
'ref-invalid-type': { severity: 'Error' },
|
|
91
90
|
'ref-unexpected-self': { severity: 'Error' },
|
|
92
91
|
'ref-invalid-include': { severity: 'Error' },
|
|
@@ -98,6 +97,7 @@ const centralMessages = {
|
|
|
98
97
|
'ref-expecting-foreign-key': { severity: 'Error' },
|
|
99
98
|
'ref-invalid-source': { severity: 'Error' },
|
|
100
99
|
'ref-invalid-target': { severity: 'Error' },
|
|
100
|
+
'ref-missing-self-counterpart': { severity: 'Error', configurableFor: true },
|
|
101
101
|
'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
|
|
102
102
|
|
|
103
103
|
'extend-repeated-intralayer': { severity: 'Warning' },
|
|
@@ -152,7 +152,7 @@ const centralMessages = {
|
|
|
152
152
|
// 'syntax-duplicate-annotate' came late with v3 - make it configurable as
|
|
153
153
|
// fallback, but then parse.cdl is not supposed to work correctly (it can
|
|
154
154
|
// then either issue an error or produce a CSN missing some annotations):
|
|
155
|
-
'syntax-duplicate-annotate': { severity: 'Error'
|
|
155
|
+
'syntax-duplicate-annotate': { severity: 'Error' },
|
|
156
156
|
'syntax-duplicate-clause': { severity: 'Error', configurableFor: true },
|
|
157
157
|
'syntax-duplicate-equal-clause': { severity: 'Warning' },
|
|
158
158
|
'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
|
|
@@ -162,36 +162,38 @@ const centralMessages = {
|
|
|
162
162
|
'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
|
|
163
163
|
'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'deprecated' },
|
|
164
164
|
'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
|
|
165
|
-
'syntax-
|
|
165
|
+
'syntax-invalid-space': { severity: 'Error', configurableFor: 'test' },
|
|
166
|
+
'syntax-expecting-space': { severity: 'Error' },
|
|
167
|
+
'syntax-unexpected-anno': { severity: 'Error' },
|
|
166
168
|
|
|
167
169
|
'type-unsupported-precision-change': { severity: 'Error' },
|
|
168
170
|
'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
|
|
169
|
-
'type-missing-enum-value': { severity: '
|
|
171
|
+
'type-missing-enum-value': { severity: 'Error', configurableFor: 'test' },
|
|
170
172
|
|
|
171
173
|
'def-missing-element': { severity: 'Error' },
|
|
172
|
-
'def-expected-structured': { severity: '
|
|
174
|
+
'def-expected-structured': { severity: 'Error', configurableFor: true },
|
|
173
175
|
'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
|
|
174
176
|
|
|
175
177
|
'def-invalid-key-cardinality': { severity: 'Error' },
|
|
176
178
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
177
|
-
'odata-spec-violation-array': { severity: 'Warning'
|
|
179
|
+
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
178
180
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
179
|
-
'odata-spec-violation-assoc': { severity: 'Warning'
|
|
181
|
+
'odata-spec-violation-assoc': { severity: 'Warning' }, // more than 30 chars
|
|
180
182
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
181
183
|
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
182
184
|
'odata-spec-violation-id': { severity: 'Error', configurableFor: true },
|
|
183
|
-
'odata-spec-violation-namespace': { severity: 'Warning'
|
|
185
|
+
'odata-spec-violation-namespace': { severity: 'Warning' }, // more than 30 chars
|
|
184
186
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
185
|
-
'odata-spec-violation-param': { severity: 'Warning'
|
|
187
|
+
'odata-spec-violation-param': { severity: 'Warning' }, // more than 30 chars
|
|
186
188
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
187
|
-
'odata-spec-violation-returns': { severity: 'Warning'
|
|
189
|
+
'odata-spec-violation-returns': { severity: 'Warning' }, // more than 30 chars
|
|
188
190
|
'odata-spec-violation-type': { severity: 'Error', configurableFor: true },
|
|
189
|
-
'odata-spec-violation-type-unknown': { severity: '
|
|
190
|
-
'odata-spec-violation-no-key': { severity: '
|
|
191
|
+
'odata-spec-violation-type-unknown': { severity: 'Error', configurableFor: true },
|
|
192
|
+
'odata-spec-violation-no-key': { severity: 'Error', configurableFor: true },
|
|
191
193
|
'odata-spec-violation-key-array': { severity: 'Error', configurableFor: true }, // more than 30 chars
|
|
192
194
|
'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
|
|
193
|
-
'odata-spec-violation-key-type': { severity: 'Warning'
|
|
194
|
-
'odata-spec-violation-property-name': { severity: 'Warning'
|
|
195
|
+
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
196
|
+
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
195
197
|
'odata-anno-preproc': { severity: 'Warning' },
|
|
196
198
|
'odata-anno-dict': { severity: 'Warning' },
|
|
197
199
|
'odata-anno-vocref': { severity: 'Warning' },
|
|
@@ -290,7 +292,6 @@ const centralMessageTexts = {
|
|
|
290
292
|
'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)',
|
|
291
293
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
292
294
|
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
293
|
-
'anno-unexpected-mixin': 'Unexpected annotation on mixin definition',
|
|
294
295
|
|
|
295
296
|
'anno-unexpected-localized-skip': {
|
|
296
297
|
std: 'Compiler generated entity $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
|
|
@@ -351,7 +352,7 @@ const centralMessageTexts = {
|
|
|
351
352
|
'syntax-ignoring-decimal': {
|
|
352
353
|
std: 'Ignoring decimal places, because an integer was expected'
|
|
353
354
|
},
|
|
354
|
-
'syntax-
|
|
355
|
+
'syntax-unexpected-anno': {
|
|
355
356
|
std: 'Annotations can\'t be used in a column with $(CODE)',
|
|
356
357
|
doc: 'Doc comments can\'t be used in a column with $(CODE)',
|
|
357
358
|
},
|
|
@@ -435,7 +436,6 @@ const centralMessageTexts = {
|
|
|
435
436
|
service: 'Annotations can\'t be defined inside services',
|
|
436
437
|
context: 'Annotations can\'t be defined inside contexts',
|
|
437
438
|
},
|
|
438
|
-
// 'syntax-unexpected-space' (TODO: also use for :param, #variant, @(…) and @Begin),
|
|
439
439
|
// 'syntax-unexpected-alias' (is 'syntax-unexpected-property' in CSN)
|
|
440
440
|
'syntax-unsupported-param': {
|
|
441
441
|
std: 'Parameter not supported', // unused
|
|
@@ -557,6 +557,10 @@ const centralMessageTexts = {
|
|
|
557
557
|
// Messages for erroneous references -----------------------------------------
|
|
558
558
|
// location at erroneous reference (if possible)
|
|
559
559
|
'ref-deprecated-orderby': 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected',
|
|
560
|
+
'ref-missing-self-counterpart' : {
|
|
561
|
+
std: 'Expected to find a matching element in $self-comparison for foreign key $(PROP) of association $(NAME)',
|
|
562
|
+
unmanaged: 'Expected to find a matching element in $self-comparison for $(PROP) of association $(NAME)'
|
|
563
|
+
},
|
|
560
564
|
'ref-unexpected-self': {
|
|
561
565
|
std: 'Unexpected $(ID) reference; is valid only in ON-conditions of unmanaged associations',
|
|
562
566
|
on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
|
|
@@ -642,6 +646,8 @@ const centralMessageTexts = {
|
|
|
642
646
|
std: 'Unexpected reference to association $(NAME)', // "std" currently unused
|
|
643
647
|
unmanaged: 'Unexpected reference to an unmanaged association',
|
|
644
648
|
'self-unmanaged': 'Unexpected column reference starting with $(ALIAS) to an unmanaged association',
|
|
649
|
+
'with-filter': 'Unexpected reference to an association with filter',
|
|
650
|
+
'self-with-filter': 'Unexpected column reference starting with $(ALIAS) to an association with filter',
|
|
645
651
|
self: 'A reference to an unmanaged association is only valid when compared via $(CODE)',
|
|
646
652
|
expr: 'Associations can\'t be used as values in expressions',
|
|
647
653
|
'expr-comp': 'Compositions can\'t be used as values in expressions',
|
|
@@ -683,6 +689,10 @@ const centralMessageTexts = {
|
|
|
683
689
|
hdbcds:'Type $(TYPE) is not supported in HDBCDS',
|
|
684
690
|
odata: 'Type $(TYPE) is not supported for OData'
|
|
685
691
|
},
|
|
692
|
+
'ref-unexpected-var': {
|
|
693
|
+
std: 'Variable $(NAME) can\'t be used here',
|
|
694
|
+
'annotation': 'Variable $(NAME) can only be used in annotation values',
|
|
695
|
+
},
|
|
686
696
|
|
|
687
697
|
// TODO: Better text ?
|
|
688
698
|
'rewrite-not-supported': 'The ON-condition is not rewritten here - provide an explicit ON-condition',
|
|
@@ -746,6 +756,7 @@ const centralMessageTexts = {
|
|
|
746
756
|
returns: 'Return value of $(ART) has no element $(NAME)',
|
|
747
757
|
'enum-returns': 'Return value of $(ART) has no enum $(NAME)',
|
|
748
758
|
},
|
|
759
|
+
'ext-undefined-key': 'Foreign key $(NAME) has not been found',
|
|
749
760
|
'ext-undefined-action': {
|
|
750
761
|
std: 'Action $(ART) has not been found',
|
|
751
762
|
action: 'Artifact $(ART) has no action $(NAME)'
|
|
@@ -837,12 +848,6 @@ const centralMessageTexts = {
|
|
|
837
848
|
'include-elements': 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
838
849
|
'include-actions': 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
839
850
|
},
|
|
840
|
-
// TODO: Remove in v5, use duplicate-definition (or another ID?)
|
|
841
|
-
'ref-duplicate-include-member': {
|
|
842
|
-
std: 'Duplicate member $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
843
|
-
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
844
|
-
actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
845
|
-
},
|
|
846
851
|
'ref-invalid-element': {
|
|
847
852
|
std: 'Invalid element reference',
|
|
848
853
|
$tableAlias: 'Can\'t refer to source elements of table alias $(ID)',
|
|
@@ -859,6 +864,7 @@ const centralMessageTexts = {
|
|
|
859
864
|
'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
|
|
860
865
|
},
|
|
861
866
|
|
|
867
|
+
'ref-expecting-$self': 'Use $(NEWCODE) instead of $(CODE) here or remove $(CODE) altogether if possible; the compiler has rewritten it to $(NEWCODE) in CSN',
|
|
862
868
|
'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
|
|
863
869
|
'ref-expecting-const': 'A constant expression or variable is expected here',
|
|
864
870
|
'ref-expecting-foreign-key': 'Expecting foreign key access after managed association $(NAME) in filter expression of $(ID), but found $(ALIAS)',
|
|
@@ -908,9 +914,7 @@ const centralMessageTexts = {
|
|
|
908
914
|
'indirect': 'Type property $(PROP) can only be extended if directly provided at the definition',
|
|
909
915
|
'new-prop': 'Type property $(PROP) can only be extended, not added',
|
|
910
916
|
string: 'Only numerical properties can be extended, but found string for $(PROP)',
|
|
911
|
-
// eslint-disable-next-line max-len
|
|
912
917
|
number: 'Value of type property $(PROP) must be $(NUMBER) or higher, it can\'t be smaller than originally provided',
|
|
913
|
-
// eslint-disable-next-line max-len
|
|
914
918
|
scale: 'With the extension for type property $(OTHERPROP), the value of $(PROP) must be $(NUMBER) or higher',
|
|
915
919
|
},
|
|
916
920
|
'ext-missing-type-property': 'Type extension with property $(PROP) must also have property $(OTHERPROP) because $(ART) has both',
|
|
@@ -966,6 +970,15 @@ const centralMessageTexts = {
|
|
|
966
970
|
'managed': 'Ignoring managed association $(NAME) that is published in a UNION',
|
|
967
971
|
'std': 'Ignoring association $(NAME) that is published in a UNION'
|
|
968
972
|
},
|
|
973
|
+
'query-missing-element': {
|
|
974
|
+
std: 'Element $(ID) is missing in specified elements',
|
|
975
|
+
enum: 'Enum $(ID) is missing in specified enum values',
|
|
976
|
+
foreignKeys: 'Foreign key $(ID) is missing in specified foreign keys',
|
|
977
|
+
},
|
|
978
|
+
'query-unspecified-element': {
|
|
979
|
+
std: 'Element $(ID) does not result from the query',
|
|
980
|
+
foreignKeys: 'Foreign key $(ID) does not result from the query',
|
|
981
|
+
},
|
|
969
982
|
|
|
970
983
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
971
984
|
|
|
@@ -1193,7 +1206,8 @@ const centralMessageTexts = {
|
|
|
1193
1206
|
'notaneelement': 'EDM Parameter path $(ELEMREF) can\'t be used in $(ANNO) which is applied to a type entity',
|
|
1194
1207
|
'notrendered': 'EDM Path step $(COUNT) of $(ELEMREF) in $(ANNO) refers to an unrendered property in the OData API',
|
|
1195
1208
|
'magic': 'Unexpected magic variable $(ELEMREF) in $(ANNO)',
|
|
1196
|
-
'
|
|
1209
|
+
'bparam_v2_expl': 'Unexpected explicit binding parameter path $(ELEMREF) for OData $(VERSION) in $(ANNO)',
|
|
1210
|
+
'bparam_v2_impl': 'Unexpected implicit binding parameter path $(ELEMREF) for OData $(VERSION) in $(ANNO)',
|
|
1197
1211
|
},
|
|
1198
1212
|
// -----------------------------------------------------------------------------------
|
|
1199
1213
|
// OData Message section ends here, no messages below this line
|
package/lib/base/messages.js
CHANGED
|
@@ -13,6 +13,7 @@ const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
|
|
|
13
13
|
const { CompilerAssertion } = require('./error');
|
|
14
14
|
const { getArtifactName } = require('../compiler/base');
|
|
15
15
|
const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
16
|
+
const meta = require('./meta');
|
|
16
17
|
|
|
17
18
|
const fs = require('fs');
|
|
18
19
|
const path = require('path');
|
|
@@ -73,8 +74,8 @@ function isDowngradable( messageId, moduleName, options ) {
|
|
|
73
74
|
return false;
|
|
74
75
|
if (msg.severity !== 'Error')
|
|
75
76
|
return true;
|
|
76
|
-
//
|
|
77
|
-
if (msg.errorFor && msg.errorFor.includes('
|
|
77
|
+
// v6 messages are downgradable (except if errorFor also contains the current module).
|
|
78
|
+
if (msg.errorFor && msg.errorFor.includes('v6'))
|
|
78
79
|
return true;
|
|
79
80
|
const { configurableFor } = msg;
|
|
80
81
|
return (Array.isArray( configurableFor ))
|
|
@@ -91,9 +92,11 @@ function isDowngradable( messageId, moduleName, options ) {
|
|
|
91
92
|
function severityChangeMarker(msg, config) {
|
|
92
93
|
const severity = msg.severity || 'Error';
|
|
93
94
|
if (config.moduleForMarker) {
|
|
94
|
-
if (severity === 'Error' &&
|
|
95
|
+
if (severity === 'Error' &&
|
|
96
|
+
isDowngradable( msg.messageId, config.moduleForMarker,
|
|
97
|
+
{ deprecated: { downgradableErrors: true } }))
|
|
95
98
|
return '‹↓›';
|
|
96
|
-
if (msg.messageId && centralMessages[msg.messageId]?.errorFor?.includes('
|
|
99
|
+
if (msg.messageId && centralMessages[msg.messageId]?.errorFor?.includes('v6'))
|
|
97
100
|
return '‹↑›';
|
|
98
101
|
}
|
|
99
102
|
return '';
|
|
@@ -107,12 +110,16 @@ function severityChangeMarker(msg, config) {
|
|
|
107
110
|
class CompilationError extends Error {
|
|
108
111
|
/**
|
|
109
112
|
* @param {CompileMessage[]} messages
|
|
110
|
-
* @param {boolean} [dontSerializeMessages] If true, compiler messages are NOT part of the errors message text.
|
|
111
113
|
* @param {XSN.Model} [model] the XSN model, only to be set with options.attachValidNames
|
|
112
114
|
*/
|
|
113
|
-
constructor(messages, model
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
constructor(messages, model) {
|
|
116
|
+
// Because test frameworks such as mocha and jest to not call `toString()` on
|
|
117
|
+
// an unhandled CompilationError and instead use `e.stack` directly, there is
|
|
118
|
+
// no proper message about _what_ the root cause of the exception was.
|
|
119
|
+
// To mitigate that, we serialize the first error in the message as well.
|
|
120
|
+
const firstError = messages.find( m => m.severity === 'Error' )?.toString() || '';
|
|
121
|
+
super( `CDS compilation failed (@sap/cds-compiler v${ meta.version() })\n${firstError}` );
|
|
122
|
+
|
|
116
123
|
/** @since v4.0.0 */
|
|
117
124
|
this.code = 'ERR_CDS_COMPILATION_FAILURE';
|
|
118
125
|
this.messages = [ ...messages ].sort(compareMessageSeverityAware);
|
|
@@ -240,7 +247,7 @@ function reclassifiedSeverity( msg, options, moduleName ) {
|
|
|
240
247
|
if (errorFor.includes(moduleName))
|
|
241
248
|
return 'Error';
|
|
242
249
|
|
|
243
|
-
if (errorFor.includes('
|
|
250
|
+
if (errorFor.includes('v6') && isBetaEnabled(options, 'v6preview')) {
|
|
244
251
|
severity = 'Error';
|
|
245
252
|
if (!isDowngradable(msg.messageId, moduleName, options))
|
|
246
253
|
return severity;
|
|
@@ -555,8 +562,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
555
562
|
|
|
556
563
|
function throwWithError() {
|
|
557
564
|
if (hasNewError) {
|
|
558
|
-
|
|
559
|
-
throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
|
|
565
|
+
throw new CompilationError(messages, options.attachValidNames && model);
|
|
560
566
|
}
|
|
561
567
|
}
|
|
562
568
|
|
|
@@ -573,8 +579,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
573
579
|
return;
|
|
574
580
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
575
581
|
if (hasError( messages, moduleName, options )) {
|
|
576
|
-
|
|
577
|
-
throw new CompilationError(messages, options.attachValidNames && model, dontSerializeMessages);
|
|
582
|
+
throw new CompilationError(messages, options.attachValidNames && model);
|
|
578
583
|
}
|
|
579
584
|
}
|
|
580
585
|
|
|
@@ -1018,10 +1023,6 @@ function replaceInString( text, params ) {
|
|
|
1018
1023
|
* @param {boolean} [config.noMessageId]
|
|
1019
1024
|
* If true, will _not_ show the message ID (+ explanation hint) in the output.
|
|
1020
1025
|
*
|
|
1021
|
-
* @param {boolean} [config.idInBrackets]
|
|
1022
|
-
* If true, the message ID (if there is one and noMessageId is falsey) will be put in brackets.
|
|
1023
|
-
* This will be the default in cds-compiler v5.
|
|
1024
|
-
*
|
|
1025
1026
|
* @param {boolean} [config.noHome]
|
|
1026
1027
|
* If true, will _not_ show message's semantic location.
|
|
1027
1028
|
*
|
|
@@ -1049,11 +1050,7 @@ function messageString( err, config ) {
|
|
|
1049
1050
|
const downgradable = severityChangeMarker(err, config);
|
|
1050
1051
|
// even with noHome, print err.home if the location is weak
|
|
1051
1052
|
const home = !err.home || config.noHome && err.$location?.endLine ? '' : ` (in ${ err.home })`;
|
|
1052
|
-
|
|
1053
|
-
let msgId = ''; // TODO(v5): Use brackets only
|
|
1054
|
-
if (err.messageId && !config.noMessageId)
|
|
1055
|
-
msgId = (config.idInBrackets) ? `[${err.messageId}]` : ` ${err.messageId}`;
|
|
1056
|
-
|
|
1053
|
+
const msgId = (err.messageId && !config.noMessageId) ? `[${err.messageId}]` : '';
|
|
1057
1054
|
return `${ location }${ severity }${ downgradable }${ msgId }: ${ err.message }${ home }`;
|
|
1058
1055
|
}
|
|
1059
1056
|
|
|
@@ -1065,7 +1062,7 @@ function messageString( err, config ) {
|
|
|
1065
1062
|
* @returns {string} can be used to uniquely identify a message
|
|
1066
1063
|
*/
|
|
1067
1064
|
function messageHash( msg ) {
|
|
1068
|
-
// parser messages do not provide semantic location, therefore we need to use the file location
|
|
1065
|
+
// parser messages do not provide semantic location, therefore$ we need to use the file location
|
|
1069
1066
|
if (!msg.home)
|
|
1070
1067
|
return messageString(msg);
|
|
1071
1068
|
const copy = { ...msg };
|
|
@@ -1118,7 +1115,7 @@ function messageHash( msg ) {
|
|
|
1118
1115
|
* If true/'always', ANSI escape codes will be used for coloring the severity. If false/'never',
|
|
1119
1116
|
* no coloring will be used. If 'auto', we will decide based on certain factors such
|
|
1120
1117
|
* as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
|
|
1121
|
-
* unset.
|
|
1118
|
+
* unset or whether `FORCE_COLOR` is set.
|
|
1122
1119
|
*
|
|
1123
1120
|
* @returns {string}
|
|
1124
1121
|
*/
|
|
@@ -1285,7 +1282,7 @@ function _messageContext( err, config ) {
|
|
|
1285
1282
|
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
|
|
1286
1283
|
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
1287
1284
|
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
1288
|
-
* unset.
|
|
1285
|
+
* unset or `FORCE_COLOR` is set.
|
|
1289
1286
|
* @returns {string}
|
|
1290
1287
|
*
|
|
1291
1288
|
* @deprecated Use `messageStringMultiline()` with `config.sourceMap` and `config.sourceLineMap` instead!
|
|
@@ -1353,7 +1350,7 @@ function compareMessageSeverityAware( a, b ) {
|
|
|
1353
1350
|
/**
|
|
1354
1351
|
* Return sort-relevant part of semantic location (after the ':').
|
|
1355
1352
|
* Messages without semantic locations are considered smaller (for syntax errors)
|
|
1356
|
-
* and (currently - should not happen in
|
|
1353
|
+
* and (currently - should not happen in v6) larger for other messages.
|
|
1357
1354
|
*
|
|
1358
1355
|
* @param {CompileMessage} msg
|
|
1359
1356
|
*/
|
package/lib/base/meta.js
ADDED
package/lib/base/model.js
CHANGED
|
@@ -31,11 +31,9 @@ const availableBetaFlags = {
|
|
|
31
31
|
enableUniversalCsn: true,
|
|
32
32
|
odataTerms: true,
|
|
33
33
|
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
34
|
-
annotateForeignKeys: true,
|
|
35
34
|
effectiveCsn: true,
|
|
36
35
|
tenantVariable: true,
|
|
37
36
|
calcAssoc: true,
|
|
38
|
-
v5preview: true,
|
|
39
37
|
temporalRawProjection: true,
|
|
40
38
|
// disabled by --beta-mode
|
|
41
39
|
nestedServices: false,
|
package/lib/base/node-helpers.js
CHANGED
|
@@ -42,6 +42,7 @@ function createOptionProcessor() {
|
|
|
42
42
|
},
|
|
43
43
|
help,
|
|
44
44
|
processCmdLine,
|
|
45
|
+
makePositionalArgumentsOptional,
|
|
45
46
|
};
|
|
46
47
|
return optionProcessor;
|
|
47
48
|
|
|
@@ -291,6 +292,16 @@ function createOptionProcessor() {
|
|
|
291
292
|
};
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
function makePositionalArgumentsOptional() {
|
|
296
|
+
for (const arg of optionProcessor.positionalArguments || [])
|
|
297
|
+
arg.required = false;
|
|
298
|
+
|
|
299
|
+
for (const cmd in optionProcessor.commands) {
|
|
300
|
+
for (const arg of optionProcessor.commands[cmd].positionalArguments || [])
|
|
301
|
+
arg.required = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
294
305
|
// API: Let the option processor digest a command line 'argv'
|
|
295
306
|
// The expectation is to get a commandline like this:
|
|
296
307
|
// $ node cdsc.js -x 1 --foo toXyz -y --bar-wiz bla arg1 arg2
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp } = require('../base/model');
|
|
4
4
|
const { featureFlags } = require('../transform/db/featureFlags');
|
|
5
|
+
const { isSqlService } = require('../transform/db/processSqlServices');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
*
|
|
@@ -25,4 +26,8 @@ module.exports = {
|
|
|
25
26
|
value: setFeatureFlag('$calculatedElements'),
|
|
26
27
|
expand: setFeatureFlag('$expandInline'),
|
|
27
28
|
inline: setFeatureFlag('$expandInline'),
|
|
29
|
+
kind: function setFeatureFlagForSqlService( artifact ) {
|
|
30
|
+
if (isSqlService(artifact))
|
|
31
|
+
setFeatureFlag( '$sqlService' ).call(this);
|
|
32
|
+
},
|
|
28
33
|
};
|
package/lib/checks/enricher.js
CHANGED
|
@@ -49,7 +49,6 @@ function enrichCsn( csn, options ) {
|
|
|
49
49
|
dictionary( csn, 'definitions', csn.definitions );
|
|
50
50
|
return { csn, cleanup };
|
|
51
51
|
|
|
52
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
53
52
|
function standard( parent, prop, node ) {
|
|
54
53
|
if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ))
|
|
55
54
|
return;
|
|
@@ -69,7 +68,7 @@ function enrichCsn( csn, options ) {
|
|
|
69
68
|
}
|
|
70
69
|
csnPath.pop();
|
|
71
70
|
}
|
|
72
|
-
|
|
71
|
+
|
|
73
72
|
function dictionary( node, prop, dict ) {
|
|
74
73
|
setProp(node, '$path', [ ...csnPath ]);
|
|
75
74
|
cleanupCallbacks.push(() => delete node.$path);
|
|
@@ -115,7 +114,6 @@ function enrichCsn( csn, options ) {
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
119
117
|
function columns( parent, prop, node ) {
|
|
120
118
|
// Establish the link relationships
|
|
121
119
|
parent[prop].forEach((column) => {
|
|
@@ -130,7 +128,6 @@ function enrichCsn( csn, options ) {
|
|
|
130
128
|
standard(parent, prop, node);
|
|
131
129
|
}
|
|
132
130
|
|
|
133
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
134
131
|
function simpleRef( node, prop, ref ) {
|
|
135
132
|
setProp(node, '$path', [ ...csnPath ]);
|
|
136
133
|
cleanupCallbacks.push(() => delete node.$path);
|
|
@@ -158,7 +155,6 @@ function enrichCsn( csn, options ) {
|
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
|
|
161
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
162
158
|
function pathRef( node, prop, path ) {
|
|
163
159
|
const {
|
|
164
160
|
links, art, scope, $env,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isBuiltinType } = require('../base/builtins');
|
|
4
|
+
const { transformExpression, applyTransformationsOnNonDictionary } = require('../model/csnUtils');
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {object} member
|
|
8
|
+
*/
|
|
9
|
+
function checkAnnotationExpression( member, _memberName, _prop, path ) {
|
|
10
|
+
Object.keys(member).filter(pn => pn[0] === '@').forEach((anno) => {
|
|
11
|
+
applyTransformationsOnNonDictionary(member, anno, {
|
|
12
|
+
xpr: (parent, prop, _xpr, xprPath) => {
|
|
13
|
+
transformExpression(parent, prop, {
|
|
14
|
+
ref: (elemref, __prop, ref, refPath) => {
|
|
15
|
+
const { art, scope } = this.csnUtils.inspectRef(refPath);
|
|
16
|
+
if (scope !== '$magic' && art) {
|
|
17
|
+
const ft = this.csnUtils.getFinalTypeInfo(art.type);
|
|
18
|
+
if (!isBuiltinType(ft?.type))
|
|
19
|
+
this.error('odata-anno-xpr-ref', refPath, { anno, elemref, '#': 'flatten_builtin_type' });
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
}, xprPath);
|
|
23
|
+
},
|
|
24
|
+
}, {}, path);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
checkAnnotationExpression,
|
|
30
|
+
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -15,6 +15,7 @@ const navigationIntoMany = require('./manyNavigations');
|
|
|
15
15
|
const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
|
|
16
16
|
const validateHasPersistedElements = require('./hasPersistedElements');
|
|
17
17
|
const checkForHanaTypes = require('./checkForTypes');
|
|
18
|
+
const { checkAnnotationExpression } = require('./structuredAnnoExpressions');
|
|
18
19
|
const checkForParams = require('./parameters');
|
|
19
20
|
// forOdata
|
|
20
21
|
const { validateDefaultValues } = require('./defaultValues');
|
|
@@ -210,6 +211,13 @@ function forRelationalDB( csn, that ) {
|
|
|
210
211
|
duplicate messages due to the forEachMemberRecursively.
|
|
211
212
|
TODO: check if this recursion can be factored out of the validator */
|
|
212
213
|
forEachMember(artifact, checkUsedTypesForAnonymousAspectComposition.bind(that));
|
|
214
|
+
},
|
|
215
|
+
(artifact, artifactName) => {
|
|
216
|
+
if (that.options.transformation === 'effective') {
|
|
217
|
+
forEachMemberRecursively(artifact, checkAnnotationExpression.bind(that), [ 'definitions', artifactName ], false, {
|
|
218
|
+
skipArtifact: a => a.returns || (a.params && !a.query),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
213
221
|
}
|
|
214
222
|
),
|
|
215
223
|
forRelationalDBQueryValidators.concat(commonQueryValidators),
|
|
@@ -208,6 +208,7 @@ function assertConsistency( model, stage ) {
|
|
|
208
208
|
'elements', '$autoElement', '$uncheckedElements', '_origin', '_extensions',
|
|
209
209
|
'$requireElementAccess', '_effectiveType', '$effectiveSeqNo', '_deps',
|
|
210
210
|
'$calcDepElement', '$filtered', '$enclosed', '_parent',
|
|
211
|
+
'deprecated', '$onlyInExprCtx',
|
|
211
212
|
],
|
|
212
213
|
schema: {
|
|
213
214
|
kind: { test: isString, enum: [ 'builtin' ] },
|
|
@@ -215,6 +216,8 @@ function assertConsistency( model, stage ) {
|
|
|
215
216
|
$autoElement: { test: isString },
|
|
216
217
|
$uncheckedElements: { test: isBoolean },
|
|
217
218
|
$requireElementAccess: { test: isBoolean },
|
|
219
|
+
deprecated: { test: isBoolean },
|
|
220
|
+
$onlyInExprCtx: { test: TODO },
|
|
218
221
|
// missing location for normal "elements"
|
|
219
222
|
elements: { test: TODO },
|
|
220
223
|
},
|
|
@@ -252,6 +255,7 @@ function assertConsistency( model, stage ) {
|
|
|
252
255
|
elements: { kind: true, inherits: 'definitions', also: [ 0 ] }, // 0 for cyclic expansions
|
|
253
256
|
// specified elements in query entities (TODO: introduce real "specified elements" instead):
|
|
254
257
|
elements$: { kind: true, enumerable: false, test: TODO },
|
|
258
|
+
foreignKeys$: { kind: true, enumerable: false, test: TODO },
|
|
255
259
|
enum$: { kind: true, enumerable: false, test: TODO },
|
|
256
260
|
typeProps$: { kind: true, enumerable: false, test: TODO },
|
|
257
261
|
// helper property for faster processing:
|
|
@@ -703,7 +707,7 @@ function assertConsistency( model, stage ) {
|
|
|
703
707
|
'localized', // e.g. compiler-generated elements for localized: `text` assoc, etc.
|
|
704
708
|
'localized-entity', // `.texts` entity
|
|
705
709
|
'localized-origin', // `.texts` entity
|
|
706
|
-
'nav', // only used for MASKED, TODO(
|
|
710
|
+
'nav', // only used for MASKED, TODO(v6): Remove
|
|
707
711
|
'none', // only used in ensureColumnName(): Used in object representing empty alias
|
|
708
712
|
'query', // inferred query properties, e.g. `key`
|
|
709
713
|
'rewrite', // on-conditions or FKeys are rewritten
|
package/lib/compiler/base.js
CHANGED
|
@@ -48,7 +48,7 @@ const kindProperties = {
|
|
|
48
48
|
normalized: 'action',
|
|
49
49
|
dict: 'actions',
|
|
50
50
|
}, // no extend params, only annotate
|
|
51
|
-
key: { normalized: 'element' },
|
|
51
|
+
key: { normalized: 'element', dict: 'elements' }, // dict for annotate
|
|
52
52
|
param: { elements: () => false, enum: () => false, dict: 'params' },
|
|
53
53
|
source: { artifacts: true }, // TODO -> $source
|
|
54
54
|
using: {},
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -178,14 +178,15 @@ const magicVariables = {
|
|
|
178
178
|
// Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
|
|
179
179
|
$autoElement: 'id',
|
|
180
180
|
},
|
|
181
|
-
$at: {
|
|
181
|
+
$at: { // $at is considered deprecated since cds-compiler v5
|
|
182
182
|
elements: {
|
|
183
183
|
from: {}, to: {},
|
|
184
184
|
},
|
|
185
185
|
// Require that elements are accessed, i.e. no $at, only $at.<element>.
|
|
186
186
|
$requireElementAccess: true,
|
|
187
|
+
deprecated: true, // $at is deprecated; use $valid
|
|
187
188
|
},
|
|
188
|
-
$valid: {
|
|
189
|
+
$valid: {
|
|
189
190
|
elements: {
|
|
190
191
|
from: {}, to: {},
|
|
191
192
|
},
|
|
@@ -200,6 +201,15 @@ const magicVariables = {
|
|
|
200
201
|
$uncheckedElements: true,
|
|
201
202
|
$requireElementAccess: true,
|
|
202
203
|
},
|
|
204
|
+
$draft: {
|
|
205
|
+
elements: {
|
|
206
|
+
IsActiveEntity: {},
|
|
207
|
+
},
|
|
208
|
+
// Require that elements are accessed, i.e. no $draft, only $draft.<element>.
|
|
209
|
+
$requireElementAccess: true,
|
|
210
|
+
// See reference semantics in shared.js
|
|
211
|
+
$onlyInExprCtx: [ 'annotation', 'annoRewrite' ],
|
|
212
|
+
},
|
|
203
213
|
};
|
|
204
214
|
|
|
205
215
|
// see lib/render/renderUtil.js for DB-specific magic vars, specified in CAP CDS via function
|
|
@@ -437,6 +447,10 @@ function initBuiltins( model ) {
|
|
|
437
447
|
art.$uncheckedElements = magic.$uncheckedElements;
|
|
438
448
|
if (magic.$requireElementAccess)
|
|
439
449
|
art.$requireElementAccess = magic.$requireElementAccess;
|
|
450
|
+
if (magic.deprecated)
|
|
451
|
+
art.deprecated = magic.deprecated;
|
|
452
|
+
if (magic.$onlyInExprCtx)
|
|
453
|
+
art.$onlyInExprCtx = magic.$onlyInExprCtx;
|
|
440
454
|
|
|
441
455
|
createMagicElements( art, magic.elements );
|
|
442
456
|
if (options.variableReplacements?.[id])
|
|
@@ -461,6 +475,8 @@ function initBuiltins( model ) {
|
|
|
461
475
|
// Propagate this property so that it is available for sub-elements.
|
|
462
476
|
if (art.$uncheckedElements)
|
|
463
477
|
magic.$uncheckedElements = art.$uncheckedElements;
|
|
478
|
+
if (art.$onlyInExprCtx)
|
|
479
|
+
magic.$onlyInExprCtx = art.$onlyInExprCtx;
|
|
464
480
|
setProp( magic, '_parent', art );
|
|
465
481
|
// setProp( magic, '_effectiveType', magic );
|
|
466
482
|
if (elements[id] && typeof elements[id] === 'object')
|
package/lib/compiler/checks.js
CHANGED
|
@@ -54,9 +54,6 @@ function check( model ) {
|
|
|
54
54
|
function checkEvent( def ) {
|
|
55
55
|
// Ensure that events are structured. Up to compiler v4, we allowed non-structured events,
|
|
56
56
|
// because when we introduced them, it was not fully specified what they are.
|
|
57
|
-
// TODO(v5):
|
|
58
|
-
// - Make this a (configurable) error; move to acceptTypeOrElement
|
|
59
|
-
// - require type/elements to be set in parser
|
|
60
57
|
if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)
|
|
61
58
|
message( 'def-expected-structured', [ (def.type || def.name).location, def ] );
|
|
62
59
|
}
|
|
@@ -862,8 +859,8 @@ function check( model ) {
|
|
|
862
859
|
// Tree-ish expression from the compiler (not augmented)
|
|
863
860
|
// eslint-disable-next-line max-len
|
|
864
861
|
return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[2] ) ||
|
|
865
|
-
|
|
866
|
-
|
|
862
|
+
// eslint-disable-next-line max-len
|
|
863
|
+
isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
|
|
867
864
|
}
|
|
868
865
|
|
|
869
866
|
// Nothing else qualifies
|