From 3039ddba1986a1940f468ebed913e2d68da52c32 Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 11 Mar 2026 14:12:49 +0100 Subject: [PATCH 1/2] fix loop warnings --- .../jurst/AntlrJurstParseTreeTransformer.java | 12 +++---- .../antlr/AntlrWurstParseTreeTransformer.java | 12 +++---- .../tests/wurstscript/tests/BugTests.java | 31 +++++++++++++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java index 2a2599de3..289ee73cd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java @@ -769,8 +769,8 @@ private WStatement transformForLoop(StmtForLoopContext s) { private WStatement transformForRangeLoop(ForRangeLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar); - loopVar.setInitialExpr(transformExpr(s.start)); + Expr start = transformExpr(s.start); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start); Expr to = transformExpr(s.end); Expr step; if (s.step == null) { @@ -787,17 +787,17 @@ private WStatement transformForRangeLoop(ForRangeLoopContext s) { throw error(s, "for range loop not implemented: " + text(s)); } - private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v) { - Modifiers modifiers = Ast.Modifiers(); + private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr) { + // Loop variables behave like let-variables for user code. + Modifiers modifiers = Ast.Modifiers(Ast.ModConstant(source(v))); OptTypeExpr optTyp = transformOptionalType(v.typeExpr()); Identifier name = text(v.name); - OptExpr initialExpr = Ast.NoExpr(); return Ast.LocalVarDef(source(v), modifiers, optTyp, name, initialExpr); } private WStatement transformForIteratorLoop(ForIteratorLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr()); Expr in = transformExpr(s.iteratorExpr); WStatements body = transformStatements(s.statementsBlock()); if (s.iterStyle.getType() == JurstParser.IN) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 3b20d5377..31e53030b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -933,8 +933,8 @@ private WStatement transformForLoop(StmtForLoopContext s) { private WStatement transformForRangeLoop(ForRangeLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar); - loopVar.setInitialExpr(transformExpr(s.start)); + Expr start = transformExpr(s.start); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start); Expr to = transformExpr(s.end); Expr step; if (s.step == null) { @@ -951,17 +951,17 @@ private WStatement transformForRangeLoop(ForRangeLoopContext s) { throw error(s, "not implemented: " + text(s)); } - private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v) { - Modifiers modifiers = Ast.Modifiers(); + private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr) { + // Loop variables behave like let-variables for user code. + Modifiers modifiers = Ast.Modifiers(Ast.ModConstant(source(v))); OptTypeExpr optTyp = transformOptionalType(v.typeExpr()); Identifier name = text(v.name); - OptExpr initialExpr = Ast.NoExpr(); return Ast.LocalVarDef(source(v), modifiers, optTyp, name, initialExpr); } private WStatement transformForIteratorLoop(ForIteratorLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr()); Expr in = transformExpr(s.iteratorExpr); WStatements body = transformStatements(s.statementsBlock()); if (s.iterStyle.getType() == WurstParser.IN) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java index 4f72ec463..5f7d347d1 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java @@ -898,6 +898,37 @@ public void unreadVarWarning3() { // #380 ); } + @Test + public void forRangeStartReadsParameter() { + CompilationResult result = test() + .setStopOnFirstError(false) + .executeProg(false) + .lines( + "package test", + "function foo(int bar)", + " for i = bar to 10", + " skip", + "endpackage" + ); + + Assert.assertTrue( + result.getGui().getWarningList().stream() + .noneMatch(w -> w.getMessage().contains("The parameter bar is never read")), + "Unexpected unused warning for parameter bar: " + result.getGui().getWarningList() + ); + } + + @Test + public void forRangeLoopVarIsImmutable() { + testAssertErrorsLines(false, "Cannot assign a new value to constant", + "package test", + "init", + " int stackPointer = 3", + " for i = 0 to stackPointer", + " i--", + "endpackage"); + } + @Test public void unreadVarWarningArrays() { // #813 From da79eae32a405a31e193ab73ca46c6b83f610500 Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 11 Mar 2026 15:26:52 +0100 Subject: [PATCH 2/2] build fixes --- .../attributes/prettyPrint/PrettyPrinter.java | 2 +- .../jurst/AntlrJurstParseTreeTransformer.java | 13 ++++++++----- .../antlr/AntlrWurstParseTreeTransformer.java | 13 ++++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index 017c84995..b19f826e2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -876,7 +876,7 @@ public static void prettyPrint(LocalVarDef e, Spacer spacer, StringBuilder sb, i printCommentsBefore(sb, e, indent); printIndent(sb, indent); if ((e.getOptTyp() instanceof NoTypeExpr)) { - if (e.attrIsConstant()) { + if (e.attrIsConstant() && !(e.getParent() instanceof StmtForRange)) { sb.append("let"); } else if (!(e.getParent() instanceof StmtForRange)) { sb.append("var"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java index c725d8943..dea5a1dce 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java @@ -783,7 +783,7 @@ private WStatement transformForLoop(StmtForLoopContext s) { private WStatement transformForRangeLoop(ForRangeLoopContext s) { WPos source = source(s); Expr start = transformExpr(s.start); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start, true); Expr to = transformExpr(s.end); Expr step; if (s.step == null) { @@ -800,9 +800,12 @@ private WStatement transformForRangeLoop(ForRangeLoopContext s) { throw error(s, "for range loop not implemented: " + text(s)); } - private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr) { - // Loop variables behave like let-variables for user code. - Modifiers modifiers = Ast.Modifiers(Ast.ModConstant(source(v))); + private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr, boolean constant) { + Modifiers modifiers = Ast.Modifiers(); + if (constant) { + // Range-loop variables behave like let-variables for user code. + modifiers.add(Ast.ModConstant(source(v))); + } OptTypeExpr optTyp = transformOptionalType(v.typeExpr()); Identifier name = text(v.name); return Ast.LocalVarDef(source(v), modifiers, optTyp, name, initialExpr); @@ -810,7 +813,7 @@ private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr ini private WStatement transformForIteratorLoop(ForIteratorLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr()); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr(), false); Expr in = transformExpr(s.iteratorExpr); WStatements body = transformStatements(s.statementsBlock()); if (s.iterStyle.getType() == JurstParser.IN) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index dde891359..6bab9fa0c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -947,7 +947,7 @@ private WStatement transformForLoop(StmtForLoopContext s) { private WStatement transformForRangeLoop(ForRangeLoopContext s) { WPos source = source(s); Expr start = transformExpr(s.start); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, start, true); Expr to = transformExpr(s.end); Expr step; if (s.step == null) { @@ -964,9 +964,12 @@ private WStatement transformForRangeLoop(ForRangeLoopContext s) { throw error(s, "not implemented: " + text(s)); } - private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr) { - // Loop variables behave like let-variables for user code. - Modifiers modifiers = Ast.Modifiers(Ast.ModConstant(source(v))); + private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr initialExpr, boolean constant) { + Modifiers modifiers = Ast.Modifiers(); + if (constant) { + // Range-loop variables behave like let-variables for user code. + modifiers.add(Ast.ModConstant(source(v))); + } OptTypeExpr optTyp = transformOptionalType(v.typeExpr()); Identifier name = text(v.name); return Ast.LocalVarDef(source(v), modifiers, optTyp, name, initialExpr); @@ -974,7 +977,7 @@ private LocalVarDef transformLocalVarDef(LocalVarDefInlineContext v, OptExpr ini private WStatement transformForIteratorLoop(ForIteratorLoopContext s) { WPos source = source(s); - LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr()); + LocalVarDef loopVar = transformLocalVarDef(s.loopVar, Ast.NoExpr(), false); Expr in = transformExpr(s.iteratorExpr); WStatements body = transformStatements(s.statementsBlock()); if (s.iterStyle.getType() == WurstParser.IN) {