@sap/cds-compiler 2.12.0 → 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.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -5,9 +5,13 @@
5
5
 
6
6
  const { searchName } = require('../base/messages');
7
7
  const { dictAddArray } = require('../base/dictionaries');
8
- const { setProp } = require('../base/model');
9
8
 
10
- const { setLink, dependsOn, pathName } = require('./utils');
9
+ const {
10
+ setLink,
11
+ setArtifactLink,
12
+ dependsOn,
13
+ pathName,
14
+ } = require('./utils');
11
15
 
12
16
  function artifactsEnv( art ) {
13
17
  return art._subArtifacts || Object.create(null);
@@ -153,15 +157,15 @@ function fns( model ) {
153
157
  const VolatileFns = model.$volatileFunctions;
154
158
  Object.assign( model.$functions, {
155
159
  resolveUncheckedPath,
160
+ resolveTypeArgumentsUnchecked,
156
161
  resolvePath,
157
- resolveTypeArguments,
158
162
  defineAnnotations,
159
163
  attachAndEmitValidNames,
160
164
  } );
161
165
  return;
162
166
 
163
167
  function checkConstRef( art ) {
164
- return ![ 'builtin', 'param' ].includes( art.kind );
168
+ return art.kind !== 'builtin' && art.kind !== 'param';
165
169
  }
166
170
 
167
171
  function checkIncludesRef( art ) {
@@ -265,9 +269,9 @@ function fns( model ) {
265
269
  return ref._artifact;
266
270
  if (!ref.path || ref.path.broken || !ref.path.length) {
267
271
  // incomplete type AST or empty env (already reported)
268
- return setLink( ref, undefined );
272
+ return setArtifactLink( ref, undefined );
269
273
  }
270
- setLink( ref, 0 ); // avoid cycles for type T: association to T.m;
274
+ setArtifactLink( ref, 0 ); // avoid cycles for type T: association to T.m;
271
275
 
272
276
  let spec = specExpected[expected];
273
277
  const { path } = ref;
@@ -280,7 +284,7 @@ function fns( model ) {
280
284
  if (!spec.escape) {
281
285
  error( 'ref-unexpected-scope', [ ref.location, user ], {},
282
286
  'Unexpected parameter reference' );
283
- return setLink( ref, null );
287
+ return setArtifactLink( ref, null );
284
288
  }
285
289
  spec = specExpected[spec.escape];
286
290
  // In queries and query entities, the first lexical search environment
@@ -298,7 +302,7 @@ function fns( model ) {
298
302
  const query = (spec.lexical === 'main') ? user._main : userQuery( user );
299
303
  // in path filter, just $magic (and $parameters)
300
304
  env = (spec.lexical === 'from') ? query._parent : query || user._main || user;
301
- // queries: first tabaliases, then $magic - value refs: first $self, then $magic
305
+ // queries: first table aliases, then $magic - value refs: first $self, then $magic
302
306
  if (!extDict && !spec.noExt) {
303
307
  // TODO: change to name restriction for $joins, not own environments
304
308
  extDict = query && spec.rootEnv !== 'elements' &&
@@ -314,18 +318,34 @@ function fns( model ) {
314
318
  ? getPathRoot( path, spec, user, {}, model[spec.global || 'definitions'] )
315
319
  : getPathRoot( path, spec, user, env, extDict, msgArt || 0 );
316
320
  if (!art) {
317
- return setLink( ref, art );
321
+ return setArtifactLink( ref, art );
318
322
  }
319
323
  else if (!spec.envFn && user._pathHead) {
320
324
  // eslint-disable-next-line no-empty
321
325
  }
322
326
  else if (art.kind === 'using') {
323
- art = model.definitions[art.name.absolute];
324
- if (!art)
325
- return setLink( ref, art );
326
- else if (art.$duplicates) // redefined art referenced by using proxy
327
- return setLink( ref, false );
328
- setLink( head, art ); // we do not want to see the using
327
+ const def = model.definitions[art.name.absolute];
328
+ if (!def) {
329
+ // It could be that the artifact was removed and that the using-proxy needs to be reported.
330
+ // The check for $inferred is required to avoid consequential errors for cases such as:
331
+ // using unknown.abc;
332
+ // entity P as projection on abc; // <-- no consequential error here
333
+ if (art.$inferred === 'path-prefix') {
334
+ // head._artifact referred to the `using`. Remove the reference,
335
+ // so that getPathItem() below emits an error.
336
+ setArtifactLink( head, false );
337
+ setArtifactLink( ref, false );
338
+ }
339
+ else {
340
+ return setArtifactLink( ref, false );
341
+ }
342
+ }
343
+ else if (def.$duplicates) { // redefined art referenced by using proxy
344
+ return setArtifactLink( ref, false );
345
+ }
346
+ else {
347
+ setArtifactLink( head, def ); // we do not want to see the using
348
+ }
329
349
  }
330
350
  else if (art.kind === 'mixin') {
331
351
  if (spec.noAliasOrMixin) {
@@ -333,14 +353,14 @@ function fns( model ) {
333
353
  signalNotFound( 'ref-rejected-on', [ head.location, user ], extDict && [ extDict ],
334
354
  { '#': 'mixin', id: head.id } );
335
355
  // also set link on head?
336
- return setLink( ref, false );
356
+ return setArtifactLink( ref, false );
337
357
  }
338
358
  // console.log(message( null, art.location, art, {}, 'Info','MIX').toString())
339
- setLink( head, art, '_navigation' );
359
+ setLink( head, '_navigation', art );
340
360
  }
341
361
  else if (art.kind === '$navElement') {
342
- setLink( head, art, '_navigation' );
343
- setLink( head, art._origin );
362
+ setLink( head, '_navigation', art );
363
+ setArtifactLink( head, art._origin );
344
364
  // TODO: set art?
345
365
  }
346
366
  else if (art.kind === '$tableAlias' || art.kind === '$self') {
@@ -349,13 +369,17 @@ function fns( model ) {
349
369
  signalNotFound( 'ref-rejected-on', [ head.location, user ], extDict && [ extDict ],
350
370
  { '#': 'alias', id: head.id } );
351
371
  // also set link on head?
352
- return setLink( ref, false );
372
+ return setArtifactLink( ref, false );
353
373
  }
354
- setLink( head, art, '_navigation' );
355
- setLink( head, art._origin ); // query source or leading query in FROM
374
+ setLink( head, '_navigation', art );
375
+ setArtifactLink( head, art._origin ); // query source or leading query in FROM
356
376
  // require('../model/revealInternalProperties').log(model, 'foo.bar.S.V1a')
357
377
  if (!art._origin)
358
- return setLink( ref, art._origin );
378
+ return setArtifactLink( ref, art._origin );
379
+ // if just table alias (with expand), mark `user` with `$noOrigin` to indicate
380
+ // that the corresponding entity should not be put as $origin into the CSN
381
+ if (path.length === 1 && user && art.kind === '$tableAlias')
382
+ user.$noOrigin = true;
359
383
  }
360
384
 
361
385
  // how many path items are for artifacts (rest: elements)
@@ -365,20 +389,20 @@ function fns( model ) {
365
389
  // console.log(expected, ref.path.map(a=>a.id),artItemsCount)
366
390
  art = getPathItem( path, spec, user, artItemsCount, !spec.envFn && user._pathHead && art);
367
391
  if (!art)
368
- return setLink( ref, art );
392
+ return setArtifactLink( ref, art );
369
393
 
370
394
  if (art.$autoElement) {
371
395
  const { location } = path[path.length - 1];
372
396
  const step = { id: art.$autoElement, $inferred: '$autoElement', location };
373
397
  art = art.elements[step.id];
374
- setLink( step, art );
398
+ setArtifactLink( step, art );
375
399
  path.push( step );
376
400
  }
377
401
  if (spec.check) {
378
402
  const fail = spec.check( art, path );
379
403
  if (fail === true) {
380
404
  signalNotFound( spec.expectedMsgId, [ ref.location, user ], null );
381
- return setLink( ref, false );
405
+ return setArtifactLink( ref, false );
382
406
  }
383
407
  else if (fail) {
384
408
  signalNotFound( spec.sloppyMsgId, [ ref.location, user ], null );
@@ -410,7 +434,7 @@ function fns( model ) {
410
434
  !(env.$frontend && env.$frontend !== 'cdl'))
411
435
  deprecateSmart( ref, art, user );
412
436
  // TODO: follow FROM here, see csnRef - fromRef
413
- return setLink( ref, art );
437
+ return setArtifactLink( ref, art );
414
438
  }
415
439
 
416
440
  // Issue errors for "smart" element-in-artifact references
@@ -435,35 +459,64 @@ function fns( model ) {
435
459
  }
436
460
  }
437
461
 
438
- // Resolve the type arguments provided with a type referenced for artifact or
439
- // element `artifact`. This function does nothing if the referred type
440
- // `typeArtifact` does not have a `parameters` property (currently, only
441
- // builtin-types have it, see ./builtins.js).
442
- //
443
- // For each property name `<prop>` in `typeArtifact.parameters`, we move a number
444
- // in art.$typeArgs (a vector of numbers with locations) to `artifact.<prop>`.
445
- // TODO: error if no parameters applicable
446
- // TODO: also check for number
447
- function resolveTypeArguments(artifact, typeArtifact, user) {
448
- const args = artifact.$typeArgs || [];
462
+ /**
463
+ * Resolve the type arguments of `artifact` according to the type `typeArtifact`.
464
+ * User is used for semantic message location.
465
+ *
466
+ * For builtins, for each property name `<prop>` in `typeArtifact.parameters`, we move a value
467
+ * in art.$typeArgs (a vector of numbers with locations) to `artifact.<prop>`.
468
+ *
469
+ * For non-builtins, we take either one or two arguments and interpret them
470
+ * as `length` or `precision`/`scale`.
471
+ *
472
+ * Left-over arguments are errors for non-builtins and warnings for builtins.
473
+ *
474
+ * @param {object} artifact
475
+ * @param {object} typeArtifact
476
+ * @param {CSN.Artifact} user
477
+ */
478
+ function resolveTypeArgumentsUnchecked(artifact, typeArtifact, user) {
479
+ let args = artifact.$typeArgs || [];
449
480
  const parameters = typeArtifact.parameters || [];
450
- const parLength = parameters.length;
451
481
 
452
- for (let i = 0; i < parLength; ++i) {
453
- let par = parameters[i];
454
- if (!(par instanceof Object))
455
- par = { name: par };
456
- if (!artifact[par.name] && i < args.length)
457
- artifact[par.name] = args[i];
458
- }
459
- if (args.length > parLength) {
460
- artifact.$typeArgs = artifact.$typeArgs.slice(parLength);
461
- warning( 'unexpected-type-arg', [ artifact.$typeArgs[0].location, user ],
462
- { art: typeArtifact }, 'Too many arguments for type $(ART)' );
482
+ if (parameters.length > 0) {
483
+ // For Builtins
484
+ for (let i = 0; i < parameters.length; ++i) {
485
+ let par = parameters[i];
486
+ if (!(par instanceof Object))
487
+ par = { name: par };
488
+ if (!artifact[par.name] && i < args.length)
489
+ artifact[par.name] = args[i];
490
+ }
491
+ args = args.slice(parameters.length);
492
+ }
493
+ else if (args.length > 0 && !typeArtifact.builtin) {
494
+ // One or two arguments are interpreted as either length or precision/scale.
495
+ // For builtins, we know what arguments are expected, and we do not need this mapping.
496
+ // Also, we expect non-structured types.
497
+ if (args.length === 1) {
498
+ artifact.length = args[0];
499
+ args = args.slice(1);
500
+ }
501
+ else if (args.length === 2) {
502
+ artifact.precision = args[0];
503
+ artifact.scale = args[1];
504
+ args = args.slice(2);
505
+ }
463
506
  }
464
- else if (artifact.$typeArgs) {
465
- delete artifact.$typeArgs;
507
+
508
+ if (!artifact.$typeArgs)
509
+ return;
510
+
511
+ // Warn about left-over arguments.
512
+ if (args.length > 0) {
513
+ const loc = [ args[args.length - 1].location, user ];
514
+ if (typeArtifact.builtin)
515
+ warning( 'type-ignoring-argument', loc, { art: typeArtifact } );
516
+ else
517
+ error( 'type-unexpected-argument', loc, { '#': 'std', art: typeArtifact });
466
518
  }
519
+ artifact.$typeArgs = undefined;
467
520
  }
468
521
 
469
522
  // Return artifact or element referred by name `head`. The first environment
@@ -475,6 +528,8 @@ function fns( model ) {
475
528
  // TODO: not necessarily for explicit ON condition in expand
476
529
  VolatileFns.environment( user._pathHead ); // make sure _origin is set
477
530
  return user._pathHead._origin;
531
+ // const { _origin } = user._pathHead;
532
+ // return (_origin && _origin.kind === '$tableAlias') ? _origin._origin : _origin;
478
533
  }
479
534
  const head = path[0];
480
535
  if (!head || !head.id || !env)
@@ -501,7 +556,7 @@ function fns( model ) {
501
556
  const r = e[head.id];
502
557
  if (r) {
503
558
  if (Array.isArray(r)) { // redefinitions
504
- setLink( head, r );
559
+ setArtifactLink( head, r );
505
560
  return false;
506
561
  }
507
562
  // if (head.$delimited && r.kind !== '$tableAlias' && r.kind !== 'mixin')
@@ -512,18 +567,18 @@ function fns( model ) {
512
567
  { code: `$parameters.${ path[1].id }`, newcode: `:${ path[1].id }` },
513
568
  'Obsolete $(CODE) - replace by $(NEWCODE)' );
514
569
  // TODO: replace it in to-csn correspondingly
515
- return setLink( head, r );
570
+ return setArtifactLink( head, r );
516
571
  }
517
572
  }
518
573
  else if (r.kind === '$self') {
519
574
  // TODO: handle $delimited differently
520
575
  // TODO: $projection only if not delimited _and_ length > 1
521
- return setLink( head, r );
576
+ return setArtifactLink( head, r );
522
577
  }
523
578
  else if (r.kind !== '$tableAlias' || path.length > 1 || user.expand || user.inline) {
524
579
  // except "real" table aliases (not $self) with path len 1
525
580
  // TODO: $projection only if not delimited _and_ length > 1
526
- return setLink( head, r );
581
+ return setArtifactLink( head, r );
527
582
  }
528
583
  }
529
584
  }
@@ -540,11 +595,11 @@ function fns( model ) {
540
595
  'Ambiguous $(ID), replace by $(NAMES)' );
541
596
  }
542
597
  }
543
- setLink( head, r );
598
+ setArtifactLink( head, r );
544
599
  return false;
545
600
  }
546
601
  else if (r) {
547
- return setLink( head, r );
602
+ return setArtifactLink( head, r );
548
603
  }
549
604
  }
550
605
  if (spec.noMessage || msgArt === true && extDict === model.definitions)
@@ -598,7 +653,7 @@ function fns( model ) {
598
653
  signalNotFound( spec.undefinedArt || 'ref-undefined-art', [ head.location, user ],
599
654
  valid, { name: head.id } );
600
655
  }
601
- return setLink( head, null );
656
+ return setArtifactLink( head, null );
602
657
  }
603
658
 
604
659
  // Return artifact or element referred by path (array of ids) `tail`. The
@@ -606,6 +661,7 @@ function fns( model ) {
606
661
  // missing artifacts (as opposed to elements), provide the `head` (first
607
662
  // element item in the path)
608
663
  function getPathItem( path, spec, user, artItemsCount, headArt ) {
664
+ // let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
609
665
  let art = headArt;
610
666
  let nav = spec.assoc !== '$keys' && null; // false for '$keys'
611
667
  const last = path[path.length - 1];
@@ -622,7 +678,7 @@ function fns( model ) {
622
678
 
623
679
  const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : VolatileFns.environment;
624
680
  const env = fn( art, item.location, user, spec.assoc );
625
- const sub = setLink( item, env && env[item.id] );
681
+ const sub = setArtifactLink( item, env && env[item.id] );
626
682
 
627
683
  if (!sub) {
628
684
  // element was not found in environment
@@ -684,7 +740,7 @@ function fns( model ) {
684
740
  if (node._artifact) {
685
741
  // set the original(!) foreign key for the assoc - the "right" ones
686
742
  // after rewriteKeys() is the one with the same name.id
687
- setLink( item, node._artifact, '_navigation' );
743
+ setLink( item, '_navigation', node._artifact );
688
744
  if (item === last)
689
745
  return;
690
746
  }
@@ -705,7 +761,7 @@ function fns( model ) {
705
761
  signalNotFound( spec.undefinedDef || 'ref-undefined-def', [ item.location, user ],
706
762
  [ env ], { art: a } );
707
763
  }
708
- else if (art.name.select && art.name.select > 1) {
764
+ else if (art.name && art.name.select && art.name.select > 1) {
709
765
  // TODO: 'The current query has no element $(MEMBER)' with $self.MEMBER
710
766
  // and 'The sub query for alias $(ALIAS) has no element $(MEMBER)'
711
767
  // TODO: probably not extra messageId, but text variant
@@ -721,8 +777,13 @@ function fns( model ) {
721
777
  { param: 'Entity $(ART) has no parameter $(MEMBER)' } );
722
778
  }
723
779
  else {
780
+ const variant = art.kind === 'aspect' && !art.name && 'aspect';
724
781
  signalNotFound( spec.undefinedDef || 'ref-undefined-element', [ item.location, user ],
725
- [ env ], { art: searchName( art, item.id, 'element' ) } );
782
+ [ env ], {
783
+ '#': variant,
784
+ art: (variant ? '' : searchName( art, item.id, 'element' )),
785
+ id: item.id,
786
+ } );
726
787
  }
727
788
  return null;
728
789
  }
@@ -817,7 +878,7 @@ function fns( model ) {
817
878
  if (!(Array.isArray(annos)))
818
879
  annos = [ annos ];
819
880
  for (const a of annos) {
820
- setProp( a, '_block', block );
881
+ setLink( a, '_block', block );
821
882
  a.$priority = priority;
822
883
  if (construct !== art)
823
884
  addAnnotation( art, annoProp, a );
@@ -867,7 +928,7 @@ function fns( model ) {
867
928
  value.name && value.name.location ||
868
929
  value.path && value.path.location,
869
930
  };
870
- setProp( anno, '_block', block );
931
+ setLink( anno, '_block', block );
871
932
  // TODO: _parent, _main is set later (if we have ElementRef), or do we
872
933
  // set _artifact?
873
934
  anno.$priority = priority;