sysml-validate 0.15.2 → 0.15.3

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
@@ -45489,8 +45489,17 @@ var SysMLGrammar = () => loadedSysMLGrammar ?? (loadedSysMLGrammar = loadGrammar
45489
45489
  "$type": "Group",
45490
45490
  "elements": [
45491
45491
  {
45492
- "$type": "Keyword",
45493
- "value": "."
45492
+ "$type": "Alternatives",
45493
+ "elements": [
45494
+ {
45495
+ "$type": "Keyword",
45496
+ "value": "."
45497
+ },
45498
+ {
45499
+ "$type": "Keyword",
45500
+ "value": "::"
45501
+ }
45502
+ ]
45494
45503
  },
45495
45504
  {
45496
45505
  "$type": "Assignment",
@@ -58759,6 +58768,15 @@ var DIAGNOSTIC_MESSAGES = {
58759
58768
  // RES001s — but an unevaluated filter produces a scope or view that looks
58760
58769
  // authoritative and is not, so the fail-open is stated rather than assumed.
58761
58770
  RES018_UNEVALUATED_FILTER: (subject, construct) => `${subject} filter is not evaluated: ${construct} is outside the metadata-filter evaluator, which decides '@'/'@@' metadata tests combined with 'not', 'and'/'&', and 'or'/'|'. The filter fails open \u2014 it is treated as pass-through and removes nothing.`,
58771
+ // REQ-389 — Namespace qualification versus feature chaining in every written path
58772
+ // issue #213 — the KerML textual BNF binds `::` tighter than `.`: each
58773
+ // dot-separated link of a FeatureChain is a complete QualifiedName, and only
58774
+ // a Feature can be an `OwnedFeatureChaining::chainingFeature`. A Definition
58775
+ // specializes Classifier, not Feature, so it can never sit on the LEFT of a
58776
+ // dot. Our permissive scoping resolves the dotted form anyway (a definition
58777
+ // is a Type, hence a Namespace), which is exactly why it needs saying: the
58778
+ // Pilot implementation refuses the same text at linking time.
58779
+ RES019_DEFINITION_IN_FEATURE_CHAIN: (segment, member) => `'${segment}' is a definition, not a usage, so '${member}' is reached through it with '::' rather than '.'. Only a feature can be chained with '.' (KerML FeatureChain).`,
58762
58780
  // REQ-007 — SysML v1 → v2 migration guardrail.
58763
58781
  MIG001_V1_KEYWORD: (keyword, v2, note) => `'${keyword}' is a SysML v1 construct, not a SysML v2 keyword. ${note} Use '${v2}'.`,
58764
58782
  MIG002_V1_STEREOTYPE_KNOWN: (stereotype, v2, note) => `SysML v2 has no stereotypes. ${note} Replace \xAB${stereotype}\xBB with '${v2}'.`,
@@ -59169,7 +59187,31 @@ var FeaturePathResolver = class {
59169
59187
  }
59170
59188
  // REQ-220/317 — resolve every independently clickable/diagnosable segment.
59171
59189
  resolve(node) {
59172
- const segments = this.segmentsOf(node);
59190
+ return this.resolveSegments(node, this.segmentsOf(node));
59191
+ }
59192
+ // REQ-389 — Namespace qualification versus feature chaining in every written path
59193
+ // issue #213 — a `perform` / `exhibit` target is stored as `name` plus an
59194
+ // untyped `chainSegs` array, so `A::b`, `A.b` and `A.b::c` all collapse to the
59195
+ // same AST and the separator rule is unenforceable from it. The CST still
59196
+ // carries the separators verbatim; read the path back off it and resolve it
59197
+ // exactly as an expression path is resolved.
59198
+ resolveDeclarationPath(node) {
59199
+ return this.resolveSegments(node, this.declarationSegmentsOf(node));
59200
+ }
59201
+ // REQ-389 — Namespace qualification versus feature chaining in every written path
59202
+ // issue #213 — a connector end keeps its whole path in ONE datatype-string
59203
+ // property (`ConnectorEnd.path`, a `RelationPath`), so there is no `chainSegs`
59204
+ // list to read at all. Resolve it off that property's CST span instead; the
59205
+ // separator rule is the same one, because a connector end reaches the world
59206
+ // through the same OwnedReferenceSubsetting as `perform` and `exhibit`.
59207
+ resolvePropertyPath(node, property3) {
59208
+ const cst = node.$cstNode;
59209
+ const propertyNode = cst ? grammar_utils_exports.findNodeForProperty(cst, property3) : void 0;
59210
+ if (!cst || !propertyNode)
59211
+ return { segments: [] };
59212
+ return this.resolveSegments(node, this.segmentsInSpan(cst, propertyNode.offset, propertyNode.end));
59213
+ }
59214
+ resolveSegments(node, segments) {
59173
59215
  if (segments.length === 0)
59174
59216
  return { segments };
59175
59217
  const root3 = ast_utils_exports.getDocument(node).parseResult.value;
@@ -59218,6 +59260,61 @@ var FeaturePathResolver = class {
59218
59260
  }
59219
59261
  return segments;
59220
59262
  }
59263
+ // REQ-389 — Namespace qualification versus feature chaining in every written path
59264
+ // issue #213 — the path of a reference-form declaration (`perform A::b.c;`,
59265
+ // `exhibit VehicleStates::operating;`) spans the `name` property through the
59266
+ // last `chainSegs` element. Everything after it (a `redefines` tail, a typing,
59267
+ // a body) belongs to other properties and must not be read as path segments,
59268
+ // so the CST leaves are taken only from that span.
59269
+ declarationSegmentsOf(node) {
59270
+ const cst = node.$cstNode;
59271
+ if (!cst)
59272
+ return [];
59273
+ const nameNode = grammar_utils_exports.findNodeForProperty(cst, "name");
59274
+ if (!nameNode)
59275
+ return [];
59276
+ const chainNodes = grammar_utils_exports.findNodesForProperty(cst, "chainSegs");
59277
+ const end = chainNodes.length > 0 ? chainNodes[chainNodes.length - 1].end : nameNode.end;
59278
+ return this.segmentsInSpan(cst, nameNode.offset, end);
59279
+ }
59280
+ /** The path segments spelled between two offsets of `cst`, separators retained.
59281
+ * `$` is the root-namespace GlobalQualification prefix and carries no segment;
59282
+ * a `RelationSegment`'s own `[n]` multiplicity addresses the segment it follows
59283
+ * (`engine[2]`) and is likewise not one, so bracketed text is skipped whole. */
59284
+ segmentsInSpan(cst, from, to) {
59285
+ const segments = [];
59286
+ let separator;
59287
+ let bracketDepth = 0;
59288
+ for (const leaf of cst_utils_exports.flattenCst(cst)) {
59289
+ if (leaf.offset < from)
59290
+ continue;
59291
+ if (leaf.end > to)
59292
+ break;
59293
+ if (leaf.text === "[") {
59294
+ bracketDepth += 1;
59295
+ continue;
59296
+ }
59297
+ if (leaf.text === "]") {
59298
+ bracketDepth = Math.max(0, bracketDepth - 1);
59299
+ continue;
59300
+ }
59301
+ if (bracketDepth > 0)
59302
+ continue;
59303
+ if (leaf.text === "$")
59304
+ continue;
59305
+ if (leaf.text === "." || leaf.text === "::") {
59306
+ separator = leaf.text;
59307
+ continue;
59308
+ }
59309
+ segments.push({
59310
+ text: canonicalEscapedName(leaf.text),
59311
+ separator: segments.length === 0 ? void 0 : separator,
59312
+ cst: leaf
59313
+ });
59314
+ separator = void 0;
59315
+ }
59316
+ return segments;
59317
+ }
59221
59318
  resolveRoot(node, name, snapshot) {
59222
59319
  if (name === "self" || name === "this" || name === "that") {
59223
59320
  for (let owner = node.$container; owner; owner = owner.$container) {
@@ -59408,6 +59505,28 @@ var FeaturePathResolver = class {
59408
59505
  }
59409
59506
  };
59410
59507
 
59508
+ // ../language-server/out/src/services/namespace-kind.js
59509
+ var KERML_CLASSIFIER_DECL_TYPES = /* @__PURE__ */ new Set([
59510
+ "AssociationDecl",
59511
+ "BehaviorDecl",
59512
+ "ClassDecl",
59513
+ "ClassifierDecl",
59514
+ "DatatypeDecl",
59515
+ "FunctionDecl",
59516
+ "InteractionDecl",
59517
+ "MetaclassDecl",
59518
+ "PredicateDecl",
59519
+ "StructDecl",
59520
+ "TypeDecl"
59521
+ ]);
59522
+ function isNamespaceOnlyDecl(node) {
59523
+ if (isPackage(node) || node.$type === "NamespaceDecl" || node.$type === "BareDefDecl")
59524
+ return true;
59525
+ if (KERML_CLASSIFIER_DECL_TYPES.has(node.$type))
59526
+ return true;
59527
+ return node.isDef === true;
59528
+ }
59529
+
59411
59530
  // ../language-server/out/src/services/validator.js
59412
59531
  var severityOverrides = {};
59413
59532
  function setSeverityOverrides(overrides) {
@@ -59419,6 +59538,9 @@ function severity(code, defaultSeverity) {
59419
59538
  function isLibraryDocument2(node) {
59420
59539
  return isLibraryDocument(ast_utils_exports.getDocument(node));
59421
59540
  }
59541
+ function isDefinitionOnlyNode(node) {
59542
+ return !isPackage(node) && isNamespaceOnlyDecl(node);
59543
+ }
59422
59544
  var TABLE_4_ESCAPES = /* @__PURE__ */ new Set(['"', "'", "\\", "b", "f", "t", "n"]);
59423
59545
  var IDENT_START = /[\p{L}_]/u;
59424
59546
  var IDENT_PART = /[\p{L}\p{N}_]/u;
@@ -59601,6 +59723,53 @@ var SysmlValidator = class _SysmlValidator {
59601
59723
  data: { featurePathSegment: true }
59602
59724
  });
59603
59725
  }
59726
+ // REQ-389 — Namespace qualification versus feature chaining in every written path
59727
+ // issue #213 — RES019: `::` binds tighter than `.`. Each dot-separated link of
59728
+ // a KerML FeatureChain is a complete QualifiedName, and an
59729
+ // `OwnedFeatureChaining::chainingFeature` is scoped to `[SysML::Feature|…]`,
59730
+ // so only a USAGE may sit on the left of a dot; a Definition specializes
59731
+ // Classifier and a package is a plain Namespace. `perform`, `exhibit` and
59732
+ // every connector end reach the world through the same
59733
+ // OwnedReferenceSubsetting, so one rule governs them all. This extension's
59734
+ // permissive scoping resolves the dotted form regardless (a definition IS a
59735
+ // Namespace), which is precisely why it is worth a warning rather than
59736
+ // silence: the Pilot implementation refuses the same text at LINKING time, so
59737
+ // the model is not portable. A warning, never an error — the text is
59738
+ // well-formed here and the fix is mechanical.
59739
+ checkChainSeparators(node, accept) {
59740
+ const chainSegs = node.chainSegs;
59741
+ if (!Array.isArray(chainSegs) || chainSegs.length === 0)
59742
+ return;
59743
+ this.reportChainSeparators(node, this.featurePaths.resolveDeclarationPath(node).segments, accept);
59744
+ }
59745
+ // REQ-389 — issue #213. A connector end (`connect`, `flow`, `bind`,
59746
+ // `succession`, `interface`, `allocate`, the transition ends) keeps its whole
59747
+ // path in one `RelationPath` datatype string, so there is no `chainSegs` list —
59748
+ // but it reaches its target through the same OwnedReferenceSubsetting as
59749
+ // `perform` and `exhibit`, and the rule does not change with the statement that
59750
+ // carries it. `reference` is the `references` / `::>` tail, which is a second
59751
+ // OwnedReferenceSubsetting on the same end and is judged the same way.
59752
+ checkConnectorEndSeparators(node, accept) {
59753
+ for (const property3 of ["path", "reference"]) {
59754
+ const text = node[property3];
59755
+ if (typeof text !== "string" || !text.includes("."))
59756
+ continue;
59757
+ this.reportChainSeparators(node, this.featurePaths.resolvePropertyPath(node, property3).segments, accept);
59758
+ }
59759
+ }
59760
+ /** RES019 for one resolved path: the first non-final link that is chained with
59761
+ * `.` from something that can never be a Feature. */
59762
+ reportChainSeparators(node, segments, accept) {
59763
+ for (let index = 0; index + 1 < segments.length; index += 1) {
59764
+ if (segments[index + 1].separator !== ".")
59765
+ continue;
59766
+ const owner = segments[index].target;
59767
+ if (!owner || !isDefinitionOnlyNode(owner))
59768
+ continue;
59769
+ accept(severity("RES019", "warning"), DIAGNOSTIC_MESSAGES.RES019_DEFINITION_IN_FEATURE_CHAIN(segments[index].text, segments[index + 1].text), { node, range: segments[index].cst.range, code: "RES019" });
59770
+ return;
59771
+ }
59772
+ }
59604
59773
  // REQ-169, REQ-329 — SSM001 def/usage mismatch for parts
59605
59774
  checkPartDecl(node, accept) {
59606
59775
  if (!node.isDef && node.name && /^[A-Z]/.test(node.name)) {
@@ -61465,6 +61634,11 @@ ${baseIndent}}`;
61465
61634
  Document: this.checkDocument.bind(this),
61466
61635
  PartDecl: this.checkPartDecl.bind(this),
61467
61636
  PathExpr: this.checkPathExpr.bind(this),
61637
+ // issue #213 — RES019: the `::` vs `.` rule on the reference-form
61638
+ // declarations that reach their target through OwnedReferenceSubsetting.
61639
+ PerformStmt: this.checkChainSeparators.bind(this),
61640
+ ExhibitStateDecl: this.checkChainSeparators.bind(this),
61641
+ ConnectorEnd: this.checkConnectorEndSeparators.bind(this),
61468
61642
  RequirementDecl: this.checkRequirementDecl.bind(this),
61469
61643
  AttributeDecl: this.checkAttributeDecl.bind(this),
61470
61644
  ConstraintDecl: this.checkConstraintDecl.bind(this),
@@ -69105,7 +69279,7 @@ async function runValidation(command) {
69105
69279
  }
69106
69280
 
69107
69281
  // src/main.ts
69108
- var VERSION2 = true ? "0.15.2" : "dev";
69282
+ var VERSION2 = true ? "0.15.3" : "dev";
69109
69283
  async function main(argv) {
69110
69284
  const command = parseArgs(argv);
69111
69285
  if (command.kind === "help") {