@sap/cds-compiler 2.11.2 → 2.13.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.
Files changed (140) hide show
  1. package/CHANGELOG.md +175 -2
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +23 -17
  5. package/bin/cdsse.js +2 -2
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +25 -6
  9. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  10. package/doc/NameResolution.md +21 -16
  11. package/lib/api/main.js +32 -79
  12. package/lib/api/options.js +3 -2
  13. package/lib/api/validate.js +2 -1
  14. package/lib/backends.js +16 -26
  15. package/lib/base/dictionaries.js +0 -8
  16. package/lib/base/error.js +26 -0
  17. package/lib/base/keywords.js +10 -19
  18. package/lib/base/location.js +9 -4
  19. package/lib/base/message-registry.js +75 -9
  20. package/lib/base/messages.js +31 -35
  21. package/lib/base/model.js +2 -62
  22. package/lib/base/optionProcessorHelper.js +246 -183
  23. package/lib/checks/.eslintrc.json +2 -0
  24. package/lib/checks/actionsFunctions.js +2 -1
  25. package/lib/checks/annotationsOData.js +1 -1
  26. package/lib/checks/cdsPersistence.js +2 -1
  27. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  28. package/lib/checks/enricher.js +17 -1
  29. package/lib/checks/foreignKeys.js +4 -4
  30. package/lib/checks/invalidTarget.js +3 -1
  31. package/lib/checks/managedInType.js +4 -4
  32. package/lib/checks/managedWithoutKeys.js +3 -1
  33. package/lib/checks/queryNoDbArtifacts.js +1 -3
  34. package/lib/checks/selectItems.js +4 -4
  35. package/lib/checks/sql-snippets.js +94 -0
  36. package/lib/checks/types.js +1 -1
  37. package/lib/checks/unknownMagic.js +1 -1
  38. package/lib/checks/validator.js +12 -7
  39. package/lib/compiler/assert-consistency.js +12 -8
  40. package/lib/compiler/base.js +0 -1
  41. package/lib/compiler/builtins.js +42 -21
  42. package/lib/compiler/checks.js +46 -12
  43. package/lib/compiler/cycle-detector.js +1 -1
  44. package/lib/compiler/define.js +1103 -0
  45. package/lib/compiler/extend.js +983 -0
  46. package/lib/compiler/finalize-parse-cdl.js +231 -0
  47. package/lib/compiler/index.js +46 -39
  48. package/lib/compiler/kick-start.js +190 -0
  49. package/lib/compiler/moduleLayers.js +4 -4
  50. package/lib/compiler/populate.js +1226 -0
  51. package/lib/compiler/propagator.js +113 -47
  52. package/lib/compiler/resolve.js +1433 -0
  53. package/lib/compiler/shared.js +100 -65
  54. package/lib/compiler/tweak-assocs.js +529 -0
  55. package/lib/compiler/utils.js +215 -33
  56. package/lib/edm/.eslintrc.json +5 -0
  57. package/lib/edm/annotations/genericTranslation.js +38 -25
  58. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  59. package/lib/edm/csn2edm.js +10 -9
  60. package/lib/edm/edm.js +19 -20
  61. package/lib/edm/edmPreprocessor.js +166 -95
  62. package/lib/edm/edmUtils.js +127 -34
  63. package/lib/gen/Dictionary.json +92 -43
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +11 -1
  66. package/lib/gen/language.tokens +86 -82
  67. package/lib/gen/languageLexer.interp +18 -1
  68. package/lib/gen/languageLexer.js +925 -847
  69. package/lib/gen/languageLexer.tokens +78 -74
  70. package/lib/gen/languageParser.js +5434 -4298
  71. package/lib/json/from-csn.js +59 -17
  72. package/lib/json/to-csn.js +189 -71
  73. package/lib/language/antlrParser.js +3 -3
  74. package/lib/language/docCommentParser.js +3 -3
  75. package/lib/language/errorStrategy.js +26 -8
  76. package/lib/language/genericAntlrParser.js +144 -53
  77. package/lib/language/language.g4 +424 -200
  78. package/lib/language/multiLineStringParser.js +536 -0
  79. package/lib/main.d.ts +550 -61
  80. package/lib/main.js +38 -11
  81. package/lib/model/api.js +3 -1
  82. package/lib/model/csnRefs.js +322 -198
  83. package/lib/model/csnUtils.js +226 -370
  84. package/lib/model/enrichCsn.js +124 -69
  85. package/lib/model/revealInternalProperties.js +29 -7
  86. package/lib/model/sortViews.js +10 -2
  87. package/lib/modelCompare/compare.js +17 -12
  88. package/lib/optionProcessor.js +8 -3
  89. package/lib/render/.eslintrc.json +1 -2
  90. package/lib/render/DuplicateChecker.js +1 -1
  91. package/lib/render/manageConstraints.js +36 -33
  92. package/lib/render/toCdl.js +174 -275
  93. package/lib/render/toHdbcds.js +203 -122
  94. package/lib/render/toRename.js +7 -10
  95. package/lib/render/toSql.js +161 -82
  96. package/lib/render/utils/common.js +22 -8
  97. package/lib/render/utils/sql.js +10 -7
  98. package/lib/render/utils/stringEscapes.js +111 -0
  99. package/lib/sql-identifier.js +1 -1
  100. package/lib/transform/.eslintrc.json +5 -0
  101. package/lib/transform/braceExpression.js +4 -2
  102. package/lib/transform/db/.eslintrc.json +2 -0
  103. package/lib/transform/db/applyTransformations.js +212 -0
  104. package/lib/transform/db/assertUnique.js +1 -1
  105. package/lib/transform/db/associations.js +187 -0
  106. package/lib/transform/db/cdsPersistence.js +150 -0
  107. package/lib/transform/db/constraints.js +61 -56
  108. package/lib/transform/db/expansion.js +50 -29
  109. package/lib/transform/db/flattening.js +556 -106
  110. package/lib/transform/db/groupByOrderBy.js +3 -1
  111. package/lib/transform/db/temporal.js +236 -0
  112. package/lib/transform/db/transformExists.js +103 -28
  113. package/lib/transform/db/views.js +92 -44
  114. package/lib/transform/draft/.eslintrc.json +38 -0
  115. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  116. package/lib/transform/draft/odata.js +227 -0
  117. package/lib/transform/forHanaNew.js +98 -783
  118. package/lib/transform/forOdataNew.js +22 -175
  119. package/lib/transform/localized.js +36 -32
  120. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  121. package/lib/transform/odata/referenceFlattener.js +95 -89
  122. package/lib/transform/odata/structureFlattener.js +1 -1
  123. package/lib/transform/odata/toFinalBaseType.js +86 -12
  124. package/lib/transform/odata/typesExposure.js +5 -5
  125. package/lib/transform/odata/utils.js +2 -2
  126. package/lib/transform/transformUtilsNew.js +47 -33
  127. package/lib/transform/translateAssocsToJoins.js +13 -30
  128. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  129. package/lib/transform/universalCsn/coreComputed.js +170 -0
  130. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  131. package/lib/transform/universalCsn/utils.js +63 -0
  132. package/lib/utils/file.js +8 -3
  133. package/lib/utils/objectUtils.js +30 -0
  134. package/lib/utils/timetrace.js +8 -2
  135. package/package.json +1 -1
  136. package/share/messages/README.md +26 -0
  137. package/lib/compiler/definer.js +0 -2349
  138. package/lib/compiler/resolver.js +0 -2922
  139. package/lib/transform/db/helpers.js +0 -58
  140. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -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 } = 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);
