Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<SymTypeExpression> 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());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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
Comment thread
jra-se marked this conversation as resolved.
implements MCReturnStatementsVisitor2 {

Map<ASTNode, Optional<ASTExpression>> returnExpressionMap;

public ReturnStatementCollectionVisitor(
Map<ASTNode, Optional<ASTExpression>> returnExpressionList) {
this.returnExpressionMap = returnExpressionList;
}

public Map<ASTNode, Optional<ASTExpression>> 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());
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -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<ASTReturnStatement> returnStatements = returnStatementCollector.getReturnStatementList();

SymTypeExpression typeOfMethod;
// support deprecated behavior
if (typeCheck != null) {
typeOfMethod = typeCheck.symTypeFromAST(node.getMCReturnType());
} else {
typeOfMethod = TypeCheck3.symTypeFromAST(node.getMCReturnType());
}
Map<ASTNode, Optional<ASTExpression>> 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<ASTNode, Optional<ASTExpression>> entry : returnStatements.entrySet()) {
if (entry.getValue().isPresent()) {
Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart());
}
}
Expand All @@ -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<ASTNode, Optional<ASTExpression>> 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());
}
Expand All @@ -93,19 +62,29 @@ public void check(ASTMethodDeclaration node) {
}
}
}

private static class JavaReturnStatementCollector implements MCReturnStatementsVisitor2 {

List<ASTReturnStatement> returnStatementList = new ArrayList<>();

private List<ASTReturnStatement> 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.
* <p>
* 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.
* <p>
* <b>Note:</b> 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<ASTNode, Optional<ASTExpression>> getReturnExpressions(ASTNode node) {
JavaLightTraverser traverser = JavaLightMill.inheritanceTraverser();
Comment thread
jra-se marked this conversation as resolved.
Map<ASTNode, Optional<ASTExpression>> returnExpressions = new HashMap<>();
ReturnStatementCollectionVisitor returnStatementCollectionVisitor = new ReturnStatementCollectionVisitor(returnExpressions);
traverser.add4MCReturnStatements(returnStatementCollectionVisitor);
node.accept(traverser);
return returnStatementCollectionVisitor.getReturnExpressions();
}
}
Loading