@sap/cds-compiler 4.9.2 → 5.0.6
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 +74 -0
- package/bin/cds_remove_invalid_whitespace.js +2 -1
- package/bin/cdsc.js +15 -11
- package/bin/cdshi.js +1 -0
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +7 -19
- package/lib/api/options.js +5 -11
- 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 +29 -29
- package/lib/base/messages.js +22 -26
- package/lib/base/model.js +0 -2
- package/lib/base/node-helpers.js +0 -1
- 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 +4 -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 +7 -7
- package/lib/compiler/extend.js +68 -33
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/index.js +23 -6
- package/lib/compiler/lsp-api.js +501 -2
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +1 -4
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +112 -31
- package/lib/compiler/tweak-assocs.js +2 -16
- package/lib/compiler/utils.js +2 -1
- package/lib/compiler/xsn-model.js +4 -0
- package/lib/edm/annotations/genericTranslation.js +95 -42
- package/lib/edm/csn2edm.js +16 -4
- package/lib/edm/edm.js +2 -3
- package/lib/edm/edmAnnoPreprocessor.js +1 -2
- package/lib/edm/edmPreprocessor.js +1 -7
- package/lib/gen/Dictionary.json +29 -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 +23 -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 +7 -4
- package/lib/model/csnRefs.js +20 -4
- package/lib/model/csnUtils.js +0 -2
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/modelCompare/compare.js +1 -1
- package/lib/optionProcessor.js +28 -9
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +36 -7
- package/lib/render/toSql.js +1 -0
- 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/constraints.js +23 -25
- package/lib/transform/db/expansion.js +1 -6
- package/lib/transform/db/flattening.js +89 -111
- package/lib/transform/db/temporal.js +3 -4
- package/lib/transform/db/views.js +0 -1
- package/lib/transform/draft/odata.js +51 -3
- package/lib/transform/effective/annotations.js +3 -2
- package/lib/transform/effective/flattening.js +135 -0
- package/lib/transform/effective/main.js +6 -6
- package/lib/transform/effective/types.js +13 -9
- package/lib/transform/forOdata.js +0 -2
- package/lib/transform/forRelationalDB.js +0 -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 +21 -3
- package/lib/utils/file.js +13 -7
- 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
package/lib/compiler/lsp-api.js
CHANGED
|
@@ -1,5 +1,504 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// API for `@sap/cds-lsp`.
|
|
4
|
+
//
|
|
5
|
+
// THIS FILE IS CONSIDERED INTERNAL!
|
|
6
|
+
// We do not guarantee stability for any project besides the CAP LSP server.
|
|
7
|
+
//
|
|
8
|
+
// This files includes an iterator over "semantic tokens" in an XSN model.
|
|
9
|
+
// "Semantic tokens" are identifiers, but also the "return" parameter.
|
|
10
|
+
// See `internalDoc/lsp/IdentifierCrawling.md` for details.
|
|
4
11
|
|
|
5
|
-
|
|
12
|
+
const { CompilerAssertion } = require('../base/error');
|
|
13
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
14
|
+
|
|
15
|
+
// TODO: Remove hints; they should not be necessary in the best case
|
|
16
|
+
const HINTS = {
|
|
17
|
+
USING_ALIAS: 'using-alias',
|
|
18
|
+
DEFINITION_NAME: 'definition',
|
|
19
|
+
NAMESPACE_STATEMENT: 'namespace-statement',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-unused-vars
|
|
23
|
+
class LspSemanticTokenEvent {
|
|
24
|
+
event; // 'reference' | 'definition',
|
|
25
|
+
semanticToken;
|
|
26
|
+
node;
|
|
27
|
+
hint; // TODO: Remove
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* All actions to report semantic tokens in a model.
|
|
32
|
+
*/
|
|
33
|
+
const artifactActions = {
|
|
34
|
+
__proto__: null,
|
|
35
|
+
|
|
36
|
+
// e.g. sources or services
|
|
37
|
+
artifacts: dictOf( artifactTokens ),
|
|
38
|
+
extensions: arrayOf( extensionTokens ),
|
|
39
|
+
namespace: namespaceTokens,
|
|
40
|
+
// e.g. via CSN input
|
|
41
|
+
vocabularies: dictOf( artifactTokens ),
|
|
42
|
+
definitions: dictOf( artifactTokens ),
|
|
43
|
+
|
|
44
|
+
extern: artifactTokens,
|
|
45
|
+
name: definitionNameTokens,
|
|
46
|
+
path: pathReferenceTokens,
|
|
47
|
+
|
|
48
|
+
type: artifactTokens,
|
|
49
|
+
target: artifactTokens,
|
|
50
|
+
targetAspect: artifactTokens,
|
|
51
|
+
targetElement: artifactTokens,
|
|
52
|
+
returns: returnsTokens,
|
|
53
|
+
items: artifactTokens,
|
|
54
|
+
elements: elementsTokens,
|
|
55
|
+
|
|
56
|
+
enum: dictOf( artifactTokens ),
|
|
57
|
+
foreignKeys: dictOf( artifactTokens ),
|
|
58
|
+
actions: dictOf( artifactTokens ),
|
|
59
|
+
params: dictOf( artifactTokens ),
|
|
60
|
+
mixin: dictOf( artifactTokens ),
|
|
61
|
+
excludingDict: dictOf( nameAsReference ),
|
|
62
|
+
|
|
63
|
+
// Don't crawl `$tableAliases`, as they are set multiple times in queries
|
|
64
|
+
// via different `$tableAliases`.
|
|
65
|
+
// $tableAliases: null,
|
|
66
|
+
|
|
67
|
+
// NOT $queries, as that doesn't cover UNIONs (e.g. `orderBy` vs `$orderBy`)
|
|
68
|
+
query: artifactTokens,
|
|
69
|
+
|
|
70
|
+
from: artifactTokens,
|
|
71
|
+
includes: arrayOf( artifactTokens ),
|
|
72
|
+
columns: arrayOf( artifactTokens ),
|
|
73
|
+
expand: arrayOf( artifactTokens ),
|
|
74
|
+
inline: arrayOf( artifactTokens ),
|
|
75
|
+
|
|
76
|
+
args: argsTokens,
|
|
77
|
+
on: artifactTokens,
|
|
78
|
+
default: artifactTokens,
|
|
79
|
+
value: artifactTokens,
|
|
80
|
+
sym: enumSymToken,
|
|
81
|
+
where: artifactTokens,
|
|
82
|
+
groupBy: artifactTokens,
|
|
83
|
+
orderBy: artifactTokens,
|
|
84
|
+
having: artifactTokens,
|
|
85
|
+
suffix: artifactTokens,
|
|
86
|
+
limit: artifactTokens,
|
|
87
|
+
rows: artifactTokens,
|
|
88
|
+
offset: artifactTokens,
|
|
89
|
+
|
|
90
|
+
'@': annotationTokens,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** Returns a generator that applies the given function on all entries and yields the result. */
|
|
94
|
+
function dictOf( func ) {
|
|
95
|
+
return function* dictionary( dict ) {
|
|
96
|
+
for (const [ item ] of iterateGeneric({ dict }, 'dict'))
|
|
97
|
+
yield* func( item );
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Returns a generator that applies the given function on all entries and yields the result. */
|
|
102
|
+
function arrayOf( func ) {
|
|
103
|
+
return function* array( arr ) {
|
|
104
|
+
if (!Array.isArray(arr))
|
|
105
|
+
return;
|
|
106
|
+
for (const item of arr)
|
|
107
|
+
yield* func( item );
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Generator equivalent of iterateGeneric of forEachGeneric() */
|
|
112
|
+
function* iterateGeneric( obj, prop ) {
|
|
113
|
+
const dict = obj[prop];
|
|
114
|
+
if (!dict)
|
|
115
|
+
return;
|
|
116
|
+
|
|
117
|
+
for (const name in dict) {
|
|
118
|
+
obj = dict[name];
|
|
119
|
+
if (Array.isArray( obj )) {
|
|
120
|
+
for (const item of obj)
|
|
121
|
+
yield [ item, name, prop ]; // parser or source duplicates (e.g. USING vs definition)
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
yield [ obj, name, prop ];
|
|
125
|
+
if (Array.isArray( obj.$duplicates )) { // redefinitions
|
|
126
|
+
for (const dup of obj.$duplicates)
|
|
127
|
+
yield [ dup, name, prop ];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A generator that yields all semantic tokens in an XSN model.
|
|
135
|
+
* Semantic tokens include identifiers (references/definitions) and the "returns" parameter.
|
|
136
|
+
*
|
|
137
|
+
* @param {XSN.Model} xsn
|
|
138
|
+
* @param {CSN.Options} options
|
|
139
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
140
|
+
*/
|
|
141
|
+
function* traverseSemanticTokens( xsn, options ) {
|
|
142
|
+
if (!xsn)
|
|
143
|
+
throw new CompilerAssertion('Expected valid XSN model for traverseSemanticTokens(…)');
|
|
144
|
+
if (!options)
|
|
145
|
+
throw new CompilerAssertion('Expected valid options for traverseSemanticTokens(…)');
|
|
146
|
+
|
|
147
|
+
if (xsn.sources)
|
|
148
|
+
yield* dictOf( artifactTokens )( xsn.sources );
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Report semantic tokens in artifacts, including definitions, elements, params, etc.
|
|
153
|
+
*
|
|
154
|
+
* @param {XSN.Artifact} art
|
|
155
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
156
|
+
*/
|
|
157
|
+
function* artifactTokens( art ) {
|
|
158
|
+
if (!art || art.builtin || art.$inferred)
|
|
159
|
+
return null;
|
|
160
|
+
|
|
161
|
+
if (Array.isArray( art )) {
|
|
162
|
+
for (const entry of art)
|
|
163
|
+
yield* artifactTokens( entry );
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const prop in art) {
|
|
168
|
+
if (artifactActions[prop])
|
|
169
|
+
yield* artifactActions[prop](art[prop], art);
|
|
170
|
+
else if (prop.charAt(0) === '@')
|
|
171
|
+
yield* artifactActions['@'](art[prop], art);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* For an extension, yield all semantic tokens.
|
|
179
|
+
* We don't use `artifactTokens` for it, because extensions are a special case:
|
|
180
|
+
* - they have a name, but actually refer to some other artifact.
|
|
181
|
+
* - their artifacts such as elements may overlap with existing definitions, because
|
|
182
|
+
* extensions are applied; if they were applied, `_parent` does not point to the
|
|
183
|
+
* extension, which means we can't use it to skip them in `artifactTokens`.
|
|
184
|
+
* - we only need to handle `annotate` and `extend` kinds specifically:
|
|
185
|
+
* if an extension was not applied, pass it to `artifactTokens`;
|
|
186
|
+
* if an extension was applied, we only need to report its name (i.e. reference)
|
|
187
|
+
* and traverse over all artifacts
|
|
188
|
+
*
|
|
189
|
+
* @param {XSN.Extension} ext
|
|
190
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
191
|
+
*/
|
|
192
|
+
function* extensionTokens( ext ) {
|
|
193
|
+
if (ext.kind !== 'extend' && ext.kind !== 'annotate')
|
|
194
|
+
return null;
|
|
195
|
+
|
|
196
|
+
const wasApplied = ext.name._artifact && !ext.name._artifact.$inferred;
|
|
197
|
+
if (!wasApplied) {
|
|
198
|
+
yield* artifactTokens( ext );
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
yield* nameAsReference( ext );
|
|
203
|
+
|
|
204
|
+
// We need to traverse all dictionaries that could themselves contain
|
|
205
|
+
// extensions. Enum extensions or columns don't need to be traversed,
|
|
206
|
+
// for example, because there can't be inner extensions.
|
|
207
|
+
yield* dictOf( extensionTokens )( ext.params );
|
|
208
|
+
yield* dictOf( extensionTokens )( ext.actions );
|
|
209
|
+
yield* dictOf( extensionTokens )( ext.elements );
|
|
210
|
+
|
|
211
|
+
if (ext.returns)
|
|
212
|
+
yield* extensionTokens( ext.returns );
|
|
213
|
+
|
|
214
|
+
// Artifact extensions are always definitions, and can't have nested `extend`s,
|
|
215
|
+
// hence no need to traverse them with `extensionTokens`.
|
|
216
|
+
yield* dictOf( artifactTokens )( ext.artifacts );
|
|
217
|
+
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Report all semantic tokens in an annotation assignment.
|
|
223
|
+
*
|
|
224
|
+
* @param {XSN.Artifact} anno
|
|
225
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
226
|
+
*/
|
|
227
|
+
function* annotationTokens( anno ) {
|
|
228
|
+
// TODO: Also report annotation names
|
|
229
|
+
if (anno.kind === '$annotation')
|
|
230
|
+
yield* annotationValueTokens( anno );
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function* argsTokens( args, art ) {
|
|
234
|
+
if (Array.isArray(args)) {
|
|
235
|
+
// e.g. unnamed function arguments
|
|
236
|
+
yield* arrayOf( artifactTokens )( args );
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// e.g. named arguments
|
|
240
|
+
for (const [ param ] of iterateGeneric( art, 'args' )) {
|
|
241
|
+
yield* nameAsReference( param );
|
|
242
|
+
yield* artifactTokens( param );
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function* enumSymToken( sym, expr ) {
|
|
248
|
+
yield {
|
|
249
|
+
event: 'reference',
|
|
250
|
+
semanticToken: expr.sym,
|
|
251
|
+
node: expr,
|
|
252
|
+
hint: undefined,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* A namespace is always considered a reference and not a definition.
|
|
258
|
+
*
|
|
259
|
+
* @param {XSN.Artifact} def
|
|
260
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
261
|
+
*/
|
|
262
|
+
function* namespaceTokens( def ) {
|
|
263
|
+
if (!def.name)
|
|
264
|
+
return null;
|
|
265
|
+
|
|
266
|
+
for (let i = 0; i < def.name.path.length; ++i) {
|
|
267
|
+
yield {
|
|
268
|
+
event: 'reference',
|
|
269
|
+
semanticToken: def.name.path[i],
|
|
270
|
+
node: def,
|
|
271
|
+
hint: (i === def.name.path.length - 1) ? HINTS.NAMESPACE_STATEMENT : null,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* An annotation value may contain expressions which we need to report.
|
|
280
|
+
*
|
|
281
|
+
* @param {object} anno
|
|
282
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
283
|
+
*/
|
|
284
|
+
function* annotationValueTokens( anno ) {
|
|
285
|
+
if (Array.isArray(anno)) {
|
|
286
|
+
for (const entry of anno)
|
|
287
|
+
yield* annotationValueTokens( entry );
|
|
288
|
+
}
|
|
289
|
+
else if (anno.$tokenTexts) {
|
|
290
|
+
yield* artifactTokens( anno );
|
|
291
|
+
}
|
|
292
|
+
else if (Array.isArray(anno.val)) {
|
|
293
|
+
yield* annotationValueTokens( anno.val );
|
|
294
|
+
}
|
|
295
|
+
else if (anno.struct) {
|
|
296
|
+
for (const [ struct ] of iterateGeneric( anno, 'struct' ))
|
|
297
|
+
yield* annotationValueTokens( struct );
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* A `returns` structure may contain sub-elements. But we report the `returns`
|
|
303
|
+
* token as well, as it is considered a token with semantic value.
|
|
304
|
+
*
|
|
305
|
+
* @param {XSN.Artifact} art
|
|
306
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
307
|
+
*/
|
|
308
|
+
function* returnsTokens( art ) {
|
|
309
|
+
if (art.kind === 'param') {
|
|
310
|
+
// report the `returns` parameter
|
|
311
|
+
yield {
|
|
312
|
+
event: 'definition',
|
|
313
|
+
semanticToken: art.name,
|
|
314
|
+
node: art,
|
|
315
|
+
hint: undefined,
|
|
316
|
+
};
|
|
317
|
+
yield* artifactTokens( art );
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Report elements if they should be traversed. They are not always traversed
|
|
323
|
+
* to avoid duplication due to `expand` and `columns` also being traversed.
|
|
324
|
+
*
|
|
325
|
+
* @param {Record<string, XSN.Artifact>} elements
|
|
326
|
+
* @param {XSN.Artifact} art
|
|
327
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
328
|
+
*/
|
|
329
|
+
function* elementsTokens( elements, art ) {
|
|
330
|
+
if (shouldTraverseElements( art ))
|
|
331
|
+
yield* dictOf( artifactTokens )( elements );
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Report all references in `ref`.
|
|
336
|
+
*
|
|
337
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
338
|
+
*/
|
|
339
|
+
function* pathReferenceTokens( path, ref, user = ref, hint = null ) {
|
|
340
|
+
if (!path)
|
|
341
|
+
return null;
|
|
342
|
+
|
|
343
|
+
// don't report cds.Association/cds.Composition
|
|
344
|
+
// TODO: Or report the `Association` keyword, similar to `returns`?
|
|
345
|
+
if (path.length === 1 && ref._artifact?.category === 'relation')
|
|
346
|
+
return null;
|
|
347
|
+
|
|
348
|
+
yield* artifactTokens( path );
|
|
349
|
+
|
|
350
|
+
// parser prepends a fake `type of` segment, which we need to skip
|
|
351
|
+
const root = ref.scope === 'typeOf' ? 1 : 0;
|
|
352
|
+
for (let i = root; i < path.length; ++i) {
|
|
353
|
+
if (!path[i].$inferred) { // e.g. `id` when expanded from `$user`
|
|
354
|
+
yield {
|
|
355
|
+
event: 'reference',
|
|
356
|
+
semanticToken: path[i],
|
|
357
|
+
node: user,
|
|
358
|
+
hint,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Some XSN nodes such as entries in `excludingDict` or named arguments are references
|
|
368
|
+
* but don't have a `path` property, only a `name` property. Report such names
|
|
369
|
+
* as references.
|
|
370
|
+
*
|
|
371
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
372
|
+
*/
|
|
373
|
+
function* nameAsReference( ref, hint = null ) {
|
|
374
|
+
if (!ref.name || ref.name.$inferred)
|
|
375
|
+
return null;
|
|
376
|
+
|
|
377
|
+
if (ref.name.path) {
|
|
378
|
+
yield* pathReferenceTokens( ref.name.path, ref.name, ref, hint );
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
yield {
|
|
382
|
+
event: 'reference',
|
|
383
|
+
semanticToken: ref.name,
|
|
384
|
+
node: ref,
|
|
385
|
+
hint,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Traverse the name of a definition, and report N-1 path steps as references
|
|
393
|
+
* and of course the definition itself.
|
|
394
|
+
*
|
|
395
|
+
* @returns {Generator<LspSemanticTokenEvent>}
|
|
396
|
+
*/
|
|
397
|
+
function* definitionNameTokens( name, art ) {
|
|
398
|
+
if (!art.kind)
|
|
399
|
+
return null; // e.g. parameter references
|
|
400
|
+
if (art.kind === '$annotation')
|
|
401
|
+
return null; // annotation name, e.g. in `@anno: (elem)`
|
|
402
|
+
|
|
403
|
+
if ((name.$inferred && name.$inferred !== 'as') ||
|
|
404
|
+
art.kind === 'select' || art.kind === '$join') {
|
|
405
|
+
// Internal names such as numbers for SELECTs or the `$internal` names must
|
|
406
|
+
// not be reported.
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (art.kind === 'extend' || art.kind === 'annotate') {
|
|
411
|
+
yield* nameAsReference( art );
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Report references in a name (N-1 path steps).
|
|
416
|
+
for (let i = 0; i < name.path?.length - 1; ++i) {
|
|
417
|
+
yield {
|
|
418
|
+
event: 'reference',
|
|
419
|
+
semanticToken: name.path[i],
|
|
420
|
+
node: art,
|
|
421
|
+
hint: HINTS.DEFINITION_NAME,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const hint = art.kind === 'using' ? HINTS.USING_ALIAS : null;
|
|
426
|
+
|
|
427
|
+
if (name.path) {
|
|
428
|
+
// Only take the last path step; all others are considered references.
|
|
429
|
+
const implicitName = name.path[name.path.length - 1];
|
|
430
|
+
yield {
|
|
431
|
+
event: 'definition',
|
|
432
|
+
semanticToken: implicitName,
|
|
433
|
+
node: art,
|
|
434
|
+
hint,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
else if (name.id) {
|
|
438
|
+
// Not all names have a path; some (e.g. parameters) only have an ID.
|
|
439
|
+
yield {
|
|
440
|
+
event: 'definition',
|
|
441
|
+
semanticToken: name,
|
|
442
|
+
node: art,
|
|
443
|
+
hint,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Returns true if `elements` of the given `art` should be traversed.
|
|
452
|
+
* Elements are _not_ traversed, e.g. for `expand`, to avoid duplicates.
|
|
453
|
+
*
|
|
454
|
+
* @returns {boolean}
|
|
455
|
+
*/
|
|
456
|
+
function shouldTraverseElements( art ) {
|
|
457
|
+
return (
|
|
458
|
+
// $expand: 'origin' -> normal expansion
|
|
459
|
+
// $expand: 'annotate' -> additional annotation (needs to traverse annotation expressions)
|
|
460
|
+
art.$expand !== 'origin' && !art.elements[$inferred] && (
|
|
461
|
+
// sub-elements are always traversed except for `expand`, which is handled on its own.
|
|
462
|
+
art.kind === 'element' && !art.expand ||
|
|
463
|
+
// all non-query elements are traversed; because `_main` on bound actions may point
|
|
464
|
+
// to a query, we handle parameters explicitly.
|
|
465
|
+
art.kind === 'param' || !(art._main || art).$queries
|
|
466
|
+
)
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Given a LspSemanticTokenEvent, returns a generator that yields the referenced
|
|
472
|
+
* object and its origin's until the deepest entry is found.
|
|
473
|
+
*
|
|
474
|
+
* @param obj
|
|
475
|
+
* @returns {Generator<*, void, *>}
|
|
476
|
+
*/
|
|
477
|
+
function* getSemanticTokenOrigin( obj ) {
|
|
478
|
+
let ref = obj.semanticToken;
|
|
479
|
+
if (obj.event === 'definition') {
|
|
480
|
+
ref = obj.node;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
if (!ref?._artifact)
|
|
484
|
+
return; // unknown -> abort
|
|
485
|
+
// take first artifact for duplicates (best effort)
|
|
486
|
+
ref = Array.isArray(ref._artifact) ? ref._artifact[0] : ref._artifact;
|
|
487
|
+
yield ref;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (!ref._effectiveType)
|
|
491
|
+
return; // abort for unresolved references and cyclic ones
|
|
492
|
+
|
|
493
|
+
while (ref._origin) {
|
|
494
|
+
yield ref._origin;
|
|
495
|
+
ref = ref._origin;
|
|
496
|
+
if (!ref || typeof ref === 'string')
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
module.exports = {
|
|
502
|
+
traverseSemanticTokens,
|
|
503
|
+
getSemanticTokenOrigin,
|
|
504
|
+
};
|
package/lib/compiler/populate.js
CHANGED
|
@@ -1069,10 +1069,10 @@ function populate( model ) {
|
|
|
1069
1069
|
else {
|
|
1070
1070
|
// referred (and probably inferred) assoc (without a user-provided target at that place)
|
|
1071
1071
|
// HINT: consider bin/cdsv2m.js when changing the following message text
|
|
1072
|
-
// No grouped and sub messages yet (TODO
|
|
1072
|
+
// No grouped and sub messages yet (TODO v6): mention at all target places with all assocs
|
|
1073
1073
|
const withAnno = annotationVal( exposed[0]['@cds.redirection.target'] );
|
|
1074
1074
|
for (const proj of exposed) {
|
|
1075
|
-
// TODO: def-ambiguous-target (just
|
|
1075
|
+
// TODO: def-ambiguous-target (just v6, as the current is infamous and used in options),
|
|
1076
1076
|
message( 'redirected-implicitly-ambiguous',
|
|
1077
1077
|
[ weakLocation( proj.name.location ), proj ],
|
|
1078
1078
|
{
|
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
forEachMember,
|
|
14
14
|
forEachGeneric,
|
|
15
15
|
isDeprecatedEnabled,
|
|
16
|
-
isBetaEnabled,
|
|
17
16
|
} = require( '../base/model');
|
|
18
17
|
const {
|
|
19
18
|
setLink,
|
|
@@ -56,7 +55,6 @@ function propagate( model ) {
|
|
|
56
55
|
enum: expensive,
|
|
57
56
|
params: expensive, // actually only with parent action
|
|
58
57
|
returns,
|
|
59
|
-
$filtered: annotation, // TODO(v5): Remove
|
|
60
58
|
$enclosed: annotation,
|
|
61
59
|
};
|
|
62
60
|
const ruleToFunction = {
|
|
@@ -75,8 +73,7 @@ function propagate( model ) {
|
|
|
75
73
|
const { warning, throwWithError } = model.$messageFunctions;
|
|
76
74
|
|
|
77
75
|
forEachDefinition( model, run );
|
|
78
|
-
|
|
79
|
-
forEachGeneric( model, 'vocabularies', run );
|
|
76
|
+
forEachGeneric( model, 'vocabularies', run );
|
|
80
77
|
|
|
81
78
|
// TODO: move 'virtual' handling/checks to resolver if
|
|
82
79
|
// 'deprecated.oldVirtualNotNullPropagation' is gone
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -88,6 +88,7 @@ function resolve( model ) {
|
|
|
88
88
|
} = model.$messageFunctions;
|
|
89
89
|
const {
|
|
90
90
|
resolvePath,
|
|
91
|
+
resolveDefinitionName,
|
|
91
92
|
traverseExpr,
|
|
92
93
|
createRemainingAnnotateStatements,
|
|
93
94
|
effectiveType,
|
|
@@ -442,7 +443,7 @@ function resolve( model ) {
|
|
|
442
443
|
obj.items = items;
|
|
443
444
|
obj.$expand = 'origin';
|
|
444
445
|
}
|
|
445
|
-
if (obj.items) { // TODO: make this a while in
|
|
446
|
+
if (obj.items) { // TODO: make this a while in v6 (also items proxy)
|
|
446
447
|
obj = obj.items || obj; // the object which has type properties
|
|
447
448
|
effectiveType( obj );
|
|
448
449
|
}
|
|
@@ -1266,7 +1267,6 @@ function resolve( model ) {
|
|
|
1266
1267
|
{ art: target, '#': variant, keyword: op || '' }, {
|
|
1267
1268
|
std: 'Redirection involves the complex view $(ART)',
|
|
1268
1269
|
target: 'The redirected target $(ART) is a complex view',
|
|
1269
|
-
// eslint-disable-next-line max-len
|
|
1270
1270
|
targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD)',
|
|
1271
1271
|
} );
|
|
1272
1272
|
break;
|
|
@@ -1360,19 +1360,6 @@ function resolve( model ) {
|
|
|
1360
1360
|
// General resolver functions
|
|
1361
1361
|
//--------------------------------------------------------------------------
|
|
1362
1362
|
|
|
1363
|
-
// Resolve the n-1 path steps before the definition name for LSP.
|
|
1364
|
-
function resolveDefinitionName( art ) {
|
|
1365
|
-
const path = art?.name?.path;
|
|
1366
|
-
if (!art || art._main || !path || path.length <= 1)
|
|
1367
|
-
return;
|
|
1368
|
-
|
|
1369
|
-
let name = art.name.id;
|
|
1370
|
-
for (let i = path.length - 1; i > 0; --i) {
|
|
1371
|
-
name = name.substring(0, name.length - path[i].id.length - 1);
|
|
1372
|
-
setArtifactLink( path[i - 1], model.definitions[name] || false );
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
1363
|
// Resolve the type and its arguments if applicable.
|
|
1377
1364
|
function resolveTypeExpr( art, user ) {
|
|
1378
1365
|
const typeArt = resolvePath( art.type, 'type', user );
|