Check that a variable name of variable declaration does not shadow an enum definition.

This commit is contained in:
2023-03-15 23:33:12 +01:00
parent f77d6a002d
commit 30dfbbbbba
2 changed files with 111 additions and 40 deletions

View File

@@ -179,43 +179,54 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
var variableName = ctx.IDENT().getText();
var declaredType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (!declaredType.isPrimitiveType() && this.structDefs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
var error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (this.vars.get(name) != null) {
String error = "Redeclaration of variable with name \"" + name + "\".";
if (structDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (vars.get(variableName) != null) {
var error = "Redeclaration of variable with name \"" + variableName + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create the appropriate instance
VariableDeclaration result;
VariableDeclaration variableDeclaration;
if (ctx.expression() != null) {
Node expression = this.visit(ctx.expression());
var expression = visit(ctx.expression());
try {
declaredType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result = new VariableDeclaration(name, (Expression) expression);
result.initialized = true;
variableDeclaration = new VariableDeclaration(variableName, (Expression) expression);
variableDeclaration.initialized = true;
} else {
result = new VariableDeclaration(name);
variableDeclaration = new VariableDeclaration(variableName);
}
// Add it to the global map of variable declarations
this.vars.put(name, result);
vars.put(variableName, variableDeclaration);
result.line = line;
result.col = col;
result.type = declaredType;
return result;
variableDeclaration.line = line;
variableDeclaration.col = col;
variableDeclaration.type = declaredType;
return variableDeclaration;
}
@Override

View File

@@ -1,34 +1,94 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
import static org.junit.jupiter.api.Assertions.*;
public class VariableDeclarationTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; 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));
void shouldNotThrowIfDeclaredTypeIsAStruct() {
// given
var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let a: bar; return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfDeclaredTypeIsAnEnum() {
// given
var tree = Helper.prepareParser("enum bar { A, B } function foo(): int { let a: bar; return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowExceptionIfDeclaredNameShadowsEnumName() {
// given
var tree = Helper.prepareParser("enum bar { A, B } function foo(): int { let bar: 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:40 Variable name bar shadows an enum of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfDeclaredNameShadowsStruct() {
// given
var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let bar: 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:45 Variable name bar shadows a struct of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfDeclaredTypeIsNotDefined() {
// given
var tree = Helper.prepareParser("function foo(): int { let X: unk; 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:22 Type unk not defined.", e.getMessage());
}
@Test
void variableRedeclaration()
{
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; 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));
void shouldThrowExceptionIfVariableIsRedeclared() {
// given
var tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; 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:34 Redeclaration of variable with name \"x\".", e.getMessage());
}
}