@sap/cds-compiler 4.2.4 → 4.3.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 (66) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +9 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +9 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +44 -39
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
@@ -10,16 +10,13 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const builtins = require('../compiler/builtins');
14
13
  const {
15
14
  forEachGeneric,
16
15
  forEachDefinition,
17
16
  forEachMember,
18
17
  forEachMemberRecursively,
19
- isBetaEnabled,
20
18
  } = require('../base/model');
21
19
  const { CompilerAssertion } = require('../base/error');
22
- const { pathName } = require('./utils');
23
20
  const { typeParameters } = require('./builtins');
24
21
 
25
22
  const $location = Symbol.for( 'cds.$location' );
@@ -94,8 +91,8 @@ function check( model ) {
94
91
  const isKey = parentProps.key?.val || elem.key?.val;
95
92
  const isVirtual = parentProps.virtual?.val || elem.virtual?.val;
96
93
  if (isKey && isVirtual) {
97
- error( 'def-unexpected-key', [ isKey.location, elem ],
98
- { '#': 'virtual', art: elem.name.element, prop: 'key' } );
94
+ error( 'def-unexpected-key', [ (parentProps.key || elem.key).location, elem ],
95
+ { '#': 'virtual', prop: 'key' } );
99
96
  }
100
97
  }
101
98
 