@@ -26,7 +30,6 @@ function artifactsEnv( art ) {
26
30
  */
27
31
  // TODO: yes, this function will be renamed
28
32
  function fns( model ) {
29
- /** @type {CSN.Options} */
30
33
  const { options } = model;
31
34
  const {
32
35
  info, warning, error, message,
@@ -162,7 +165,7 @@ function fns( model ) {
162
165
  return;
163
166
 
164
167
  function checkConstRef( art ) {
165
- return ![ 'builtin', 'param' ].includes( art.kind );
168
+ return art.kind !== 'builtin' && art.kind !== 'param';
166
169
  }
167
170
 
168
171
  function checkIncludesRef( art ) {
@@ -173,16 +176,25 @@ function fns( model ) {
173
176
  return !(art.elements && !art.query && !art.type && !art.params);
174
177
  }
175
178
 
179
+ /**
180
+ * @returns {boolean|string}
181
+ */
176
182
  function checkTypeRef( art ) {
177
183
  if (art.kind === 'type' || art.kind === 'element')
178
184
  return false;
179
185
  return ![ 'entity', 'aspect', 'event' ].includes( art.kind ) || 'sloppy';
180
186
  }
181
187
 
188
+ /**
189
+ * @returns {boolean|string}
190
+ */
182
191
  function checkActionParamTypeRef( art ) {
183
192
  return !(art.kind === 'entity' && art._service) && checkTypeRef( art );
184
193
  }
185
194
 
195
+ /**
196
+ * @returns {boolean|string}
197
+ */
186
198
  function checkEventTypeRef( art ) {
187
199
  return art.kind !== 'event' && checkActionParamTypeRef( art );
188
200
  }
@@ -191,6 +203,9 @@ function fns( model ) {
191
203
  return art.kind !== 'entity';
192
204
  }
193
205
 
206
+ /**
207
+ * @returns {boolean|string}
208
+ */
194
209
  function checkTargetRef( art ) {
195
210
  if (art.kind === 'entity' || art.kind === 'aspect')
196
211
  return false;
@@ -254,9 +269,9 @@ function fns( model ) {
254
269
  return ref._artifact;
255
270
  if (!ref.path || ref.path.broken || !ref.path.length) {
256
271
  // incomplete type AST or empty env (already reported)
257
- return setLink( ref, undefined );
272
+ return setArtifactLink( ref, undefined );
258
273
  }
259
- 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;
260
275
 
261
276
  let spec = specExpected[expected];
262
277
  const { path } = ref;
@@ -269,7 +284,7 @@ function fns( model ) {
269
284
  if (!spec.escape) {
270
285
  error( 'ref-unexpected-scope', [ ref.location, user ], {},
271
286
  'Unexpected parameter reference' );
272
- return setLink( ref, null );
287
+ return setArtifactLink( ref, null );
273
288
  }
274
289
  spec = specExpected[spec.escape];
275
290
  // In queries and query entities, the first lexical search environment
@@ -287,7 +302,7 @@ function fns( model ) {
287
302
  const query = (spec.lexical === 'main') ? user._main : userQuery( user );
288
303
  // in path filter, just $magic (and $parameters)
289
304
  env = (spec.lexical === 'from') ? query._parent : query || user._main || user;
290
- // queries: first tabaliases, then $magic - value refs: first $self, then $magic
305
+ // queries: first table aliases, then $magic - value refs: first $self, then $magic
291
306
  if (!extDict && !spec.noExt) {
292
307
  // TODO: change to name restriction for $joins, not own environments
293
308
  extDict = query && spec.rootEnv !== 'elements' &&
@@ -303,18 +318,34 @@ function fns( model ) {
303
318
  ? getPathRoot( path, spec, user, {}, model[spec.global || 'definitions'] )
304
319
  : getPathRoot( path, spec, user, env, extDict, msgArt || 0 );
305
320
  if (!art) {
306
- return setLink( ref, art );
321
+ return setArtifactLink( ref, art );
307
322
  }
308
323
  else if (!spec.envFn && user._pathHead) {
309
324
  // eslint-disable-next-line no-empty
310
325
  }
311
326
  else if (art.kind === 'using') {
312
- art = model.definitions[art.name.absolute];
313
- if (!art)
314
- return setLink( ref, art );
315
- else if (art.$duplicates) // redefined art referenced by using proxy
316
- return setLink( ref, false );
317
- 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
+ }
318
349
  }
319
350
  else if (art.kind === 'mixin') {
320
351
  if (spec.noAliasOrMixin) {
@@ -322,14 +353,14 @@ function fns( model ) {
322
353
  signalNotFound( 'ref-rejected-on', [ head.location, user ], extDict && [ extDict ],
323
354
  { '#': 'mixin', id: head.id } );
324
355
  // also set link on head?
325
- return setLink( ref, false );
356
+ return setArtifactLink( ref, false );
326
357
  }
327
358
  // console.log(message( null, art.location, art, {}, 'Info','MIX').toString())
328
- setLink( head, art, '_navigation' );
359
+ setLink( head, '_navigation', art );
329
360
  }
330
361
  else if (art.kind === '$navElement') {
331
- setLink( head, art, '_navigation' );
332
- setLink( head, art._origin );
362
+ setLink( head, '_navigation', art );
363
+ setArtifactLink( head, art._origin );
333
364
  // TODO: set art?
334
365
  }
335
366
  else if (art.kind === '$tableAlias' || art.kind === '$self') {
@@ -338,13 +369,17 @@ function fns( model ) {
338
369
  signalNotFound( 'ref-rejected-on', [ head.location, user ], extDict && [ extDict ],
339
370
  { '#': 'alias', id: head.id } );
340
371
  // also set link on head?
341
- return setLink( ref, false );
372
+ return setArtifactLink( ref, false );
342
373
  }
343
- setLink( head, art, '_navigation' );
344
- 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
345
376
  // require('../model/revealInternalProperties').log(model, 'foo.bar.S.V1a')
346
377
  if (!art._origin)
347
- 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;
348
383
  }
349
384
 
350
385
  // how many path items are for artifacts (rest: elements)
@@ -354,20 +389,20 @@ function fns( model ) {
354
389
  // console.log(expected, ref.path.map(a=>a.id),artItemsCount)
355
390
  art = getPathItem( path, spec, user, artItemsCount, !spec.envFn && user._pathHead && art);
356
391
  if (!art)
357
- return setLink( ref, art );
392
+ return setArtifactLink( ref, art );
358
393
 
359
394
  if (art.$autoElement) {
360
395
  const { location } = path[path.length - 1];
361
396
  const step = { id: art.$autoElement, $inferred: '$autoElement', location };
362
397
  art = art.elements[step.id];
363
- setLink( step, art );
398
+ setArtifactLink( step, art );
364
399
  path.push( step );
365
400
  }
366
401
  if (spec.check) {
367
402
  const fail = spec.check( art, path );
368
403
  if (fail === true) {
369
404
  signalNotFound( spec.expectedMsgId, [ ref.location, user ], null );
370
- return setLink( ref, false );
405
+ return setArtifactLink( ref, false );
371
406
  }
372
407
  else if (fail) {
373
408
  signalNotFound( spec.sloppyMsgId, [ ref.location, user ], null );
@@ -399,7 +434,7 @@ function fns( model ) {
399
434
  !(env.$frontend && env.$frontend !== 'cdl'))
400
435
  deprecateSmart( ref, art, user );
401
436
  // TODO: follow FROM here, see csnRef - fromRef
402
- return setLink( ref, art );
437
+ return setArtifactLink( ref, art );
403
438
  }
404
439
 
405
440
  // Issue errors for "smart" element-in-artifact references
@@ -464,6 +499,8 @@ function fns( model ) {
464
499
  // TODO: not necessarily for explicit ON condition in expand
465
500
  VolatileFns.environment( user._pathHead ); // make sure _origin is set
466
501
  return user._pathHead._origin;
502
+ // const { _origin } = user._pathHead;
503
+ // return (_origin && _origin.kind === '$tableAlias') ? _origin._origin : _origin;
467
504
  }
468
505
  const head = path[0];
469
506
  if (!head || !head.id || !env)
@@ -490,7 +527,7 @@ function fns( model ) {
490
527
  const r = e[head.id];
491
528
  if (r) {
492
529
  if (Array.isArray(r)) { // redefinitions
493
- setLink( head, r );
530
+ setArtifactLink( head, r );
494
531
  return false;
495
532
  }
496
533
  // if (head.$delimited && r.kind !== '$tableAlias' && r.kind !== 'mixin')
@@ -501,18 +538,18 @@ function fns( model ) {
501
538
  { code: `$parameters.${ path[1].id }`, newcode: `:${ path[1].id }` },
502
539
  'Obsolete $(CODE) - replace by $(NEWCODE)' );
503
540
  // TODO: replace it in to-csn correspondingly
504
- return setLink( head, r );
541
+ return setArtifactLink( head, r );
505
542
  }
506
543
  }
507
544
  else if (r.kind === '$self') {
508
545
  // TODO: handle $delimited differently
509
546
  // TODO: $projection only if not delimited _and_ length > 1
510
- return setLink( head, r );
547
+ return setArtifactLink( head, r );
511
548
  }
512
549
  else if (r.kind !== '$tableAlias' || path.length > 1 || user.expand || user.inline) {
513
550
  // except "real" table aliases (not $self) with path len 1
514
551
  // TODO: $projection only if not delimited _and_ length > 1
515
- return setLink( head, r );
552
+ return setArtifactLink( head, r );
516
553
  }
517
554
  }
518
555
  }
@@ -529,11 +566,11 @@ function fns( model ) {
529
566
  'Ambiguous $(ID), replace by $(NAMES)' );
530
567
  }
531
568
  }
532
- setLink( head, r );
569
+ setArtifactLink( head, r );
533
570
  return false;
534
571
  }
535
572
  else if (r) {
536
- return setLink( head, r );
573
+ return setArtifactLink( head, r );
537
574
  }
538
575
  }
539
576
  if (spec.noMessage || msgArt === true && extDict === model.definitions)
@@ -587,7 +624,7 @@ function fns( model ) {
587
624
  signalNotFound( spec.undefinedArt || 'ref-undefined-art', [ head.location, user ],
588
625
  valid, { name: head.id } );
589
626
  }
590
- return setLink( head, null );
627
+ return setArtifactLink( head, null );
591
628
  }
592
629
 
593
630
  // Return artifact or element referred by path (array of ids) `tail`. The
@@ -595,6 +632,7 @@ function fns( model ) {
595
632
  // missing artifacts (as opposed to elements), provide the `head` (first
596
633
  // element item in the path)
597
634
  function getPathItem( path, spec, user, artItemsCount, headArt ) {
635
+ // let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
598
636
  let art = headArt;
599
637
  let nav = spec.assoc !== '$keys' && null; // false for '$keys'
600
638
  const last = path[path.length - 1];
@@ -611,29 +649,24 @@ function fns( model ) {
611
649
 
612
650
  const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : VolatileFns.environment;
613
651
  const env = fn( art, item.location, user, spec.assoc );
614
-
615
- // do not check any elements of the path, e.g. $session - but still don't return path-head
616
- if (art && art.$uncheckedElements) {
617
- if (env && env[item.id]) // something like $user.id/$user.locale
618
- return env[item.id];
619
-
620
- // $user.foo - build our own valid path step obj
621
- // Important: Don't directly modify item!
622
- const obj = {
623
- location: item.location,
624
- kind: 'builtin',
625
- name: { id: item.id, element: path.map(p => p.id).join('.') },
626
- };
627
- setLink(obj, art, '_parent');
628
- return obj;
652
+ const sub = setArtifactLink( item, env && env[item.id] );
653
+
654
+ if (!sub) {
655
+ // element was not found in environment
656
+ if (sub === 0)
657
+ return 0;
658
+ if (art.$uncheckedElements) { // magic variable / replacement variable
659
+ signalNotFound( 'ref-unknown-var', [ item.location, user ], [ env ],
660
+ { id: path.map(n => n.id).join('.') } );
661
+ }
662
+ else {
663
+ errorNotFound( item, env );
664
+ }
665
+ return null;
629
666
  }
630
-
631
- const sub = setLink( item, env && env[item.id] );
632
-
633
- if (!sub)
634
- return (sub === 0) ? 0 : errorNotFound( item, env );
635
- else if (Array.isArray(sub)) // redefinitions
667
+ else if (Array.isArray(sub)) { // redefinitions
636
668
  return false;
669
+ }
637
670
 
638
671
  if (nav) { // we have already "pseudo-followed" a managed association
639
672
  // We currently rely on the check that targetElement references do
@@ -678,7 +711,7 @@ function fns( model ) {
678
711
  if (node._artifact) {
679
712
  // set the original(!) foreign key for the assoc - the "right" ones
680
713
  // after rewriteKeys() is the one with the same name.id
681
- setLink( item, node._artifact, '_navigation' );
714
+ setLink( item, '_navigation', node._artifact );
682
715
  if (item === last)
683
716
  return;
684
717
  }
@@ -715,7 +748,7 @@ function fns( model ) {
715
748
  { param: 'Entity $(ART) has no parameter $(MEMBER)' } );
716
749
  }
717
750
  else {
718
- signalNotFound( 'ref-undefined-element', [ item.location, user ],
751
+ signalNotFound( spec.undefinedDef || 'ref-undefined-element', [ item.location, user ],
719
752
  [ env ], { art: searchName( art, item.id, 'element' ) } );
720
753
  }
721
754
  return null;
@@ -811,10 +844,10 @@ function fns( model ) {
811
844
  if (!(Array.isArray(annos)))
812
845
  annos = [ annos ];
813
846
  for (const a of annos) {
814
- setProp( a, '_block', block );
847
+ setLink( a, '_block', block );
815
848
  a.$priority = priority;
816
849
  if (construct !== art)
817
- dictAddArray( art, annoProp, a );
850
+ addAnnotation( art, annoProp, a );
818
851
  }
819
852
  }
820
853
  }
@@ -861,19 +894,21 @@ function fns( model ) {
861
894
  value.name && value.name.location ||
862
895
  value.path && value.path.location,
863
896
  };
864
- setProp( anno, '_block', block );
897
+ setLink( anno, '_block', block );
865
898
  // TODO: _parent, _main is set later (if we have ElementRef), or do we
866
899
  // set _artifact?
867
900
  anno.$priority = priority;
868
- dictAddArray( art, annoProp, anno );
901
+ addAnnotation( art, annoProp, anno );
869
902
  }
870
903
  }
871
904
  }
872
905
 
873
- // Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
874
- // locations):
875
- function pathName(path) {
876
- return (path.broken) ? '' : path.map( id => id.id ).join('.');
906
+ // Add annotation to definition - overwriting $inferred annos
907
+ function addAnnotation( art, annoProp, anno ) {
908
+ const old = art[annoProp];
909
+ if (old && old.$inferred)
910
+ delete art[annoProp];
911
+ dictAddArray( art, annoProp, anno );
877
912
  }
878
913
 
879
914
  module.exports = {