diff --git a/cdlang/src/main/java/de/monticore/cd4analysis/cocos/CD4AnalysisCoCos.java b/cdlang/src/main/java/de/monticore/cd4analysis/cocos/CD4AnalysisCoCos.java index 804bbfefd..88b31703e 100644 --- a/cdlang/src/main/java/de/monticore/cd4analysis/cocos/CD4AnalysisCoCos.java +++ b/cdlang/src/main/java/de/monticore/cd4analysis/cocos/CD4AnalysisCoCos.java @@ -3,6 +3,7 @@ import de.monticore.cd.cocos.CoCoParent; import de.monticore.cd4analysis._cocos.CD4AnalysisCoCoChecker; +import de.monticore.cd4analysis.cocos.ebnf.CDAssociationValidRoleSymbolsInScope; public class CD4AnalysisCoCos extends CoCoParent { @Override @@ -16,4 +17,9 @@ protected void addCheckerForAllCoCos(CD4AnalysisCoCoChecker checker) { addCheckerForMcgCoCos(checker); addCheckerForMcg2EbnfCoCos(checker); } + + @Override + protected void addEbnfCoCos(CD4AnalysisCoCoChecker checker) { + checker.addCoCo(new CDAssociationValidRoleSymbolsInScope()); + } } diff --git a/cdlang/src/main/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolsInScope.java b/cdlang/src/main/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolsInScope.java new file mode 100644 index 000000000..f6953a805 --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolsInScope.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd4analysis.cocos.ebnf; + +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.cdassociation._symboltable.CDRoleSymbol; +import de.monticore.cdbasis._ast.ASTCDDefinition; +import de.monticore.cdbasis._cocos.CDBasisASTCDDefinitionCoCo; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This CoCo checks for the usage of identical role names across multiple + * associations for a given reference type. The presence of duplicate role + * names can lead to ambiguity when accessing the referenced elements, + * and results in incomplete storage of CDRoleSymbols within the + * scope and symbol table. + *

+ * This CoCo only considers the roles of an isolated reference type, as this + * serves as the fundamental basis. More in-depth CoCos use the symbol table + * and are faulty if it is faulty. + *

+ * Example: + *

