From 793f54ce4ba60edeb7424db2f62406523d14ea99 Mon Sep 17 00:00:00 2001 From: MKZaito <127297267+MKZaito@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:12:39 +0100 Subject: [PATCH 1/3] CoCo3 --- .../lang/sysmlv2/cocos/MKPX_CoCo3.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 language/src/main/java/de/monticore/lang/sysmlv2/cocos/MKPX_CoCo3.java diff --git a/language/src/main/java/de/monticore/lang/sysmlv2/cocos/MKPX_CoCo3.java b/language/src/main/java/de/monticore/lang/sysmlv2/cocos/MKPX_CoCo3.java new file mode 100644 index 00000000..0e01ee3d --- /dev/null +++ b/language/src/main/java/de/monticore/lang/sysmlv2/cocos/MKPX_CoCo3.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.lang.sysmlv2.cocos; + +import de.monticore.lang.sysmlbasis._ast.ASTEndpoint; +import de.monticore.lang.sysmlparts._ast.ASTConnectionUsage; +import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTConnectionUsageCoCo; +import de.monticore.lang.sysmlparts._symboltable.ISysMLPartsScope; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.se_rwth.commons.logging.Log; + +/** + * CoCo3: Jeder in "connect a.b to c.d" verwendete Name von Subkomponenten (a und c) + * muss eindeutig im Modell vorhanden sein. + */ +public class MKPX_CoCo3 implements SysMLPartsASTConnectionUsageCoCo { + + @Override + public void check(ASTConnectionUsage node) { + if (!node.isPresentSrc() || !node.isPresentTgt()) { + return; // keine Verbindung + } + + var scope = node.getEnclosingScope(); + + checkSubcomponentQualifier(scope, node.getSrc(), node); + checkSubcomponentQualifier(scope, node.getTgt(), node); + } + + protected void checkSubcomponentQualifier(ISysMLPartsScope scope, + ASTEndpoint endpoint, + ASTConnectionUsage node) { + String qname = endpointQName(endpoint); + String subName = extractFirstSegment(qname); + + if (subName == null) { + return; + } + + int matches = scope + .resolvePartUsageLocallyMany(false, subName, + AccessModifier.ALL_INCLUSION, p -> true) + .size(); + + if (matches > 1) { + Log.error( + "0xMKPX03 The subcomponent name used in 'connect' \"" + subName + + "\" is not unique in the model.", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + } else if (matches != 1) { /** matches = 0 */ + Log.error( + "0xMKPX03 The subcomponent name used in 'connect' \"" + subName + + "\" does not exist in the model.", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + } + } + + protected String endpointQName(ASTEndpoint ep) { + return ep.getMCQualifiedName() != null ? ep.getMCQualifiedName().toString() : ""; + } + + protected String extractFirstSegment(String qname) { + int dot = qname.indexOf('.'); + if (dot > 0) { + return qname.substring(0, dot); + } + return null; + } +} From 605186128a112a6ec8463c15a729b548a38d0b8e Mon Sep 17 00:00:00 2001 From: MKZaito <127297267+MKZaito@users.noreply.github.com> Date: Tue, 9 Dec 2025 18:25:38 +0100 Subject: [PATCH 2/3] Test for MKPX_CoCo3 --- .../src/test/java/cocos/MKPXCoCo3Test.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 language/src/test/java/cocos/MKPXCoCo3Test.java diff --git a/language/src/test/java/cocos/MKPXCoCo3Test.java b/language/src/test/java/cocos/MKPXCoCo3Test.java new file mode 100644 index 00000000..a7b73864 --- /dev/null +++ b/language/src/test/java/cocos/MKPXCoCo3Test.java @@ -0,0 +1,103 @@ +/* (c) https://github.com/MontiCore/monticore */ +package cocos; + +import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTConnectionUsageCoCo; +import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTPartDefCoCo; +import de.monticore.lang.sysmlv2.SysMLv2Mill; +import de.monticore.lang.sysmlv2._ast.ASTSysMLModel; +import de.monticore.lang.sysmlv2._cocos.SysMLv2CoCoChecker; +import de.monticore.lang.sysmlv2._parser.SysMLv2Parser; +import de.monticore.lang.sysmlv2.cocos.MKPX_CoCo2; +import de.monticore.lang.sysmlv2.cocos.MKPX_CoCo3; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MKPXCoCo3Test { + + private static final String MODEL_PATH = "src/test/resources/parser"; + + private SysMLv2Parser parser = SysMLv2Mill.parser(); + + @BeforeAll + public static void init() { + Log.init(); + SysMLv2Mill.init(); + } + + @BeforeEach + public void reset() { + SysMLv2Mill.globalScope().clear(); + Log.getFindings().clear(); + Log.enableFailQuick(true); + } + + @Nested + public class MKPXCoCo3Tests { + @Test + public void testValid() throws IOException { + String validModel = + "part def A { port p: int; }" + + "part def B { port q: ~int; }" + + "part def System {" + + "part a: A;" + + "part b: B;" + + "connect a.p to b.q;" + + "}"; + + ASTSysMLModel ast = SysMLv2Mill.parser().parse_String(validModel).get(); + SysMLv2Mill.scopesGenitorDelegator().createFromAST(ast); + var checker = new SysMLv2CoCoChecker(); + checker.addCoCo((SysMLPartsASTConnectionUsageCoCo) new MKPX_CoCo3()); + checker.checkAll(ast); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInvalidUndefined() throws IOException { + String invalidModel = + "part def A { port p: int; }" + + "part def B { port q: ~int; }" + + "part def System {" + + "part a: A;" + + "connect a.p to c.q;" + + "}"; + + ASTSysMLModel ast = SysMLv2Mill.parser().parse_String(invalidModel).get(); + SysMLv2Mill.scopesGenitorDelegator().createFromAST(ast); + var checker = new SysMLv2CoCoChecker(); + checker.addCoCo((SysMLPartsASTConnectionUsageCoCo) new MKPX_CoCo3()); + Log.enableFailQuick(false); + checker.checkAll(ast); + assertTrue(Log.getFindings().stream() + .anyMatch(f -> f.getMsg().contains("0xMKPX03"))); + } + + @Test + public void testInvalidDuplicateName() throws IOException { + String invalidModel = + "part def A { port p; }" + + "part def System {" + + "part a: A;" + + "part a: A;" + + "connect a.p to a.p;" + + "}"; + + ASTSysMLModel ast = SysMLv2Mill.parser().parse_String(invalidModel).get(); + SysMLv2Mill.scopesGenitorDelegator().createFromAST(ast); + var checker = new SysMLv2CoCoChecker(); + checker.addCoCo(new MKPX_CoCo3()); + Log.enableFailQuick(false); + checker.checkAll(ast); + assertTrue(Log.getFindings().stream() + .anyMatch(f -> f.getMsg().contains("0xMKPX03"))); + } + } +} From 7437916c06d8bcb645d07de8dc165b809a387f48 Mon Sep 17 00:00:00 2001 From: MKZaito <127297267+MKZaito@users.noreply.github.com> Date: Tue, 9 Dec 2025 18:44:02 +0100 Subject: [PATCH 3/3] wrong import --- language/src/test/java/cocos/MKPXCoCo3Test.java | 1 - 1 file changed, 1 deletion(-) diff --git a/language/src/test/java/cocos/MKPXCoCo3Test.java b/language/src/test/java/cocos/MKPXCoCo3Test.java index a7b73864..051cbaf1 100644 --- a/language/src/test/java/cocos/MKPXCoCo3Test.java +++ b/language/src/test/java/cocos/MKPXCoCo3Test.java @@ -7,7 +7,6 @@ import de.monticore.lang.sysmlv2._ast.ASTSysMLModel; import de.monticore.lang.sysmlv2._cocos.SysMLv2CoCoChecker; import de.monticore.lang.sysmlv2._parser.SysMLv2Parser; -import de.monticore.lang.sysmlv2.cocos.MKPX_CoCo2; import de.monticore.lang.sysmlv2.cocos.MKPX_CoCo3; import de.se_rwth.commons.logging.Log; import org.junit.jupiter.api.BeforeAll;