mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 03:07:34 +00:00
Added error: "Cannot access non-static field from static context"
This commit is contained in:
1 parent
36e0e114fe
commit
0e2375e7f8
3 files changed
+107
-28
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import noppes.npcs.client.ClientProxy;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.expression.CastExpressionResolver;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.expression.ExpressionNode;
|
||||
@@ -3495,7 +3496,9 @@ public class ScriptDocument {
|
||||
currentType = null; // We'll use globalFields for the next segment
|
||||
} else if (Character.isUpperCase(firstSegment.charAt(0))) {
|
||||
// Static field access like SomeClass.field
|
||||
currentType = resolveType(firstSegment);
|
||||
TypeInfo classType = resolveType(firstSegment);
|
||||
// Class names are always in static context (can only access static members)
|
||||
currentType = (classType != null) ? classType.asStaticContext() : null;
|
||||
} else {
|
||||
// Variable or field access
|
||||
if (firstIsPrecededByDot) {
|
||||
@@ -3504,7 +3507,8 @@ public class ScriptDocument {
|
||||
TypeInfo receiverType = resolveReceiverChain(chainStart);
|
||||
if (receiverType != null && receiverType.hasField(firstSegment)) {
|
||||
FieldInfo varInfo = receiverType.getFieldInfo(firstSegment);
|
||||
currentType = (varInfo != null) ? varInfo.getTypeInfo() : null;
|
||||
// Field values are always instances, even if field itself is static
|
||||
currentType = (varInfo != null) ? varInfo.getTypeInfo().asInstanceContext() : null;
|
||||
} else {
|
||||
currentType = null; // Can't resolve
|
||||
}
|
||||
@@ -3512,7 +3516,8 @@ public class ScriptDocument {
|
||||
// This is a variable starting the chain
|
||||
FieldInfo varInfo = resolveVariable(firstSegment, chainStart);
|
||||
if (varInfo != null) {
|
||||
currentType = varInfo.getTypeInfo();
|
||||
// Variables are always instances
|
||||
currentType = varInfo.getTypeInfo().asInstanceContext();
|
||||
} else {
|
||||
currentType = null;
|
||||
}
|
||||
@@ -3544,7 +3549,8 @@ public class ScriptDocument {
|
||||
FieldAccessInfo accessInfo = createFieldAccessInfo(segment, segPos[0], segPos[1], receiverType, fieldInfo, isLastSegment, isStaticAccessSeg);
|
||||
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, accessInfo));
|
||||
currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo() : null;
|
||||
// Field values are always instances
|
||||
currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo().asInstanceContext() : null;
|
||||
} else {
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR));
|
||||
currentType = null;
|
||||
@@ -3554,29 +3560,51 @@ public class ScriptDocument {
|
||||
if (globalFields.containsKey(segment)) {
|
||||
FieldInfo fieldInfo = globalFields.get(segment);
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, fieldInfo));
|
||||
currentType = fieldInfo.getTypeInfo();
|
||||
// Field values are always instances
|
||||
currentType = fieldInfo.getTypeInfo().asInstanceContext();
|
||||
} else {
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR));
|
||||
currentType = null;
|
||||
}
|
||||
} else if (currentType != null && currentType.isResolved()) {
|
||||
// Check if this type has this field
|
||||
//TODO: THIS MAY NEED TO HANDLE STATIC VS INSTANCE FIELDS WITH DIFFERENT RENDERING
|
||||
if (currentType.hasField(segment)) {
|
||||
FieldInfo fieldInfo = currentType.getFieldInfo(segment);
|
||||
|
||||
// First check if field exists at all (ignoring static context)
|
||||
FieldInfo fieldInfo = null;
|
||||
boolean fieldExists = false;
|
||||
boolean isStaticAccessError = false;
|
||||
|
||||
// Check if field exists by temporarily using instance context
|
||||
TypeInfo instanceContext = currentType.asInstanceContext();
|
||||
if (instanceContext.hasField(segment)) {
|
||||
fieldExists = true;
|
||||
// Now check if accessible in actual context
|
||||
if (currentType.hasField(segment)) {
|
||||
fieldInfo = currentType.getFieldInfo(segment);
|
||||
} else {
|
||||
// Field exists but not accessible in static context
|
||||
isStaticAccessError = true;
|
||||
fieldInfo = instanceContext.getFieldInfo(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldInfo != null && !isStaticAccessError) {
|
||||
// Check if this is the last segment and if it's in an assignment context
|
||||
// Create and mark FieldAccessInfo for this segment (last or intermediate)
|
||||
FieldAccessInfo accessInfo = createFieldAccessInfo(segment, segPos[0], segPos[1], currentType, fieldInfo, isLastSegment, isStaticAccessSeg);
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, accessInfo));
|
||||
|
||||
// Update currentType for next segment
|
||||
currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo() : null;
|
||||
|
||||
// Update currentType for next segment - field values are always instances
|
||||
currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo().asInstanceContext() : null;
|
||||
|
||||
/*TODO THIS ELSE OVERRIDES FOR CHAINED METHOD CALLS LIKE "Minecraft.getMinecraft()"
|
||||
Do we resolve this by increasing METHOD_CALL PRIORITY or how exactly?
|
||||
Think of the best fitting solution that doesnt regress other parts of the whole framework.
|
||||
*/
|
||||
} else if (isStaticAccessError && fieldInfo != null) {
|
||||
// Mark as error - trying to access non-static field from static context
|
||||
String errorMsg = "Cannot access non-static field '" + segment + "' from static context '" + currentType.getSimpleName() + "'";
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR, errorMsg));
|
||||
currentType = null; // Can't continue resolving
|
||||
} else {
|
||||
marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR));
|
||||
currentType = null; // Can't continue resolving
|
||||
|
||||
+20
-6
@@ -34,8 +34,8 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
private ScriptTypeInfo outerClass;
|
||||
|
||||
private ScriptTypeInfo(String simpleName, String fullName, Kind kind,
|
||||
int declarationOffset, int bodyStart, int bodyEnd, int modifiers) {
|
||||
super(simpleName, fullName, "", kind, null, true, null, true);
|
||||
int declarationOffset, int bodyStart, int bodyEnd, int modifiers, boolean staticContext) {
|
||||
super(simpleName, fullName, "", kind, null, true, null, staticContext, true);
|
||||
this.scriptClassName = simpleName;
|
||||
this.declarationOffset = declarationOffset;
|
||||
this.bodyStart = bodyStart;
|
||||
@@ -46,13 +46,13 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
// Factory method
|
||||
public static ScriptTypeInfo create(String simpleName, Kind kind,
|
||||
int declarationOffset, int bodyStart, int bodyEnd, int modifiers) {
|
||||
return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd, modifiers);
|
||||
return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd, modifiers, false);
|
||||
}
|
||||
|
||||
public static ScriptTypeInfo createInner(String simpleName, Kind kind, ScriptTypeInfo outer,
|
||||
int declarationOffset, int bodyStart, int bodyEnd, int modifiers) {
|
||||
String fullName = outer.getFullName() + "$" + simpleName;
|
||||
ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd, modifiers);
|
||||
ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd, modifiers, false);
|
||||
inner.outerClass = outer;
|
||||
outer.innerClasses.add(inner);
|
||||
return inner;
|
||||
@@ -82,12 +82,26 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
|
||||
@Override
|
||||
public boolean hasField(String fieldName) {
|
||||
return fields.containsKey(fieldName);
|
||||
FieldInfo field = fields.get(fieldName);
|
||||
if (field == null) return false;
|
||||
|
||||
// In static context, only static fields are accessible
|
||||
if (isStaticContext() && !field.isStatic()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldInfo getFieldInfo(String fieldName) {
|
||||
return fields.get(fieldName);
|
||||
FieldInfo field = fields.get(fieldName);
|
||||
if (field == null) return null;
|
||||
|
||||
// In static context, only static fields are accessible
|
||||
if (isStaticContext() && !field.isStatic()) {
|
||||
return null; // Non-static field not accessible from static context
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public Map<String, FieldInfo> getFields() {
|
||||
|
||||
@@ -27,9 +27,10 @@ public class TypeInfo {
|
||||
private final Class<?> javaClass; // The actual resolved Java class (null if unresolved)
|
||||
private final boolean resolved; // Whether this type was successfully resolved
|
||||
private final TypeInfo enclosingType; // For inner classes, the outer type (null if top-level)
|
||||
private final boolean staticContext; // true if this is a class reference (can only access static members), false if instance
|
||||
|
||||
private TypeInfo(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass, boolean resolved, TypeInfo enclosingType) {
|
||||
Kind kind, Class<?> javaClass, boolean resolved, TypeInfo enclosingType, boolean staticContext) {
|
||||
this.simpleName = simpleName;
|
||||
this.fullName = fullName;
|
||||
this.packageName = packageName;
|
||||
@@ -37,12 +38,13 @@ public class TypeInfo {
|
||||
this.javaClass = javaClass;
|
||||
this.resolved = resolved;
|
||||
this.enclosingType = enclosingType;
|
||||
this.staticContext = staticContext;
|
||||
}
|
||||
|
||||
// Protected constructor for subclasses (like ScriptTypeInfo)
|
||||
protected TypeInfo(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass, boolean resolved, TypeInfo enclosingType,
|
||||
@SuppressWarnings("unused") boolean subclass) {
|
||||
boolean staticContext, @SuppressWarnings("unused") boolean subclass) {
|
||||
this.simpleName = simpleName;
|
||||
this.fullName = fullName;
|
||||
this.packageName = packageName;
|
||||
@@ -50,23 +52,24 @@ public class TypeInfo {
|
||||
this.javaClass = javaClass;
|
||||
this.resolved = resolved;
|
||||
this.enclosingType = enclosingType;
|
||||
this.staticContext = staticContext;
|
||||
}
|
||||
|
||||
// Factory methods
|
||||
public static TypeInfo resolved(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass) {
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, null);
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, null, false);
|
||||
}
|
||||
|
||||
public static TypeInfo resolvedInner(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass, TypeInfo enclosing) {
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, enclosing);
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, enclosing, false);
|
||||
}
|
||||
|
||||
public static TypeInfo unresolved(String simpleName, String fullPath) {
|
||||
int lastDot = fullPath.lastIndexOf('.');
|
||||
String pkg = lastDot > 0 ? fullPath.substring(0, lastDot) : "";
|
||||
return new TypeInfo(simpleName, fullPath, pkg, Kind.UNKNOWN, null, false, null);
|
||||
return new TypeInfo(simpleName, fullPath, pkg, Kind.UNKNOWN, null, false, null, false);
|
||||
}
|
||||
|
||||
public static TypeInfo fromClass(Class<?> clazz) {
|
||||
@@ -99,7 +102,7 @@ public class TypeInfo {
|
||||
enclosing = fromClass(clazz.getEnclosingClass());
|
||||
}
|
||||
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, clazz, true, enclosing);
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, clazz, true, enclosing, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +121,7 @@ public class TypeInfo {
|
||||
case "double": primitiveClass = double.class; break;
|
||||
case "void": primitiveClass = void.class; break;
|
||||
}
|
||||
return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null);
|
||||
return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null, false);
|
||||
}
|
||||
|
||||
// Getters
|
||||
@@ -130,6 +133,25 @@ public class TypeInfo {
|
||||
public boolean isResolved() { return resolved; }
|
||||
public TypeInfo getEnclosingType() { return enclosingType; }
|
||||
public boolean isInnerClass() { return enclosingType != null; }
|
||||
public boolean isStaticContext() { return staticContext; }
|
||||
|
||||
/**
|
||||
* Create a new TypeInfo with static context (for class references).
|
||||
* Used when referencing a class name directly, which can only access static members.
|
||||
*/
|
||||
public TypeInfo asStaticContext() {
|
||||
if (staticContext) return this;
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TypeInfo with instance context (for object instances).
|
||||
* Used when referencing an instance of a class, which can access both static and instance members.
|
||||
*/
|
||||
public TypeInfo asInstanceContext() {
|
||||
if (!staticContext) return this;
|
||||
return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate TokenType for highlighting this type.
|
||||
@@ -237,13 +259,19 @@ public class TypeInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this type has a field with the given name.
|
||||
* Check if this type has a field with the given name that is accessible in the current context.
|
||||
* In static context (class reference), only static fields are accessible.
|
||||
* In instance context, both static and instance fields are accessible.
|
||||
*/
|
||||
public boolean hasField(String fieldName) {
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
// In static context, only static fields are accessible
|
||||
if (staticContext && !java.lang.reflect.Modifier.isStatic(f.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -441,7 +469,9 @@ public class TypeInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get FieldInfo for a field by name. Returns null if not found.
|
||||
* Get FieldInfo for a field by name. Returns null if not found or not accessible.
|
||||
* In static context (class reference), only static fields are accessible.
|
||||
* In instance context, both static and instance fields are accessible.
|
||||
* Creates a synthetic FieldInfo based on reflection data.
|
||||
*/
|
||||
public FieldInfo getFieldInfo(String fieldName) {
|
||||
@@ -449,8 +479,15 @@ public class TypeInfo {
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
// In static context, only static fields are accessible
|
||||
if (staticContext && !java.lang.reflect.Modifier.isStatic(f.getModifiers())) {
|
||||
return null; // Non-static field not accessible from static context
|
||||
}
|
||||
// Create a synthetic FieldInfo from reflection
|
||||
return FieldInfo.fromReflection(f, this);
|
||||
// Always return instance context for the field's type (field values are instances)
|
||||
FieldInfo fieldInfo = FieldInfo.fromReflection(f, this);
|
||||
// The field's type should be in instance context since we're accessing a value
|
||||
return fieldInfo;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in new issue
Block a user