diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/LoopVariableParser.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/LoopVariableParser.java index b71a8438a..657ada594 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/LoopVariableParser.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/LoopVariableParser.java @@ -12,9 +12,9 @@ import java.util.regex.Pattern; class LoopVariableParser { private static final Pattern ENHANCED_FOR = Pattern.compile( - "\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*:\\s*([^)]+)\\)"); + "\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>,\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*:\\s*([^)]+)\\)"); private static final Pattern CLASSIC_FOR = Pattern.compile( - "\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*="); + "\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>,\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*="); private static final Pattern FOR_IN = Pattern.compile( "\\bfor\\s*\\(\\s*(?:var|let|const)\\s+(\\w+)\\s+in\\s+([^)]+)\\)"); private static final Pattern FOR_OF = Pattern.compile( 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 de3e56d5e..3ec3fae19 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 @@ -3765,6 +3765,44 @@ public class ScriptDocument { // Fully-qualified Java types else if (baseName.contains(".")) { resolved = typeResolver.resolveFullName(baseName); + // Fallback: "SimpleName.InnerClass" where SimpleName is a known import + // e.g., "Map.Entry" when java.util.Map is imported → java.util.Map$Entry + if (resolved == null || !resolved.isResolved()) { + int firstDot = baseName.indexOf('.'); + String outerSimple = baseName.substring(0, firstDot); + String innerPath = baseName.substring(firstDot + 1); + TypeInfo outerType = typeResolver.resolveSimpleName(outerSimple, importsBySimpleName, wildcardPackages); + if (outerType != null && outerType.isResolved() && outerType.getJavaClass() != null) { + Class> currentClass = outerType.getJavaClass(); + for (String part : innerPath.split("\\.")) { + Class> found = null; + for (Class> inner : currentClass.getClasses()) { + if (inner.getSimpleName().equals(part)) { + found = inner; + break; + } + } + currentClass = found; + if (currentClass == null) break; + } + if (currentClass != null) { + resolved = TypeInfo.fromClass(currentClass); + // Track import usage for the outer class + ImportData usedImport = importsBySimpleName.get(outerSimple); + if (usedImport != null) { + usedImport.incrementUsage(); + } else if (wildcardPackages != null) { + String resultPkg = outerType.getPackageName(); + for (ImportData imp : imports) { + if (imp.isWildcard() && resultPkg != null && resultPkg.equals(imp.getFullPath())) { + imp.incrementUsage(); + break; + } + } + } + } + } + } } // Imported/simple else { @@ -3957,8 +3995,68 @@ public class ScriptDocument { } } + /** + * Two-phase object literal parsing. + * + *
Phase 1 (structure-only): parse every object literal with a no-op type + * resolver. This produces {@link ObjectLiteralParser.ObjectLiteralAnalysis} entries + * whose property names are correct but whose value types are all {@code ANY}. + * The synthetic {@link TypeInfo} shapes built from those names are then attached to + * the corresponding {@link InnerCallableScope}s via + * {@link ObjectLiteralParser#attachObjectLiteralContext}, so that + * {@code resolveThisType()} can return the containing object literal type for + * {@code this.property} chains.
+ * + *Phase 2 (full resolution): re-parse with the real + * {@code this::resolveExpressionType} callback. Because scopes now have their + * {@code containingObjectType} wired, expressions like {@code return this.faah.length} + * inside method bodies resolve correctly instead of falling back to {@code ANY}.
+ */ private void parseObjectLiterals() { if (!isJavaScript()) return; + + List