Skip to content

Commit cef845f

Browse files
committed
Add lint validation for break/continue statement outside the loop body
1 parent ec04e5f commit cef845f

File tree

10 files changed

+125
-21
lines changed

10 files changed

+125
-21
lines changed

ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ public String perform(StagesData stagesData, Iterable<? extends SourceLocatedErr
1717
for (SourceLocatedError error : input) {
1818
sb.append(Console.newline());
1919
sb.append(error);
20-
sb.append(Console.newline());
2120
final Range range = error.getRange();
21+
if (range != null) {
22+
sb.append(' ').append(range.format());
23+
}
24+
sb.append(Console.newline());
2225
if (range != null && lines.length > 0) {
2326
var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new);
2427
positions.add(range);

ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ private Statement statement() {
135135
return doWhileStatement();
136136
}
137137
if (match(TokenType.BREAK)) {
138-
return new BreakStatement();
138+
return new BreakStatement(getRange(index - 1, index - 1));
139139
}
140140
if (match(TokenType.CONTINUE)) {
141-
return new ContinueStatement();
141+
return new ContinueStatement(getRange(index - 1, index - 1));
142142
}
143143
if (match(TokenType.RETURN)) {
144144
return new ReturnStatement(expression());

ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package com.annimon.ownlang.parser.ast;
22

33
import com.annimon.ownlang.lib.Value;
4+
import com.annimon.ownlang.util.Range;
5+
import com.annimon.ownlang.util.SourceLocation;
46

57
/**
68
*
79
* @author aNNiMON
810
*/
9-
public final class BreakStatement extends RuntimeException implements Statement {
11+
public final class BreakStatement extends RuntimeException implements Statement, SourceLocation {
12+
private final Range range;
13+
14+
public BreakStatement(Range range) {
15+
this.range = range;
16+
}
17+
18+
@Override
19+
public Range getRange() {
20+
return range;
21+
}
1022

1123
@Override
1224
public Value eval() {

ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package com.annimon.ownlang.parser.ast;
22

33
import com.annimon.ownlang.lib.Value;
4+
import com.annimon.ownlang.util.Range;
5+
import com.annimon.ownlang.util.SourceLocation;
46

57
/**
68
*
79
* @author aNNiMON
810
*/
9-
public final class ContinueStatement extends RuntimeException implements Statement {
11+
public final class ContinueStatement extends RuntimeException implements Statement, SourceLocation {
12+
private final Range range;
13+
14+
public ContinueStatement(Range range) {
15+
this.range = range;
16+
}
17+
18+
@Override
19+
public Range getRange() {
20+
return range;
21+
}
1022

1123
@Override
1224
public Value eval() {

ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public void visit(IncludeStatement st) {
3535
}
3636

3737
@Override
38-
public void visit(UseStatement st) {
39-
super.visit(st);
40-
moduleConstants.addAll(st.loadConstants().keySet());
38+
public void visit(UseStatement s) {
39+
super.visit(s);
40+
moduleConstants.addAll(s.loadConstants().keySet());
4141
}
4242
}

ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ public void visit(FunctionDefineStatement s) {
2222
}
2323

2424
@Override
25-
public void visit(IncludeStatement st) {
26-
super.visit(st);
27-
applyVisitor(st, this);
25+
public void visit(IncludeStatement s) {
26+
super.visit(s);
27+
applyVisitor(s, this);
2828
}
2929

3030
@Override
31-
public void visit(UseStatement st) {
32-
super.visit(st);
33-
moduleFunctions.addAll(st.loadFunctions().keySet());
31+
public void visit(UseStatement s) {
32+
super.visit(s);
33+
moduleFunctions.addAll(s.loadFunctions().keySet());
3434
}
3535
}

ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public Node perform(StagesData stagesData, Node input) {
2727
final LinterResults results = new LinterResults();
2828
final List<Visitor> validators = new ArrayList<>();
2929
validators.add(new IncludeSourceValidator(results));
30+
validators.add(new LoopStatementsValidator(results));
3031

3132
if (mode == Mode.SEMANTIC) {
3233
validators.forEach(input::accept);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.annimon.ownlang.parser.linters;
2+
3+
import com.annimon.ownlang.parser.ast.*;
4+
import java.util.ArrayDeque;
5+
import java.util.Deque;
6+
7+
final class LoopStatementsValidator extends LintVisitor {
8+
private enum LoopScope { FOR, WHILE, DO_WHILE, FOREACH_ARR, FOREACH_MAP };
9+
10+
private final Deque<LoopScope> loopScope;
11+
12+
LoopStatementsValidator(LinterResults results) {
13+
super(results);
14+
loopScope = new ArrayDeque<>(10);
15+
}
16+
17+
@Override
18+
public void visit(ForStatement s) {
19+
s.initialization.accept(this);
20+
s.termination.accept(this);
21+
s.increment.accept(this);
22+
loopScope.push(LoopScope.FOR);
23+
s.statement.accept(this);
24+
loopScope.remove();
25+
}
26+
27+
@Override
28+
public void visit(DoWhileStatement s) {
29+
s.condition.accept(this);
30+
loopScope.push(LoopScope.DO_WHILE);
31+
s.statement.accept(this);
32+
loopScope.remove();
33+
}
34+
35+
@Override
36+
public void visit(ForeachArrayStatement s) {
37+
s.container.accept(this);
38+
loopScope.push(LoopScope.FOREACH_ARR);
39+
s.body.accept(this);
40+
loopScope.remove();
41+
}
42+
43+
@Override
44+
public void visit(ForeachMapStatement s) {
45+
s.container.accept(this);
46+
loopScope.push(LoopScope.FOREACH_MAP);
47+
s.body.accept(this);
48+
loopScope.remove();
49+
}
50+
51+
@Override
52+
public void visit(WhileStatement s) {
53+
s.condition.accept(this);
54+
loopScope.push(LoopScope.WHILE);
55+
s.statement.accept(this);
56+
loopScope.remove();
57+
}
58+
59+
@Override
60+
public void visit(BreakStatement s) {
61+
if (loopScope.isEmpty()) {
62+
results.add(LinterResult.error(
63+
"break statement shouldn't be placed outside the loop body",
64+
s.getRange()));
65+
}
66+
}
67+
68+
@Override
69+
public void visit(ContinueStatement s) {
70+
if (loopScope.isEmpty()) {
71+
results.add(LinterResult.error(
72+
"continue statement shouldn't be placed outside the loop body",
73+
s.getRange()));
74+
}
75+
}
76+
}

ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,13 @@ public void visit(VariableExpression s) {
185185
}
186186

187187
@Override
188-
public void visit(WhileStatement st) {
189-
st.condition.accept(this);
190-
st.statement.accept(this);
188+
public void visit(WhileStatement s) {
189+
s.condition.accept(this);
190+
s.statement.accept(this);
191191
}
192192

193193
@Override
194-
public void visit(UseStatement st) {
194+
public void visit(UseStatement s) {
195195

196196
}
197197
}

ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ public Set<String> detect(Node s) {
1919
}
2020

2121
@Override
22-
public void visit(UseStatement st) {
23-
modules.addAll(st.modules);
24-
super.visit(st);
22+
public void visit(UseStatement s) {
23+
modules.addAll(s.modules);
24+
super.visit(s);
2525
}
2626
}

0 commit comments

Comments
 (0)