Skip to content

Commit f71b504

Browse files
committed
fix #770
1 parent b9f7041 commit f71b504

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ private enum Phase { LIGHT, HEAVY }
5454
private @Nullable Element lastElement = null;
5555
private final HashSet<String> trveWrapperFuncs = new HashSet<>();
5656
private final HashMap<String, HashSet<FunctionCall>> wrapperCalls = new HashMap<>();
57+
private final Map<ClassDef, Map<GlobalVarDef, Integer>> classVarInitOrderCache = new HashMap<>();
5758

5859
public WurstValidator(WurstModel root) {
5960
this.prog = root;
@@ -2881,6 +2882,59 @@ private void checkVarDef(VarDef v) {
28812882
v.addError("Initial value of variable " + v.getName() + " is 'null'. Specify a concrete type.");
28822883
}
28832884

2885+
if (v instanceof GlobalVarDef) {
2886+
checkClassMemberInitializerOrder((GlobalVarDef) v);
2887+
}
2888+
2889+
}
2890+
2891+
private void checkClassMemberInitializerOrder(GlobalVarDef v) {
2892+
if (!v.attrIsDynamicClassMember()) {
2893+
return;
2894+
}
2895+
if (!(v.getInitialExpr() instanceof Expr)) {
2896+
return;
2897+
}
2898+
ClassDef owner = v.attrNearestClassDef();
2899+
if (owner == null) {
2900+
return;
2901+
}
2902+
Map<GlobalVarDef, Integer> order = classVarInitOrder(owner);
2903+
Integer currentPos = order.get(v);
2904+
if (currentPos == null) {
2905+
return;
2906+
}
2907+
Expr initExpr = (Expr) v.getInitialExpr();
2908+
for (NameDef used : initExpr.attrReadVariables()) {
2909+
if (!(used instanceof GlobalVarDef)) {
2910+
continue;
2911+
}
2912+
GlobalVarDef usedVar = (GlobalVarDef) used;
2913+
if (usedVar == v || !usedVar.attrIsDynamicClassMember()) {
2914+
continue;
2915+
}
2916+
if (usedVar.attrNearestClassDef() != owner) {
2917+
continue;
2918+
}
2919+
Integer usedPos = order.get(usedVar);
2920+
if (usedPos != null && usedPos > currentPos) {
2921+
v.addError("Class variable <" + usedVar.getName() + "> is used before it is initialized.");
2922+
return;
2923+
}
2924+
}
2925+
}
2926+
2927+
private Map<GlobalVarDef, Integer> classVarInitOrder(ClassDef classDef) {
2928+
return classVarInitOrderCache.computeIfAbsent(classDef, cd -> {
2929+
Map<GlobalVarDef, Integer> order = new IdentityHashMap<>();
2930+
int index = 0;
2931+
for (GlobalVarDef var : cd.getVars()) {
2932+
if (var.attrIsDynamicClassMember()) {
2933+
order.put(var, index++);
2934+
}
2935+
}
2936+
return order;
2937+
});
28842938
}
28852939

28862940
private void checkLocalShadowing(LocalVarDef v) {

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ public void test_init_order_globals_warning_jass() {
162162
"endpackage");
163163
}
164164

165+
@Test
166+
public void classVarInitOrderShouldError_770() {
167+
testAssertErrorsLines(false, "used before it is initialized",
168+
"package test",
169+
"class B",
170+
" var i = 0",
171+
" function get() returns int",
172+
" return i",
173+
"class A",
174+
" private var foo = b.get()",
175+
" private var b = new B()",
176+
"endpackage");
177+
}
178+
165179

166180
@Test
167181
public void test_for_from() {

0 commit comments

Comments
 (0)