Check that a variable name of variable declaration does not shadow an enum definition.
This commit is contained in:
@@ -179,43 +179,54 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
|
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var variableName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var line = ctx.start.getLine();
|
||||||
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
if (!declaredType.isPrimitiveType() && this.structDefs.get(declaredType.getName()) == null) {
|
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
|
||||||
String error = "Type " + declaredType.getName() + " not defined.";
|
var error = "Type " + declaredType.getName() + " not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.vars.get(name) != null) {
|
if (structDefs.containsKey(variableName)) {
|
||||||
String error = "Redeclaration of variable with name \"" + name + "\".";
|
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);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the appropriate instance
|
// Create the appropriate instance
|
||||||
VariableDeclaration result;
|
VariableDeclaration variableDeclaration;
|
||||||
if (ctx.expression() != null) {
|
if (ctx.expression() != null) {
|
||||||
Node expression = this.visit(ctx.expression());
|
var expression = visit(ctx.expression());
|
||||||
try {
|
try {
|
||||||
declaredType.combine(expression.type);
|
declaredType.combine(expression.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||||
}
|
}
|
||||||
result = new VariableDeclaration(name, (Expression) expression);
|
variableDeclaration = new VariableDeclaration(variableName, (Expression) expression);
|
||||||
result.initialized = true;
|
variableDeclaration.initialized = true;
|
||||||
} else {
|
} else {
|
||||||
result = new VariableDeclaration(name);
|
variableDeclaration = new VariableDeclaration(variableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add it to the global map of variable declarations
|
// Add it to the global map of variable declarations
|
||||||
this.vars.put(name, result);
|
vars.put(variableName, variableDeclaration);
|
||||||
|
|
||||||
result.line = line;
|
variableDeclaration.line = line;
|
||||||
result.col = col;
|
variableDeclaration.col = col;
|
||||||
result.type = declaredType;
|
variableDeclaration.type = declaredType;
|
||||||
return result;
|
|
||||||
|
return variableDeclaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,34 +1,94 @@
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.ContextAnalysis;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class VariableDeclarationTest {
|
public class VariableDeclarationTest {
|
||||||
@Test
|
@Test
|
||||||
void typeNotDefined() {
|
void shouldNotThrowIfDeclaredTypeIsAStruct() {
|
||||||
ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; return 1; } foo();");
|
// given
|
||||||
var funcs = Helper.getFuncs(tree);
|
var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let a: bar; return 1; } foo();");
|
||||||
var structs = Helper.getStructs(tree);
|
var ctxAnal = new ContextAnalysis(
|
||||||
var enums = Helper.getEnums(tree);
|
Helper.getFuncs(tree),
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(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());
|
assertEquals("Error in line 1:22 Type unk not defined.", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void variableRedeclaration()
|
void shouldThrowExceptionIfVariableIsRedeclared() {
|
||||||
{
|
// given
|
||||||
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
|
var tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
|
||||||
var funcs = Helper.getFuncs(tree);
|
var ctxAnal = new ContextAnalysis(
|
||||||
var structs = Helper.getStructs(tree);
|
Helper.getFuncs(tree),
|
||||||
var enums = Helper.getEnums(tree);
|
Helper.getStructs(tree),
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(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());
|
assertEquals("Error in line 1:34 Redeclaration of variable with name \"x\".", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user