sysml-validate 0.15.4 → 0.15.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/out/main.js CHANGED
@@ -58670,7 +58670,22 @@ var DIAGNOSTIC_MESSAGES = {
58670
58670
  // REQ-007 — SysML v1 → v2 migration guardrail.
58671
58671
  MIG001_V1_KEYWORD: (keyword, v2, note) => `'${keyword}' is a SysML v1 construct, not a SysML v2 keyword. ${note} Use '${v2}'.`,
58672
58672
  MIG002_V1_STEREOTYPE_KNOWN: (stereotype, v2, note) => `SysML v2 has no stereotypes. ${note} Replace \xAB${stereotype}\xBB with '${v2}'.`,
58673
- MIG002_V1_STEREOTYPE_UNKNOWN: (stereotype) => `SysML v2 has no \xAB${stereotype}\xBB stereotype. Model it as a 'metadata def' applied with '@', or use the matching v2 definition keyword.`
58673
+ MIG002_V1_STEREOTYPE_UNKNOWN: (stereotype) => `SysML v2 has no \xAB${stereotype}\xBB stereotype. Model it as a 'metadata def' applied with '@', or use the matching v2 definition keyword.`,
58674
+ // REQ-390 — issue #104. Whole-model type conformance. Each message names the
58675
+ // two types it compared and the relationship that required them to agree, so
58676
+ // the reader can see WHICH end to change without opening the other file.
58677
+ SSM017_REDEFINITION_TYPE: (feature, subType, superType) => `Type '${subType}' does not specialize '${superType}', the type of the redefined feature '${feature}'. A redefinition narrows a feature \u2014 it cannot re-type it to something unrelated.`,
58678
+ SSM018_CONNECTOR_END_TYPES: (keyword, sourceType, targetType) => `'${keyword}' binds features of unrelated types '${sourceType}' and '${targetType}'. A binding asserts its two ends hold the same value, so their types must be related \u2014 one specializing the other, or conjugates ('~').`,
58679
+ SSM019_VALUE_TYPE: (typeName, expected, actual) => `A ${actual} literal is not a value of '${typeName}', which accepts ${expected === "number" ? "a numeric" : `a ${expected}`} value.`,
58680
+ SSM020_FLOW_PAYLOAD: (payload, endKind, endType) => `Payload '${payload}' is unrelated to the flow's ${endKind} type '${endType}'. A flow carries its payload between features that can hold it.`,
58681
+ SSM021_ABSTRACT_TYPE: (usage, typeName) => `'${usage}' is typed only by the abstract definition '${typeName}'. An abstract definition has no complete form \u2014 give the usage a concrete type, subset a concrete usage, or mark it 'abstract' too.`,
58682
+ SSM022_DEFINITION_DOMAIN: (keyword, typeName, typeKeyword, expected) => `A '${keyword}' usage cannot be defined by '${typeName}', which is a '${typeKeyword} def'. Type it by ${expected}.`,
58683
+ SSM023_GUARD_NOT_BOOLEAN: (construct) => `A ${construct} guard must be a Boolean expression \u2014 this one cannot evaluate to true or false.`,
58684
+ SSM024_ASSIGNMENT_VALUE: (target, typeName, expected, actual) => `'${target}' is a '${typeName}', which holds ${expected === "number" ? "a numeric" : `a ${expected}`} value \u2014 the assigned expression is ${actual === "number" ? "numeric" : actual}.`,
58685
+ SSM025_ACTION_PARAMETER_COUNT: (usage, declared, typeName, available) => `'${usage}' declares ${declared} directed parameter${declared === 1 ? "" : "s"} but its definition '${typeName}' has ${available}. An action usage's parameters correspond positionally to its definition's, so there is no ${available === 0 ? "parameter" : `${available + 1}th parameter`} to correspond to.`,
58686
+ SSM025_ACTION_PARAMETER_DIRECTION: (position, mine, typeName, theirs) => `Parameter ${position} is '${mine}' but the corresponding parameter of '${typeName}' is '${theirs}'. Positional correspondence redefines that parameter, and a redefinition cannot reverse a parameter's direction.`,
58687
+ SSM026_REDEFINITION_DIRECTION: (feature, mine, theirs) => `Direction '${mine}' reverses the redefined feature '${feature}', which is '${theirs}'. A redefinition refines a feature \u2014 it cannot turn an input into an output.`,
58688
+ SSM027_INTERFACE_END_TYPE: (position, endType, definition, expected) => `End ${position} is a '${endType}', but '${definition}' declares that end as '${expected}'. An interface usage connects through the ends its definition declares.`
58674
58689
  };
58675
58690
  var KEYWORD_TOOLTIPS = {
58676
58691
  // Package / namespace
@@ -58934,7 +58949,9 @@ var SysmlIndexManager = class extends DefaultIndexManager {
58934
58949
  selectionSegment: symbol.selectionSegment,
58935
58950
  // REQ-068 — preserve declared visibility for wildcard re-export.
58936
58951
  ...symbol.isPrivate ? { isPrivate: true } : {},
58937
- ...symbol.visibility ? { visibility: symbol.visibility } : {}
58952
+ ...symbol.visibility ? { visibility: symbol.visibility } : {},
58953
+ // REQ-242 — issue #103 — keeps type completion to definitions.
58954
+ ...symbol.isUsage ? { isUsage: true } : {}
58938
58955
  };
58939
58956
  }
58940
58957
  };
