ScriptTypeInfo is now properly compared to TypeInfo parents + removed parent constructor mismatch errors from interfaces

This commit is contained in:
bigguy345 committed 2026-03-11 08:17:23 +02:00
1 parent 1289778874
commit c88bd50be9
2 files changed
+54 -9

No files matched your search

@@ -979,20 +979,18 @@ public class ScriptTypeInfo extends TypeInfo {
// If no constructors defined in script type, it has an implicit default constructor
// Check if parent has a no-arg constructor
if (!hasConstructors()) {
boolean parentHasNoArg = false;
if (superClass instanceof ScriptTypeInfo) {
ScriptTypeInfo parentScript = (ScriptTypeInfo) superClass;
if (!parentScript.hasConstructors() || parentScript.findConstructor(0) != null) {
parentHasNoArg = true;
if (!parentScript.isInterface() && (!parentScript.hasConstructors() || parentScript.findConstructor(0) != null)) {
addConstructorMismatchError(superClass, superClass.getSimpleName());
}
} else {
Class<?> javaClass = superClass.getJavaClass();
if (javaClass != null) {
if (javaClass != null && !javaClass.isInterface()) {
try {
for (java.lang.reflect.Constructor<?> ctor : javaClass.getConstructors()) {
if (ctor.getParameterCount() == 0) {
parentHasNoArg = true;
addConstructorMismatchError(superClass, superClass.getSimpleName());
break;
}
}
@@ -1002,9 +1000,6 @@ public class ScriptTypeInfo extends TypeInfo {
}
}
if (!parentHasNoArg) {
addConstructorMismatchError(superClass, superClass.getSimpleName());
}
}
// If script type has constructors, we'd need to check super() calls - that's more complex
// For now, we just validate the implicit default constructor case
@@ -2,6 +2,8 @@ package noppes.npcs.client.gui.util.script.interpreter.type;
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
@@ -110,6 +112,14 @@ public final class TypeChecker {
return true;
}
// ScriptTypeInfo inheritance: actual is a script-defined type that may extend/implement expected.
// ScriptTypeInfo.getJavaClass() returns null, so the Java isAssignableFrom gate below won't fire.
if (actual instanceof ScriptTypeInfo) {
if (isScriptTypeAssignableTo((ScriptTypeInfo) actual, expected)) {
return true;
}
}
// Object type compatibility (check inheritance)
if (expected.getJavaClass() != null && actual.getJavaClass() != null) {
Class<?> expectedClass = expected.getJavaClass();
@@ -165,6 +175,46 @@ public final class TypeChecker {
return false;
}
/**
* Check assignability for ScriptTypeInfo by iteratively walking the extends/implements chain.
* Handles both direct and indirect inheritance (A extends B extends C implements D).
*/
private static boolean isScriptTypeAssignableTo(ScriptTypeInfo actual, TypeInfo expected) {
String expectedFull = expected.getFullName();
String expectedSimple = expected.getSimpleName();
Deque<TypeInfo> worklist = new ArrayDeque<>();
worklist.add(actual);
while (!worklist.isEmpty()) {
TypeInfo current = worklist.poll();
if (current == null || !current.isResolved()) continue;
String currentFull = current.getFullName();
if (currentFull != null) {
if (expectedFull != null && expectedFull.equals(currentFull)) return true;
if (expectedSimple != null && expectedSimple.equals(current.getSimpleName())) return true;
}
if (current instanceof ScriptTypeInfo) {
ScriptTypeInfo scriptCurrent = (ScriptTypeInfo) current;
TypeInfo superClass = scriptCurrent.getSuperClass();
if (superClass != null) {
worklist.add(superClass);
}
for (TypeInfo iface : scriptCurrent.getImplementedInterfaces()) {
worklist.add(iface);
}
} else if (current.getJavaClass() != null && expected.getJavaClass() != null) {
if (expected.getJavaClass().isAssignableFrom(current.getJavaClass())) {
return true;
}
}
}
return false;
}
private static boolean areTypeArgumentsCompatible(TypeInfo expected, TypeInfo actual) {
java.util.List<TypeInfo> expectedArgs = expected.getAppliedTypeArgs();
java.util.List<TypeInfo> actualArgs = actual.getAppliedTypeArgs();