mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 20:44:08 +00:00
Fixed loop vars like for(var x in list) declared in top level outside of methods being declared duplicate
This commit is contained in:
1 parent
ec885d5ddb
commit
c00ad8e0a7
2 files changed
+136
-4
No files matched your search
+68
-1
@@ -19,7 +19,7 @@ class LoopVariableParser {
|
||||
"\\bfor\\s*\\(\\s*(?:var|let|const)\\s+(\\w+)\\s+in\\s+([^)]+)\\)");
|
||||
private static final Pattern FOR_OF = Pattern.compile(
|
||||
"\\bfor\\s*\\(\\s*(?:var|let|const)\\s+(\\w+)\\s+of\\s+([^)]+)\\)");
|
||||
private static final Pattern FOR_IN_OF_LOOKAHEAD = Pattern.compile("^\\s+(?:in|of)\\s");
|
||||
static final Pattern FOR_IN_OF_LOOKAHEAD = Pattern.compile("^\\s+(?:in|of)\\s");
|
||||
|
||||
private static final Set<String> SKIP_TYPES = new HashSet<>(Arrays.asList(
|
||||
"return", "if", "while", "for", "switch", "catch", "new", "throw"));
|
||||
@@ -63,6 +63,73 @@ class LoopVariableParser {
|
||||
Map<String, List<FieldInfo>> locals = methodLocals.computeIfAbsent(ownerMethod.getDeclarationOffset(), k -> new HashMap<>());
|
||||
scanRange(bodyText, bodyStart, bodyEnd, ownerMethod, locals, js);
|
||||
}
|
||||
|
||||
scanTopLevelLoops();
|
||||
}
|
||||
|
||||
private void scanTopLevelLoops() {
|
||||
boolean js = doc.isJavaScript();
|
||||
|
||||
if (!js) {
|
||||
for (Matcher m = ENHANCED_FOR.matcher(text); m.find(); ) {
|
||||
int absForPos = m.start();
|
||||
if (doc.isExcluded(absForPos) || isInsideAnyScope(absForPos)) continue;
|
||||
String typeName = m.group(1).trim();
|
||||
String varName = m.group(2);
|
||||
TypeInfo typeInfo = doc.resolveType(typeName);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, 0, text.length());
|
||||
FieldInfo fi = FieldInfo.localField(varName, typeInfo, m.start(2), null, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
doc.addTopLevelLocal(fi);
|
||||
}
|
||||
|
||||
for (Matcher m = CLASSIC_FOR.matcher(text); m.find(); ) {
|
||||
int absForPos = m.start();
|
||||
if (doc.isExcluded(absForPos) || isInsideAnyScope(absForPos)) continue;
|
||||
String typeName = m.group(1).trim();
|
||||
String varName = m.group(2);
|
||||
if (SKIP_TYPES.contains(typeName)) continue;
|
||||
TypeInfo typeInfo = doc.resolveType(typeName);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, 0, text.length());
|
||||
FieldInfo fi = FieldInfo.localField(varName, typeInfo, m.start(2), null, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
doc.addTopLevelLocal(fi);
|
||||
}
|
||||
} else {
|
||||
for (Matcher m = FOR_IN.matcher(text); m.find(); ) {
|
||||
int absForPos = m.start();
|
||||
if (doc.isExcluded(absForPos) || isInsideAnyScope(absForPos)) continue;
|
||||
String varName = m.group(1);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, 0, text.length());
|
||||
FieldInfo fi = FieldInfo.localField(varName, TypeInfo.fromPrimitive("int"), m.start(1), null, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
doc.addTopLevelLocal(fi);
|
||||
}
|
||||
|
||||
for (Matcher m = FOR_OF.matcher(text); m.find(); ) {
|
||||
int absForPos = m.start();
|
||||
if (doc.isExcluded(absForPos) || isInsideAnyScope(absForPos)) continue;
|
||||
String varName = m.group(1);
|
||||
String iterableExpr = m.group(2).trim();
|
||||
TypeInfo iterableType = doc.resolveExpressionType(iterableExpr, absForPos);
|
||||
TypeInfo elementType = (iterableType != null) ? iterableType.getElementType() : null;
|
||||
if (elementType == null) elementType = TypeInfo.ANY;
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, 0, text.length());
|
||||
FieldInfo fi = FieldInfo.localField(varName, elementType, m.start(1), null, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
doc.addTopLevelLocal(fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInsideAnyScope(int pos) {
|
||||
for (MethodInfo method : doc.getAllMethods()) {
|
||||
if (method.containsPosition(pos)) return true;
|
||||
}
|
||||
for (InnerCallableScope scope : doc.getInnerScopes()) {
|
||||
if (scope.containsPosition(pos)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void scanRange(String bodyText, int bodyStart, int bodyEnd,
|
||||
|
||||
@@ -113,6 +113,9 @@ public class ScriptDocument {
|
||||
private final Map<String, FieldInfo> globalFields = new HashMap<>();
|
||||
// Local variables per method (methodStartOffset -> {varName -> FieldInfo})
|
||||
private final Map<Integer, Map<String, List<FieldInfo>>> methodLocals = new HashMap<>();
|
||||
// Top-level variables (outside methods) - for JavaScript, these can be treated as globals, but we keep them separate for clarity
|
||||
// Mainly top level loops like for (var x : collection) { } where x is a local variable but not inside a method
|
||||
private final Map<String, List<FieldInfo>> topLevelLocals = new HashMap<>();
|
||||
|
||||
// Inner callable scopes (lambdas, JS function expressions) - NOT in methods list
|
||||
private final List<InnerCallableScope> innerScopes = new ArrayList<>();
|
||||
@@ -349,6 +352,7 @@ public class ScriptDocument {
|
||||
wildcardPackages.clear();
|
||||
excludedRanges.clear();
|
||||
methodLocals.clear();
|
||||
topLevelLocals.clear();
|
||||
scriptTypes.clear();
|
||||
innerScopes.clear();
|
||||
methodCalls.clear();
|
||||
@@ -1442,6 +1446,36 @@ public class ScriptDocument {
|
||||
return best;
|
||||
}
|
||||
|
||||
private FieldInfo pickVisibleTopLevelLocal(String name, int position) {
|
||||
return pickVisibleLocal(topLevelLocals.get(name), position);
|
||||
}
|
||||
|
||||
void addTopLevelLocal(FieldInfo fieldInfo) {
|
||||
if (fieldInfo == null) {
|
||||
return;
|
||||
}
|
||||
topLevelLocals.computeIfAbsent(fieldInfo.getName(), k -> new ArrayList<>()).add(fieldInfo);
|
||||
}
|
||||
|
||||
private Collection<FieldInfo> getVisibleTopLevelLocals(int position) {
|
||||
List<FieldInfo> visible = new ArrayList<>();
|
||||
for (List<FieldInfo> candidates : topLevelLocals.values()) {
|
||||
FieldInfo best = pickVisibleLocal(candidates, position);
|
||||
if (best != null) {
|
||||
visible.add(best);
|
||||
}
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
Collection<FieldInfo> getTopLevelLocalCandidates() {
|
||||
List<FieldInfo> all = new ArrayList<>();
|
||||
for (List<FieldInfo> fields : topLevelLocals.values()) {
|
||||
all.addAll(fields);
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse local variables inside methods/functions - UNIFIED for both Java and JavaScript.
|
||||
* Stores results in the shared 'methodLocals' map (methodOffset -> varName -> FieldInfo).
|
||||
@@ -2488,7 +2522,10 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
if (insideInner) continue;
|
||||
|
||||
|
||||
int afterVar = m.end(1);
|
||||
if (afterVar < text.length() && LoopVariableParser.FOR_IN_OF_LOOKAHEAD.matcher(text.substring(afterVar)).find()) continue;
|
||||
|
||||
String varName = m.group(1);
|
||||
String initializer = null;
|
||||
int initializerStart = -1;
|
||||
@@ -2560,6 +2597,10 @@ public class ScriptDocument {
|
||||
if (isExcluded(position))
|
||||
continue;
|
||||
|
||||
int enclosingParen = findEnclosingParenStart(0, position);
|
||||
if (enclosingParen >= 0 && "for".equals(readKeywordBefore(enclosingParen)))
|
||||
continue;
|
||||
|
||||
int modifiers = parseModifiers(typeNameRaw);
|
||||
String typeName = stripModifiers(typeNameRaw);
|
||||
|
||||
@@ -6900,7 +6941,8 @@ public class ScriptDocument {
|
||||
FieldInfo finalTargetField = targetField;
|
||||
boolean isScriptField = targetField != null &&
|
||||
(globalFields.containsValue(targetField) ||
|
||||
methodLocals.values().stream().anyMatch(m -> m.values().stream().anyMatch(list -> list.contains(finalTargetField))));
|
||||
methodLocals.values().stream().anyMatch(m -> m.values().stream().anyMatch(list -> list.contains(finalTargetField))) ||
|
||||
topLevelLocals.values().stream().anyMatch(list -> list.contains(finalTargetField)));
|
||||
|
||||
// Determine if the target field is final
|
||||
// For script fields, use the modifiers; for external fields, use reflection
|
||||
@@ -7653,7 +7695,10 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. For Java only: Check if we're inside a script type and look for fields there
|
||||
FieldInfo topLocal = pickVisibleTopLevelLocal(name, position);
|
||||
if (topLocal != null) return topLocal;
|
||||
|
||||
// 4. For Java only: Check if we're inside a script type and look for fields there
|
||||
if (!isJavaScript()) {
|
||||
ScriptTypeInfo enclosingType = findEnclosingScriptType(position);
|
||||
if (enclosingType != null && enclosingType.hasField(name)) {
|
||||
@@ -7882,6 +7927,14 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (List<FieldInfo> fields : topLevelLocals.values()) {
|
||||
for (FieldInfo field : fields) {
|
||||
AssignmentInfo assign = field.findAssignmentAtPosition(position);
|
||||
if (assign != null) {
|
||||
return assign;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (AssignmentInfo assign : declarationErrors) {
|
||||
if (assign.containsLhsPosition(position)) {
|
||||
@@ -7974,6 +8027,8 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
|
||||
variables.addAll(getVisibleTopLevelLocals(position));
|
||||
|
||||
// Add global fields (only those visible at position)
|
||||
for (FieldInfo globalField : globalFields.values()) {
|
||||
if (globalField.isVisibleAt(position)) {
|
||||
@@ -8037,6 +8092,11 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (List<FieldInfo> fields : topLevelLocals.values()) {
|
||||
for (FieldInfo field : fields) {
|
||||
errored.addAll(field.getErroredAssignments());
|
||||
}
|
||||
}
|
||||
|
||||
// Check external field assignments
|
||||
for (AssignmentInfo assign : externalFieldAssignments) {
|
||||
@@ -8232,6 +8292,11 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo topLevelLocal = pickVisibleTopLevelLocal(varName, position);
|
||||
if (topLevelLocal != null) {
|
||||
return topLevelLocal.getTypeInfo();
|
||||
}
|
||||
|
||||
// Check global fields
|
||||
if (globalFields.containsKey(varName)) {
|
||||
return globalFields.get(varName).getTypeInfo();
|
||||
|
||||
Reference in new issue
Block a user