From 789670dbe46e5ba828d9706a6e18cc3bf4277dbf Mon Sep 17 00:00:00 2001 From: Janik Rapp Date: Fri, 7 Aug 2026 13:53:30 +0200 Subject: [PATCH 1/5] extracted JavaLight ReturnStatementCollection --- .../cocos/MethodExceptionThrows.java | 23 +++--- .../ReturnStatementCollectionVisitor.java | 35 +++++++++ .../cocos/ReturnTypeAssignmentIsValid.java | 71 +++++-------------- 3 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java index 39c1d95720..9653c6bdec 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java @@ -5,24 +5,31 @@ import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; import de.monticore.types.check.SymTypeExpression; -import de.monticore.types.check.SymTypeExpressionFactory; -import de.monticore.types.check.TypeCheck; +import de.monticore.types3.SymTypeRelations; +import de.monticore.types3.util.OOWithinScopeBasicSymbolsResolver; import de.se_rwth.commons.logging.Log; +import java.util.Optional; + public class MethodExceptionThrows implements JavaLightASTMethodDeclarationCoCo { public static final String ERROR_CODE = "0xA0811"; - public static final String ERROR_MSG_FORMAT = " No exception of type '%s' can be thrown. An exception must be a subtype of Throwable."; + public static final String ERROR_MSG_FORMAT = + " No exception of type '%s' can be thrown. An exception must be a subtype of Throwable."; @Override public void check(ASTMethodDeclaration node) { if (node.isPresentThrows()) { - SymTypeExpression throwable = SymTypeExpressionFactory.createTypeObjectViaSurrogate("java.lang.Throwable", node.getEnclosingScope()); - for (SymTypeExpression exception : node.getSymbol().getExceptionsList()) { - if (!TypeCheck.isSubtypeOf(exception, throwable)) { - Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, exception.print()), - node.get_SourcePositionStart()); + Optional throwable = + OOWithinScopeBasicSymbolsResolver.resolveType(node.getEnclosingScope(), + "java.lang.Throwable"); + if (throwable.isPresent()) { + for (SymTypeExpression exception : node.getSymbol().getExceptionsList()) { + if (!SymTypeRelations.isSubTypeOf(exception, throwable.get())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, exception.print()), + node.get_SourcePositionStart()); + } } } } diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java new file mode 100644 index 0000000000..f3fca3d5d5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java @@ -0,0 +1,35 @@ +package de.monticore.javalight.cocos; + +import de.monticore.ast.ASTNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.javalight._visitor.JavaLightVisitor2; +import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; + +import java.util.Map; +import java.util.Optional; + +public class ReturnStatementCollectionVisitor + implements JavaLightVisitor2, MCReturnStatementsVisitor2 { + + Map> returnExpressionMap; + + public ReturnStatementCollectionVisitor( + Map> returnExpressionList) { + this.returnExpressionMap = returnExpressionList; + } + + public Map> getReturnExpressions() { + return this.returnExpressionMap; + } + + @Override + public void visit(ASTReturnStatement node) { + if (node.isPresentExpression()) { + this.returnExpressionMap.put(node, Optional.of(node.getExpression())); + } + else { + this.returnExpressionMap.put(node, Optional.empty()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java index 40dac9640d..fe33c0acb5 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java @@ -1,20 +1,18 @@ /* (c) https://github.com/MontiCore/monticore */ package de.monticore.javalight.cocos; +import de.monticore.ast.ASTNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; import de.monticore.javalight.JavaLightMill; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; import de.monticore.javalight._visitor.JavaLightTraverser; -import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; -import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; import de.monticore.types.check.SymTypeExpression; -import de.monticore.types.check.TypeCalculator; import de.monticore.types3.SymTypeRelations; import de.monticore.types3.TypeCheck3; import de.se_rwth.commons.logging.Log; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class ReturnTypeAssignmentIsValid implements JavaLightASTMethodDeclarationCoCo { @@ -30,42 +28,19 @@ public class ReturnTypeAssignmentIsValid implements JavaLightASTMethodDeclaratio public static final String ERROR_MSG_FORMAT_3 = "Return statement must be of the type of the method or a subtype of it."; - @Deprecated - TypeCalculator typeCheck; - - /** - * @deprecated use default constructor - */ - @Deprecated - public ReturnTypeAssignmentIsValid(TypeCalculator typeCheck) { - this.typeCheck = typeCheck; - } - public ReturnTypeAssignmentIsValid() {} @Override public void check(ASTMethodDeclaration node) { + Map> returnStatements = getReturnExpressions(node); - // Collect return-statements - JavaLightTraverser traverser = JavaLightMill.traverser(); - JavaReturnStatementCollector returnStatementCollector = new JavaReturnStatementCollector(); - traverser.add4MCReturnStatements(returnStatementCollector); - node.accept(traverser); - List returnStatements = returnStatementCollector.getReturnStatementList(); - - SymTypeExpression typeOfMethod; - // support deprecated behavior - if (typeCheck != null) { - typeOfMethod = typeCheck.symTypeFromAST(node.getMCReturnType()); - } else { - typeOfMethod = TypeCheck3.symTypeFromAST(node.getMCReturnType()); - } + SymTypeExpression typeOfMethod = TypeCheck3.symTypeFromAST(node.getMCReturnType()); // Check return-Statements if (node.isPresentMCJavaBlock()) { if (typeOfMethod.isVoidType()) { - for (ASTReturnStatement statement : returnStatements) { - if (statement.isPresentExpression()) { + for (Map.Entry> entry : returnStatements.entrySet()) { + if (entry.getValue().isPresent()) { Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); } } @@ -74,17 +49,11 @@ public void check(ASTMethodDeclaration node) { Log.error(ERROR_CODE_2 + ERROR_MSG_FORMAT_2, node.get_SourcePositionStart()); } if (!typeOfMethod.isVoidType() && !returnStatements.isEmpty()) { - for (ASTReturnStatement returnStatement : returnStatements) { - if (!returnStatement.isPresentExpression()) { + for (Map.Entry> entry : returnStatements.entrySet()) { + if (entry.getValue().isEmpty()) { Log.error(ERROR_CODE_2 + ERROR_MSG_FORMAT_2, node.get_SourcePositionStart()); } else { - SymTypeExpression returnType; - // support deprecated behavior - if (typeCheck != null) { - returnType = typeCheck.typeOf(returnStatement.getExpression()); - } else { - returnType = TypeCheck3.typeOf(returnStatement.getExpression()); - } + SymTypeExpression returnType = TypeCheck3.typeOf(entry.getValue().get(), typeOfMethod); if (!SymTypeRelations.isCompatible(typeOfMethod, returnType)) { Log.error(ERROR_CODE_3 + ERROR_MSG_FORMAT_3, node.get_SourcePositionStart()); } @@ -94,18 +63,12 @@ public void check(ASTMethodDeclaration node) { } } - private class JavaReturnStatementCollector implements MCReturnStatementsVisitor2 { - - List returnStatementList = new ArrayList<>(); - - private List getReturnStatementList() { - return this.returnStatementList; - } - - @Override - public void visit(ASTReturnStatement node) { - returnStatementList.add(node); - } - + protected Map> getReturnExpressions(ASTNode node) { + JavaLightTraverser traverser = JavaLightMill.inheritanceTraverser(); + Map> returnExpressions = new HashMap<>(); + ReturnStatementCollectionVisitor returnStatementCollectionVisitor = new ReturnStatementCollectionVisitor(returnExpressions); + traverser.add4MCReturnStatements(returnStatementCollectionVisitor); + traverser.visit(node); + return returnStatementCollectionVisitor.getReturnExpressions(); } } From 0ed1edae29f6f262b15db5aca10e9bd895ae2f66 Mon Sep 17 00:00:00 2001 From: Janik Rapp Date: Fri, 7 Aug 2026 14:27:17 +0200 Subject: [PATCH 2/5] fix ReturnStatementCollection --- .../javalight/cocos/ReturnStatementCollectionVisitor.java | 3 +-- .../monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java index f3fca3d5d5..787ddd6feb 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java @@ -2,7 +2,6 @@ import de.monticore.ast.ASTNode; import de.monticore.expressions.expressionsbasis._ast.ASTExpression; -import de.monticore.javalight._visitor.JavaLightVisitor2; import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; @@ -10,7 +9,7 @@ import java.util.Optional; public class ReturnStatementCollectionVisitor - implements JavaLightVisitor2, MCReturnStatementsVisitor2 { + implements MCReturnStatementsVisitor2 { Map> returnExpressionMap; diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java index fe33c0acb5..e760d33e06 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java @@ -68,7 +68,7 @@ protected Map> getReturnExpressions(ASTNode nod Map> returnExpressions = new HashMap<>(); ReturnStatementCollectionVisitor returnStatementCollectionVisitor = new ReturnStatementCollectionVisitor(returnExpressions); traverser.add4MCReturnStatements(returnStatementCollectionVisitor); - traverser.visit(node); + node.accept(traverser); return returnStatementCollectionVisitor.getReturnExpressions(); } } From f26fa720e8346f21d4b42f38f90ee309d367d5ef Mon Sep 17 00:00:00 2001 From: Janik Rapp Date: Thu, 1 Oct 2026 16:02:08 +0200 Subject: [PATCH 3/5] add SourcePositionEnd --- .../de/monticore/javalight/cocos/MethodExceptionThrows.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java index 9653c6bdec..2db2c4947a 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java @@ -28,7 +28,7 @@ public void check(ASTMethodDeclaration node) { for (SymTypeExpression exception : node.getSymbol().getExceptionsList()) { if (!SymTypeRelations.isSubTypeOf(exception, throwable.get())) { Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, exception.print()), - node.get_SourcePositionStart()); + node.get_SourcePositionStart(), node.get_SourcePositionEnd()); } } } From 04358acb9e218bd81977bfdeb74a053b39200941 Mon Sep 17 00:00:00 2001 From: Janik Rapp Date: Thu, 1 Oct 2026 16:59:19 +0200 Subject: [PATCH 4/5] added some documentation --- .../ReturnStatementCollectionVisitor.java | 7 +++++++ .../cocos/ReturnTypeAssignmentIsValid.java | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java index 787ddd6feb..2fe5e02805 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java @@ -8,6 +8,13 @@ import java.util.Map; import java.util.Optional; +/** + * Basic visitor for collecting return statements and their associated expressions. + *

+ * For languages with nested scopes that allow their own return statements + * (e.g., lambda expressions), this visitor may need to be extended to handle + * scope boundaries correctly. + */ public class ReturnStatementCollectionVisitor implements MCReturnStatementsVisitor2 { diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java index cbdc8e52d8..6818feaeab 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java @@ -62,7 +62,23 @@ public void check(ASTMethodDeclaration node) { } } } - + + /** + * Collects all return statements from the given AST node using a traverser. + *

+ * This method extracts return statements and their associated expressions + * from the method body. It returns a map where each return statement node + * is mapped to an Optional containing either the return expression or an empty + * Optional for return statements without an expression. + *

+ * Note: For languages with nested scopes that allow their own return statements + * (e.g., lambda expressions, anonymous classes, or inner class methods), + * this method may need to be overridden to handle scope boundaries correctly. + * See {@link ReturnStatementCollectionVisitor} for more information. + * + * @param node the AST node (typically a method declaration) to traverse for return statements + * @return a map associating each return statement with its optional expression + */ protected Map> getReturnExpressions(ASTNode node) { JavaLightTraverser traverser = JavaLightMill.inheritanceTraverser(); Map> returnExpressions = new HashMap<>(); From 3f4ae32982ddbcaa3fab48a7bccafaad8279ce96 Mon Sep 17 00:00:00 2001 From: Janik Rapp Date: Fri, 2 Oct 2026 09:59:40 +0200 Subject: [PATCH 5/5] minor changes to clarify the documentation --- .../javalight/cocos/ReturnStatementCollectionVisitor.java | 2 +- .../javalight/cocos/ReturnTypeAssignmentIsValid.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java index 2fe5e02805..be7ba8bd46 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java @@ -12,7 +12,7 @@ * Basic visitor for collecting return statements and their associated expressions. *

* For languages with nested scopes that allow their own return statements - * (e.g., lambda expressions), this visitor may need to be extended to handle + * (e.g., lambda expressions), the traverser may need to be extended to handle * scope boundaries correctly. */ public class ReturnStatementCollectionVisitor diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java index 6818feaeab..4e75121a14 100644 --- a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java @@ -73,8 +73,8 @@ public void check(ASTMethodDeclaration node) { *

* Note: For languages with nested scopes that allow their own return statements * (e.g., lambda expressions, anonymous classes, or inner class methods), - * this method may need to be overridden to handle scope boundaries correctly. - * See {@link ReturnStatementCollectionVisitor} for more information. + * this method may need to be overridden to use an extended traverser that + * handles scope boundaries correctly. * * @param node the AST node (typically a method declaration) to traverse for return statements * @return a map associating each return statement with its optional expression