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..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 @@ -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(), node.get_SourcePositionEnd()); + } } } } 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..be7ba8bd46 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnStatementCollectionVisitor.java @@ -0,0 +1,41 @@ +package de.monticore.javalight.cocos; + +import de.monticore.ast.ASTNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; + +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), the traverser may need to be extended to handle + * scope boundaries correctly. + */ +public class ReturnStatementCollectionVisitor + implements 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 5632d6de09..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 @@ -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) { - - // 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()); - } + Map> returnStatements = getReturnExpressions(node); + + 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()); } @@ -93,19 +62,29 @@ public void check(ASTMethodDeclaration node) { } } } - - private static class JavaReturnStatementCollector implements MCReturnStatementsVisitor2 { - - List returnStatementList = new ArrayList<>(); - - private List getReturnStatementList() { - return this.returnStatementList; - } - - @Override - public void visit(ASTReturnStatement node) { - returnStatementList.add(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 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 + */ + protected Map> getReturnExpressions(ASTNode node) { + JavaLightTraverser traverser = JavaLightMill.inheritanceTraverser(); + Map> returnExpressions = new HashMap<>(); + ReturnStatementCollectionVisitor returnStatementCollectionVisitor = new ReturnStatementCollectionVisitor(returnExpressions); + traverser.add4MCReturnStatements(returnStatementCollectionVisitor); + node.accept(traverser); + return returnStatementCollectionVisitor.getReturnExpressions(); } }