From eef4e9a92271b7e14de3ec878b21ed621079f459 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 10 Mar 2026 22:14:45 +0200 Subject: [PATCH] Type Arg class ScriptTypeInfo + TokenHoverInfo + display fixes --- .../script/interpreter/ScriptDocument.java | 38 ++++++++++++++----- .../interpreter/hover/TokenHoverInfo.java | 5 ++- .../interpreter/method/MethodCallInfo.java | 18 ++++++++- .../interpreter/type/GenericContext.java | 1 - .../script/interpreter/type/TypeInfo.java | 24 ++++++++---- .../interpreter/type/TypeSubstitutor.java | 4 +- 6 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java index cf9019122..3172630d0 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java @@ -908,20 +908,38 @@ public class ScriptDocument { if (trimmed.isEmpty()) continue; int extendsIdx = trimmed.indexOf("extends"); + String paramName = extendsIdx >= 0 ? trimmed.substring(0, extendsIdx).trim() : trimmed; + + int endIdx = 0; + while (endIdx < paramName.length() && + (Character.isJavaIdentifierPart(paramName.charAt(endIdx)) || paramName.charAt(endIdx) == '.')) { + endIdx++; + } + paramName = paramName.substring(0, endIdx); + + String boundName = null; if (extendsIdx >= 0) { - String paramName = trimmed.substring(0, extendsIdx).trim(); - String boundName = trimmed.substring(extendsIdx + 7).trim(); - TypeParamInfo typeParam = new TypeParamInfo(paramName, boundName.isEmpty() ? null : boundName, null); - if (!boundName.isEmpty()) { - TypeInfo boundTypeInfo = resolveType(boundName, declOffset); - if (boundTypeInfo != null && boundTypeInfo.isResolved()) { - typeParam.setBoundTypeInfo(boundTypeInfo); + String afterExtends = trimmed.substring(extendsIdx + 7).trim(); + if (!afterExtends.isEmpty()) { + int boundEndIdx = 0; + while (boundEndIdx < afterExtends.length() && + (Character.isJavaIdentifierPart(afterExtends.charAt(boundEndIdx)) || afterExtends.charAt(boundEndIdx) == '.')) { + boundEndIdx++; + } + if (boundEndIdx > 0) { + boundName = afterExtends.substring(0, boundEndIdx); } } - scriptType.addDeclaredTypeParam(typeParam); - } else { - scriptType.addDeclaredTypeParam(new TypeParamInfo(trimmed, null, null)); } + + TypeParamInfo typeParam = new TypeParamInfo(paramName, boundName, null); + if (boundName != null) { + TypeInfo boundTypeInfo = resolveType(boundName, declOffset); + if (boundTypeInfo != null && boundTypeInfo.isResolved()) { + typeParam.setBoundTypeInfo(boundTypeInfo); + } + } + scriptType.addDeclaredTypeParam(typeParam); } } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java index 4f8f43ace..e3b835dbb 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java @@ -733,9 +733,10 @@ public class TokenHoverInfo { addSegment("type parameter ", TokenType.MODIFIER.getHexColor()); addSegment(paramName, TokenType.GENERIC_TYPE_PARAM.getHexColor()); - if (typeInfo.getJavaClass() != null && typeInfo.getJavaClass() != Object.class) { + TypeInfo bound = typeInfo.getBoundType(); + if (bound != null && bound.getJavaClass() != null && bound.getJavaClass() != Object.class) { addSegment(" extends ", TokenType.MODIFIER.getHexColor()); - addSegment(typeInfo.getSimpleName(), getColorForTypeInfo(typeInfo)); + addSegment(bound.getSimpleName(), getColorForTypeInfo(bound)); } } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodCallInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodCallInfo.java index 9b4716ec9..4654b8f8b 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodCallInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodCallInfo.java @@ -428,8 +428,8 @@ public class MethodCallInfo { TypeInfo argType = arg.getResolvedType(); if (argType != null && paramType != null) { if (!TypeChecker.isTypeCompatible(paramType, argType)) { - setArgTypeError(i, "Expected " + paramType.getDisplayName() + - " but got " + argType.getDisplayName()); + setArgTypeError(i, "Expected " + getTypeDisplayName(paramType) + + " but got " + getTypeDisplayName(argType)); } } else if (paramType == null) { setArgTypeError(i, "Parameter type of '" + para.getName() + "' is unresolved"); @@ -439,6 +439,20 @@ public class MethodCallInfo { } } + /** + * in error "Expect T but got String", if T has a resolved bound aka "int" show it instead of T to avoid confusion. + * So the error would be "Expect int but got String" + */ + private String getTypeDisplayName(TypeInfo type) { + if(type.isTypeParameter()) { + TypeInfo bound = type.getBoundType(); + if (bound != null) + return bound.getDisplayName(); + } + + return type.getDisplayName(); + } + @Override public String toString() { StringBuilder sb = new StringBuilder("MethodCallInfo{"); diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/GenericContext.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/GenericContext.java index 25f457e7c..02f4618d3 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/GenericContext.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/GenericContext.java @@ -152,7 +152,6 @@ public final class GenericContext { // Direct type variable substitution. // Handles unresolved types (Java reflection path) and TypeInfo.typeParameter() (script-defined path). - // Bounded type parameters have the bound's simpleName, so use getTypeParameterName() for the lookup. if (!type.isResolved() || type.isTypeParameter()) { String lookupKey = type.isTypeParameter() ? type.getTypeParameterName() : type.getSimpleName(); TypeInfo substitution = resolveTypeVariable(lookupKey); diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java index ca08529e9..ad902b22d 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java @@ -87,6 +87,7 @@ public class TypeInfo { private boolean isPrimitive; // Whether this is a primitive type (e.g., int, boolean) private boolean isObjectLiteral; private String typeParameterName; // Non-null if this TypeInfo represents a type parameter (e.g., "T") + private TypeInfo boundType; // For type parameters with bounds, stores the bound for resolution // JavaScript/TypeScript type info (for types from .d.ts files) private final JSTypeInfo jsTypeInfo; // The JS type info (null if Java type) @@ -191,12 +192,15 @@ public class TypeInfo { /** * Create a TypeInfo representing a bounded type parameter (e.g., {@code T extends Entity}). - * Delegates member lookup to the bound type but records the original parameter name for display. + * Keeps the type parameter's own identity (paramName as simpleName/fullName) but stores the + * bound separately for display. Uses the bound's javaClass/kind for member resolution (erasure semantics). */ public static TypeInfo typeParameter(String paramName, TypeInfo boundType) { - TypeInfo ti = new TypeInfo(boundType.simpleName, boundType.fullName, boundType.packageName, - boundType.kind, boundType.javaClass, true, boundType.enclosingType, boundType.jsTypeInfo); + Class javaClass = (boundType != null && boundType.javaClass != null) ? boundType.javaClass : Object.class; + Kind kind = (boundType != null) ? boundType.kind : Kind.CLASS; + TypeInfo ti = new TypeInfo(paramName, paramName, "", kind, javaClass, true, null); ti.typeParameterName = paramName; + ti.boundType = boundType; return ti; } @@ -208,6 +212,10 @@ public class TypeInfo { return typeParameterName; } + public TypeInfo getBoundType() { + return boundType; + } + public static TypeInfo fromClass(Class clazz) { if (clazz == null) return null; @@ -307,12 +315,14 @@ public class TypeInfo { } if (type instanceof TypeVariable) { - // Type variable like T, E, K, V TypeVariable typeVar = (TypeVariable) type; String varName = typeVar.getName(); - // Create an unresolved type representing the type variable - // The substitution system will handle replacing this with the actual type - return TypeInfo.unresolved(varName, varName); + // Check for bounds - if bounded, create typeParameter with bound + Type[] bounds = typeVar.getBounds(); + if (bounds != null && bounds.length > 0 && bounds[0] != Object.class) { + return TypeInfo.typeParameter(varName, fromGenericType(bounds[0])); + } + return TypeInfo.typeParameter(varName); } if (type instanceof WildcardType) { diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeSubstitutor.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeSubstitutor.java index dbbbbe8be..2554867d0 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeSubstitutor.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeSubstitutor.java @@ -96,9 +96,7 @@ public class TypeSubstitutor { // Two cases: // 1. Unresolved TypeInfo (Java reflection path: TypeInfo.fromGenericType creates these for T, E, K, V) // 2. TypeInfo.typeParameter() (script-defined path: resolved but tagged as a type parameter) - // For tagged type parameters, use getTypeParameterName() as the lookup key — bounded params have - // the bound's simpleName as their simpleName (e.g., "Entity" for T extends Entity), so using - // simpleName would incorrectly look up "Entity" instead of "T". + // For tagged type parameters, use getTypeParameterName() as the lookup key. if (!type.isResolved() || type.isTypeParameter()) { String lookupKey = type.isTypeParameter() ? type.getTypeParameterName() : typeName; TypeInfo substitution = bindings.get(lookupKey);