Type Arg class ScriptTypeInfo + TokenHoverInfo + display fixes

This commit is contained in:
bigguy345 committed 2026-03-10 22:14:45 +02:00
1 parent a88fed137a
commit eef4e9a922
6 files changed
+65 -25

No files matched your search

@@ -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);
}
}
@@ -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));
}
}
@@ -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{");
@@ -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);
@@ -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) {
@@ -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);