@@ -116,7 +113,7 @@ function check( model ) {
116
113
 
117
114
 
118
115
  function checkName( construct ) { // TODO: move to define.js
119
- if (model.options.$skipNameCheck)
116
+ if (model.options.$skipNameCheck || !construct._main)
120
117
  return;
121
118
  // TODO: Move a corrected version of this check to definer (but do not rely on it!):
122
119
  // The code below misses to consider CSN input!
@@ -463,15 +460,7 @@ function check( model ) {
463
460
  }
464
461
  }
465
462
  if (elem.default) {
466
- if (!isBetaEnabled( model.options, 'associationDefault' )) {
467
- error( 'type-unsupported-default', [ elem.default.location, elem ], {
468
- '#': isComposition( model, elem ) ? 'comp' : 'std',
469
- }, {
470
- std: 'Unsupported default value on an association',
471
- comp: 'Unsupported default value on a composition',
472
- } );
473
- }
474
- else if (elem.targetAspect || elem.on || fkCount !== 1) {
463
+ if (elem.targetAspect || elem.on || fkCount !== 1) {
475
464
  const variant = (elem.targetAspect && 'targetAspect') || (elem.on && 'onCond') || 'multi';
476
465
  error( 'type-unexpected-default', [ elem.default.location, elem ], {
477
466
  '#': variant, keyword: 'default', count: fkCount,
@@ -628,10 +617,9 @@ function check( model ) {
628
617
 
629
618
  function checkSelectItemValue( elem ) {
630
619
  checkExpressionAssociationUsage( elem.value, elem, false );
631
- // If a direct SQL-style cast() has no type, but type props, the compiler does not copy the type
632
- // props/does not use the cast(). To avoid duplicate messages, only run this check if there is
633
- // no explicit type, as otherwise we will check the cast() twice (once here, once via element).
634
- if (elem.value?.op?.val === 'cast' && !elem.type) {
620
+ // To avoid duplicate messages, only run this check if the type wasn't inferred from
621
+ // the cast, as otherwise we will check it twice (once here, once via element).
622
+ if (elem.value?.op?.val === 'cast' && elem.type?.$inferred !== 'cast') {
635
623
  requireExplicitTypeInSqlCast( elem.value, elem );
636
624
  checkTypeArguments( elem.value, elem );
637
625
  }
@@ -813,18 +801,24 @@ function check( model ) {
813
801
  // definitions from 'model'. Report errors on 'options.messages.
814
802
  //
815
803
  // TODO: rework completely!
804
+ // TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
816
805
 
817
806
  // Has been slightly adapted for model.vocabularies but comments need to be
818
807
  // adapted, etc.
819
808
  function checkAnnotationAssignment1( art, anno ) {
820
809
  // Sanity checks (ignore broken assignments)
821
- if (!anno.name?.path?.length)
810
+ if (!anno.name?.id)
822
811
  return;
812
+ // Just a little workaround to adapt to changed `name`s, not nice coding:
813
+ const hashIndex = anno.name.id.indexOf( '#' );
814
+ const path = (hashIndex > 0 ? anno.name.id.substring( 0, hashIndex ) : anno.name.id)
815
+ .split( '.' ).map( id => ({ id }) );
816
+
823
817
  // Annotation artifact for longest path step of annotation path
824
818
  let fromArtifact = null;
825
819
  let pathStepsFound = 0;
826
- for (let i = anno.name.path.length; i > 0; i--) {
827
- const absoluteName = anno.name.path.slice( 0, i ).map( path => path.id ).join( '.' );
820
+ for (let i = path.length; i > 0; i--) {
821
+ const absoluteName = path.slice( 0, i ).map( p => p.id ).join( '.' );
828
822
  if (model.vocabularies[absoluteName]) {
829
823
  fromArtifact = model.vocabularies[absoluteName];
830
824
  pathStepsFound = i;
@@ -837,7 +831,7 @@ function check( model ) {
837
831
  return;
838
832
  }
839
833
 
840
- const { artifact, endOfPath } = resolvePathFrom( anno.name.path.slice( pathStepsFound ),
834
+ const { artifact, endOfPath } = resolvePathFrom( path.slice( pathStepsFound ),
841
835
  fromArtifact );
842
836
 
843
837
  // Check what we actually want to check
@@ -859,7 +853,7 @@ function check( model ) {
859
853
  // Element must exist in annotation
860
854
  if (!elementDecl) {
861
855
  warning( null, [ anno.location || anno.name.location, art ],
862
- { name: pathName( anno.name.path ), anno: annoDecl.name.absolute },
856
+ { name: anno.name.id, anno: annoDecl.name.id },
863
857
  'Element $(NAME) not found for annotation $(ANNO)' );
864
858
  return;
865
859
  }
@@ -870,14 +864,14 @@ function check( model ) {
870
864
 
871
865
 
872
866
  // Must have literal or path unless it is a boolean
873
- if (!anno.literal && !anno.path && getFinalTypeNameOf( elementDecl ) !== 'cds.Boolean') {
874
- if (elementDecl.type?._artifact.name.absolute) {
867
+ if (!anno.literal && !anno.path && elementDecl._effectiveType?.category !== 'boolean') {
868
+ if (elementDecl.type?._artifact) {
875
869
  warning( 'anno-expecting-value', [ anno.location || anno.name.location, art ],
876
870
  { '#': 'type', type: elementDecl.type._artifact } );
877
871
  }
878
872
  else {
879
873
  warning( 'anno-expecting-value', [ anno.location || anno.name.location, art ],
880
- { '#': 'std', anno: anno.name.absolute } );
874
+ { '#': 'std', anno: anno.name.id } );
881
875
  }
882
876
 
883
877
  return;
@@ -896,7 +890,7 @@ function check( model ) {
896
890
  if (value.path)
897
891
  return;
898
892
 
899
- const anno = annoDef.name.absolute;
893
+ const anno = annoDef.name.id;
900
894
  const loc = [ value.location || value.name.location, art ];
901
895
 
902
896
  // Array expected?
@@ -925,47 +919,51 @@ function check( model ) {
925
919
 
926
920
  // Handle each (primitive) expected element type separately
927
921
  // TODO: Don't rely on name; use actual type
928
- const type = getFinalTypeNameOf( elementDecl );
929
- if (builtins.isStringTypeName( type )) {
922
+ const type = elementDecl._effectiveType;
923
+ if (!type)
924
+ return;
925
+ if (type.category === 'string') {
930
926
  if (value.literal !== 'string' && value.literal !== 'enum' &&
931
927
  !elementDecl._effectiveType.enum) {
932
928
  warning( null, loc, { type, anno },
933
929
  'A string value is required for type $(TYPE) for annotation $(ANNO)' );
934
930
  }
935
931
  }
936
- else if (builtins.isBinaryTypeName( type )) {
932
+ else if (type.category === 'binary') {
937
933
  if (value.literal !== 'string' && value.literal !== 'x') {
938
934
  warning( null, loc, { type, anno },
939
935
  'A hexadecimal string value is required for type $(TYPE) for annotation $(ANNO)' );
940
936
  }
941
937
  }
942
- else if (builtins.isNumericTypeName( type )) {
938
+ else if (type.category === 'decimal' || type.category === 'integer') {
943
939
  if (value.literal !== 'number' && value.literal !== 'enum' &&
944
940
  !elementDecl._effectiveType.enum) {
945
941
  warning( null, loc, { type, anno },
946
942
  'A numerical value is required for type $(TYPE) for annotation $(ANNO)' );
947
943
  }
948
944
  }
949
- else if (builtins.isDateOrTimeTypeName( type )) {
945
+ else if (type.category === 'dateTime') {
950
946
  if (value.literal !== 'date' && value.literal !== 'time' &&
951
947
  value.literal !== 'timestamp' && value.literal !== 'string') {
948
+ // Hm, actually date and time cannot be mixed
952
949
  warning( null, loc, { type, anno },
953
950
  // eslint-disable-next-line max-len
954
951
  'A date/time value or a string is required for type $(TYPE) for annotation $(ANNO)' );
955
952
  }
956
953
  }
957
- else if (builtins.isBooleanTypeName( type )) {
954
+ else if (type.category === 'boolean') {
958
955
  if (value.literal && value.literal !== 'boolean') {
959
956
  warning( null, loc, { type, anno },
960
957
  'A boolean value is required for type $(TYPE) for annotation $(ANNO)' );
961
958
  }
962
959
  }
963
- else if (builtins.isRelationTypeName( type ) || builtins.isGeoTypeName( type )) {
964
- warning( null, loc, { type, anno },
960
+ else if (type.target || type.category === 'geo') {
961
+ warning( null, loc, { type: (type.target ? 'cds.Association' : type), anno },
965
962
  'Type $(TYPE) can\'t be assigned a value for annotation $(ANNO)' );
963
+ // TODO: complain at definition instead
966
964
  }
967
- else if (!elementDecl._effectiveType.enum) {
968
- throw new CompilerAssertion(`Unknown primitive type name: ${ type }`);
965
+ else if (!type.enum) {
966
+ throw new CompilerAssertion(`Unknown primitive type name: ${ type.name.id }`);
969
967
  }
970
968
 
971
969
  // Check enums
@@ -1027,17 +1025,6 @@ function check( model ) {
1027
1025
  from._effectiveType?.elements || [];
1028
1026
  return resolvePathFrom( path.slice(1), nextStepEnv[path[0].id], result );
1029
1027
  }
1030
-
1031
- // TODO: remove
1032
- // Return the absolute name of the final type of 'node'. May return 'undefined'
1033
- // for anonymous types. DO NOT USE THIS function, it has several assumptions
1034
- // which are not necessarily true.
1035
- function getFinalTypeNameOf( node ) {
1036
- let type = node._effectiveType;
1037
- if (type.type)
1038
- type = type.type._artifact;
1039
- return type?.name?.absolute;
1040
- }
1041
1028
  }
1042
1029
 
1043
1030
  /**
@@ -124,6 +124,7 @@ const {
124
124
  forEachInOrder,
125
125
  forEachMember,
126
126
  } = require('../base/model');
127
+ const { weakLocation } = require('../base/location');
127
128
  const shuffleGen = require('../base/shuffle');
128
129
  const {
129
130
  dictAdd, dictAddArray, dictForEach, pushToDict,
@@ -135,7 +136,6 @@ const {
135
136
  storeExtension,
136
137
  dependsOnSilent,
137
138
  pathName,
138
- splitIntoPath,
139
139
  isDirectComposition,
140
140
  } = require('./utils');
141
141
  const { compareLayer } = require('./moduleLayers');
@@ -226,8 +226,12 @@ function define( model ) {
226
226
  if (!src.kind)
227
227
  src.kind = 'source';
228
228
 
229
- let namespace = src.namespace && src.namespace.path;
230
- let prefix = namespace ? `${ pathName( namespace ) }.` : '';
229
+ let { namespace } = src;
230
+ let prefix = '';
231
+ if (namespace?.path && !namespace.path.broken) {
232
+ namespace.id = pathName( namespace.path );
233
+ prefix = `${ namespace.id }.`;
234
+ }
231
235
  if (isInReservedNamespace( prefix )) {
232
236
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
233
237
  'The namespace $(NAME) is reserved for CDS builtins' );
@@ -240,12 +244,12 @@ function define( model ) {
240
244
  src.artifacts = shuffleDict( src.artifacts );
241
245
  addPathPrefixes( src.artifacts, prefix ); // before addUsing
242
246
  }
243
- else if (src.usings || src.namespace) {
247
+ else if (src.usings || namespace) {
244
248
  src.artifacts = Object.create( null );
245
249
  }
246
250
  if (src.usings)
247
251
  shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
248
- if (namespace)
252
+ if (namespace?.id) // successfully set a full name for namespace
249
253
  addNamespace( namespace, src );
250
254
  if (src.artifacts) { // addArtifact needs usings for context extensions
251
255
  src.artifacts = shuffleDict( src.artifacts );
@@ -253,7 +257,7 @@ function define( model ) {
253
257
  }
254
258
  }
255
259
  else if (src.definitions) { // CSN input
256
- prefix = '';
260
+ prefix = ''; // also for addVocabulary() below
257
261
  dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ) );
258
262
  }
259
263
  if (src.vocabularies) {
@@ -267,11 +271,8 @@ function define( model ) {
267
271
  }
268
272
 
269
273
  function addDefinition( art, block, prefix ) {
270
- if (!art.name.absolute) {
271
- // TODO: art.name.absolute = art.name.id || …
272
- art.name.absolute = (!art.name.path) ? art.name.id : prefix + pathName( art.name.path );
273
- }
274
- const { absolute } = art.name;
274
+ art.name.id ??= prefix + pathName( art.name.path );
275
+ const absolute = art.name.id;
275
276
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
276
277
  if (absolute === 'cds' || isInReservedNamespace( absolute )) {
277
278
  error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
@@ -299,24 +300,21 @@ function define( model ) {
299
300
  for (const name in artifacts) {
300
301
  const d = artifacts[name];
301
302
  const a = Array.isArray( d ) ? d[0] : d;
302
- if (!a.name.absolute)
303
- a.name.absolute = prefix + name;
303
+ a.name.id ??= prefix + pathName( a.name.path );
304
304
  const index = name.indexOf( '.' );
305
305
  if (index < 0)
306
306
  continue; // also for newly added (i.e. does not matter whether visited or not)
307
- const id = name.substring( 0, index );
308
- if (artifacts[id])
307
+ const using = name.substring( 0, index );
308
+ if (artifacts[using])
309
309
  continue;
310
310
  // TODO: enable optional locations
311
311
  const location = a.name.path && a.name.path[0].location || a.location;
312
- const absolute = prefix + id;
313
- artifacts[id] = {
312
+ const absolute = prefix + using;
313
+ artifacts[using] = {
314
314
  kind: 'using', // !, not namespace - we do not know artifact yet
315
- name: {
316
- id, absolute, location, $inferred: 'as',
317
- },
315
+ name: { id: using, location, $inferred: 'as' },
318
316
  // TODO: use global ref (in general - all uses of splitIntoPath)
319
- extern: { path: splitIntoPath( location, absolute ), location },
317
+ extern: { location, id: absolute },
320
318
  location,
321
319
  $inferred: 'path-prefix',
322
320
  };
@@ -342,37 +340,33 @@ function define( model ) {
342
340
  const { path } = decl.extern;
343
341
  if (path.broken || !path[0]) // syntax error
344
342
  return;
343
+ decl.extern.id = pathName( path );
345
344
  if (!decl.name)
346
345
  decl.name = { ...path[path.length - 1], $inferred: 'as' };
347
- decl.name.absolute = pathName( path );
348
346
  const name = decl.name.id;
349
347
  // TODO: check name: no "."
350
348
  const found = src.artifacts[name];
351
- if (found && found.$inferred === 'path-prefix' &&
352
- found.name.absolute === decl.name.absolute)
349
+ // a real `using` declaration is “nicer” than a compiler-generated one:
350
+ if (found && found.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
353
351
  src.artifacts[name] = decl;
354
352
  else
355
353
  dictAddArray( src.artifacts, name, decl );
356
354
  }
357
355
 
358
356
  // must be called after addUsing().
359
- function addNamespace( path, src ) {
360
- const absolute = pathName( path );
361
- if (path.broken) // parsing may have failed
362
- return;
357
+ function addNamespace( namespace, src ) {
363
358
  // create using for own namespace:
364
- const last = path[path.length - 1];
359
+ // TODO: should we really do that in v5? See also initNamespaceAndUsing().
360
+ const last = namespace.path[namespace.path.length - 1];
365
361
  const { id } = last;
366
362
  if (src.artifacts[id] || last.id.includes( '.' ))
367
363
  // not used as we have a definition/using with that name, or dotted last path id
368
364
  return;
369
365
  src.artifacts[id] = {
370
366
  kind: 'using',
371
- name: {
372
- id, absolute, location: last.location, $inferred: 'as',
373
- },
374
- extern: src.namespace,
375
- location: src.namespace.location,
367
+ name: { id, location: last.location, $inferred: 'as' },
368
+ extern: namespace,
369
+ location: namespace.location,
376
370
  $inferred: 'namespace',
377
371
  };
378
372
  }
@@ -381,7 +375,7 @@ function define( model ) {
381
375
  return;
382
376
  addDefinition( art, block, prefix );
383
377
  if (art.artifacts) {
384
- const p = `${ art.name.absolute }.`;
378
+ const p = `${ art.name.id }.`;
385
379
  // path prefixes (usings) must be added before extensions in artifacts:
386
380
  addPathPrefixes( art.artifacts, p );
387
381
  dictForEach( art.artifacts, a => addArtifact( a, art, p ) );
@@ -397,12 +391,12 @@ function define( model ) {
397
391
  if (!absolute) // broken path
398
392
  return;
399
393
  delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
400
- ext.name.absolute = absolute; // definition might not be there yet, no _artifact link
394
+ ext.name.id = absolute; // definition might not be there yet, no _artifact link
401
395
  const location = { file: '' }; // stupid required location
402
396
  const late = model.$collectedExtensions[absolute] ||
403
397
  (model.$collectedExtensions[absolute] = {
404
398
  kind: 'annotate',
405
- name: { absolute, location },
399
+ name: { id: absolute, location },
406
400
  $inferred: '',
407
401
  location,
408
402
  });
@@ -415,7 +409,7 @@ function define( model ) {
415
409
  model.$blocks = Object.create( null );
416
410
  // Set block number for debugging (--raw-output):
417
411
  // eslint-disable-next-line no-multi-assign
418
- ext.name.select = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
412
+ ext.$effectiveSeqNo = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
419
413
  // add "namespace" for the case that ext.artifacts is empty (TODO: later)
420
414
  // now add all definitions in ext.artifacts:
421
415
  const prefix = `${ absolute }.`;
@@ -449,13 +443,8 @@ function define( model ) {
449
443
  function addVocabulary( vocab, block, prefix ) {
450
444
  setLink( vocab, '_block', block );
451
445
  const { name } = vocab;
452
- if (!name.absolute) {
453
- // TODO: art.name.absolute = vocab.name.id ||
454
- vocab.name.absolute = (!vocab.name.path)
455
- ? vocab.name.id
456
- : prefix + pathName( vocab.name.path );
457
- }
458
- dictAdd( model.vocabularies, name.absolute, vocab );
446
+ name.id ??= prefix + pathName( name.path );
447
+ dictAdd( model.vocabularies, name.id, vocab );
459
448
  }
460
449
 
461
450
  /**
@@ -509,7 +498,8 @@ function define( model ) {
509
498
 
510
499
  // TODO: message ids
511
500
  function checkRedefinition( art ) {
512
- if (!art.$duplicates || art.$errorReported === 'syntax-duplicate-extend' ||
501
+ if (!art.$duplicates || !art.name.id ||
502
+ art.$errorReported === 'syntax-duplicate-extend' ||
513
503
  art.$errorReported === 'syntax-duplicate-annotate')
514
504
  return;
515
505
  if (art._main) {
@@ -522,7 +512,7 @@ function define( model ) {
522
512
  // TODO: better messages with definitions with the same name as builtin,
523
513
  // especially if there is just one
524
514
  error( 'duplicate-definition', [ art.name.location, art ], {
525
- name: art.name.absolute,
515
+ name: art.name.id,
526
516
  '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
527
517
  } );
528
518
  }
@@ -533,24 +523,20 @@ function define( model ) {
533
523
  return;
534
524
  if (src.namespace) {
535
525
  const decl = src.namespace;
536
- const { path } = decl;
537
- if (path.broken) // parsing may have failed
526
+ if (!decl.id) // parsing may have failed
538
527
  return;
539
- const { id } = path[path.length - 1];
540
- const absolute = pathName( path );
541
- if (!model.definitions[absolute]) {
542
- // TODO: do we really need this namespace entry - try without (msg change)
543
- const location = path.location || decl.location;
528
+ if (!model.definitions[decl.id]) {
544
529
  // TODO: make it possible to have no location
545
- const ns = { kind: 'namespace', name: { absolute, location }, location };
546
- model.definitions[absolute] = ns;
530
+ const ns = { kind: 'namespace', name: decl, location: decl.location };
531
+ model.definitions[decl.id] = ns;
547
532
  initArtifactParentLink( ns, model.definitions );
548
533
  }
549
- const builtin = model.$builtins[id];
534
+ const last = decl.path[decl.path.length - 1];
535
+ const builtin = model.$builtins[last.id];
550
536
  if (builtin && !builtin.internal &&
551
- src.artifacts[id] && src.artifacts[id].extern === decl) {
537
+ src.artifacts[last.id] && src.artifacts[last.id].extern === decl) {
552
538
  warning( 'ref-shadowed-builtin', [ decl.location, null ], // no home artifact
553
- { id, art: absolute, code: `using ${ builtin.name.absolute };` },
539
+ { id: last.id, art: decl.id, code: `using ${ builtin.name.id };` },
554
540
  '$(ID) now refers to $(ART) - consider $(CODE)' );
555
541
  }
556
542
  // setArtifactLink( decl, model.definitions[absolute] ); // TODO: necessary?
@@ -602,53 +588,51 @@ function define( model ) {
602
588
  initMembers( art, art, block );
603
589
  }
604
590
 
605
- function initArtifactParentLink( art, definitions ) {
591
+ function initArtifactParentLink( art, definitions, path, pathIndex ) {
606
592
  setLink( art, '_parent', null );
607
- const { absolute } = art.name;
608
- const dot = absolute.lastIndexOf( '.' );
593
+ const { id } = art.name;
594
+ const dot = id.lastIndexOf( '.' );
609
595
  if (dot < 0)
610
596
  return;
611
- art.name.id = absolute.substring( dot + 1 ); // XSN TODO: remove name.id for artifacts
612
- const prefix = absolute.substring( 0, dot );
597
+ const prefix = id.substring( 0, dot );
613
598
  let parent = definitions[prefix];
614
599
  if (!parent) {
615
- const { location } = art.name; // TODO: make it possible to have no location
616
- parent = { kind: 'namespace', name: { absolute: prefix, location }, location };
600
+ path ??= art.name.path;
601
+ pathIndex ??= path?.length - 1;
602
+ const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
603
+ const location = weakLocation( pathItemOrName.location );
604
+ parent = { kind: 'namespace', name: { id: prefix, location }, location };
617
605
  definitions[prefix] = parent;
618
- initArtifactParentLink( parent, definitions );
606
+ initArtifactParentLink( parent, definitions, path, pathIndex );
619
607
  }
620
608
  setLink( art, '_parent', parent );
621
609
  if (!parent._subArtifacts)
622
610
  setLink( parent, '_subArtifacts', Object.create( null ) );
623
611
  if (art.$duplicates !== true) // no redef or "first def"
624
- parent._subArtifacts[absolute.substring( dot + 1 )] = art; // not dictAdd()
612
+ parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
625
613
  }
626
614
 
627
615
  // Init special things: -------------------------------------------------------
628
616
 
629
617
  function initDollarSelf( art ) {
630
- const selfname = '$self';
631
618
  // TODO: use setMemberParent() ?
632
- const name = art.name || art._outer.name;
633
619
  const self = {
634
- name: { id: selfname, alias: selfname, absolute: name.absolute },
620
+ name: { id: '$self', location: art.location },
635
621
  kind: '$self',
636
622
  location: art.location,
637
623
  };
638
- if (name.element) // $self for anonymous aspect
639
- self.name.element = name.element;
640
624
  setLink( self, '_parent', art );
641
625
  setLink( self, '_main', art ); // used on main artifact
642
626
  setLink( self, '_origin', art );
643
627
  art.$tableAliases = Object.create( null );
644
- art.$tableAliases[selfname] = self;
628
+ art.$tableAliases.$self = self;
645
629
  }
646
630
 
647
631
  function initDollarParameters( art ) {
648
- // TODO: remove $parameters in v4?
632
+ // TODO: remove $parameters in v5?
649
633
  // TODO: use setMemberParent() ?
650
634
  const parameters = {
651
- name: { id: '$parameters', param: '$parameters', absolute: art.name.absolute },
635
+ name: { id: '$parameters' },
652
636
  kind: '$parameters',
653
637
  location: art.location,
654
638
  };
@@ -687,7 +671,7 @@ function define( model ) {
687
671
  initMixins( query, art );
688
672
  if (!query.$tableAliases.$self) { // same as $projection
689
673
  const self = {
690
- name: { alias: '$self', query: query.name.select, absolute: art.name.absolute },
674
+ name: { id: '$self', location: query.location },
691
675
  kind: '$self',
692
676
  location: query.location,
693
677
  };
@@ -727,10 +711,9 @@ function define( model ) {
727
711
  (art.kind === '$tableAlias' ? art._parent._$next : art ) );
728
712
  setLink( query, '_block', art._block );
729
713
  query.kind = 'select';
730
- query.name = { location: query.location };
731
- setMemberParent( query, main.$queries.length + 1, main );
714
+ query.name = { location: query.location, id: main.$queries.length + 1 };
715
+ setMemberParent( query, null, main );
732
716
  // console.log(art.kind,art.name,query.name,query._$next.name)
733
- // if (query.name.query === 1 && query.name.absolute === 'S') throw new CompilerAssertion();
734
717
  main.$queries.push( query );
735
718
  setLink( query, '_parent', art ); // _parent should point to alias/main/query
736
719
  query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
@@ -794,13 +777,13 @@ function define( model ) {
794
777
  } );
795
778
  }
796
779
  if (table.on) { // after processing args to get the $tableAliases
797
- setMemberParent( table, query.name.select, query ); // sets _parent,_main
780
+ setMemberParent( table, query.name.id, query ); // sets _parent,_main
798
781
  initSubQuery( table ); // init sub queries in ON
799
782
  const aliases = Object.keys( table.$tableAliases || {} );
800
783
  // Use first tabalias name on the right side of the join to name the
801
784
  // (internal) query, should only be relevant for --raw-output, not for
802
785
  // user messages or references - TODO: correct if join on left?
803
- table.name.param = aliases[1] || aliases[0] || '<unknown>';
786
+ table.name.id = aliases[1] || aliases[0] || '<unknown>';
804
787
  setLink( table, '_user', query ); // TODO: do not set kind/name
805
788
  setLink( table, '_$next', query._$next );
806
789
  // TODO: probably set this to query if we switch to name restriction in JOIN
@@ -815,6 +798,8 @@ function define( model ) {
815
798
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
816
799
  if (tableAlias.$inferred === '$internal') {
817
800
  const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
801
+ // TODO: the semanticLoc query is not initialized yet, and thus cannot
802
+ // be used here
818
803
  error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
819
804
  { '#': 'duplicate', code: 'as ‹alias›' } );
820
805
  }
@@ -878,7 +863,6 @@ function define( model ) {
878
863
  const mixin = query.mixin[name];
879
864
  if (!(mixin.$duplicates)) {
880
865
  setMemberParent( mixin, name, query );
881
- mixin.name.alias = mixin.name.id;
882
866
  setLink( mixin, '_block', art._block );
883
867
  // TODO: do some initMembers() ? If people had annotation
884
868
  // assignments on the mixin... (also for future mixin definitions
@@ -1121,12 +1105,7 @@ function define( model ) {
1121
1105
  // another entity - might induce auto-redirection):
1122
1106
  if (inEntity && !targetAspect.elements.up_) {
1123
1107
  const up = {
1124
- name: {
1125
- id: 'up_',
1126
- alias: 'up_',
1127
- element: obj.name.element,
1128
- absolute: obj.name.absolute,
1129
- },
1108
+ name: { id: 'up_' },
1130
1109
  kind: '$navElement',
1131
1110
  location: obj.location,
1132
1111
  };
@@ -1200,13 +1179,14 @@ function define( model ) {
1200
1179
  if (boundSelfParamType === true) { // first try
1201
1180
  const def = model.definitions.$self;
1202
1181
  if (def) {
1203
- // TODO v4: bring this always, probably even as error
1182
+ // TODO v5: bring this always, probably even as error
1204
1183
  warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
1205
1184
  'Do not use $(NAME) as name for an artifact definition' );
1206
1185
  boundSelfParamType = false;
1207
1186
  return;
1208
1187
  }
1209
- boundSelfParamType = { name: { absolute: '$self' } };
1188
+ const location = { file: '' };
1189
+ boundSelfParamType = { name: { id: '$self', location }, location };
1210
1190
  }
1211
1191
  const first = params[Object.keys( params )[0] || ''];
1212
1192
  const type = first?.type || first?.items?.type; // this sequence = no derived type