diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 4ba5b6f..825a5bc 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -774,69 +774,72 @@ public class ContextAnalysis extends KlangBaseVisitor { @Override public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) { - String name = ctx.funcName.getText(); - int line = ctx.start.getLine(); - int col = ctx.start.getCharPositionInLine(); - Type returnType = Type.getByName(ctx.returnType.type().getText()); - this.currentDeclaredReturnType = returnType; - this.currentFunctionDefinitionName = name; + var name = ctx.funcName.getText(); + var returnType = Type.getByName(ctx.returnType.type().getText()); + currentDeclaredReturnType = returnType; + currentFunctionDefinitionName = name; - if (!returnType.isPrimitiveType() && this.structDefs.get(returnType.getName()) == null) { - String error = "Type " + returnType.getName() + " not defined."; + if (!returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName())) { + var line = ctx.returnType.start.getLine(); + var col = ctx.returnType.start.getCharPositionInLine(); + var error = "Type " + returnType.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } // Create a new set for the variables of the current function - // this will be filled in the variable declaration visitor aswell - this.vars = new HashMap<>(); + // this will be filled in the variable declaration visitor as well + vars = new HashMap<>(); - // Process the paremter list by visiting every paremter in it - int paramCount = ctx.params.parameter().size(); - Parameter[] params = new Parameter[paramCount]; + // Process the parameter list by visiting every parameter in it + var paramCount = ctx.params.parameter().size(); + var params = new Parameter[paramCount]; for (int i = 0; i < paramCount; i++) { // Add the parameter to the list of parameters - Parameter param = (Parameter) this.visit(ctx.params.parameter(i)); + var param = (Parameter) visit(ctx.params.parameter(i)); params[i] = param; // add the param as a variable - VariableDeclaration var = new VariableDeclaration(param.name); + var var = new VariableDeclaration(param.name); var.initialized = true; // parameters can always be considered initialized var.type = param.type; - this.vars.put(param.name, var); + vars.put(param.name, var); } // Visit the block, make sure that a return value is guaranteed - Node block = this.visit(ctx.braced_block()); + var block = visit(ctx.braced_block()); if (block.type == null) { - String error = "Function " + name + " has to return something of type " + returnType.getName() + "."; + var line = ctx.braced_block().start.getLine(); + var col = ctx.braced_block().start.getCharPositionInLine(); + var error = "Function " + name + " has to return something of type " + returnType.getName() + "."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } - FunctionDefinition result = new FunctionDefinition(name, params, (Block) block); - result.type = returnType; + var functionDef = new FunctionDefinition(name, params, (Block) block); + functionDef.type = returnType; + functionDef.line = ctx.start.getLine(); + functionDef.col = ctx.start.getCharPositionInLine(); - result.line = ctx.start.getLine(); - result.col = ctx.start.getCharPositionInLine(); - return result; + return functionDef; } @Override public Node visitParameter(KlangParser.ParameterContext ctx) { - String name = ctx.IDENT().getText(); - int line = ctx.start.getLine(); - int col = ctx.start.getCharPositionInLine(); - Type type = Type.getByName(ctx.type_annotation().type().getText()); + var parameterName = ctx.IDENT().getText(); + var parameterType = Type.getByName(ctx.type_annotation().type().getText()); - if (!type.isPrimitiveType() && this.structDefs.get(type.getName()) == null) { - String error = "Type " + type.getName() + " not defined."; + if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) { + var line = ctx.type_annotation().start.getLine(); + var col = ctx.type_annotation().start.getCharPositionInLine(); + String error = "Type " + parameterType.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } - Parameter result = new Parameter(name); - result.type = type; - result.line = line; - result.col = col; - return result; + var parameter = new Parameter(parameterName); + parameter.type = parameterType; + parameter.line = ctx.start.getLine(); + parameter.col = ctx.start.getCharPositionInLine(); + + return parameter; } @Override diff --git a/src/test/java/FunctionDefinitionTest.java b/src/test/java/FunctionDefinitionTest.java index 665b6e9..0d79c6a 100644 --- a/src/test/java/FunctionDefinitionTest.java +++ b/src/test/java/FunctionDefinitionTest.java @@ -1,34 +1,82 @@ -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import org.antlr.v4.runtime.tree.ParseTree; import org.junit.jupiter.api.Test; import de.hsrm.compiler.Klang.ContextAnalysis; +import static org.junit.jupiter.api.Assertions.*; + public class FunctionDefinitionTest { @Test - void typeNotDefined() { - ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();"); - var funcs = Helper.getFuncs(tree); - var structs = Helper.getStructs(tree); - var enums = Helper.getEnums(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); - - Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); - assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage()); + void shouldNotThrowIfReturnTypeIsReferringToAnEnum() { + // given + var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo();"); + var ctxAnal = new ContextAnalysis( + Helper.getFuncs(tree), + Helper.getStructs(tree), + Helper.getEnums(tree) + ); + + // when / then + assertDoesNotThrow(() -> ctxAnal.visit(tree)); } @Test - void noReturnExpression() { - ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();"); - var funcs = Helper.getFuncs(tree); - var structs = Helper.getStructs(tree); - var enums = Helper.getEnums(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); - - Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); - assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage()); + void shouldNotThrowIfParameterTypeIsReferringToAnEnum() { + // given + var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);"); + var ctxAnal = new ContextAnalysis( + Helper.getFuncs(tree), + Helper.getStructs(tree), + Helper.getEnums(tree) + ); + + // when / then + assertDoesNotThrow(() -> ctxAnal.visit(tree)); + } + + @Test + void shouldThrowExceptionIfParameterTypeIsNotDefined() { + // given + var tree = Helper.prepareParser("function foo(a: schwurbel): int { return 1; } foo();"); + var ctxAnal = new ContextAnalysis( + Helper.getFuncs(tree), + Helper.getStructs(tree), + Helper.getEnums(tree) + ); + + // when / then + var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); + assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage()); + } + + @Test + void shouldThrowExceptionIfReturnTypeIsNotDefined() { + // given + var tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();"); + var ctxAnal = new ContextAnalysis( + Helper.getFuncs(tree), + Helper.getStructs(tree), + Helper.getEnums(tree) + ); + + // when / then + var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); + assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage()); + } + + @Test + void shouldThrowExceptionIfReturnStatementIsMissing() { + // given + var tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();"); + var ctxAnal = new ContextAnalysis( + Helper.getFuncs(tree), + Helper.getStructs(tree), + Helper.getEnums(tree) + ); + + // when / then + var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); + assertEquals("Error in line 1:20 Function foo has to return something of type int.", e.getMessage()); } } \ No newline at end of file