+ * class Person {
+ * }
+ * association knows [1] Person -> Person [*];
+ * 
+ * Since no explicit role names were provided, the name of the reference type (in lowercase) + * is used as the role name. Since this is identical in both cases, only one of the two + * CDRoleSymbols is stored in the scope of the Person type. + */ +public class CDAssociationValidRoleSymbolsInScope implements CDBasisASTCDDefinitionCoCo { + + @Override + public void check(ASTCDDefinition definition) { + // maps class to all contained role names + Map> knownRoleNames = new HashMap<>(); + for (ASTCDAssociation assoc : definition.getCDAssociationsList()) { + // left side + CDRoleSymbol rightRoleSymbol = assoc.getRight().getSymbol(); + String leftTypeFQN = assoc.getLeftQualifiedName().getQName(); + knownRoleNames.putIfAbsent(leftTypeFQN, new ArrayList<>()); + + if (knownRoleNames.get(leftTypeFQN).contains(rightRoleSymbol.getName())) { + Log.error("0xCDCE4: Duplicate role (" + rightRoleSymbol.getName() + ") in reference Type " + + assoc.getLeft().getMCQualifiedType().printType(), + rightRoleSymbol.getSourcePosition()); + } + else { + knownRoleNames.get(leftTypeFQN).add(rightRoleSymbol.getName()); + } + + // right side + CDRoleSymbol leftRoleSymbol = assoc.getLeft().getSymbol(); + String rightTypeFQN = assoc.getRightQualifiedName().getQName(); + knownRoleNames.putIfAbsent(rightTypeFQN, new ArrayList<>()); + + if (knownRoleNames.get(rightTypeFQN).contains(leftRoleSymbol.getName())) { + Log.error("0xCDCE4: Duplicate role (" + leftRoleSymbol.getName() + ") in reference Type " + + assoc.getRight().getMCQualifiedType().printType(), + leftRoleSymbol.getSourcePosition()); + } + else { + knownRoleNames.get(rightTypeFQN).add(leftRoleSymbol.getName()); + } + } + } +} diff --git a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java index ea10dce79..37695a546 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java @@ -166,6 +166,8 @@ public static void addRoleToTheirType(CDRoleSymbol symbol, TypeSymbol otherType) if (!spannedScope.getCDRoleSymbols().containsKey(symbol.getName())) { // add the symbol to the type; add to all relevant lists spannedScope.add(symbol); + } else { + Log.warn("0xCDCE5: Ignored duplicate role symbol (" + symbol.getName()+") for reference Type "+otherType.getFullName(), symbol.getSourcePosition()); } } diff --git a/cdlang/src/test/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolInScopeTest.java b/cdlang/src/test/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolInScopeTest.java new file mode 100644 index 000000000..c56f6e3ca --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd4analysis/cocos/ebnf/CDAssociationValidRoleSymbolInScopeTest.java @@ -0,0 +1,58 @@ +package de.monticore.cd4analysis.cocos.ebnf; + +import de.monticore.cd4analysis.CD4AnalysisTestBasis; +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CDAssociationValidRoleSymbolInScopeTest extends CD4AnalysisTestBasis { + + @Test + public void testValid() throws IOException { + String modelPath = "cd4analysis/cocos/CDAssociationValidRoleSymbolInScope.cd"; + + coCoChecker.addCoCo(new CDAssociationValidRoleSymbolsInScope()); + ASTCDCompilationUnit ast = parse(modelPath); + prepareST(ast); + + coCoChecker.checkAll(ast); + assertEquals(0, Log.getFindings().size()); + } + + @Test + public void testInvalid() throws IOException { + String modelPath = "cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid.cd"; + + coCoChecker.addCoCo(new CDAssociationValidRoleSymbolsInScope()); + ASTCDCompilationUnit ast = parse(modelPath); + prepareST(ast); + + coCoChecker.checkAll(ast); + assertEquals(2, Log.getFindings().size()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xCDCE5")); + assertTrue(Log.getFindings().get(1).getMsg().startsWith("0xCDCE4")); + Log.clearFindings(); + } + + @Test + public void testInvalid2() throws IOException { + String modelPath = "cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid2.cd"; + + coCoChecker.addCoCo(new CDAssociationValidRoleSymbolsInScope()); + ASTCDCompilationUnit ast = parse(modelPath); + prepareST(ast); + + coCoChecker.checkAll(ast); + assertEquals(4, Log.getFindings().size()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xCDCE5")); + assertTrue(Log.getFindings().get(1).getMsg().startsWith("0xCDCE5")); + assertTrue(Log.getFindings().get(2).getMsg().startsWith("0xCDCE4")); + assertTrue(Log.getFindings().get(3).getMsg().startsWith("0xCDCE4")); + Log.clearFindings(); + } +} diff --git a/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScope.cd b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScope.cd new file mode 100644 index 000000000..034401105 --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScope.cd @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram CDAssociationValidRoleSymbolInScope { + + class Person { + public String fullName; + int age; + } + + association marriage [1] Person (partnerOpposite) <-> (partner) Person [1]; +} \ No newline at end of file diff --git a/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid.cd b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid.cd new file mode 100644 index 000000000..e18f9a4fe --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid.cd @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram CDAssociationValidRoleSymbolInScopeInvalid { + + class Person { + public String fullName; + int age; + } + + association marriage [1] Person <-> Person [1]; +} \ No newline at end of file diff --git a/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid2.cd b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid2.cd new file mode 100644 index 000000000..a7a846e51 --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd4analysis/cocos/CDAssociationValidRoleSymbolInScopeInvalid2.cd @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram CDAssociationValidRoleSymbolInScopeInvalid2 { + + class Person { + public String fullName; + int age; + } + + association mother [1] Person <-> (child) Person [1]; + association father [1] Person <-> (child) Person [1]; +} \ No newline at end of file diff --git a/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/InviDas.cd b/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/InviDas.cd index 8f5819b44..a09a9a4d4 100644 --- a/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/InviDas.cd +++ b/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/InviDas.cd @@ -111,7 +111,7 @@ classdiagram InviDas { String name; } - association DataEntry -> DataEntry [*]; + association DataEntry -> (linked) DataEntry [*]; association DataEntry -> DataCategory [*]; /************************************************************************************************* diff --git a/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/MaCoCo.cd b/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/MaCoCo.cd index e90e35bd4..41591fcfc 100644 --- a/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/MaCoCo.cd +++ b/cdlang/src/test/resources/de/monticore/cd4analysis/examples/industrial_strength_models/MaCoCo.cd @@ -24,8 +24,8 @@ classdiagram MaCoCo { } association [1] Konto (konto) <-> (gesamtBudget) Budget [0..1]; - association [1] Konto -> (kommentare) Freitext [*]; - association [1] Konto -> (notiz) Freitext [0..1]; + association [1] Konto (kommentiert) -> (kommentare) Freitext [*]; + association [1] Konto (annotiert) -> (notiz) Freitext [0..1]; association [1] Konto <-> MailAlert [*]; association [1] Konto -> (abgleichsKonto) ExternKonto [0..1]; diff --git a/cdtool/src/test/java/de/monticore/CDDiffCLIToolTest.java b/cdtool/src/test/java/de/monticore/CDDiffCLIToolTest.java index 9b63644c1..cd39e1289 100644 --- a/cdtool/src/test/java/de/monticore/CDDiffCLIToolTest.java +++ b/cdtool/src/test/java/de/monticore/CDDiffCLIToolTest.java @@ -33,11 +33,11 @@ public void init() { @Test public void testChain() { - final String cd1 = TOOL_PATH + "cddiff/Employees/Employees2.cd"; - final String cd2 = TOOL_PATH + "cddiff/Employees/Employees1.cd"; + final String cd1 = TOOL_PATH + "cddiff/Employees/Employees10.cd"; + final String cd2 = TOOL_PATH + "cddiff/Employees/Employees9.cd"; final String output = "./target/generated/chain"; String[] args = { - "-i", cd1, "--merge", cd2, "--semdiff", cd2, "-o", output, "-pp", "Employees12.cd" + "-i", cd1, "--merge", cd2, "--semdiff", cd2, "-o", output, "-pp", "Employees910.cd" }; CD4CodeTool.main(args); diff --git a/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees10.cd b/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees10.cd new file mode 100644 index 000000000..a682690b4 --- /dev/null +++ b/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees10.cd @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ + +import java.util.Date; +import java.lang.String; + +classdiagram Employees10 { + enum PositionKind{fullTime, partTime, external;} + + class Employee { + PositionKind kind; + } + + class Task { + Date startDate; + } + + class Manager extends Employee; + + association [*] Employee (manages) -> (managedBy) Manager [0..1]; + association [1] Employee (priorityTask) -- (createdBy) Task [0..1]; +} diff --git a/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees9.cd b/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees9.cd new file mode 100644 index 000000000..c8077269c --- /dev/null +++ b/cdtool/src/test/resources/de/monticore/cddiff/Employees/Employees9.cd @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ + +import java.util.Date; + +classdiagram Employees9 { + enum PositionKind{fullTime, partTime;} + + class Employee { + PositionKind kind; + } + + class Task { + Date startDate; + } + + class Manager; + + association [*] Employee (manages) -> (managedBy) Manager [0..1]; + association [1] Employee (tasks) -- (assignedTo) Task [*]; +}