From 6a9e538ecfb75e396535eb82fba61767d1b95b10 Mon Sep 17 00:00:00 2001 From: Manoel Brunnen Date: Sat, 7 Nov 2026 23:30:39 +0100 Subject: [PATCH] Report connectors without ends and make messages abstract KerML 8.3.4.5.3 requires two related features of a concrete connector and 8.3.4.5.2 exactly two of any binding connector. The rules skipped a connector with no ends. A message has no ends and is always abstract (SysML 8.2.2.16, 7.16.2), but was lowered as concrete, so the wider rule needs the flag: the full form gets it for the 40 messages of the corpus. The opensysml fixture k49 gains the finding on its abstract binding with no ends. A typing test wrote an end-less connection as a shorthand; it is abstract now. --- CHANGELOG.md | 7 ++++ crates/sysmlv2-model/src/check.rs | 38 +++++++++++++++++++ .../sysmlv2-model/src/check/relationships.rs | 4 +- crates/sysmlv2-model/src/json.rs | 9 ++++- crates/sysmlv2-parser/tests/check.rs | 2 +- .../tests/fixtures/full-form-baseline.tsv | 2 +- .../fixtures/opensysml/baseline-library.json | 7 ++++ .../tests/fixtures/opensysml/baseline.json | 7 ++++ 8 files changed, 70 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d370d5e..6189305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +### Fixed + +- A message is abstract (SysML 8.2.2.16 `Message : FlowUsage = ... { isAbstract = true }`, 7.16.2 "A message is always abstract"): `message m of M;` exports `isAbstract: true`. The full-form baseline moves for the corpus's 40 messages. +- `validateConnectorRelatedFeatures` reports a concrete connector with no ends, and `validateBindingConnectorIsBinary` a binding connector with no ends, abstract or not (KerML 8.3.4.5.3, 8.3.4.5.2); both reported only one end before. The opensysml fixture `k49-binding-connector-with-three-ends` gains the finding on its `abstract binding b2;`. + ## v0.11.3 ### Added diff --git a/crates/sysmlv2-model/src/check.rs b/crates/sysmlv2-model/src/check.rs index da39904..688522d 100644 --- a/crates/sysmlv2-model/src/check.rs +++ b/crates/sysmlv2-model/src/check.rs @@ -1168,6 +1168,44 @@ mod tests { assert_eq!(shown(&on_threads), shown(&in_line)); } + /// KerML 8.3.4.5.3 validateConnectorRelatedFeatures and 8.3.4.5.2 + /// validateBindingConnectorIsBinary hold without ends too. A message + /// is always abstract (SysML 7.16.2, 8.2.2.16), so it needs no ends. + #[test] + fn connectors_without_ends() { + let mut model = Model::new(); + model.add_source( + "user.kerml", + "package P { + classifier C { + connector c; + abstract connector d; + abstract binding b; + } + }", + ); + model.add_source( + "user.sysml", + "package Q { item def M; part p { message m of M; } }", + ); + let found: Vec = findings(&model) + .into_iter() + .map(|(_, m)| m) + .filter(|m| m.contains("connector")) + .collect(); + assert_eq!(found.len(), 2, "{found:?}"); + assert!( + found + .iter() + .any(|m| m.contains("validateConnectorRelatedFeatures")) + ); + assert!( + found + .iter() + .any(|m| m.contains("validateBindingConnectorIsBinary")) + ); + } + #[test] fn semantic_checks_walk_tables_without_copying_them() { // Fresh build: library rows and user rows share one storage. diff --git a/crates/sysmlv2-model/src/check/relationships.rs b/crates/sysmlv2-model/src/check/relationships.rs index 8d69b33..872caf5 100644 --- a/crates/sysmlv2-model/src/check/relationships.rs +++ b/crates/sysmlv2-model/src/check/relationships.rs @@ -85,7 +85,7 @@ pub(super) fn validate( Vec::new() }; if is_a!(b.elements[e], "BindingConnector") { - if !ends.is_empty() && ends.len() != 2 { + if ends.len() != 2 { report( "validateBindingConnectorIsBinary", "A binding connector must have exactly two ends", @@ -95,7 +95,7 @@ pub(super) fn validate( bindings.push((unit, span, g.typed(b, *a), g.typed(b, *z))); } } - if is_a!(b.elements[e], "Connector") && !flag(b, e, "isAbstract") && ends.len() == 1 { + if is_a!(b.elements[e], "Connector") && !flag(b, e, "isAbstract") && ends.len() < 2 { report( "validateConnectorRelatedFeatures", "A concrete connector must relate at least two features", diff --git a/crates/sysmlv2-model/src/json.rs b/crates/sysmlv2-model/src/json.rs index 447a4d7..07098ca 100644 --- a/crates/sysmlv2-model/src/json.rs +++ b/crates/sysmlv2-model/src/json.rs @@ -5422,11 +5422,16 @@ impl Builder { self.emit_multiplicity(cf, mult, body_scope); } } - // A variation is implicitly abstract (pilot `UsageAdapter.postProcess`). + // A variation is implicitly abstract (pilot `UsageAdapter.postProcess`), + // and so is a message (SysML 8.2.2.16 `{ isAbstract = true }`). self.set( e, "isAbstract", - json!(u.prefix.is_abstract || u.prefix.is_variation), + json!( + u.prefix.is_abstract + || u.prefix.is_variation + || matches!(u.kind, UsageKind::Message) + ), ); if declares_variation(ty) { self.set(e, "isVariation", json!(u.prefix.is_variation)); diff --git a/crates/sysmlv2-parser/tests/check.rs b/crates/sysmlv2-parser/tests/check.rs index 0447f72..44da504 100644 --- a/crates/sysmlv2-parser/tests/check.rs +++ b/crates/sysmlv2-parser/tests/check.rs @@ -858,7 +858,7 @@ fn usage_typing_respects_metaclass_inheritance_and_single_type_rules() { ); } assert!(!sem_warnings("attribute def D; occurrence x : D;").is_empty()); - assert!(sem_warnings("interface def I; connection x : I;").is_empty()); + assert!(sem_warnings("interface def I; abstract connection x : I;").is_empty()); assert!(sem_warnings("requirement def R; constraint x : R;").is_empty()); assert!(sem_warnings("calc def C; action x : C;").is_empty()); assert!(sem_warnings("metadata def M; item x : M;").is_empty()); diff --git a/crates/sysmlv2-parser/tests/fixtures/full-form-baseline.tsv b/crates/sysmlv2-parser/tests/fixtures/full-form-baseline.tsv index 5049dae..ee62ce9 100644 --- a/crates/sysmlv2-parser/tests/fixtures/full-form-baseline.tsv +++ b/crates/sysmlv2-parser/tests/fixtures/full-form-baseline.tsv @@ -3990,7 +3990,7 @@ FlowUsage inheritedMembership 6739ccf89892f02c 138 0 FlowUsage input 6739ccf89892f02c 138 0 FlowUsage interaction 437960690ffe330c 138 8 FlowUsage intersectingType 6739ccf89892f02c 138 0 -FlowUsage isAbstract 7a1270115d01d5aa 138 0 +FlowUsage isAbstract 27db29890d44d382 138 40 FlowUsage isComposite d6f5f34e0a9981d7 138 137 FlowUsage isConjugated 7a1270115d01d5aa 138 0 FlowUsage isConstant 7a1270115d01d5aa 138 0 diff --git a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json index b0e256d..80385f6 100644 --- a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json +++ b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json @@ -2025,6 +2025,13 @@ "severity": "Error", "stage": "semantics", "start": 322 + }, + { + "end": 392, + "message": "A binding connector must have exactly two ends [validateBindingConnectorIsBinary]", + "severity": "Error", + "stage": "semantics", + "start": 390 } ], "status": "diagnostic baseline" diff --git a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json index e5e723b..100d4e7 100644 --- a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json +++ b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json @@ -2060,6 +2060,13 @@ "severity": "Error", "stage": "semantics", "start": 322 + }, + { + "end": 392, + "message": "A binding connector must have exactly two ends [validateBindingConnectorIsBinary]", + "severity": "Error", + "stage": "semantics", + "start": 390 } ], "status": "diagnostic baseline"