From f1d705e7a5740c2d47eb67fc94fb7603a867bcc4 Mon Sep 17 00:00:00 2001 From: Felix Steinfurth Date: Wed, 30 Sep 2026 18:04:01 +0200 Subject: [PATCH 1/3] Undirected attributes no longer default to input DirectionCompleter.isIn now returns true only for "in" and "inout"; attributes without a direction are neither in nor out. Tests that relied on the old implicit "in" now declare their port attributes explicitly as "in". Also adds in/out to the ItemUsage symbol and registers the DirectionCompleter for SysMLOccurrences. Co-Authored-By: Claude Opus 5.5 --- .../de/monticore/lang/SysMLOccurrences.mc4 | 4 ++ .../monticore/lang/sysmlv2/SysMLv2Tool.java | 1 + .../completers/DirectionCompleter.java | 10 +++-- .../ComponentTypeSymbolAdapterTest.java | 2 +- ...FieldAccessExpressionInStateUsageTest.java | 14 +++--- ...AccessExpressionInConstraintUsageTest.java | 12 ++--- ...FieldAccessExpressionInStateUsageTest.java | 44 +++++++++---------- 7 files changed, 47 insertions(+), 40 deletions(-) diff --git a/language/src/main/grammars/de/monticore/lang/SysMLOccurrences.mc4 b/language/src/main/grammars/de/monticore/lang/SysMLOccurrences.mc4 index abc0ec9b0..9e7f5a526 100644 --- a/language/src/main/grammars/de/monticore/lang/SysMLOccurrences.mc4 +++ b/language/src/main/grammars/de/monticore/lang/SysMLOccurrences.mc4 @@ -53,4 +53,8 @@ component grammar SysMLOccurrences extends SysMLBasis { SysMLElement* "}" | ";")? ; + symbolrule ItemUsage = types:de.monticore.types.check.SymTypeExpression* + in:boolean + out:boolean; + } diff --git a/language/src/main/java/de/monticore/lang/sysmlv2/SysMLv2Tool.java b/language/src/main/java/de/monticore/lang/sysmlv2/SysMLv2Tool.java index b5beec32b..0531a5013 100644 --- a/language/src/main/java/de/monticore/lang/sysmlv2/SysMLv2Tool.java +++ b/language/src/main/java/de/monticore/lang/sysmlv2/SysMLv2Tool.java @@ -210,6 +210,7 @@ public void completeSymbolTable(ASTSysMLModel node) { traverser.add4SysMLBasis(new SpecializationCompleter()); traverser.add4SysMLBasis(new DirectionCompleter()); traverser.add4SysMLParts(new DirectionCompleter()); + traverser.add4SysMLOccurrences(new DirectionCompleter()); traverser.add4SysMLParts(new ConvertEnumUsagesToFields()); traverser.add4SysMLParts(new SysMLPartsCompleter()); traverser.add4SysMLParts(new IdentifierCompleter()); diff --git a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java index aedd9fc25..60353f449 100644 --- a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java +++ b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java @@ -4,24 +4,26 @@ import de.monticore.lang.sysmlbasis._ast.ASTAnonymousUsage; import de.monticore.lang.sysmlbasis._ast.ASTModifier; import de.monticore.lang.sysmlbasis._visitor.SysMLBasisVisitor2; +import de.monticore.lang.sysmloccurrences._visitor.SysMLOccurrencesVisitor2; import de.monticore.lang.sysmlparts._ast.ASTAttributeUsage; import de.monticore.lang.sysmlparts._visitor.SysMLPartsVisitor2; /** * Sets the isIn / isOut properties of symbols based on Modifiers parsed to the AST. */ -public class DirectionCompleter implements SysMLBasisVisitor2, SysMLPartsVisitor2 { +public class DirectionCompleter implements SysMLBasisVisitor2, SysMLPartsVisitor2, + SysMLOccurrencesVisitor2 { /** - * Returns whether this is an input. Defaults to {@code true} if no direction was explicitly set. The keyword "inout" + * Returns whether this is an input. Defaults to {@code false} if no direction was explicitly set. The keyword "inout" * yields true. */ protected boolean isIn(ASTModifier modifier) { - return modifier.isIn() || !modifier.isOut() && !modifier.isReturn(); + return modifier.isIn() || modifier.isInout(); } /** - * Returns whether this is an input. Defaults to {@code false} if no direction was explicitly set. The keyword "inout" + * Returns whether this is an output. Defaults to {@code false} if no direction was explicitly set. The keyword "inout" * yields true. The keyword "return" is treated as output. */ protected boolean isOut(ASTModifier modifier) { diff --git a/language/src/test/java/symboltable/ComponentTypeSymbolAdapterTest.java b/language/src/test/java/symboltable/ComponentTypeSymbolAdapterTest.java index a5f457112..3d7f58171 100644 --- a/language/src/test/java/symboltable/ComponentTypeSymbolAdapterTest.java +++ b/language/src/test/java/symboltable/ComponentTypeSymbolAdapterTest.java @@ -169,7 +169,7 @@ public void testUserEnum() throws IOException { @Test public void testPortFullName() throws IOException { - var as = process("port def A { attribute b: boolean; } part def B { port a: A; }"); + var as = process("port def A { in attribute b: boolean; } part def B { port a: A; }"); var port = as.resolveMildComponent("B").get().getPorts().get(0); // Name muss widerspiegeln, dass der Port in B liegt und "a.b" heisst diff --git a/language/src/test/java/typecheck/FieldAccessExpressionInStateUsageTest.java b/language/src/test/java/typecheck/FieldAccessExpressionInStateUsageTest.java index e9ad4304a..014b3ffec 100644 --- a/language/src/test/java/typecheck/FieldAccessExpressionInStateUsageTest.java +++ b/language/src/test/java/typecheck/FieldAccessExpressionInStateUsageTest.java @@ -57,23 +57,23 @@ public void reset() { static Stream createInputs() { return Stream.of( Arguments.of( - "port def F { attribute a: boolean; }" + + "port def F { in attribute a: boolean; }" + "part def X { port f: F; state s { transition first S if f.a then S; } }", false), Arguments.of( - "port def F { attribute a: boolean[3]; }" + + "port def F { in attribute a: boolean[3]; }" + "part def X { port f: F; state s { transition first S if f.a[1] then S; } }", false), Arguments.of( - "port def F { attribute a: boolean; } " + + "port def F { in attribute a: boolean; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a then S; } }", false), Arguments.of( - "port def F { attribute a: boolean[3]; } " + + "port def F { in attribute a: boolean[3]; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a[1] then S; } }", false), Arguments.of( - "port def F { attribute a: boolean; } part def X { port f: F; constraint e { f.a } }", + "port def F { in attribute a: boolean; } part def X { port f: F; constraint e { f.a } }", true) ); } @@ -106,7 +106,7 @@ public void test(String model, boolean isStream) throws IOException { @Test public void testProblem() throws IOException { - var ast = parser.parse_String("attribute a: boolean;"); + var ast = parser.parse_String("in attribute a: boolean;"); assertThat(ast).isPresent(); var astSysmlmodel = ast.get(); SysMLv2Mill.scopesGenitorDelegator().createFromAST(astSysmlmodel); @@ -117,7 +117,7 @@ public void testProblem() throws IOException { @Test public void testProblem2() throws IOException { - var ast = parser.parse_String("port def P { attribute a: boolean; }"); + var ast = parser.parse_String("port def P { in attribute a: boolean; }"); assertThat(ast).isPresent(); var astSysmlmodel = ast.get(); SysMLv2Mill.scopesGenitorDelegator().createFromAST(astSysmlmodel); diff --git a/language/src/test/java/types3/FieldAccessExpressionInConstraintUsageTest.java b/language/src/test/java/types3/FieldAccessExpressionInConstraintUsageTest.java index fa96a73b7..bd1b7d10a 100644 --- a/language/src/test/java/types3/FieldAccessExpressionInConstraintUsageTest.java +++ b/language/src/test/java/types3/FieldAccessExpressionInConstraintUsageTest.java @@ -106,11 +106,11 @@ public void reset() { @ParameterizedTest @ValueSource(strings = { - "port def F { attribute a: boolean[2]; } part s { port f: F; constraint e { f.a[1] } }", - "port def F { attribute a: boolean; } part s { port f: F[1]; constraint e { f[1].a } }", + "port def F { in attribute a: boolean[2]; } part s { port f: F; constraint e { f.a[1] } }", + "port def F { in attribute a: boolean; } part s { port f: F[1]; constraint e { f[1].a } }", - "port def F { attribute a: boolean[2]; } part s { port f: F; constraint e { f[1] } }", - "port def F { attribute a: boolean; } part s { port f: F[1]; constraint e { f[1] } }" + "port def F { in attribute a: boolean[2]; } part s { port f: F; constraint e { f[1] } }", + "port def F { in attribute a: boolean; } part s { port f: F[1]; constraint e { f[1] } }" }) public void test4ValidExpr1(String model) throws IOException { var ast = parser.parse_String(model); assertThat(ast).isPresent(); @@ -131,8 +131,8 @@ public void reset() { @ParameterizedTest @ValueSource(strings = { - "port def F { attribute a: boolean; } part s { port f: F[2]; constraint e { f.a } }", - "port def F { attribute a: boolean; } part s { port f: F[2]; constraint e { f } }" + "port def F { in attribute a: boolean; } part s { port f: F[2]; constraint e { f.a } }", + "port def F { in attribute a: boolean; } part s { port f: F[2]; constraint e { f } }" }) public void test4InvalidExpr(String model) throws IOException { var ast = parser.parse_String(model);; diff --git a/language/src/test/java/types3/FieldAccessExpressionInStateUsageTest.java b/language/src/test/java/types3/FieldAccessExpressionInStateUsageTest.java index 28f5b2408..581089e41 100644 --- a/language/src/test/java/types3/FieldAccessExpressionInStateUsageTest.java +++ b/language/src/test/java/types3/FieldAccessExpressionInStateUsageTest.java @@ -121,74 +121,74 @@ public void reset() { static Stream createInputs() { return Stream.of( Arguments.of( - "port def F { attribute a: boolean; }" + + "port def F { in attribute a: boolean; }" + "part def X { port f: F; state s { transition first S if f then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; }" + + "port def F { in attribute a: boolean[3]; }" + "part def X { port f: F; state s { transition first S if f[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; } " + + "port def F { in attribute a: boolean; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; } " + + "port def F { in attribute a: boolean[3]; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1][1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; } part def X { port f: F; constraint e { f } }") + "port def F { in attribute a: boolean; } part def X { port f: F; constraint e { f } }") ,Arguments.of( - "port def F { attribute a: boolean; }" + + "port def F { in attribute a: boolean; }" + "part def X { port f: F; state s { transition first S if f.a then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; }" + + "port def F { in attribute a: boolean[3]; }" + "part def X { port f: F; state s { transition first S if f.a[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; } " + + "port def F { in attribute a: boolean; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; } " + + "port def F { in attribute a: boolean[3]; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; } part def X { port f: F; constraint e { f.a } }") + "port def F { in attribute a: boolean; } part def X { port f: F; constraint e { f.a } }") ,Arguments.of( - "port def F { attribute a: boolean; } part def X { port f: F[3]; constraint e { f[1].a } }") + "port def F { in attribute a: boolean; } part def X { port f: F[3]; constraint e { f[1].a } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; }" + + "port def F { in attribute a: boolean; in attribute b: nat; }" + "part def X { port f: F; state s { transition first S if f.a then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; attribute b: nat[3]; }" + + "port def F { in attribute a: boolean[3]; in attribute b: nat[3]; }" + "part def X { port f: F; state s { transition first S if f.a[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; } " + + "port def F { in attribute a: boolean; in attribute b: nat; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; attribute b: nat[3]; } " + + "port def F { in attribute a: boolean[3]; in attribute b: nat[3]; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1].a[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; } part def X { port f: F; constraint e { f.a } }") + "port def F { in attribute a: boolean; in attribute b: nat; } part def X { port f: F; constraint e { f.a } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; } part def X { port f: F[3]; constraint e { f[1].a } }") + "port def F { in attribute a: boolean; in attribute b: nat; } part def X { port f: F[3]; constraint e { f[1].a } }") ); } static Stream createInvalidInputs() { return Stream.of( Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; }" + + "port def F { in attribute a: boolean; in attribute b: nat; }" + "part def X { port f: F; state s { transition first S if f then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; attribute b: nat[3]; }" + + "port def F { in attribute a: boolean[3]; in attribute b: nat[3]; }" + "part def X { port f: F; state s { transition first S if f[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; } " + + "port def F { in attribute a: boolean; in attribute b: nat; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean[3]; attribute b: nat[3]; } " + + "port def F { in attribute a: boolean[3]; in attribute b: nat[3]; } " + "part def X { port f: F[3]; exhibit state s { transition first S if f[1][1] then S; } }") ,Arguments.of( - "port def F { attribute a: boolean; attribute b: nat; } part def X { port f: F; constraint e { f } }") + "port def F { in attribute a: boolean; in attribute b: nat; } part def X { port f: F; constraint e { f } }") // TODO one field access with a stream method ); } From 6a36f87461582269acc4ff936d09e556ce6582ec Mon Sep 17 00:00:00 2001 From: FSteinfurth <135279295+FSteinfurth@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:37:19 +0200 Subject: [PATCH 2/3] Update language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java Co-authored-by: adriancostin-sd --- .../sysmlv2/symboltable/completers/DirectionCompleter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java index 60353f449..1b942f595 100644 --- a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java +++ b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java @@ -54,4 +54,11 @@ public void visit(ASTAttributeUsage node) { } } + @Override + public void visit(ASTItemUsage node) { + if(node.isPresentSymbol()) { + node.getSymbol().setIn(isIn(node.getModifier())); + node.getSymbol().setOut(isOut(node.getModifier())); + } + } } From 9c521d692f1c5c7007f68e4db08667fb7ca300d6 Mon Sep 17 00:00:00 2001 From: adriancostin-sd Date: Wed, 30 Sep 2026 18:41:08 +0200 Subject: [PATCH 3/3] missing import --- .../lang/sysmlv2/symboltable/completers/DirectionCompleter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java index 1b942f595..7a85f648d 100644 --- a/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java +++ b/language/src/main/java/de/monticore/lang/sysmlv2/symboltable/completers/DirectionCompleter.java @@ -4,6 +4,7 @@ import de.monticore.lang.sysmlbasis._ast.ASTAnonymousUsage; import de.monticore.lang.sysmlbasis._ast.ASTModifier; import de.monticore.lang.sysmlbasis._visitor.SysMLBasisVisitor2; +import de.monticore.lang.sysmloccurrences._ast.ASTItemUsage; import de.monticore.lang.sysmloccurrences._visitor.SysMLOccurrencesVisitor2; import de.monticore.lang.sysmlparts._ast.ASTAttributeUsage; import de.monticore.lang.sysmlparts._visitor.SysMLPartsVisitor2;