@@ -59133,6 +59150,46 @@ var FeaturePathResolver = class {
59133
59150
  isPathExpr(node) {
59134
59151
  return isPathExpr(node);
59135
59152
  }
59153
+ // REQ-242 — issue #103: the members reachable through a path the user is
59154
+ // still typing (`engine.power.`). Completion cannot go through `resolve`,
59155
+ // which needs a parsed `PathExpr` with a CST leaf per segment; the text
59156
+ // after the last separator is not part of any node yet. The resolution
59157
+ // rules are otherwise identical, so the same root/child walk answers it —
59158
+ // which is what makes member completion see cross-file and inherited
59159
+ // members that a document-local name search cannot reach.
59160
+ membersForPath(context, steps) {
59161
+ if (steps.length === 0)
59162
+ return [];
59163
+ const root3 = ast_utils_exports.getDocument(context).parseResult.value;
59164
+ const snapshot = this.snapshot(root3);
59165
+ let current2 = this.resolveRoot(context, steps[0].text, snapshot);
59166
+ for (let index = 1; index < steps.length; index += 1) {
59167
+ if (!current2.node && !current2.description)
59168
+ return [];
59169
+ current2 = this.resolveChild(current2, steps[index].text, steps, index, snapshot);
59170
+ }
59171
+ const owner = this.followAlias(current2, snapshot);
59172
+ const node = owner.node ?? this.nodeOf(owner.description);
59173
+ return node ? this.visibleMembers(node, snapshot) : [];
59174
+ }
59175
+ // REQ-242 — issue #103: an element's own named children plus everything it
59176
+ // inherits, following typing (`part p : Engine`) and every specialization
59177
+ // relationship transitively. A usage carries almost no members of its own;
59178
+ // essentially all of them come from the type it is defined by and that
59179
+ // type's supertypes.
59180
+ visibleMembers(node, snapshot = this.snapshot(ast_utils_exports.getDocument(node).parseResult.value)) {
59181
+ const result = [];
59182
+ const seen = /* @__PURE__ */ new Set();
59183
+ for (const owner of [node, ...this.typedAndSpecializedOwners(node, snapshot)]) {
59184
+ for (const child of directNamedChildren(owner)) {
59185
+ if (seen.has(child))
59186
+ continue;
59187
+ seen.add(child);
59188
+ result.push(child);
59189
+ }
59190
+ }
59191
+ return result;
59192
+ }
59136
59193
  segmentsOf(node) {
59137
59194
  const cst = node.$cstNode;
59138
59195
  if (!cst)
@@ -59422,6 +59479,282 @@ function isNamespaceOnlyDecl(node) {
59422
59479
  return node.isDef === true;
59423
59480
  }
59424
59481
 
59482
+ // ../language-server/out/src/services/conformance.js
59483
+ var SPECIALIZATION_KINDS2 = /* @__PURE__ */ new Set([
59484
+ ":>",
59485
+ ":>>",
59486
+ "specializes",
59487
+ "subsets",
59488
+ "redefines"
59489
+ ]);
59490
+ var COMPOSITION_KINDS = /* @__PURE__ */ new Set(["unions", "intersects", "differences"]);
59491
+ var MAX_CLOSURE_DEPTH = 32;
59492
+ function simpleTypeName(name) {
59493
+ const last2 = name.trim().split(/::|\./u).pop() ?? name.trim();
59494
+ return last2.replace(/^'(.*)'$/u, "$1");
59495
+ }
59496
+ function declaredTypesOf(node) {
59497
+ const decl = node;
59498
+ const out = [];
59499
+ for (const typing of [decl.typing, ...decl.moreTypings ?? []]) {
59500
+ if (!typing)
59501
+ continue;
59502
+ const conjugated = typing.conjugate === true;
59503
+ for (const ref of [typing.type, ...typing.moreTypes ?? []]) {
59504
+ const text = ref?.$refText?.trim();
59505
+ if (text)
59506
+ out.push({ text, simple: simpleTypeName(text), conjugated });
59507
+ }
59508
+ }
59509
+ return out;
59510
+ }
59511
+ function specializedNamesOf(node) {
59512
+ const decl = node;
59513
+ const out = [];
59514
+ for (const rel of [...decl.preRelationships ?? [], ...decl.relationships ?? []]) {
59515
+ if (!rel.kind || !SPECIALIZATION_KINDS2.has(rel.kind))
59516
+ continue;
59517
+ for (const target of rel.targets ?? []) {
59518
+ const text = target.trim();
59519
+ if (text)
59520
+ out.push(text);
59521
+ }
59522
+ }
59523
+ return out;
59524
+ }
59525
+ function compositionGroupsOf(node) {
59526
+ const decl = node;
59527
+ const out = [];
59528
+ for (const rel of [...decl.preRelationships ?? [], ...decl.relationships ?? []]) {
59529
+ if (!rel.kind || !COMPOSITION_KINDS.has(rel.kind))
59530
+ continue;
59531
+ const targets = (rel.targets ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
59532
+ if (targets.length > 0)
59533
+ out.push({ kind: rel.kind, targets });
59534
+ }
59535
+ return out;
59536
+ }
59537
+ function isAbstractDecl(node) {
59538
+ return (node.modifiers ?? []).includes("abstract");
59539
+ }
59540
+ var SCALAR_ROOT_KINDS = {
59541
+ String: "string",
59542
+ Boolean: "boolean",
59543
+ NumericalValue: "number",
59544
+ Number: "number",
59545
+ Integer: "number",
59546
+ Rational: "number",
59547
+ Real: "number",
59548
+ Complex: "number",
59549
+ Natural: "number",
59550
+ Positive: "number",
59551
+ Cardinal: "number"
59552
+ };
59553
+ function literalKindOf(node) {
59554
+ if (!node)
59555
+ return void 0;
59556
+ switch (node.$type) {
59557
+ case "LiteralStr":
59558
+ return "string";
59559
+ case "LiteralBool":
59560
+ return "boolean";
59561
+ case "NumericPrimary":
59562
+ return "number";
59563
+ // A signed literal is still a literal of the same kind.
59564
+ case "UnaryMinusExpr":
59565
+ case "UnaryPlusExpr":
59566
+ return literalKindOf(node.operand);
59567
+ // A single-item parenthesis carries its item's kind through.
59568
+ case "ParenExpr": {
59569
+ const items = node.items ?? [];
59570
+ return items.length === 1 ? literalKindOf(items[0]) : void 0;
59571
+ }
59572
+ default:
59573
+ return void 0;
59574
+ }
59575
+ }
59576
+ var BOOLEAN_RESULT_OPS = /* @__PURE__ */ new Set([
59577
+ "and",
59578
+ "&",
59579
+ "or",
59580
+ "|",
59581
+ "xor",
59582
+ "implies",
59583
+ "==",
59584
+ "!=",
59585
+ "===",
59586
+ "!==",
59587
+ "<",
59588
+ "<=",
59589
+ ">",
59590
+ ">=",
59591
+ "hastype",
59592
+ "istype",
59593
+ "@",
59594
+ "@@"
59595
+ ]);
59596
+ var NUMERIC_RESULT_OPS = /* @__PURE__ */ new Set(["-", "*", "/", "%", "**", "^"]);
59597
+ function expressionKindOf(node) {
59598
+ if (!node)
59599
+ return void 0;
59600
+ const literal = literalKindOf(node);
59601
+ if (literal)
59602
+ return literal;
59603
+ switch (node.$type) {
59604
+ case "NotExpr":
59605
+ case "QuantifierExpr":
59606
+ case "SelfClassifyExpr":
59607
+ return "boolean";
59608
+ case "ClassifyOp": {
59609
+ const op = node.op;
59610
+ return op && BOOLEAN_RESULT_OPS.has(op) ? "boolean" : void 0;
59611
+ }
59612
+ case "ParenExpr": {
59613
+ const items = node.items ?? [];
59614
+ return items.length === 1 ? expressionKindOf(items[0]) : void 0;
59615
+ }
59616
+ case "UnaryMinusExpr":
59617
+ case "UnaryPlusExpr": {
59618
+ return expressionKindOf(node.operand) === "number" ? "number" : void 0;
59619
+ }
59620
+ case "IfExpr": {
59621
+ const branch = node;
59622
+ const left = expressionKindOf(branch.thenExpr ?? branch.then);
59623
+ const right = expressionKindOf(branch.elseExpr ?? branch.else);
59624
+ return left && left === right ? left : void 0;
59625
+ }
59626
+ case "BinExpr": {
59627
+ const bin = node;
59628
+ const op = bin.op;
59629
+ if (!op)
59630
+ return void 0;
59631
+ if (BOOLEAN_RESULT_OPS.has(op))
59632
+ return "boolean";
59633
+ if (NUMERIC_RESULT_OPS.has(op))
59634
+ return "number";
59635
+ const left = expressionKindOf(bin.left);
59636
+ return (op === "+" || op === "??") && left && left === expressionKindOf(bin.right) ? left : void 0;
59637
+ }
59638
+ default:
59639
+ return void 0;
59640
+ }
59641
+ }
59642
+ var ConformanceModel = class {
59643
+ resolve;
59644
+ closures = /* @__PURE__ */ new Map();
59645
+ /**
59646
+ * @param resolve A written type name → its declaration, or `undefined` when
59647
+ * the name is absent, ambiguous, or its document is not loaded. The
59648
+ * validator supplies its `resolveUnique`, so an ambiguous name is treated
59649
+ * exactly as an unreadable one: it makes the walk incomplete rather than
59650
+ * resolving to an arbitrary same-named element.
59651
+ */
59652
+ constructor(resolve7) {
59653
+ this.resolve = resolve7;
59654
+ }
59655
+ /** The transitive supertype names of the type written as `name`. */
59656
+ closureOf(name) {
59657
+ return this.walk(name, /* @__PURE__ */ new Set());
59658
+ }
59659
+ // The closure is keyed by SIMPLE name throughout: a supertype is written
59660
+ // qualified in one file and bare in the next, and both spell the same type.
59661
+ // Two unrelated types that share a simple name can therefore look related —
59662
+ // which only ever makes a check stay silent, never fire.
59663
+ walk(name, visiting) {
59664
+ const key = name.trim();
59665
+ const simple = simpleTypeName(key);
59666
+ const cached = this.closures.get(key);
59667
+ if (cached)
59668
+ return cached;
59669
+ if (visiting.has(key) || visiting.size >= MAX_CLOSURE_DEPTH) {
59670
+ return { names: /* @__PURE__ */ new Set([simple]), complete: false };
59671
+ }
59672
+ const node = this.resolve(key);
59673
+ if (!node)
59674
+ return { names: /* @__PURE__ */ new Set([simple]), complete: false };
59675
+ visiting.add(key);
59676
+ const names = /* @__PURE__ */ new Set([simple]);
59677
+ let complete = true;
59678
+ const absorb = (closure3) => {
59679
+ for (const each of closure3.names)
59680
+ names.add(each);
59681
+ complete &&= closure3.complete;
59682
+ };
59683
+ for (const supertype of [...declaredTypesOf(node).map((t) => t.text), ...specializedNamesOf(node)]) {
59684
+ absorb(this.walk(supertype, visiting));
59685
+ }
59686
+ for (const group of compositionGroupsOf(node)) {
59687
+ if (group.kind === "intersects") {
59688
+ for (const operand of group.targets)
59689
+ absorb(this.walk(operand, visiting));
59690
+ } else if (group.kind === "differences") {
59691
+ absorb(this.walk(group.targets[0], visiting));
59692
+ } else {
59693
+ let shared;
59694
+ for (const operand of group.targets) {
59695
+ const closure3 = this.walk(operand, visiting);
59696
+ complete &&= closure3.complete;
59697
+ shared = shared === void 0 ? new Set(closure3.names) : new Set([...shared].filter((each) => closure3.names.has(each)));
59698
+ }
59699
+ for (const each of shared ?? [])
59700
+ names.add(each);
59701
+ }
59702
+ }
59703
+ visiting.delete(key);
59704
+ const closure2 = { names, complete };
59705
+ this.closures.set(key, closure2);
59706
+ return closure2;
59707
+ }
59708
+ /** Does the type written as `subName` specialize the type written as `superName`? */
59709
+ conforms(subName, superName) {
59710
+ const target = simpleTypeName(superName);
59711
+ const closure2 = this.closureOf(subName);
59712
+ if (closure2.names.has(target))
59713
+ return "conforms";
59714
+ return closure2.complete ? "unrelated" : "unknown";
59715
+ }
59716
+ /** Are two written types related in EITHER direction? Used where the model
59717
+ * states an equality (a binding, a flow payload) rather than a refinement. */
59718
+ compatible(a2, b) {
59719
+ const forward = this.conforms(a2, b);
59720
+ if (forward === "conforms")
59721
+ return "conforms";
59722
+ const backward = this.conforms(b, a2);
59723
+ if (backward === "conforms")
59724
+ return "conforms";
59725
+ if (forward === "unknown" || backward === "unknown")
59726
+ return "unknown";
59727
+ return "unrelated";
59728
+ }
59729
+ /** The declaration a written type name names, when it is readable. */
59730
+ declarationOf(name) {
59731
+ return this.resolve(name);
59732
+ }
59733
+ /**
59734
+ * The literal shape a declared type accepts, or `undefined` when the model
59735
+ * does not say. Two independent sources agree on the answer:
59736
+ * • the specialization closure reaching one of the ScalarValues roots, and
59737
+ * • the precomputed dimension table, which knows every `*Value` quantity of
59738
+ * the OMG `Quantities and Units` library is numeric — so `MassValue` is
59739
+ * decided without parsing a single library file.
59740
+ * A closure that reaches two DIFFERENT roots is a model defect of its own
59741
+ * and is reported as undecided here rather than guessed.
59742
+ */
59743
+ literalKindAccepted(typeName) {
59744
+ const closure2 = this.closureOf(typeName);
59745
+ let kind;
59746
+ for (const name of closure2.names) {
59747
+ const root3 = SCALAR_ROOT_KINDS[name] ?? (quantityDimension(name) ? "number" : void 0);
59748
+ if (!root3)
59749
+ continue;
59750
+ if (kind && kind !== root3)
59751
+ return void 0;
59752
+ kind = root3;
59753
+ }
59754
+ return kind;
59755
+ }
59756
+ };
59757
+
59425
59758
  // ../language-server/out/src/services/validator.js
59426
59759
  var severityOverrides = {};
59427
59760
  function setSeverityOverrides(overrides) {
@@ -59494,7 +59827,7 @@ function maskNonCode(text) {
59494
59827
  }
59495
59828
  return out.join("");
59496
59829
  }
59497
- var SPECIALIZATION_KINDS2 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
59830
+ var SPECIALIZATION_KINDS3 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
59498
59831
  var CONSTRAINT_SIDE_EFFECT_TYPES = /* @__PURE__ */ new Set([
59499
59832
  "AssignNode",
59500
59833
  "SendNode",
@@ -60737,6 +61070,336 @@ ${baseIndent}}`;
60737
61070
  this.checkInverseOfTargets(decl, index, accept);
60738
61071
  this.checkTypeComposition(decl, index, accept);
60739
61072
  }
61073
+ this.checkTypeConformance(decls, index, accept);
61074
+ }
61075
+ // ══════════════════════════════════════════════════════════════════════
61076
+ // REQ-390 — SSM017-SSM021: whole-model type conformance (issue #104)
61077
+ // ══════════════════════════════════════════════════════════════════════
61078
+ // Every check below is silent unless the model says otherwise DEFINITIVELY:
61079
+ // `ConformanceModel` answers `unrelated` only when it read every supertype on
61080
+ // the walk, so a type rooted in the unparsed standard library yields
61081
+ // `unknown` and no diagnostic. That is what keeps the OMG corpus clean while
61082
+ // still catching the workspace-local mistakes these codes exist for.
61083
+ checkTypeConformance(decls, index, accept) {
61084
+ const model = new ConformanceModel((name) => this.resolveUnique(name, index));
61085
+ for (const decl of decls) {
61086
+ this.checkRedefinitionTypeConformance(decl, model, index, accept);
61087
+ this.checkValueAssignability(decl, model, accept);
61088
+ this.checkAbstractTyping(decl, model, accept);
61089
+ this.checkConnectorEndTypes(decl, model, index, accept);
61090
+ this.checkFlowPayload(decl, model, index, accept);
61091
+ this.checkDefinitionDomain(decl, model, accept);
61092
+ this.checkGuardIsBoolean(decl, accept);
61093
+ this.checkAssignmentValue(decl, model, index, accept);
61094
+ this.checkActionParameterCorrespondence(decl, model, accept);
61095
+ this.checkRedefinitionDirection(decl, index, accept);
61096
+ this.checkInterfaceEndTypes(decl, model, index, accept);
61097
+ }
61098
+ }
61099
+ // REQ-390 — SSM017: a redefining feature narrows the feature it redefines, so
61100
+ // its type must specialize the redefined feature's type (OMG KerML 8.4.4.5.4
61101
+ // Redefinition; SysML v2 Part 1 §7.7.4). Re-typing to something unrelated
61102
+ // silently produces a feature that no longer fits where the original did.
61103
+ // KSM002 already guards the multiplicity half of the same rule.
61104
+ checkRedefinitionTypeConformance(decl, model, index, accept) {
61105
+ const mine = soleDeclaredType(decl);
61106
+ if (!mine || mine.conjugated)
61107
+ return;
61108
+ for (const rel of [...decl.preRelationships ?? [], ...decl.relationships ?? []]) {
61109
+ const kind = rel.kind;
61110
+ if (kind !== ":>>" && kind !== "redefines")
61111
+ continue;
61112
+ for (const target of rel.targets ?? []) {
61113
+ const redefined = this.resolveUnique(target, index);
61114
+ if (!redefined)
61115
+ continue;
61116
+ const theirs = soleDeclaredType(redefined);
61117
+ if (!theirs || theirs.conjugated)
61118
+ continue;
61119
+ if (model.conforms(mine.text, theirs.text) !== "unrelated")
61120
+ continue;
61121
+ accept(severity("SSM017", "error"), DIAGNOSTIC_MESSAGES.SSM017_REDEFINITION_TYPE(simpleTypeName(target), mine.text, theirs.text), { node: decl, code: "SSM017", relatedInformation: declarationSite(redefined, target) });
61122
+ }
61123
+ }
61124
+ }
61125
+ // REQ-390 — SSM018: a BINDING connector asserts that its two ends have the
61126
+ // same values (OMG KerML 8.2.5.5.2 BindingConnector; SysML v2 Part 1
61127
+ // §7.9.4), so their types must be related — one specializing the other, or
61128
+ // both the same. Nothing can be both a `Fuel` and a `Signal`, so a binding
61129
+ // across unrelated types describes a model that cannot hold.
61130
+ //
61131
+ // A plain `connect` is deliberately NOT judged, and the OMG corpus is why:
61132
+ // Annex A's SimpleVehicleModel connects `DiffPort` to `AxlePort` and
61133
+ // `ShaftPort_a` to `ShaftPort_b`, four bare port definitions with nothing in
61134
+ // common. A connection relates two things — it does not equate them — so
61135
+ // conformance is not a rule there, and the deeper interface-end rule (an
61136
+ // `interface def`'s own ends constraining what its usages may connect) is
61137
+ // the remaining part of issue #104's connector bullet.
61138
+ //
61139
+ // A conjugated end is skipped throughout: `~T` inverts the type's directions
61140
+ // and is exactly how two `T` ends are legitimately joined.
61141
+ checkConnectorEndTypes(decl, model, index, accept) {
61142
+ const keyword = BINDING_KEYWORDS[decl.$type];
61143
+ if (!keyword)
61144
+ return;
61145
+ for (const [sourcePath, targetPath] of binaryConnectorEnds(decl)) {
61146
+ const source = this.endType(sourcePath, index);
61147
+ const target = this.endType(targetPath, index);
61148
+ if (!source || !target)
61149
+ continue;
61150
+ if (source.conjugated || target.conjugated)
61151
+ continue;
61152
+ if (model.compatible(source.text, target.text) !== "unrelated")
61153
+ continue;
61154
+ accept(severity("SSM018", "error"), DIAGNOSTIC_MESSAGES.SSM018_CONNECTOR_END_TYPES(keyword, source.text, target.text), { node: decl, code: "SSM018" });
61155
+ }
61156
+ }
61157
+ /** The declared type of a connector end, when the end resolves unambiguously
61158
+ * to a usage carrying exactly one written type. */
61159
+ endType(path10, index) {
61160
+ const node = this.resolveUnique(path10, index);
61161
+ if (!node || node.isDef === true)
61162
+ return void 0;
61163
+ return soleDeclaredType(node);
61164
+ }
61165
+ // REQ-390 — SSM019: a feature's value must be a value of its type (OMG KerML
61166
+ // 8.4.4.7 FeatureValue). Only BARE LITERALS are judged, against types whose
61167
+ // scalar root the model can name — `ScalarValues` reached through the
61168
+ // specialization closure, or a `Quantities and Units` value type named by the
61169
+ // dimension table. Anything computed keeps its silence: inferring the type of
61170
+ // an arbitrary expression is the deferred half of issue #104.
61171
+ checkValueAssignability(decl, model, accept) {
61172
+ const declared = soleDeclaredType(decl);
61173
+ if (!declared || declared.conjugated)
61174
+ return;
61175
+ const expected = model.literalKindAccepted(declared.text);
61176
+ if (!expected)
61177
+ return;
61178
+ const withDefault = decl;
61179
+ for (const value of [withDefault.value, withDefault.default?.value]) {
61180
+ const actual = expressionKindOf(value);
61181
+ if (!actual || actual === expected)
61182
+ continue;
61183
+ accept(severity("SSM019", "error"), DIAGNOSTIC_MESSAGES.SSM019_VALUE_TYPE(declared.text, expected, actual), { node: value, code: "SSM019" });
61184
+ }
61185
+ }
61186
+ // REQ-390 — SSM020: a flow's payload travels from its source feature to its
61187
+ // target feature, so each end must be able to hold it (OMG SysML v2 Part 1
61188
+ // §7.12 ItemFlow / PayloadFeature). Reported per end, and only when the end
61189
+ // resolves unambiguously and carries exactly one written type.
61190
+ checkFlowPayload(decl, model, index, accept) {
61191
+ if (decl.$type !== "FlowStmt")
61192
+ return;
61193
+ const flow = decl;
61194
+ const payload = flowPayloadType(flow.payload);
61195
+ if (!payload || payload.conjugated)
61196
+ return;
61197
+ for (const [endKind, path10] of [["source", flow.source], ["target", flow.target]]) {
61198
+ if (!path10)
61199
+ continue;
61200
+ const end = this.resolveUnique(path10, index);
61201
+ const endType = end ? soleDeclaredType(end) : void 0;
61202
+ if (!endType || endType.conjugated)
61203
+ continue;
61204
+ if (model.compatible(payload.text, endType.text) !== "unrelated")
61205
+ continue;
61206
+ accept(severity("SSM020", "error"), DIAGNOSTIC_MESSAGES.SSM020_FLOW_PAYLOAD(payload.text, endKind, endType.text), { node: decl, code: "SSM020" });
61207
+ }
61208
+ }
61209
+ // REQ-390 — SSM021: a usage typed ONLY by an abstract definition names no
61210
+ // complete form (OMG SysML v2 Part 1 §7.4.2 — an abstract definition
61211
+ // classifies nothing on its own; every instance belongs to a specialization).
61212
+ // Deliberately narrow, because typing against an abstract definition is a
61213
+ // normal intermediate step:
61214
+ // · the usage itself may be `abstract`, a `variation`, or a `variant`;
61215
+ // · it may narrow the abstract type through a further `:>`/`subsets`/
61216
+ // `redefines`, which is the sanctioned way to reach a concrete form;
61217
+ // · a definition is not a usage — `part def A :> AbstractB` is ordinary
61218
+ // specialization and is not touched;
61219
+ // · a usage that owns members refines the type in place.
61220
+ // What is left is the case the code is for: a leaf usage that says only
61221
+ // "one of these", where the model never says which. It defaults to a HINT,
61222
+ // not an error — the text is legal, and the modeller may be mid-decomposition.
61223
+ checkAbstractTyping(decl, model, accept) {
61224
+ if (decl.isDef === true || !isCompositeOrRefUsage(decl))
61225
+ return;
61226
+ if (isAbstractDecl(decl))
61227
+ return;
61228
+ const modifiers2 = decl.modifiers ?? [];
61229
+ if (modifiers2.includes("variation") || modifiers2.includes("variant"))
61230
+ return;
61231
+ if (specializedNamesOf(decl).length > 0)
61232
+ return;
61233
+ if ((decl.members ?? []).length > 0)
61234
+ return;
61235
+ const declared = soleDeclaredType(decl);
61236
+ if (!declared || declared.conjugated)
61237
+ return;
61238
+ const type = model.declarationOf(declared.text);
61239
+ if (!type || !isAbstractDecl(type) || type.isDef !== true)
61240
+ return;
61241
+ accept(severity("SSM021", "hint"), DIAGNOSTIC_MESSAGES.SSM021_ABSTRACT_TYPE(decl.name ?? `this ${kindLabel(decl)}`, declared.text), { node: decl, code: "SSM021", relatedInformation: declarationSite(type, declared.text) });
61242
+ }
61243
+ // REQ-390 — SSM022: a usage names a definition of ITS OWN kind (OMG SysML v2
61244
+ // Part 1 §7.2 — every usage kind has a matching definition kind, and §7.4's
61245
+ // `Usage::definition` is typed to it). `part p : SomeAttributeDef` is not a
61246
+ // narrower part, it is a category error the rest of the model then inherits.
61247
+ //
61248
+ // Judged only when the written type resolves to a SysML DEFINITION whose
61249
+ // keyword is knowable. A KerML classifier (`class`, `struct`, `datatype`,
61250
+ // `behavior`) is always accepted — a SysML definition specializes the kernel,
61251
+ // and typing a usage straight against a kernel classifier is legal — as is
61252
+ // anything the workspace cannot read. `USAGE_DEFINITION_DOMAINS` errs WIDE,
61253
+ // admitting each kind's metamodel supertypes (a part definition is an item
61254
+ // definition, a case definition is an action definition), so only genuinely
61255
+ // disjoint families are reported.
61256
+ checkDefinitionDomain(decl, model, accept) {
61257
+ if (decl.isDef === true)
61258
+ return;
61259
+ const domain = USAGE_DEFINITION_DOMAINS[decl.$type];
61260
+ if (!domain)
61261
+ return;
61262
+ for (const declared of declaredTypesOf(decl)) {
61263
+ const type = model.declarationOf(declared.text);
61264
+ if (!type || type.isDef !== true)
61265
+ continue;
61266
+ if (domain.allowed.has(type.$type))
61267
+ continue;
61268
+ accept(severity("SSM022", "error"), DIAGNOSTIC_MESSAGES.SSM022_DEFINITION_DOMAIN(domain.keyword, declared.text, kindLabel(type), domain.expected), { node: decl, code: "SSM022", relatedInformation: declarationSite(type, declared.text) });
61269
+ }
61270
+ }
61271
+ // REQ-390 — SSM023: a guard decides whether a transition or succession is
61272
+ // taken, so it must be able to be true or false (OMG SysML v2 Part 1 §7.19.4
61273
+ // TransitionUsage `guard`, §7.17.5 GuardedSuccession). Uses the same
61274
+ // conservative "definitely not Boolean" test as KSM008 on filter conditions —
61275
+ // a bare feature path may well resolve to a Boolean attribute and is left
61276
+ // alone; a string, a quantity, or an arithmetic expression cannot.
61277
+ checkGuardIsBoolean(decl, accept) {
61278
+ const guard = decl.guard;
61279
+ if (!guard)
61280
+ return;
61281
+ const nonBoolean = isDefinitelyNonBoolean(guard) || expressionKindOf(guard) === "string" || expressionKindOf(guard) === "number";
61282
+ if (!nonBoolean)
61283
+ return;
61284
+ accept(severity("SSM023", "error"), DIAGNOSTIC_MESSAGES.SSM023_GUARD_NOT_BOOLEAN(GUARD_CONSTRUCTS[decl.$type] ?? "succession"), { node: guard, code: "SSM023" });
61285
+ }
61286
+ // REQ-390 — SSM024: `assign x := expr` writes a value into a feature, so the
61287
+ // same rule SSM019 applies to a declared value applies here (OMG SysML v2
61288
+ // Part 1 §7.17.7 AssignmentActionUsage). The target is a feature path; only
61289
+ // an unambiguously resolved one carrying exactly one written type is judged.
61290
+ checkAssignmentValue(decl, model, index, accept) {
61291
+ if (decl.$type !== "AssignNode")
61292
+ return;
61293
+ const node = decl;
61294
+ if (!node.target || !node.value)
61295
+ return;
61296
+ const target = this.resolveUnique(node.target, index);
61297
+ const declared = target ? soleDeclaredType(target) : void 0;
61298
+ if (!declared || declared.conjugated)
61299
+ return;
61300
+ const expected = model.literalKindAccepted(declared.text);
61301
+ const actual = expressionKindOf(node.value);
61302
+ if (!expected || !actual || expected === actual)
61303
+ return;
61304
+ accept(severity("SSM024", "error"), DIAGNOSTIC_MESSAGES.SSM024_ASSIGNMENT_VALUE(node.target, declared.text, expected, actual), { node: node.value, code: "SSM024" });
61305
+ }
61306
+ // REQ-390 — SSM025: an action usage's directed parameters correspond
61307
+ // POSITIONALLY to its definition's (OMG SysML v2 Part 1 §7.16.4 — the n-th
61308
+ // parameter of the usage implicitly redefines the n-th of the definition).
61309
+ // Two consequences are checkable without modelling implicit redefinition
61310
+ // itself: the usage cannot declare more parameters than there are to
61311
+ // correspond to, and a parameter it does declare cannot reverse the
61312
+ // direction of the one it lines up with.
61313
+ //
61314
+ // A parameter that redefines EXPLICITLY is skipped — it names its
61315
+ // counterpart rather than taking it by position, and SSM026 judges that one.
61316
+ checkActionParameterCorrespondence(decl, model, accept) {
61317
+ if (decl.isDef === true || !PARAMETERIZED_USAGE_TYPES.has(decl.$type))
61318
+ return;
61319
+ const declared = soleDeclaredType(decl);
61320
+ if (!declared || declared.conjugated)
61321
+ return;
61322
+ const definition = model.declarationOf(declared.text);
61323
+ if (!definition || definition.isDef !== true)
61324
+ return;
61325
+ const mine = directedParameters(decl);
61326
+ if (mine.length === 0)
61327
+ return;
61328
+ const theirs = directedParameters(definition);
61329
+ if (theirs.length === 0)
61330
+ return;
61331
+ if (mine.length > theirs.length) {
61332
+ accept(severity("SSM025", "error"), DIAGNOSTIC_MESSAGES.SSM025_ACTION_PARAMETER_COUNT(decl.name ?? `this ${kindLabel(decl)}`, mine.length, declared.text, theirs.length), { node: mine[theirs.length].node, code: "SSM025" });
61333
+ return;
61334
+ }
61335
+ for (let position = 0; position < mine.length; position += 1) {
61336
+ if (specializedNamesOf(mine[position].node).length > 0)
61337
+ continue;
61338
+ if (mine[position].direction === theirs[position].direction)
61339
+ continue;
61340
+ accept(severity("SSM025", "error"), DIAGNOSTIC_MESSAGES.SSM025_ACTION_PARAMETER_DIRECTION(position + 1, mine[position].direction, declared.text, theirs[position].direction), { node: mine[position].node, code: "SSM025" });
61341
+ }
61342
+ }
61343
+ // REQ-390 — SSM026: a redefinition refines the feature it names, and a
61344
+ // feature's direction is part of what it is (OMG KerML 8.4.4.5.4 — a
61345
+ // redefining feature's direction must be consistent with the redefined
61346
+ // one's). Turning an `in` into an `out` does not narrow anything; it
61347
+ // reverses the flow the supertype declared and every caller relies on.
61348
+ // `inout` is compatible with either, so only a straight in/out flip is
61349
+ // reported.
61350
+ checkRedefinitionDirection(decl, index, accept) {
61351
+ const mine = directionOf(decl);
61352
+ if (mine !== "in" && mine !== "out")
61353
+ return;
61354
+ for (const rel of [...decl.preRelationships ?? [], ...decl.relationships ?? []]) {
61355
+ const kind = rel.kind;
61356
+ if (kind !== ":>>" && kind !== "redefines")
61357
+ continue;
61358
+ for (const target of rel.targets ?? []) {
61359
+ const redefined = this.resolveUnique(target, index);
61360
+ if (!redefined)
61361
+ continue;
61362
+ const theirs = directionOf(redefined);
61363
+ if (theirs !== "in" && theirs !== "out")
61364
+ continue;
61365
+ if (theirs === mine)
61366
+ continue;
61367
+ accept(severity("SSM026", "error"), DIAGNOSTIC_MESSAGES.SSM026_REDEFINITION_DIRECTION(simpleTypeName(target), mine, theirs), { node: decl, code: "SSM026", relatedInformation: declarationSite(redefined, target) });
61368
+ }
61369
+ }
61370
+ }
61371
+ // REQ-390 — SSM027: an interface usage connects through the ends its
61372
+ // definition declares (OMG SysML v2 Part 1 §7.11.3 — an InterfaceUsage's
61373
+ // connector ends redefine the InterfaceDefinition's `end` features, in
61374
+ // order). This is the part of issue #104's connector bullet that survives
61375
+ // the corpus: a plain `connect` states no conformance, but an interface
61376
+ // whose DEFINITION names its end types does.
61377
+ checkInterfaceEndTypes(decl, model, index, accept) {
61378
+ if (decl.$type !== "InterfaceDecl" || decl.isDef === true)
61379
+ return;
61380
+ const declared = soleDeclaredType(decl);
61381
+ if (!declared || declared.conjugated)
61382
+ return;
61383
+ const definition = model.declarationOf(declared.text);
61384
+ if (!definition || definition.isDef !== true)
61385
+ return;
61386
+ const expected = interfaceEndTypes(definition);
61387
+ const connect = decl.connect;
61388
+ const written = [connect?.source, connect?.target];
61389
+ if (expected.length !== written.length)
61390
+ return;
61391
+ for (let position = 0; position < written.length; position += 1) {
61392
+ const path10 = endPath(written[position]);
61393
+ const wanted = expected[position];
61394
+ if (!path10 || !wanted || wanted.conjugated)
61395
+ continue;
61396
+ const end = this.endType(path10, index);
61397
+ if (!end || end.conjugated)
61398
+ continue;
61399
+ if (model.conforms(end.text, wanted.text) !== "unrelated")
61400
+ continue;
61401
+ accept(severity("SSM027", "error"), DIAGNOSTIC_MESSAGES.SSM027_INTERFACE_END_TYPE(position + 1, end.text, declared.text, wanted.text), { node: decl, code: "SSM027" });
61402
+ }
60740
61403
  }
60741
61404
  // ══════════════════════════════════════════════════════════════════════
60742
61405
  // REQ-160 — occurrence portions: `timeslice` / `snapshot` well-formedness
@@ -61768,7 +62431,7 @@ function specializationTargets(node) {
61768
62431
  ...node.relationships ?? []
61769
62432
  ];
61770
62433
  for (const rel of rels) {
61771
- if (rel.kind && SPECIALIZATION_KINDS2.has(rel.kind))
62434
+ if (rel.kind && SPECIALIZATION_KINDS3.has(rel.kind))
61772
62435
  out.push(...rel.targets);
61773
62436
  }
61774
62437
  return out;
@@ -61821,6 +62484,103 @@ function literalBound(b) {
61821
62484
  function sameDocument(a2, b) {
61822
62485
  return ast_utils_exports.getDocument(a2).uri.toString() === ast_utils_exports.getDocument(b).uri.toString();
61823
62486
  }
62487
+ function soleDeclaredType(node) {
62488
+ const types = declaredTypesOf(node);
62489
+ return types.length === 1 ? types[0] : void 0;
62490
+ }
62491
+ function declarationSite(node, label) {
62492
+ const range = node.$cstNode?.range;
62493
+ if (!range)
62494
+ return void 0;
62495
+ return [{
62496
+ location: { uri: ast_utils_exports.getDocument(node).uri.toString(), range },
62497
+ message: `'${simpleTypeName(label)}' is declared here`
62498
+ }];
62499
+ }
62500
+ var BINDING_KEYWORDS = {
62501
+ BindStmt: "bind",
62502
+ BindingDecl: "binding",
62503
+ BindingConnectorStmt: "binding"
62504
+ };
62505
+ function endPath(end) {
62506
+ const path10 = end?.path?.trim();
62507
+ return path10 && path10.length > 0 ? path10 : void 0;
62508
+ }
62509
+ function binaryConnectorEnds(decl) {
62510
+ const node = decl;
62511
+ const pairs = [];
62512
+ const push2 = (a2, b) => {
62513
+ const from = endPath(a2);
62514
+ const to = endPath(b);
62515
+ if (from && to)
62516
+ pairs.push([from, to]);
62517
+ };
62518
+ push2(node.source, node.target);
62519
+ push2(node.left, node.right);
62520
+ return pairs;
62521
+ }
62522
+ function flowPayloadType(payload) {
62523
+ if (!payload)
62524
+ return void 0;
62525
+ const bare = payload.type?.$refText?.trim();
62526
+ if (bare)
62527
+ return { text: bare, simple: simpleTypeName(bare), conjugated: false };
62528
+ return soleDeclaredType(payload);
62529
+ }
62530
+ function isCompositeOrRefUsage(node) {
62531
+ return COMPOSITE_FEATURE_USAGE_TYPES.has(node.$type) && node.isDef !== true;
62532
+ }
62533
+ var USAGE_DEFINITION_DOMAINS = {
62534
+ PartDecl: { keyword: "part", expected: "a 'part def' (or the 'item def' / 'occurrence def' it specializes)", allowed: /* @__PURE__ */ new Set(["PartDecl", "ItemDecl", "OccurrenceDecl"]) },
62535
+ ItemDecl: { keyword: "item", expected: "an 'item def' (a 'part def' is one)", allowed: /* @__PURE__ */ new Set(["ItemDecl", "PartDecl", "OccurrenceDecl"]) },
62536
+ PortDecl: { keyword: "port", expected: "a 'port def'", allowed: /* @__PURE__ */ new Set(["PortDecl"]) },
62537
+ AttributeDecl: { keyword: "attribute", expected: "an 'attribute def' or an 'enum def'", allowed: /* @__PURE__ */ new Set(["AttributeDecl", "EnumDecl"]) },
62538
+ EnumDecl: { keyword: "enum", expected: "an 'enum def'", allowed: /* @__PURE__ */ new Set(["EnumDecl", "AttributeDecl"]) },
62539
+ OccurrenceDecl: { keyword: "occurrence", expected: "an 'occurrence def', 'part def' or 'item def'", allowed: /* @__PURE__ */ new Set(["OccurrenceDecl", "PartDecl", "ItemDecl"]) },
62540
+ ActionDecl: { keyword: "action", expected: "an 'action def' (or a 'calc def' / 'case def', which are ones)", allowed: /* @__PURE__ */ new Set(["ActionDecl", "CalcDecl", "CaseDecl", "UseCaseDecl", "AnalysisCaseDecl", "VerificationCaseDecl", "StateDecl"]) },
62541
+ StateDecl: { keyword: "state", expected: "a 'state def' (an 'action def' is one)", allowed: /* @__PURE__ */ new Set(["StateDecl", "ActionDecl"]) },
62542
+ CalcDecl: { keyword: "calc", expected: "a 'calc def' (an 'action def' is one)", allowed: /* @__PURE__ */ new Set(["CalcDecl", "ActionDecl", "CaseDecl", "AnalysisCaseDecl"]) },
62543
+ ConstraintDecl: { keyword: "constraint", expected: "a 'constraint def' (a 'requirement def' is one)", allowed: /* @__PURE__ */ new Set(["ConstraintDecl", "RequirementDecl"]) },
62544
+ RequirementDecl: { keyword: "requirement", expected: "a 'requirement def' (a 'constraint def' is one)", allowed: /* @__PURE__ */ new Set(["RequirementDecl", "ConstraintDecl", "ConcernDecl"]) },
62545
+ ConcernDecl: { keyword: "concern", expected: "a 'concern def' (a 'requirement def' is one)", allowed: /* @__PURE__ */ new Set(["ConcernDecl", "RequirementDecl", "ConstraintDecl"]) },
62546
+ ConnectionDecl: { keyword: "connection", expected: "a 'connection def' (an 'interface def' / 'allocation def' is one)", allowed: /* @__PURE__ */ new Set(["ConnectionDecl", "InterfaceDecl", "AllocationDecl", "PartDecl", "ItemDecl"]) },
62547
+ InterfaceDecl: { keyword: "interface", expected: "an 'interface def' (a 'connection def' is one)", allowed: /* @__PURE__ */ new Set(["InterfaceDecl", "ConnectionDecl"]) },
62548
+ AllocationDecl: { keyword: "allocation", expected: "an 'allocation def' (a 'connection def' is one)", allowed: /* @__PURE__ */ new Set(["AllocationDecl", "ConnectionDecl"]) },
62549
+ ViewDecl: { keyword: "view", expected: "a 'view def'", allowed: /* @__PURE__ */ new Set(["ViewDecl", "PartDecl"]) },
62550
+ ViewpointDecl: { keyword: "viewpoint", expected: "a 'viewpoint def' (a 'requirement def' is one)", allowed: /* @__PURE__ */ new Set(["ViewpointDecl", "RequirementDecl", "ConcernDecl"]) },
62551
+ RenderingDecl: { keyword: "rendering", expected: "a 'rendering def'", allowed: /* @__PURE__ */ new Set(["RenderingDecl", "PartDecl"]) },
62552
+ MetadataDecl: { keyword: "metadata", expected: "a 'metadata def'", allowed: /* @__PURE__ */ new Set(["MetadataDecl"]) }
62553
+ };
62554
+ var GUARD_CONSTRUCTS = {
62555
+ TransitionDecl: "transition",
62556
+ SuccessionStmt: "succession",
62557
+ TransitionStmt: "transition"
62558
+ };
62559
+ var PARAMETERIZED_USAGE_TYPES = /* @__PURE__ */ new Set([
62560
+ "ActionDecl",
62561
+ "CalcDecl",
62562
+ "CaseDecl",
62563
+ "UseCaseDecl",
62564
+ "AnalysisCaseDecl",
62565
+ "VerificationCaseDecl",
62566
+ "ConstraintDecl"
62567
+ ]);
62568
+ var PARAMETER_DIRECTIONS = /* @__PURE__ */ new Set(["in", "out", "inout"]);
62569
+ function directionOf(node) {
62570
+ return (node.modifiers ?? []).find((modifier) => PARAMETER_DIRECTIONS.has(modifier));
62571
+ }
62572
+ function directedParameters(node) {
62573
+ const out = [];
62574
+ for (const member of node.members ?? []) {
62575
+ const direction = directionOf(member);
62576
+ if (direction)
62577
+ out.push({ node: member, direction });
62578
+ }
62579
+ return out;
62580
+ }
62581
+ function interfaceEndTypes(definition) {
62582
+ return (definition.members ?? []).filter((member) => member.$type === "EndDecl" || member.modifiers?.includes("end")).map((member) => soleDeclaredType(member));
62583
+ }
61824
62584
  var NON_BOOLEAN_BIN_OPS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%", "**", "^", ".."]);
61825
62585
  function isDefinitelyNonBoolean(node) {
61826
62586
  const type = node.$type;
@@ -62651,7 +63411,7 @@ function trailingBlockBody(node) {
62651
63411
  }
62652
63412
 
62653
63413
  // ../language-server/out/src/services/document-symbol-provider.js
62654
- var SPECIALIZATION_KINDS3 = /* @__PURE__ */ new Set([":>", "specializes"]);
63414
+ var SPECIALIZATION_KINDS4 = /* @__PURE__ */ new Set([":>", "specializes"]);
62655
63415
  var REDEFINITION_KINDS = /* @__PURE__ */ new Set([":>>", "redefines"]);
62656
63416
  var INHERITABLE_FEATURE_TYPES = /* @__PURE__ */ new Set([
62657
63417
  "ActionDecl",
@@ -62720,7 +63480,7 @@ function specializationTargets2(node) {
62720
63480
  const n = node;
62721
63481
  const out = [];
62722
63482
  for (const rel of [...n.preRelationships ?? [], ...n.relationships ?? []]) {
62723
- if (rel.kind && SPECIALIZATION_KINDS3.has(rel.kind))
63483
+ if (rel.kind && SPECIALIZATION_KINDS4.has(rel.kind))
62724
63484
  out.push(...rel.targets ?? []);
62725
63485
  }
62726
63486
  return out;
@@ -64341,24 +65101,46 @@ var MULTIPLICITY_TEMPLATES = [
64341
65101
  insertText,
64342
65102
  detail: "Multiplicity"
64343
65103
  }));
65104
+ var ARROW_FUNCTION_LIBRARIES = /* @__PURE__ */ new Set([
65105
+ "ControlFunctions",
65106
+ "SequenceFunctions",
65107
+ "CollectionFunctions"
65108
+ ]);
64344
65109
  var ARROW_FUNCTIONS = [
65110
+ // ControlFunctions — take a body expression over each element.
65111
+ ["collect", "collect { in ${1:x}; ${0:x} }"],
65112
+ ["select", "select { in ${1:x}; ${0:true} }"],
65113
+ ["selectOne", "selectOne { in ${1:x}; ${0:true} }"],
65114
+ ["reject", "reject { in ${1:x}; ${0:false} }"],
65115
+ ["reduce", "reduce ${0:'+'}"],
65116
+ ["forAll", "forAll { in ${1:x}; ${0:true} }"],
65117
+ ["exists", "exists { in ${1:x}; ${0:true} }"],
65118
+ ["minimize", "minimize { in ${1:x}; ${0:x} }"],
65119
+ ["maximize", "maximize { in ${1:x}; ${0:x} }"],
65120
+ ["allTrue", "allTrue()"],
65121
+ ["anyTrue", "anyTrue()"],
65122
+ // SequenceFunctions / CollectionFunctions — size and element access.
64345
65123
  ["size", "size()"],
64346
65124
  ["isEmpty", "isEmpty()"],
64347
65125
  ["notEmpty", "notEmpty()"],
64348
- ["at", "at(${1:index})"],
64349
65126
  ["head", "head()"],
64350
65127
  ["tail", "tail()"],
64351
- ["includes", "includes(${1:item})"],
64352
- ["excludes", "excludes(${1:item})"],
64353
- ["including", "including(${1:item})"],
64354
- ["excluding", "excluding(${1:item})"],
64355
- ["union", "union(${1:other})"],
64356
- ["intersection", "intersection(${1:other})"],
64357
- ["difference", "difference(${1:other})"],
64358
- ["select", "select { in ${1:x}; ${0:true} }"],
64359
- ["forAll", "forAll { in ${1:x}; ${0:true} }"],
64360
- ["exists", "exists { in ${1:x}; ${0:true} }"],
64361
- ["collect", "collect { in ${1:x}; ${0:x} }"]
65128
+ ["last", "last()"],
65129
+ ["subsequence", "subsequence(${1:startIndex}, ${0:endIndex})"],
65130
+ // Membership and set algebra.
65131
+ ["includes", "includes(${0:item})"],
65132
+ ["includesOnly", "includesOnly(${0:other})"],
65133
+ ["excludes", "excludes(${0:item})"],
65134
+ ["contains", "contains(${0:item})"],
65135
+ ["containsAll", "containsAll(${0:other})"],
65136
+ ["including", "including(${0:values})"],
65137
+ ["includingAt", "includingAt(${1:values}, ${0:index})"],
65138
+ ["excluding", "excluding(${0:values})"],
65139
+ ["excludingAt", "excludingAt(${1:startIndex}, ${0:endIndex})"],
65140
+ ["union", "union(${0:other})"],
65141
+ ["intersection", "intersection(${0:other})"],
65142
+ ["equals", "equals(${0:other})"],
65143
+ ["same", "same(${0:other})"]
64362
65144
  ].map(([label, insertText]) => ({
64363
65145
  label,
64364
65146
  kind: import_vscode_languageserver16.CompletionItemKind.Function,
@@ -64551,6 +65333,10 @@ var SNIPPETS = [
64551
65333
  ];
64552
65334
  var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64553
65335
  indexManager;
65336
+ // REQ-242 — issue #103: member completion resolves the written receiver with
65337
+ // the same service that answers go-to-definition, so `.` offers exactly the
65338
+ // members navigation can reach — including inherited and cross-file ones.
65339
+ featurePaths;
64554
65340
  // REQ-245, REQ-268 — Qualified names of every indexed element, keyed by the
64555
65341
  // element it names. Built once per workspace build and evicted with the rest
64556
65342
  // of the workspace caches; see [[qualifiedNamesByElement]].
@@ -64561,6 +65347,7 @@ var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64561
65347
  constructor(services) {
64562
65348
  super(services);
64563
65349
  this.indexManager = services.shared.workspace.IndexManager;
65350
+ this.featurePaths = new FeaturePathResolver(services);
64564
65351
  this.qualifiedNameCache = new WorkspaceCache(services.shared);
64565
65352
  }
64566
65353
  async getCompletion(document, params) {
@@ -64591,6 +65378,21 @@ var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64591
65378
  item.labelDetails = { description: categoryLabel(category), ...item.labelDetails ?? {} };
64592
65379
  }
64593
65380
  }
65381
+ // REQ-242 — issue #103: a `CompletionItem` keeps only a label and the AST
65382
+ // type name, so once Langium has produced a cross-reference item there is no
65383
+ // way back to the description that justified it. Record the two facts the
65384
+ // typing filter needs while the description is still in hand.
65385
+ createReferenceCompletionItem(nodeDescription) {
65386
+ const item = super.createReferenceCompletionItem(nodeDescription);
65387
+ return {
65388
+ ...item,
65389
+ data: {
65390
+ ...item.data,
65391
+ isDefinition: isDefinitionDescription(nodeDescription),
65392
+ isDottedPath: nodeDescription.name.includes(".")
65393
+ }
65394
+ };
65395
+ }
64594
65396
  filterDefaultsForContext(items, context) {
64595
65397
  if (context.kind === "package-body" || context.kind === "member-body")
64596
65398
  return;
@@ -64604,11 +65406,15 @@ var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64604
65406
  allowedLabels.add("subsets");
64605
65407
  allowedLabels.add("redefines");
64606
65408
  }
65409
+ const definitionsOnly = context.kind === "typing";
65410
+ const rejectDottedPaths = definitionsOnly || context.kind === "specialization";
64607
65411
  const allowedTypes = new Set(this.symbolTypesForContext(context));
64608
65412
  for (let i = items.length - 1; i >= 0; i--) {
64609
65413
  const item = items[i];
64610
65414
  const detail = typeof item.detail === "string" ? item.detail : void 0;
64611
- if (allowedLabels.has(item.label) || detail && allowedTypes.has(detail))
65415
+ if (allowedLabels.has(item.label))
65416
+ continue;
65417
+ if (detail && allowedTypes.has(detail) && (!definitionsOnly || isDefinitionItem(item)) && (!rejectDottedPaths || !isDottedPathItem(item)))
64612
65418
  continue;
64613
65419
  items.splice(i, 1);
64614
65420
  }
@@ -64647,14 +65453,17 @@ var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64647
65453
  detail: "Conjugated port type"
64648
65454
  })));
64649
65455
  }
65456
+ if (context.kind === "specialization") {
65457
+ addUnique(items, this.inheritedFeatureItems(document, offset, context));
65458
+ }
64650
65459
  return;
64651
65460
  }
64652
65461
  case "member-access": {
64653
- addUnique(items, this.memberAccessItems(document, context.receiver));
65462
+ addUnique(items, this.memberAccessItems(document, context.receiver, offset));
64654
65463
  return;
64655
65464
  }
64656
65465
  case "arrow": {
64657
- addUnique(items, ARROW_FUNCTIONS);
65466
+ addUnique(items, this.arrowFunctionItems());
64658
65467
  return;
64659
65468
  }
64660
65469
  case "import": {
@@ -64786,24 +65595,54 @@ var SysmlCompletionProvider = class extends DefaultCompletionProvider {
64786
65595
  }
64787
65596
  return items;
64788
65597
  }
64789
- memberAccessItems(document, receiver) {
65598
+ memberAccessItems(document, receiver, offset) {
64790
65599
  if (!receiver)
64791
65600
  return [];
64792
65601
  const root3 = document.parseResult.value;
64793
- const members = resolveReceiverMembers(root3, receiver);
65602
+ const context = smallestNodeAtOffset(root3, offset) ?? root3;
65603
+ const members = this.featurePaths.membersForPath(context, receiverSteps(receiver));
64794
65604
  return members.map((node) => this.itemForNode(node)).filter(isDefined);
64795
65605
  }
64796
65606
  connectPortItems(document, offset) {
64797
65607
  const root3 = document.parseResult.value;
64798
65608
  const enclosing = smallestNodeAtOffset(root3, offset);
64799
65609
  const owner = nearestMemberOwner(enclosing) ?? root3;
64800
- return collectReachablePorts(owner).map((path10) => ({
65610
+ return collectReachablePorts(owner, (node) => this.featurePaths.visibleMembers(node)).map((path10) => ({
64801
65611
  label: path10,
64802
65612
  kind: import_vscode_languageserver16.CompletionItemKind.Interface,
64803
65613
  detail: "Reachable port",
64804
65614
  sortText: "0"
64805
65615
  }));
64806
65616
  }
65617
+ // REQ-242 — issue #103: `subsets` and `redefines` name a FEATURE of the
65618
+ // enclosing declaration, and the ones worth offering are normally the
65619
+ // inherited ones — refining a feature the supertype introduced is what the
65620
+ // operators are for. Those features are declared in another namespace, so
65621
+ // no scope rooted at the current one reaches them; walk the enclosing
65622
+ // declaration's own supertypes instead. The declaration being written is
65623
+ // excluded, so `part frontWheels :>` never offers `frontWheels`.
65624
+ inheritedFeatureItems(document, offset, context) {
65625
+ const root3 = document.parseResult.value;
65626
+ const owner = nearestMemberOwner(smallestNodeAtOffset(root3, offset));
65627
+ if (!owner)
65628
+ return [];
65629
+ const allowed = new Set(this.symbolTypesForContext(context));
65630
+ return this.featurePaths.visibleMembers(owner).filter((node) => allowed.has(node.$type) && isUsage(node) && !coversOffset(node, offset)).map((node) => this.itemForNode(node)).filter(isDefined);
65631
+ }
65632
+ // REQ-242 — issue #103: the curated table carries the argument shapes, but
65633
+ // the vendored library is the authority on WHICH functions exist. Merge in
65634
+ // any collection-processing function the index knows and the table does not,
65635
+ // so a library revision does not silently stop being offered.
65636
+ arrowFunctionItems() {
65637
+ const items = [...ARROW_FUNCTIONS];
65638
+ addUnique(items, this.indexManager.allElements().filter((desc) => desc.type === "FunctionDecl" || desc.type === "PredicateDecl").map((desc) => desc.name.split("::")).filter((parts) => parts.length === 2 && ARROW_FUNCTION_LIBRARIES.has(parts[0]) && isArrowInvocableName(parts[1])).map((parts) => ({
65639
+ label: parts[1],
65640
+ kind: import_vscode_languageserver16.CompletionItemKind.Function,
65641
+ detail: "Kernel Function Library",
65642
+ sortText: "0"
65643
+ })).toArray());
65644
+ return items;
65645
+ }
64807
65646
  unitSymbolItems() {
64808
65647
  const symbols = getDimensionTable()?.unitSymbols ?? COMMON_UNIT_SYMBOLS;
64809
65648
  return symbols.map((symbol) => ({
@@ -64959,9 +65798,21 @@ function isPackageBody(prefix) {
64959
65798
  function elementKey(desc) {
64960
65799
  return `${desc.documentUri.toString()}#${desc.path}#${desc.type}`;
64961
65800
  }
65801
+ function isArrowInvocableName(name) {
65802
+ return /^[\p{L}_][\p{L}\p{N}_]*$/u.test(name);
65803
+ }
64962
65804
  function isDefinitionDescription(desc) {
64963
65805
  const node = desc.node;
64964
- return node?.isDef === void 0 || node.isDef === true;
65806
+ if (node?.isDef !== void 0)
65807
+ return node.isDef === true;
65808
+ const usage = desc.isUsage;
65809
+ return usage !== true;
65810
+ }
65811
+ function isDefinitionItem(item) {
65812
+ return item.data?.isDefinition !== false;
65813
+ }
65814
+ function isDottedPathItem(item) {
65815
+ return item.data?.isDottedPath === true;
64965
65816
  }
64966
65817
  function addUnique(items, additions) {
64967
65818
  const labels = new Map(items.map((item, index) => [item.label, index]));
@@ -65015,54 +65866,33 @@ function isUsage(node) {
65015
65866
  const isDef = node.isDef;
65016
65867
  return isDef === void 0 || isDef === false;
65017
65868
  }
65018
- function typingTarget(node) {
65019
- const ref = node?.typing?.type;
65020
- if (!ref)
65021
- return void 0;
65022
- try {
65023
- if (ref.ref)
65024
- return ref.ref;
65025
- } catch {
65026
- }
65027
- const refText = ref.$refText;
65028
- if (typeof refText !== "string")
65029
- return void 0;
65030
- return findNamedNode(ast_utils_exports.getDocument(node).parseResult.value, simpleName(refText), { definitionsOnly: true });
65031
- }
65032
- function resolveReceiverMembers(root3, receiver) {
65033
- let current2 = findNamedNode(root3, simpleName(receiver.split(/[.:]+/u)[0]));
65034
- for (const segment of receiver.split(/(?:\.|::)/u).slice(1)) {
65035
- current2 = collectMembersWithTyped(current2).find((member) => nodeName(member) === segment);
65036
- if (!current2)
65037
- return [];
65038
- }
65039
- return collectMembersWithTyped(current2);
65040
- }
65041
- function collectMembersWithTyped(node) {
65042
- if (!node)
65043
- return [];
65044
- const members = [...nodeMembers(node)];
65045
- const typed = typingTarget(node);
65046
- if (typed && typed !== node) {
65047
- members.push(...nodeMembers(typed));
65869
+ function receiverSteps(receiver) {
65870
+ const steps = [];
65871
+ const pattern = /(::|\.)?\s*([\p{L}_'][\p{L}\p{N}_']*)/gu;
65872
+ let match;
65873
+ while ((match = pattern.exec(receiver)) !== null) {
65874
+ steps.push({
65875
+ text: unquoteName2(match[2]),
65876
+ separator: steps.length === 0 ? void 0 : match[1]
65877
+ });
65048
65878
  }
65049
- return uniqueNodes(members).filter((member) => nodeName(member) !== void 0);
65879
+ return steps;
65050
65880
  }
65051
- function collectReachablePorts(owner) {
65881
+ function collectReachablePorts(owner, members) {
65052
65882
  const seenOwners = /* @__PURE__ */ new Set();
65053
65883
  const paths = /* @__PURE__ */ new Set();
65054
65884
  const visitOwner = (node, prefix) => {
65055
65885
  if (!node || seenOwners.has(node))
65056
65886
  return;
65057
65887
  seenOwners.add(node);
65058
- for (const member of collectMembersWithTyped(node)) {
65888
+ for (const member of members(node)) {
65059
65889
  const name = nodeName(member);
65060
65890
  if (!name || !isUsage(member))
65061
65891
  continue;
65062
65892
  if (member.$type === "PortDecl") {
65063
65893
  paths.add(`${prefix}${name}`);
65064
65894
  } else if (member.$type === "PartDecl") {
65065
- visitOwner(typingTarget(member) ?? member, `${prefix}${name}.`);
65895
+ visitOwner(member, `${prefix}${name}.`);
65066
65896
  }
65067
65897
  }
65068
65898
  seenOwners.delete(node);
@@ -65070,12 +65900,6 @@ function collectReachablePorts(owner) {
65070
65900
  visitOwner(owner, "");
65071
65901
  return [...paths].sort();
65072
65902
  }
65073
- function findNamedNode(root3, name, options = {}) {
65074
- if (!name)
65075
- return void 0;
65076
- const matches = [root3, ...ast_utils_exports.streamAllContents(root3).toArray()].filter((node) => nodeName(node) === name).filter((node) => !options.definitionsOnly || node.isDef !== false);
65077
- return matches.find((node) => node.isDef === true) ?? matches[0];
65078
- }
65079
65903
  function smallestNodeAtOffset(root3, offset) {
65080
65904
  let best;
65081
65905
  for (const node of [root3, ...ast_utils_exports.streamAllContents(root3).toArray()]) {
@@ -65088,6 +65912,10 @@ function smallestNodeAtOffset(root3, offset) {
65088
65912
  }
65089
65913
  return best;
65090
65914
  }
65915
+ function coversOffset(node, offset) {
65916
+ const cst = node.$cstNode;
65917
+ return cst !== void 0 && cst.offset <= offset && cst.end >= offset;
65918
+ }
65091
65919
  function nearestMemberOwner(node) {
65092
65920
  let current2 = node;
65093
65921
  while (current2) {
@@ -65097,9 +65925,6 @@ function nearestMemberOwner(node) {
65097
65925
  }
65098
65926
  return void 0;
65099
65927
  }
65100
- function uniqueNodes(nodes) {
65101
- return [...new Set(nodes)];
65102
- }
65103
65928
  function simpleName(name) {
65104
65929
  if (!name)
65105
65930
  return void 0;
@@ -65519,12 +66344,12 @@ function importSignature(imp) {
65519
66344
  const segments = imp.segs.map((seg) => `${seg.name ?? ""}${seg.star ? "*" : ""}${seg.recursive ? "**" : ""}`);
65520
66345
  return `${importVisibility(imp)} ${imp.head}::${segments.join("::")}${imp.alias ? ` as ${imp.alias}` : ""}`;
65521
66346
  }
65522
- var SPECIALIZATION_KINDS4 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
66347
+ var SPECIALIZATION_KINDS5 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
65523
66348
  function specializationTargets3(node) {
65524
66349
  const n = node;
65525
66350
  const out = [];
65526
66351
  for (const rel of [...n.preRelationships ?? [], ...n.relationships ?? []]) {
65527
- if (rel.kind && SPECIALIZATION_KINDS4.has(rel.kind))
66352
+ if (rel.kind && SPECIALIZATION_KINDS5.has(rel.kind))
65528
66353
  out.push(...rel.targets ?? []);
65529
66354
  }
65530
66355
  return out;
@@ -65729,7 +66554,11 @@ var SysmlScopeComputation = class extends DefaultScopeComputation {
65729
66554
  ...base,
65730
66555
  ...visibility ? { visibility } : {},
65731
66556
  ...visibility === "private" ? { isPrivate: true } : {},
65732
- ...alias.derived ? { isDerivedAlias: true } : {}
66557
+ ...alias.derived ? { isDerivedAlias: true } : {},
66558
+ // REQ-242 — issue #103 — mirrors what the precomputed library index
66559
+ // records, so consumers read def-ness the same way for workspace and
66560
+ // library symbols.
66561
+ ...node.isDef === false ? { isUsage: true } : {}
65733
66562
  };
65734
66563
  if (!alias.cstNode)
65735
66564
  return description;
@@ -65801,12 +66630,12 @@ function directImportEntries(imp, descriptions) {
65801
66630
  }
65802
66631
  return entries;
65803
66632
  }
65804
- var SPECIALIZATION_KINDS5 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
66633
+ var SPECIALIZATION_KINDS6 = /* @__PURE__ */ new Set([":>", ":>>", "specializes", "subsets", "redefines"]);
65805
66634
  function specializationTargets4(node) {
65806
66635
  const value = node;
65807
66636
  const targets = [];
65808
66637
  for (const relationship of [...value.preRelationships ?? [], ...value.relationships ?? []]) {
65809
- if (relationship.kind && SPECIALIZATION_KINDS5.has(relationship.kind)) {
66638
+ if (relationship.kind && SPECIALIZATION_KINDS6.has(relationship.kind)) {
65810
66639
  targets.push(...relationship.targets ?? []);
65811
66640
  }
65812
66641
  }
@@ -69183,7 +70012,7 @@ async function runValidation(command) {
69183
70012
  }
69184
70013
 
69185
70014
  // src/main.ts
69186
- var VERSION2 = true ? "0.15.4" : "dev";
70015
+ var VERSION2 = true ? "0.15.6" : "dev";
69187
70016
  async function main(argv) {
69188
70017
  const command = parseArgs(argv);
69189
70018
  if (command.kind === "help") {