sysml-validate 0.14.4 → 0.15.0
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 +138 -2
- package/out/main.js.map +2 -2
- package/package.json +1 -1
- package/resources/sysml.dimension-table.json +1 -1
package/out/main.js
CHANGED
|
@@ -58699,6 +58699,11 @@ var DIAGNOSTIC_MESSAGES = {
|
|
|
58699
58699
|
LEX004_INVALID_STRING_ESCAPE: (escape) => `Invalid escape '\\${escape}'. KerML Table 4 permits \\' \\" \\b \\f \\t \\n and \\\\.`,
|
|
58700
58700
|
LEX008_NON_ASCII_IDENTIFIER: (name) => `Identifier '${name}' contains a non-ASCII character. Wrap it in single quotes ('${name}') to use it as an unrestricted name.`,
|
|
58701
58701
|
LEX009_TRAILING_WHITESPACE: "Trailing whitespace.",
|
|
58702
|
+
// REQ-385 — OMG SysML v2 Part 1 §7.17.3 restricts control nodes to the body of
|
|
58703
|
+
// an action definition or usage. Successions are NOT covered (§7.13.5 sanctions
|
|
58704
|
+
// `first a then b;` and the `then` occurrence shorthand in a part), so the
|
|
58705
|
+
// message names the construct it means rather than "action language".
|
|
58706
|
+
SEM011_ACTION_ONLY_CONSTRUCT: (construct, clause, owner) => `${construct} may only be declared in the body of an action definition or usage (OMG SysML v2 Part 1 \xA7${clause}), not in ${owner}.`,
|
|
58702
58707
|
RES016_LIBRARY_SHADOWING: (name) => `'${name}' shadows a standard-library element of the same name; the library symbol is hidden in this scope.`,
|
|
58703
58708
|
// issue #183 — KerML namespace distinguishability (validateNamespaceDistinguishability).
|
|
58704
58709
|
// Owned siblings sharing a name/short name (or an alias clashing with an owned
|
|
@@ -59449,6 +59454,68 @@ var CONSTRAINT_SIDE_EFFECT_TYPES = /* @__PURE__ */ new Set([
|
|
|
59449
59454
|
"DoAction",
|
|
59450
59455
|
"ExitAction"
|
|
59451
59456
|
]);
|
|
59457
|
+
var ACTION_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
59458
|
+
"ActionDecl",
|
|
59459
|
+
"PerformStmt",
|
|
59460
|
+
"MergeNode",
|
|
59461
|
+
"DecideNode",
|
|
59462
|
+
"JoinNode",
|
|
59463
|
+
"ForkNode",
|
|
59464
|
+
"IfActionNode",
|
|
59465
|
+
"WhileLoopNode",
|
|
59466
|
+
"LoopNode",
|
|
59467
|
+
"ForLoopNode",
|
|
59468
|
+
"SendNode",
|
|
59469
|
+
"AcceptNode",
|
|
59470
|
+
"AssignNode",
|
|
59471
|
+
"TerminateNode",
|
|
59472
|
+
"StateDecl",
|
|
59473
|
+
"ExhibitStateDecl",
|
|
59474
|
+
"TransitionDecl",
|
|
59475
|
+
"EntryAction",
|
|
59476
|
+
"DoAction",
|
|
59477
|
+
"ExitAction",
|
|
59478
|
+
"CalcDecl",
|
|
59479
|
+
"CaseDecl",
|
|
59480
|
+
"UseCaseDecl",
|
|
59481
|
+
"AnalysisCaseDecl",
|
|
59482
|
+
"VerificationCaseDecl"
|
|
59483
|
+
]);
|
|
59484
|
+
var CONTROL_NODE_KEYWORDS = {
|
|
59485
|
+
MergeNode: "merge",
|
|
59486
|
+
DecideNode: "decide",
|
|
59487
|
+
JoinNode: "join",
|
|
59488
|
+
ForkNode: "fork"
|
|
59489
|
+
};
|
|
59490
|
+
var SUCCESSION_MEMBER_WRAPPERS = /* @__PURE__ */ new Set([
|
|
59491
|
+
"ThenActionMember",
|
|
59492
|
+
"ElseSuccessionMember",
|
|
59493
|
+
"PrefixMetadataMember"
|
|
59494
|
+
]);
|
|
59495
|
+
function actionOnlyConstruct(node) {
|
|
59496
|
+
const keyword = CONTROL_NODE_KEYWORDS[node.$type];
|
|
59497
|
+
return keyword ? { construct: `A '${keyword}' control node`, clause: "7.17.3" } : void 0;
|
|
59498
|
+
}
|
|
59499
|
+
function portionKindOf(node) {
|
|
59500
|
+
const mods = node.modifiers;
|
|
59501
|
+
if (!Array.isArray(mods))
|
|
59502
|
+
return void 0;
|
|
59503
|
+
return mods.find((m) => m === "timeslice" || m === "snapshot");
|
|
59504
|
+
}
|
|
59505
|
+
var NAMESPACE_ONLY_TYPES = /* @__PURE__ */ new Set(["Package", "Document", "NamespaceDecl"]);
|
|
59506
|
+
function owningTypeOf(node) {
|
|
59507
|
+
let owner = node.$container;
|
|
59508
|
+
while (owner && (owner.$type === "PrefixMetadataMember" || owner.$type === "ThenActionMember")) {
|
|
59509
|
+
owner = owner.$container;
|
|
59510
|
+
}
|
|
59511
|
+
if (!owner)
|
|
59512
|
+
return void 0;
|
|
59513
|
+
return NAMESPACE_ONLY_TYPES.has(owner.$type) ? void 0 : owner;
|
|
59514
|
+
}
|
|
59515
|
+
function declLabel(node, kind) {
|
|
59516
|
+
const name = node.name;
|
|
59517
|
+
return name ? `'${name}'` : `this '${kind}'`;
|
|
59518
|
+
}
|
|
59452
59519
|
var SysmlValidator = class _SysmlValidator {
|
|
59453
59520
|
indexManager;
|
|
59454
59521
|
langiumDocuments;
|
|
@@ -59538,6 +59605,26 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
59538
59605
|
}
|
|
59539
59606
|
}
|
|
59540
59607
|
}
|
|
59608
|
+
// REQ-385 — SEM011. A fork/join/decide/merge control node "can only be declared
|
|
59609
|
+
// in the body of an action definition or usage" (OMG SysML v2 Part 1 §7.17.3).
|
|
59610
|
+
// Written in a part, one parsed and drew silently, which is how a part came to
|
|
59611
|
+
// show control flow it cannot own (user report 2026-08-09). See
|
|
59612
|
+
// `actionOnlyConstruct` for the constructs deliberately NOT covered.
|
|
59613
|
+
checkActionFlowPlacement(node, accept) {
|
|
59614
|
+
const restricted = actionOnlyConstruct(node);
|
|
59615
|
+
if (!restricted)
|
|
59616
|
+
return;
|
|
59617
|
+
if (SUCCESSION_MEMBER_WRAPPERS.has(node.$container?.$type ?? ""))
|
|
59618
|
+
return;
|
|
59619
|
+
let owner = node.$container;
|
|
59620
|
+
while (owner && SUCCESSION_MEMBER_WRAPPERS.has(owner.$type))
|
|
59621
|
+
owner = owner.$container;
|
|
59622
|
+
if (!owner || ACTION_BODY_TYPES.has(owner.$type))
|
|
59623
|
+
return;
|
|
59624
|
+
const ownerName = owner.name;
|
|
59625
|
+
const ownerKind = DECL_KEYWORDS[owner.$type] ?? "this context";
|
|
59626
|
+
accept(severity("SEM011", "warning"), DIAGNOSTIC_MESSAGES.SEM011_ACTION_ONLY_CONSTRUCT(restricted.construct, restricted.clause, ownerName ? `${ownerKind} '${ownerName}'` : ownerKind), { node, code: "SEM011" });
|
|
59627
|
+
}
|
|
59541
59628
|
// REQ-148, REQ-294, REQ-315, REQ-330 — SEM006/SYN092 requirement definition must have a subject
|
|
59542
59629
|
checkRequirementDecl(node, accept) {
|
|
59543
59630
|
if (!node.isDef || !node.name)
|
|
@@ -59652,6 +59739,7 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
59652
59739
|
this.checkLanguageDisjointness(node, accept);
|
|
59653
59740
|
this.checkKermlWellFormedness(node, accept);
|
|
59654
59741
|
this.checkFilterExpressions(node, accept);
|
|
59742
|
+
this.checkOccurrencePortions(node, accept);
|
|
59655
59743
|
this.checkCrossReferences(node, accept);
|
|
59656
59744
|
this.checkAutoImportSuggestions(node, accept);
|
|
59657
59745
|
this.checkUnits(node, accept);
|
|
@@ -60535,6 +60623,49 @@ ${baseIndent}}`;
|
|
|
60535
60623
|
this.checkTypeComposition(decl, index, accept);
|
|
60536
60624
|
}
|
|
60537
60625
|
}
|
|
60626
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
60627
|
+
// REQ-160 — occurrence portions: `timeslice` / `snapshot` well-formedness
|
|
60628
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
60629
|
+
// The declaration kinds that are NOT occurrences, so a portion of their life is
|
|
60630
|
+
// meaningless. Kept as a deny-list rather than an allow-list because nearly every
|
|
60631
|
+
// SysML usage kind IS an OccurrenceUsage (part, item, action, state, port,
|
|
60632
|
+
// connection, case, …) while the data-valued ones are few and closed: an
|
|
60633
|
+
// AttributeUsage is typed by a DataType and an enumeration is a special attribute,
|
|
60634
|
+
// neither of which has a life to carve up.
|
|
60635
|
+
static NON_OCCURRENCE_OWNER_TYPES = /* @__PURE__ */ new Set(["AttributeDecl", "EnumDecl", "EnumValueDecl", "MetadataDecl", "MultiplicityDecl"]);
|
|
60636
|
+
// REQ-160 — OMG SysML v2 §8.2.2.9 (Occurrences) with the Kernel Semantic Library
|
|
60637
|
+
// `Occurrences::TimeSlice` / `Occurrences::Snapshot`:
|
|
60638
|
+
//
|
|
60639
|
+
// SSM015 — a portion is a portion OF something. Its owning type must be an
|
|
60640
|
+
// occurrence; declared at package level (or inside an attribute /
|
|
60641
|
+
// enumeration body) there is no life for it to be a portion of.
|
|
60642
|
+
// SSM016 — `Snapshot :> TimeSlice` with a zero duration: a snapshot is the
|
|
60643
|
+
// occurrence at ONE instant. Every portion of a zero-duration
|
|
60644
|
+
// occurrence is itself zero-duration, so a snapshot may own further
|
|
60645
|
+
// snapshots but never a `timeslice`.
|
|
60646
|
+
//
|
|
60647
|
+
// Both are structural — they read modifiers and owners only — so they hold
|
|
60648
|
+
// without resolving a single cross-reference.
|
|
60649
|
+
checkOccurrencePortions(node, accept) {
|
|
60650
|
+
for (const child of ast_utils_exports.streamAllContents(node)) {
|
|
60651
|
+
const kind = portionKindOf(child);
|
|
60652
|
+
if (!kind)
|
|
60653
|
+
continue;
|
|
60654
|
+
const label = declLabel(child, kind);
|
|
60655
|
+
const owner = owningTypeOf(child);
|
|
60656
|
+
if (!owner) {
|
|
60657
|
+
accept(severity("SSM015", "error"), `A '${kind}' is a portion of an occurrence's life, so ${label} must be declared inside an occurrence \u2014 not directly in a package.`, { node: child, code: "SSM015" });
|
|
60658
|
+
continue;
|
|
60659
|
+
}
|
|
60660
|
+
if (_SysmlValidator.NON_OCCURRENCE_OWNER_TYPES.has(owner.$type)) {
|
|
60661
|
+
accept(severity("SSM015", "error"), `A '${kind}' is a portion of an occurrence's life, and '${owner.name ?? owner.$type}' is not an occurrence.`, { node: child, code: "SSM015" });
|
|
60662
|
+
continue;
|
|
60663
|
+
}
|
|
60664
|
+
if (kind === "timeslice" && portionKindOf(owner) === "snapshot") {
|
|
60665
|
+
accept(severity("SSM016", "error"), `A 'snapshot' is a zero-duration timeslice, so ${label} cannot be a timeslice of it \u2014 a portion of an instant is itself a snapshot.`, { node: child, code: "SSM016" });
|
|
60666
|
+
}
|
|
60667
|
+
}
|
|
60668
|
+
}
|
|
60538
60669
|
// REQ-328 — KSM008: a `filter` membership condition must be Boolean. Only a
|
|
60539
60670
|
// *definitely* non-Boolean expression is flagged — a string or numeric/quantity
|
|
60540
60671
|
// literal, or an arithmetic/range binary expression. A bare feature path is left
|
|
@@ -61267,7 +61398,12 @@ ${baseIndent}}`;
|
|
|
61267
61398
|
StateDecl: this.checkStateDecl.bind(this),
|
|
61268
61399
|
Import: this.checkImport.bind(this),
|
|
61269
61400
|
Package: this.checkPackage.bind(this),
|
|
61270
|
-
VerifyStmt: this.checkVerifyStmt.bind(this)
|
|
61401
|
+
VerifyStmt: this.checkVerifyStmt.bind(this),
|
|
61402
|
+
// REQ-385 — SEM011: the action language OMG confines to an action body.
|
|
61403
|
+
MergeNode: this.checkActionFlowPlacement.bind(this),
|
|
61404
|
+
DecideNode: this.checkActionFlowPlacement.bind(this),
|
|
61405
|
+
JoinNode: this.checkActionFlowPlacement.bind(this),
|
|
61406
|
+
ForkNode: this.checkActionFlowPlacement.bind(this)
|
|
61271
61407
|
};
|
|
61272
61408
|
}
|
|
61273
61409
|
};
|
|
@@ -68895,7 +69031,7 @@ async function runValidation(command) {
|
|
|
68895
69031
|
}
|
|
68896
69032
|
|
|
68897
69033
|
// src/main.ts
|
|
68898
|
-
var VERSION2 = true ? "0.
|
|
69034
|
+
var VERSION2 = true ? "0.15.0" : "dev";
|
|
68899
69035
|
async function main(argv) {
|
|
68900
69036
|
const command = parseArgs(argv);
|
|
68901
69037
|
if (command.kind === "help") {
|