mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 23:37:32 +00:00
Inferred type of 'any' vars upon their first assignment
i.e.: var npc npc = event.getNpc() npc becomes ICustomNpc
This commit is contained in:
1 parent
761b59479e
commit
cd942fea30
7 files changed
+80
-15
No files matched your search
@@ -4480,7 +4480,7 @@ public class ScriptDocument {
|
||||
// Simple variable
|
||||
targetField = resolveVariable(targetName, lhsStart);
|
||||
if (targetField != null) {
|
||||
targetType = targetField.getDeclaredType();
|
||||
targetType = targetField.getTypeInfo();
|
||||
reflectionField = targetField.getReflectionField();
|
||||
}
|
||||
}
|
||||
@@ -4494,6 +4494,16 @@ public class ScriptDocument {
|
||||
ExpressionTypeResolver.CURRENT_EXPECTED_TYPE = null;
|
||||
}
|
||||
|
||||
// Type inference: If the target field has "any" type and no inferred type yet,
|
||||
// set the inferred type from the resolved source type (first assignment wins)
|
||||
if (targetField != null && targetField.canInferType() && targetField.getInferredType() == null
|
||||
&& sourceType != null && sourceType.isResolved()) {
|
||||
// Don't infer "any" type - that doesn't refine anything
|
||||
if (!"any".equals(sourceType.getFullName())) {
|
||||
targetField.setInferredType(sourceType);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if this is a script-defined field or external field
|
||||
FieldInfo finalTargetField = targetField;
|
||||
boolean isScriptField = targetField != null &&
|
||||
@@ -4590,7 +4600,7 @@ public class ScriptDocument {
|
||||
return; // Field doesn't exist, can't attach assignment
|
||||
}
|
||||
|
||||
TypeInfo targetType = targetField.getDeclaredType();
|
||||
TypeInfo targetType = targetField.getTypeInfo();
|
||||
|
||||
// Resolve the source type with expected type context
|
||||
TypeInfo sourceType;
|
||||
@@ -4601,6 +4611,15 @@ public class ScriptDocument {
|
||||
ExpressionTypeResolver.CURRENT_EXPECTED_TYPE = null;
|
||||
}
|
||||
|
||||
// Type inference: If the target field has "any" type and no inferred type yet,
|
||||
// set the inferred type from the resolved source type
|
||||
if (targetField.canInferType() && targetField.getInferredType() == null && sourceType != null && sourceType.isResolved()) {
|
||||
// Don't infer "any" type - that doesn't refine anything
|
||||
if (!"any".equals(sourceType.getFullName())) {
|
||||
targetField.setInferredType(sourceType);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the assignment info
|
||||
// Declaration assignments should NOT check final status - this is the one place where final fields can be assigned
|
||||
AssignmentInfo info = new AssignmentInfo(
|
||||
@@ -5575,14 +5594,14 @@ public class ScriptDocument {
|
||||
if (locals != null && locals.containsKey(varName)) {
|
||||
FieldInfo field = locals.get(varName);
|
||||
if (field.isVisibleAt(position)) {
|
||||
return field.getDeclaredType();
|
||||
return field.getTypeInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check global fields
|
||||
if (globalFields.containsKey(varName)) {
|
||||
return globalFields.get(varName).getDeclaredType();
|
||||
return globalFields.get(varName).getTypeInfo();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ public class FieldAccessInfo {
|
||||
|
||||
// Check return type compatibility with expected type (e.g., assignment LHS)
|
||||
if (expectedType != null && resolvedField != null) {
|
||||
TypeInfo fieldType = resolvedField.getDeclaredType();
|
||||
TypeInfo fieldType = resolvedField.getTypeInfo();
|
||||
if (fieldType != null && !TypeChecker.isTypeCompatible(expectedType, fieldType)) {
|
||||
//extra space is necessary for alignment
|
||||
//setError(ErrorType.TYPE_MISMATCH, "Provided type: " + fieldType.getSimpleName()+
|
||||
|
||||
@@ -57,6 +57,11 @@ public final class FieldInfo {
|
||||
// Enum constant specific info
|
||||
private EnumConstantInfo enumConstantInfo;
|
||||
|
||||
// Type inference support (for JavaScript "any" typed variables)
|
||||
// When a variable is declared without type (var x;), it starts as "any" but can be
|
||||
// refined through assignments or JSDoc comments
|
||||
private TypeInfo inferredType;
|
||||
|
||||
private FieldInfo(String name, Scope scope, TypeInfo declaredType,
|
||||
int declarationOffset, boolean resolved, MethodInfo containingMethod,
|
||||
String documentation, int initStart, int initEnd, int modifiers,
|
||||
@@ -411,12 +416,53 @@ public final class FieldInfo {
|
||||
return reflectionField;
|
||||
}
|
||||
|
||||
// ==================== TYPE INFERENCE ====================
|
||||
|
||||
/**
|
||||
* Get the inferred type for this field.
|
||||
* This is set when a variable declared as "any" has its type refined through
|
||||
* assignment analysis or JSDoc comments.
|
||||
*/
|
||||
public TypeInfo getInferredType() {
|
||||
return inferredType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inferred type for this field.
|
||||
* Used when type inference determines a more specific type than the declared type.
|
||||
* @param inferredType The refined type based on assignments or JSDoc
|
||||
*/
|
||||
public void setInferredType(TypeInfo inferredType) {
|
||||
this.inferredType = inferredType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this field has a type that can be refined (currently "any" type).
|
||||
*/
|
||||
public boolean canInferType() {
|
||||
return declaredType != null && "any".equals(declaredType.getFullName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effective type for this field, considering inference.
|
||||
* Returns inferredType if available, otherwise declaredType.
|
||||
*/
|
||||
public TypeInfo getEffectiveType() {
|
||||
if (inferredType != null) {
|
||||
return inferredType;
|
||||
}
|
||||
return declaredType;
|
||||
}
|
||||
|
||||
// ==================== BASIC GETTERS ====================
|
||||
|
||||
public String getName() { return name; }
|
||||
public Scope getScope() { return scope; }
|
||||
public TypeInfo getDeclaredType() { return declaredType; }
|
||||
public TypeInfo getTypeInfo() { return declaredType; } // Alias for getDeclaredType
|
||||
public TypeInfo getTypeInfo() {
|
||||
// Return the effective type (inferred if available, otherwise declared)
|
||||
return getEffectiveType();
|
||||
}
|
||||
public int getDeclarationOffset() { return declarationOffset; }
|
||||
public boolean isResolved() { return resolved; }
|
||||
public MethodInfo getContainingMethod() { return containingMethod; }
|
||||
|
||||
+5
-5
@@ -719,7 +719,7 @@ public class TokenHoverInfo {
|
||||
}
|
||||
}
|
||||
|
||||
TypeInfo declaredType = fieldInfo.getDeclaredType();
|
||||
TypeInfo declaredType = fieldInfo.getTypeInfo();
|
||||
|
||||
// Try to get modifiers from Java reflection if this is a chained field
|
||||
boolean foundModifiers = false;
|
||||
@@ -825,7 +825,7 @@ public class TokenHoverInfo {
|
||||
}
|
||||
}
|
||||
|
||||
TypeInfo declaredType = fieldInfo.getDeclaredType();
|
||||
TypeInfo declaredType = fieldInfo.getTypeInfo();
|
||||
if (declaredType != null) {
|
||||
int typeColor = getColorForTypeInfo(declaredType);
|
||||
addSegment(getName(declaredType), typeColor);
|
||||
@@ -873,7 +873,7 @@ public class TokenHoverInfo {
|
||||
}
|
||||
}
|
||||
|
||||
TypeInfo declaredType = fieldInfo.getDeclaredType();
|
||||
TypeInfo declaredType = fieldInfo.getTypeInfo();
|
||||
if (declaredType != null) {
|
||||
int typeColor = getColorForTypeInfo(declaredType);
|
||||
addSegment(getName(declaredType), typeColor);
|
||||
@@ -928,7 +928,7 @@ public class TokenHoverInfo {
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
if (i > 0) addSegment(", ", TokenType.DEFAULT.getHexColor());
|
||||
FieldInfo param = params.get(i);
|
||||
TypeInfo paramType = param.getDeclaredType();
|
||||
TypeInfo paramType = param.getTypeInfo();
|
||||
if (paramType != null) {
|
||||
int paramTypeColor = getColorForTypeInfo(paramType);
|
||||
addSegment(getName(paramType), paramTypeColor);
|
||||
@@ -1025,7 +1025,7 @@ public class TokenHoverInfo {
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
if (i > 0) addSegment(", ", TokenType.DEFAULT.getHexColor());
|
||||
FieldInfo param = params.get(i);
|
||||
TypeInfo paramType = param.getDeclaredType();
|
||||
TypeInfo paramType = param.getTypeInfo();
|
||||
if (paramType != null) {
|
||||
int paramTypeColor = getColorForTypeInfo(paramType);
|
||||
addSegment(getName(paramType), paramTypeColor);
|
||||
|
||||
+1
-1
@@ -362,7 +362,7 @@ public class MethodCallInfo {
|
||||
FieldInfo para = resolvedMethod.getParameters().get(i);
|
||||
|
||||
TypeInfo argType = arg.getResolvedType();
|
||||
TypeInfo paramType = para.getDeclaredType();
|
||||
TypeInfo paramType = para.getTypeInfo();
|
||||
if (argType != null && paramType != null) {
|
||||
if (!TypeChecker.isTypeCompatible(paramType, argType)) {
|
||||
setArgTypeError(i, "Expected " + paramType.getSimpleName() +
|
||||
|
||||
@@ -362,7 +362,7 @@ public final class MethodInfo {
|
||||
if (cachedSignature == null) {
|
||||
List<TypeInfo> paramTypes = new ArrayList<>();
|
||||
for (FieldInfo param : parameters)
|
||||
paramTypes.add(param.getDeclaredType());
|
||||
paramTypes.add(param.getTypeInfo());
|
||||
cachedSignature = new MethodSignature(name, paramTypes);
|
||||
}
|
||||
return cachedSignature;
|
||||
@@ -584,7 +584,7 @@ public final class MethodInfo {
|
||||
}
|
||||
|
||||
// Check for unresolved parameter types
|
||||
TypeInfo paramType = param.getDeclaredType();
|
||||
TypeInfo paramType = param.getTypeInfo();
|
||||
if (paramType == null || !paramType.isResolved()) {
|
||||
String typeName = paramType != null ? paramType.getSimpleName() : "unknown";
|
||||
addParameterError(param, i, ErrorType.PARAMETER_UNDEFINED,
|
||||
|
||||
+1
-1
@@ -910,7 +910,7 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
TypeInfo paramType = params.get(i).getDeclaredType();
|
||||
TypeInfo paramType = params.get(i).getTypeInfo();
|
||||
if (paramType == null)
|
||||
continue; // Unresolved param, skip check
|
||||
|
||||
|
||||
Reference in new issue
Block a user